aiexecode 1.0.65 → 1.0.68
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.
Potentially problematic release.
This version of aiexecode might be problematic. Click here for more details.
- package/README.md +17 -0
- package/config_template/settings.json +1 -3
- package/index.js +46 -71
- package/package.json +1 -12
- package/payload_viewer/out/404/index.html +1 -1
- package/payload_viewer/out/404.html +1 -1
- package/payload_viewer/out/index.html +1 -1
- package/payload_viewer/out/index.txt +1 -1
- package/payload_viewer/web_server.js +0 -163
- package/src/ai_based/completion_judge.js +96 -5
- package/src/ai_based/orchestrator.js +71 -3
- package/src/ai_based/pip_package_installer.js +14 -12
- package/src/ai_based/pip_package_lookup.js +13 -10
- package/src/commands/apikey.js +8 -34
- package/src/commands/help.js +3 -4
- package/src/commands/model.js +17 -74
- package/src/commands/reasoning_effort.js +1 -1
- package/src/config/feature_flags.js +0 -12
- package/src/{ui → frontend}/App.js +23 -25
- package/src/frontend/README.md +81 -0
- package/src/{ui/components/SuggestionsDisplay.js → frontend/components/AutocompleteMenu.js} +3 -3
- package/src/{ui/components/HistoryItemDisplay.js → frontend/components/ConversationItem.js} +37 -89
- package/src/{ui → frontend}/components/CurrentModelView.js +3 -5
- package/src/{ui → frontend}/components/Footer.js +4 -6
- package/src/{ui → frontend}/components/Header.js +2 -5
- package/src/{ui/components/InputPrompt.js → frontend/components/Input.js} +16 -54
- package/src/frontend/components/ModelListView.js +106 -0
- package/src/{ui → frontend}/components/ModelUpdatedView.js +3 -5
- package/src/{ui → frontend}/components/SessionSpinner.js +3 -3
- package/src/{ui → frontend}/components/SetupWizard.js +8 -101
- package/src/{ui → frontend}/components/ToolApprovalPrompt.js +16 -14
- package/src/frontend/design/themeColors.js +42 -0
- package/src/{ui → frontend}/index.js +7 -7
- package/src/frontend/utils/inputBuffer.js +441 -0
- package/src/{ui/utils/markdownRenderer.js → frontend/utils/markdownParser.js} +3 -3
- package/src/{ui/utils/ConsolePatcher.js → frontend/utils/outputRedirector.js} +9 -9
- package/src/{ui/utils/codeColorizer.js → frontend/utils/syntaxHighlighter.js} +2 -3
- package/src/system/ai_request.js +145 -595
- package/src/system/code_executer.js +111 -16
- package/src/system/file_integrity.js +5 -7
- package/src/system/log.js +3 -3
- package/src/system/mcp_integration.js +15 -13
- package/src/system/output_helper.js +0 -20
- package/src/system/session.js +97 -23
- package/src/system/session_memory.js +2 -82
- package/src/system/system_info.js +1 -1
- package/src/system/ui_events.js +0 -43
- package/src/tools/code_editor.js +17 -2
- package/src/tools/file_reader.js +17 -2
- package/src/tools/glob.js +9 -1
- package/src/tools/response_message.js +0 -2
- package/src/tools/ripgrep.js +9 -1
- package/src/tools/web_downloader.js +9 -1
- package/src/util/config.js +3 -8
- package/src/util/debug_log.js +4 -11
- package/src/util/mcp_config_manager.js +3 -5
- package/src/util/output_formatter.js +0 -47
- package/src/util/prompt_loader.js +3 -4
- package/src/util/safe_fs.js +60 -0
- package/src/util/setup_wizard.js +1 -3
- package/src/util/text_formatter.js +0 -86
- package/src/config/claude_models.js +0 -195
- package/src/ui/README.md +0 -208
- package/src/ui/api.js +0 -167
- package/src/ui/components/AgenticProgressDisplay.js +0 -126
- package/src/ui/components/Composer.js +0 -55
- package/src/ui/components/LoadingIndicator.js +0 -54
- package/src/ui/components/ModelListView.js +0 -214
- package/src/ui/components/Notifications.js +0 -55
- package/src/ui/components/StreamingIndicator.js +0 -36
- package/src/ui/contexts/AppContext.js +0 -25
- package/src/ui/contexts/StreamingContext.js +0 -20
- package/src/ui/contexts/UIStateContext.js +0 -117
- package/src/ui/example-usage.js +0 -180
- package/src/ui/hooks/useTerminalResize.js +0 -39
- package/src/ui/themes/semantic-tokens.js +0 -73
- package/src/ui/utils/text-buffer.js +0 -975
- /package/payload_viewer/out/_next/static/{pvY11afExc4r1kApy4_Gi → Z3AZSKhutj-kS4L8VpcOl}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{pvY11afExc4r1kApy4_Gi → Z3AZSKhutj-kS4L8VpcOl}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{pvY11afExc4r1kApy4_Gi → Z3AZSKhutj-kS4L8VpcOl}/_ssgManifest.js +0 -0
- /package/src/{ui → frontend}/components/BlankLine.js +0 -0
- /package/src/{ui → frontend}/components/FileDiffViewer.js +0 -0
- /package/src/{ui → frontend}/components/HelpView.js +0 -0
- /package/src/{ui → frontend}/hooks/useCompletion.js +0 -0
- /package/src/{ui → frontend}/hooks/useKeypress.js +0 -0
- /package/src/{ui → frontend}/utils/diffUtils.js +0 -0
- /package/src/{ui → frontend}/utils/renderInkComponent.js +0 -0
package/src/system/session.js
CHANGED
|
@@ -12,7 +12,7 @@ import { clampOutput, formatToolStdout } from "../util/output_formatter.js";
|
|
|
12
12
|
import { buildToolHistoryEntry } from "../util/rag_helper.js";
|
|
13
13
|
import { createSessionData, getLastConversationState, loadPreviousSessions, saveSessionToHistory } from "./session_memory.js";
|
|
14
14
|
import { uiEvents } from "./ui_events.js";
|
|
15
|
-
import { logSystem,
|
|
15
|
+
import { logSystem, logError, logAssistantMessage, logToolCall, logToolResult, logCodeExecution, logCodeResult, logIteration, logMissionComplete, logConversationRestored } from "./output_helper.js";
|
|
16
16
|
import { requiresApproval, requestApproval } from "./tool_approval.js";
|
|
17
17
|
import { setCurrentSession, saveFileSnapshot, getFileSnapshot } from "./file_integrity.js";
|
|
18
18
|
import { abortCurrentRequest } from "./ai_request.js";
|
|
@@ -110,23 +110,32 @@ function processMessageOutput(output, shouldOutput = false) {
|
|
|
110
110
|
* 일반 도구 호출을 처리하는 함수
|
|
111
111
|
*/
|
|
112
112
|
async function processToolCall(name, argument, toolMap, sessionID) {
|
|
113
|
+
debugLog(`[processToolCall] START - tool: ${name}`);
|
|
114
|
+
debugLog(`[processToolCall] Arguments: ${JSON.stringify(argument, null, 2)}`);
|
|
115
|
+
|
|
113
116
|
// edit_file_range, edit_file_replace인 경우 파일 스냅샷을 승인 요청 전에 저장
|
|
114
117
|
// (ToolApprovalPrompt에서 DiffViewer 표시를 위해 필요)
|
|
115
118
|
if ((name === 'edit_file_range' || name === 'edit_file_replace') && argument.file_path) {
|
|
116
119
|
try {
|
|
120
|
+
debugLog(`[processToolCall] Saving file snapshot for: ${argument.file_path}`);
|
|
117
121
|
const fileContent = await safeReadFile(argument.file_path, 'utf8');
|
|
118
122
|
saveFileSnapshot(argument.file_path, fileContent);
|
|
123
|
+
debugLog(`[processToolCall] Snapshot saved successfully (${fileContent.length} bytes)`);
|
|
119
124
|
} catch (error) {
|
|
125
|
+
debugLog(`[processToolCall] Snapshot save failed: ${error.message}`);
|
|
120
126
|
// 파일을 읽을 수 없는 경우 (존재하지 않는 파일 등)
|
|
121
127
|
// 스냅샷 저장 실패는 무시 (도구 실행에서 처리됨)
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
// 승인이 필요한 도구인지 확인 (args 전달하여 실패 여부 판단)
|
|
132
|
+
debugLog(`[processToolCall] Checking approval requirement...`);
|
|
126
133
|
const approvalCheck = await requiresApproval(name, argument);
|
|
134
|
+
debugLog(`[processToolCall] Approval check result: ${JSON.stringify(approvalCheck)}`);
|
|
127
135
|
|
|
128
136
|
// 승인이 스킵되는 경우 (확실히 실패할 것으로 예상) - 바로 에러 반환
|
|
129
137
|
if (approvalCheck && approvalCheck.skipApproval) {
|
|
138
|
+
debugLog(`[processToolCall] Approval skipped - tool will fail: ${approvalCheck.reason}`);
|
|
130
139
|
const errorResult = {
|
|
131
140
|
operation_successful: false,
|
|
132
141
|
error_message: approvalCheck.reason
|
|
@@ -138,6 +147,7 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
138
147
|
logToolCall(name, argument);
|
|
139
148
|
logToolResult(name, exeResult.stdout, errorResult, argument);
|
|
140
149
|
|
|
150
|
+
debugLog(`[processToolCall] END - skipped with error`);
|
|
141
151
|
return {
|
|
142
152
|
exeResult,
|
|
143
153
|
originalResult: errorResult,
|
|
@@ -147,7 +157,9 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
147
157
|
|
|
148
158
|
// 승인이 필요한 경우 - 승인 요청을 먼저 처리 (History에 표시되기 전)
|
|
149
159
|
if (approvalCheck === true) {
|
|
160
|
+
debugLog(`[processToolCall] Requesting user approval...`);
|
|
150
161
|
const approved = await requestApproval(name, argument);
|
|
162
|
+
debugLog(`[processToolCall] User approval result: ${approved}`);
|
|
151
163
|
if (!approved) {
|
|
152
164
|
const errorResult = {
|
|
153
165
|
operation_successful: false,
|
|
@@ -160,6 +172,7 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
160
172
|
logToolCall(name, argument);
|
|
161
173
|
logToolResult(name, exeResult.stdout, errorResult, argument);
|
|
162
174
|
|
|
175
|
+
debugLog(`[processToolCall] END - user denied`);
|
|
163
176
|
return {
|
|
164
177
|
exeResult,
|
|
165
178
|
originalResult: errorResult,
|
|
@@ -169,10 +182,12 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
// 승인 완료 후 또는 승인 불필요한 경우 - 도구 호출 메시지 출력
|
|
185
|
+
debugLog(`[processToolCall] Approval passed, executing tool...`);
|
|
172
186
|
logToolCall(name, argument);
|
|
173
187
|
|
|
174
188
|
// 도구가 toolMap에 없는 경우 처리
|
|
175
189
|
if (!toolMap[name] || typeof toolMap[name] !== 'function') {
|
|
190
|
+
debugLog(`[processToolCall] Tool not found in toolMap: ${name}`);
|
|
176
191
|
const errorResult = {
|
|
177
192
|
operation_successful: false,
|
|
178
193
|
error_message: `Tool '${name}' is not available. This tool may be disabled due to missing system dependencies.`
|
|
@@ -182,6 +197,7 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
182
197
|
|
|
183
198
|
logToolResult(name, exeResult.stdout, errorResult, argument);
|
|
184
199
|
|
|
200
|
+
debugLog(`[processToolCall] END - tool not available`);
|
|
185
201
|
return {
|
|
186
202
|
exeResult,
|
|
187
203
|
originalResult: errorResult,
|
|
@@ -190,7 +206,10 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
190
206
|
}
|
|
191
207
|
|
|
192
208
|
const argumentWithSession = { ...argument, sessionID };
|
|
209
|
+
debugLog(`[processToolCall] Calling tool function...`);
|
|
193
210
|
const result = await toolMap[name](argumentWithSession);
|
|
211
|
+
debugLog(`[processToolCall] Tool execution completed`);
|
|
212
|
+
debugLog(`[processToolCall] Result type: ${result?.constructor?.name}, keys: ${Object.keys(result || {}).join(', ')}`);
|
|
194
213
|
|
|
195
214
|
if (result.constructor !== Object) {
|
|
196
215
|
logError('Tool result is not an object');
|
|
@@ -199,12 +218,14 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
199
218
|
|
|
200
219
|
const stdout = formatToolStdout(name, result);
|
|
201
220
|
const exeResult = prepareExecutionResult(stdout, '', 0);
|
|
221
|
+
debugLog(`[processToolCall] Formatted stdout length: ${stdout.length} bytes`);
|
|
202
222
|
|
|
203
223
|
// edit_file_range인 경우 스냅샷도 함께 전달
|
|
204
224
|
let snapshotForLog = null;
|
|
205
225
|
if (name === 'edit_file_range' && argument.file_path) {
|
|
206
226
|
const snapshot = getFileSnapshot(argument.file_path);
|
|
207
227
|
if (snapshot) {
|
|
228
|
+
debugLog(`[processToolCall] Retrieved snapshot for log: ${snapshot.content.length} bytes`);
|
|
208
229
|
snapshotForLog = {
|
|
209
230
|
content: snapshot.content,
|
|
210
231
|
timestamp: snapshot.timestamp
|
|
@@ -214,6 +235,7 @@ async function processToolCall(name, argument, toolMap, sessionID) {
|
|
|
214
235
|
|
|
215
236
|
logToolResult(name, exeResult.stdout, result, argument, snapshotForLog);
|
|
216
237
|
|
|
238
|
+
debugLog(`[processToolCall] END - success`);
|
|
217
239
|
return {
|
|
218
240
|
exeResult,
|
|
219
241
|
originalResult: result, // 원본 도구 결과 반환
|
|
@@ -338,6 +360,7 @@ async function processOrchestratorResponses(params) {
|
|
|
338
360
|
let missionSolved = false;
|
|
339
361
|
let improvementPoints = '';
|
|
340
362
|
let improvementPointsIsAutoGenerated = false;
|
|
363
|
+
let completionJudgeExecuted = false;
|
|
341
364
|
|
|
342
365
|
debugLog(`Starting response processing loop...`);
|
|
343
366
|
let loopIteration = 0;
|
|
@@ -589,6 +612,7 @@ async function processOrchestratorResponses(params) {
|
|
|
589
612
|
judgement = await judgeMissionCompletion({
|
|
590
613
|
what_user_requests: mission
|
|
591
614
|
});
|
|
615
|
+
completionJudgeExecuted = true; // completion_judge 실행 완료
|
|
592
616
|
} catch (err) {
|
|
593
617
|
if (err.name === 'AbortError' || sessionInterrupted) {
|
|
594
618
|
debugLog(`judgeMissionCompletion aborted or interrupted: ${err.name}`);
|
|
@@ -695,6 +719,7 @@ async function processOrchestratorResponses(params) {
|
|
|
695
719
|
debugLog(` missionSolved: ${missionSolved}`);
|
|
696
720
|
debugLog(` improvementPoints: ${improvementPoints}`);
|
|
697
721
|
debugLog(` improvementPointsIsAutoGenerated: ${improvementPointsIsAutoGenerated}`);
|
|
722
|
+
debugLog(` completionJudgeExecuted: ${completionJudgeExecuted}`);
|
|
698
723
|
debugLog(` Total loop iterations: ${loopIteration}`);
|
|
699
724
|
debugLog(` Final toolUsageHistory: ${toolUsageHistory.length} items`);
|
|
700
725
|
|
|
@@ -703,7 +728,8 @@ async function processOrchestratorResponses(params) {
|
|
|
703
728
|
stallDetected,
|
|
704
729
|
missionSolved,
|
|
705
730
|
improvementPoints,
|
|
706
|
-
improvementPointsIsAutoGenerated
|
|
731
|
+
improvementPointsIsAutoGenerated,
|
|
732
|
+
completionJudgeExecuted
|
|
707
733
|
};
|
|
708
734
|
}
|
|
709
735
|
|
|
@@ -738,6 +764,13 @@ let sessionInterrupted = false;
|
|
|
738
764
|
* @returns {Promise<AgentLoopResult>} 실행 결과
|
|
739
765
|
*/
|
|
740
766
|
export async function runSession(options) {
|
|
767
|
+
debugLog('========================================');
|
|
768
|
+
debugLog('========== runSession START ==========');
|
|
769
|
+
debugLog('========================================');
|
|
770
|
+
debugLog(`Options: ${JSON.stringify(Object.keys(options))}`);
|
|
771
|
+
debugLog(`Mission: ${options.mission?.substring(0, 200)}${options.mission?.length > 200 ? '...' : ''}`);
|
|
772
|
+
debugLog(`MaxIterations: ${options.maxIterations || DEFAULT_MAX_ITERATIONS}`);
|
|
773
|
+
|
|
741
774
|
// 세션 중단 플래그 초기화
|
|
742
775
|
sessionInterrupted = false;
|
|
743
776
|
|
|
@@ -747,6 +780,7 @@ export async function runSession(options) {
|
|
|
747
780
|
sessionInterrupted = true;
|
|
748
781
|
abortCurrentRequest(); // API 요청 즉시 중단
|
|
749
782
|
uiEvents.addErrorMessage('Session interrupted by user');
|
|
783
|
+
debugLog('[runSession] Session interrupted by user');
|
|
750
784
|
}
|
|
751
785
|
};
|
|
752
786
|
uiEvents.on('session:interrupt', handleInterrupt);
|
|
@@ -764,6 +798,9 @@ export async function runSession(options) {
|
|
|
764
798
|
previousSessions = null
|
|
765
799
|
} = options;
|
|
766
800
|
|
|
801
|
+
debugLog(`MCP tool schemas count: ${mcpToolSchemas.length}`);
|
|
802
|
+
debugLog(`MCP tool functions count: ${Object.keys(mcpToolFunctions).length}`);
|
|
803
|
+
|
|
767
804
|
// previousSessions가 제공되지 않은 경우 자동으로 로드
|
|
768
805
|
const sessionsToUse = previousSessions ?? await loadPreviousSessions(process.app_custom.sessionID);
|
|
769
806
|
|
|
@@ -835,8 +872,14 @@ export async function runSession(options) {
|
|
|
835
872
|
}
|
|
836
873
|
|
|
837
874
|
// 메인 루프
|
|
875
|
+
debugLog(`[runSession] Starting main loop - maxIterations: ${maxIterations}`);
|
|
838
876
|
while (!mission_solved && iteration_count < maxIterations && !sessionInterrupted) {
|
|
839
877
|
iteration_count++;
|
|
878
|
+
debugLog('');
|
|
879
|
+
debugLog('========================================');
|
|
880
|
+
debugLog(`========== ITERATION ${iteration_count} START ==========`);
|
|
881
|
+
debugLog('========================================');
|
|
882
|
+
debugLog(`Current state - mission_solved: ${mission_solved}, sessionInterrupted: ${sessionInterrupted}`);
|
|
840
883
|
logIteration(iteration_count);
|
|
841
884
|
|
|
842
885
|
// 첫 반복에서 improvement_points 설정
|
|
@@ -874,10 +917,10 @@ export async function runSession(options) {
|
|
|
874
917
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
875
918
|
currentSessionData.orchestratorRequestOptions = null;
|
|
876
919
|
|
|
877
|
-
debugLog(`[ITERATION ${iteration_count}] Session aborted during orchestration - saving session to history`);
|
|
878
|
-
await saveSessionToHistory(currentSessionData).catch(saveErr => {
|
|
879
|
-
|
|
880
|
-
});
|
|
920
|
+
debugLog(`[ITERATION ${iteration_count}] Session aborted during orchestration - NOT saving session to history (disabled)`);
|
|
921
|
+
// await saveSessionToHistory(currentSessionData).catch(saveErr => {
|
|
922
|
+
// debugLog(`[ITERATION ${iteration_count}] Failed to save session: ${saveErr.message}`);
|
|
923
|
+
// });
|
|
881
924
|
break;
|
|
882
925
|
}
|
|
883
926
|
throw err;
|
|
@@ -897,10 +940,10 @@ export async function runSession(options) {
|
|
|
897
940
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
898
941
|
currentSessionData.orchestratorRequestOptions = null;
|
|
899
942
|
|
|
900
|
-
debugLog(`[ITERATION ${iteration_count}] Session interrupted after orchestration - saving session to history`);
|
|
901
|
-
await saveSessionToHistory(currentSessionData).catch(err => {
|
|
902
|
-
|
|
903
|
-
});
|
|
943
|
+
debugLog(`[ITERATION ${iteration_count}] Session interrupted after orchestration - NOT saving session to history (disabled)`);
|
|
944
|
+
// await saveSessionToHistory(currentSessionData).catch(err => {
|
|
945
|
+
// debugLog(`[ITERATION ${iteration_count}] Failed to save session: ${err.message}`);
|
|
946
|
+
// });
|
|
904
947
|
break;
|
|
905
948
|
}
|
|
906
949
|
|
|
@@ -937,6 +980,21 @@ export async function runSession(options) {
|
|
|
937
980
|
|
|
938
981
|
debugLog(`[ITERATION ${iteration_count}] orchestratorResult.improvementPoints: "${orchestratorResult.improvementPoints || '(empty)'}", isAutoGenerated: ${orchestratorResult.improvementPointsIsAutoGenerated}`);
|
|
939
982
|
|
|
983
|
+
// completion_judge가 완료된 직후 세션 저장 (mission_solved 상태와 무관하게 무조건 저장)
|
|
984
|
+
if (orchestratorResult.completionJudgeExecuted) {
|
|
985
|
+
currentSessionData.completed_at = new Date().toISOString();
|
|
986
|
+
currentSessionData.mission_solved = mission_solved;
|
|
987
|
+
currentSessionData.iteration_count = iteration_count;
|
|
988
|
+
currentSessionData.toolUsageHistory = toolUsageHistory;
|
|
989
|
+
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
990
|
+
currentSessionData.orchestratorRequestOptions = null;
|
|
991
|
+
|
|
992
|
+
debugLog(`[ITERATION ${iteration_count}] Saving session to history after completion_judge (mission_solved=${mission_solved})`);
|
|
993
|
+
await saveSessionToHistory(currentSessionData).catch(err => {
|
|
994
|
+
debugLog(`[ITERATION ${iteration_count}] Failed to save session after completion_judge: ${err.message}`);
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
|
|
940
998
|
// improvementPoints가 반환된 경우 다음 iteration에 사용
|
|
941
999
|
// 반환되지 않은 경우 리셋
|
|
942
1000
|
if (orchestratorResult.improvementPoints) {
|
|
@@ -959,14 +1017,14 @@ export async function runSession(options) {
|
|
|
959
1017
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
960
1018
|
currentSessionData.orchestratorRequestOptions = null;
|
|
961
1019
|
|
|
962
|
-
debugLog(`[ITERATION ${iteration_count}] Session interrupted - saving session to history`);
|
|
963
|
-
await saveSessionToHistory(currentSessionData).catch(err => {
|
|
964
|
-
|
|
965
|
-
});
|
|
1020
|
+
debugLog(`[ITERATION ${iteration_count}] Session interrupted - NOT saving session to history (disabled)`);
|
|
1021
|
+
// await saveSessionToHistory(currentSessionData).catch(err => {
|
|
1022
|
+
// debugLog(`[ITERATION ${iteration_count}] Failed to save session: ${err.message}`);
|
|
1023
|
+
// });
|
|
966
1024
|
break;
|
|
967
1025
|
}
|
|
968
1026
|
|
|
969
|
-
// 매 iteration마다 세션 히스토리 저장 (실시간 저장)
|
|
1027
|
+
// 매 iteration마다 세션 히스토리 저장 (실시간 저장) - DISABLED
|
|
970
1028
|
currentSessionData.completed_at = new Date().toISOString();
|
|
971
1029
|
currentSessionData.mission_solved = mission_solved;
|
|
972
1030
|
currentSessionData.iteration_count = iteration_count;
|
|
@@ -974,13 +1032,17 @@ export async function runSession(options) {
|
|
|
974
1032
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
975
1033
|
currentSessionData.orchestratorRequestOptions = null;
|
|
976
1034
|
|
|
977
|
-
debugLog(`[ITERATION ${iteration_count}]
|
|
978
|
-
await saveSessionToHistory(currentSessionData).catch(err => {
|
|
979
|
-
|
|
980
|
-
});
|
|
1035
|
+
debugLog(`[ITERATION ${iteration_count}] NOT saving session to history (disabled - will only save after completion_judge)`);
|
|
1036
|
+
// await saveSessionToHistory(currentSessionData).catch(err => {
|
|
1037
|
+
// debugLog(`[ITERATION ${iteration_count}] Failed to save session: ${err.message}`);
|
|
1038
|
+
// });
|
|
981
1039
|
|
|
982
1040
|
// 미션 완료 처리
|
|
983
1041
|
if (mission_solved) {
|
|
1042
|
+
debugLog(`[ITERATION ${iteration_count}] Mission solved, breaking loop`);
|
|
1043
|
+
debugLog('========================================');
|
|
1044
|
+
debugLog(`========== ITERATION ${iteration_count} END ==========`);
|
|
1045
|
+
debugLog('========================================');
|
|
984
1046
|
break;
|
|
985
1047
|
}
|
|
986
1048
|
|
|
@@ -991,11 +1053,18 @@ export async function runSession(options) {
|
|
|
991
1053
|
? '⚠ Session ended due to orchestrator stall'
|
|
992
1054
|
: '✅ No function calls detected - mission complete';
|
|
993
1055
|
uiEvents.addSystemMessage(message);
|
|
1056
|
+
debugLog(`[ITERATION ${iteration_count}] ${message}`);
|
|
1057
|
+
debugLog('========================================');
|
|
1058
|
+
debugLog(`========== ITERATION ${iteration_count} END ==========`);
|
|
1059
|
+
debugLog('========================================');
|
|
994
1060
|
break;
|
|
995
1061
|
}
|
|
996
1062
|
|
|
997
1063
|
// improvement_points가 설정되어 있으면 다음 iteration 계속 진행
|
|
998
1064
|
debugLog(`[ITERATION ${iteration_count}] Continuing with improvement_points: "${improvement_points?.substring(0, 100) || '(empty)'}"`);
|
|
1065
|
+
debugLog('========================================');
|
|
1066
|
+
debugLog(`========== ITERATION ${iteration_count} END ==========`);
|
|
1067
|
+
debugLog('========================================');
|
|
999
1068
|
continue;
|
|
1000
1069
|
}
|
|
1001
1070
|
|
|
@@ -1024,11 +1093,16 @@ export async function runSession(options) {
|
|
|
1024
1093
|
currentSessionData.orchestratorConversation = result.orchestratorConversation;
|
|
1025
1094
|
currentSessionData.orchestratorRequestOptions = null; // 필요시 orchestrator에서 가져올 수 있음
|
|
1026
1095
|
|
|
1027
|
-
debugLog(`[runSession]
|
|
1096
|
+
debugLog(`[runSession] NOT saving final session to history (disabled - will only save after completion_judge) - sessionID: ${currentSessionData.sessionID}`);
|
|
1028
1097
|
debugLog(`[runSession] Session data size: ${JSON.stringify(currentSessionData).length} bytes`);
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1098
|
+
debugLog(`[runSession] Final state - mission_solved: ${currentSessionData.mission_solved}, iteration_count: ${currentSessionData.iteration_count}`);
|
|
1099
|
+
debugLog(`[runSession] Tool usage history entries: ${currentSessionData.toolUsageHistory.length}`);
|
|
1100
|
+
// 최종 세션 히스토리에 저장 (중복 저장이지만 최신 상태 보장) - DISABLED
|
|
1101
|
+
// await saveSessionToHistory(currentSessionData);
|
|
1102
|
+
|
|
1103
|
+
debugLog('========================================');
|
|
1104
|
+
debugLog('========== runSession END ==========');
|
|
1105
|
+
debugLog('========================================');
|
|
1032
1106
|
return currentSessionData;
|
|
1033
1107
|
} catch (error) {
|
|
1034
1108
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -26,8 +26,6 @@ async function ensureHistoryDir(sessionID) {
|
|
|
26
26
|
await safeMkdir(sessionDir, { recursive: true });
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function consolelog() { }
|
|
30
|
-
|
|
31
29
|
/**
|
|
32
30
|
* 세션 히스토리 파일을 삭제
|
|
33
31
|
* @param {string} sessionID - 세션 ID
|
|
@@ -54,11 +52,11 @@ export async function loadPreviousSessions(sessionID) {
|
|
|
54
52
|
if (await safeExists(historyFile)) {
|
|
55
53
|
const content = await safeReadFile(historyFile, 'utf8');
|
|
56
54
|
const sessions = JSON.parse(content);
|
|
57
|
-
|
|
55
|
+
debugLog(`Loaded ${sessions.length} previous sessions from history`);
|
|
58
56
|
return sessions;
|
|
59
57
|
}
|
|
60
58
|
} catch (err) {
|
|
61
|
-
|
|
59
|
+
debugLog(`Could not load previous sessions: ${err.message}`);
|
|
62
60
|
}
|
|
63
61
|
return [];
|
|
64
62
|
}
|
|
@@ -114,84 +112,6 @@ export async function saveSessionToHistory(sessionData) {
|
|
|
114
112
|
return null;
|
|
115
113
|
}
|
|
116
114
|
|
|
117
|
-
/**
|
|
118
|
-
* 최근 N개의 세션만 추출
|
|
119
|
-
* @param {Array} sessions - 전체 세션 배열
|
|
120
|
-
* @param {number} count - 가져올 개수
|
|
121
|
-
* @returns {Array} 최근 N개 세션
|
|
122
|
-
*/
|
|
123
|
-
export function getRecentSessions(sessions, count = 5) {
|
|
124
|
-
return sessions.slice(-count);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* 현재 미션과 관련된 세션들을 필터링
|
|
129
|
-
* @param {Array} sessions - 전체 세션 배열
|
|
130
|
-
* @param {string} currentMission - 현재 미션
|
|
131
|
-
* @returns {Array} 관련 세션들
|
|
132
|
-
*/
|
|
133
|
-
export function getRelatedSessions(sessions, currentMission) {
|
|
134
|
-
const keywords = currentMission.toLowerCase().split(/\s+/);
|
|
135
|
-
|
|
136
|
-
return sessions.filter(session => {
|
|
137
|
-
const sessionMission = (session.mission || '').toLowerCase();
|
|
138
|
-
// 키워드 중 하나라도 매칭되면 관련 세션으로 판단
|
|
139
|
-
return keywords.some(keyword =>
|
|
140
|
-
keyword.length > 2 && sessionMission.includes(keyword)
|
|
141
|
-
);
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* 이전 세션들을 AI가 이해할 수 있는 컨텍스트로 변환
|
|
147
|
-
* @param {Array} sessions - 세션 배열
|
|
148
|
-
* @param {string} currentMission - 현재 미션
|
|
149
|
-
* @returns {string} AI용 컨텍스트 문자열
|
|
150
|
-
*/
|
|
151
|
-
export function buildHistoricalContext(sessions, currentMission) {
|
|
152
|
-
if (!sessions || sessions.length === 0) {
|
|
153
|
-
return '';
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// 현재 미션과 관련된 세션 + 최근 세션 조합
|
|
157
|
-
const relatedSessions = getRelatedSessions(sessions, currentMission);
|
|
158
|
-
const recentSessions = getRecentSessions(sessions, 3);
|
|
159
|
-
|
|
160
|
-
// 중복 제거하면서 합치기
|
|
161
|
-
const sessionSet = new Set([...relatedSessions, ...recentSessions]);
|
|
162
|
-
const relevantSessions = Array.from(sessionSet).slice(-5); // 최대 5개만
|
|
163
|
-
|
|
164
|
-
if (relevantSessions.length === 0) {
|
|
165
|
-
return '';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const contextParts = [
|
|
169
|
-
'# Previous Execution History',
|
|
170
|
-
`You have access to ${relevantSessions.length} previous session(s) that may be relevant:`,
|
|
171
|
-
''
|
|
172
|
-
];
|
|
173
|
-
|
|
174
|
-
relevantSessions.forEach((session, idx) => {
|
|
175
|
-
contextParts.push(`## Session ${idx + 1} (${session.started_at || 'unknown time'})`);
|
|
176
|
-
contextParts.push(`**Mission**: ${session.mission}`);
|
|
177
|
-
contextParts.push(`**Status**: ${session.mission_solved ? '✅ Completed' : '❌ Not completed'}`);
|
|
178
|
-
contextParts.push(`**Iterations**: ${session.iteration_count || 0}`);
|
|
179
|
-
|
|
180
|
-
// 도구 사용 통계
|
|
181
|
-
if (session.toolUsageHistory && session.toolUsageHistory.length > 0) {
|
|
182
|
-
const toolCounts = {};
|
|
183
|
-
session.toolUsageHistory.forEach(tool => {
|
|
184
|
-
toolCounts[tool.toolName] = (toolCounts[tool.toolName] || 0) + 1;
|
|
185
|
-
});
|
|
186
|
-
contextParts.push(`**Tools Used**: ${Object.entries(toolCounts).map(([name, count]) => `${name}(${count})`).join(', ')}`);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
contextParts.push('');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
return contextParts.join('\n');
|
|
193
|
-
}
|
|
194
|
-
|
|
195
115
|
/**
|
|
196
116
|
* 현재 세션 데이터 구조 생성
|
|
197
117
|
* @param {string} sessionID - 세션 ID
|
package/src/system/ui_events.js
CHANGED
|
@@ -106,27 +106,6 @@ class UIEventEmitter extends EventEmitter {
|
|
|
106
106
|
});
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* 검증 시작 알림
|
|
111
|
-
*/
|
|
112
|
-
startVerification() {
|
|
113
|
-
this.emit('history:add', {
|
|
114
|
-
type: 'verification_start',
|
|
115
|
-
timestamp: Date.now()
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* 검증 결과
|
|
121
|
-
*/
|
|
122
|
-
addVerificationResult(result) {
|
|
123
|
-
this.emit('history:add', {
|
|
124
|
-
type: 'verification_result',
|
|
125
|
-
result,
|
|
126
|
-
timestamp: Date.now()
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
|
|
130
109
|
/**
|
|
131
110
|
* 미션 완료
|
|
132
111
|
*/
|
|
@@ -138,28 +117,6 @@ class UIEventEmitter extends EventEmitter {
|
|
|
138
117
|
});
|
|
139
118
|
}
|
|
140
119
|
|
|
141
|
-
/**
|
|
142
|
-
* 미션 실패
|
|
143
|
-
*/
|
|
144
|
-
missionFailed(reason) {
|
|
145
|
-
this.emit('history:add', {
|
|
146
|
-
type: 'mission_failed',
|
|
147
|
-
reason,
|
|
148
|
-
timestamp: Date.now()
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* RAG 검색 결과
|
|
154
|
-
*/
|
|
155
|
-
addRagSearch(contextLength) {
|
|
156
|
-
this.emit('history:add', {
|
|
157
|
-
type: 'rag_search',
|
|
158
|
-
contextLength,
|
|
159
|
-
timestamp: Date.now()
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
120
|
/**
|
|
164
121
|
* 대화 복원 알림
|
|
165
122
|
*/
|
package/src/tools/code_editor.js
CHANGED
|
@@ -5,6 +5,7 @@ import { assertFileIntegrity, trackFileRead, saveFileSnapshot } from '../system/
|
|
|
5
5
|
import { createDebugLogger } from '../util/debug_log.js';
|
|
6
6
|
import { DEBUG_LOG_DIR } from '../util/config.js';
|
|
7
7
|
import { toDisplayPath } from '../util/path_helper.js';
|
|
8
|
+
import { theme } from '../frontend/design/themeColors.js';
|
|
8
9
|
|
|
9
10
|
const debugLog = createDebugLogger('code_editor.log', 'code_editor');
|
|
10
11
|
|
|
@@ -231,7 +232,14 @@ export const writeFileSchema = {
|
|
|
231
232
|
if (result.operation_successful) {
|
|
232
233
|
const lines = result.total_line_count || 0;
|
|
233
234
|
const action = result.file_existed ? 'Overwrote' : 'Created';
|
|
234
|
-
return
|
|
235
|
+
return {
|
|
236
|
+
type: 'formatted',
|
|
237
|
+
parts: [
|
|
238
|
+
{ text: `${action} `, style: {} },
|
|
239
|
+
{ text: String(lines), style: { color: theme.brand.light, bold: true } },
|
|
240
|
+
{ text: ` line${lines !== 1 ? 's' : ''}`, style: {} }
|
|
241
|
+
]
|
|
242
|
+
};
|
|
235
243
|
}
|
|
236
244
|
return result.error_message || 'Error writing file';
|
|
237
245
|
}
|
|
@@ -704,7 +712,14 @@ export const editFileReplaceSchema = {
|
|
|
704
712
|
"format_tool_result": (result) => {
|
|
705
713
|
if (result.operation_successful) {
|
|
706
714
|
const count = result.replacement_count || 0;
|
|
707
|
-
return
|
|
715
|
+
return {
|
|
716
|
+
type: 'formatted',
|
|
717
|
+
parts: [
|
|
718
|
+
{ text: 'Replaced ', style: {} },
|
|
719
|
+
{ text: String(count), style: { color: theme.brand.light, bold: true } },
|
|
720
|
+
{ text: ` occurrence${count !== 1 ? 's' : ''}`, style: {} }
|
|
721
|
+
]
|
|
722
|
+
};
|
|
708
723
|
}
|
|
709
724
|
return result.error_message || 'Error replacing string';
|
|
710
725
|
}
|
package/src/tools/file_reader.js
CHANGED
|
@@ -3,6 +3,7 @@ import { resolve } from 'path';
|
|
|
3
3
|
import { trackFileRead, saveFileSnapshot } from '../system/file_integrity.js';
|
|
4
4
|
import { createDebugLogger } from '../util/debug_log.js';
|
|
5
5
|
import { toDisplayPath } from '../util/path_helper.js';
|
|
6
|
+
import { theme } from '../frontend/design/themeColors.js';
|
|
6
7
|
|
|
7
8
|
const debugLog = createDebugLogger('file_reader.log', 'file_reader');
|
|
8
9
|
|
|
@@ -128,7 +129,14 @@ export const readFileSchema = {
|
|
|
128
129
|
"format_tool_result": (result) => {
|
|
129
130
|
if (result.operation_successful) {
|
|
130
131
|
const lines = result.total_line_count || 0;
|
|
131
|
-
return
|
|
132
|
+
return {
|
|
133
|
+
type: 'formatted',
|
|
134
|
+
parts: [
|
|
135
|
+
{ text: 'Read ', style: {} },
|
|
136
|
+
{ text: String(lines), style: { color: theme.brand.light, bold: true } },
|
|
137
|
+
{ text: ` line${lines !== 1 ? 's' : ''}`, style: {} }
|
|
138
|
+
]
|
|
139
|
+
};
|
|
132
140
|
}
|
|
133
141
|
return result.error_message || 'Error reading file';
|
|
134
142
|
}
|
|
@@ -288,7 +296,14 @@ export const readFileRangeSchema = {
|
|
|
288
296
|
"format_tool_result": (result) => {
|
|
289
297
|
if (result.operation_successful) {
|
|
290
298
|
const lineCount = result.file_lines?.length || 0;
|
|
291
|
-
return
|
|
299
|
+
return {
|
|
300
|
+
type: 'formatted',
|
|
301
|
+
parts: [
|
|
302
|
+
{ text: 'Read ', style: {} },
|
|
303
|
+
{ text: String(lineCount), style: { color: theme.brand.light, bold: true } },
|
|
304
|
+
{ text: ` line${lineCount !== 1 ? 's' : ''}`, style: {} }
|
|
305
|
+
]
|
|
306
|
+
};
|
|
292
307
|
}
|
|
293
308
|
return result.error_message || 'Error reading file';
|
|
294
309
|
}
|
package/src/tools/glob.js
CHANGED
|
@@ -2,6 +2,7 @@ import { glob } from 'glob';
|
|
|
2
2
|
import { resolve, relative } from 'path';
|
|
3
3
|
import { safeStat } from '../util/safe_fs.js';
|
|
4
4
|
import { createDebugLogger } from '../util/debug_log.js';
|
|
5
|
+
import { theme } from '../frontend/design/themeColors.js';
|
|
5
6
|
|
|
6
7
|
const debugLog = createDebugLogger('glob.log', 'glob');
|
|
7
8
|
|
|
@@ -241,7 +242,14 @@ export const globSearchSchema = {
|
|
|
241
242
|
format_tool_result: (result) => {
|
|
242
243
|
if (result.operation_successful) {
|
|
243
244
|
const matches = result.total_matches || 0;
|
|
244
|
-
return
|
|
245
|
+
return {
|
|
246
|
+
type: 'formatted',
|
|
247
|
+
parts: [
|
|
248
|
+
{ text: 'Found ', style: {} },
|
|
249
|
+
{ text: String(matches), style: { color: theme.brand.light, bold: true } },
|
|
250
|
+
{ text: ` match${matches !== 1 ? 'es' : ''}`, style: {} }
|
|
251
|
+
]
|
|
252
|
+
};
|
|
245
253
|
}
|
|
246
254
|
return result.error_message || 'Search failed';
|
|
247
255
|
}
|
package/src/tools/ripgrep.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
+
import { theme } from '../frontend/design/themeColors.js';
|
|
2
3
|
|
|
3
4
|
// 파일 내용 검색, 파일 경로 필터링, 다양한 출력 모드 등을 지원합니다.
|
|
4
5
|
|
|
@@ -441,7 +442,14 @@ export const ripgrepSchema = {
|
|
|
441
442
|
format_tool_result: (result) => {
|
|
442
443
|
if (result.operation_successful) {
|
|
443
444
|
const matches = result.totalMatches || 0;
|
|
444
|
-
return
|
|
445
|
+
return {
|
|
446
|
+
type: 'formatted',
|
|
447
|
+
parts: [
|
|
448
|
+
{ text: 'Found ', style: {} },
|
|
449
|
+
{ text: String(matches), style: { color: theme.brand.light, bold: true } },
|
|
450
|
+
{ text: ` match${matches !== 1 ? 'es' : ''}`, style: {} }
|
|
451
|
+
]
|
|
452
|
+
};
|
|
445
453
|
}
|
|
446
454
|
return result.error_message || 'Search failed';
|
|
447
455
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execPythonCode } from "../system/code_executer.js";
|
|
2
2
|
import { safeReadFile } from '../util/safe_fs.js';
|
|
3
3
|
import { join } from 'path';
|
|
4
|
+
import { theme } from '../frontend/design/themeColors.js';
|
|
4
5
|
|
|
5
6
|
// 이 파일은 웹 페이지를 가져와서 텍스트 형식으로 변환하는 도구를 제공합니다.
|
|
6
7
|
// 외부 참고 자료가 필요한 미션일 때 Orchestrator가 지식을 확보하고 Verifier가 근거를 남길 수 있게 보조합니다.
|
|
@@ -60,7 +61,14 @@ export const fetchWebPageSchema = {
|
|
|
60
61
|
"format_tool_result": (result) => {
|
|
61
62
|
if (result.operation_successful) {
|
|
62
63
|
const contentLength = result.content?.length || 0;
|
|
63
|
-
return
|
|
64
|
+
return {
|
|
65
|
+
type: 'formatted',
|
|
66
|
+
parts: [
|
|
67
|
+
{ text: 'Fetched ', style: {} },
|
|
68
|
+
{ text: String(contentLength), style: { color: theme.brand.light, bold: true } },
|
|
69
|
+
{ text: ' characters', style: {} }
|
|
70
|
+
]
|
|
71
|
+
};
|
|
64
72
|
}
|
|
65
73
|
return result.error_message || 'Fetch failed';
|
|
66
74
|
}
|