diff --git a/README.md b/README.md index e5446e3..8dd956e 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ Windows 전용 시맨틱 런처 & 워크스페이스 매니저 - AX Agent 직접 대화(Chat 탭) 경로에 실제 스트리밍 응답 연결을 복구했습니다. LLM 서비스는 원래 SSE/스트리밍을 지원하고 있었지만 UI 실행 경로가 최종 문자열만 받아 한 번에 붙이던 상태였고, 이제 설정상 스트리밍이 켜져 있으면 채팅 응답이 타자 치듯 점진적으로 표시됩니다. - Cowork/Code는 기존처럼 agent loop 진행 메시지 중심을 유지하고, 직접 대화 재생성은 같은 스트리밍 경로를 공유하도록 정리했습니다. +- 업데이트: 2026-04-07 02:31 (KST) +- Cowork/Code도 에이전트 루프가 끝난 뒤 최종 답변을 한 번에 붙이지 않고, 짧은 라이브 타이핑 프리뷰를 거친 다음 정식 메시지로 확정되도록 보강했습니다. 처리 중에는 진행 로그를 유지하고, 최종 응답 구간만 자연스럽게 이어지도록 정리했습니다. + - 업데이트: 2026-04-07 02:03 (KST) - Cowork 진행 표시가 오래 비거나 완료 후 실패처럼 깜박이던 흐름을 정리했습니다. 진행 힌트는 작업 시작 직후부터 즉시 보이게 하고, 중간 이벤트가 들어와도 불필요하게 사라지지 않도록 유지 로직을 조정했습니다. - 프리셋/입력창/진행 카드에 남아 있던 깨진 한글을 복구했고, 내부 실행 게이트 문구도 정상 한국어로 정리해 Cowork 루프가 잘못된 안내 문구에 끌리지 않도록 보강했습니다. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 4b6178f..9c41129 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -5286,3 +5286,4 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎. - Document update: 2026-04-07 02:23 (KST) - Reconnected the AX Agent direct-chat execution path to the existing SSE/streaming transport in `LlmService`. Chat replies no longer wait for the final full string before rendering; when streaming is enabled they now advance through the existing streaming container and typing-timer path. - Document update: 2026-04-07 02:23 (KST) - Updated `AxAgentExecutionEngine.ResolveExecutionMode()` so non-agent chat can opt into streaming transport, and wired `ChatWindow.ExecutePreparedTurnAsync()` to consume `LlmService.StreamAsync(...)` for direct conversations while keeping Cowork/Code on the agent-loop progress-feed path. +- Document update: 2026-04-07 02:31 (KST) - Added a typed final-response preview for Cowork/Code agent-loop completions. Those tabs still use progress-feed execution during the loop, but once a final assistant answer is available it now passes through the same streaming container/typing presentation before the final transcript message is committed. diff --git a/src/AxCopilot/Views/ChatWindow.xaml.cs b/src/AxCopilot/Views/ChatWindow.xaml.cs index 09ad1d0..df65c07 100644 --- a/src/AxCopilot/Views/ChatWindow.xaml.cs +++ b/src/AxCopilot/Views/ChatWindow.xaml.cs @@ -5653,7 +5653,13 @@ public partial class ChatWindow : Window draftSucceeded = true; if (streamingContainer != null && streamingText != null) + { FinalizeStreamingContainer(streamingContainer, streamingText, assistantContent); + } + else if (preparedExecution.Mode.UseAgentLoop && !string.IsNullOrWhiteSpace(assistantContent)) + { + await ShowTypedAssistantPreviewAsync(assistantContent, _streamCts.Token); + } } catch (OperationCanceledException) { @@ -5699,6 +5705,33 @@ public partial class ChatWindow : Window return new PreparedTurnOutcome(conversation, assistantContent, draftSucceeded, draftCancelled, draftFailure); } + private async Task ShowTypedAssistantPreviewAsync(string finalContent, CancellationToken ct) + { + if (string.IsNullOrWhiteSpace(finalContent) || MessagePanel == null) + return; + + var container = CreateStreamingContainer(out var streamText); + _activeStreamText = streamText; + _cachedStreamContent = finalContent; + _displayedLength = 0; + _cursorVisible = true; + MessagePanel.Children.Add(container); + ForceScrollToEnd(); + _cursorTimer.Start(); + _typingTimer.Start(); + streamText.Text = _cursorVisible ? "\u258c" : " "; + + var deadline = DateTime.UtcNow.AddMilliseconds(Math.Clamp(finalContent.Length * 6, 500, 1800)); + while (_displayedLength < _cachedStreamContent.Length && DateTime.UtcNow < deadline && !ct.IsCancellationRequested) + await Task.Delay(30, ct); + + _displayedLength = _cachedStreamContent.Length; + if (_activeStreamText != null) + _activeStreamText.Text = _cachedStreamContent; + + FinalizeStreamingContainer(container, streamText, finalContent); + } + private void ScheduleExecutionHistoryRender(bool autoScroll = true) { _pendingExecutionHistoryAutoScroll |= autoScroll;