aiexecode 1.0.157

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/LICENSE +68 -0
  2. package/README.md +347 -0
  3. package/config_template/mcp_config.json +3 -0
  4. package/config_template/package_name_store.json +5 -0
  5. package/config_template/settings.json +5 -0
  6. package/index.js +879 -0
  7. package/mcp-agent-lib/example/01-basic-usage.js +82 -0
  8. package/mcp-agent-lib/example/02-quick-start.js +52 -0
  9. package/mcp-agent-lib/example/03-http-server.js +76 -0
  10. package/mcp-agent-lib/example/04-multiple-servers.js +117 -0
  11. package/mcp-agent-lib/example/05-error-handling.js +116 -0
  12. package/mcp-agent-lib/example/06-resources-and-prompts.js +174 -0
  13. package/mcp-agent-lib/example/07-advanced-configuration.js +191 -0
  14. package/mcp-agent-lib/example/08-real-world-chatbot.js +331 -0
  15. package/mcp-agent-lib/example/README.md +346 -0
  16. package/mcp-agent-lib/index.js +19 -0
  17. package/mcp-agent-lib/init.sh +3 -0
  18. package/mcp-agent-lib/package-lock.json +1216 -0
  19. package/mcp-agent-lib/package.json +53 -0
  20. package/mcp-agent-lib/sampleFastMCPClient/client.py +25 -0
  21. package/mcp-agent-lib/sampleFastMCPClient/run.sh +3 -0
  22. package/mcp-agent-lib/sampleFastMCPServer/run.sh +3 -0
  23. package/mcp-agent-lib/sampleFastMCPServer/server.py +12 -0
  24. package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/run.sh +3 -0
  25. package/mcp-agent-lib/sampleFastMCPServerElicitationRequest/server.py +43 -0
  26. package/mcp-agent-lib/sampleFastMCPServerRootsRequest/server.py +63 -0
  27. package/mcp-agent-lib/sampleMCPHost/index.js +386 -0
  28. package/mcp-agent-lib/sampleMCPHost/mcp_config.json +24 -0
  29. package/mcp-agent-lib/sampleMCPHostFeatures/elicitation.js +151 -0
  30. package/mcp-agent-lib/sampleMCPHostFeatures/index.js +166 -0
  31. package/mcp-agent-lib/sampleMCPHostFeatures/roots.js +197 -0
  32. package/mcp-agent-lib/src/mcp_client.js +1860 -0
  33. package/mcp-agent-lib/src/mcp_message_logger.js +517 -0
  34. package/package.json +72 -0
  35. package/payload_viewer/out/404/index.html +1 -0
  36. package/payload_viewer/out/404.html +1 -0
  37. package/payload_viewer/out/_next/static/chunks/060f9a97930f3d04.js +1 -0
  38. package/payload_viewer/out/_next/static/chunks/103c802c8f4a5ea1.js +1 -0
  39. package/payload_viewer/out/_next/static/chunks/16474fd6c6910c45.js +1 -0
  40. package/payload_viewer/out/_next/static/chunks/17722e3ac4e00587.js +1 -0
  41. package/payload_viewer/out/_next/static/chunks/305b077a9873cf54.js +1 -0
  42. package/payload_viewer/out/_next/static/chunks/4c1d05c6741c2bdd.js +5 -0
  43. package/payload_viewer/out/_next/static/chunks/538cc02e54714b23.js +1 -0
  44. package/payload_viewer/out/_next/static/chunks/6251fa5907d2b226.js +5 -0
  45. package/payload_viewer/out/_next/static/chunks/a6dad97d9634a72d.js +1 -0
  46. package/payload_viewer/out/_next/static/chunks/b6c0459f3789d25c.js +1 -0
  47. package/payload_viewer/out/_next/static/chunks/b75131b58f8ca46a.css +3 -0
  48. package/payload_viewer/out/_next/static/chunks/bd2dcf98c9b362f6.js +1 -0
  49. package/payload_viewer/out/_next/static/chunks/c8a542ae21335479.js +1 -0
  50. package/payload_viewer/out/_next/static/chunks/cdd12d5c1a5a6064.js +1 -0
  51. package/payload_viewer/out/_next/static/chunks/e411019f55d87c42.js +1 -0
  52. package/payload_viewer/out/_next/static/chunks/e60ef129113f6e24.js +1 -0
  53. package/payload_viewer/out/_next/static/chunks/f1ac9047ac4a3fde.js +1 -0
  54. package/payload_viewer/out/_next/static/chunks/turbopack-0ac29803ce3c3c7a.js +3 -0
  55. package/payload_viewer/out/_next/static/chunks/turbopack-89db4c64206a73e4.js +3 -0
  56. package/payload_viewer/out/_next/static/chunks/turbopack-a5b8235fa59d7119.js +3 -0
  57. package/payload_viewer/out/_next/static/media/4fa387ec64143e14-s.c1fdd6c2.woff2 +0 -0
  58. package/payload_viewer/out/_next/static/media/7178b3e590c64307-s.b97b3418.woff2 +0 -0
  59. package/payload_viewer/out/_next/static/media/797e433ab948586e-s.p.dbea232f.woff2 +0 -0
  60. package/payload_viewer/out/_next/static/media/8a480f0b521d4e75-s.8e0177b5.woff2 +0 -0
  61. package/payload_viewer/out/_next/static/media/bbc41e54d2fcbd21-s.799d8ef8.woff2 +0 -0
  62. package/payload_viewer/out/_next/static/media/caa3a2e1cccd8315-s.p.853070df.woff2 +0 -0
  63. package/payload_viewer/out/_next/static/media/favicon.0b3bf435.ico +0 -0
  64. package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_buildManifest.js +14 -0
  65. package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_clientMiddlewareManifest.json +1 -0
  66. package/payload_viewer/out/_next/static/uqQYtKpKV7kCSkUbLgdfJ/_ssgManifest.js +1 -0
  67. package/payload_viewer/out/favicon.ico +0 -0
  68. package/payload_viewer/out/file.svg +1 -0
  69. package/payload_viewer/out/globe.svg +1 -0
  70. package/payload_viewer/out/index.html +1 -0
  71. package/payload_viewer/out/index.txt +23 -0
  72. package/payload_viewer/out/next.svg +1 -0
  73. package/payload_viewer/out/vercel.svg +1 -0
  74. package/payload_viewer/out/window.svg +1 -0
  75. package/payload_viewer/web_server.js +861 -0
  76. package/prompts/completion_judge.txt +128 -0
  77. package/prompts/orchestrator.txt +1213 -0
  78. package/src/LLMClient/client.js +1375 -0
  79. package/src/LLMClient/converters/input-normalizer.js +238 -0
  80. package/src/LLMClient/converters/responses-to-claude.js +503 -0
  81. package/src/LLMClient/converters/responses-to-gemini.js +648 -0
  82. package/src/LLMClient/converters/responses-to-ollama.js +348 -0
  83. package/src/LLMClient/converters/responses-to-zai.js +667 -0
  84. package/src/LLMClient/errors.js +398 -0
  85. package/src/LLMClient/index.js +36 -0
  86. package/src/ai_based/completion_judge.js +421 -0
  87. package/src/ai_based/orchestrator.js +527 -0
  88. package/src/ai_based/pip_package_installer.js +173 -0
  89. package/src/ai_based/pip_package_lookup.js +197 -0
  90. package/src/cli/mcp_cli.js +70 -0
  91. package/src/cli/mcp_commands.js +255 -0
  92. package/src/commands/agents.js +18 -0
  93. package/src/commands/apikey.js +55 -0
  94. package/src/commands/bg.js +140 -0
  95. package/src/commands/commands.js +56 -0
  96. package/src/commands/debug.js +54 -0
  97. package/src/commands/exit.js +19 -0
  98. package/src/commands/help.js +35 -0
  99. package/src/commands/mcp.js +128 -0
  100. package/src/commands/model.js +176 -0
  101. package/src/commands/setup.js +13 -0
  102. package/src/commands/skills.js +51 -0
  103. package/src/commands/tools.js +165 -0
  104. package/src/commands/viewer.js +147 -0
  105. package/src/config/ai_models.js +312 -0
  106. package/src/config/config.js +10 -0
  107. package/src/config/constants.js +71 -0
  108. package/src/config/feature_flags.js +15 -0
  109. package/src/frontend/App.js +1263 -0
  110. package/src/frontend/README.md +81 -0
  111. package/src/frontend/components/AutocompleteMenu.js +47 -0
  112. package/src/frontend/components/BackgroundProcessList.js +175 -0
  113. package/src/frontend/components/BlankLine.js +62 -0
  114. package/src/frontend/components/ConversationItem.js +893 -0
  115. package/src/frontend/components/CurrentModelView.js +43 -0
  116. package/src/frontend/components/FileDiffViewer.js +616 -0
  117. package/src/frontend/components/Footer.js +25 -0
  118. package/src/frontend/components/Header.js +42 -0
  119. package/src/frontend/components/HelpView.js +154 -0
  120. package/src/frontend/components/Input.js +344 -0
  121. package/src/frontend/components/LoadingIndicator.js +31 -0
  122. package/src/frontend/components/ModelListView.js +49 -0
  123. package/src/frontend/components/ModelUpdatedView.js +22 -0
  124. package/src/frontend/components/SessionSpinner.js +66 -0
  125. package/src/frontend/components/SetupWizard.js +242 -0
  126. package/src/frontend/components/StreamOutput.js +34 -0
  127. package/src/frontend/components/TodoList.js +56 -0
  128. package/src/frontend/components/ToolApprovalPrompt.js +452 -0
  129. package/src/frontend/design/themeColors.js +42 -0
  130. package/src/frontend/hooks/useCompletion.js +84 -0
  131. package/src/frontend/hooks/useFileCompletion.js +467 -0
  132. package/src/frontend/hooks/useKeypress.js +145 -0
  133. package/src/frontend/index.js +65 -0
  134. package/src/frontend/utils/GridRenderer.js +140 -0
  135. package/src/frontend/utils/InlineFormatter.js +156 -0
  136. package/src/frontend/utils/diffUtils.js +235 -0
  137. package/src/frontend/utils/inputBuffer.js +441 -0
  138. package/src/frontend/utils/markdownParser.js +377 -0
  139. package/src/frontend/utils/outputRedirector.js +47 -0
  140. package/src/frontend/utils/renderInkComponent.js +42 -0
  141. package/src/frontend/utils/syntaxHighlighter.js +149 -0
  142. package/src/frontend/utils/toolUIFormatter.js +261 -0
  143. package/src/system/agents_loader.js +170 -0
  144. package/src/system/ai_request.js +737 -0
  145. package/src/system/background_process.js +317 -0
  146. package/src/system/code_executer.js +1233 -0
  147. package/src/system/command_loader.js +40 -0
  148. package/src/system/command_parser.js +133 -0
  149. package/src/system/conversation_state.js +265 -0
  150. package/src/system/conversation_trimmer.js +265 -0
  151. package/src/system/custom_command_loader.js +395 -0
  152. package/src/system/file_integrity.js +466 -0
  153. package/src/system/import_analyzer.py +174 -0
  154. package/src/system/log.js +82 -0
  155. package/src/system/mcp_integration.js +304 -0
  156. package/src/system/output_helper.js +89 -0
  157. package/src/system/session.js +1393 -0
  158. package/src/system/session_memory.js +481 -0
  159. package/src/system/skill_loader.js +324 -0
  160. package/src/system/system_info.js +483 -0
  161. package/src/system/tool_approval.js +160 -0
  162. package/src/system/tool_registry.js +184 -0
  163. package/src/system/ui_events.js +279 -0
  164. package/src/tools/code_editor.js +792 -0
  165. package/src/tools/file_reader.js +385 -0
  166. package/src/tools/glob.js +263 -0
  167. package/src/tools/response_message.js +30 -0
  168. package/src/tools/ripgrep.js +554 -0
  169. package/src/tools/skill_tool.js +122 -0
  170. package/src/tools/todo_write.js +182 -0
  171. package/src/tools/web_download.py +74 -0
  172. package/src/tools/web_downloader.js +83 -0
  173. package/src/util/clone.js +174 -0
  174. package/src/util/config.js +203 -0
  175. package/src/util/config_migration.js +174 -0
  176. package/src/util/debug_log.js +49 -0
  177. package/src/util/exit_handler.js +53 -0
  178. package/src/util/file_reference_parser.js +132 -0
  179. package/src/util/mcp_config_manager.js +159 -0
  180. package/src/util/output_formatter.js +50 -0
  181. package/src/util/path_helper.js +27 -0
  182. package/src/util/path_validator.js +178 -0
  183. package/src/util/prompt_loader.js +184 -0
  184. package/src/util/rag_helper.js +101 -0
  185. package/src/util/safe_fs.js +645 -0
  186. package/src/util/setup_wizard.js +62 -0
  187. package/src/util/text_formatter.js +33 -0
  188. package/src/util/version_check.js +116 -0
