AX Agent 코워크·코드 흐름과 컨텍스트 관리를 claude-code 기준으로 대폭 정리

- 코워크·코드 프롬프트, 도구 선택, 문서 생성/검증 흐름을 claude-code 동등 품질 기준으로 재정렬함

- OpenAI/vLLM 경로의 오래된 tool history를 평탄화하고 최근 이력만 구조화해 컨텍스트 직렬화를 경량화함

- AX Agent UI를 테마 기준으로 재구성하고 플랜 승인/오버레이/이벤트 렌더링/명령 입력 상호작용을 개선함

- 파일 후보 제안, 반복 경로 정체 복구, LSP 보강, 문서·PPT 처리 개선, 설정/서비스 인터페이스 정리를 함께 반영함

- README.md 및 docs/DEVELOPMENT.md를 작업 시점별로 갱신함

- 검증: 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:
2026-04-12 22:02:14 +09:00
parent b8f4df1892
commit fb0bea41f7
137 changed files with 18532 additions and 1144 deletions

View File

@@ -51,7 +51,7 @@ public sealed class ChatSessionStateService
return CurrentConversation;
}
public void Load(SettingsService settings)
public void Load(ISettingsService settings)
{
var llm = settings.Settings.Llm;
ActiveTab = NormalizeTab(llm.LastActiveTab);
@@ -67,7 +67,7 @@ public sealed class ChatSessionStateService
}
}
public void Save(SettingsService settings)
public void Save(ISettingsService settings)
{
var llm = settings.Settings.Llm;
llm.LastActiveTab = NormalizeTab(ActiveTab);
@@ -87,7 +87,7 @@ public sealed class ChatSessionStateService
TabConversationIds[NormalizeTab(tab)] = string.IsNullOrWhiteSpace(conversationId) ? null : conversationId;
}
public ChatConversation LoadOrCreateConversation(string tab, ChatStorageService storage, SettingsService settings)
public ChatConversation LoadOrCreateConversation(string tab, IChatStorageService storage, ISettingsService settings)
{
var normalizedTab = NormalizeTab(tab);
var rememberedId = GetConversationId(normalizedTab);
@@ -153,13 +153,19 @@ public sealed class ChatSessionStateService
return CreateFreshConversation(normalizedTab, settings);
}
public ChatConversation CreateFreshConversation(string tab, SettingsService settings)
public ChatConversation CreateFreshConversation(string tab, ISettingsService settings)
{
var normalizedTab = NormalizeTab(tab);
var created = new ChatConversation { Tab = normalizedTab };
var workFolder = settings.Settings.Llm.WorkFolder;
if (!string.IsNullOrWhiteSpace(workFolder) && normalizedTab != "Chat")
created.WorkFolder = workFolder;
// Code/Cowork 탭: 매 대화마다 폴더를 새로 선택하도록 빈 상태로 시작
if (string.Equals(normalizedTab, "Code", StringComparison.OrdinalIgnoreCase)
|| string.Equals(normalizedTab, "Cowork", StringComparison.OrdinalIgnoreCase))
{
created.WorkFolder = "";
CurrentConversation = created;
return created;
}
CurrentConversation = created;
return created;
@@ -239,7 +245,7 @@ public sealed class ChatSessionStateService
return fork;
}
public void SaveCurrentConversation(ChatStorageService storage, string tab)
public void SaveCurrentConversation(IChatStorageService storage, string tab)
{
var conv = CurrentConversation;
if (conv == null) return;
@@ -261,7 +267,7 @@ public sealed class ChatSessionStateService
RememberConversation(tab, null);
}
public ChatConversation SetCurrentConversation(string tab, ChatConversation conversation, ChatStorageService? storage = null, bool remember = true)
public ChatConversation SetCurrentConversation(string tab, ChatConversation conversation, IChatStorageService? storage = null, bool remember = true)
{
var normalizedTab = NormalizeTab(tab);
conversation.Tab = normalizedTab;
@@ -275,7 +281,7 @@ public sealed class ChatSessionStateService
return conversation;
}
public ChatMessage AppendMessage(string tab, ChatMessage message, ChatStorageService? storage = null, bool useForTitle = false)
public ChatMessage AppendMessage(string tab, ChatMessage message, IChatStorageService? storage = null, bool useForTitle = false)
{
var conv = EnsureCurrentConversation(tab);
conv.Messages.Add(message);
@@ -294,7 +300,7 @@ public sealed class ChatSessionStateService
public ChatConversation UpdateConversationMetadata(
string tab,
Action<ChatConversation> apply,
ChatStorageService? storage = null,
IChatStorageService? storage = null,
bool ensureConversation = true)
{
var conv = ensureConversation ? EnsureCurrentConversation(tab) : (CurrentConversation ?? new ChatConversation { Tab = NormalizeTab(tab) });
@@ -311,7 +317,7 @@ public sealed class ChatSessionStateService
string? dataUsage,
string? outputFormat,
string? mood,
ChatStorageService? storage = null)
IChatStorageService? storage = null)
{
return UpdateConversationMetadata(tab, conv =>
{
@@ -322,7 +328,7 @@ public sealed class ChatSessionStateService
}, storage);
}
public bool RemoveLastAssistantMessage(string tab, ChatStorageService? storage = null)
public bool RemoveLastAssistantMessage(string tab, IChatStorageService? storage = null)
{
var conv = CurrentConversation;
if (conv == null || conv.Messages.Count == 0)
@@ -336,7 +342,7 @@ public sealed class ChatSessionStateService
return true;
}
public bool UpdateUserMessageAndTrim(string tab, int userMessageIndex, string newText, ChatStorageService? storage = null)
public bool UpdateUserMessageAndTrim(string tab, int userMessageIndex, string newText, IChatStorageService? storage = null)
{
var conv = CurrentConversation;
if (conv == null)
@@ -353,7 +359,7 @@ public sealed class ChatSessionStateService
return true;
}
public bool UpdateMessageFeedback(string tab, ChatMessage message, string? feedback, ChatStorageService? storage = null)
public bool UpdateMessageFeedback(string tab, ChatMessage message, string? feedback, IChatStorageService? storage = null)
{
var conv = CurrentConversation;
if (conv == null)
@@ -368,7 +374,7 @@ public sealed class ChatSessionStateService
return true;
}
public ChatConversation AppendExecutionEvent(string tab, Agent.AgentEvent evt, ChatStorageService? storage = null)
public ChatConversation AppendExecutionEvent(string tab, Agent.AgentEvent evt, IChatStorageService? storage = null)
{
var conv = EnsureCurrentConversation(tab);
conv.ExecutionEvents ??= new List<ChatExecutionEvent>();
@@ -417,7 +423,7 @@ public sealed class ChatSessionStateService
return conv;
}
public ChatConversation AppendAgentRun(string tab, Agent.AgentEvent evt, string status, string summary, ChatStorageService? storage = null)
public ChatConversation AppendAgentRun(string tab, Agent.AgentEvent evt, string status, string summary, IChatStorageService? storage = null)
{
var conv = EnsureCurrentConversation(tab);
conv.AgentRunHistory ??= new List<ChatAgentRunRecord>();
@@ -448,7 +454,7 @@ public sealed class ChatSessionStateService
return conv;
}
public DraftQueueItem? EnqueueDraft(string tab, string text, string priority = "next", ChatStorageService? storage = null, string kind = "message")
public DraftQueueItem? EnqueueDraft(string tab, string text, string priority = "next", IChatStorageService? storage = null, string kind = "message")
{
var trimmed = text?.Trim() ?? "";
if (string.IsNullOrWhiteSpace(trimmed))
@@ -480,16 +486,16 @@ public sealed class ChatSessionStateService
return (conv.DraftQueueItems ?? []).ToList();
}
public bool MarkDraftRunning(string tab, string draftId, ChatStorageService? storage = null)
public bool MarkDraftRunning(string tab, string draftId, IChatStorageService? storage = null)
=> UpdateDraftItem(tab, draftId, item => _draftQueue.MarkRunning(item), storage);
public bool MarkDraftCompleted(string tab, string draftId, ChatStorageService? storage = null)
public bool MarkDraftCompleted(string tab, string draftId, IChatStorageService? storage = null)
=> UpdateDraftItem(tab, draftId, item => _draftQueue.MarkCompleted(item), storage);
public bool MarkDraftFailed(string tab, string draftId, string? error, ChatStorageService? storage = null)
public bool MarkDraftFailed(string tab, string draftId, string? error, IChatStorageService? storage = null)
=> UpdateDraftItem(tab, draftId, item => _draftQueue.MarkFailed(item, error), storage);
public bool ScheduleDraftRetry(string tab, string draftId, string? error, int maxAutoRetries = 3, ChatStorageService? storage = null)
public bool ScheduleDraftRetry(string tab, string draftId, string? error, int maxAutoRetries = 3, IChatStorageService? storage = null)
{
return UpdateDraftItem(tab, draftId, item =>
{
@@ -503,10 +509,10 @@ public sealed class ChatSessionStateService
}, storage);
}
public bool ResetDraftToQueued(string tab, string draftId, ChatStorageService? storage = null)
public bool ResetDraftToQueued(string tab, string draftId, IChatStorageService? storage = null)
=> UpdateDraftItem(tab, draftId, item => _draftQueue.ResetToQueued(item), storage);
public bool RemoveDraft(string tab, string draftId, ChatStorageService? storage = null)
public bool RemoveDraft(string tab, string draftId, IChatStorageService? storage = null)
{
if (string.IsNullOrWhiteSpace(draftId))
return false;
@@ -520,7 +526,7 @@ public sealed class ChatSessionStateService
return true;
}
public bool ToggleExecutionHistory(string tab, ChatStorageService? storage = null)
public bool ToggleExecutionHistory(string tab, IChatStorageService? storage = null)
{
var conv = EnsureCurrentConversation(tab);
conv.ShowExecutionHistory = !conv.ShowExecutionHistory;
@@ -528,7 +534,7 @@ public sealed class ChatSessionStateService
return conv.ShowExecutionHistory;
}
public void SaveConversationListPreferences(string tab, bool failedOnly, bool runningOnly, bool sortByRecent, ChatStorageService? storage = null)
public void SaveConversationListPreferences(string tab, bool failedOnly, bool runningOnly, bool sortByRecent, IChatStorageService? storage = null)
{
var conv = EnsureCurrentConversation(tab);
conv.ConversationFailedOnlyFilter = failedOnly;
@@ -758,7 +764,7 @@ public sealed class ChatSessionStateService
return result;
}
private void TouchConversation(ChatStorageService? storage, string tab)
private void TouchConversation(IChatStorageService? storage, string tab)
{
var conv = EnsureCurrentConversation(tab);
conv.UpdatedAt = DateTime.Now;
@@ -768,7 +774,7 @@ public sealed class ChatSessionStateService
try { storage?.Save(conv); } catch { }
}
private bool UpdateDraftItem(string tab, string draftId, Func<DraftQueueItem, bool> update, ChatStorageService? storage)
private bool UpdateDraftItem(string tab, string draftId, Func<DraftQueueItem, bool> update, IChatStorageService? storage)
{
if (string.IsNullOrWhiteSpace(draftId))
return false;