AX Agent 실행 마감 helper 통합과 계획 노출 최소화
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- send와 regenerate가 같은 실행/예외/취소/최종 커밋/후처리 helper를 타도록 ExecutePreparedTurnAsync 추가 - 계획 이벤트는 기본적으로 얇은 요약 pill만 노출하고 debug에서만 큰 카드가 보이도록 조정 - README와 DEVELOPMENT 문서에 2026-04-05 18:08 (KST) 기준 이력 반영 - 검증: dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ (경고 0 / 오류 0)
This commit is contained in:
@@ -833,6 +833,9 @@ ow + toggle 시각 언어로 통일했습니다.
|
||||
- 큰 카드형 요소도 더 `claw-code` 쪽으로 눌렀습니다. [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 `AddPlanningCard(...)` 에서 계획 카드 라운드, 패딩, 헤더 아이콘/텍스트, 진행률 텍스트, 단계 행 폰트를 전반적으로 줄였고, 계획 헤더 문구도 더 짧게 정리했습니다.
|
||||
- 같은 변경에서 `CreateCompactEventPill(...)`, `CreateTimelineLoadMoreCard(...)`도 함께 축소해 컨텍스트 압축 pill과 “이전 대화 더 보기” 카드가 본문보다 과하게 두껍게 보이지 않도록 맞췄습니다.
|
||||
- 업데이트: 2026-04-05 18:01 (KST)
|
||||
- 엔진 마감도 한 단계 더 진행했습니다. [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)에 `ExecutePreparedTurnAsync(...)`를 추가해 `send`와 `regenerate`가 같은 실행/예외/취소/최종 커밋/후처리 경로를 타도록 묶었습니다. 이제 전송과 재생성은 같은 prepared-execution 축에서 닫히고, 실패 토스트와 최종 assistant 커밋도 같은 helper가 담당합니다.
|
||||
- 같은 변경에서 계획 이벤트는 기본적으로 큰 카드가 아니라 얇은 요약 pill로만 보이고, `debug` 로그일 때만 `AddPlanningCard(...)`가 펼쳐지도록 바꿨습니다. 문서형 Cowork/Code 작업에서도 기본 노출이 더 `claw-code`처럼 차분한 상태가 됐습니다.
|
||||
- 업데이트: 2026-04-05 18:08 (KST)
|
||||
- 좌측 패널과 하단 바도 `claw-code` 쪽 밀도로 다시 맞췄습니다. [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 에서 사이드바 폭을 줄이고, 헤더 앱 배지를 강조색 채운 정사각형 대신 `HintBackground + BorderColor` 기반의 작은 배지형으로 바꿨습니다.
|
||||
- `새 대화`, `검색`, `작업 유형/워크스페이스`, 하단 사용자 영역, 삭제 영역까지 패딩과 폰트, 아이콘 크기를 함께 낮췄고, 하단 상태바는 다이아몬드 아이콘을 작은 원형 점으로 바꿔 더 단순한 상태선처럼 보이게 정리했습니다.
|
||||
- 실행 로그 배너도 본문 침범을 더 줄였습니다. [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)의 `AddAgentEventBanner(...)` 에서 debug 전용 `ToolInput` 카드 길이를 더 짧게 줄였고, `FilePath`는 일반 로그에서는 빠른 액션이 붙은 카드형 대신 파일명 한 줄만 약하게 표시하도록 바꿨습니다.
|
||||
|
||||
@@ -4612,3 +4612,7 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
|
||||
- 같은 변경에서 `CreateCompactEventPill(...)`, `CreateTimelineLoadMoreCard(...)`도 함께 축소해 컨텍스트 압축 pill과 “이전 대화 더 보기” 카드가 본문보다 과하게 두껍게 보이지 않도록 맞췄습니다.
|
||||
- 검증 예정: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\`
|
||||
- 업데이트: 2026-04-05 18:01 (KST)
|
||||
- 엔진 마감도 한 단계 더 진행했습니다. [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs)에 `ExecutePreparedTurnAsync(...)`를 추가해 `send`와 `regenerate`가 같은 실행/예외/취소/최종 커밋/후처리 helper를 타도록 묶었습니다. 이로써 prepared-execution 이후 마감 경로도 하나로 수렴했습니다.
|
||||
- 같은 변경에서 계획 이벤트는 기본적으로 큰 카드가 아니라 얇은 요약 pill로만 보이고, `debug` 로그일 때만 `AddPlanningCard(...)`가 펼쳐지도록 바꿨습니다. 문서형 Cowork/Code 작업에서도 기본 노출이 더 최소화됐습니다.
|
||||
- 검증 예정: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\`
|
||||
- 업데이트: 2026-04-05 18:08 (KST)
|
||||
|
||||
@@ -8372,30 +8372,17 @@ public partial class ChatWindow : Window
|
||||
}
|
||||
}
|
||||
|
||||
var response = await _chatEngine.ExecutePreparedAsync(
|
||||
var executionOutcome = await ExecutePreparedTurnAsync(
|
||||
conv,
|
||||
runTab,
|
||||
originTab,
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, originTab, conv, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
StopAiIconPulse();
|
||||
_cachedStreamContent = response;
|
||||
|
||||
draftSucceeded = true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true);
|
||||
assistantContent = finalized.Content;
|
||||
draftCancelled = finalized.Cancelled;
|
||||
draftFailure = finalized.FailureReason;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, ex);
|
||||
assistantContent = finalized.Content;
|
||||
ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600);
|
||||
draftFailure = finalized.FailureReason;
|
||||
"응답 생성 중...");
|
||||
conv = executionOutcome.Conversation;
|
||||
assistantContent = executionOutcome.AssistantContent;
|
||||
draftSucceeded = executionOutcome.DraftSucceeded;
|
||||
draftCancelled = executionOutcome.DraftCancelled;
|
||||
draftFailure = executionOutcome.DraftFailure;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -8405,18 +8392,7 @@ public partial class ChatWindow : Window
|
||||
_llm.ClearRouteOverride();
|
||||
UpdateModelLabel();
|
||||
}
|
||||
ResetStreamingUiState();
|
||||
}
|
||||
|
||||
lock (_convLock)
|
||||
{
|
||||
var session = ChatSession;
|
||||
assistantContent = _chatEngine.FinalizeAssistantTurn(session, conv, runTab, assistantContent, _storage);
|
||||
_currentConversation = session?.CurrentConversation ?? conv;
|
||||
conv = _currentConversation!;
|
||||
}
|
||||
|
||||
FinalizeConversationTurn(originTab, conv);
|
||||
FinalizeQueuedDraft(originTab, queuedDraftId, draftSucceeded, draftCancelled, draftFailure);
|
||||
}
|
||||
|
||||
@@ -8553,6 +8529,87 @@ public partial class ChatWindow : Window
|
||||
_ = Dispatcher.BeginInvoke(new Action(() => StartNextQueuedDraftIfAny()), DispatcherPriority.Background);
|
||||
}
|
||||
|
||||
private sealed record PreparedTurnOutcome(
|
||||
ChatConversation Conversation,
|
||||
string AssistantContent,
|
||||
bool DraftSucceeded,
|
||||
bool DraftCancelled,
|
||||
string? DraftFailure);
|
||||
|
||||
private async Task<PreparedTurnOutcome> ExecutePreparedTurnAsync(
|
||||
ChatConversation conversation,
|
||||
string runTab,
|
||||
string rememberTab,
|
||||
AxAgentExecutionEngine.PreparedExecution preparedExecution,
|
||||
string busyStatus)
|
||||
{
|
||||
_isStreaming = true;
|
||||
_streamRunTab = runTab;
|
||||
BtnSend.IsEnabled = false;
|
||||
BtnSend.Visibility = Visibility.Collapsed;
|
||||
BtnStop.Visibility = Visibility.Visible;
|
||||
if (runTab == "Cowork" || runTab == "Code")
|
||||
BtnPause.Visibility = Visibility.Visible;
|
||||
_streamCts = new CancellationTokenSource();
|
||||
ForceScrollToEnd();
|
||||
|
||||
var assistantContent = string.Empty;
|
||||
var draftSucceeded = false;
|
||||
var draftCancelled = false;
|
||||
string? draftFailure = null;
|
||||
|
||||
_activeStreamText = null;
|
||||
_cachedStreamContent = "";
|
||||
_displayedLength = 0;
|
||||
_cursorVisible = true;
|
||||
_aiIconPulseStopped = false;
|
||||
_streamStartTime = DateTime.UtcNow;
|
||||
_elapsedTimer.Start();
|
||||
SetStatus(busyStatus, spinning: true);
|
||||
|
||||
try
|
||||
{
|
||||
var response = await _chatEngine.ExecutePreparedAsync(
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, rememberTab, conversation, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
StopAiIconPulse();
|
||||
_cachedStreamContent = response;
|
||||
draftSucceeded = true;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true);
|
||||
assistantContent = finalized.Content;
|
||||
draftCancelled = finalized.Cancelled;
|
||||
draftFailure = finalized.FailureReason;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var finalized = _chatEngine.FinalizeExecutionContent(assistantContent, ex);
|
||||
assistantContent = finalized.Content;
|
||||
draftFailure = finalized.FailureReason;
|
||||
ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetStreamingUiState();
|
||||
}
|
||||
|
||||
lock (_convLock)
|
||||
{
|
||||
var session = ChatSession;
|
||||
assistantContent = _chatEngine.FinalizeAssistantTurn(session, conversation, runTab, assistantContent, _storage);
|
||||
_currentConversation = session?.CurrentConversation ?? conversation;
|
||||
conversation = _currentConversation!;
|
||||
}
|
||||
|
||||
FinalizeConversationTurn(rememberTab, conversation);
|
||||
return new PreparedTurnOutcome(conversation, assistantContent, draftSucceeded, draftCancelled, draftFailure);
|
||||
}
|
||||
|
||||
private void ScheduleExecutionHistoryRender(bool autoScroll = true)
|
||||
{
|
||||
_pendingExecutionHistoryAutoScroll |= autoScroll;
|
||||
@@ -10287,10 +10344,29 @@ public partial class ChatWindow : Window
|
||||
{
|
||||
var logLevel = _settings.Settings.Llm.AgentLogLevel;
|
||||
|
||||
// Planning 이벤트는 단계 목록 카드로 별도 렌더링
|
||||
// Planning 이벤트는 기본적으로 얇은 요약만 보이고, debug에서만 큰 카드로 펼칩니다.
|
||||
if (evt.Type == AgentEventType.Planning && evt.Steps is { Count: > 0 })
|
||||
{
|
||||
AddPlanningCard(evt);
|
||||
if (string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
AddPlanningCard(evt);
|
||||
}
|
||||
else
|
||||
{
|
||||
var compactPrimaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||
var compactSecondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||
var compactHintBg = TryFindResource("HintBackground") as Brush
|
||||
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||
var compactBorderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||
var compactAccentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue;
|
||||
var summary = !string.IsNullOrWhiteSpace(evt.Summary)
|
||||
? evt.Summary!
|
||||
: $"계획 {evt.Steps.Count}단계";
|
||||
var pill = CreateCompactEventPill(summary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush);
|
||||
pill.Opacity = 0;
|
||||
pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160)));
|
||||
MessagePanel.Children.Add(pill);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10876,64 +10952,15 @@ public partial class ChatWindow : Window
|
||||
private async Task SendRegenerateAsync(ChatConversation conv)
|
||||
{
|
||||
var runTab = NormalizeTabName(conv.Tab);
|
||||
_isStreaming = true;
|
||||
_streamRunTab = runTab;
|
||||
BtnSend.IsEnabled = false;
|
||||
BtnSend.Visibility = Visibility.Collapsed;
|
||||
BtnStop.Visibility = Visibility.Visible;
|
||||
_streamCts = new CancellationTokenSource();
|
||||
ForceScrollToEnd(); // 응답 시작 시 강제 하단 이동
|
||||
|
||||
var assistantContent = string.Empty;
|
||||
_activeStreamText = null;
|
||||
_cachedStreamContent = "";
|
||||
_displayedLength = 0;
|
||||
_cursorVisible = true;
|
||||
_aiIconPulseStopped = false;
|
||||
_streamStartTime = DateTime.UtcNow;
|
||||
_elapsedTimer.Start();
|
||||
SetStatus("에이전트 작업 중...", spinning: true);
|
||||
|
||||
try
|
||||
{
|
||||
List<ChatMessage> sendMessages;
|
||||
AxAgentExecutionEngine.PreparedExecution preparedExecution;
|
||||
lock (_convLock)
|
||||
preparedExecution = PrepareExecutionForConversation(conv, runTab, null);
|
||||
var executionMode = preparedExecution.Mode;
|
||||
sendMessages = preparedExecution.Messages;
|
||||
|
||||
var response = await _chatEngine.ExecutePreparedAsync(
|
||||
preparedExecution,
|
||||
(messages, token) => RunAgentLoopAsync(runTab, runTab, conv, messages, token),
|
||||
(messages, token) => _llm.SendAsync(messages.ToList(), token),
|
||||
_streamCts.Token);
|
||||
assistantContent = response;
|
||||
StopAiIconPulse();
|
||||
_cachedStreamContent = response;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
assistantContent = _chatEngine.FinalizeExecutionContent(assistantContent, cancelled: true).Content;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
assistantContent = _chatEngine.FinalizeExecutionContent(assistantContent, ex).Content;
|
||||
ShowToast("실패한 요청은 작업 요약에서 다시 시도할 수 있습니다.", "\uE783", 2600);
|
||||
}
|
||||
finally
|
||||
{
|
||||
ResetStreamingUiState();
|
||||
}
|
||||
|
||||
AxAgentExecutionEngine.PreparedExecution preparedExecution;
|
||||
lock (_convLock)
|
||||
{
|
||||
var session = ChatSession;
|
||||
assistantContent = _chatEngine.FinalizeAssistantTurn(session, conv, runTab, assistantContent, _storage);
|
||||
_currentConversation = session?.CurrentConversation ?? conv;
|
||||
conv = _currentConversation!;
|
||||
}
|
||||
FinalizeConversationTurn(conv.Tab ?? _activeTab, conv);
|
||||
preparedExecution = PrepareExecutionForConversation(conv, runTab, null);
|
||||
await ExecutePreparedTurnAsync(
|
||||
conv,
|
||||
runTab,
|
||||
conv.Tab ?? _activeTab,
|
||||
preparedExecution,
|
||||
"에이전트 작업 중...");
|
||||
}
|
||||
|
||||
/// <summary>채팅 본문 폭을 세 탭에서 동일한 기준으로 맞춥니다.</summary>
|
||||
|
||||
Reference in New Issue
Block a user