@leo000001/codex-mcp 2.1.5 → 2.1.7
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/README.md +56 -10
- package/dist/index.js +520 -63
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -243,7 +243,7 @@ function getPathExtensions(env) {
|
|
|
243
243
|
}
|
|
244
244
|
return Array.from(new Set(merged));
|
|
245
245
|
}
|
|
246
|
-
function
|
|
246
|
+
function resolveCommandFromPath(command, env) {
|
|
247
247
|
const dirs = getPathEntries(env);
|
|
248
248
|
const ext = process.platform === "win32" ? path2.extname(command) : "";
|
|
249
249
|
const names = process.platform === "win32" ? Array.from(
|
|
@@ -254,10 +254,10 @@ function commandExistsOnPath(command, env) {
|
|
|
254
254
|
for (const dir of dirs) {
|
|
255
255
|
for (const name of names) {
|
|
256
256
|
const candidate = path2.join(dir, name);
|
|
257
|
-
if (isExecutableFile(candidate)) return
|
|
257
|
+
if (isExecutableFile(candidate)) return path2.normalize(candidate);
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
|
-
return
|
|
260
|
+
return void 0;
|
|
261
261
|
}
|
|
262
262
|
function looksLikePath(value) {
|
|
263
263
|
return value.includes("/") || value.includes("\\");
|
|
@@ -288,14 +288,16 @@ function resolveDefaultCodexExecutable(env = process.env) {
|
|
|
288
288
|
`${CODEX_MCP_COMMAND}="${envCommand}" looks like a path. Use ${CODEX_MCP_PATH} for filesystem paths.`
|
|
289
289
|
);
|
|
290
290
|
}
|
|
291
|
-
|
|
291
|
+
const resolved = resolveCommandFromPath(envCommand, env);
|
|
292
|
+
if (!resolved) {
|
|
292
293
|
throw new Error(`${CODEX_MCP_COMMAND}="${envCommand}" was not found in PATH.`);
|
|
293
294
|
}
|
|
294
|
-
return { command:
|
|
295
|
+
return { command: resolved, isPath: true, source: "env_command" };
|
|
295
296
|
}
|
|
296
297
|
for (const candidate of AUTO_CODEX_COMMANDS) {
|
|
297
|
-
|
|
298
|
-
|
|
298
|
+
const resolved = resolveCommandFromPath(candidate, env);
|
|
299
|
+
if (resolved) {
|
|
300
|
+
return { command: resolved, isPath: true, source: "auto_detect" };
|
|
299
301
|
}
|
|
300
302
|
}
|
|
301
303
|
return { command: "codex", isPath: false, source: "default" };
|
|
@@ -308,16 +310,15 @@ function getDefaultCodexExecutable() {
|
|
|
308
310
|
}
|
|
309
311
|
function checkDefaultCodexExecutableAvailability() {
|
|
310
312
|
const info = getDefaultCodexExecutable();
|
|
311
|
-
const label = info.isPath ? "path" : "command";
|
|
312
313
|
switch (info.source) {
|
|
313
314
|
case "env_path":
|
|
314
315
|
console.error(`[codex-executable] Using ${CODEX_MCP_PATH}: ${info.command}`);
|
|
315
316
|
break;
|
|
316
317
|
case "env_command":
|
|
317
|
-
console.error(`[codex-executable] Using ${CODEX_MCP_COMMAND}: ${info.command}`);
|
|
318
|
+
console.error(`[codex-executable] Using ${CODEX_MCP_COMMAND}: resolved to ${info.command}`);
|
|
318
319
|
break;
|
|
319
320
|
case "auto_detect":
|
|
320
|
-
console.error(`[codex-executable] Auto-detected
|
|
321
|
+
console.error(`[codex-executable] Auto-detected executable: ${info.command}`);
|
|
321
322
|
break;
|
|
322
323
|
case "default":
|
|
323
324
|
console.error(
|
|
@@ -339,6 +340,7 @@ var SESSION_ACTIONS = [
|
|
|
339
340
|
"cancel",
|
|
340
341
|
"interrupt",
|
|
341
342
|
"fork",
|
|
343
|
+
"clean",
|
|
342
344
|
"clean_background_terminals"
|
|
343
345
|
];
|
|
344
346
|
var CHECK_ACTIONS = ["poll", "respond_permission", "respond_user_input"];
|
|
@@ -392,7 +394,7 @@ var DEFAULT_TERMINAL_CLEANUP_MS = 5 * 60 * 1e3;
|
|
|
392
394
|
var CLEANUP_INTERVAL_MS = 6e4;
|
|
393
395
|
|
|
394
396
|
// src/app-server/client.ts
|
|
395
|
-
var CLIENT_VERSION = true ? "2.1.
|
|
397
|
+
var CLIENT_VERSION = true ? "2.1.7" : "0.0.0-dev";
|
|
396
398
|
var DEFAULT_REQUEST_TIMEOUT = 3e4;
|
|
397
399
|
var STARTUP_REQUEST_TIMEOUT = 9e4;
|
|
398
400
|
var MAX_WRITE_QUEUE_BYTES = 5 * 1024 * 1024;
|
|
@@ -843,6 +845,31 @@ function resolveAndValidateFilePath(inputPath, baseDir, label = "path") {
|
|
|
843
845
|
return resolved;
|
|
844
846
|
}
|
|
845
847
|
|
|
848
|
+
// src/utils/turn-compat.ts
|
|
849
|
+
function classifyTurnCompatibilityError(err) {
|
|
850
|
+
const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
|
|
851
|
+
const mentionsMinimal = message.includes("minimal");
|
|
852
|
+
const mentionsWebSearch = message.includes("web_search") || message.includes("web search");
|
|
853
|
+
const mentionsEffort = message.includes("effort") || message.includes("reasoning_effort") || message.includes("reasoning effort");
|
|
854
|
+
return mentionsMinimal && mentionsWebSearch && mentionsEffort ? "minimal_web_search" : void 0;
|
|
855
|
+
}
|
|
856
|
+
function compatibilityErrorMessage(kind) {
|
|
857
|
+
switch (kind) {
|
|
858
|
+
case "minimal_web_search":
|
|
859
|
+
return `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: effort=minimal is incompatible with the Codex web_search tool in this CLI build. Use effort=low or higher, or let codex-mcp auto-upgrade it.`;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
function toFriendlyTurnCompatibilityError(err) {
|
|
863
|
+
const kind = classifyTurnCompatibilityError(err);
|
|
864
|
+
if (kind) {
|
|
865
|
+
return new Error(compatibilityErrorMessage(kind));
|
|
866
|
+
}
|
|
867
|
+
return err instanceof Error ? err : new Error(String(err));
|
|
868
|
+
}
|
|
869
|
+
function buildEffortFallbackWarning(from, to) {
|
|
870
|
+
return `effort=${from} is incompatible with the Codex web_search tool in this CLI build; automatically retried with effort=${to}.`;
|
|
871
|
+
}
|
|
872
|
+
|
|
846
873
|
// src/session/manager.ts
|
|
847
874
|
var COALESCED_PROGRESS_DELTA_METHODS = /* @__PURE__ */ new Set([
|
|
848
875
|
Methods.COMMAND_OUTPUT_DELTA,
|
|
@@ -850,6 +877,14 @@ var COALESCED_PROGRESS_DELTA_METHODS = /* @__PURE__ */ new Set([
|
|
|
850
877
|
Methods.REASONING_TEXT_DELTA,
|
|
851
878
|
Methods.REASONING_SUMMARY_DELTA
|
|
852
879
|
]);
|
|
880
|
+
var SKIPPABLE_DELTA_METHODS = /* @__PURE__ */ new Set([
|
|
881
|
+
Methods.AGENT_MESSAGE_DELTA,
|
|
882
|
+
Methods.COMMAND_OUTPUT_DELTA,
|
|
883
|
+
Methods.FILE_CHANGE_OUTPUT_DELTA,
|
|
884
|
+
Methods.REASONING_TEXT_DELTA,
|
|
885
|
+
Methods.REASONING_SUMMARY_DELTA,
|
|
886
|
+
Methods.PLAN_DELTA
|
|
887
|
+
]);
|
|
853
888
|
var MAX_COALESCED_DELTA_CHARS = 16384;
|
|
854
889
|
var AUTH_REFRESH_UNSUPPORTED_CODE = -32e3;
|
|
855
890
|
var AUTH_REFRESH_UNSUPPORTED_MESSAGE = "account/chatgptAuthTokens/refresh unsupported: codex-mcp does not manage external ChatGPT auth tokens";
|
|
@@ -882,6 +917,22 @@ function stripShellNoise(delta) {
|
|
|
882
917
|
}
|
|
883
918
|
var MAX_WAITERS_PER_SESSION = 4;
|
|
884
919
|
var MAX_WAIT_MS = 12e4;
|
|
920
|
+
var EFFORT_FALLBACK_LEVEL = "low";
|
|
921
|
+
var CLEANABLE_SESSION_STATUSES = ["idle", "error", "cancelled"];
|
|
922
|
+
var REASONING_PROGRESS_METHODS = /* @__PURE__ */ new Set([
|
|
923
|
+
Methods.REASONING_TEXT_DELTA,
|
|
924
|
+
Methods.REASONING_SUMMARY_DELTA,
|
|
925
|
+
Methods.REASONING_SUMMARY_PART_ADDED,
|
|
926
|
+
Methods.PLAN_DELTA
|
|
927
|
+
]);
|
|
928
|
+
var ACTING_PROGRESS_METHODS = /* @__PURE__ */ new Set([
|
|
929
|
+
Methods.COMMAND_OUTPUT_DELTA,
|
|
930
|
+
Methods.COMMAND_TERMINAL_INTERACTION,
|
|
931
|
+
Methods.FILE_CHANGE_OUTPUT_DELTA,
|
|
932
|
+
Methods.MCP_TOOL_PROGRESS,
|
|
933
|
+
Methods.TURN_DIFF_UPDATED,
|
|
934
|
+
Methods.TURN_PLAN_UPDATED
|
|
935
|
+
]);
|
|
885
936
|
var SessionManager = class {
|
|
886
937
|
sessions = /* @__PURE__ */ new Map();
|
|
887
938
|
clients = /* @__PURE__ */ new Map();
|
|
@@ -936,7 +987,12 @@ var SessionManager = class {
|
|
|
936
987
|
sandbox: rec.meta.sandbox,
|
|
937
988
|
eventBuffer: createEventBuffer(),
|
|
938
989
|
pendingRequests: /* @__PURE__ */ new Map(),
|
|
939
|
-
lastResult: rec.result
|
|
990
|
+
lastResult: rec.result,
|
|
991
|
+
lastAgentMessageText: typeof rec.result?.text === "string" ? rec.result.text : typeof rec.result?.output === "string" ? rec.result.output : void 0,
|
|
992
|
+
progressState: {
|
|
993
|
+
lastEventAt: rec.meta.lastActiveAt ?? now,
|
|
994
|
+
tokens: extractTokens(rec.result?.turn)
|
|
995
|
+
}
|
|
940
996
|
};
|
|
941
997
|
this.sessions.set(rec.sessionId, session);
|
|
942
998
|
if (rec.lastSeq >= 0) {
|
|
@@ -971,6 +1027,26 @@ var SessionManager = class {
|
|
|
971
1027
|
} catch {
|
|
972
1028
|
}
|
|
973
1029
|
}
|
|
1030
|
+
async startTurnWithCompatibilityFallback(client, turnParams) {
|
|
1031
|
+
try {
|
|
1032
|
+
return { turnStartResult: await client.turnStart(turnParams) };
|
|
1033
|
+
} catch (err) {
|
|
1034
|
+
if (turnParams.effort === "minimal" && classifyTurnCompatibilityError(err) === "minimal_web_search") {
|
|
1035
|
+
try {
|
|
1036
|
+
return {
|
|
1037
|
+
turnStartResult: await client.turnStart({
|
|
1038
|
+
...turnParams,
|
|
1039
|
+
effort: EFFORT_FALLBACK_LEVEL
|
|
1040
|
+
}),
|
|
1041
|
+
compatWarnings: [buildEffortFallbackWarning("minimal", EFFORT_FALLBACK_LEVEL)]
|
|
1042
|
+
};
|
|
1043
|
+
} catch (retryErr) {
|
|
1044
|
+
throw toFriendlyTurnCompatibilityError(retryErr);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
throw toFriendlyTurnCompatibilityError(err);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
974
1050
|
// ── Session Creation ─────────────────────────────────────────────
|
|
975
1051
|
async createSession(prompt, cwd, spawnOpts, effort, advanced) {
|
|
976
1052
|
const sessionId = `sess_${randomUUID().slice(0, 12)}`;
|
|
@@ -992,7 +1068,9 @@ var SessionManager = class {
|
|
|
992
1068
|
sandbox: spawnOpts.sandbox,
|
|
993
1069
|
config: spawnOpts.config,
|
|
994
1070
|
eventBuffer: createEventBuffer(),
|
|
995
|
-
pendingRequests: /* @__PURE__ */ new Map()
|
|
1071
|
+
pendingRequests: /* @__PURE__ */ new Map(),
|
|
1072
|
+
lastAgentMessageText: void 0,
|
|
1073
|
+
progressState: { lastEventAt: now }
|
|
996
1074
|
};
|
|
997
1075
|
this.sessions.set(sessionId, session);
|
|
998
1076
|
this.clients.set(sessionId, client);
|
|
@@ -1028,20 +1106,23 @@ var SessionManager = class {
|
|
|
1028
1106
|
input.push({ type: "localImage", path: imagePath });
|
|
1029
1107
|
}
|
|
1030
1108
|
}
|
|
1031
|
-
const
|
|
1109
|
+
const turnStart = await this.startTurnWithCompatibilityFallback(client, {
|
|
1032
1110
|
threadId,
|
|
1033
1111
|
input,
|
|
1034
1112
|
effort,
|
|
1035
1113
|
summary: advanced?.summary,
|
|
1036
1114
|
outputSchema: advanced?.outputSchema
|
|
1037
1115
|
});
|
|
1116
|
+
const turnStartResult = turnStart.turnStartResult;
|
|
1038
1117
|
const startedTurnId = extractTurnId(turnStartResult);
|
|
1039
1118
|
if (startedTurnId) session.activeTurnId = startedTurnId;
|
|
1040
1119
|
return {
|
|
1041
1120
|
sessionId,
|
|
1042
1121
|
threadId,
|
|
1043
1122
|
status: "running",
|
|
1044
|
-
pollInterval: DEFAULT_POLL_INTERVAL
|
|
1123
|
+
pollInterval: DEFAULT_POLL_INTERVAL,
|
|
1124
|
+
compatWarnings: turnStart.compatWarnings,
|
|
1125
|
+
progress: this.getProgress(sessionId)
|
|
1045
1126
|
};
|
|
1046
1127
|
} catch (err) {
|
|
1047
1128
|
session.status = "error";
|
|
@@ -1074,6 +1155,7 @@ var SessionManager = class {
|
|
|
1074
1155
|
);
|
|
1075
1156
|
}
|
|
1076
1157
|
clearTerminalEvents(session.eventBuffer);
|
|
1158
|
+
session.lastAgentMessageText = void 0;
|
|
1077
1159
|
session.status = "running";
|
|
1078
1160
|
session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1079
1161
|
this.persistSessionIfChanged(session);
|
|
@@ -1093,8 +1175,11 @@ var SessionManager = class {
|
|
|
1093
1175
|
if (overrides?.sandbox) {
|
|
1094
1176
|
turnParams.sandboxPolicy = toSandboxPolicy(overrides.sandbox);
|
|
1095
1177
|
}
|
|
1178
|
+
let compatWarnings;
|
|
1096
1179
|
try {
|
|
1097
|
-
const
|
|
1180
|
+
const turnStart = await this.startTurnWithCompatibilityFallback(client, turnParams);
|
|
1181
|
+
compatWarnings = turnStart.compatWarnings;
|
|
1182
|
+
const turnStartResult = turnStart.turnStartResult;
|
|
1098
1183
|
const startedTurnId = extractTurnId(turnStartResult);
|
|
1099
1184
|
if (startedTurnId) session.activeTurnId = startedTurnId;
|
|
1100
1185
|
const canOverride = client.supportsTurnOverrides;
|
|
@@ -1119,7 +1204,9 @@ var SessionManager = class {
|
|
|
1119
1204
|
sessionId,
|
|
1120
1205
|
threadId: session.threadId,
|
|
1121
1206
|
status: "running",
|
|
1122
|
-
pollInterval: DEFAULT_POLL_INTERVAL
|
|
1207
|
+
pollInterval: DEFAULT_POLL_INTERVAL,
|
|
1208
|
+
compatWarnings,
|
|
1209
|
+
progress: this.getProgress(sessionId)
|
|
1123
1210
|
};
|
|
1124
1211
|
}
|
|
1125
1212
|
// ── Session Management ───────────────────────────────────────────
|
|
@@ -1165,6 +1252,9 @@ var SessionManager = class {
|
|
|
1165
1252
|
getLastResult(sessionId) {
|
|
1166
1253
|
return this.getSessionOrThrow(sessionId).lastResult;
|
|
1167
1254
|
}
|
|
1255
|
+
getProgress(sessionId) {
|
|
1256
|
+
return buildProgressInfo(this.getSessionOrThrow(sessionId));
|
|
1257
|
+
}
|
|
1168
1258
|
getPendingActionTypes(sessionId) {
|
|
1169
1259
|
const session = this.getSessionOrThrow(sessionId);
|
|
1170
1260
|
const actionTypes = /* @__PURE__ */ new Set();
|
|
@@ -1284,6 +1374,50 @@ var SessionManager = class {
|
|
|
1284
1374
|
true
|
|
1285
1375
|
);
|
|
1286
1376
|
}
|
|
1377
|
+
async cleanSessions(options) {
|
|
1378
|
+
const statuses = new Set(options?.statuses ?? CLEANABLE_SESSION_STATUSES);
|
|
1379
|
+
const olderThanMs = options?.olderThanMs;
|
|
1380
|
+
const dryRun = options?.dryRun ?? false;
|
|
1381
|
+
const includeDisk = options?.includeDisk ?? true;
|
|
1382
|
+
const now = Date.now();
|
|
1383
|
+
const matchedSessionIds = [];
|
|
1384
|
+
for (const [sessionId, session] of Array.from(this.sessions.entries())) {
|
|
1385
|
+
if (!statuses.has(session.status)) continue;
|
|
1386
|
+
if (typeof olderThanMs === "number" && olderThanMs > 0) {
|
|
1387
|
+
const lastActive = new Date(session.lastActiveAt).getTime();
|
|
1388
|
+
if (!Number.isFinite(lastActive)) continue;
|
|
1389
|
+
if (now - lastActive < olderThanMs) continue;
|
|
1390
|
+
}
|
|
1391
|
+
matchedSessionIds.push(sessionId);
|
|
1392
|
+
}
|
|
1393
|
+
if (dryRun) {
|
|
1394
|
+
return {
|
|
1395
|
+
matchedSessionIds,
|
|
1396
|
+
removedSessionIds: [],
|
|
1397
|
+
removedCount: 0,
|
|
1398
|
+
diskSessionsRemoved: 0,
|
|
1399
|
+
dryRun: true
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
let diskSessionsRemoved = 0;
|
|
1403
|
+
const removedSessionIds = [];
|
|
1404
|
+
for (const sessionId of matchedSessionIds) {
|
|
1405
|
+
const evicted = this.evictSession(sessionId, includeDisk);
|
|
1406
|
+
if (evicted.deleted) {
|
|
1407
|
+
removedSessionIds.push(sessionId);
|
|
1408
|
+
}
|
|
1409
|
+
if (evicted.diskRemoved) {
|
|
1410
|
+
diskSessionsRemoved++;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
return {
|
|
1414
|
+
matchedSessionIds,
|
|
1415
|
+
removedSessionIds,
|
|
1416
|
+
removedCount: removedSessionIds.length,
|
|
1417
|
+
diskSessionsRemoved,
|
|
1418
|
+
dryRun: false
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1287
1421
|
async forkSession(sessionId) {
|
|
1288
1422
|
const session = this.getSessionOrThrow(sessionId);
|
|
1289
1423
|
const originalClient = this.getClientOrThrow(sessionId);
|
|
@@ -1407,26 +1541,48 @@ var SessionManager = class {
|
|
|
1407
1541
|
const buf = session.eventBuffer;
|
|
1408
1542
|
const responseMode = options.responseMode ?? "full";
|
|
1409
1543
|
const pollOptions = options.pollOptions;
|
|
1410
|
-
const
|
|
1544
|
+
const finalOnly = pollOptions?.finalOnly ?? false;
|
|
1545
|
+
const skipDeltas = pollOptions?.skipDeltas ?? false;
|
|
1546
|
+
const includeEvents = finalOnly ? false : pollOptions?.includeEvents ?? true;
|
|
1411
1547
|
const includeActions = pollOptions?.includeActions ?? true;
|
|
1412
|
-
const includeResult = pollOptions?.includeResult ?? true;
|
|
1548
|
+
const includeResult = finalOnly ? true : pollOptions?.includeResult ?? true;
|
|
1413
1549
|
const maxBytes = pollOptions?.maxBytes;
|
|
1414
1550
|
const effectiveCursor = cursor ?? session.lastEventCursor;
|
|
1415
|
-
|
|
1551
|
+
const unseenEvents = buf.events.filter((e) => e.id >= effectiveCursor);
|
|
1552
|
+
let events = includeEvents ? unseenEvents : [];
|
|
1416
1553
|
let cursorResetTo;
|
|
1417
|
-
if (
|
|
1554
|
+
if (buf.events.length > 0) {
|
|
1418
1555
|
const earliest = buf.events[0].id;
|
|
1419
1556
|
if (earliest > effectiveCursor) {
|
|
1420
1557
|
cursorResetTo = earliest;
|
|
1421
|
-
|
|
1558
|
+
if (includeEvents) {
|
|
1559
|
+
events = buf.events;
|
|
1560
|
+
}
|
|
1422
1561
|
}
|
|
1423
1562
|
}
|
|
1424
1563
|
const cursorFloor = cursorResetTo ?? effectiveCursor;
|
|
1425
|
-
|
|
1426
|
-
|
|
1564
|
+
let highestConsumedEventId;
|
|
1565
|
+
if (includeEvents) {
|
|
1566
|
+
if (skipDeltas) {
|
|
1567
|
+
const filtered = [];
|
|
1568
|
+
for (const event of events) {
|
|
1569
|
+
highestConsumedEventId = event.id;
|
|
1570
|
+
if (isSkippableDeltaEvent(event)) continue;
|
|
1571
|
+
filtered.push(event);
|
|
1572
|
+
if (filtered.length >= maxEvents) break;
|
|
1573
|
+
}
|
|
1574
|
+
events = filtered;
|
|
1575
|
+
} else if (events.length > maxEvents) {
|
|
1576
|
+
events = events.slice(0, maxEvents);
|
|
1577
|
+
highestConsumedEventId = events.length > 0 ? events[events.length - 1]?.id : void 0;
|
|
1578
|
+
} else if (events.length > 0) {
|
|
1579
|
+
highestConsumedEventId = events[events.length - 1]?.id;
|
|
1580
|
+
}
|
|
1581
|
+
} else if (finalOnly && unseenEvents.length > 0) {
|
|
1582
|
+
highestConsumedEventId = unseenEvents[unseenEvents.length - 1]?.id;
|
|
1427
1583
|
}
|
|
1428
1584
|
let nextCursor = clampCursorToLatest(
|
|
1429
|
-
|
|
1585
|
+
typeof highestConsumedEventId === "number" ? highestConsumedEventId + 1 : cursorFloor,
|
|
1430
1586
|
buf.nextId
|
|
1431
1587
|
);
|
|
1432
1588
|
const actions = [];
|
|
@@ -1456,6 +1612,7 @@ var SessionManager = class {
|
|
|
1456
1612
|
sessionId,
|
|
1457
1613
|
status: session.status,
|
|
1458
1614
|
pollInterval: pollIntervalForStatus(session.status),
|
|
1615
|
+
progress: buildProgressInfo(session),
|
|
1459
1616
|
events: events.map((event) => serializeEventForMode(event, responseMode)),
|
|
1460
1617
|
nextCursor,
|
|
1461
1618
|
cursorResetTo,
|
|
@@ -1482,6 +1639,10 @@ var SessionManager = class {
|
|
|
1482
1639
|
result.result = void 0;
|
|
1483
1640
|
truncatedFields.push("result");
|
|
1484
1641
|
}
|
|
1642
|
+
if (typeof result.progress !== "undefined" && payloadByteSize(result) > normalizedMaxBytes) {
|
|
1643
|
+
result.progress = void 0;
|
|
1644
|
+
truncatedFields.push("progress");
|
|
1645
|
+
}
|
|
1485
1646
|
if (typeof result.actions !== "undefined" && payloadByteSize(result) > normalizedMaxBytes) {
|
|
1486
1647
|
if (session.status === "waiting_approval") {
|
|
1487
1648
|
result.actions = compactActionsForBudget(result.actions);
|
|
@@ -1509,7 +1670,7 @@ var SessionManager = class {
|
|
|
1509
1670
|
}
|
|
1510
1671
|
}
|
|
1511
1672
|
}
|
|
1512
|
-
if (includeEvents) {
|
|
1673
|
+
if (includeEvents || finalOnly) {
|
|
1513
1674
|
session.lastEventCursor = persistMonotonicCursor(
|
|
1514
1675
|
session.lastEventCursor,
|
|
1515
1676
|
result.nextCursor,
|
|
@@ -1738,6 +1899,7 @@ var SessionManager = class {
|
|
|
1738
1899
|
client.onNotification((method, params) => {
|
|
1739
1900
|
session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1740
1901
|
const p = params;
|
|
1902
|
+
recordProgressObservation(session, method, p);
|
|
1741
1903
|
switch (method) {
|
|
1742
1904
|
case Methods.THREAD_STARTED: {
|
|
1743
1905
|
const thread = isRecord(p.thread) ? p.thread : void 0;
|
|
@@ -1781,17 +1943,21 @@ var SessionManager = class {
|
|
|
1781
1943
|
if (session.status === "cancelled") break;
|
|
1782
1944
|
const turnObj = p.turn;
|
|
1783
1945
|
const completedTurnId = turnObj?.id ?? session.activeTurnId ?? "";
|
|
1946
|
+
const rawTurnOutput = normalizeOptionalString(turnObj?.output);
|
|
1947
|
+
const finalText = normalizeOptionalString(turnObj?.output) ?? session.lastAgentMessageText;
|
|
1784
1948
|
session.status = "idle";
|
|
1785
1949
|
session.activeTurnId = void 0;
|
|
1786
1950
|
session.lastResult = {
|
|
1787
1951
|
turnId: completedTurnId,
|
|
1788
|
-
|
|
1952
|
+
text: finalText,
|
|
1953
|
+
output: rawTurnOutput,
|
|
1789
1954
|
structuredOutput: turnObj?.structuredOutput,
|
|
1790
1955
|
turn: p.turn,
|
|
1791
1956
|
status: turnObj?.status,
|
|
1792
1957
|
turnError: turnObj?.error,
|
|
1793
1958
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1794
1959
|
};
|
|
1960
|
+
mergeProgressTokens(session, extractTokens(turnObj?.usage));
|
|
1795
1961
|
pushEvent(
|
|
1796
1962
|
session.eventBuffer,
|
|
1797
1963
|
"result",
|
|
@@ -1846,6 +2012,10 @@ var SessionManager = class {
|
|
|
1846
2012
|
const item = p.item;
|
|
1847
2013
|
const itemType = item && typeof item.type === "string" ? item.type : void 0;
|
|
1848
2014
|
const status = normalizeOptionalString(item?.status);
|
|
2015
|
+
const completedItem = method === Methods.ITEM_COMPLETED || method === Methods.RAW_RESPONSE_ITEM_COMPLETED;
|
|
2016
|
+
if (itemType === "agentMessage" && completedItem && status === "completed" && typeof item?.text === "string") {
|
|
2017
|
+
session.lastAgentMessageText = item.text;
|
|
2018
|
+
}
|
|
1849
2019
|
const eventType = itemType === "agentMessage" || itemType === "userMessage" ? "output" : "progress";
|
|
1850
2020
|
pushEvent(session.eventBuffer, eventType, {
|
|
1851
2021
|
method,
|
|
@@ -1889,6 +2059,7 @@ var SessionManager = class {
|
|
|
1889
2059
|
}
|
|
1890
2060
|
session.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1891
2061
|
const p = params;
|
|
2062
|
+
recordProgressObservation(session, method, p);
|
|
1892
2063
|
switch (method) {
|
|
1893
2064
|
case Methods.COMMAND_APPROVAL: {
|
|
1894
2065
|
const requestId = `req_${randomUUID().slice(0, 8)}`;
|
|
@@ -2175,16 +2346,7 @@ var SessionManager = class {
|
|
|
2175
2346
|
this.ttlWarningEmitted.delete(id);
|
|
2176
2347
|
this.requestCancellation(id, "Running timeout");
|
|
2177
2348
|
} else if ((session.status === "cancelled" || session.status === "error") && age > DEFAULT_TERMINAL_CLEANUP_MS) {
|
|
2178
|
-
this.
|
|
2179
|
-
this.clients.get(id)?.destroy().catch((err) => {
|
|
2180
|
-
console.error(
|
|
2181
|
-
`[codex-mcp] Failed to destroy app-server client during cleanup: session=${id} error=${err instanceof Error ? err.message : String(err)}`
|
|
2182
|
-
);
|
|
2183
|
-
});
|
|
2184
|
-
this.clients.delete(id);
|
|
2185
|
-
this.sessions.delete(id);
|
|
2186
|
-
this.lastPersistedStatus.delete(id);
|
|
2187
|
-
this.ttlWarningEmitted.delete(id);
|
|
2349
|
+
this.evictSession(id, true);
|
|
2188
2350
|
} else {
|
|
2189
2351
|
let ttlMs;
|
|
2190
2352
|
if (session.status === "idle") {
|
|
@@ -2215,6 +2377,34 @@ var SessionManager = class {
|
|
|
2215
2377
|
);
|
|
2216
2378
|
});
|
|
2217
2379
|
}
|
|
2380
|
+
evictSession(sessionId, removeDisk) {
|
|
2381
|
+
const session = this.sessions.get(sessionId);
|
|
2382
|
+
if (!session) return { deleted: false, diskRemoved: false };
|
|
2383
|
+
clearSessionPendingRequests(session);
|
|
2384
|
+
this.notifyWaiters(sessionId);
|
|
2385
|
+
this.clients.get(sessionId)?.destroy().catch((err) => {
|
|
2386
|
+
console.error(
|
|
2387
|
+
`[codex-mcp] Failed to destroy app-server client during cleanup: session=${sessionId} error=${err instanceof Error ? err.message : String(err)}`
|
|
2388
|
+
);
|
|
2389
|
+
});
|
|
2390
|
+
this.clients.delete(sessionId);
|
|
2391
|
+
const deleted = this.sessions.delete(sessionId);
|
|
2392
|
+
this.lastPersistedStatus.delete(sessionId);
|
|
2393
|
+
this.ttlWarningEmitted.delete(sessionId);
|
|
2394
|
+
this.sessionNotifiers.delete(sessionId);
|
|
2395
|
+
this.cancellationInFlight.delete(sessionId);
|
|
2396
|
+
let diskRemoved = false;
|
|
2397
|
+
if (removeDisk) {
|
|
2398
|
+
try {
|
|
2399
|
+
if (this.persistence) {
|
|
2400
|
+
this.persistence.removeSession(sessionId);
|
|
2401
|
+
diskRemoved = true;
|
|
2402
|
+
}
|
|
2403
|
+
} catch {
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
return { deleted, diskRemoved };
|
|
2407
|
+
}
|
|
2218
2408
|
};
|
|
2219
2409
|
function pollIntervalForStatus(status) {
|
|
2220
2410
|
if (status === "waiting_approval") return WAITING_APPROVAL_POLL_INTERVAL;
|
|
@@ -2229,6 +2419,114 @@ function createEventBuffer() {
|
|
|
2229
2419
|
nextId: 0
|
|
2230
2420
|
};
|
|
2231
2421
|
}
|
|
2422
|
+
function buildProgressInfo(session) {
|
|
2423
|
+
const storedTokens = session.progressState?.tokens;
|
|
2424
|
+
const resultTokens = extractTokens(session.lastResult?.turn);
|
|
2425
|
+
return {
|
|
2426
|
+
phase: deriveProgressPhase(session),
|
|
2427
|
+
lastEventAt: session.progressState?.lastEventAt ?? session.lastActiveAt,
|
|
2428
|
+
activeTurnId: session.activeTurnId,
|
|
2429
|
+
pendingActionCount: countPendingRequests(session),
|
|
2430
|
+
lastMethod: session.progressState?.lastMethod,
|
|
2431
|
+
percent: session.progressState?.percent,
|
|
2432
|
+
tokens: mergeTokens(storedTokens, resultTokens)
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
function countPendingRequests(session) {
|
|
2436
|
+
let count = 0;
|
|
2437
|
+
for (const req of session.pendingRequests.values()) {
|
|
2438
|
+
if (!req.resolved) count++;
|
|
2439
|
+
}
|
|
2440
|
+
return count;
|
|
2441
|
+
}
|
|
2442
|
+
function deriveProgressPhase(session) {
|
|
2443
|
+
if (session.status === "waiting_approval") return "waiting_approval";
|
|
2444
|
+
if (session.status === "cancelled") return "cancelled";
|
|
2445
|
+
if (session.status === "error") return "error";
|
|
2446
|
+
if (session.status === "idle") return "finished";
|
|
2447
|
+
if (!session.activeTurnId) return "starting";
|
|
2448
|
+
const lastMethod = session.progressState?.lastMethod;
|
|
2449
|
+
if (typeof lastMethod === "string") {
|
|
2450
|
+
if (REASONING_PROGRESS_METHODS.has(lastMethod)) return "reasoning";
|
|
2451
|
+
if (ACTING_PROGRESS_METHODS.has(lastMethod)) return "acting";
|
|
2452
|
+
}
|
|
2453
|
+
return "running";
|
|
2454
|
+
}
|
|
2455
|
+
function recordProgressObservation(session, method, params) {
|
|
2456
|
+
const next = session.progressState ?? { lastEventAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2457
|
+
next.lastEventAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2458
|
+
if (method !== Methods.THREAD_TOKEN_USAGE_UPDATED) {
|
|
2459
|
+
next.lastMethod = method;
|
|
2460
|
+
}
|
|
2461
|
+
const percent = extractPercent(params);
|
|
2462
|
+
if (typeof percent === "number") next.percent = percent;
|
|
2463
|
+
mergeProgressTokens(session, extractTokens(params));
|
|
2464
|
+
session.progressState = next;
|
|
2465
|
+
}
|
|
2466
|
+
function mergeProgressTokens(session, tokens) {
|
|
2467
|
+
if (!tokens) return;
|
|
2468
|
+
const next = session.progressState ?? { lastEventAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
2469
|
+
next.tokens = mergeTokens(next.tokens, tokens);
|
|
2470
|
+
session.progressState = next;
|
|
2471
|
+
}
|
|
2472
|
+
function mergeTokens(base, extra) {
|
|
2473
|
+
if (!base && !extra) return void 0;
|
|
2474
|
+
return {
|
|
2475
|
+
input: extra?.input ?? base?.input,
|
|
2476
|
+
output: extra?.output ?? base?.output,
|
|
2477
|
+
total: extra?.total ?? base?.total
|
|
2478
|
+
};
|
|
2479
|
+
}
|
|
2480
|
+
function extractPercent(value) {
|
|
2481
|
+
if (!isRecord(value)) return void 0;
|
|
2482
|
+
const candidates = [value.percent, value.percentage, value.progress, value.fractionComplete];
|
|
2483
|
+
for (const candidate of candidates) {
|
|
2484
|
+
if (typeof candidate !== "number" || !Number.isFinite(candidate)) continue;
|
|
2485
|
+
if (candidate >= 0 && candidate <= 1) return Math.round(candidate * 100);
|
|
2486
|
+
if (candidate >= 0 && candidate <= 100) return candidate;
|
|
2487
|
+
}
|
|
2488
|
+
return void 0;
|
|
2489
|
+
}
|
|
2490
|
+
function extractTokens(value) {
|
|
2491
|
+
if (!isRecord(value)) return void 0;
|
|
2492
|
+
const usage = isRecord(value.usage) ? value.usage : void 0;
|
|
2493
|
+
const source = usage ?? value;
|
|
2494
|
+
const input = pickNumber(source, [
|
|
2495
|
+
"inputTokens",
|
|
2496
|
+
"input_tokens",
|
|
2497
|
+
"promptTokens",
|
|
2498
|
+
"prompt_tokens"
|
|
2499
|
+
]);
|
|
2500
|
+
const output = pickNumber(source, [
|
|
2501
|
+
"outputTokens",
|
|
2502
|
+
"output_tokens",
|
|
2503
|
+
"completionTokens",
|
|
2504
|
+
"completion_tokens"
|
|
2505
|
+
]);
|
|
2506
|
+
const total = pickNumber(source, [
|
|
2507
|
+
"totalTokens",
|
|
2508
|
+
"total_tokens",
|
|
2509
|
+
"tokenCount",
|
|
2510
|
+
"token_count",
|
|
2511
|
+
"total"
|
|
2512
|
+
]);
|
|
2513
|
+
if (typeof input !== "number" && typeof output !== "number" && typeof total !== "number") {
|
|
2514
|
+
return void 0;
|
|
2515
|
+
}
|
|
2516
|
+
return { input, output, total };
|
|
2517
|
+
}
|
|
2518
|
+
function pickNumber(source, keys) {
|
|
2519
|
+
for (const key of keys) {
|
|
2520
|
+
const value = source[key];
|
|
2521
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
2522
|
+
}
|
|
2523
|
+
return void 0;
|
|
2524
|
+
}
|
|
2525
|
+
function isSkippableDeltaEvent(event) {
|
|
2526
|
+
if (!isRecord(event.data)) return false;
|
|
2527
|
+
const method = event.data.method;
|
|
2528
|
+
return typeof method === "string" && SKIPPABLE_DELTA_METHODS.has(method);
|
|
2529
|
+
}
|
|
2232
2530
|
function clearTerminalEvents(buf) {
|
|
2233
2531
|
buf.events = buf.events.filter((e) => e.type !== "result" && e.type !== "error");
|
|
2234
2532
|
}
|
|
@@ -2718,6 +3016,20 @@ function buildExecutionInfo(waitForResultMs, status, fallbackReason) {
|
|
|
2718
3016
|
fallbackReason: effective === "background" ? fallbackReason : void 0
|
|
2719
3017
|
};
|
|
2720
3018
|
}
|
|
3019
|
+
function coerceProgressForStatus(status, progress, options) {
|
|
3020
|
+
if (!progress) return void 0;
|
|
3021
|
+
let phase = progress.phase;
|
|
3022
|
+
if (status === "idle") phase = "finished";
|
|
3023
|
+
else if (status === "error") phase = "error";
|
|
3024
|
+
else if (status === "cancelled") phase = "cancelled";
|
|
3025
|
+
else if (status === "waiting_approval") phase = "waiting_approval";
|
|
3026
|
+
return {
|
|
3027
|
+
...progress,
|
|
3028
|
+
phase,
|
|
3029
|
+
lastEventAt: options?.completedAt ?? progress.lastEventAt,
|
|
3030
|
+
pendingActionCount: status === "waiting_approval" ? Math.max(progress.pendingActionCount, options?.pendingActionCount ?? 0) : status === "running" ? progress.pendingActionCount : 0
|
|
3031
|
+
};
|
|
3032
|
+
}
|
|
2721
3033
|
async function waitForCodexSessionForegroundResult(sessionManager, sessionId, waitForResultMs, signal) {
|
|
2722
3034
|
const deadline = Date.now() + Math.min(waitForResultMs, 3e5);
|
|
2723
3035
|
while (Date.now() < deadline) {
|
|
@@ -2760,6 +3072,9 @@ async function waitForCodexSessionForegroundResult(sessionManager, sessionId, wa
|
|
|
2760
3072
|
}
|
|
2761
3073
|
|
|
2762
3074
|
// src/tools/codex.ts
|
|
3075
|
+
function safeGetProgress(sessionManager, sessionId) {
|
|
3076
|
+
return typeof sessionManager.getProgress === "function" ? sessionManager.getProgress(sessionId) : void 0;
|
|
3077
|
+
}
|
|
2763
3078
|
async function executeCodex(args, sessionManager, serverCwd, requestSignal) {
|
|
2764
3079
|
const cwd = resolveAndValidateCwd(args.cwd, serverCwd);
|
|
2765
3080
|
const spawnOpts = extractSpawnOptions(args);
|
|
@@ -2774,6 +3089,10 @@ async function executeCodex(args, sessionManager, serverCwd, requestSignal) {
|
|
|
2774
3089
|
const waitMs = args.advanced?.waitForResult;
|
|
2775
3090
|
const baseResult = {
|
|
2776
3091
|
...startResult,
|
|
3092
|
+
progress: coerceProgressForStatus(
|
|
3093
|
+
"running",
|
|
3094
|
+
safeGetProgress(sessionManager, startResult.sessionId) ?? startResult.progress
|
|
3095
|
+
),
|
|
2777
3096
|
execution: buildExecutionInfo(waitMs, "running"),
|
|
2778
3097
|
interactionState: interactionStateForStatus("running"),
|
|
2779
3098
|
recommendedNextAction: recommendedNextActionForStatus("running")
|
|
@@ -2791,6 +3110,15 @@ async function executeCodex(args, sessionManager, serverCwd, requestSignal) {
|
|
|
2791
3110
|
result: foreground.result,
|
|
2792
3111
|
status: foreground.status,
|
|
2793
3112
|
completedAt: foreground.completedAt,
|
|
3113
|
+
compatWarnings: startResult.compatWarnings,
|
|
3114
|
+
progress: coerceProgressForStatus(
|
|
3115
|
+
foreground.status,
|
|
3116
|
+
safeGetProgress(sessionManager, startResult.sessionId) ?? startResult.progress,
|
|
3117
|
+
{
|
|
3118
|
+
completedAt: foreground.completedAt,
|
|
3119
|
+
pendingActionCount: foreground.pendingActionTypes?.length ?? 0
|
|
3120
|
+
}
|
|
3121
|
+
),
|
|
2794
3122
|
pollInterval: foreground.status === "waiting_approval" ? WAITING_APPROVAL_POLL_INTERVAL : foreground.status === "running" ? startResult.pollInterval : void 0,
|
|
2795
3123
|
execution: buildExecutionInfo(waitMs, foreground.status, foreground.fallbackReason),
|
|
2796
3124
|
interactionState: interactionStateForStatus(foreground.status),
|
|
@@ -2802,6 +3130,9 @@ async function executeCodex(args, sessionManager, serverCwd, requestSignal) {
|
|
|
2802
3130
|
}
|
|
2803
3131
|
|
|
2804
3132
|
// src/tools/codex-reply.ts
|
|
3133
|
+
function safeGetProgress2(sessionManager, sessionId) {
|
|
3134
|
+
return typeof sessionManager.getProgress === "function" ? sessionManager.getProgress(sessionId) : void 0;
|
|
3135
|
+
}
|
|
2805
3136
|
async function executeCodexReply(args, sessionManager, requestSignal) {
|
|
2806
3137
|
const startResult = await sessionManager.replyToSession(args.sessionId, args.prompt, {
|
|
2807
3138
|
model: args.model,
|
|
@@ -2816,6 +3147,10 @@ async function executeCodexReply(args, sessionManager, requestSignal) {
|
|
|
2816
3147
|
const waitMs = args.waitForResult;
|
|
2817
3148
|
const baseResult = {
|
|
2818
3149
|
...startResult,
|
|
3150
|
+
progress: coerceProgressForStatus(
|
|
3151
|
+
"running",
|
|
3152
|
+
safeGetProgress2(sessionManager, startResult.sessionId) ?? startResult.progress
|
|
3153
|
+
),
|
|
2819
3154
|
execution: buildExecutionInfo(waitMs, "running"),
|
|
2820
3155
|
interactionState: interactionStateForStatus("running"),
|
|
2821
3156
|
recommendedNextAction: recommendedNextActionForStatus("running")
|
|
@@ -2833,6 +3168,15 @@ async function executeCodexReply(args, sessionManager, requestSignal) {
|
|
|
2833
3168
|
sessionId: startResult.sessionId,
|
|
2834
3169
|
threadId: startResult.threadId,
|
|
2835
3170
|
status: foreground.status,
|
|
3171
|
+
compatWarnings: startResult.compatWarnings,
|
|
3172
|
+
progress: coerceProgressForStatus(
|
|
3173
|
+
foreground.status,
|
|
3174
|
+
safeGetProgress2(sessionManager, startResult.sessionId) ?? startResult.progress,
|
|
3175
|
+
{
|
|
3176
|
+
completedAt: foreground.completedAt,
|
|
3177
|
+
pendingActionCount: foreground.pendingActionTypes?.length ?? 0
|
|
3178
|
+
}
|
|
3179
|
+
),
|
|
2836
3180
|
pollInterval: foreground.status === "waiting_approval" ? WAITING_APPROVAL_POLL_INTERVAL : foreground.status === "running" ? startResult.pollInterval : void 0,
|
|
2837
3181
|
result: foreground.result,
|
|
2838
3182
|
completedAt: foreground.completedAt,
|
|
@@ -2884,6 +3228,13 @@ async function executeCodexSession(args, sessionManager) {
|
|
|
2884
3228
|
};
|
|
2885
3229
|
}
|
|
2886
3230
|
return await sessionManager.forkSession(args.sessionId);
|
|
3231
|
+
case "clean":
|
|
3232
|
+
return await sessionManager.cleanSessions({
|
|
3233
|
+
statuses: args.statuses,
|
|
3234
|
+
olderThanMs: args.olderThanMs,
|
|
3235
|
+
dryRun: args.dryRun,
|
|
3236
|
+
includeDisk: args.includeDisk
|
|
3237
|
+
});
|
|
2887
3238
|
case "clean_background_terminals":
|
|
2888
3239
|
if (!args.sessionId) {
|
|
2889
3240
|
return {
|
|
@@ -2919,24 +3270,15 @@ function executeCodexCheck(args, sessionManager, requestSignal) {
|
|
|
2919
3270
|
const maxEvents = typeof args.maxEvents === "number" ? Math.max(POLL_MIN_MAX_EVENTS, Math.floor(args.maxEvents)) : POLL_DEFAULT_MAX_EVENTS;
|
|
2920
3271
|
const waitMs = pollOptions?.waitMs;
|
|
2921
3272
|
if (typeof waitMs === "number" && waitMs > 0) {
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
sessionManager,
|
|
2932
|
-
sessionManager.pollEvents(args.sessionId, args.cursor, maxEvents, {
|
|
2933
|
-
responseMode,
|
|
2934
|
-
pollOptions
|
|
2935
|
-
})
|
|
2936
|
-
)
|
|
2937
|
-
);
|
|
2938
|
-
}
|
|
2939
|
-
return enrichCheckResult(sessionManager, firstCheck);
|
|
3273
|
+
return pollWithWait(
|
|
3274
|
+
sessionManager,
|
|
3275
|
+
args.sessionId,
|
|
3276
|
+
args.cursor,
|
|
3277
|
+
maxEvents,
|
|
3278
|
+
{ responseMode, pollOptions },
|
|
3279
|
+
Math.min(waitMs, 12e4),
|
|
3280
|
+
requestSignal
|
|
3281
|
+
).then((result) => enrichCheckResult(sessionManager, result));
|
|
2940
3282
|
}
|
|
2941
3283
|
return enrichCheckResult(
|
|
2942
3284
|
sessionManager,
|
|
@@ -3057,6 +3399,31 @@ function executeCodexCheck(args, sessionManager, requestSignal) {
|
|
|
3057
3399
|
};
|
|
3058
3400
|
}
|
|
3059
3401
|
}
|
|
3402
|
+
function hasVisibleData(result) {
|
|
3403
|
+
return result.events.length > 0 || result.actions !== void 0 && result.actions.length > 0 || result.result !== void 0;
|
|
3404
|
+
}
|
|
3405
|
+
async function pollWithWait(sessionManager, sessionId, cursor, maxEvents, options, waitMs, signal) {
|
|
3406
|
+
const deadline = Date.now() + waitMs;
|
|
3407
|
+
let currentCursor = cursor;
|
|
3408
|
+
while (true) {
|
|
3409
|
+
const result = sessionManager.pollEvents(sessionId, currentCursor, maxEvents, options);
|
|
3410
|
+
if (hasVisibleData(result)) {
|
|
3411
|
+
return result;
|
|
3412
|
+
}
|
|
3413
|
+
if (signal?.aborted) {
|
|
3414
|
+
return result;
|
|
3415
|
+
}
|
|
3416
|
+
const remainingMs = deadline - Date.now();
|
|
3417
|
+
if (remainingMs <= 0) {
|
|
3418
|
+
return result;
|
|
3419
|
+
}
|
|
3420
|
+
currentCursor = result.nextCursor;
|
|
3421
|
+
await sessionManager.waitForChange(sessionId, remainingMs, signal).catch(() => void 0);
|
|
3422
|
+
if (signal?.aborted) {
|
|
3423
|
+
return sessionManager.pollEvents(sessionId, currentCursor, maxEvents, options);
|
|
3424
|
+
}
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3060
3427
|
function enrichCheckResult(sessionManager, result) {
|
|
3061
3428
|
const actionTypes = result.status === "waiting_approval" ? sessionManager.getPendingActionTypes(result.sessionId) : [];
|
|
3062
3429
|
return {
|
|
@@ -3147,6 +3514,10 @@ function probeAppServer(codexCommand, codexIsPath, env) {
|
|
|
3147
3514
|
}
|
|
3148
3515
|
|
|
3149
3516
|
// src/tools/codex-setup.ts
|
|
3517
|
+
function isCodexInternalExecutable(info) {
|
|
3518
|
+
const ext = path5.extname(info.command);
|
|
3519
|
+
return path5.basename(info.command, ext).toLowerCase() === "codex-internal";
|
|
3520
|
+
}
|
|
3150
3521
|
function classifyAuthResult(status, combined) {
|
|
3151
3522
|
if (status === 0) return "authenticated";
|
|
3152
3523
|
if (/(not (logged|authenticated)|login required|run\s+codex\s+login)/i.test(combined)) {
|
|
@@ -3159,6 +3530,13 @@ function resolveCodexStateDir() {
|
|
|
3159
3530
|
return configured && configured !== "" ? configured : path5.join(homedir(), ".codex-mcp", "state");
|
|
3160
3531
|
}
|
|
3161
3532
|
function probeCodexAuth(info) {
|
|
3533
|
+
if (isCodexInternalExecutable(info)) {
|
|
3534
|
+
return {
|
|
3535
|
+
ok: true,
|
|
3536
|
+
state: "unknown",
|
|
3537
|
+
detail: "Using a codex-internal executable; auth/login readiness is not probed and does not block setup readiness."
|
|
3538
|
+
};
|
|
3539
|
+
}
|
|
3162
3540
|
const invocation = resolveCodexInvocation(["login", "status"], {
|
|
3163
3541
|
codexCommand: info.command,
|
|
3164
3542
|
codexIsPath: info.isPath
|
|
@@ -3191,9 +3569,11 @@ async function executeCodexSetup(input, serverCwd) {
|
|
|
3191
3569
|
let executable;
|
|
3192
3570
|
let auth;
|
|
3193
3571
|
let clientMode;
|
|
3572
|
+
let internalExecutable = false;
|
|
3194
3573
|
try {
|
|
3195
3574
|
const info = resolveDefaultCodexExecutable();
|
|
3196
3575
|
const available = info.source !== "default";
|
|
3576
|
+
internalExecutable = isCodexInternalExecutable(info);
|
|
3197
3577
|
executable = {
|
|
3198
3578
|
ok: available,
|
|
3199
3579
|
source: info.source,
|
|
@@ -3237,7 +3617,7 @@ async function executeCodexSetup(input, serverCwd) {
|
|
|
3237
3617
|
if (auth.state === "unauthenticated") {
|
|
3238
3618
|
warnings.push(auth.detail);
|
|
3239
3619
|
nextSteps.push("Run `codex login` and rerun `codex_setup`.");
|
|
3240
|
-
} else if (auth.state === "unknown") {
|
|
3620
|
+
} else if (auth.state === "unknown" && !internalExecutable) {
|
|
3241
3621
|
warnings.push(auth.detail);
|
|
3242
3622
|
nextSteps.push(
|
|
3243
3623
|
"Verify Codex authentication explicitly (for example with `codex login status`) before relying on this environment."
|
|
@@ -3527,8 +3907,11 @@ function buildConfigGuideText() {
|
|
|
3527
3907
|
"- `codex_check.pollOptions.includeEvents`: default `true`.",
|
|
3528
3908
|
"- `codex_check.pollOptions.includeActions`: default `true`.",
|
|
3529
3909
|
"- `codex_check.pollOptions.includeResult`: default `true`.",
|
|
3910
|
+
"- `codex_check.pollOptions.skipDeltas`: default `false`.",
|
|
3911
|
+
"- `codex_check.pollOptions.finalOnly`: default `false`.",
|
|
3530
3912
|
"- `codex_check.pollOptions.maxBytes`: default unlimited.",
|
|
3531
3913
|
"- `codex_check.cursor`: default is session last consumed cursor when omitted.",
|
|
3914
|
+
"- `progress` is included on `codex`, `codex_reply`, and `codex_check` responses.",
|
|
3532
3915
|
""
|
|
3533
3916
|
].join("\n");
|
|
3534
3917
|
}
|
|
@@ -3542,6 +3925,8 @@ function buildGotchasText() {
|
|
|
3542
3925
|
`- Poll enforces minimum \`maxEvents=${POLL_MIN_MAX_EVENTS}\`; sending \`0\` is normalized to \`${POLL_MIN_MAX_EVENTS}\`.`,
|
|
3543
3926
|
`- \`respond_permission\` and \`respond_user_input\` default to compact ACK with \`maxEvents=${RESPOND_DEFAULT_MAX_EVENTS}\`.`,
|
|
3544
3927
|
"- Default response mode is `minimal`; use `full` if you need full raw event payloads.",
|
|
3928
|
+
"- Use `pollOptions.skipDeltas=true` to suppress delta-heavy stream chunks while still advancing the cursor.",
|
|
3929
|
+
"- Use `pollOptions.finalOnly=true` when you only care about actions + terminal result; it also advances the cursor past hidden events.",
|
|
3545
3930
|
"- respond_* uses monotonic cursor handling: `max(cursor, sessionLastCursor)`.",
|
|
3546
3931
|
"- If `cursorResetTo` is present, your cursor is stale (old events were evicted); restart from that value.",
|
|
3547
3932
|
"- **Poll frequency guidance**: Adapt poll interval to task complexity and previous poll results. For `running` sessions, start at 2 minutes and increase for long tasks. Only poll frequently (~1s) when `waiting_approval`. Do NOT high-frequency poll \u2014 it wastes tokens and provides no benefit.",
|
|
@@ -3556,6 +3941,8 @@ function buildGotchasText() {
|
|
|
3556
3941
|
"## Event model",
|
|
3557
3942
|
"",
|
|
3558
3943
|
"- Top-level `events[].type` is one of: `output`, `progress`, `approval_request`, `approval_result`, `result`, `error`.",
|
|
3944
|
+
"- Terminal `result.text` provides a stable final assistant message, even when the backend omits `turn.output`; `result.output` remains the raw backend field.",
|
|
3945
|
+
"- `progress` normalizes the current phase, pending action count, last observed method, and token totals when available.",
|
|
3559
3946
|
"- Fine-grained stream semantics are in `events[].data.method` (for example command output delta, reasoning delta, turn updates).",
|
|
3560
3947
|
'- Retryable interruptions surface as `progress` with `method="codex-mcp/reconnect"` and include retry fields.',
|
|
3561
3948
|
"- During reconnect/retry, continue polling normally; if retries stop (`willRetry=false`), session transitions to error path.",
|
|
@@ -3572,7 +3959,8 @@ function buildGotchasText() {
|
|
|
3572
3959
|
`- Idle sessions are auto-cleaned after ${msToMinutes(DEFAULT_IDLE_CLEANUP_MS)} minutes.`,
|
|
3573
3960
|
`- Running/waiting sessions are auto-cleaned after ${msToMinutes(DEFAULT_RUNNING_CLEANUP_MS)} minutes.`,
|
|
3574
3961
|
`- Error/cancelled sessions are retained for about ${msToMinutes(DEFAULT_TERMINAL_CLEANUP_MS)} minutes, then removed.`,
|
|
3575
|
-
|
|
3962
|
+
'- Use `codex_session(action="clean")` to batch-remove idle/error/cancelled sessions on demand.',
|
|
3963
|
+
"- Session metadata/results are persisted for recovery; manual clean can also delete those disk artifacts.",
|
|
3576
3964
|
"",
|
|
3577
3965
|
"## Capacity",
|
|
3578
3966
|
"",
|
|
@@ -3659,6 +4047,7 @@ function buildQuickstartText() {
|
|
|
3659
4047
|
"```",
|
|
3660
4048
|
"",
|
|
3661
4049
|
"5. Continue polling until terminal status (`idle`, `error`, or `cancelled`), respecting the >=2 minute interval while `running`.",
|
|
4050
|
+
"6. Read `progress.phase` / `progress.tokens` for a coarse execution snapshot without parsing raw delta events.",
|
|
3662
4051
|
"",
|
|
3663
4052
|
"## Cursor notes",
|
|
3664
4053
|
"",
|
|
@@ -3667,6 +4056,13 @@ function buildQuickstartText() {
|
|
|
3667
4056
|
"- Omit `responseMode`: default is `minimal`.",
|
|
3668
4057
|
"- Use returned `nextCursor` for the next call.",
|
|
3669
4058
|
"- If `cursorResetTo` appears, reset to that value and continue.",
|
|
4059
|
+
"- If you need schema-constrained results, pass `advanced.outputSchema` (or top-level `outputSchema` in `codex_reply`) and read terminal `result.structuredOutput`.",
|
|
4060
|
+
"",
|
|
4061
|
+
"## Read next",
|
|
4062
|
+
"",
|
|
4063
|
+
"- `codex-mcp:///config`: parameter-by-parameter guide, including `advanced.*` mapping and reply overrides.",
|
|
4064
|
+
"- `codex-mcp:///delegation-guide`: task presets for approvalPolicy/sandbox selection.",
|
|
4065
|
+
"- `codex-mcp:///gotchas`: polling, approval timeout, cursor, and exec-mode failure modes.",
|
|
3670
4066
|
""
|
|
3671
4067
|
].join("\n");
|
|
3672
4068
|
}
|
|
@@ -3715,6 +4111,13 @@ function buildDelegationGuideText() {
|
|
|
3715
4111
|
"",
|
|
3716
4112
|
"**Key rule:** `read-only` sandbox already prevents writes, so `approvalPolicy: 'never'` is safe with it. Avoid `untrusted` + `read-only` \u2014 every read command triggers approval for no safety gain.",
|
|
3717
4113
|
"",
|
|
4114
|
+
"## Approval policy quick guide",
|
|
4115
|
+
"- `never`: no interactive prompts. Best for read-only review or tightly scoped trusted tasks.",
|
|
4116
|
+
"- `on-failure`: pragmatic default for implementation work when you still want some safety rails.",
|
|
4117
|
+
"- `on-request`: use when a human or outer agent will actively poll and answer approvals.",
|
|
4118
|
+
"- `untrusted`: strictest interactive mode; expect frequent prompts and higher timeout sensitivity.",
|
|
4119
|
+
`- Default approval timeout is ${DEFAULT_APPROVAL_TIMEOUT_MS}ms. If interactive approvals are possible, raise \`advanced.approvalTimeoutMs\` to at least 300000 so requests do not expire between normal running-session polls.`,
|
|
4120
|
+
"",
|
|
3718
4121
|
"## Quick mode: `waitForResult`",
|
|
3719
4122
|
"For short tasks (< 2 min), set `advanced.waitForResult` to get the final result in a single tool call:",
|
|
3720
4123
|
"```json",
|
|
@@ -3742,6 +4145,11 @@ function buildDelegationGuideText() {
|
|
|
3742
4145
|
"",
|
|
3743
4146
|
"**Approval timeout:** Default is 60s; infrequent polling causes silent auto-decline. See `codex-mcp:///gotchas`.",
|
|
3744
4147
|
"",
|
|
4148
|
+
"## Read next",
|
|
4149
|
+
"- `codex-mcp:///quickstart` for the exact start -> poll -> respond loop",
|
|
4150
|
+
"- `codex-mcp:///config` for parameter mapping and override persistence",
|
|
4151
|
+
"- `codex-mcp:///gotchas` for timeout, cursor, and exec-mode caveats",
|
|
4152
|
+
"",
|
|
3745
4153
|
"## Security notes",
|
|
3746
4154
|
"- `sandbox: 'read-only'` is the strongest isolation \u2014 blocks all writes regardless of approval policy",
|
|
3747
4155
|
"- `approvalPolicy: 'never'` + `sandbox: 'workspace-write'` gives the agent full write access with no human oversight \u2014 use only for well-defined, low-risk tasks",
|
|
@@ -3929,9 +4337,13 @@ function registerResources(server, deps) {
|
|
|
3929
4337
|
}
|
|
3930
4338
|
|
|
3931
4339
|
// src/server.ts
|
|
3932
|
-
var SERVER_VERSION = true ? "2.1.
|
|
4340
|
+
var SERVER_VERSION = true ? "2.1.7" : "0.0.0-dev";
|
|
3933
4341
|
function formatErrorMessage(err) {
|
|
3934
4342
|
const message = err instanceof Error ? err.message : String(err);
|
|
4343
|
+
const compatibilityKind = classifyTurnCompatibilityError(err);
|
|
4344
|
+
if (compatibilityKind) {
|
|
4345
|
+
return compatibilityErrorMessage(compatibilityKind);
|
|
4346
|
+
}
|
|
3935
4347
|
const m = /^Error \[([A-Z_]+)\]:\s*(.*)$/.exec(message);
|
|
3936
4348
|
if (m) {
|
|
3937
4349
|
const [, code, rest] = m;
|
|
@@ -3983,6 +4395,28 @@ function createServer(serverCwd, options) {
|
|
|
3983
4395
|
});
|
|
3984
4396
|
const interactionStateSchema = z.enum(["working", "waiting_input", "finished"]);
|
|
3985
4397
|
const nextActionSchema = z.enum(["poll", "respond_permission", "respond_user_input", "none"]);
|
|
4398
|
+
const progressSchema = z.object({
|
|
4399
|
+
phase: z.enum([
|
|
4400
|
+
"starting",
|
|
4401
|
+
"running",
|
|
4402
|
+
"reasoning",
|
|
4403
|
+
"acting",
|
|
4404
|
+
"waiting_approval",
|
|
4405
|
+
"finished",
|
|
4406
|
+
"error",
|
|
4407
|
+
"cancelled"
|
|
4408
|
+
]),
|
|
4409
|
+
lastEventAt: z.string(),
|
|
4410
|
+
activeTurnId: z.string().optional(),
|
|
4411
|
+
pendingActionCount: z.number().int(),
|
|
4412
|
+
lastMethod: z.string().optional(),
|
|
4413
|
+
percent: z.number().optional(),
|
|
4414
|
+
tokens: z.object({
|
|
4415
|
+
input: z.number().optional(),
|
|
4416
|
+
output: z.number().optional(),
|
|
4417
|
+
total: z.number().optional()
|
|
4418
|
+
}).optional()
|
|
4419
|
+
});
|
|
3986
4420
|
const setupResultShape = {
|
|
3987
4421
|
ready: z.boolean(),
|
|
3988
4422
|
cwd: z.string(),
|
|
@@ -4019,6 +4453,8 @@ function createServer(serverCwd, options) {
|
|
|
4019
4453
|
),
|
|
4020
4454
|
result: z.unknown().optional().describe("Final result when waitForResult is set and session completed."),
|
|
4021
4455
|
completedAt: z.string().optional().describe("ISO timestamp when the session completed (only when waitForResult succeeded)."),
|
|
4456
|
+
compatWarnings: z.array(z.string()).optional(),
|
|
4457
|
+
progress: progressSchema.optional(),
|
|
4022
4458
|
execution: executionInfoSchema.optional(),
|
|
4023
4459
|
interactionState: interactionStateSchema.optional(),
|
|
4024
4460
|
recommendedNextAction: nextActionSchema.optional(),
|
|
@@ -4028,6 +4464,10 @@ function createServer(serverCwd, options) {
|
|
|
4028
4464
|
includeEvents: z.boolean().optional().describe("Default: true. Include events[] in response."),
|
|
4029
4465
|
includeActions: z.boolean().optional().describe("Default: true. Include actions[] in response."),
|
|
4030
4466
|
includeResult: z.boolean().optional().describe("Default: true. Include result in response."),
|
|
4467
|
+
skipDeltas: z.boolean().optional().describe(
|
|
4468
|
+
"Default: false. Drop delta-heavy streaming events while still advancing the cursor."
|
|
4469
|
+
),
|
|
4470
|
+
finalOnly: z.boolean().optional().describe("Default: false. Omit events and focus on actions + terminal result."),
|
|
4031
4471
|
maxBytes: z.number().int().positive().optional().describe("Default: unlimited. Best-effort response payload cap in bytes."),
|
|
4032
4472
|
waitMs: z.number().int().nonnegative().optional().describe(
|
|
4033
4473
|
"Long-poll: block up to this many ms for new events (max 120000). Omit or 0 for immediate return."
|
|
@@ -4173,7 +4613,7 @@ function createServer(serverCwd, options) {
|
|
|
4173
4613
|
"codex",
|
|
4174
4614
|
{
|
|
4175
4615
|
title: "Start Codex Session",
|
|
4176
|
-
description: "Start session asynchronously and return `{ sessionId, threadId, status, pollInterval }`.
|
|
4616
|
+
description: "Start a Codex session asynchronously and return `{ sessionId, threadId, status, pollInterval }`. Poll with `codex_check(action='poll')` until terminal status, and treat `pollInterval` as a minimum hint: `running` >=120000ms, `waiting_approval` ~=1000ms. See `codex-mcp:///quickstart` for the main loop, `codex-mcp:///config` for parameter guidance, and `codex-mcp:///delegation-guide` for approval/sandbox presets.",
|
|
4177
4617
|
inputSchema: {
|
|
4178
4618
|
prompt: z.string().describe("Task or question"),
|
|
4179
4619
|
approvalPolicy: z.enum(APPROVAL_POLICIES).describe("Required enum: untrusted/on-failure/on-request/never."),
|
|
@@ -4301,18 +4741,23 @@ function createServer(serverCwd, options) {
|
|
|
4301
4741
|
"codex_session",
|
|
4302
4742
|
{
|
|
4303
4743
|
title: "Manage Sessions",
|
|
4304
|
-
description: `Session actions: list, get, cancel, interrupt, fork, clean_background_terminals.
|
|
4744
|
+
description: `Session actions: list, get, cancel, interrupt, fork, clean, clean_background_terminals.
|
|
4305
4745
|
|
|
4306
4746
|
- list: sessions in memory.
|
|
4307
4747
|
- get: details. includeSensitive defaults to false; true adds threadId/cwd/profile/config.
|
|
4308
4748
|
- cancel: terminal.
|
|
4309
4749
|
- interrupt: stop current turn.
|
|
4310
4750
|
- fork: clone current thread into a new session; source remains unchanged.
|
|
4751
|
+
- clean: batch-remove idle/error/cancelled sessions, optionally from disk too.
|
|
4311
4752
|
- clean_background_terminals: ask app-server to clean stale background terminals for this thread.`,
|
|
4312
4753
|
inputSchema: {
|
|
4313
4754
|
action: z.enum(SESSION_ACTIONS),
|
|
4314
4755
|
sessionId: z.string().optional().describe("Required for get/cancel/interrupt/fork/clean_background_terminals"),
|
|
4315
|
-
includeSensitive: z.boolean().default(false).optional().describe("Include cwd/config/threadId/profile in get (default: false)")
|
|
4756
|
+
includeSensitive: z.boolean().default(false).optional().describe("Include cwd/config/threadId/profile in get (default: false)"),
|
|
4757
|
+
statuses: z.array(z.enum(["idle", "error", "cancelled"])).optional().describe("For clean only. Default: idle/error/cancelled."),
|
|
4758
|
+
olderThanMs: z.number().int().nonnegative().optional().describe("For clean only. Remove sessions idle for at least this many ms."),
|
|
4759
|
+
dryRun: z.boolean().optional().describe("For clean only. Preview matched sessions."),
|
|
4760
|
+
includeDisk: z.boolean().optional().describe("For clean only. Default: true. Also remove persisted session state.")
|
|
4316
4761
|
},
|
|
4317
4762
|
outputSchema: {
|
|
4318
4763
|
sessions: z.array(publicSessionInfoSchema).optional(),
|
|
@@ -4333,6 +4778,11 @@ function createServer(serverCwd, options) {
|
|
|
4333
4778
|
pollInterval: z.number().int().optional().describe(
|
|
4334
4779
|
"Recommended minimum delay before next poll (ms): running >=120000, waiting_approval ~=1000."
|
|
4335
4780
|
),
|
|
4781
|
+
matchedSessionIds: z.array(z.string()).optional(),
|
|
4782
|
+
removedSessionIds: z.array(z.string()).optional(),
|
|
4783
|
+
removedCount: z.number().int().optional(),
|
|
4784
|
+
diskSessionsRemoved: z.number().int().optional(),
|
|
4785
|
+
dryRun: z.boolean().optional(),
|
|
4336
4786
|
success: z.boolean().optional(),
|
|
4337
4787
|
message: z.string().optional(),
|
|
4338
4788
|
...errorOutputShape
|
|
@@ -4368,7 +4818,7 @@ function createServer(serverCwd, options) {
|
|
|
4368
4818
|
"codex_check",
|
|
4369
4819
|
{
|
|
4370
4820
|
title: "Poll & Respond",
|
|
4371
|
-
description: `Poll session for events or respond to approval/input requests. Use pollInterval as a minimum hint; stop polling on terminal status (idle/error/cancelled). See codex-mcp:///
|
|
4821
|
+
description: `Poll session for events or respond to approval/input requests. Use pollInterval as a minimum hint; stop polling on terminal status (idle/error/cancelled). WARNING: running sessions usually poll at >=120000ms, but approvalTimeoutMs defaults to ${DEFAULT_APPROVAL_TIMEOUT_MS}ms, so approvals can expire between polls unless you raise the timeout or use non-interactive policies. See codex-mcp:///quickstart and codex-mcp:///gotchas.
|
|
4372
4822
|
|
|
4373
4823
|
poll: events since cursor. Default maxEvents=${POLL_DEFAULT_MAX_EVENTS}.
|
|
4374
4824
|
respond_permission: approval decision. Default maxEvents=${RESPOND_DEFAULT_MAX_EVENTS} (compact ACK).
|
|
@@ -4380,6 +4830,7 @@ respond_user_input: user-input answers. Default maxEvents=${RESPOND_DEFAULT_MAX_
|
|
|
4380
4830
|
pollInterval: z.number().int().optional().describe(
|
|
4381
4831
|
"Recommended minimum delay before next poll (ms): running >=120000, waiting_approval ~=1000."
|
|
4382
4832
|
),
|
|
4833
|
+
progress: progressSchema.optional(),
|
|
4383
4834
|
interactionState: interactionStateSchema.optional(),
|
|
4384
4835
|
recommendedNextAction: nextActionSchema.optional(),
|
|
4385
4836
|
events: z.array(
|
|
@@ -4415,6 +4866,7 @@ respond_user_input: user-input answers. Default maxEvents=${RESPOND_DEFAULT_MAX_
|
|
|
4415
4866
|
).optional(),
|
|
4416
4867
|
result: z.object({
|
|
4417
4868
|
turnId: z.string(),
|
|
4869
|
+
text: z.string().optional(),
|
|
4418
4870
|
output: z.string().optional(),
|
|
4419
4871
|
structuredOutput: z.unknown().optional(),
|
|
4420
4872
|
turn: z.unknown().optional(),
|
|
@@ -5013,7 +5465,7 @@ var ExecClient = class extends EventEmitter2 {
|
|
|
5013
5465
|
|
|
5014
5466
|
// src/session/persistence.ts
|
|
5015
5467
|
import { join as join5 } from "path";
|
|
5016
|
-
import { mkdirSync as mkdirSync4, existsSync as existsSync7 } from "fs";
|
|
5468
|
+
import { mkdirSync as mkdirSync4, existsSync as existsSync7, rmSync as rmSync3 } from "fs";
|
|
5017
5469
|
import { homedir as homedir2 } from "os";
|
|
5018
5470
|
|
|
5019
5471
|
// src/persistence/atomic-writer.ts
|
|
@@ -5460,6 +5912,11 @@ var SessionPersistence = class {
|
|
|
5460
5912
|
this.eventLogs.delete(sessionId);
|
|
5461
5913
|
}
|
|
5462
5914
|
}
|
|
5915
|
+
/** Remove a persisted session directory from disk. */
|
|
5916
|
+
removeSession(sessionId) {
|
|
5917
|
+
this.destroySessionLog(sessionId);
|
|
5918
|
+
rmSync3(join5(this.sessionsDir, sessionId), { recursive: true, force: true });
|
|
5919
|
+
}
|
|
5463
5920
|
/** Clean up: flush all logs, release lock. */
|
|
5464
5921
|
destroy() {
|
|
5465
5922
|
for (const log of this.eventLogs.values()) {
|