diff --git a/README.md b/README.md index d7e7937..60ee9e8 100644 --- a/README.md +++ b/README.md @@ -2302,3 +2302,10 @@ MIT License - 테스트: - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_tab_loop_isolation\\ -p:IntermediateOutputPath=obj\\verify_tab_loop_isolation\\` 경고 0 / 오류 0 - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AppStateServiceTests" -p:OutputPath=bin\\verify_tab_loop_isolation_tests\\ -p:IntermediateOutputPath=obj\\verify_tab_loop_isolation_tests\\` 통과 45 +?낅뜲?댄듃: 2026-04-15 22:39 (KST) +- AX Agent 媛숈? ???덉쓽 ?ㅻⅨ ?€?붾? 蹂닿퀬 ?덉쓣 ??, ?щ쒕? ?꾨씪?대툕 移대뱶?€ ?ㅽ뻾 以??ㅽ???ㅻ벐 ?ㅼ븘?섎굹??븯?쇰뒗 ?뚭?瑜??섏젙?덉뒿?덈떎. `src/AxCopilot/Views/ChatStreamingUiPolicy.cs`?먯꽌 ?곷떒 ?쇱씠釉?媛€?대뱶 ?몄텧??`ActiveConversation`?먯꽌留?媛€?ν븯寃?留뚮뱾???낅땲?? +- `src/AxCopilot/Views/ChatWindow.xaml.cs`??`GetGuideVisibilityForActiveTab()`?쇰줈 ?꾩옱 ?좏깮?섏씠吏€???ㅽ듃由щ컢 媛€?ν꽦???듬났 愿€由ы븯怨? `RefreshStreamingControlsForActiveTab()`?먯꽌 ?쇱씠釉?移대뱶, ?곹깭 諛?, ?낅젰李?諛붾줈 ???쒓컙쨌?좏겙 ?섏씠釉붿쓣 媛숈? 湲곗쨪?쇰줈 泥섎━?섎룄濡?留욎톬?듬땲?? +- `src/AxCopilot/Views/ChatWindow.AgentStatusPresentation.cs`?€ `src/AxCopilot/Views/ChatWindow.StatusPresentation.cs`?먯꽌 idle ?€?몄뿉 background conversation?댁뼱?ㅽ듃由щ컢 ?뚰듃?€ 硫뷀듃由?以꾩쓣 ?ㅼ떆 ?뚮━吏€ ?딆쾶 ??諛꾧퉴吏€ 媛숈? ?대? ?쇱씠釉?UI媛€ 媛꾩슂?먯뿉 寃⑥?蹂댁씠吏€ ?딄쾶 ?섏젙?덉뒿?덈떎. +- 寃€利? + - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_background_conversation_live_ui\\ -p:IntermediateOutputPath=obj\\verify_background_conversation_live_ui\\` 寃쎄퀬 0 / ?ㅻ쪟 0 + - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatStreamingUiPolicyTests|AppStateServiceTests" -p:OutputPath=bin\\verify_background_conversation_live_ui_tests\\ -p:IntermediateOutputPath=obj\\verify_background_conversation_live_ui_tests\\` ?듦낵 49 diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 07443e0..6e35584 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -1625,3 +1625,12 @@ UI ?遺우쁽????域뱀뮆???귐뗫솯?醫딆춦 ???袁る퓮 ?臾믩씜 ??疫 - `src/AxCopilot/Services/AppStateService.cs`는 `GetAgentRunForTab(...)`과 탭 지정 `ApplyAgentEvent(...)`를 지원하도록 확장했고, `src/AxCopilot/Views/ChatWindow.ConversationListPresentation.cs`, `src/AxCopilot/Views/ChatWindow.TaskSummary.cs`가 현재 활성 탭의 run 메타를 읽도록 변경했습니다. - 테스트: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_tab_loop_isolation\\ -p:IntermediateOutputPath=obj\\verify_tab_loop_isolation\\` 경고 0 / 오류 0 - 테스트: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "AppStateServiceTests" -p:OutputPath=bin\\verify_tab_loop_isolation_tests\\ -p:IntermediateOutputPath=obj\\verify_tab_loop_isolation_tests\\` 통과 45 +업데이트: 2026-04-15 22:39 (KST) +- AX Agent 동일 탭 내 background conversation 표시 정책을 다시 정리했습니다. 기존 `src/AxCopilot/Views/ChatStreamingUiPolicy.cs`는 `BackgroundConversation`에도 상단 라이브 가이드를 유지하도록 되어 있어, 사용자가 같은 탭의 다른 대화를 보고 있을 때 idle 타이머가 다시 실행중 카드와 상태 바를 살려내는 회귀가 있었습니다. +- `ChatStreamingUiPolicy.ShouldShowTopLevelGuide(...)`를 `ActiveConversation` 전용으로 좁히고, `src/AxCopilot/Views/ChatWindow.xaml.cs`에는 `GetGuideVisibilityForActiveTab()`를 추가해 `RefreshStreamingControlsForActiveTab()`와 실시간 이벤트 렌더 분기가 동일한 가시성 기준을 공유하도록 맞췄습니다. +- `src/AxCopilot/Views/ChatWindow.AgentStatusPresentation.cs`의 `AgentProgressHintTimer_Tick(...)`는 background conversation 상태에서 `RemoveAgentLiveCard(animated: false)`, `HideStreamingStatusBar()`, `HideStickyProgress()`를 유지하고 실행 힌트 상태만 탭별로 보존합니다. 이로써 사용자가 다른 대화에 머무는 동안에는 실행중 카드가 다시 생기지 않고, 실제 실행 대화로 돌아왔을 때만 최신 run 상태가 다시 복원됩니다. +- `src/AxCopilot/Views/ChatWindow.StatusPresentation.cs`의 `UpdateStreamMetricsLabel(...)`도 같은 기준을 따르도록 조정했습니다. `_isStreaming`만 보던 기존 로직과 달리, 현재 활성 대화가 실제 스트리밍 대화일 때만 시간/토큰 라벨을 표시해 background conversation에서 메트릭 줄이 다시 나타나는 회귀를 막습니다. +- 테스트: `src/AxCopilot.Tests/Views/ChatStreamingUiPolicyTests.cs`의 상단 가이드 가시성 기대값을 갱신했습니다. +- 검증: + - `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_background_conversation_live_ui\\ -p:IntermediateOutputPath=obj\\verify_background_conversation_live_ui\\` 경고 0 / 오류 0 + - `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatStreamingUiPolicyTests|AppStateServiceTests" -p:OutputPath=bin\\verify_background_conversation_live_ui_tests\\ -p:IntermediateOutputPath=obj\\verify_background_conversation_live_ui_tests\\` 통과 49 diff --git a/src/AxCopilot.Tests/Views/ChatStreamingUiPolicyTests.cs b/src/AxCopilot.Tests/Views/ChatStreamingUiPolicyTests.cs index 69470a4..aba05bf 100644 --- a/src/AxCopilot.Tests/Views/ChatStreamingUiPolicyTests.cs +++ b/src/AxCopilot.Tests/Views/ChatStreamingUiPolicyTests.cs @@ -27,8 +27,8 @@ public class ChatStreamingUiPolicyTests [Theory] [InlineData(nameof(StreamingGuideVisibility.Hidden), false)] [InlineData(nameof(StreamingGuideVisibility.ActiveConversation), true)] - [InlineData(nameof(StreamingGuideVisibility.BackgroundConversation), true)] - public void ShouldShowTopLevelGuide_ShouldKeepGuideVisibleForBackgroundConversation( + [InlineData(nameof(StreamingGuideVisibility.BackgroundConversation), false)] + public void ShouldShowTopLevelGuide_ShouldOnlyShowGuideForVisibleStreamingConversation( string visibilityName, bool expected) { diff --git a/src/AxCopilot/Views/ChatStreamingUiPolicy.cs b/src/AxCopilot/Views/ChatStreamingUiPolicy.cs index d40d883..db2c70c 100644 --- a/src/AxCopilot/Views/ChatStreamingUiPolicy.cs +++ b/src/AxCopilot/Views/ChatStreamingUiPolicy.cs @@ -24,7 +24,7 @@ internal static class ChatStreamingUiPolicy } internal static bool ShouldShowTopLevelGuide(StreamingGuideVisibility visibility) - => visibility != StreamingGuideVisibility.Hidden; + => visibility == StreamingGuideVisibility.ActiveConversation; internal static bool ShouldRenderConversationBoundContent(StreamingGuideVisibility visibility) => visibility == StreamingGuideVisibility.ActiveConversation; diff --git a/src/AxCopilot/Views/ChatWindow.AgentStatusPresentation.cs b/src/AxCopilot/Views/ChatWindow.AgentStatusPresentation.cs index 4f2ba41..06a2320 100644 --- a/src/AxCopilot/Views/ChatWindow.AgentStatusPresentation.cs +++ b/src/AxCopilot/Views/ChatWindow.AgentStatusPresentation.cs @@ -319,6 +319,19 @@ public partial class ChatWindow return; } + var guideVisibility = GetGuideVisibilityForActiveTab(); + if (!ChatStreamingUiPolicy.ShouldShowTopLevelGuide(guideVisibility)) + { + RemoveAgentLiveCard(animated: false); + HideStreamingStatusBar(); + HideStickyProgress(); + UpdateLiveAgentProgressHint(null, runTab: _activeTab); + UpdateStreamMetricsLabel( + (int)Math.Max(0, _tabCumulativeInputTokens.GetValueOrDefault(_activeTab)), + (int)Math.Max(0, _tabCumulativeOutputTokens.GetValueOrDefault(_activeTab))); + return; + } + // _streamingTabs.Contains(_activeTab)가 위에서 이미 검증됨 — _streamRunTab은 다른 탭 시작 시 덮어써질 수 있으므로 _activeTab 사용 var runTab = _activeTab; if (!string.Equals(runTab, "Cowork", StringComparison.OrdinalIgnoreCase) diff --git a/src/AxCopilot/Views/ChatWindow.StatusPresentation.cs b/src/AxCopilot/Views/ChatWindow.StatusPresentation.cs index cc403d5..082c18e 100644 --- a/src/AxCopilot/Views/ChatWindow.StatusPresentation.cs +++ b/src/AxCopilot/Views/ChatWindow.StatusPresentation.cs @@ -236,12 +236,18 @@ public partial class ChatWindow { if (StreamMetricsLabel == null) return; - if (!_isStreaming) + if (!_isStreaming || !ChatStreamingUiPolicy.ShouldShowTopLevelGuide(GetGuideVisibilityForActiveTab())) { StreamMetricsLabel.Visibility = Visibility.Collapsed; return; } + if (inputTokens == 0 && outputTokens == 0) + { + inputTokens = (int)Math.Max(0, _tabCumulativeInputTokens.GetValueOrDefault(_activeTab)); + outputTokens = (int)Math.Max(0, _tabCumulativeOutputTokens.GetValueOrDefault(_activeTab)); + } + StreamMetricsLabel.Visibility = Visibility.Visible; var elapsedText = "0s"; diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index cf851a6..4d7bfc6 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -1684,9 +1684,7 @@ public partial class ChatWindow : Window private void RefreshStreamingControlsForActiveTab() { var isOwningTab = _streamingTabs.Contains(_activeTab); - var guideVisibility = ChatStreamingUiPolicy.ResolveGuideVisibility( - isOwningTab, - isOwningTab && IsStreamingConversationVisible(_activeTab)); + var guideVisibility = GetGuideVisibilityForActiveTab(); var shouldShowTopLevelGuide = ChatStreamingUiPolicy.ShouldShowTopLevelGuide(guideVisibility); // 실행 중에도 Send 버튼 표시 — 메시지가 큐에 추가됨 @@ -1724,6 +1722,10 @@ public partial class ChatWindow : Window if (pendingUiEvent != null) UpdateAgentLiveCardV2(pendingUiEvent); } + + UpdateStreamMetricsLabel( + (int)Math.Max(0, _tabCumulativeInputTokens.GetValueOrDefault(_activeTab)), + (int)Math.Max(0, _tabCumulativeOutputTokens.GetValueOrDefault(_activeTab))); } private void TrackStreamingConversation(string tab, ChatConversation conversation) @@ -1759,6 +1761,14 @@ public partial class ChatWindow : Window } } + private StreamingGuideVisibility GetGuideVisibilityForActiveTab() + { + var isOwningTab = _streamingTabs.Contains(_activeTab); + return ChatStreamingUiPolicy.ResolveGuideVisibility( + isOwningTab, + isOwningTab && IsStreamingConversationVisible(_activeTab)); + } + private void ClearStreamingConversation(string tab, ChatConversation? expectedConversation = null) { var normalizedTab = NormalizeTabName(tab);