aiexecode 1.0.126 → 1.0.128
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.
- package/index.js +134 -20
- package/package.json +1 -1
- 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/prompts/completion_judge.txt +4 -0
- package/prompts/orchestrator.txt +59 -0
- package/src/ai_based/orchestrator.js +5 -2
- package/src/commands/bg.js +129 -0
- package/src/frontend/App.js +100 -1
- package/src/frontend/components/BackgroundProcessList.js +175 -0
- package/src/system/ai_request.js +0 -19
- package/src/system/background_process.js +317 -0
- package/src/system/code_executer.js +487 -57
- package/src/system/output_helper.js +52 -9
- package/src/system/session.js +89 -10
- package/src/system/session_memory.js +2 -1
- package/src/util/exit_handler.js +8 -0
- /package/payload_viewer/out/_next/static/{Ciog50_gZfMGwKNqVaI0v → s1c0-hQ_HEGmr_04DEOse}/_buildManifest.js +0 -0
- /package/payload_viewer/out/_next/static/{Ciog50_gZfMGwKNqVaI0v → s1c0-hQ_HEGmr_04DEOse}/_clientMiddlewareManifest.json +0 -0
- /package/payload_viewer/out/_next/static/{Ciog50_gZfMGwKNqVaI0v → s1c0-hQ_HEGmr_04DEOse}/_ssgManifest.js +0 -0
|
@@ -1,35 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Output Helper - UI 이벤트 발생
|
|
3
|
+
* Pipe mode에서는 stderr로 출력하거나 무시
|
|
3
4
|
*/
|
|
4
5
|
|
|
5
6
|
import { uiEvents } from './ui_events.js';
|
|
6
7
|
|
|
8
|
+
// Pipe mode 감지
|
|
9
|
+
const isPipeMode = () => process.app_custom?.pipeMode === true;
|
|
10
|
+
|
|
7
11
|
export function logSystem(message) {
|
|
8
|
-
|
|
12
|
+
if (isPipeMode()) {
|
|
13
|
+
console.error(`[SYSTEM] ${message}`);
|
|
14
|
+
} else {
|
|
15
|
+
uiEvents.addSystemMessage(message);
|
|
16
|
+
}
|
|
9
17
|
}
|
|
10
18
|
|
|
11
19
|
export function logError(message) {
|
|
12
|
-
|
|
20
|
+
if (isPipeMode()) {
|
|
21
|
+
console.error(`[ERROR] ${message}`);
|
|
22
|
+
} else {
|
|
23
|
+
uiEvents.addErrorMessage(message);
|
|
24
|
+
}
|
|
13
25
|
}
|
|
14
26
|
|
|
15
27
|
export function logAssistantMessage(message) {
|
|
16
|
-
|
|
28
|
+
if (isPipeMode()) {
|
|
29
|
+
console.error(`[ASSISTANT] ${message}`);
|
|
30
|
+
} else {
|
|
31
|
+
uiEvents.addAssistantMessage(message);
|
|
32
|
+
}
|
|
17
33
|
}
|
|
18
34
|
|
|
19
35
|
export function logToolCall(toolName, args) {
|
|
20
|
-
|
|
36
|
+
if (isPipeMode()) {
|
|
37
|
+
console.error(`[TOOL] ${toolName}`);
|
|
38
|
+
} else {
|
|
39
|
+
uiEvents.startToolExecution(toolName, args);
|
|
40
|
+
}
|
|
21
41
|
}
|
|
22
42
|
|
|
23
43
|
export function logToolResult(toolName, stdout, originalResult = null, toolInput = null, fileSnapshot = null) {
|
|
24
|
-
|
|
44
|
+
if (isPipeMode()) {
|
|
45
|
+
// Pipe mode에서는 도구 결과를 간략하게 stderr로
|
|
46
|
+
const preview = stdout?.substring(0, 200) || '';
|
|
47
|
+
console.error(`[TOOL_RESULT] ${toolName}: ${preview}${stdout?.length > 200 ? '...' : ''}`);
|
|
48
|
+
} else {
|
|
49
|
+
uiEvents.addToolResult(toolName, { stdout, originalResult, fileSnapshot }, toolInput);
|
|
50
|
+
}
|
|
25
51
|
}
|
|
26
52
|
|
|
27
53
|
export function logCodeExecution(language, code) {
|
|
28
|
-
|
|
54
|
+
if (isPipeMode()) {
|
|
55
|
+
console.error(`[CODE] ${language}`);
|
|
56
|
+
} else {
|
|
57
|
+
uiEvents.startCodeExecution(language, code);
|
|
58
|
+
}
|
|
29
59
|
}
|
|
30
60
|
|
|
31
61
|
export function logCodeResult(stdout, stderr, exitCode) {
|
|
32
|
-
|
|
62
|
+
if (isPipeMode()) {
|
|
63
|
+
if (stdout) console.error(`[CODE_STDOUT] ${stdout.substring(0, 200)}${stdout.length > 200 ? '...' : ''}`);
|
|
64
|
+
if (stderr) console.error(`[CODE_STDERR] ${stderr.substring(0, 200)}${stderr.length > 200 ? '...' : ''}`);
|
|
65
|
+
} else {
|
|
66
|
+
uiEvents.addCodeResult(stdout, stderr, exitCode);
|
|
67
|
+
}
|
|
33
68
|
}
|
|
34
69
|
|
|
35
70
|
export function logIteration(iterationNumber) {
|
|
@@ -38,9 +73,17 @@ export function logIteration(iterationNumber) {
|
|
|
38
73
|
}
|
|
39
74
|
|
|
40
75
|
export function logMissionComplete() {
|
|
41
|
-
|
|
76
|
+
if (isPipeMode()) {
|
|
77
|
+
console.error('[COMPLETE] Mission completed');
|
|
78
|
+
} else {
|
|
79
|
+
uiEvents.missionCompleted(0);
|
|
80
|
+
}
|
|
42
81
|
}
|
|
43
82
|
|
|
44
83
|
export function logConversationRestored(count) {
|
|
45
|
-
|
|
84
|
+
if (isPipeMode()) {
|
|
85
|
+
console.error(`[RESTORED] ${count} conversation(s) restored`);
|
|
86
|
+
} else {
|
|
87
|
+
uiEvents.conversationRestored(count);
|
|
88
|
+
}
|
|
46
89
|
}
|
package/src/system/session.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Agent Loop: 계획·실행·검증 사이클을 수행하는 핵심 모듈
|
|
2
2
|
import { orchestrateMission, continueOrchestratorConversation, resetOrchestratorConversation, recordOrchestratorToolResult, getOrchestratorConversation, restoreOrchestratorConversation } from "../ai_based/orchestrator.js";
|
|
3
3
|
import { judgeMissionCompletion } from "../ai_based/completion_judge.js";
|
|
4
|
-
import { execPythonCode, execShellScript } from "./code_executer.js";
|
|
4
|
+
import { execPythonCode, execShellScript, resetShellCwd, getPersistentShell } from "./code_executer.js";
|
|
5
|
+
import { runInBackground, getBackgroundProcess } from "./background_process.js";
|
|
5
6
|
import { FILE_READER_FUNCTIONS } from "../tools/file_reader.js";
|
|
6
7
|
import { RIPGREP_FUNCTIONS } from "../tools/ripgrep.js";
|
|
7
8
|
import { GLOB_FUNCTIONS } from "../tools/glob.js";
|
|
@@ -30,6 +31,47 @@ import {
|
|
|
30
31
|
|
|
31
32
|
const debugLog = createDebugLogger('session.log', 'session');
|
|
32
33
|
|
|
34
|
+
// Pipe mode helper: UI 이벤트를 조건부로 처리
|
|
35
|
+
const isPipeMode = () => process.app_custom?.pipeMode === true;
|
|
36
|
+
|
|
37
|
+
// Pipe mode에서는 stderr로 출력, 일반 모드에서는 uiEvents 사용
|
|
38
|
+
const safeUiEvents = {
|
|
39
|
+
addSystemMessage: (msg) => {
|
|
40
|
+
if (isPipeMode()) {
|
|
41
|
+
console.error(`[SYSTEM] ${msg}`);
|
|
42
|
+
} else {
|
|
43
|
+
uiEvents.addSystemMessage(msg);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
addErrorMessage: (msg) => {
|
|
47
|
+
if (isPipeMode()) {
|
|
48
|
+
console.error(`[ERROR] ${msg}`);
|
|
49
|
+
} else {
|
|
50
|
+
uiEvents.addErrorMessage(msg);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
sessionStart: (msg) => {
|
|
54
|
+
if (!isPipeMode()) {
|
|
55
|
+
uiEvents.sessionStart(msg);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
sessionEnd: () => {
|
|
59
|
+
if (!isPipeMode()) {
|
|
60
|
+
uiEvents.sessionEnd();
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
on: (event, handler) => {
|
|
64
|
+
if (!isPipeMode()) {
|
|
65
|
+
uiEvents.on(event, handler);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
off: (event, handler) => {
|
|
69
|
+
if (!isPipeMode()) {
|
|
70
|
+
uiEvents.off(event, handler);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
33
75
|
|
|
34
76
|
/**
|
|
35
77
|
* 서브미션 이름을 추론하는 헬퍼 함수
|
|
@@ -397,7 +439,7 @@ async function processOrchestratorResponses(params) {
|
|
|
397
439
|
debugLog(`No processable output, reasoningOnlyResponses: ${reasoningOnlyResponses}`);
|
|
398
440
|
if (reasoningOnlyResponses >= MAX_REASONING_ONLY_RESPONSES) {
|
|
399
441
|
debugLog(`Reached MAX_REASONING_ONLY_RESPONSES (${MAX_REASONING_ONLY_RESPONSES}), treating as stall`);
|
|
400
|
-
|
|
442
|
+
safeUiEvents.addSystemMessage('⚠ Orchestrator stalled (reasoning only) - treating as completion');
|
|
401
443
|
stallDetected = true;
|
|
402
444
|
break;
|
|
403
445
|
}
|
|
@@ -816,14 +858,14 @@ export async function runSession(options) {
|
|
|
816
858
|
if (!sessionInterrupted) {
|
|
817
859
|
sessionInterrupted = true;
|
|
818
860
|
abortCurrentRequest(); // API 요청 즉시 중단
|
|
819
|
-
|
|
861
|
+
safeUiEvents.addErrorMessage('Session interrupted by user');
|
|
820
862
|
debugLog('[runSession] Session interrupted by user');
|
|
821
863
|
}
|
|
822
864
|
};
|
|
823
|
-
|
|
865
|
+
safeUiEvents.on('session:interrupt', handleInterrupt);
|
|
824
866
|
|
|
825
867
|
// 세션 시작 알림
|
|
826
|
-
|
|
868
|
+
safeUiEvents.sessionStart('Running agent session...');
|
|
827
869
|
|
|
828
870
|
// catch 블록에서도 접근 가능하도록 try 블록 밖에서 선언
|
|
829
871
|
let currentSessionData = null;
|
|
@@ -895,6 +937,11 @@ export async function runSession(options) {
|
|
|
895
937
|
restoreTrimmedFileReadsFromSession({ trimmedFileReads: conversationState.trimmedFileReads });
|
|
896
938
|
debugLog(`[runSession] Restored ${conversationState.trimmedFileReads.length} trimmed file reads from previous session`);
|
|
897
939
|
}
|
|
940
|
+
// Shell CWD 복원 (세션 이어가기 시 이전 작업 디렉토리 유지)
|
|
941
|
+
if (conversationState.shellCwd) {
|
|
942
|
+
await resetShellCwd(conversationState.shellCwd);
|
|
943
|
+
debugLog(`[runSession] Restored shell cwd to: ${conversationState.shellCwd}`);
|
|
944
|
+
}
|
|
898
945
|
// logSuccess('✓ Conversation state restored');
|
|
899
946
|
} else {
|
|
900
947
|
// logSystem('ℹ Starting with fresh conversation state');
|
|
@@ -917,6 +964,11 @@ export async function runSession(options) {
|
|
|
917
964
|
updateCurrentTodos([]);
|
|
918
965
|
debugLog(`[runSession] Starting fresh session - no todos in memory`);
|
|
919
966
|
}
|
|
967
|
+
|
|
968
|
+
// 새 세션 시작 시 쉘의 작업 디렉토리를 프로젝트 루트로 리셋
|
|
969
|
+
// (이전 미션에서 cd한 디렉토리가 남아있지 않도록)
|
|
970
|
+
await resetShellCwd();
|
|
971
|
+
debugLog(`[runSession] Shell cwd reset to: ${process.cwd()}`);
|
|
920
972
|
}
|
|
921
973
|
|
|
922
974
|
// Python 사용 가능 여부 확인
|
|
@@ -932,7 +984,23 @@ export async function runSession(options) {
|
|
|
932
984
|
...TODO_WRITE_FUNCTIONS,
|
|
933
985
|
...SKILL_FUNCTIONS, // 스킬 호출 도구
|
|
934
986
|
...mcpToolFunctions,
|
|
935
|
-
"bash": async (args) =>
|
|
987
|
+
"bash": async (args) => {
|
|
988
|
+
if (args.background) {
|
|
989
|
+
// 백그라운드 실행
|
|
990
|
+
const result = await runInBackground(args.script);
|
|
991
|
+
return {
|
|
992
|
+
stdout: `Background process started: ${result.id} (pid: ${result.pid})`,
|
|
993
|
+
stderr: '',
|
|
994
|
+
code: 0,
|
|
995
|
+
background: true,
|
|
996
|
+
processId: result.id,
|
|
997
|
+
pid: result.pid
|
|
998
|
+
};
|
|
999
|
+
} else {
|
|
1000
|
+
// 일반 실행
|
|
1001
|
+
return execShellScript(args.script);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
936
1004
|
};
|
|
937
1005
|
|
|
938
1006
|
// Python이 있는 경우에만 Python 관련 도구 추가
|
|
@@ -1059,6 +1127,9 @@ export async function runSession(options) {
|
|
|
1059
1127
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
1060
1128
|
currentSessionData.orchestratorRequestOptions = null;
|
|
1061
1129
|
|
|
1130
|
+
// Shell CWD를 세션에 저장 (세션 이어가기 시 복원용)
|
|
1131
|
+
currentSessionData.shellCwd = await getPersistentShell().getCwd();
|
|
1132
|
+
|
|
1062
1133
|
// Todos를 세션에 저장
|
|
1063
1134
|
saveTodosToSession(currentSessionData);
|
|
1064
1135
|
|
|
@@ -1093,6 +1164,9 @@ export async function runSession(options) {
|
|
|
1093
1164
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
1094
1165
|
currentSessionData.orchestratorRequestOptions = null;
|
|
1095
1166
|
|
|
1167
|
+
// Shell CWD를 세션에 저장
|
|
1168
|
+
currentSessionData.shellCwd = await getPersistentShell().getCwd();
|
|
1169
|
+
|
|
1096
1170
|
// 인터럽트 시에도 Todos와 TrimmedFileReads 저장 (다음 세션에서 복원 가능하도록)
|
|
1097
1171
|
saveTodosToSession(currentSessionData);
|
|
1098
1172
|
saveTrimmedFileReadsToSession(currentSessionData);
|
|
@@ -1132,7 +1206,7 @@ export async function runSession(options) {
|
|
|
1132
1206
|
const message = stallDetected
|
|
1133
1207
|
? '⚠ Session ended due to orchestrator stall'
|
|
1134
1208
|
: '✅ No function calls detected - mission complete';
|
|
1135
|
-
|
|
1209
|
+
safeUiEvents.addSystemMessage(message);
|
|
1136
1210
|
debugLog(`[ITERATION ${iteration_count}] ${message}`);
|
|
1137
1211
|
|
|
1138
1212
|
// completion_judge 없이 종료되는 경우에도 Todos와 TrimmedFileReads 저장
|
|
@@ -1142,6 +1216,7 @@ export async function runSession(options) {
|
|
|
1142
1216
|
currentSessionData.toolUsageHistory = toolUsageHistory;
|
|
1143
1217
|
currentSessionData.orchestratorConversation = getOrchestratorConversation();
|
|
1144
1218
|
currentSessionData.orchestratorRequestOptions = null;
|
|
1219
|
+
currentSessionData.shellCwd = await getPersistentShell().getCwd();
|
|
1145
1220
|
saveTodosToSession(currentSessionData);
|
|
1146
1221
|
saveTrimmedFileReadsToSession(currentSessionData);
|
|
1147
1222
|
debugLog(`[ITERATION ${iteration_count}] Saving session on no-function-call exit`);
|
|
@@ -1188,6 +1263,9 @@ export async function runSession(options) {
|
|
|
1188
1263
|
currentSessionData.orchestratorConversation = result.orchestratorConversation;
|
|
1189
1264
|
currentSessionData.orchestratorRequestOptions = null; // 필요시 orchestrator에서 가져올 수 있음
|
|
1190
1265
|
|
|
1266
|
+
// Shell CWD를 세션에 저장
|
|
1267
|
+
currentSessionData.shellCwd = await getPersistentShell().getCwd();
|
|
1268
|
+
|
|
1191
1269
|
// 루프 정상 종료 시에도 Todos와 TrimmedFileReads 저장
|
|
1192
1270
|
saveTodosToSession(currentSessionData);
|
|
1193
1271
|
saveTrimmedFileReadsToSession(currentSessionData);
|
|
@@ -1252,12 +1330,13 @@ export async function runSession(options) {
|
|
|
1252
1330
|
consolidatedErrorMessage = `[Session] ${userFriendlyMessage}`;
|
|
1253
1331
|
}
|
|
1254
1332
|
|
|
1255
|
-
|
|
1333
|
+
safeUiEvents.addErrorMessage(consolidatedErrorMessage);
|
|
1256
1334
|
|
|
1257
1335
|
// 에러 발생 시에도 Todos와 TrimmedFileReads 저장 시도
|
|
1258
1336
|
if (currentSessionData) {
|
|
1259
1337
|
currentSessionData.completed_at = new Date().toISOString();
|
|
1260
1338
|
currentSessionData.mission_solved = false;
|
|
1339
|
+
currentSessionData.shellCwd = await getPersistentShell().getCwd();
|
|
1261
1340
|
saveTodosToSession(currentSessionData);
|
|
1262
1341
|
saveTrimmedFileReadsToSession(currentSessionData);
|
|
1263
1342
|
debugLog(`[runSession] Saving session on error`);
|
|
@@ -1270,9 +1349,9 @@ export async function runSession(options) {
|
|
|
1270
1349
|
return null;
|
|
1271
1350
|
} finally {
|
|
1272
1351
|
// 세션 중단 이벤트 리스너 제거
|
|
1273
|
-
|
|
1352
|
+
safeUiEvents.off('session:interrupt', handleInterrupt);
|
|
1274
1353
|
|
|
1275
1354
|
// 세션 종료 알림
|
|
1276
|
-
|
|
1355
|
+
safeUiEvents.sessionEnd();
|
|
1277
1356
|
}
|
|
1278
1357
|
}
|
|
@@ -152,7 +152,8 @@ export function getLastConversationState(sessions) {
|
|
|
152
152
|
orchestratorConversation: lastSession.orchestratorConversation || [],
|
|
153
153
|
orchestratorRequestOptions: lastSession.orchestratorRequestOptions || null,
|
|
154
154
|
currentTodos: lastSession.currentTodos || [],
|
|
155
|
-
trimmedFileReads: lastSession.trimmedFileReads || []
|
|
155
|
+
trimmedFileReads: lastSession.trimmedFileReads || [],
|
|
156
|
+
shellCwd: lastSession.shellCwd || null
|
|
156
157
|
};
|
|
157
158
|
}
|
|
158
159
|
|
package/src/util/exit_handler.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { uiEvents } from '../system/ui_events.js';
|
|
2
|
+
import { closePersistentShell } from '../system/code_executer.js';
|
|
3
|
+
import { killAllBackgroundProcesses } from '../system/background_process.js';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Unified exit handler for the application
|
|
@@ -17,6 +19,12 @@ export async function performExit(options = {}) {
|
|
|
17
19
|
uiEvents.addSystemMessage(`Goodbye! Session ID: ${sessionID}`);
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
// 백그라운드 프로세스 정리
|
|
23
|
+
await killAllBackgroundProcesses();
|
|
24
|
+
|
|
25
|
+
// PersistentShell 정리
|
|
26
|
+
await closePersistentShell();
|
|
27
|
+
|
|
20
28
|
// MCP Integration 정리
|
|
21
29
|
if (mcpIntegration) {
|
|
22
30
|
await mcpIntegration.cleanup();
|
|
File without changes
|
|
File without changes
|
|
File without changes
|