설정 fan-out 통합과 AX Agent 대기열 상태 분리 정리
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- AX Agent가 SettingsService 변경 이벤트를 직접 구독해 메인 설정과 AX Agent 설정 어느 경로에서 저장하더라도 테마, 모델, 권한, 데이터 활용, composer, 대기열 UI를 즉시 다시 읽어오도록 fan-out 경로를 통합함 - AX Agent 설정 저장 경로와 구형 Agent 설정창에서 표현 수준을 rich로 덮어쓰던 처리를 제거해 풍부하게/적절하게/간단하게 설정이 유지되도록 보정함 - DraftQueue 패널을 실행 중/다음 작업/보류/완료/실패 개별 섹션으로 재구성해 queue state를 더 빠르게 파악할 수 있게 정리함 - README, DEVELOPMENT, AGENT_ROADMAP 문서 이력을 2026-04-04 23:23 (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:
@@ -383,7 +383,6 @@ public partial class AgentSettingsWindow : Window
|
||||
_llm.PlanMode = _planMode;
|
||||
_llm.AgentDecisionLevel = _reasoningMode;
|
||||
_llm.FolderDataUsage = _folderDataUsage;
|
||||
_llm.AgentUiExpressionLevel = "rich";
|
||||
|
||||
_llm.EnableProactiveContextCompact = ChkEnableProactiveCompact.IsChecked == true;
|
||||
_llm.ContextCompactTriggerPercent = ParseInt(TxtContextCompactTriggerPercent.Text, 80, 10, 95);
|
||||
|
||||
@@ -94,6 +94,7 @@ public partial class ChatWindow : Window
|
||||
private string _gitBranchSearchText = "";
|
||||
private StackPanel? _selectedMessageActionBar;
|
||||
private Border? _selectedMessageBorder;
|
||||
private bool _isRefreshingFromSettings;
|
||||
private void ApplyQuickActionVisual(Button button, bool active, string activeBg, string activeFg)
|
||||
{
|
||||
if (button?.Content is not string text)
|
||||
@@ -134,6 +135,7 @@ public partial class ChatWindow : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
_settings = settings;
|
||||
_settings.SettingsChanged += Settings_SettingsChanged;
|
||||
_storage = new ChatStorageService();
|
||||
_llm = new LlmService(settings);
|
||||
_router = new ModelRouterService(settings);
|
||||
@@ -297,6 +299,7 @@ public partial class ChatWindow : Window
|
||||
};
|
||||
Closed += (_, _) =>
|
||||
{
|
||||
_settings.SettingsChanged -= Settings_SettingsChanged;
|
||||
SubAgentTool.StatusChanged -= OnSubAgentStatusChanged;
|
||||
_streamCts?.Cancel();
|
||||
_cursorTimer.Stop();
|
||||
@@ -306,6 +309,28 @@ public partial class ChatWindow : Window
|
||||
};
|
||||
}
|
||||
|
||||
private void Settings_SettingsChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (_forceClose || !IsLoaded || _isRefreshingFromSettings)
|
||||
return;
|
||||
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
if (_forceClose || !IsLoaded)
|
||||
return;
|
||||
|
||||
_isRefreshingFromSettings = true;
|
||||
try
|
||||
{
|
||||
RefreshFromSavedSettings();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isRefreshingFromSettings = false;
|
||||
}
|
||||
}), DispatcherPriority.Input);
|
||||
}
|
||||
|
||||
private bool IsPermissionAutoApprovedForSession(string toolName, string target)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(toolName) || string.IsNullOrWhiteSpace(target))
|
||||
@@ -13956,6 +13981,8 @@ public partial class ChatWindow : Window
|
||||
RefreshContextUsageVisual();
|
||||
UpdateTabUI();
|
||||
BuildBottomBar();
|
||||
RefreshDraftQueueUi();
|
||||
RefreshConversationList();
|
||||
}, DispatcherPriority.Input);
|
||||
}
|
||||
|
||||
@@ -13978,7 +14005,6 @@ public partial class ChatWindow : Window
|
||||
llm.Code.EnableCodeVerification = ChkOverlayEnableCodeVerification?.IsChecked == true;
|
||||
llm.EnableParallelTools = ChkOverlayEnableParallelTools?.IsChecked == true;
|
||||
llm.FolderDataUsage = _folderDataUsage;
|
||||
llm.AgentUiExpressionLevel = "rich";
|
||||
|
||||
CommitOverlayEndpointInput(normalizeOnInvalid: true);
|
||||
CommitOverlayApiKeyInput();
|
||||
@@ -17228,45 +17254,35 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
|
||||
}
|
||||
|
||||
DraftQueuePanel.Visibility = Visibility.Visible;
|
||||
const int maxVisible = 8;
|
||||
var summary = _appState.GetDraftQueueSummary(_activeTab);
|
||||
var activeItems = visibleItems
|
||||
.Where(item => !string.Equals(item.State, "completed", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(item.State, "failed", StringComparison.OrdinalIgnoreCase))
|
||||
.Take(maxVisible)
|
||||
.ToList();
|
||||
var historyItems = visibleItems
|
||||
.Where(item => string.Equals(item.State, "completed", StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(item.State, "failed", StringComparison.OrdinalIgnoreCase))
|
||||
.Take(Math.Max(0, maxVisible - activeItems.Count))
|
||||
.ToList();
|
||||
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueSummaryStrip(summary));
|
||||
const int maxPerSection = 3;
|
||||
var runningItems = visibleItems
|
||||
.Where(item => string.Equals(item.State, "running", StringComparison.OrdinalIgnoreCase))
|
||||
.Take(maxPerSection)
|
||||
.ToList();
|
||||
var queuedItems = visibleItems
|
||||
.Where(item => string.Equals(item.State, "queued", StringComparison.OrdinalIgnoreCase) && !IsDraftBlocked(item))
|
||||
.Take(maxPerSection)
|
||||
.ToList();
|
||||
var blockedItems = visibleItems
|
||||
.Where(IsDraftBlocked)
|
||||
.Take(maxPerSection)
|
||||
.ToList();
|
||||
var completedItems = visibleItems
|
||||
.Where(item => string.Equals(item.State, "completed", StringComparison.OrdinalIgnoreCase))
|
||||
.Take(maxPerSection)
|
||||
.ToList();
|
||||
var failedItems = visibleItems
|
||||
.Where(item => string.Equals(item.State, "failed", StringComparison.OrdinalIgnoreCase))
|
||||
.Take(maxPerSection)
|
||||
.ToList();
|
||||
|
||||
if (activeItems.Count > 0)
|
||||
{
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueSectionLabel($"실행 대기 · {activeItems.Count}"));
|
||||
foreach (var item in activeItems)
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueCard(item));
|
||||
}
|
||||
|
||||
if (historyItems.Count > 0)
|
||||
{
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueSectionLabel($"최근 결과 · {historyItems.Count}"));
|
||||
foreach (var item in historyItems)
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueCard(item));
|
||||
}
|
||||
|
||||
if (visibleItems.Count > activeItems.Count + historyItems.Count)
|
||||
{
|
||||
DraftQueuePanel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"추가 항목 {visibleItems.Count - (activeItems.Count + historyItems.Count)}개",
|
||||
Margin = new Thickness(8, 4, 0, 0),
|
||||
FontSize = 11,
|
||||
Foreground = TryFindResource("SecondaryText") as Brush ?? BrushFromHex("#7A7F87"),
|
||||
});
|
||||
}
|
||||
AddDraftQueueSection("실행 중", runningItems, summary.RunningCount);
|
||||
AddDraftQueueSection("다음 작업", queuedItems, summary.QueuedCount);
|
||||
AddDraftQueueSection("보류", blockedItems, summary.BlockedCount);
|
||||
AddDraftQueueSection("완료", completedItems, summary.CompletedCount);
|
||||
AddDraftQueueSection("실패", failedItems, summary.FailedCount);
|
||||
|
||||
if (summary.CompletedCount > 0 || summary.FailedCount > 0)
|
||||
{
|
||||
@@ -17286,6 +17302,27 @@ private static (string icon, string label, string bgHex, string fgHex) GetDecisi
|
||||
}
|
||||
}
|
||||
|
||||
private void AddDraftQueueSection(string label, IReadOnlyList<DraftQueueItem> items, int totalCount)
|
||||
{
|
||||
if (DraftQueuePanel == null || totalCount <= 0)
|
||||
return;
|
||||
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueSectionLabel($"{label} · {totalCount}"));
|
||||
foreach (var item in items)
|
||||
DraftQueuePanel.Children.Add(CreateDraftQueueCard(item));
|
||||
|
||||
if (totalCount > items.Count)
|
||||
{
|
||||
DraftQueuePanel.Children.Add(new TextBlock
|
||||
{
|
||||
Text = $"추가 항목 {totalCount - items.Count}개",
|
||||
Margin = new Thickness(8, -2, 0, 8),
|
||||
FontSize = 10.5,
|
||||
Foreground = TryFindResource("SecondaryText") as Brush ?? BrushFromHex("#7A7F87"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private UIElement CreateDraftQueueSummaryStrip(AppStateService.DraftQueueSummaryState summary)
|
||||
{
|
||||
var wrap = new WrapPanel
|
||||
|
||||
Reference in New Issue
Block a user