@@ -0,0 +1,527 @@
1
+ import dotenv from "dotenv";
2
+ // 이 파일은 계획을 실제 행동으로 옮기기 위해 어떤 도구를 호출할지 결정합니다.
3
+ import path from 'path';
4
+ import { truncateWithOmit } from "../util/text_formatter.js";
5
+ import { createSystemMessage, createTodoReminder, createSystemReminder, createTrimmedFileReminder } from "../util/prompt_loader.js";
6
+ import { request, shouldRetryWithTrim, getModelForProvider } from "../system/ai_request.js";
7
+ import { supportsCaching } from "../config/ai_models.js";
8
+ import { cleanupOrphanOutputs, trimConversation, getTrimmedFileReads, clearTrimmedFileReads } from "../system/conversation_trimmer.js";
9
+ import { runPythonCodeSchema, bashSchema, getPersistentShell } from "../system/code_executer.js";
10
+ import { readFileSchema, readFileRangeSchema } from "../tools/file_reader.js";
11
+ import { writeFileSchema, editFileRangeSchema, editFileReplaceSchema } from "../tools/code_editor.js";
12
+ import { fetchWebPageSchema } from "../tools/web_downloader.js";
13
+ import { responseMessageSchema } from "../tools/response_message.js";
14
+ import { todoWriteSchema } from "../tools/todo_write.js";
15
+ import { ripgrepSchema } from "../tools/ripgrep.js";
16
+ import { globSearchSchema } from "../tools/glob.js";
17
+ import { skillSchema, generateSkillListForPrompt } from "../tools/skill_tool.js";
18
+ import { loadSettings } from "../util/config.js";
19
+ import { createDebugLogger } from "../util/debug_log.js";
20
+ import { getCurrentTodos } from "../system/session_memory.js";
21
+ import { loadAgentsMdForPrompt } from "../system/agents_loader.js";
22
+ dotenv.config({ quiet: true });
23
+
24
+ const debugLog = createDebugLogger('orchestrator.log', 'orchestrator');
25
+
26
+ const orchestratorConversation = [];
27
+ let orchestratorRequestOptions = null;
28
+
29
+ /**
30
+ * Orchestrator conversation을 초기화하거나 시스템 프롬프트를 업데이트합니다.
31
+ * 매 요청마다 프롬프트 파일을 새로 읽어서 변경사항을 즉시 반영합니다.
32
+ *
33
+ * 로그 분석에서 발견한 전략 적용:
34
+ * 1. 캐시 제어 (cache_control: ephemeral) - Z.AI/GLM 모델의 경우
35
+ * 2. System-Reminder 패턴 - TODO 상태 등을 별도 블록으로 분리
36
+ * 3. 다중 시스템 메시지 구조 - 캐시 효과 극대화
37
+ */
38
+ async function ensureConversationInitialized() {
39
+ // 현재 모델 확인 (캐시 제어 여부 결정)
40
+ const currentModel = await getModelForProvider();
41
+ const useCache = supportsCaching(currentModel);
42
+ debugLog(`[ensureConversationInitialized] Model: ${currentModel}, Cache enabled: ${useCache}`);
43
+
44
+ // 매번 최신 시스템 프롬프트를 로드 (캐시 제어 옵션 적용)
45
+ // 셸의 실제 현재 작업 디렉토리를 가져옴 (셸 상태가 유지되므로 process.cwd()와 다를 수 있음)
46
+ const shellCwd = await getPersistentShell().getCwd() || process.cwd();
47
+ debugLog(`[ensureConversationInitialized] Shell CWD: ${shellCwd}, process.cwd(): ${process.cwd()}`);
48
+ const systemMessage = await createSystemMessage("orchestrator.txt", {
49
+ CWD: shellCwd,
50
+ OS: process.app_custom?.systemInfo?.os || 'unknown'
51
+ }, {
52
+ model: currentModel,
53
+ enableCache: useCache
54
+ });
55
+
56
+ // 현재 todos 가져오기
57
+ const currentTodos = getCurrentTodos();
58
+
59
+ // 사용 가능한 스킬 목록 생성
60
+ const skillListText = await generateSkillListForPrompt();
61
+ debugLog(`[ensureConversationInitialized] Generated skill list: ${skillListText ? 'has skills' : 'no skills'}`);
62
+
63
+ // AGENTS.md 로드 (프로젝트별 에이전트 지침)
64
+ const agentsMdText = await loadAgentsMdForPrompt();
65
+ debugLog(`[ensureConversationInitialized] AGENTS.md: ${agentsMdText ? `loaded (${agentsMdText.length} chars)` : 'not found'}`);
66
+
67
+ // 시스템 메시지 구성 (캐싱 모델의 경우 다중 블록 구조)
68
+ let systemMessageEntry;
69
+
70
+ if (useCache && Array.isArray(systemMessage.content)) {
71
+ // 캐싱 모델: 다중 블록 구조 사용 (Claude Code 스타일)
72
+ const contentBlocks = [...systemMessage.content];
73
+
74
+ // AGENTS.md를 별도 블록으로 추가 (프로젝트별 지침)
75
+ if (agentsMdText) {
76
+ contentBlocks.push({
77
+ type: "input_text",
78
+ text: agentsMdText,
79
+ cache_control: { type: "ephemeral" } // 프로젝트가 바뀔 때만 변경되므로 캐시
80
+ });
81
+ debugLog(`[ensureConversationInitialized] Added AGENTS.md as separate block (cached)`);
82
+ }
83
+
84
+ // 스킬 목록을 별도 블록으로 추가
85
+ if (skillListText) {
86
+ contentBlocks.push({
87
+ type: "input_text",
88
+ text: skillListText
89
+ // 스킬 목록은 자주 변경될 수 있으므로 캐시하지 않음
90
+ });
91
+ debugLog(`[ensureConversationInitialized] Added skill list as separate block`);
92
+ }
93
+
94
+ // TODO 리마인더를 별도 블록으로 추가 (캐시 대상 아님)
95
+ if (currentTodos && currentTodos.length > 0) {
96
+ const todoReminder = createTodoReminder(currentTodos);
97
+ contentBlocks.push({
98
+ type: "input_text",
99
+ text: todoReminder
100
+ // cache_control 없음 - 동적 데이터이므로
101
+ });
102
+ debugLog(`[ensureConversationInitialized] Added ${currentTodos.length} todos as separate block (uncached)`);
103
+ }
104
+
105
+ // Trim된 파일 읽기 알림 추가 (파일 경로 알림 기능)
106
+ const trimmedFiles = getTrimmedFileReads();
107
+ if (trimmedFiles.length > 0) {
108
+ const trimmedFileReminder = createTrimmedFileReminder(trimmedFiles);
109
+ if (trimmedFileReminder) {
110
+ contentBlocks.push({
111
+ type: "input_text",
112
+ text: trimmedFileReminder
113
+ // cache_control 없음 - 동적 데이터이므로
114
+ });
115
+ debugLog(`[ensureConversationInitialized] Added trimmed file reminder for ${trimmedFiles.length} files (uncached)`);
116
+ }
117
+ }
118
+
119
+ systemMessageEntry = {
120
+ role: "system",
121
+ content: contentBlocks
122
+ };
123
+ } else {
124
+ // 일반 모델: 기존 방식 유지
125
+ let systemMessageText = systemMessage.content;
126
+
127
+ // AGENTS.md 추가 (프로젝트별 지침)
128
+ if (agentsMdText) {
129
+ systemMessageText += agentsMdText;
130
+ debugLog(`[ensureConversationInitialized] Added AGENTS.md to system message`);
131
+ }
132
+
133
+ // 스킬 목록 추가
134
+ if (skillListText) {
135
+ systemMessageText += skillListText;
136
+ debugLog(`[ensureConversationInitialized] Added skill list to system message`);
137
+ }
138
+
139
+ if (currentTodos && currentTodos.length > 0) {
140
+ const todosSection = '\n\n## Current Task List (Your TODO tracker)\n\n' +
141
+ 'You have created the following task list to track your work. Continue working on these tasks systematically:\n\n' +
142
+ currentTodos.map((todo, index) => {
143
+ const statusIcon = todo.status === 'completed' ? '✓' :
144
+ todo.status === 'in_progress' ? '→' : '○';
145
+ return `${index + 1}. [${statusIcon}] ${todo.content} (${todo.status})`;
146
+ }).join('\n') +
147
+ '\n\n**Remember**: Mark tasks as completed immediately after finishing them. Keep exactly ONE task as in_progress at a time.\n';
148
+
149
+ systemMessageText += todosSection;
150
+ debugLog(`[ensureConversationInitialized] Added ${currentTodos.length} todos to system message`);
151
+ }
152
+
153
+ // Trim된 파일 읽기 알림 추가 (파일 경로 알림 기능)
154
+ const trimmedFiles = getTrimmedFileReads();
155
+ if (trimmedFiles.length > 0) {
156
+ const trimmedFileReminder = createTrimmedFileReminder(trimmedFiles);
157
+ if (trimmedFileReminder) {
158
+ systemMessageText += '\n\n' + trimmedFileReminder;
159
+ debugLog(`[ensureConversationInitialized] Added trimmed file reminder for ${trimmedFiles.length} files`);
160
+ }
161
+ }
162
+
163
+ systemMessageEntry = {
164
+ role: "system",
165
+ content: [
166
+ {
167
+ type: "input_text",
168
+ text: systemMessageText
169
+ }
170
+ ]
171
+ };
172
+ }
173
+
174
+ // conversation이 비어있으면 새로 추가
175
+ if (!orchestratorConversation.length) {
176
+ orchestratorConversation.push(systemMessageEntry);
177
+ } else {
178
+ // conversation이 있으면 첫 번째 system 메시지를 업데이트
179
+ if (orchestratorConversation[0]?.role === "system") {
180
+ orchestratorConversation[0] = systemMessageEntry;
181
+ } else {
182
+ // system 메시지가 맨 앞에 없으면 추가
183
+ orchestratorConversation.unshift(systemMessageEntry);
184
+ }
185
+ }
186
+ }
187
+
188
+ function appendResponseToConversation(response) {
189
+ if (!response?.output || !Array.isArray(response.output)) {
190
+ debugLog(`[appendResponseToConversation] No output to append`);
191
+ return;
192
+ }
193
+
194
+ debugLog(`[appendResponseToConversation] Appending ${response.output.length} items from response`);
195
+
196
+ for (const item of response.output) {
197
+ if (!item) continue;
198
+ debugLog(`[appendResponseToConversation] Appending item type: ${item.type}, role: ${item.role}, tool_calls: ${item.tool_calls?.length || 0}`);
199
+ orchestratorConversation.push(JSON.parse(JSON.stringify(item)));
200
+ }
201
+
202
+ debugLog(`[appendResponseToConversation] Conversation now has ${orchestratorConversation.length} entries`);
203
+ }
204
+
205
+ export function resetOrchestratorConversation() {
206
+ orchestratorConversation.length = 0;
207
+ orchestratorRequestOptions = null;
208
+ clearTrimmedFileReads(); // 세션 초기화 시 trim된 파일 목록도 초기화
209
+ }
210
+
211
+ export function getOrchestratorConversation() {
212
+ return orchestratorConversation;
213
+ }
214
+
215
+ export function restoreOrchestratorConversation(savedConversation, savedRequestOptions = null) {
216
+ orchestratorConversation.length = 0;
217
+ if (Array.isArray(savedConversation)) {
218
+ orchestratorConversation.push(...savedConversation);
219
+ }
220
+ if (savedRequestOptions) {
221
+ orchestratorRequestOptions = savedRequestOptions;
222
+ }
223
+ }
224
+
225
+ export function recordOrchestratorToolResult({ toolCallId, toolName = '', arguments: toolArguments = null, stdout = '', stderr = '', exitCode = null, originalResult = null }) {
226
+ debugLog(`[recordOrchestratorToolResult] Called - toolName: ${toolName}, toolCallId: ${toolCallId}, stdout: ${stdout?.length || 0} bytes, stderr: ${stderr?.length || 0} bytes`);
227
+
228
+ if (!toolCallId) {
229
+ debugLog(`[recordOrchestratorToolResult] No toolCallId provided, returning null`);
230
+ return null;
231
+ }
232
+
233
+ const payload = {
234
+ tool: toolName || null,
235
+ call_id: toolCallId,
236
+ exit_code: exitCode,
237
+ stdout: stdout ? truncateWithOmit(stdout, 8000) : '',
238
+ stderr: stderr ? truncateWithOmit(stderr, 4000) : '',
239
+ original_result: originalResult // 원본 도구 결과 저장
240
+ };
241
+
242
+ debugLog(`[recordOrchestratorToolResult] payload - stdout: ${payload.stdout.length} bytes, stderr: ${payload.stderr.length} bytes, stderr content: "${payload.stderr}"`);
243
+
244
+ const toolResult = {
245
+ type: "function_call_output",
246
+ call_id: toolCallId,
247
+ output: JSON.stringify(payload)
248
+ };
249
+
250
+ debugLog(`[recordOrchestratorToolResult] toolResult output length: ${toolResult.output.length} bytes`);
251
+ orchestratorConversation.push(toolResult);
252
+ return toolResult;
253
+ }
254
+
255
+ // 대화 cleanup 및 trim 함수는 conversation_trimmer 모듈에서 가져옴
256
+
257
+ async function dispatchOrchestratorRequest({ toolChoice }) {
258
+ debugLog(`[dispatchOrchestratorRequest] START - toolChoice: ${toolChoice}`);
259
+
260
+ if (!orchestratorRequestOptions) {
261
+ debugLog(`[dispatchOrchestratorRequest] ERROR - orchestratorRequestOptions not initialized`);
262
+ throw new Error('Orchestrator request options not initialized.');
263
+ }
264
+
265
+ debugLog(`[dispatchOrchestratorRequest] Options - model: ${orchestratorRequestOptions.model}, tools count: ${orchestratorRequestOptions.tools?.length || 0}`);
266
+
267
+ let attemptCount = 0;
268
+ while (true) {
269
+ attemptCount++;
270
+ debugLog(`[dispatchOrchestratorRequest] Attempt ${attemptCount} - starting cleanup...`);
271
+ cleanupOrphanOutputs(orchestratorConversation);
272
+
273
+ debugLog(`[dispatchOrchestratorRequest] Conversation has ${orchestratorConversation.length} entries`);
274
+
275
+ // Conversation 구조 분석
276
+ const conversationTypes = {};
277
+ orchestratorConversation.forEach(entry => {
278
+ const type = entry?.type || entry?.role || 'unknown';
279
+ conversationTypes[type] = (conversationTypes[type] || 0) + 1;
280
+ });
281
+ debugLog(`[dispatchOrchestratorRequest] Conversation structure: ${JSON.stringify(conversationTypes)}`);
282
+
283
+ // function_call_output 항목들 확인
284
+ const functionCallOutputs = orchestratorConversation.filter(item => item.type === 'function_call_output');
285
+ debugLog(`[dispatchOrchestratorRequest] Found ${functionCallOutputs.length} function_call_output entries`);
286
+
287
+ functionCallOutputs.forEach((item, index) => {
288
+ debugLog(`[dispatchOrchestratorRequest] function_call_output[${index}] - call_id: ${item.call_id}, output length: ${item.output?.length || 0}, output preview: ${item.output?.substring(0, 200)}`);
289
+ });
290
+
291
+ const { model, isGpt5Model, tools, taskName } = orchestratorRequestOptions;
292
+ const requestConfig = {
293
+ model,
294
+ input: orchestratorConversation,
295
+ reasoning: {},
296
+ tools,
297
+ tool_choice: toolChoice ?? "auto",
298
+ top_p: 1,
299
+ store: true
300
+ };
301
+
302
+ if (!isGpt5Model) {
303
+ requestConfig.temperature = 0;
304
+ }
305
+
306
+ debugLog(`[dispatchOrchestratorRequest] Request config - model: ${model}, tool_choice: ${requestConfig.tool_choice}, tools: ${tools.length}, conversation entries: ${orchestratorConversation.length}`);
307
+
308
+ // Conversation 토큰 추정 (대략적)
309
+ const conversationJson = JSON.stringify(orchestratorConversation);
310
+ const estimatedTokens = Math.ceil(conversationJson.length / 4); // 대략 4 chars = 1 token
311
+ debugLog(`[dispatchOrchestratorRequest] Estimated tokens: ${estimatedTokens} (conversation size: ${conversationJson.length} bytes)`);
312
+
313
+ const apiStartTime = Date.now();
314
+ debugLog(`[dispatchOrchestratorRequest] >>>>> Sending API request at ${new Date(apiStartTime).toISOString()}...`);
315
+
316
+ try {
317
+ const response = await request(taskName, requestConfig);
318
+ const apiDuration = Date.now() - apiStartTime;
319
+ debugLog(`[dispatchOrchestratorRequest] <<<<< API request successful in ${apiDuration}ms`);
320
+ debugLog(`[dispatchOrchestratorRequest] Response outputs: ${response?.output?.length || 0}`);
321
+ if (response?.output) {
322
+ const outputTypes = {};
323
+ response.output.forEach(o => {
324
+ const type = o?.type || 'unknown';
325
+ outputTypes[type] = (outputTypes[type] || 0) + 1;
326
+ });
327
+ debugLog(`[dispatchOrchestratorRequest] Response output types: ${JSON.stringify(outputTypes)}`);
328
+ }
329
+ debugLog(`[dispatchOrchestratorRequest] END - success`);
330
+ // consolelog(JSON.stringify(response, null, 2));
331
+ return response;
332
+ } catch (error) {
333
+ const apiDuration = Date.now() - apiStartTime;
334
+ debugLog(`[dispatchOrchestratorRequest] <<<<< API request FAILED after ${apiDuration}ms: ${error.message}`);
335
+ debugLog(`[dispatchOrchestratorRequest] Error name: ${error.name}, type: ${error?.constructor?.name}`);
336
+
337
+ // AbortError는 즉시 전파 (세션 중단)
338
+ if (error.name === 'AbortError') {
339
+ debugLog(`[dispatchOrchestratorRequest] Request aborted by user, propagating AbortError`);
340
+ throw error;
341
+ }
342
+
343
+ if (!shouldRetryWithTrim(error)) {
344
+ debugLog(`[dispatchOrchestratorRequest] Not recoverable by trimming, re-throwing`);
345
+ throw error;
346
+ }
347
+
348
+ debugLog(`[dispatchOrchestratorRequest] Recoverable error detected, attempting to trim conversation...`);
349
+ const trimmed = trimConversation(orchestratorConversation);
350
+ debugLog(`[dispatchOrchestratorRequest] Trim result: ${trimmed}, new conversation length: ${orchestratorConversation.length}`);
351
+ if (!trimmed) {
352
+ debugLog(`[dispatchOrchestratorRequest] Cannot trim further, re-throwing error`);
353
+ throw error;
354
+ }
355
+ debugLog(`[dispatchOrchestratorRequest] Retrying with trimmed conversation...`);
356
+ }
357
+ }
358
+ }
359
+
360
+ export async function continueOrchestratorConversation() {
361
+ debugLog(`[continueOrchestratorConversation] START`);
362
+ debugLog(`[continueOrchestratorConversation] Current conversation length: ${orchestratorConversation.length}`);
363
+
364
+ // 매번 최신 프롬프트를 반영
365
+ await ensureConversationInitialized();
366
+ debugLog(`[continueOrchestratorConversation] Conversation initialized/updated`);
367
+
368
+ const response = await dispatchOrchestratorRequest({ toolChoice: "auto" });
369
+ debugLog(`[continueOrchestratorConversation] Received response, appending to conversation...`);
370
+ appendResponseToConversation(response);
371
+ debugLog(`[continueOrchestratorConversation] Conversation length after append: ${orchestratorConversation.length}`);
372
+ debugLog(`[continueOrchestratorConversation] END`);
373
+ return response;
374
+ }
375
+ // 쉘스크립트 코딩 규칙:
376
+ // - 사용자 확인이 필요한 명령은 피하고 -y 또는 -f 플래그로 자동 확인 설정
377
+ // - 출력이 과도한 명령은 피하고 필요한 경우 파일에 저장
378
+ // - 여러 명령은 && 연산자로 연결하여 중단 최소화
379
+ // - 명령 출력을 파이프 연산자로 전달하여 작업 단순화
380
+ // - 단순 계산은 비대화형 bc, 복잡한 수학 계산은 Python 사용; 절대 암산 금지
381
+
382
+
383
+
384
+ // 탐색 기반으로 현재 상황을 분석하고, 다음에 취할 행동을 AI에게 결정받습니다.
385
+ export async function orchestrateMission({ improvement_points = '', mcpToolSchemas = [], isAutoGenerated = false }) {
386
+ debugLog('');
387
+ debugLog('========================================');
388
+ debugLog('===== orchestrateMission START =========');
389
+ debugLog('========================================');
390
+
391
+ const taskName = 'orchestrator';
392
+
393
+ debugLog(`[orchestrateMission] Called with improvement_points: "${improvement_points?.substring(0, 100) || '(empty)'}", isAutoGenerated: ${isAutoGenerated}`);
394
+ debugLog(`[orchestrateMission] improvement_points length: ${improvement_points?.length || 0} characters`);
395
+ // mcpToolSchemas: MCP Integration에서 전달받은 MCP 도구들의 스키마 배열
396
+ debugLog(`[orchestrateMission] mcpToolSchemas count: ${mcpToolSchemas.length}`);
397
+ const improvementPointsText = typeof improvement_points === 'string' && improvement_points.trim().length ? improvement_points : '';
398
+ debugLog(`[orchestrateMission] improvementPointsText after processing: "${improvementPointsText.substring(0, 100) || '(empty)'}"`);
399
+ if (isAutoGenerated) {
400
+ debugLog(`[orchestrateMission] This is an auto-generated user message (from completion_judge)`);
401
+ }
402
+
403
+
404
+ // Python 사용 가능 여부 확인
405
+ const hasPython = process.app_custom?.systemInfo?.commands?.hasPython || false;
406
+ debugLog(`[orchestrateMission] Python available: ${hasPython}`);
407
+
408
+ // 설정 로드
409
+ const settings = await loadSettings();
410
+ const toolsEnabled = settings?.TOOLS_ENABLED || {};
411
+
412
+ // 도구 이름과 스키마를 매핑
413
+ const availableTools = {
414
+ bash: bashSchema,
415
+ read_file: readFileSchema,
416
+ read_file_range: readFileRangeSchema,
417
+ write_file: writeFileSchema,
418
+ edit_file_range: editFileRangeSchema,
419
+ edit_file_replace: editFileReplaceSchema,
420
+ response_message: responseMessageSchema,
421
+ todo_write: todoWriteSchema,
422
+ ripgrep: ripgrepSchema,
423
+ glob_search: globSearchSchema,
424
+ invoke_skill: skillSchema // 스킬 호출 도구
425
+ };
426
+
427
+ // Python 관련 도구 (조건부 추가)
428
+ if (hasPython) {
429
+ availableTools.run_python_code = runPythonCodeSchema;
430
+ availableTools.fetch_web_page = fetchWebPageSchema;
431
+ }
432
+
433
+ // 활성화된 도구만 선택
434
+ const tools = [];
435
+ for (const [toolName, schema] of Object.entries(availableTools)) {
436
+ // 설정에 도구가 명시되어 있지 않거나 true로 설정된 경우 포함
437
+ if (toolsEnabled[toolName] !== false) {
438
+ tools.push(schema);
439
+ }
440
+ }
441
+
442
+ // MCP Tools 추가 (MCP 서버로부터 동적으로 제공받은 도구들)
443
+ // mcpToolSchemas는 MCP Integration이 연결된 MCP 서버들로부터 조회한 도구 스키마
444
+ // 각 스키마는 OpenAI function calling 형식으로 변환되어 있음
445
+ debugLog(`[orchestrateMission] Adding ${mcpToolSchemas.length} MCP tools to available tools`);
446
+ tools.push(...mcpToolSchemas);
447
+ debugLog(`[orchestrateMission] Total tools after MCP: ${tools.length}`);
448
+
449
+ // Planner가 선언한 도구 이름을 실제 함수 스키마로 연결해, LLM이 지시한 작업이 즉시 실행되도록 합니다.
450
+ tools.forEach(tool => {
451
+ tool.type = "function";
452
+ });
453
+
454
+ const model = await getModelForProvider();
455
+ const isGpt5Model = model.startsWith("gpt-5");
456
+ debugLog(`[orchestrateMission] Using model: ${model}, isGpt5Model: ${isGpt5Model}`);
457
+
458
+ orchestratorRequestOptions = {
459
+ model,
460
+ isGpt5Model,
461
+ tools,
462
+ taskName,
463
+ parallel_tool_calls: true
464
+ };
465
+ debugLog(`[orchestrateMission] orchestratorRequestOptions configured with ${tools.length} tools`);
466
+
467
+ await ensureConversationInitialized();
468
+ debugLog(`[orchestrateMission] Conversation initialized, current length: ${orchestratorConversation.length}`);
469
+
470
+ debugLog(`[orchestrateMission] Adding user message to conversation: "${improvementPointsText.substring(0, 100)}"`);
471
+
472
+ // 캐싱 모델인 경우 사용자 메시지에도 캐시 제어 적용 (Claude Code 스타일)
473
+ const useCache = supportsCaching(model);
474
+ const userContentBlocks = [];
475
+
476
+ // Auto-generated 메시지의 경우 system-reminder 추가
477
+ if (isAutoGenerated) {
478
+ const autoGenReminder = createSystemReminder(
479
+ '이 메시지는 시스템에서 자동 생성되었습니다. 사용자가 직접 입력한 것이 아닙니다.',
480
+ { hideFromUser: true }
481
+ );
482
+ userContentBlocks.push({
483
+ type: "input_text",
484
+ text: autoGenReminder
485
+ });
486
+ }
487
+
488
+ // 메인 사용자 메시지
489
+ const mainContent = {
490
+ type: "input_text",
491
+ text: improvementPointsText
492
+ };
493
+
494
+ // 캐싱 모델의 경우 사용자 메시지에도 캐시 적용 (반복되는 긴 메시지에 효과적)
495
+ if (useCache && improvementPointsText.length > 500) {
496
+ mainContent.cache_control = { type: "ephemeral" };
497
+ debugLog(`[orchestrateMission] Applied cache control to user message (${improvementPointsText.length} chars)`);
498
+ }
499
+
500
+ userContentBlocks.push(mainContent);
501
+
502
+ const userMessage = {
503
+ role: "user",
504
+ content: userContentBlocks
505
+ };
506
+
507
+ // Auto-generated user message인 경우 _internal_only 플래그 추가
508
+ if (isAutoGenerated) {
509
+ userMessage._internal_only = true;
510
+ debugLog(`[_internal_only] Marked user message as internal-only (auto-generated from completion_judge)`);
511
+ }
512
+
513
+ orchestratorConversation.push(userMessage);
514
+ debugLog(`[orchestrateMission] Conversation length after adding user message: ${orchestratorConversation.length}`);
515
+
516
+ debugLog(`[orchestrateMission] Dispatching orchestrator request with toolChoice: required`);
517
+ const response = await dispatchOrchestratorRequest({ toolChoice: "required" });
518
+ debugLog(`[orchestrateMission] Received response, appending to conversation...`);
519
+ appendResponseToConversation(response);
520
+ debugLog(`[orchestrateMission] Conversation length after response: ${orchestratorConversation.length}`);
521
+
522
+ debugLog('========================================');
523
+ debugLog('===== orchestrateMission END ===========');
524
+ debugLog('========================================');
525
+ debugLog('');
526
+ return response;
527
+ }