From 90f92ccee5da67f79edef79420c80ba1b45c0d2b Mon Sep 17 00:00:00 2001 From: lacvet Date: Wed, 15 Apr 2026 15:58:40 +0900 Subject: [PATCH] =?UTF-8?q?=EF=BB=BF=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=EC=A7=84=ED=96=89=20=EC=B9=B4=EB=93=9C=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 메인 루프2 이후 Cowork/Code 실행 중 채팅창 상단 라이브 진행 카드가 transcript 재렌더링이나 이벤트 타이밍에 따라 사라지던 회귀를 수정했다. - ChatWindow.LiveProgressPresentation에 EnsureAgentLiveCardVisible를 추가해 라이브 카드 컨테이너가 없거나 transcript에서 떨어졌을 때 즉시 재생성 및 재부착되도록 보강 - ChatWindow.xaml.cs에서 agent event 수신과 live hint 시작 시 eligible 탭이면 항상 라이브 카드 복구를 먼저 수행한 뒤 상태를 갱신하도록 정리 - ChatWindow.V2Rendering에서 부분/전체 렌더 재구성 후 _isStreaming 상태면 라이브 카드를 자동 복원하도록 변경 - README와 docs/DEVELOPMENT.md에 수정 배경, 적용 파일, 검증 결과를 2026-04-15 15:56 (KST) 기준으로 반영 검증: - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_live_card_guard\\ -p:IntermediateOutputPath=obj\\verify_live_card_guard\\ (경고 0 / 오류 0) - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests" -p:OutputPath=bin\\verify_live_card_guard_tests\\ -p:IntermediateOutputPath=obj\\verify_live_card_guard_tests\\ (통과 49) --- README.md | 7 +++++++ docs/DEVELOPMENT.md | 6 ++++++ .../ChatWindow.LiveProgressPresentation.cs | 21 ++++++++++++++++++- src/AxCopilot/Views/ChatWindow.V2Rendering.cs | 8 +++---- src/AxCopilot/Views/ChatWindow.xaml.cs | 7 ++++--- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index ef3a9b9..81c6913 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,13 @@ Windows 전용 시맨틱 런처 & 워크스페이스 매니저 개발 참고: Claw Code 동등성 작업 추적 문서 `docs/claw-code-parity-plan.md` +- 업데이트: 2026-04-15 15:56 (KST) +- 메인 루프2 이후 채팅창 바로 위 라이브 진행 카드가 다시 사라질 수 있던 경로를 보강했습니다. [ChatWindow.LiveProgressPresentation.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.LiveProgressPresentation.cs)에 `EnsureAgentLiveCardVisible(...)`를 추가해, Cowork/Code 실행 중 라이브 카드 컨테이너가 아직 없거나 transcript 재구성으로 빠졌을 때 즉시 재생성/재부착되도록 했습니다. +- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)는 agent event 수신과 live hint 시작 시 eligible 탭이면 위 helper를 먼저 호출한 뒤 상태를 갱신하도록 정리했습니다. 이로써 이벤트는 오는데 채팅창 바로 위 라이브 카드만 사라진 채 남는 회귀를 막습니다. +- [ChatWindow.V2Rendering.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.V2Rendering.cs)는 부분/전체 transcript 재렌더링 때 `_v2LiveContainer` 존재 여부에 의존하지 않고 `_isStreaming` 상태면 동일 helper를 호출해 라이브 카드를 자동 복원합니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_live_card_guard\\ -p:IntermediateOutputPath=obj\\verify_live_card_guard\\` 경고 0 / 오류 0 +- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests" -p:OutputPath=bin\\verify_live_card_guard_tests\\ -p:IntermediateOutputPath=obj\\verify_live_card_guard_tests\\` 통과 49 + - 업데이트: 2026-04-15 15:40 (KST) - Code 탭의 실제 도구 실행 워크스페이스가 설정값의 `CodeWorkFolder`/`WorkFolder`로 고정되고, 현재 대화가 가진 `WorkFolder`와 어긋날 수 있던 문제를 수정했습니다. [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs)는 새 `RuntimeWorkFolderOverride`를 통해 실행 턴마다 대화 기준 워크스페이스를 우선 사용하고, [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)는 `RunAgentLoopAsync(...)`에서 현재 conversation의 `WorkFolder`를 루프에 직접 주입합니다. - 같은 기준을 권한 팝업에도 적용했습니다. 사내 모드에서 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 권한 안내 문구는 이제 `_currentConversation`의 우연한 상태가 아니라, 실제 실행 중인 탭 루프가 들고 있는 워크스페이스 override를 우선 기준으로 삼습니다. 그 결과 `지정한 워크스페이스 하위 경로는 무승인`, `그 외 경로만 승인` 규칙이 Code 탭에서도 Cowork와 같은 기준으로 맞춰집니다. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 17898b2..91b791e 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -1,5 +1,11 @@ 업데이트: 2026-04-14 19:50 (KST) 업데이트: 2026-04-15 12:51 (KST) +- 업데이트: 2026-04-15 15:56 (KST) +- AX Agent 상단 라이브 진행 카드 복원 가드를 추가했습니다. `src/AxCopilot/Views/ChatWindow.LiveProgressPresentation.cs`에 `EnsureAgentLiveCardVisible(...)`를 만들고, Cowork/Code 실행 중 라이브 카드가 아직 생성되지 않았거나 transcript 재구성으로 빠졌을 때 즉시 재생성/재부착되도록 했습니다. +- `src/AxCopilot/Views/ChatWindow.xaml.cs`는 agent event 수신과 live hint 시작 시 eligible 탭이면 위 helper를 먼저 호출한 뒤 live card 상태를 갱신하도록 바꿨습니다. 메인 루프2 이후 이벤트는 오는데 채팅창 바로 위 라이브 카드만 사라지는 회귀를 막는 목적입니다. +- `src/AxCopilot/Views/ChatWindow.V2Rendering.cs`는 부분/전체 transcript 재렌더링 중 `_v2LiveContainer` 존재 여부만 보지 않고 `_isStreaming` 상태면 helper를 통해 라이브 카드를 자동 복원합니다. +- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_live_card_guard\\ -p:IntermediateOutputPath=obj\\verify_live_card_guard\\` 경고 0 / 오류 0 +- 검증: `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter "ChatWindowSlashPolicyTests" -p:OutputPath=bin\\verify_live_card_guard_tests\\ -p:IntermediateOutputPath=obj\\verify_live_card_guard_tests\\` 통과 49 - AX Agent 진행 이력 정제를 위해 `src/AxCopilot/Services/Agent/AgentProgressSummarySanitizer.cs`를 추가했습니다. 스트리밍 중간 preview, `Thinking` 요약, `[이전 도구 호출: ...]` transcript 꼬리, 숫자/대괄호 같은 저품질 조각 문자열을 공통 규칙으로 정리합니다. - `src/AxCopilot/Services/Agent/AgentLoopService.cs`는 스트리밍 `TextDelta` preview emit과 일반 `Thinking` emit 전에 정제기를 적용합니다. 정제 후 비어버린 summary는 이벤트 자체를 만들지 않아, 중간 응답의 `1`, `[`, `file_read]` 같은 파편이 timeline/history에 쌓이지 않습니다. - `src/AxCopilot/Views/ChatWindow.V2LiveProgressPresentation.cs`, `src/AxCopilot/Views/ChatWindow.V2AgentEventPresentation.cs`, `src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs`는 렌더링 직전에도 같은 정제 로직을 사용합니다. 기존 세션의 오래된 low-signal thinking event가 다시 그려질 때도 빈약한 단문을 숨기고 process feed에는 `진행 내용 정리`로 폴백합니다. diff --git a/src/AxCopilot/Views/ChatWindow.LiveProgressPresentation.cs b/src/AxCopilot/Views/ChatWindow.LiveProgressPresentation.cs index 3c64cd4..0004028 100644 --- a/src/AxCopilot/Views/ChatWindow.LiveProgressPresentation.cs +++ b/src/AxCopilot/Views/ChatWindow.LiveProgressPresentation.cs @@ -19,7 +19,26 @@ public partial class ChatWindow if (MessageList == null) return; if (!string.Equals(runTab, _activeTab, StringComparison.OrdinalIgnoreCase)) return; - ShowAgentLiveCardV2(runTab); + EnsureAgentLiveCardVisible(runTab); + } + + private void EnsureAgentLiveCardVisible(string runTab) + { + if (MessageList == null) return; + if (!IsAgentLiveCardEligibleTab(runTab)) return; + if (!string.Equals(runTab, _activeTab, StringComparison.OrdinalIgnoreCase)) return; + + if (_v2LiveContainer == null) + { + ShowAgentLiveCardV2(runTab); + return; + } + + if (!ContainsTranscriptElement(_v2LiveContainer)) + { + AddTranscriptElement(_v2LiveContainer); + ForceScrollToEnd(); + } } private void UpdateAgentLiveCard(string message, string? subItem = null, diff --git a/src/AxCopilot/Views/ChatWindow.V2Rendering.cs b/src/AxCopilot/Views/ChatWindow.V2Rendering.cs index 7dbc1ba..4a7e33a 100644 --- a/src/AxCopilot/Views/ChatWindow.V2Rendering.cs +++ b/src/AxCopilot/Views/ChatWindow.V2Rendering.cs @@ -100,8 +100,8 @@ public partial class ChatWindow } // 라이브 컨테이너 재삽입 - if (_v2LiveContainer != null && _isStreaming) - AddTranscriptElement(_v2LiveContainer); + if (_isStreaming) + EnsureAgentLiveCardVisible(_activeTab); } else { @@ -122,8 +122,8 @@ public partial class ChatWindow } // 라이브 컨테이너 재삽입 - if (_v2LiveContainer != null && _isStreaming) - AddTranscriptElement(_v2LiveContainer); + if (_isStreaming) + EnsureAgentLiveCardVisible(_activeTab); } _v2LastRenderedKeys.Clear(); diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index a9b3a3f..219d695 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -6656,9 +6656,10 @@ public partial class ChatWindow : Window var eventTab = runTab; // V2 라이브 카드 실시간 업데이트 - if (_v2LiveContainer != null - && string.Equals(runTab, _activeTab, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(runTab, _activeTab, StringComparison.OrdinalIgnoreCase) + && IsAgentLiveCardEligibleTab(runTab)) { + EnsureAgentLiveCardVisible(runTab); UpdateAgentLiveCardV2(evt); } @@ -6735,7 +6736,7 @@ public partial class ChatWindow : Window var runTab = string.IsNullOrWhiteSpace(_streamRunTab) ? _activeTab : _streamRunTab!; if (IsAgentLiveCardEligibleTab(runTab)) { - ShowAgentLiveCard(runTab); + EnsureAgentLiveCardVisible(runTab); var initialStatus = AgentStatusNarrativeCatalog.BuildInitial(runTab); UpdateLiveAgentProgressHint(initialStatus.Message, "agent_wait"); ShowStreamingStatusBar(initialStatus.Message, detail: initialStatus.Detail);