AX Agent 도구·스킬 정합성 재구성 및 실행 품질 보강

변경 목적:
- AX Agent의 도구 이름, 내부 설정, 스킬 정책, 실행 루프 사이의 불일치를 줄이고 전체 동작 품질을 높인다.
- claw-code 수준의 일관된 동작 품질을 참고하되 AX 구조에 맞는 고유한 카탈로그·정규화 레이어로 재구성한다.

핵심 수정사항:
- 도구 canonical id, legacy alias, 탭 노출, 설정 카테고리, read-only 분류를 중앙 카탈로그로 통합했다.
- ToolRegistry, AgentLoopService, 병렬 실행 분류, 권한 처리, 훅 처리, 스킬 allowed-tools 해석이 같은 이름 체계를 사용하도록 정리했다.
- Agent 설정/일반 설정/도움말의 도구 카드와 훅 편집기, 스킬 설명을 현재 런타임 구조에 맞게 갱신했다.
- 컨텍스트 압축, intent gate, spawn agents, session learning, model prompt adapter, workspace context 관련 변경과 테스트 추가를 함께 반영했다.
- 문서 이력과 비교/로드맵 문서를 최신 상태로 갱신했다.

검증 결과:
- dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_toolcat\ -p:IntermediateOutputPath=obj\verify_toolcat\ : 경고 0 / 오류 0
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter AgentToolCatalogTests -p:OutputPath=bin\verify_toolcat_tests\ -p:IntermediateOutputPath=obj\verify_toolcat_tests\ : 통과 8
This commit is contained in:
2026-04-14 17:52:46 +09:00
parent fa33b98f7e
commit 8cb08576d5
200 changed files with 13522 additions and 5764 deletions

View File

