Compare commits
3 Commits
5c142e1235
...
f53f35bbed
| Author | SHA1 | Date | |
|---|---|---|---|
| f53f35bbed | |||
| 9fafcd0192 | |||
| 35fbfc933d |
14
README.md
14
README.md
@@ -7,6 +7,16 @@ Windows 전용 시맨틱 런처 & 워크스페이스 매니저
|
|||||||
개발 참고: Claw Code 동등성 작업 추적 문서
|
개발 참고: Claw Code 동등성 작업 추적 문서
|
||||||
`docs/claw-code-parity-plan.md`
|
`docs/claw-code-parity-plan.md`
|
||||||
|
|
||||||
|
- 업데이트: 2026-04-05 16:02 (KST)
|
||||||
|
- `document_plan` 후속 실행 분기를 `claw-code` 기준으로 다시 보강했습니다. 이제 문서 플래너 출력에서 body 골격과 즉시 실행 지시를 깨진 문자열 비교에 의존하지 않고 안정적으로 추출해, `html_create / document_assemble / docx_create / markdown_create` 후속 호출 유도가 실제로 이어집니다.
|
||||||
|
- 코워크 문서형 작업은 설정이 `planMode=off`여도 내부적으로 `always` 플랜 경로를 타도록 보정했습니다. 그래서 문서/보고서/제안서 요청은 먼저 계획을 세우고, 그 계획을 바탕으로 실제 문서 생성 단계까지 이어가도록 정리했습니다.
|
||||||
|
- 코워크 시스템 프롬프트도 강화해 문서 작업은 계획만 제시하고 끝내지 말고 실제 산출물 파일 경로까지 만들어야 완료로 판단하도록 바꿨습니다.
|
||||||
|
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0
|
||||||
|
|
||||||
|
- 업데이트: 2026-04-05 15:42 (KST)
|
||||||
|
- AX Agent 엔진 공통화 1차로, Cowork/Code 실행 이벤트와 Agent run 기록을 탭별 현재 대화에 누적한 뒤 원래 활성 탭 대화를 복원하는 로직을 `ChatWindow`에서 `AxAgentExecutionEngine` helper로 옮겼습니다.
|
||||||
|
- 이제 실행 이벤트/최근 run 기록 반영 시 창 코드가 직접 교차 탭 복원 경로를 중복 처리하지 않고, 엔진의 공통 세션 mutation 경로를 사용합니다.
|
||||||
|
|
||||||
- 업데이트: 2026-04-05 15:34 (KST)
|
- 업데이트: 2026-04-05 15:34 (KST)
|
||||||
- AX Agent 개선 계획 기준을 이전 AX 비교본이 아니라 실제 `claw-code` 런타임 축으로 다시 고정했습니다. 현재 참조 spine은 `bootstrap/state.ts -> bridge/initReplBridge.ts -> bridge/sessionRunner.ts -> screens/REPL.tsx -> components/Messages.tsx -> components/StatusLine.tsx` 입니다.
|
- AX Agent 개선 계획 기준을 이전 AX 비교본이 아니라 실제 `claw-code` 런타임 축으로 다시 고정했습니다. 현재 참조 spine은 `bootstrap/state.ts -> bridge/initReplBridge.ts -> bridge/sessionRunner.ts -> screens/REPL.tsx -> components/Messages.tsx -> components/StatusLine.tsx` 입니다.
|
||||||
- 이에 맞춰 AX Agent 개선도 `상태 정규화 -> 실행 준비 공통화 -> AgentLoop 이벤트 정규화 -> 타임라인 렌더 일원화 -> 컴포저/상태바 단순화 -> 복구/재개 검증` 순서로 진행하도록 parity 문서를 갱신했습니다.
|
- 이에 맞춰 AX Agent 개선도 `상태 정규화 -> 실행 준비 공통화 -> AgentLoop 이벤트 정규화 -> 타임라인 렌더 일원화 -> 컴포저/상태바 단순화 -> 복구/재개 검증` 순서로 진행하도록 parity 문서를 갱신했습니다.
|
||||||
@@ -796,6 +806,10 @@ ow + toggle 시각 언어로 통일했습니다.
|
|||||||
- 이제 `도구` 탭에서는 훅과 도구/커넥터 목록을, `스킬` 탭에서는 스킬 폴더, 슬래시 설정, 드래그 앤 드롭, 로드된 스킬, 폴백 모델, MCP 서버를, `차단` 탭에서는 차단 경로/확장자만 관리합니다. 같이 [SettingsWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml.cs) 에서 메인 설정의 `AX Agent` 바로가기 탭을 좌측 사이드바 맨 아래로 재배치했습니다.
|
- 이제 `도구` 탭에서는 훅과 도구/커넥터 목록을, `스킬` 탭에서는 스킬 폴더, 슬래시 설정, 드래그 앤 드롭, 로드된 스킬, 폴백 모델, MCP 서버를, `차단` 탭에서는 차단 경로/확장자만 관리합니다. 같이 [SettingsWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml.cs) 에서 메인 설정의 `AX Agent` 바로가기 탭을 좌측 사이드바 맨 아래로 재배치했습니다.
|
||||||
- 런처 하단 바도 요소별로 제어할 수 있게 바꿨습니다. [AppSettings.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Models/AppSettings.cs), [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml) 에 `성능 / 포모도로 / 메모 / 날씨 / 일정 / 배터리` 하단 위젯 표시 토글을 추가해서 일반 설정에서 항목별로 바로 켜고 끌 수 있게 했습니다.
|
- 런처 하단 바도 요소별로 제어할 수 있게 바꿨습니다. [AppSettings.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Models/AppSettings.cs), [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml) 에 `성능 / 포모도로 / 메모 / 날씨 / 일정 / 배터리` 하단 위젯 표시 토글을 추가해서 일반 설정에서 항목별로 바로 켜고 끌 수 있게 했습니다.
|
||||||
- [LauncherWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/LauncherWindow.xaml), [LauncherWindow.Widgets.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/LauncherWindow.Widgets.cs) 에서는 `Ollama / API / MCP` 서버 상태 위젯을 런처 하단 기능에서 완전히 제거했고, 남은 위젯들만 설정값에 따라 실제 표시되도록 연결했습니다. 배터리 위젯도 노트북 상태와 사용자 토글을 함께 반영해 보이게 정리했습니다.
|
- [LauncherWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/LauncherWindow.xaml), [LauncherWindow.Widgets.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/LauncherWindow.Widgets.cs) 에서는 `Ollama / API / MCP` 서버 상태 위젯을 런처 하단 기능에서 완전히 제거했고, 남은 위젯들만 설정값에 따라 실제 표시되도록 연결했습니다. 배터리 위젯도 노트북 상태와 사용자 토글을 함께 반영해 보이게 정리했습니다.
|
||||||
|
- `claw-code` 기준으로 계획 UX도 다시 눌렀습니다. [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs) 에서 저장된 `PlanMode` 값과 무관하게 런타임 계획 모드를 `off`로 고정해, 코워크/코드에서 매번 계획 승인 팝업이 뜨지 않도록 바꿨습니다.
|
||||||
|
- [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml), [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 에서는 메인 설정과 AX Agent 내부 설정의 `계획 모드` 행을 숨겼고, [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs) 에서도 항상 `off`만 저장/반영되게 정리했습니다.
|
||||||
|
- 계획 확인 팝업은 [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs), [PlanViewerWindow.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/PlanViewerWindow.cs) 기준으로 AX Agent 창을 owner로 받아 리소스를 그대로 합치게 바꿨고, 채팅 본문에 별도 인라인 승인 버튼을 다시 꽂지 않도록 정리했습니다.
|
||||||
|
- 업데이트: 2026-04-05 16:20 (KST)
|
||||||
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0
|
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` 경고 0 / 오류 0
|
||||||
- 업데이트: 2026-04-05 15:16 (KST)
|
- 업데이트: 2026-04-05 15:16 (KST)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
# AX Copilot - 媛쒕컻 臾몄꽌
|
# AX Copilot - 媛쒕컻 臾몄꽌
|
||||||
|
|
||||||
|
- Document update: 2026-04-05 16:02 (KST) - Fixed the Cowork document execution handoff around `document_plan`. The loop no longer depends on broken localized marker strings to detect the scaffold/body block or the immediate-next-step hint; it now extracts body markers robustly and resolves the correct follow-up tool (`html_create`, `document_assemble`, `docx_create`, `markdown_create`) before re-prompting the model.
|
||||||
|
- Document update: 2026-04-05 16:02 (KST) - Added `ResolveEffectivePlanMode(...)` so Cowork document/content tasks automatically use the `always` plan path even when the persisted plan mode is `off`. This brings Cowork closer to the `claw-code` expectation of plan-first execution for document-heavy work.
|
||||||
|
- Document update: 2026-04-05 16:02 (KST) - Strengthened `BuildCoworkSystemPrompt()` so document/report/proposal/manual requests must produce an execution plan first and are not considered complete until a real output file path has been created or updated.
|
||||||
- Document update: 2026-04-05 15:34 (KST) - Rebased the AX Agent improvement plan on actual `claw-code` runtime files instead of prior AX snapshots. The active reference spine is `src/bootstrap/state.ts -> src/bridge/initReplBridge.ts -> src/bridge/sessionRunner.ts -> src/screens/REPL.tsx -> src/components/Messages.tsx -> src/components/StatusLine.tsx`.
|
- Document update: 2026-04-05 15:34 (KST) - Rebased the AX Agent improvement plan on actual `claw-code` runtime files instead of prior AX snapshots. The active reference spine is `src/bootstrap/state.ts -> src/bridge/initReplBridge.ts -> src/bridge/sessionRunner.ts -> src/screens/REPL.tsx -> src/components/Messages.tsx -> src/components/StatusLine.tsx`.
|
||||||
- Document update: 2026-04-05 15:34 (KST) - Locked the AX implementation order to the same quality sequence used by that spine: runtime state canonicalization, prepared execution unification, loop event normalization, timeline render parity, composer/status strip simplification, and recovery/resume validation.
|
- Document update: 2026-04-05 15:34 (KST) - Locked the AX implementation order to the same quality sequence used by that spine: runtime state canonicalization, prepared execution unification, loop event normalization, timeline render parity, composer/status strip simplification, and recovery/resume validation.
|
||||||
|
- Document update: 2026-04-05 15:42 (KST) - Moved the cross-tab conversation restoration path for execution events and agent-run history from `ChatWindow.xaml.cs` into `AxAgentExecutionEngine`. `AppendExecutionEvent` and `AppendAgentRun` now go through one engine-owned session mutation helper, which preserves the active tab conversation while updating the target tab timeline.
|
||||||
|
- Document update: 2026-04-05 15:42 (KST) - Verified the first runtime-state/common-engine step with `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\` and confirmed warning 0 / error 0.
|
||||||
- Document update: 2026-04-05 07:11 (KST) - Simplified the AX Agent footer for Cowork/Code by removing the duplicated `MoodIconPanel` chip group from those tabs and leaving workspace context only in the main folder path row. Also removed the outline border from the data-usage button so the footer option strip reads flatter and less pill-heavy.
|
- Document update: 2026-04-05 07:11 (KST) - Simplified the AX Agent footer for Cowork/Code by removing the duplicated `MoodIconPanel` chip group from those tabs and leaving workspace context only in the main folder path row. Also removed the outline border from the data-usage button so the footer option strip reads flatter and less pill-heavy.
|
||||||
- Document update: 2026-04-05 07:08 (KST) - Improved AX Agent responsiveness in three hot paths: added an ordered meta cache in `ChatStorageService` so repeated conversation-list refreshes stop re-sorting the full meta set every time, short-circuited `SaveConversationSettings()` when permission/data-usage/mood/output-format values are unchanged, and debounced the sidebar conversation search refresh to avoid re-filtering on every keystroke.
|
- Document update: 2026-04-05 07:08 (KST) - Improved AX Agent responsiveness in three hot paths: added an ordered meta cache in `ChatStorageService` so repeated conversation-list refreshes stop re-sorting the full meta set every time, short-circuited `SaveConversationSettings()` when permission/data-usage/mood/output-format values are unchanged, and debounced the sidebar conversation search refresh to avoid re-filtering on every keystroke.
|
||||||
- Document update: 2026-04-05 02:00 (KST) - Reworked the AX Agent in-chat gear overlay navigation itself to match the restored internal settings taxonomy: `basic / chat / cowork / code / dev / tools / skill-block`. The left nav labels now follow that scheme, and the overlay rows/toggles are regrouped per tab instead of the earlier `common / service / permission / advanced` split.
|
- Document update: 2026-04-05 02:00 (KST) - Reworked the AX Agent in-chat gear overlay navigation itself to match the restored internal settings taxonomy: `basic / chat / cowork / code / dev / tools / skill-block`. The left nav labels now follow that scheme, and the overlay rows/toggles are regrouped per tab instead of the earlier `common / service / permission / advanced` split.
|
||||||
@@ -4565,3 +4570,8 @@ ow + toggle ?쒓컖 ?몄뼱濡??ㅼ떆 ?뺣젹?덈떎.
|
|||||||
- 배터리 위젯은 사용자 토글과 실제 배터리 가용 상태를 함께 반영하도록 `UpdateWidgetVisibility()`에서 최종 가시성을 결정하게 바꿨고, 모든 위젯이 꺼져 있으면 하단 위젯 바 전체도 자동으로 숨깁니다.
|
- 배터리 위젯은 사용자 토글과 실제 배터리 가용 상태를 함께 반영하도록 `UpdateWidgetVisibility()`에서 최종 가시성을 결정하게 바꿨고, 모든 위젯이 꺼져 있으면 하단 위젯 바 전체도 자동으로 숨깁니다.
|
||||||
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` 경고 0 / 오류 0
|
- 검증: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\` 경고 0 / 오류 0
|
||||||
- 업데이트: 2026-04-05 15:16 (KST)
|
- 업데이트: 2026-04-05 15:16 (KST)
|
||||||
|
- `claw-code` 기준 계획 UX 정리도 반영했습니다. [AgentLoopService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/Agent/AgentLoopService.cs) 의 `ResolveEffectivePlanMode(...)` 는 이제 항상 `off`를 반환해, 저장된 예전 계획 모드 값이 남아 있어도 런타임에서는 자동 계획/자동 승인 팝업이 다시 살아나지 않게 했습니다.
|
||||||
|
- [SettingsWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/SettingsWindow.xaml) 과 [ChatWindow.xaml](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml) 에서 메인 설정과 AX Agent 내부 설정의 `계획 모드` UI를 `Collapsed`로 전환했고, [SettingsViewModel.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/ViewModels/SettingsViewModel.cs), [AppStateService.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Services/AppStateService.cs) 도 `off` 고정으로 바꿔 예전 persisted 값이 다시 UI나 상태바에 반영되지 않게 정리했습니다.
|
||||||
|
- [ChatWindow.xaml.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/ChatWindow.xaml.cs) 의 `CreatePlanDecisionCallback()`은 더 이상 채팅 본문에 인라인 승인 버튼을 추가하지 않고, [PlanViewerWindow.cs](/E:/AX%20Copilot%20-%20Codex/src/AxCopilot/Views/PlanViewerWindow.cs) 를 AX Agent 창 owner와 merged resources 기준으로 생성해 플랜 팝업이 AX Agent 테마와 같은 리소스 축을 따르도록 바꿨습니다.
|
||||||
|
- 검증 예정: `dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify\ -p:IntermediateOutputPath=obj\verify\`
|
||||||
|
- 업데이트: 2026-04-05 16:20 (KST)
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ public partial class AgentLoopService
|
|||||||
maxRetry = ComputeQualityAwareMaxRetry(maxRetry, recentTaskRetryQuality, taskPolicy.TaskType);
|
maxRetry = ComputeQualityAwareMaxRetry(maxRetry, recentTaskRetryQuality, taskPolicy.TaskType);
|
||||||
|
|
||||||
// 플랜 모드 설정
|
// 플랜 모드 설정
|
||||||
var planMode = llm.PlanMode ?? "off"; // off | always | auto
|
var planMode = ResolveEffectivePlanMode(llm.PlanMode, ActiveTab, taskPolicy.TaskType); // off | always | auto
|
||||||
|
|
||||||
var context = BuildContext();
|
var context = BuildContext();
|
||||||
InjectTaskTypeGuidance(messages, taskPolicy);
|
InjectTaskTypeGuidance(messages, taskPolicy);
|
||||||
@@ -2096,6 +2096,11 @@ public partial class AgentLoopService
|
|||||||
return string.Equals(activeTab, "Code", StringComparison.OrdinalIgnoreCase) ? "feature" : "general";
|
return string.Equals(activeTab, "Code", StringComparison.OrdinalIgnoreCase) ? "feature" : "general";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static string ResolveEffectivePlanMode(string? configuredPlanMode, string? activeTab, string? taskType)
|
||||||
|
{
|
||||||
|
return "off";
|
||||||
|
}
|
||||||
|
|
||||||
private static void InjectTaskTypeGuidance(List<ChatMessage> messages, TaskTypePolicy taskPolicy)
|
private static void InjectTaskTypeGuidance(List<ChatMessage> messages, TaskTypePolicy taskPolicy)
|
||||||
{
|
{
|
||||||
if (messages.Any(m => m.Role == "user" && m.Content.StartsWith("[System:TaskType]", StringComparison.OrdinalIgnoreCase)))
|
if (messages.Any(m => m.Role == "user" && m.Content.StartsWith("[System:TaskType]", StringComparison.OrdinalIgnoreCase)))
|
||||||
|
|||||||
@@ -1327,31 +1327,82 @@ public partial class AgentLoopService
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
documentPlanCalled = true;
|
documentPlanCalled = true;
|
||||||
var po = result.Output;
|
var po = result.Output ?? string.Empty;
|
||||||
var pm = System.Text.RegularExpressions.Regex.Match(po, @"path:\s*""([^""]+)""");
|
var pm = System.Text.RegularExpressions.Regex.Match(po, @"path:\s*""([^""]+)""");
|
||||||
if (pm.Success) documentPlanPath = pm.Groups[1].Value;
|
if (pm.Success) documentPlanPath = pm.Groups[1].Value;
|
||||||
var tm = System.Text.RegularExpressions.Regex.Match(po, @"title:\s*""([^""]+)""");
|
var tm = System.Text.RegularExpressions.Regex.Match(po, @"title:\s*""([^""]+)""");
|
||||||
if (tm.Success) documentPlanTitle = tm.Groups[1].Value;
|
if (tm.Success) documentPlanTitle = tm.Groups[1].Value;
|
||||||
var bs = po.IndexOf("--- body ?쒖옉 ---", StringComparison.Ordinal);
|
documentPlanScaffold = ExtractDocumentPlanScaffold(po);
|
||||||
var be = po.IndexOf("--- body ??---", StringComparison.Ordinal);
|
|
||||||
if (bs >= 0 && be > bs)
|
|
||||||
documentPlanScaffold = po[(bs + "--- body ?쒖옉 ---".Length)..be].Trim();
|
|
||||||
|
|
||||||
if (!result.Output.Contains("利됱떆 ?ㅽ뻾:", StringComparison.Ordinal))
|
if (!ContainsDocumentPlanFollowUpInstruction(po))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var toolHint = result.Output.Contains("html_create", StringComparison.OrdinalIgnoreCase) ? "html_create" :
|
var toolHint = ResolveDocumentPlanFollowUpTool(po);
|
||||||
result.Output.Contains("document_assemble", StringComparison.OrdinalIgnoreCase) ? "document_assemble" :
|
|
||||||
result.Output.Contains("file_write", StringComparison.OrdinalIgnoreCase) ? "file_write" : "html_create";
|
|
||||||
messages.Add(new ChatMessage
|
messages.Add(new ChatMessage
|
||||||
{
|
{
|
||||||
Role = "user",
|
Role = "user",
|
||||||
Content = $"document_plan???꾨즺?섏뿀?듬땲?? " +
|
Content =
|
||||||
$"??寃곌낵??body/sections??[?댁슜...] 遺遺꾩쓣 ?ㅼ젣 ?곸꽭 ?댁슜?쇰줈 紐⑤몢 梨꾩썙??" +
|
"document_plan이 완료되었습니다. " +
|
||||||
$"{toolHint} ?꾧뎄瑜?吏湲?利됱떆 ?몄텧?섏꽭?? " +
|
"방금 생성된 골격의 [내용...] 자리와 각 섹션 내용을 실제 상세 본문으로 모두 채운 뒤 " +
|
||||||
$"媛??뱀뀡留덈떎 諛섎뱶??異⑸텇???댁슜???묒꽦?섍퀬, ?ㅻ챸 ?놁씠 ?꾧뎄瑜?諛붾줈 ?몄텧?섏꽭??"
|
$"{toolHint} 도구를 지금 즉시 호출하세요. " +
|
||||||
|
"설명만 하지 말고 실제 문서 생성 도구 호출로 바로 이어가세요."
|
||||||
});
|
});
|
||||||
EmitEvent(AgentEventType.Thinking, "", $"臾몄꽌 媛쒖슂 ?꾩꽦 ??{toolHint} ?몄텧 以?..");
|
EmitEvent(AgentEventType.Thinking, "", $"문서 개요 완료 · {toolHint} 실행 유도");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? ExtractDocumentPlanScaffold(string output)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(output))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var markers = new (string Start, string End)[]
|
||||||
|
{
|
||||||
|
("--- body 시작 ---", "--- body 끝 ---"),
|
||||||
|
("--- body start ---", "--- body end ---"),
|
||||||
|
("<!-- body start marker -->", "<!-- body end marker -->"),
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var (startMarker, endMarker) in markers)
|
||||||
|
{
|
||||||
|
var start = output.IndexOf(startMarker, StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (start < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var contentStart = start + startMarker.Length;
|
||||||
|
var end = output.IndexOf(endMarker, contentStart, StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (end <= contentStart)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var scaffold = output[contentStart..end].Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(scaffold))
|
||||||
|
return scaffold;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsDocumentPlanFollowUpInstruction(string output)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(output))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return output.Contains("즉시 실행", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| output.Contains("immediate next step", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| output.Contains("call html_create", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| output.Contains("call document_assemble", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ResolveDocumentPlanFollowUpTool(string output)
|
||||||
|
{
|
||||||
|
if (output.Contains("document_assemble", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return "document_assemble";
|
||||||
|
if (output.Contains("docx_create", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return "docx_create";
|
||||||
|
if (output.Contains("markdown_create", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return "markdown_create";
|
||||||
|
if (output.Contains("file_write", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return "file_write";
|
||||||
|
return "html_create";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyCodeQualityFollowUpTransition(
|
private void ApplyCodeQualityFollowUpTransition(
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ public sealed class AxAgentExecutionEngine
|
|||||||
ExecutionMode Mode,
|
ExecutionMode Mode,
|
||||||
IReadOnlyList<string> PromptStack,
|
IReadOnlyList<string> PromptStack,
|
||||||
List<ChatMessage> Messages);
|
List<ChatMessage> Messages);
|
||||||
|
public sealed record SessionMutationResult(
|
||||||
|
ChatConversation CurrentConversation,
|
||||||
|
ChatConversation UpdatedConversation);
|
||||||
|
|
||||||
public IReadOnlyList<string> BuildPromptStack(
|
public IReadOnlyList<string> BuildPromptStack(
|
||||||
string? conversationSystem,
|
string? conversationSystem,
|
||||||
@@ -159,6 +162,42 @@ public sealed class AxAgentExecutionEngine
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SessionMutationResult AppendExecutionEvent(
|
||||||
|
ChatSessionStateService session,
|
||||||
|
ChatStorageService storage,
|
||||||
|
ChatConversation? activeConversation,
|
||||||
|
string activeTab,
|
||||||
|
string targetTab,
|
||||||
|
AgentEvent evt)
|
||||||
|
{
|
||||||
|
return ApplyConversationMutation(
|
||||||
|
session,
|
||||||
|
storage,
|
||||||
|
activeConversation,
|
||||||
|
activeTab,
|
||||||
|
targetTab,
|
||||||
|
normalizedTarget => session.AppendExecutionEvent(normalizedTarget, evt, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public SessionMutationResult AppendAgentRun(
|
||||||
|
ChatSessionStateService session,
|
||||||
|
ChatStorageService storage,
|
||||||
|
ChatConversation? activeConversation,
|
||||||
|
string activeTab,
|
||||||
|
string targetTab,
|
||||||
|
AgentEvent evt,
|
||||||
|
string status,
|
||||||
|
string summary)
|
||||||
|
{
|
||||||
|
return ApplyConversationMutation(
|
||||||
|
session,
|
||||||
|
storage,
|
||||||
|
activeConversation,
|
||||||
|
activeTab,
|
||||||
|
targetTab,
|
||||||
|
normalizedTarget => session.AppendAgentRun(normalizedTarget, evt, status, summary, null));
|
||||||
|
}
|
||||||
|
|
||||||
public string NormalizeAssistantContent(
|
public string NormalizeAssistantContent(
|
||||||
ChatConversation conversation,
|
ChatConversation conversation,
|
||||||
string runTab,
|
string runTab,
|
||||||
@@ -208,4 +247,86 @@ public sealed class AxAgentExecutionEngine
|
|||||||
FileName = source.FileName,
|
FileName = source.FileName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SessionMutationResult ApplyConversationMutation(
|
||||||
|
ChatSessionStateService session,
|
||||||
|
ChatStorageService storage,
|
||||||
|
ChatConversation? activeConversation,
|
||||||
|
string activeTab,
|
||||||
|
string targetTab,
|
||||||
|
Func<string, ChatConversation> mutate)
|
||||||
|
{
|
||||||
|
var normalizedTarget = NormalizeTabName(targetTab);
|
||||||
|
var normalizedActive = NormalizeTabName(activeTab);
|
||||||
|
ChatConversation updatedConversation;
|
||||||
|
|
||||||
|
if (string.Equals(normalizedTarget, normalizedActive, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
session.CurrentConversation = updatedConversation = mutate(normalizedTarget);
|
||||||
|
return new SessionMutationResult(updatedConversation, updatedConversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeSnapshot = activeConversation;
|
||||||
|
var previousSessionConversation = session.CurrentConversation;
|
||||||
|
updatedConversation = mutate(normalizedTarget);
|
||||||
|
|
||||||
|
if (activeSnapshot != null
|
||||||
|
&& string.Equals(NormalizeTabName(activeSnapshot.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
session.CurrentConversation = activeSnapshot;
|
||||||
|
return new SessionMutationResult(activeSnapshot, updatedConversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (previousSessionConversation != null
|
||||||
|
&& string.Equals(NormalizeTabName(previousSessionConversation.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
session.CurrentConversation = previousSessionConversation;
|
||||||
|
return new SessionMutationResult(previousSessionConversation, updatedConversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var activeId = session.GetConversationId(normalizedActive);
|
||||||
|
var restoredConversation = string.IsNullOrWhiteSpace(activeId)
|
||||||
|
? null
|
||||||
|
: storage.Load(activeId);
|
||||||
|
|
||||||
|
if (restoredConversation != null)
|
||||||
|
{
|
||||||
|
session.CurrentConversation = restoredConversation;
|
||||||
|
return new SessionMutationResult(restoredConversation, updatedConversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fallbackConversation = session.LoadOrCreateConversation(normalizedActive, storage, GetFallbackSettings());
|
||||||
|
session.CurrentConversation = fallbackConversation;
|
||||||
|
return new SessionMutationResult(fallbackConversation, updatedConversation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SettingsService GetFallbackSettings()
|
||||||
|
{
|
||||||
|
return (System.Windows.Application.Current as App)?.SettingsService
|
||||||
|
?? new SettingsService();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeTabName(string? tab)
|
||||||
|
{
|
||||||
|
var normalized = (tab ?? "").Trim();
|
||||||
|
if (string.IsNullOrEmpty(normalized))
|
||||||
|
return "Chat";
|
||||||
|
|
||||||
|
if (normalized.Contains("코워크", StringComparison.OrdinalIgnoreCase))
|
||||||
|
return "Cowork";
|
||||||
|
|
||||||
|
var canonical = new string(normalized
|
||||||
|
.Where(char.IsLetterOrDigit)
|
||||||
|
.ToArray())
|
||||||
|
.ToLowerInvariant();
|
||||||
|
|
||||||
|
if (canonical is "cowork" or "coworkcode" or "coworkcodetab")
|
||||||
|
return "Cowork";
|
||||||
|
|
||||||
|
if (normalized.Contains("코드", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| canonical is "code" or "codetab")
|
||||||
|
return "Code";
|
||||||
|
|
||||||
|
return "Chat";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,9 @@ public class DocumentPlannerTool : IAgentTool
|
|||||||
output.AppendLine("(주석의 '활용 가능 요소'는 참고용이며, 내용에 맞게 다른 요소를 써도 됩니다)");
|
output.AppendLine("(주석의 '활용 가능 요소'는 참고용이며, 내용에 맞게 다른 요소를 써도 됩니다)");
|
||||||
output.AppendLine();
|
output.AppendLine();
|
||||||
output.AppendLine("--- body 시작 ---");
|
output.AppendLine("--- body 시작 ---");
|
||||||
|
output.AppendLine("<!-- body start marker -->");
|
||||||
output.Append(bodySb);
|
output.Append(bodySb);
|
||||||
|
output.AppendLine("<!-- body end marker -->");
|
||||||
output.AppendLine("--- body 끝 ---");
|
output.AppendLine("--- body 끝 ---");
|
||||||
output.AppendLine();
|
output.AppendLine();
|
||||||
output.AppendLine("⚠ html_create를 지금 즉시 호출하세요. 모든 섹션에 충분한 실제 내용을 작성하세요.");
|
output.AppendLine("⚠ html_create를 지금 즉시 호출하세요. 모든 섹션에 충분한 실제 내용을 작성하세요.");
|
||||||
|
|||||||
@@ -196,7 +196,7 @@ public sealed class AppStateService
|
|||||||
|
|
||||||
Permissions.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission);
|
Permissions.FilePermission = PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission);
|
||||||
Permissions.AgentDecisionLevel = llm.AgentDecisionLevel ?? "detailed";
|
Permissions.AgentDecisionLevel = llm.AgentDecisionLevel ?? "detailed";
|
||||||
Permissions.PlanMode = llm.PlanMode ?? "off";
|
Permissions.PlanMode = "off";
|
||||||
Permissions.ToolOverrideCount = llm.ToolPermissions?.Count ?? 0;
|
Permissions.ToolOverrideCount = llm.ToolPermissions?.Count ?? 0;
|
||||||
Permissions.ToolOverrides = llm.ToolPermissions?
|
Permissions.ToolOverrides = llm.ToolPermissions?
|
||||||
.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase)
|
.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase)
|
||||||
|
|||||||
@@ -370,11 +370,13 @@ public class SettingsViewModel : INotifyPropertyChanged
|
|||||||
set { _agentDecisionLevel = value; OnPropertyChanged(); }
|
set { _agentDecisionLevel = value; OnPropertyChanged(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
private string _planMode = "off";
|
|
||||||
public string PlanMode
|
public string PlanMode
|
||||||
{
|
{
|
||||||
get => _planMode;
|
get => "off";
|
||||||
set { _planMode = value; OnPropertyChanged(); }
|
set
|
||||||
|
{
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _enableMultiPassDocument;
|
private bool _enableMultiPassDocument;
|
||||||
@@ -1138,7 +1140,6 @@ public class SettingsViewModel : INotifyPropertyChanged
|
|||||||
_planDiffSeverityMediumRatioPercent = llm.PlanDiffSeverityMediumRatioPercent > 0 ? Math.Clamp(llm.PlanDiffSeverityMediumRatioPercent, 1, 100) : 25;
|
_planDiffSeverityMediumRatioPercent = llm.PlanDiffSeverityMediumRatioPercent > 0 ? Math.Clamp(llm.PlanDiffSeverityMediumRatioPercent, 1, 100) : 25;
|
||||||
_planDiffSeverityHighRatioPercent = llm.PlanDiffSeverityHighRatioPercent > 0 ? Math.Clamp(llm.PlanDiffSeverityHighRatioPercent, 1, 100) : 60;
|
_planDiffSeverityHighRatioPercent = llm.PlanDiffSeverityHighRatioPercent > 0 ? Math.Clamp(llm.PlanDiffSeverityHighRatioPercent, 1, 100) : 60;
|
||||||
_agentDecisionLevel = llm.AgentDecisionLevel;
|
_agentDecisionLevel = llm.AgentDecisionLevel;
|
||||||
_planMode = string.IsNullOrEmpty(llm.PlanMode) ? "off" : llm.PlanMode;
|
|
||||||
_enableMultiPassDocument = llm.EnableMultiPassDocument;
|
_enableMultiPassDocument = llm.EnableMultiPassDocument;
|
||||||
_enableCoworkVerification = llm.EnableCoworkVerification;
|
_enableCoworkVerification = llm.EnableCoworkVerification;
|
||||||
_enableFilePathHighlight = llm.EnableFilePathHighlight;
|
_enableFilePathHighlight = llm.EnableFilePathHighlight;
|
||||||
@@ -1580,7 +1581,7 @@ public class SettingsViewModel : INotifyPropertyChanged
|
|||||||
s.Llm.PlanDiffSeverityMediumRatioPercent = _planDiffSeverityMediumRatioPercent;
|
s.Llm.PlanDiffSeverityMediumRatioPercent = _planDiffSeverityMediumRatioPercent;
|
||||||
s.Llm.PlanDiffSeverityHighRatioPercent = _planDiffSeverityHighRatioPercent;
|
s.Llm.PlanDiffSeverityHighRatioPercent = _planDiffSeverityHighRatioPercent;
|
||||||
s.Llm.AgentDecisionLevel = _agentDecisionLevel;
|
s.Llm.AgentDecisionLevel = _agentDecisionLevel;
|
||||||
s.Llm.PlanMode = _planMode;
|
s.Llm.PlanMode = "off";
|
||||||
s.Llm.EnableMultiPassDocument = _enableMultiPassDocument;
|
s.Llm.EnableMultiPassDocument = _enableMultiPassDocument;
|
||||||
s.Llm.EnableCoworkVerification = _enableCoworkVerification;
|
s.Llm.EnableCoworkVerification = _enableCoworkVerification;
|
||||||
s.Llm.EnableFilePathHighlight = _enableFilePathHighlight;
|
s.Llm.EnableFilePathHighlight = _enableFilePathHighlight;
|
||||||
|
|||||||
@@ -2880,7 +2880,7 @@
|
|||||||
<ComboBoxItem Content="계획 · 항상 계획" Tag="always"/>
|
<ComboBoxItem Content="계획 · 항상 계획" Tag="always"/>
|
||||||
</ComboBox>
|
</ComboBox>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid Visibility="Collapsed">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="Auto"/>
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
|||||||
@@ -8735,7 +8735,9 @@ public partial class ChatWindow : Window
|
|||||||
sb.AppendLine("You are AX Copilot Agent. You can read, write, and edit files using the provided tools.");
|
sb.AppendLine("You are AX Copilot Agent. You can read, write, and edit files using the provided tools.");
|
||||||
sb.AppendLine($"Today's date: {DateTime.Now:yyyy년 M월 d일} ({DateTime.Now:yyyy-MM-dd}, {DateTime.Now:dddd}).");
|
sb.AppendLine($"Today's date: {DateTime.Now:yyyy년 M월 d일} ({DateTime.Now:yyyy-MM-dd}, {DateTime.Now:dddd}).");
|
||||||
sb.AppendLine("Available skills: excel_create (.xlsx), docx_create (.docx), csv_create (.csv), markdown_create (.md), html_create (.html), script_create (.bat/.ps1), document_review (품질 검증), format_convert (포맷 변환).");
|
sb.AppendLine("Available skills: excel_create (.xlsx), docx_create (.docx), csv_create (.csv), markdown_create (.md), html_create (.html), script_create (.bat/.ps1), document_review (품질 검증), format_convert (포맷 변환).");
|
||||||
sb.AppendLine("Always explain your plan step by step BEFORE executing tools. After creating files, summarize what was created.");
|
sb.AppendLine("Only present a step-by-step execution plan when the user explicitly asks for a plan or when the session is already in plan mode.");
|
||||||
|
sb.AppendLine("For ordinary Cowork requests, proceed directly with the work instead of stopping for plan approval.");
|
||||||
|
sb.AppendLine("After creating files, summarize what was created and include the actual output path.");
|
||||||
sb.AppendLine("Do not stop after a single step. Continue autonomously until the request is completed or a concrete blocker (permission denial, missing dependency, hard error) is encountered.");
|
sb.AppendLine("Do not stop after a single step. Continue autonomously until the request is completed or a concrete blocker (permission denial, missing dependency, hard error) is encountered.");
|
||||||
sb.AppendLine("When adapting external references, rewrite names/structure/comments to AX Copilot style. Avoid clone-like outputs.");
|
sb.AppendLine("When adapting external references, rewrite names/structure/comments to AX Copilot style. Avoid clone-like outputs.");
|
||||||
sb.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above. Never use placeholder or fictional dates.");
|
sb.AppendLine("IMPORTANT: When creating documents with dates, always use today's actual date above. Never use placeholder or fictional dates.");
|
||||||
@@ -8746,6 +8748,7 @@ public partial class ChatWindow : Window
|
|||||||
sb.AppendLine(" 3. Then immediately call html_create (or docx_create/file_write) using the scaffold from document_plan.");
|
sb.AppendLine(" 3. Then immediately call html_create (or docx_create/file_write) using the scaffold from document_plan.");
|
||||||
sb.AppendLine(" 4. Write actual detailed content for EVERY section — no skipping, no placeholders, no minimal content.");
|
sb.AppendLine(" 4. Write actual detailed content for EVERY section — no skipping, no placeholders, no minimal content.");
|
||||||
sb.AppendLine(" 5. Do NOT call html_create directly without document_plan for multi-section documents.");
|
sb.AppendLine(" 5. Do NOT call html_create directly without document_plan for multi-section documents.");
|
||||||
|
sb.AppendLine(" 6. Do not finish with a plan only. The task is complete only after the document file has actually been created or updated.");
|
||||||
|
|
||||||
// 문서 품질 검증 루프
|
// 문서 품질 검증 루프
|
||||||
sb.AppendLine("\n## Document Quality Review");
|
sb.AppendLine("\n## Document Quality Review");
|
||||||
@@ -9166,48 +9169,17 @@ public partial class ChatWindow : Window
|
|||||||
if (session == null)
|
if (session == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var normalizedTarget = NormalizeTabName(targetTab);
|
var result = _chatEngine.AppendAgentRun(
|
||||||
var normalizedActive = NormalizeTabName(_activeTab);
|
session,
|
||||||
ChatConversation updatedConversation;
|
_storage,
|
||||||
if (string.Equals(normalizedTarget, normalizedActive, StringComparison.OrdinalIgnoreCase))
|
_currentConversation,
|
||||||
{
|
_activeTab,
|
||||||
_currentConversation = updatedConversation = session.AppendAgentRun(normalizedTarget, evt, status, summary, null);
|
targetTab,
|
||||||
ScheduleConversationPersist(updatedConversation);
|
evt,
|
||||||
return;
|
status,
|
||||||
}
|
summary);
|
||||||
|
_currentConversation = result.CurrentConversation;
|
||||||
var activeSnapshot = _currentConversation;
|
ScheduleConversationPersist(result.UpdatedConversation);
|
||||||
var previousSessionConversation = session.CurrentConversation;
|
|
||||||
updatedConversation = session.AppendAgentRun(normalizedTarget, evt, status, summary, null);
|
|
||||||
ScheduleConversationPersist(updatedConversation);
|
|
||||||
|
|
||||||
if (activeSnapshot != null && string.Equals(NormalizeTabName(activeSnapshot.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
session.CurrentConversation = activeSnapshot;
|
|
||||||
_currentConversation = activeSnapshot;
|
|
||||||
}
|
|
||||||
else if (previousSessionConversation != null
|
|
||||||
&& string.Equals(NormalizeTabName(previousSessionConversation.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
session.CurrentConversation = previousSessionConversation;
|
|
||||||
_currentConversation = previousSessionConversation;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var activeId = session.GetConversationId(normalizedActive);
|
|
||||||
var activeConv = string.IsNullOrWhiteSpace(activeId) ? null : _storage.Load(activeId);
|
|
||||||
if (activeConv != null)
|
|
||||||
{
|
|
||||||
session.CurrentConversation = activeConv;
|
|
||||||
_currentConversation = activeConv;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fallback = session.LoadOrCreateConversation(normalizedActive, _storage, _settings);
|
|
||||||
session.CurrentConversation = fallback;
|
|
||||||
_currentConversation = fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9219,48 +9191,15 @@ public partial class ChatWindow : Window
|
|||||||
if (session == null)
|
if (session == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var normalizedTarget = NormalizeTabName(targetTab);
|
var result = _chatEngine.AppendExecutionEvent(
|
||||||
var normalizedActive = NormalizeTabName(_activeTab);
|
session,
|
||||||
ChatConversation updatedConversation;
|
_storage,
|
||||||
if (string.Equals(normalizedTarget, normalizedActive, StringComparison.OrdinalIgnoreCase))
|
_currentConversation,
|
||||||
{
|
_activeTab,
|
||||||
_currentConversation = updatedConversation = session.AppendExecutionEvent(normalizedTarget, evt, null);
|
targetTab,
|
||||||
ScheduleConversationPersist(updatedConversation);
|
evt);
|
||||||
return;
|
_currentConversation = result.CurrentConversation;
|
||||||
}
|
ScheduleConversationPersist(result.UpdatedConversation);
|
||||||
|
|
||||||
var activeSnapshot = _currentConversation;
|
|
||||||
var previousSessionConversation = session.CurrentConversation;
|
|
||||||
updatedConversation = session.AppendExecutionEvent(normalizedTarget, evt, null);
|
|
||||||
ScheduleConversationPersist(updatedConversation);
|
|
||||||
|
|
||||||
if (activeSnapshot != null && string.Equals(NormalizeTabName(activeSnapshot.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
session.CurrentConversation = activeSnapshot;
|
|
||||||
_currentConversation = activeSnapshot;
|
|
||||||
}
|
|
||||||
else if (previousSessionConversation != null
|
|
||||||
&& string.Equals(NormalizeTabName(previousSessionConversation.Tab), normalizedActive, StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
session.CurrentConversation = previousSessionConversation;
|
|
||||||
_currentConversation = previousSessionConversation;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var activeId = session.GetConversationId(normalizedActive);
|
|
||||||
var activeConv = string.IsNullOrWhiteSpace(activeId) ? null : _storage.Load(activeId);
|
|
||||||
if (activeConv != null)
|
|
||||||
{
|
|
||||||
session.CurrentConversation = activeConv;
|
|
||||||
_currentConversation = activeConv;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var fallback = session.LoadOrCreateConversation(normalizedActive, _storage, _settings);
|
|
||||||
session.CurrentConversation = fallback;
|
|
||||||
_currentConversation = fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9920,7 +9859,7 @@ public partial class ChatWindow : Window
|
|||||||
// PlanViewerWindow 생성 또는 재사용
|
// PlanViewerWindow 생성 또는 재사용
|
||||||
if (_planViewerWindow == null || !IsWindowAlive(_planViewerWindow))
|
if (_planViewerWindow == null || !IsWindowAlive(_planViewerWindow))
|
||||||
{
|
{
|
||||||
_planViewerWindow = new PlanViewerWindow();
|
_planViewerWindow = new PlanViewerWindow(this);
|
||||||
_planViewerWindow.Closing += (_, e) =>
|
_planViewerWindow.Closing += (_, e) =>
|
||||||
{
|
{
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
@@ -9931,9 +9870,6 @@ public partial class ChatWindow : Window
|
|||||||
// 계획 표시 + 승인 대기
|
// 계획 표시 + 승인 대기
|
||||||
_planViewerWindow.ShowPlanAsync(planSummary, steps, tcs);
|
_planViewerWindow.ShowPlanAsync(planSummary, steps, tcs);
|
||||||
|
|
||||||
// 채팅 창에 간략 배너 추가 + 인라인 승인 버튼도 표시
|
|
||||||
AddDecisionButtons(tcs, options);
|
|
||||||
|
|
||||||
// 하단 바 계획 버튼 표시
|
// 하단 바 계획 버튼 표시
|
||||||
ShowPlanButton(true);
|
ShowPlanButton(true);
|
||||||
});
|
});
|
||||||
@@ -14153,7 +14089,7 @@ public partial class ChatWindow : Window
|
|||||||
|
|
||||||
BtnInlineFastMode.Content = GetQuickActionLabel("Fast", llm.FreeTierMode ? "켜짐" : "꺼짐");
|
BtnInlineFastMode.Content = GetQuickActionLabel("Fast", llm.FreeTierMode ? "켜짐" : "꺼짐");
|
||||||
BtnInlineReasoning.Content = GetQuickActionLabel("추론", ReasoningLabel(llm.AgentDecisionLevel));
|
BtnInlineReasoning.Content = GetQuickActionLabel("추론", ReasoningLabel(llm.AgentDecisionLevel));
|
||||||
BtnInlinePlanMode.Content = GetQuickActionLabel("계획", PlanModeLabel(llm.PlanMode));
|
BtnInlinePlanMode.Content = GetQuickActionLabel("계획", PlanModeLabel("off"));
|
||||||
BtnInlinePermission.Content = GetQuickActionLabel("권한", PermissionModeCatalog.ToDisplayLabel(llm.FilePermission));
|
BtnInlinePermission.Content = GetQuickActionLabel("권한", PermissionModeCatalog.ToDisplayLabel(llm.FilePermission));
|
||||||
BtnInlineSkill.Content = $"스킬 · {(llm.EnableSkillSystem ? "On" : "Off")}";
|
BtnInlineSkill.Content = $"스킬 · {(llm.EnableSkillSystem ? "On" : "Off")}";
|
||||||
BtnInlineCommandBrowser.Content = "명령/스킬 브라우저";
|
BtnInlineCommandBrowser.Content = "명령/스킬 브라우저";
|
||||||
@@ -14164,7 +14100,7 @@ public partial class ChatWindow : Window
|
|||||||
|
|
||||||
ApplyQuickActionVisual(BtnInlineFastMode, llm.FreeTierMode, "#ECFDF5", "#166534");
|
ApplyQuickActionVisual(BtnInlineFastMode, llm.FreeTierMode, "#ECFDF5", "#166534");
|
||||||
ApplyQuickActionVisual(BtnInlineReasoning, !string.Equals(llm.AgentDecisionLevel, "normal", StringComparison.OrdinalIgnoreCase), "#EEF2FF", "#1D4ED8");
|
ApplyQuickActionVisual(BtnInlineReasoning, !string.Equals(llm.AgentDecisionLevel, "normal", StringComparison.OrdinalIgnoreCase), "#EEF2FF", "#1D4ED8");
|
||||||
ApplyQuickActionVisual(BtnInlinePlanMode, !string.Equals(llm.PlanMode, "off", StringComparison.OrdinalIgnoreCase), "#EEF2FF", "#4338CA");
|
ApplyQuickActionVisual(BtnInlinePlanMode, false, "#EEF2FF", "#4338CA");
|
||||||
ApplyQuickActionVisual(BtnInlinePermission,
|
ApplyQuickActionVisual(BtnInlinePermission,
|
||||||
!string.Equals(PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission), PermissionModeCatalog.Deny, StringComparison.OrdinalIgnoreCase),
|
!string.Equals(PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission), PermissionModeCatalog.Deny, StringComparison.OrdinalIgnoreCase),
|
||||||
"#FFF7ED",
|
"#FFF7ED",
|
||||||
@@ -16399,7 +16335,7 @@ public partial class ChatWindow : Window
|
|||||||
SelectComboTag(CmbOverlayOperationMode, OperationModePolicy.Normalize(_settings.Settings.OperationMode));
|
SelectComboTag(CmbOverlayOperationMode, OperationModePolicy.Normalize(_settings.Settings.OperationMode));
|
||||||
SelectComboTag(CmbOverlayFolderDataUsage, _folderDataUsage);
|
SelectComboTag(CmbOverlayFolderDataUsage, _folderDataUsage);
|
||||||
SelectComboTag(CmbOverlayPermission, PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission));
|
SelectComboTag(CmbOverlayPermission, PermissionModeCatalog.NormalizeGlobalMode(llm.FilePermission));
|
||||||
SelectComboTag(CmbOverlayPlanMode, llm.PlanMode);
|
SelectComboTag(CmbOverlayPlanMode, "off");
|
||||||
SelectComboTag(CmbOverlayReasoning, llm.AgentDecisionLevel);
|
SelectComboTag(CmbOverlayReasoning, llm.AgentDecisionLevel);
|
||||||
SelectComboTag(CmbOverlayFastMode, llm.FreeTierMode ? "on" : "off");
|
SelectComboTag(CmbOverlayFastMode, llm.FreeTierMode ? "on" : "off");
|
||||||
SelectComboTag(CmbOverlayDefaultOutputFormat, llm.DefaultOutputFormat ?? "auto");
|
SelectComboTag(CmbOverlayDefaultOutputFormat, llm.DefaultOutputFormat ?? "auto");
|
||||||
@@ -16897,10 +16833,10 @@ public partial class ChatWindow : Window
|
|||||||
|
|
||||||
private void CmbOverlayPlanMode_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void CmbOverlayPlanMode_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (_isOverlaySettingsSyncing || CmbOverlayPlanMode.SelectedItem is not ComboBoxItem selected || selected.Tag is not string tag)
|
if (_isOverlaySettingsSyncing)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_settings.Settings.Llm.PlanMode = tag;
|
_settings.Settings.Llm.PlanMode = "off";
|
||||||
PersistOverlaySettingsState(refreshOverlayDeferredInputs: false);
|
PersistOverlaySettingsState(refreshOverlayDeferredInputs: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -17113,7 +17049,7 @@ public partial class ChatWindow : Window
|
|||||||
private void BtnInlinePlanMode_Click(object sender, RoutedEventArgs e)
|
private void BtnInlinePlanMode_Click(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var llm = _settings.Settings.Llm;
|
var llm = _settings.Settings.Llm;
|
||||||
llm.PlanMode = NextPlanMode(llm.PlanMode);
|
llm.PlanMode = "off";
|
||||||
_settings.Save();
|
_settings.Save();
|
||||||
_appState.LoadFromSettings(_settings);
|
_appState.LoadFromSettings(_settings);
|
||||||
RefreshInlineSettingsPanel();
|
RefreshInlineSettingsPanel();
|
||||||
|
|||||||
@@ -49,28 +49,37 @@ internal sealed class PlanViewerWindow : Window
|
|||||||
private bool _isExecuting;
|
private bool _isExecuting;
|
||||||
private readonly string _uiExpressionLevel;
|
private readonly string _uiExpressionLevel;
|
||||||
|
|
||||||
public PlanViewerWindow()
|
public PlanViewerWindow(Window? owner = null)
|
||||||
{
|
{
|
||||||
|
if (owner != null)
|
||||||
|
{
|
||||||
|
Owner = owner;
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
Resources.MergedDictionaries.Add(owner.Resources);
|
||||||
|
}
|
||||||
|
|
||||||
_uiExpressionLevel = ResolveUiExpressionLevel();
|
_uiExpressionLevel = ResolveUiExpressionLevel();
|
||||||
|
|
||||||
Width = 640;
|
Width = 640;
|
||||||
Height = 520;
|
Height = 520;
|
||||||
MinWidth = 480;
|
MinWidth = 480;
|
||||||
MinHeight = 360;
|
MinHeight = 360;
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterScreen;
|
WindowStartupLocation = owner == null
|
||||||
|
? WindowStartupLocation.CenterScreen
|
||||||
|
: WindowStartupLocation.CenterOwner;
|
||||||
WindowStyle = WindowStyle.None;
|
WindowStyle = WindowStyle.None;
|
||||||
AllowsTransparency = true;
|
AllowsTransparency = true;
|
||||||
Background = Brushes.Transparent;
|
Background = Brushes.Transparent;
|
||||||
ResizeMode = ResizeMode.CanResize; // WndProc로 직접 처리
|
ResizeMode = ResizeMode.CanResize; // WndProc로 직접 처리
|
||||||
ShowInTaskbar = false;
|
ShowInTaskbar = false;
|
||||||
|
|
||||||
var bgBrush = Application.Current.TryFindResource("LauncherBackground") as Brush
|
var bgBrush = TryFindResource("LauncherBackground") as Brush
|
||||||
?? new SolidColorBrush(Color.FromRgb(0x1A, 0x1B, 0x2E));
|
?? new SolidColorBrush(Color.FromRgb(0x1A, 0x1B, 0x2E));
|
||||||
var primaryText = Application.Current.TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
var primaryText = TryFindResource("PrimaryText") as Brush ?? Brushes.White;
|
||||||
var secondaryText = Application.Current.TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
var secondaryText = TryFindResource("SecondaryText") as Brush ?? Brushes.Gray;
|
||||||
var accentBrush = Application.Current.TryFindResource("AccentColor") as Brush
|
var accentBrush = TryFindResource("AccentColor") as Brush
|
||||||
?? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC));
|
?? new SolidColorBrush(Color.FromRgb(0x4B, 0x5E, 0xFC));
|
||||||
var borderBrush = Application.Current.TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
var borderBrush = TryFindResource("BorderColor") as Brush ?? Brushes.Gray;
|
||||||
|
|
||||||
var root = new Border
|
var root = new Border
|
||||||
{
|
{
|
||||||
@@ -151,7 +160,7 @@ internal sealed class PlanViewerWindow : Window
|
|||||||
mainGrid.Children.Add(_statusBar);
|
mainGrid.Children.Add(_statusBar);
|
||||||
|
|
||||||
// ── 툴바: 모두 열기 / 모두 닫기 ──
|
// ── 툴바: 모두 열기 / 모두 닫기 ──
|
||||||
var hoverBgTb = Application.Current.TryFindResource("ItemHoverBackground") as Brush
|
var hoverBgTb = TryFindResource("ItemHoverBackground") as Brush
|
||||||
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
?? new SolidColorBrush(Color.FromArgb(0x18, 0xFF, 0xFF, 0xFF));
|
||||||
var toolBar = new StackPanel
|
var toolBar = new StackPanel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4653,7 +4653,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
<Border Style="{StaticResource AgentSettingsRow}">
|
<Border Style="{StaticResource AgentSettingsRow}" Visibility="Collapsed">
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel HorizontalAlignment="Left" Margin="0,0,180,0">
|
<StackPanel HorizontalAlignment="Left" Margin="0,0,180,0">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
|
|||||||
Reference in New Issue
Block a user