AX Agent 중간 처리 메시지를 Claude Code 스타일에 가깝게 정리
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- 진행 중 이벤트(Planning, StepStart, StepDone, Thinking, ToolCall, SkillCall)를 작은 상태 칩 대신 요약줄+본문 설명 구조로 재구성했습니다. - claw-code의 AssistantToolUseMessage/HookProgressMessage 흐름을 참고해 중간 처리 과정이 hover 없이도 transcript 안에서 읽히도록 조정했습니다. - 권한 요청 및 결과 카드는 기존 richer card 구조를 유지하고, process feed 이벤트만 별도 경량 표현으로 분리했습니다. - README와 DEVELOPMENT 문서에 2026-04-06 22:31 (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:
@@ -1368,3 +1368,6 @@ MIT License
|
|||||||
- AX Agent 루프에 남아 있던 `무료 티어 모드` 대기를 Gemini 서비스에서만 적용하도록 좁혔습니다. 이제 예전 Gemini 무료 티어용 대기 설정이 vLLM/Ollama/Claude 같은 다른 서비스 작업을 불필요하게 늦추지 않습니다.
|
- AX Agent 루프에 남아 있던 `무료 티어 모드` 대기를 Gemini 서비스에서만 적용하도록 좁혔습니다. 이제 예전 Gemini 무료 티어용 대기 설정이 vLLM/Ollama/Claude 같은 다른 서비스 작업을 불필요하게 늦추지 않습니다.
|
||||||
- AX Agent 내부 설정의 `Fast` 표기를 `Gemini 무료 티어 대기`로 바꾸고 설명 문구도 실제 동작 기준으로 수정했습니다. 사용자는 이제 내부 설정에서 이 대기를 명확히 끄고 켤 수 있습니다.
|
- AX Agent 내부 설정의 `Fast` 표기를 `Gemini 무료 티어 대기`로 바꾸고 설명 문구도 실제 동작 기준으로 수정했습니다. 사용자는 이제 내부 설정에서 이 대기를 명확히 끄고 켤 수 있습니다.
|
||||||
- Cowork/Code 중간 진행 정보가 hover처럼 우연히만 보이던 문제를 줄이기 위해 에이전트 이벤트 카드 스타일을 다시 조정했습니다. 단계 시작, 도구 호출, 대기/생각 중 상태가 더 큰 글씨와 얇은 배경 카드로 기본 노출되도록 정리해, 장시간 작업 중에도 “지금 무엇을 하는지”를 본문에서 바로 읽을 수 있게 했습니다.
|
- Cowork/Code 중간 진행 정보가 hover처럼 우연히만 보이던 문제를 줄이기 위해 에이전트 이벤트 카드 스타일을 다시 조정했습니다. 단계 시작, 도구 호출, 대기/생각 중 상태가 더 큰 글씨와 얇은 배경 카드로 기본 노출되도록 정리해, 장시간 작업 중에도 “지금 무엇을 하는지”를 본문에서 바로 읽을 수 있게 했습니다.
|
||||||
|
- 업데이트: 2026-04-06 22:31 (KST)
|
||||||
|
- AX Agent의 중간 처리 메시지 형식을 `claw-code`에 더 가깝게 재정리했습니다. `Thinking / ToolCall / StepStart / Planning` 계열은 작은 상태칩보다 `요약줄 + 본문 설명` 구조로 보이게 바꿔, 장시간 작업 중 “무슨 작업을 하고 있는지”를 일반 메시지처럼 읽을 수 있게 했습니다.
|
||||||
|
- 진행 메시지의 요약줄은 좌측 chevron과 보조 텍스트 중심으로 정리하고, 상세 설명은 바로 아래 본문 텍스트로 분리해 `claw-code / Claude Code`의 처리 메시지 밀도와 감각에 더 가깝게 맞췄습니다.
|
||||||
|
|||||||
@@ -5058,3 +5058,11 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
|
|||||||
- 단계 시작 이벤트도 progress bar만 갱신하고 끝나는 것이 아니라 transcript pill로 함께 남기도록 변경
|
- 단계 시작 이벤트도 progress bar만 갱신하고 끝나는 것이 아니라 transcript pill로 함께 남기도록 변경
|
||||||
- `ToolCall` 이벤트를 기본 transcript에서 숨기지 않도록 복원
|
- `ToolCall` 이벤트를 기본 transcript에서 숨기지 않도록 복원
|
||||||
- 생각/단계/도구 호출 카드의 글씨 크기, 배경, 테두리, 패딩을 키워 hover로 우연히 보이는 tiny 로그 느낌을 줄임
|
- 생각/단계/도구 호출 카드의 글씨 크기, 배경, 테두리, 패딩을 키워 hover로 우연히 보이는 tiny 로그 느낌을 줄임
|
||||||
|
|
||||||
|
## 2026-04-06 22:31 (KST)
|
||||||
|
|
||||||
|
- [ChatWindow.AgentEventRendering.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.AgentEventRendering.cs)의 중간 처리 메시지 렌더를 `claude-code` 레퍼런스에 더 가깝게 재구성했다.
|
||||||
|
- `Planning`, `StepStart`, `StepDone`, `Thinking`, `ToolCall`, `SkillCall`을 별도 `process feed event`로 분리
|
||||||
|
- 기존의 작은 pill 카드 대신 `chevron 요약줄 + 본문 설명` 구조로 렌더
|
||||||
|
- 장시간 작업 중에도 단계/툴 실행/생각 중 상태가 일반 assistant 진행 메시지처럼 읽히도록 정리
|
||||||
|
- permission/result 카드와 진행 중 feed를 분리해, 실제 처리 메시지는 `claude-code / Claude Code`처럼 더 담백한 transcript 흐름으로 보이게 조정했다.
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ public partial class ChatWindow
|
|||||||
{
|
{
|
||||||
return new Border
|
return new Border
|
||||||
{
|
{
|
||||||
Background = hintBg,
|
Background = Brushes.Transparent,
|
||||||
BorderBrush = borderBrush,
|
BorderBrush = Brushes.Transparent,
|
||||||
BorderThickness = new Thickness(1),
|
BorderThickness = new Thickness(0),
|
||||||
CornerRadius = new CornerRadius(999),
|
CornerRadius = new CornerRadius(0),
|
||||||
Padding = new Thickness(8, 4, 8, 4),
|
Padding = new Thickness(0),
|
||||||
Margin = new Thickness(8, 3, 180, 3),
|
Margin = new Thickness(12, 4, 12, 2),
|
||||||
HorizontalAlignment = HorizontalAlignment.Left,
|
HorizontalAlignment = HorizontalAlignment.Left,
|
||||||
Child = new StackPanel
|
Child = new StackPanel
|
||||||
{
|
{
|
||||||
@@ -28,26 +28,144 @@ public partial class ChatWindow
|
|||||||
{
|
{
|
||||||
new TextBlock
|
new TextBlock
|
||||||
{
|
{
|
||||||
Text = "\uE9CE",
|
Text = "›",
|
||||||
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
FontSize = 12,
|
||||||
FontSize = 9.5,
|
Foreground = secondaryText,
|
||||||
Foreground = accentBrush,
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = new Thickness(0, 0, 6, 0),
|
||||||
},
|
},
|
||||||
new TextBlock
|
new TextBlock
|
||||||
{
|
{
|
||||||
Text = summary,
|
Text = summary,
|
||||||
FontSize = 9.5,
|
FontSize = 11,
|
||||||
FontWeight = FontWeights.Medium,
|
FontWeight = FontWeights.Normal,
|
||||||
Foreground = primaryText,
|
Foreground = secondaryText,
|
||||||
Margin = new Thickness(5, 0, 0, 0),
|
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
TextWrapping = TextWrapping.Wrap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Border CreateProcessFeedBody(string text, Brush primaryText)
|
||||||
|
{
|
||||||
|
return new Border
|
||||||
|
{
|
||||||
|
Background = Brushes.Transparent,
|
||||||
|
BorderBrush = Brushes.Transparent,
|
||||||
|
BorderThickness = new Thickness(0),
|
||||||
|
Padding = new Thickness(0),
|
||||||
|
Margin = new Thickness(26, 3, 12, 8),
|
||||||
|
Child = new TextBlock
|
||||||
|
{
|
||||||
|
Text = text,
|
||||||
|
FontSize = 13.5,
|
||||||
|
FontWeight = FontWeights.Medium,
|
||||||
|
Foreground = primaryText,
|
||||||
|
TextWrapping = TextWrapping.Wrap,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsProcessFeedEvent(AgentEvent evt)
|
||||||
|
{
|
||||||
|
return evt.Type is AgentEventType.Planning
|
||||||
|
or AgentEventType.StepStart
|
||||||
|
or AgentEventType.StepDone
|
||||||
|
or AgentEventType.Thinking
|
||||||
|
or AgentEventType.ToolCall
|
||||||
|
or AgentEventType.SkillCall;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildProcessFeedSummary(AgentEvent evt, string transcriptBadgeLabel, string itemDisplayName)
|
||||||
|
{
|
||||||
|
return evt.Type switch
|
||||||
|
{
|
||||||
|
AgentEventType.Planning when evt.Steps is { Count: > 0 }
|
||||||
|
=> $"{evt.Steps.Count}단계 계획 정리",
|
||||||
|
AgentEventType.StepStart when evt.StepTotal > 0
|
||||||
|
=> $"{evt.StepCurrent}/{evt.StepTotal} 단계 진행",
|
||||||
|
AgentEventType.StepDone when evt.StepTotal > 0
|
||||||
|
=> $"{evt.StepCurrent}/{evt.StepTotal} 단계 완료",
|
||||||
|
AgentEventType.Thinking when !string.IsNullOrWhiteSpace(evt.Summary)
|
||||||
|
=> evt.Summary,
|
||||||
|
AgentEventType.ToolCall
|
||||||
|
=> string.IsNullOrWhiteSpace(itemDisplayName)
|
||||||
|
? $"{transcriptBadgeLabel} 실행"
|
||||||
|
: $"{itemDisplayName} 실행",
|
||||||
|
AgentEventType.SkillCall
|
||||||
|
=> string.IsNullOrWhiteSpace(itemDisplayName)
|
||||||
|
? "스킬 실행"
|
||||||
|
: $"{itemDisplayName} 실행",
|
||||||
|
_ => string.IsNullOrWhiteSpace(evt.Summary) ? transcriptBadgeLabel : evt.Summary,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddProcessFeedMessage(AgentEvent evt, string transcriptBadgeLabel, string itemDisplayName, string? eventSummaryText)
|
||||||
|
{
|
||||||
|
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
|
||||||
|
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray;
|
||||||
|
var hintBg = TryFindResource("HintBackground") as Brush
|
||||||
|
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||||
|
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||||
|
var accentBrush = TryFindResource("AccentColor") as Brush ?? Brushes.CornflowerBlue;
|
||||||
|
|
||||||
|
var summary = BuildProcessFeedSummary(evt, transcriptBadgeLabel, itemDisplayName).Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(summary))
|
||||||
|
summary = transcriptBadgeLabel;
|
||||||
|
|
||||||
|
var stack = new StackPanel
|
||||||
|
{
|
||||||
|
Margin = new Thickness(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
var summaryRow = CreateCompactEventPill(summary, primaryText, secondaryText, hintBg, borderBrush, accentBrush);
|
||||||
|
summaryRow.Opacity = 0;
|
||||||
|
summaryRow.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(140)));
|
||||||
|
stack.Children.Add(summaryRow);
|
||||||
|
|
||||||
|
var body = (eventSummaryText ?? string.Empty).Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(body)
|
||||||
|
&& !string.Equals(body, summary, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var bodyBlock = CreateProcessFeedBody(body, primaryText);
|
||||||
|
bodyBlock.Opacity = 0;
|
||||||
|
bodyBlock.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(180)));
|
||||||
|
stack.Children.Add(bodyBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(evt.FilePath))
|
||||||
|
{
|
||||||
|
var compactPathRow = new StackPanel
|
||||||
|
{
|
||||||
|
Orientation = Orientation.Horizontal,
|
||||||
|
Margin = new Thickness(26, 0, 12, 8),
|
||||||
|
ToolTip = evt.FilePath,
|
||||||
|
};
|
||||||
|
compactPathRow.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = "\uE8B7",
|
||||||
|
FontFamily = new FontFamily("Segoe MDL2 Assets"),
|
||||||
|
FontSize = 9,
|
||||||
|
Foreground = secondaryText,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
Margin = new Thickness(0, 0, 4, 0),
|
||||||
|
});
|
||||||
|
compactPathRow.Children.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Text = System.IO.Path.GetFileName(evt.FilePath),
|
||||||
|
FontSize = 10.5,
|
||||||
|
Foreground = secondaryText,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
TextTrimming = TextTrimming.CharacterEllipsis,
|
||||||
|
});
|
||||||
|
stack.Children.Add(compactPathRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
MessagePanel.Children.Add(stack);
|
||||||
|
}
|
||||||
|
|
||||||
private Border CreateAgentMetaChip(string text, string icon, Brush foreground, Brush background, Brush borderBrush)
|
private Border CreateAgentMetaChip(string text, string icon, Brush foreground, Brush background, Brush borderBrush)
|
||||||
{
|
{
|
||||||
return new Border
|
return new Border
|
||||||
@@ -422,59 +540,6 @@ public partial class ChatWindow
|
|||||||
{
|
{
|
||||||
var logLevel = _settings.Settings.Llm.AgentLogLevel;
|
var logLevel = _settings.Settings.Llm.AgentLogLevel;
|
||||||
|
|
||||||
if (evt.Type == AgentEventType.Planning && evt.Steps is { Count: > 0 })
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt.Type == AgentEventType.StepStart && evt.StepTotal > 0)
|
|
||||||
{
|
|
||||||
UpdateProgressBar(evt);
|
|
||||||
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 stepSummary = !string.IsNullOrWhiteSpace(evt.Summary)
|
|
||||||
? evt.Summary!
|
|
||||||
: $"단계 진행 중 ({evt.StepCurrent}/{evt.StepTotal})";
|
|
||||||
var pill = CreateCompactEventPill(stepSummary, compactPrimaryText, compactSecondaryText, compactHintBg, compactBorderBrush, compactAccentBrush);
|
|
||||||
pill.Opacity = 0;
|
|
||||||
pill.BeginAnimation(UIElement.OpacityProperty, new DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(160)));
|
|
||||||
MessagePanel.Children.Add(pill);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt.Type == AgentEventType.Thinking &&
|
|
||||||
ContainsAny(evt.Summary ?? "", "컨텍스트 압축", "microcompact", "session memory", "compact"))
|
|
||||||
{
|
|
||||||
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 pill = CreateCompactEventPill(string.IsNullOrWhiteSpace(evt.Summary) ? "컨텍스트 압축" : evt.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase)
|
if (!string.Equals(logLevel, "debug", StringComparison.OrdinalIgnoreCase)
|
||||||
&& evt.Type is AgentEventType.Paused or AgentEventType.Resumed)
|
&& evt.Type is AgentEventType.Paused or AgentEventType.Resumed)
|
||||||
return;
|
return;
|
||||||
@@ -524,6 +589,15 @@ public partial class ChatWindow
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (evt.Type == AgentEventType.StepStart && evt.StepTotal > 0)
|
||||||
|
UpdateProgressBar(evt);
|
||||||
|
|
||||||
|
if (IsProcessFeedEvent(evt))
|
||||||
|
{
|
||||||
|
AddProcessFeedMessage(evt, transcriptBadgeLabel, itemDisplayName, eventSummaryText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
|
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
|
||||||
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray;
|
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.DimGray;
|
||||||
var hintBg = TryFindResource("HintBackground") as Brush ?? BrushFromHex("#F8FAFC");
|
var hintBg = TryFindResource("HintBackground") as Brush ?? BrushFromHex("#F8FAFC");
|
||||||
|
|||||||
Reference in New Issue
Block a user