@@ -261,7 +261,7 @@ public partial class ChatWindow
if (_isInlineSettingsSyncing)
return;
_settings.Settings.Llm.Service = capturedService;
_settings.Save();
ScheduleSettingsSave();
UpdateModelLabel();
RefreshInlineSettingsPanel();
};
@@ -331,7 +331,7 @@ public partial class ChatWindow
if (_isInlineSettingsSyncing)
return;
_settings.Settings.Llm.Model = capturedId;
_settings.Save();
ScheduleSettingsSave();
UpdateModelLabel();
RefreshInlineSettingsPanel();
SetStatus($"모델 전환: {capturedLabel}", spinning: false);
@@ -360,8 +360,27 @@ public partial class ChatWindow
private void OpenAgentSettingsWindow()
{
RefreshOverlaySettingsPanel();
// Scale + fade-in animation
AgentSettingsOverlay.RenderTransform = new ScaleTransform(0.98, 0.98);
AgentSettingsOverlay.RenderTransformOrigin = new Point(0.5, 0.5);
AgentSettingsOverlay.Opacity = 0;
AgentSettingsOverlay.Visibility = Visibility.Visible;
var ease = new System.Windows.Media.Animation.CubicEase();
var scaleXAnim = new System.Windows.Media.Animation.DoubleAnimation(0.98, 1.0, TimeSpan.FromMilliseconds(200)) { EasingFunction = ease };
var scaleYAnim = new System.Windows.Media.Animation.DoubleAnimation(0.98, 1.0, TimeSpan.FromMilliseconds(200)) { EasingFunction = ease };
var fadeAnim = new System.Windows.Media.Animation.DoubleAnimation(0, 1, TimeSpan.FromMilliseconds(180));
((ScaleTransform)AgentSettingsOverlay.RenderTransform).BeginAnimation(ScaleTransform.ScaleXProperty, scaleXAnim);
((ScaleTransform)AgentSettingsOverlay.RenderTransform).BeginAnimation(ScaleTransform.ScaleYProperty, scaleYAnim);
AgentSettingsOverlay.BeginAnimation(UIElement.OpacityProperty, fadeAnim);
InlineSettingsPanel.IsOpen = false;
// 탭 RadioButton을 "공통"으로 확실히 리셋 — 재진입 시 탭/패널 불일치 방지
if (OverlayNavBasic != null)
OverlayNavBasic.IsChecked = true;
SetOverlaySection("basic");
Dispatcher.BeginInvoke(() =>
{
@@ -425,12 +444,14 @@ public partial class ChatWindow
llm.EnableHookPermissionUpdate = ChkOverlayEnableHookPermissionUpdate?.IsChecked == true;
llm.EnableCoworkVerification = ChkOverlayEnableCoworkVerification?.IsChecked == true;
llm.CoworkOnComplete = (CmbOverlayCoworkOnComplete?.SelectedItem as System.Windows.Controls.ComboBoxItem)?.Tag?.ToString() ?? "none";
llm.AutoPreview = (CmbOverlayAutoPreview?.SelectedItem as System.Windows.Controls.ComboBoxItem)?.Tag?.ToString() ?? "off";
llm.Code.EnableCodeVerification = ChkOverlayEnableCodeVerification?.IsChecked == true;
llm.Code.EnableCodeReview = ChkOverlayEnableCodeReview?.IsChecked == true;
llm.EnableImageInput = ChkOverlayEnableImageInput?.IsChecked == true;
llm.EnableParallelTools = ChkOverlayEnableParallelTools?.IsChecked == true;
llm.EnableProjectRules = ChkOverlayEnableProjectRules?.IsChecked == true;
llm.EnableAgentMemory = ChkOverlayEnableAgentMemory?.IsChecked == true;
llm.EnableIbmDiagnosticLog = ChkOverlayEnableIbmDiagnosticLog?.IsChecked == true;
llm.Code.EnableWorktreeTools = ChkOverlayEnableWorktreeTools?.IsChecked == true;
llm.Code.EnableTeamTools = ChkOverlayEnableTeamTools?.IsChecked == true;
llm.Code.EnableCronTools = ChkOverlayEnableCronTools?.IsChecked == true;
@@ -442,8 +463,7 @@ public partial class ChatWindow
_settings.Settings.Launcher.EnableChatIconRandomAnimation = ChkOverlayEnableChatIconRandomAnim?.IsChecked == true;
_settings.Settings.Launcher.ChatIconGlowIntensity =
(CmbOverlayChatIconGlow?.SelectedItem as System.Windows.Controls.ComboBoxItem)?.Tag?.ToString() ?? "medium";
llm.EnableNewPlanViewer = ChkOverlayEnableNewPlanViewer?.IsChecked == true;
llm.EnableNewChatRendering = ChkOverlayEnableNewChatRendering?.IsChecked == true;
// V2 뷰어/렌더링 전환 완료 — 항상 V2 사용
CommitOverlayEndpointInput(normalizeOnInvalid: true);
CommitOverlayApiKeyInput();
@@ -467,7 +487,16 @@ public partial class ChatWindow
PersistOverlaySettingsState(refreshOverlayDeferredInputs: false);
if (closeOverlay)
AgentSettingsOverlay.Visibility = Visibility.Collapsed;
{
var fadeOut = new System.Windows.Media.Animation.DoubleAnimation(1, 0, TimeSpan.FromMilliseconds(120));
fadeOut.Completed += (_, _) =>
{
AgentSettingsOverlay.Visibility = Visibility.Collapsed;
AgentSettingsOverlay.BeginAnimation(UIElement.OpacityProperty, null);
AgentSettingsOverlay.Opacity = 1;
};
AgentSettingsOverlay.BeginAnimation(UIElement.OpacityProperty, fadeOut);
}
if (showToast)
ShowToast("AX Agent 설정이 저장되었습니다.");
InputBox.Focus();
@@ -475,7 +504,7 @@ public partial class ChatWindow
private void PersistOverlaySettingsState(bool refreshOverlayDeferredInputs)
{
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
ApplyAgentThemeResources();
UpdatePermissionUI();
@@ -628,6 +657,7 @@ public partial class ChatWindow
if (ChkOverlayEnableCoworkVerification != null)
ChkOverlayEnableCoworkVerification.IsChecked = llm.EnableCoworkVerification;
SelectComboBoxByTag(CmbOverlayCoworkOnComplete, llm.CoworkOnComplete);
SelectComboBoxByTag(CmbOverlayAutoPreview, llm.AutoPreview);
if (ChkOverlayEnableCodeVerification != null)
ChkOverlayEnableCodeVerification.IsChecked = llm.Code.EnableCodeVerification;
if (ChkOverlayEnableCodeReview != null)
@@ -640,6 +670,8 @@ public partial class ChatWindow
ChkOverlayEnableProjectRules.IsChecked = llm.EnableProjectRules;
if (ChkOverlayEnableAgentMemory != null)
ChkOverlayEnableAgentMemory.IsChecked = llm.EnableAgentMemory;
if (ChkOverlayEnableIbmDiagnosticLog != null)
ChkOverlayEnableIbmDiagnosticLog.IsChecked = llm.EnableIbmDiagnosticLog;
if (ChkOverlayEnableWorktreeTools != null)
ChkOverlayEnableWorktreeTools.IsChecked = llm.Code.EnableWorktreeTools;
if (ChkOverlayEnableTeamTools != null)
@@ -664,10 +696,7 @@ public partial class ChatWindow
ChkOverlayEnableChatIconRandomAnim.IsChecked = _settings.Settings.Launcher.EnableChatIconRandomAnimation;
if (CmbOverlayChatIconGlow != null)
SelectComboBoxByTag(CmbOverlayChatIconGlow, _settings.Settings.Launcher.ChatIconGlowIntensity ?? "medium");
if (ChkOverlayEnableNewPlanViewer != null)
ChkOverlayEnableNewPlanViewer.IsChecked = llm.EnableNewPlanViewer;
if (ChkOverlayEnableNewChatRendering != null)
ChkOverlayEnableNewChatRendering.IsChecked = llm.EnableNewChatRendering;
// V2 뷰어/렌더링 전환 완료 — 토글 제거됨
}
RefreshOverlayThemeCards();
@@ -1583,6 +1612,8 @@ public partial class ChatWindow
OverlayToggleCoworkVerification.Visibility = showCowork ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleCoworkOnComplete != null)
OverlayToggleCoworkOnComplete.Visibility = showCowork ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleAutoPreview != null)
OverlayToggleAutoPreview.Visibility = showCowork ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleCodeVerification != null)
OverlayToggleCodeVerification.Visibility = showCode ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleCodeReview != null)
@@ -1593,6 +1624,8 @@ public partial class ChatWindow
OverlayToggleProjectRules.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleAgentMemory != null)
OverlayToggleAgentMemory.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleIbmDiagnostic != null)
OverlayToggleIbmDiagnostic.Visibility = showDev ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleWorktreeTools != null)
OverlayToggleWorktreeTools.Visibility = showCode ? Visibility.Visible : Visibility.Collapsed;
if (OverlayToggleTeamTools != null)
@@ -1605,8 +1638,7 @@ public partial class ChatWindow
OverlaySectionGlowEffects.Visibility = showBasic ? Visibility.Visible : Visibility.Collapsed;
if (OverlaySectionIconEffects != null)
OverlaySectionIconEffects.Visibility = showBasic ? Visibility.Visible : Visibility.Collapsed;
if (OverlaySectionPlanViewer != null)
OverlaySectionPlanViewer.Visibility = showBasic ? Visibility.Visible : Visibility.Collapsed;
// V2 전환 완료 — OverlaySectionPlanViewer 제거됨
if (showTools || showSkill || showBlock)
RefreshOverlayEtcPanels();
@@ -2814,7 +2846,7 @@ public partial class ChatWindow
=> "데이터 처리",
"clipboard_tool" or "notify_tool" or "env_tool" or "zip_tool" or "http_tool" or "open_external" or "image_analyze" or "file_watch"
=> "시스템/환경",
"spawn_agent" or "wait_agents" or "memory" or "skill_manager" or "user_ask" or "task_tracker" or "todo_write" or "task_create" or "task_get" or "task_list" or "task_update" or "task_stop" or "task_output" or "enter_plan_mode" or "exit_plan_mode" or "enter_worktree" or "exit_worktree" or "team_create" or "team_delete" or "cron_create" or "cron_delete" or "cron_list" or "suggest_actions" or "checkpoint" or "playbook"
"spawn_agent" or "spawn_agents" or "wait_agents" or "memory" or "skill_manager" or "user_ask" or "task_tracker" or "todo_write" or "task_create" or "task_get" or "task_list" or "task_update" or "task_stop" or "task_output" or "enter_plan_mode" or "exit_plan_mode" or "enter_worktree" or "exit_worktree" or "team_create" or "team_delete" or "cron_create" or "cron_delete" or "cron_list" or "suggest_actions" or "checkpoint" or "playbook"
=> "에이전트",
_ => "기타"
};
@@ -2859,7 +2891,7 @@ public partial class ChatWindow
SetOverlayCardSelection(OverlayThemeLightCard, selected == "light");
SetOverlayCardSelection(OverlayThemeDarkCard, selected == "dark");
var preset = (_settings.Settings.Llm.AgentThemePreset ?? "claude").ToLowerInvariant();
SetOverlayCardSelection(OverlayThemeStyleClawCard, preset is "claw" or "claude");
SetOverlayCardSelection(OverlayThemeStyleClaudeCard, preset == "claude");
SetOverlayCardSelection(OverlayThemeStyleCodexCard, preset == "codex");
SetOverlayCardSelection(OverlayThemeStyleNordCard, preset == "nord");
SetOverlayCardSelection(OverlayThemeStyleEmberCard, preset == "ember");
@@ -3312,7 +3344,7 @@ public partial class ChatWindow
PersistOverlaySettingsState(refreshOverlayDeferredInputs: false);
}
private void OverlayThemeStyleClawCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
private void OverlayThemeStyleClaudeCard_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
_settings.Settings.Llm.AgentThemePreset = "claude";
PersistOverlaySettingsState(refreshOverlayDeferredInputs: false);
@@ -3426,7 +3458,8 @@ public partial class ChatWindow
private void CmbOverlayAutoPreview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// 제거됨 (중복 설정 항목)
if (_isOverlaySettingsSyncing) return;
ApplyOverlaySettingsChanges(showToast: false, closeOverlay: false);
}
private void CmbOverlayOperationMode_SelectionChanged(object sender, SelectionChangedEventArgs e)
@@ -3563,7 +3596,7 @@ public partial class ChatWindow
if (candidates.Count > 0 && !candidates.Any(m => m.Id == llm.Model))
llm.Model = candidates[0].Id;
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
UpdateModelLabel();
RefreshInlineSettingsPanel();
@@ -3575,7 +3608,7 @@ public partial class ChatWindow
return;
_settings.Settings.Llm.Model = modelId;
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
UpdateModelLabel();
RefreshInlineSettingsPanel();
@@ -3584,7 +3617,7 @@ public partial class ChatWindow
private void BtnInlineFastMode_Click(object sender, RoutedEventArgs e)
{
_settings.Settings.Llm.FreeTierMode = !_settings.Settings.Llm.FreeTierMode;
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
RefreshInlineSettingsPanel();
RefreshOverlayVisualState(loadDeferredInputs: false);
@@ -3594,7 +3627,7 @@ public partial class ChatWindow
{
var llm = _settings.Settings.Llm;
llm.AgentDecisionLevel = NextReasoning(llm.AgentDecisionLevel);
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
RefreshInlineSettingsPanel();
RefreshOverlayVisualState(loadDeferredInputs: false);
@@ -3604,7 +3637,7 @@ public partial class ChatWindow
{
var llm = _settings.Settings.Llm;
llm.FilePermission = NextPermission(llm.FilePermission);
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
UpdatePermissionUI();
SaveConversationSettings();
@@ -3623,7 +3656,7 @@ public partial class ChatWindow
UpdateConditionalSkillActivation(reset: true);
}
_settings.Save();
ScheduleSettingsSave();
_appState.LoadFromSettings(_settings);
RefreshInlineSettingsPanel();