very-happy-cli 0.2.134 → 0.2.136
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/dist/{AcpBackend-B8KEjDwF.mjs → AcpBackend-C142vP3p.mjs} +2 -2
- package/dist/{AcpBackend-CeNp7tgD.cjs → AcpBackend-CRzu0_1Z.cjs} +2 -2
- package/dist/{AcpSessionManager-Bu1d05bx.cjs → AcpSessionManager-Ba6WMLXc.cjs} +1 -1
- package/dist/{AcpSessionManager-KlDZbEmZ.mjs → AcpSessionManager-CosTbAlR.mjs} +1 -1
- package/dist/{agentGuidance-Cg8Lha39.cjs → agentGuidance-6yh_wPwD.cjs} +1 -1
- package/dist/{agentGuidance-CPG5Cr5T.mjs → agentGuidance-ASxKHxBP.mjs} +1 -1
- package/dist/codex/happyMcpStdioBridge.cjs +2 -2
- package/dist/codex/happyMcpStdioBridge.mjs +2 -2
- package/dist/{config-C3e90AIH.cjs → config--cxxmkOd.cjs} +4 -4
- package/dist/{config-C4gWfnJX.mjs → config-VqHQ67d8.mjs} +4 -4
- package/dist/{index-B1plzL9M.cjs → index-8bwU9d-4.cjs} +1596 -1208
- package/dist/{index-jqG8CV0c.cjs → index-BPVQFcKW.cjs} +6 -6
- package/dist/{index-CX-zp3FH.cjs → index-BqbtKX0T.cjs} +4 -4
- package/dist/{index-BqUOj1zK.mjs → index-CHI7iTa0.mjs} +1594 -1206
- package/dist/{index-B6UvB88K.mjs → index-D3vlFjXy.mjs} +7 -7
- package/dist/{index-CEDPokKf.mjs → index-vN4Xf0zB.mjs} +4 -4
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/{installTerminalHooks-DPmEaHg2.cjs → installTerminalHooks-2POBqVlu.cjs} +4 -4
- package/dist/{installTerminalHooks-BT4Dq28Y.mjs → installTerminalHooks-D0YRu_p-.mjs} +4 -4
- package/dist/lib.cjs +2 -2
- package/dist/lib.d.cts +14 -0
- package/dist/lib.d.mts +14 -0
- package/dist/lib.mjs +2 -2
- package/dist/{limits-D6kiAfXJ.cjs → limits-DRrdJ7gr.cjs} +8 -8
- package/dist/{limits-BGeIBBE3.mjs → limits-kjHZUlSp.mjs} +8 -8
- package/dist/{mcp-DGZmUpuZ.cjs → mcp-C1JQsM1s.cjs} +4 -4
- package/dist/{mcp-BGu_dUaJ.mjs → mcp-CgjEHlky.mjs} +4 -4
- package/dist/{runGemini-BPPnulIk.cjs → runGemini-B9Rc-H0G.cjs} +6 -6
- package/dist/{runGemini-DGTNwiLo.mjs → runGemini-DufGnnl2.mjs} +6 -6
- package/dist/{runOpenClaw-BB3Ivt1S.cjs → runOpenClaw-BsRRKDvF.cjs} +5 -5
- package/dist/{runOpenClaw-CxgTFOSM.mjs → runOpenClaw-DAl2nnjB.mjs} +5 -5
- package/dist/{send-DHShoBeq.mjs → send-BHquPaeG.mjs} +4 -4
- package/dist/{send-Bo6iKuXr.cjs → send-DP6n2J9x.cjs} +4 -4
- package/dist/{sessions-CJuwvEr0.mjs → sessions-BtrLzv5U.mjs} +4 -4
- package/dist/{sessions-DyC5Attg.cjs → sessions-Q4BR02dG.cjs} +4 -4
- package/dist/{spawn-gha6nGx-.cjs → spawn-CMjhlwlD.cjs} +4 -4
- package/dist/{spawn-QtKlr8Ig.mjs → spawn-DuQXydqI.mjs} +4 -4
- package/dist/{teams-BbzWNV9e.cjs → teams-CkdUH10-.cjs} +4 -4
- package/dist/{teams-BF-GxUZc.mjs → teams-D5HxnyaW.mjs} +4 -4
- package/dist/{todo-CkJ5UjWs.mjs → todo-BiLIZH4t.mjs} +3 -3
- package/dist/{todo-BaNaNZ8c.cjs → todo-CaYedmtq.cjs} +3 -3
- package/dist/{types-Cq-tvv9t.mjs → types-BKrZubg8.mjs} +45 -9
- package/dist/{types-Dabm17Ln.cjs → types-DbBPNIwf.cjs} +46 -9
- package/package.json +8 -8
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
var chalk = require('chalk');
|
|
4
4
|
var os = require('node:os');
|
|
5
5
|
var node_crypto = require('node:crypto');
|
|
6
|
-
var api = require('./types-
|
|
7
|
-
var persistence = require('./limits-
|
|
6
|
+
var api = require('./types-DbBPNIwf.cjs');
|
|
7
|
+
var persistence = require('./limits-DRrdJ7gr.cjs');
|
|
8
8
|
var spawn = require('cross-spawn');
|
|
9
9
|
var path = require('node:path');
|
|
10
10
|
var node_readline = require('node:readline');
|
|
11
11
|
var node_fs = require('node:fs');
|
|
12
|
-
var agentGuidance = require('./agentGuidance-
|
|
12
|
+
var agentGuidance = require('./agentGuidance-6yh_wPwD.cjs');
|
|
13
13
|
var promises = require('node:fs/promises');
|
|
14
14
|
var fs = require('fs/promises');
|
|
15
15
|
var ink = require('ink');
|
|
@@ -342,6 +342,20 @@ class Session {
|
|
|
342
342
|
sessionId;
|
|
343
343
|
mode = "local";
|
|
344
344
|
thinking = false;
|
|
345
|
+
/**
|
|
346
|
+
* Window after a user-initiated abort during which stray promise rejections
|
|
347
|
+
* from tearing down the SDK query (MCP transports, in-flight tool calls,
|
|
348
|
+
* the aborted streaming generator) are benign teardown noise — NOT a real
|
|
349
|
+
* crash. The process-level unhandledRejection/uncaughtException handlers
|
|
350
|
+
* must not treat them as fatal and force-archive the session (the reported
|
|
351
|
+
* "点终止就 archive" bug). Self-expiring so a genuine later crash still
|
|
352
|
+
* archives normally.
|
|
353
|
+
*/
|
|
354
|
+
abortingUntil = 0;
|
|
355
|
+
markAborting = (windowMs = 8e3) => {
|
|
356
|
+
this.abortingUntil = Date.now() + windowMs;
|
|
357
|
+
};
|
|
358
|
+
isAborting = () => Date.now() < this.abortingUntil;
|
|
345
359
|
/**
|
|
346
360
|
* Account-encrypted notification producer. Set lazily once we have a
|
|
347
361
|
* server session id (notifications are addressed per session). May be
|
|
@@ -1353,6 +1367,152 @@ async function claudeLocalLauncher(session) {
|
|
|
1353
1367
|
return exitReason || { type: "exit", code: 0 };
|
|
1354
1368
|
}
|
|
1355
1369
|
|
|
1370
|
+
const text = (value) => typeof value === "string" && value.length > 0 && value.length <= 1024 && !value.includes("\0") ? value : null;
|
|
1371
|
+
const publicMcpStatus = (servers) => servers.map(({ name, status, serverInfo, scope, tools }) => ({ name, status, serverInfo, scope, tools: tools?.map(({ name: name2 }) => ({ name: name2 })) }));
|
|
1372
|
+
const fingerprint = (result) => JSON.stringify([result.canRewind, [...result.filesChanged ?? []].sort(), result.insertions, result.deletions]);
|
|
1373
|
+
function isRuntimeCheckpointReplay(message) {
|
|
1374
|
+
const m = message;
|
|
1375
|
+
return m.type === "user" && m.isReplay === true && !m.parent_tool_use_id && !(Array.isArray(m.message?.content) && m.message.content.some((block) => block.type === "tool_result"));
|
|
1376
|
+
}
|
|
1377
|
+
function createRuntimeControls(isThinking) {
|
|
1378
|
+
let query = null, generation = node_crypto.randomInt(0, 2 ** 40), activityRevision = 0;
|
|
1379
|
+
const operations = /* @__PURE__ */ new Map();
|
|
1380
|
+
const confirmations = /* @__PURE__ */ new Map();
|
|
1381
|
+
const tasks = /* @__PURE__ */ new Map();
|
|
1382
|
+
const checkpoints = /* @__PURE__ */ new Map();
|
|
1383
|
+
let pending = false;
|
|
1384
|
+
const setQuery = (next) => {
|
|
1385
|
+
for (const operation of operations.values()) if (operation.status === "running") {
|
|
1386
|
+
operation.status = "failed";
|
|
1387
|
+
operation.error = "Claude query ended before this operation completed";
|
|
1388
|
+
}
|
|
1389
|
+
query = next;
|
|
1390
|
+
generation++;
|
|
1391
|
+
pending = false;
|
|
1392
|
+
confirmations.clear();
|
|
1393
|
+
tasks.clear();
|
|
1394
|
+
checkpoints.clear();
|
|
1395
|
+
};
|
|
1396
|
+
function observe(message) {
|
|
1397
|
+
const m = message;
|
|
1398
|
+
if (m.type === "user" || m.type === "assistant") {
|
|
1399
|
+
activityRevision++;
|
|
1400
|
+
confirmations.clear();
|
|
1401
|
+
}
|
|
1402
|
+
const content = typeof m.message?.content === "string" ? m.message.content : Array.isArray(m.message?.content) ? m.message.content.filter((block) => block.type === "text").map((block) => block.text).join(" ") : "";
|
|
1403
|
+
if (isRuntimeCheckpointReplay(message) && text(m.uuid) && content) {
|
|
1404
|
+
checkpoints.set(m.uuid, { id: m.uuid, title: content.slice(0, 120) });
|
|
1405
|
+
if (checkpoints.size > 100) checkpoints.delete(checkpoints.keys().next().value);
|
|
1406
|
+
}
|
|
1407
|
+
if (m.type === "system" && text(m.task_id) && ["task_started", "task_updated", "task_progress", "task_notification"].includes(m.subtype)) {
|
|
1408
|
+
const previous = tasks.get(m.task_id);
|
|
1409
|
+
if (m.subtype !== "task_notification" && ["completed", "failed", "stopped", "cancelled"].includes(String(previous?.status))) return;
|
|
1410
|
+
tasks.set(m.task_id, {
|
|
1411
|
+
...previous,
|
|
1412
|
+
id: m.task_id,
|
|
1413
|
+
...text(m.tool_use_id) ? { toolUseId: m.tool_use_id } : {},
|
|
1414
|
+
...text(m.description) ? { description: m.description } : {},
|
|
1415
|
+
...text(m.task_type) ? { type: m.task_type } : {},
|
|
1416
|
+
status: m.subtype === "task_notification" ? m.status ?? "completed" : "running"
|
|
1417
|
+
});
|
|
1418
|
+
if (tasks.size > 100) tasks.delete(tasks.keys().next().value);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function request(input) {
|
|
1422
|
+
const r = input && typeof input === "object" ? input : {};
|
|
1423
|
+
if (r.action === "status") return { queryGeneration: generation, active: !!query, isRunning: isThinking(), canRewind: !!query && !isThinking() && !pending && query.canControl(), busy: pending || !!query && !query.canControl(), tasks: [...tasks.values()], checkpoints: [...checkpoints.values()], operations: [...operations.values()].filter((op2) => op2.queryGeneration === generation) };
|
|
1424
|
+
if (r.action === "operation") {
|
|
1425
|
+
const op2 = operations.get(String(r.operationId));
|
|
1426
|
+
return op2 ? { ...op2 } : { error: "Operation not found" };
|
|
1427
|
+
}
|
|
1428
|
+
const q = query;
|
|
1429
|
+
if (!q) return { error: "No active Claude SDK query" };
|
|
1430
|
+
if (pending || !q.canControl()) return { error: "Claude is waiting for an interaction or another control operation; retry after it finishes" };
|
|
1431
|
+
const action = String(r.action), server = text(r.serverName), task = text(r.taskId), target = text(r.userMessageId);
|
|
1432
|
+
const runGeneration = generation, runRevision = activityRevision;
|
|
1433
|
+
let execute;
|
|
1434
|
+
switch (action) {
|
|
1435
|
+
case "reload-skills":
|
|
1436
|
+
execute = () => q.reloadSkills();
|
|
1437
|
+
break;
|
|
1438
|
+
case "reload-plugins":
|
|
1439
|
+
execute = async () => {
|
|
1440
|
+
const result = await q.reloadPlugins();
|
|
1441
|
+
return { ...result, mcpServers: publicMcpStatus(result.mcpServers ?? []) };
|
|
1442
|
+
};
|
|
1443
|
+
break;
|
|
1444
|
+
case "mcp-status":
|
|
1445
|
+
execute = async () => publicMcpStatus(await q.mcpServerStatus());
|
|
1446
|
+
break;
|
|
1447
|
+
case "mcp-reconnect":
|
|
1448
|
+
if (!server) return { error: "Invalid server name" };
|
|
1449
|
+
execute = () => q.reconnectMcpServer(server);
|
|
1450
|
+
break;
|
|
1451
|
+
case "mcp-toggle":
|
|
1452
|
+
if (!server || typeof r.enabled !== "boolean") return { error: "Invalid server or enabled flag" };
|
|
1453
|
+
execute = () => q.toggleMcpServer(server, r.enabled);
|
|
1454
|
+
break;
|
|
1455
|
+
case "stop-task":
|
|
1456
|
+
if (!task || !tasks.has(task)) return { error: "Unknown task in this query" };
|
|
1457
|
+
execute = () => q.stopTask(task);
|
|
1458
|
+
break;
|
|
1459
|
+
case "background-tasks":
|
|
1460
|
+
if (r.toolUseId !== void 0 && !text(r.toolUseId)) return { error: "Invalid tool use ID" };
|
|
1461
|
+
execute = async () => ({ backgrounded: await q.backgroundTasks(r.toolUseId) });
|
|
1462
|
+
break;
|
|
1463
|
+
case "rewind-preview":
|
|
1464
|
+
if (isThinking() || !target || !checkpoints.has(target)) return { error: "Rewind requires an idle query and a known user checkpoint" };
|
|
1465
|
+
execute = async () => {
|
|
1466
|
+
const result = await q.rewindFiles(target, { dryRun: true });
|
|
1467
|
+
if (!result.canRewind) return result;
|
|
1468
|
+
if (query !== q || generation !== runGeneration || activityRevision !== runRevision || isThinking() || !q.canControl()) throw Error("Query changed; preview again");
|
|
1469
|
+
const token = node_crypto.randomUUID();
|
|
1470
|
+
confirmations.set(token, { generation, revision: activityRevision, userMessageId: target, fingerprint: fingerprint(result), expires: Date.now() + 3e5 });
|
|
1471
|
+
return { ...result, confirmationToken: token };
|
|
1472
|
+
};
|
|
1473
|
+
break;
|
|
1474
|
+
case "rewind-apply": {
|
|
1475
|
+
const confirmation = confirmations.get(String(r.confirmationToken));
|
|
1476
|
+
if (isThinking() || !confirmation || confirmation.generation !== generation || confirmation.expires < Date.now()) return { error: "Preview expired or query is running; preview again" };
|
|
1477
|
+
confirmations.delete(String(r.confirmationToken));
|
|
1478
|
+
execute = async () => {
|
|
1479
|
+
const fresh = await q.rewindFiles(confirmation.userMessageId, { dryRun: true });
|
|
1480
|
+
if (query !== q || generation !== runGeneration || activityRevision !== confirmation.revision || isThinking() || !q.canControl() || fingerprint(fresh) !== confirmation.fingerprint) throw Error("Files or query changed; preview again");
|
|
1481
|
+
return q.rewindFiles(confirmation.userMessageId, { dryRun: false });
|
|
1482
|
+
};
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
default:
|
|
1486
|
+
return { error: "Unknown Claude control action" };
|
|
1487
|
+
}
|
|
1488
|
+
pending = true;
|
|
1489
|
+
const op = { id: node_crypto.randomUUID(), queryGeneration: runGeneration, action, status: "running" };
|
|
1490
|
+
operations.set(op.id, op);
|
|
1491
|
+
if (operations.size > 50) for (const [id, old] of operations) {
|
|
1492
|
+
if (old.status !== "running") {
|
|
1493
|
+
operations.delete(id);
|
|
1494
|
+
break;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
void Promise.resolve().then(() => {
|
|
1498
|
+
if (query !== q || generation !== runGeneration || !q.canControl()) throw Error("Claude query changed or is waiting for an interaction");
|
|
1499
|
+
return execute();
|
|
1500
|
+
}).then((result) => {
|
|
1501
|
+
if (generation !== runGeneration || op.status !== "running") return;
|
|
1502
|
+
op.status = "completed";
|
|
1503
|
+
op.result = result ?? null;
|
|
1504
|
+
}, (error) => {
|
|
1505
|
+
if (generation !== runGeneration || op.status !== "running") return;
|
|
1506
|
+
op.status = "failed";
|
|
1507
|
+
op.error = error instanceof Error ? error.message : "Claude control failed";
|
|
1508
|
+
}).finally(() => {
|
|
1509
|
+
if (generation === runGeneration) pending = false;
|
|
1510
|
+
});
|
|
1511
|
+
return { operationId: op.id };
|
|
1512
|
+
}
|
|
1513
|
+
return { setQuery, observe, request };
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1356
1516
|
class MessageBuffer {
|
|
1357
1517
|
messages = [];
|
|
1358
1518
|
listeners = [];
|
|
@@ -1622,6 +1782,9 @@ function query(params) {
|
|
|
1622
1782
|
tools: opts?.tools,
|
|
1623
1783
|
persistSession: opts?.persistSession,
|
|
1624
1784
|
includePartialMessages: opts?.includePartialMessages,
|
|
1785
|
+
forwardSubagentText: true,
|
|
1786
|
+
enableFileCheckpointing: true,
|
|
1787
|
+
extraArgs: { "replay-user-messages": null },
|
|
1625
1788
|
onElicitation: opts?.onElicitation,
|
|
1626
1789
|
onUserDialog: opts?.onUserDialog,
|
|
1627
1790
|
supportedDialogKinds: opts?.onUserDialog ? opts.supportedDialogKinds : void 0
|
|
@@ -2064,6 +2227,20 @@ async function claudeRemote(opts) {
|
|
|
2064
2227
|
content: initial.message
|
|
2065
2228
|
}
|
|
2066
2229
|
});
|
|
2230
|
+
let callbackDepth = 0;
|
|
2231
|
+
const guardCallback = (callback) => (async (...args) => {
|
|
2232
|
+
callbackDepth++;
|
|
2233
|
+
try {
|
|
2234
|
+
return await callback(...args);
|
|
2235
|
+
} finally {
|
|
2236
|
+
setImmediate(() => {
|
|
2237
|
+
callbackDepth--;
|
|
2238
|
+
});
|
|
2239
|
+
}
|
|
2240
|
+
});
|
|
2241
|
+
if (sdkOptions.canCallTool) sdkOptions.canCallTool = guardCallback(sdkOptions.canCallTool);
|
|
2242
|
+
if (sdkOptions.onElicitation) sdkOptions.onElicitation = guardCallback(sdkOptions.onElicitation);
|
|
2243
|
+
if (sdkOptions.onUserDialog) sdkOptions.onUserDialog = guardCallback(sdkOptions.onUserDialog);
|
|
2067
2244
|
const response = query({
|
|
2068
2245
|
prompt: messages,
|
|
2069
2246
|
options: sdkOptions
|
|
@@ -2084,6 +2261,15 @@ async function claudeRemote(opts) {
|
|
|
2084
2261
|
}
|
|
2085
2262
|
if (opts.onQueryReady) {
|
|
2086
2263
|
opts.onQueryReady({
|
|
2264
|
+
canControl: () => callbackDepth === 0,
|
|
2265
|
+
reloadSkills: () => response.reloadSkills(),
|
|
2266
|
+
reloadPlugins: () => response.reloadPlugins(),
|
|
2267
|
+
mcpServerStatus: () => response.mcpServerStatus(),
|
|
2268
|
+
reconnectMcpServer: (name) => response.reconnectMcpServer(name),
|
|
2269
|
+
toggleMcpServer: (name, enabled) => response.toggleMcpServer(name, enabled),
|
|
2270
|
+
stopTask: (id) => response.stopTask(id),
|
|
2271
|
+
backgroundTasks: (id) => response.backgroundTasks(id),
|
|
2272
|
+
rewindFiles: (id, options) => response.rewindFiles(id, options),
|
|
2087
2273
|
setPermissionMode: (mode2) => response.setPermissionMode(mode2),
|
|
2088
2274
|
setModel: (model) => response.setModel(modelTarget(model)),
|
|
2089
2275
|
interrupt: async () => {
|
|
@@ -2727,7 +2913,7 @@ class PermissionHandler {
|
|
|
2727
2913
|
} else if (pending.kind === "elicitation") {
|
|
2728
2914
|
pending.resolve(message.approved ? { action: "accept", content: elicitationContent(message.updatedInput) } : { action: message.decision === "abort" ? "cancel" : "decline" });
|
|
2729
2915
|
} else {
|
|
2730
|
-
pending.resolve(message.approved ? { behavior: "completed", result:
|
|
2916
|
+
pending.resolve(message.approved && pending.request.dialogKind === "refusal_fallback_prompt" && (message.updatedInput?.result === void 0 || message.updatedInput.result === "retry_fallback") ? { behavior: "completed", result: "retry_fallback" } : { behavior: "cancelled" });
|
|
2731
2917
|
}
|
|
2732
2918
|
this.recordCompletedRequest(id, effectiveResponse);
|
|
2733
2919
|
this.scheduleDeferredPermissionModeUpdate();
|
|
@@ -3189,6 +3375,7 @@ class SDKToLogConverter {
|
|
|
3189
3375
|
}
|
|
3190
3376
|
}
|
|
3191
3377
|
|
|
3378
|
+
const transcriptSystemSubtypes = /* @__PURE__ */ new Set(["task_started", "task_updated", "task_progress", "task_notification"]);
|
|
3192
3379
|
class OutgoingMessageQueue {
|
|
3193
3380
|
constructor(sendFunction) {
|
|
3194
3381
|
this.sendFunction = sendFunction;
|
|
@@ -3270,7 +3457,7 @@ class OutgoingMessageQueue {
|
|
|
3270
3457
|
break;
|
|
3271
3458
|
}
|
|
3272
3459
|
if (!item.sent) {
|
|
3273
|
-
if (item.logMessage.type !== "system") {
|
|
3460
|
+
if (item.logMessage.type !== "system" || transcriptSystemSubtypes.has(item.logMessage.subtype)) {
|
|
3274
3461
|
this.sendFunction(item.logMessage);
|
|
3275
3462
|
}
|
|
3276
3463
|
item.sent = true;
|
|
@@ -3752,6 +3939,18 @@ function createTurnSteeringController() {
|
|
|
3752
3939
|
throw error;
|
|
3753
3940
|
}
|
|
3754
3941
|
},
|
|
3942
|
+
/**
|
|
3943
|
+
* Interrupt the current turn WITHOUT injecting a follow-up message —
|
|
3944
|
+
* used by the stop button. Like steer it keeps the streaming query
|
|
3945
|
+
* alive (same process, same claudeSessionId); unlike steer it does not
|
|
3946
|
+
* set the steering flag, so the turn simply ends and the query waits
|
|
3947
|
+
* for the next user message. Returns false when no query is attached.
|
|
3948
|
+
*/
|
|
3949
|
+
async interruptTurn() {
|
|
3950
|
+
if (!interrupt) return false;
|
|
3951
|
+
await interrupt();
|
|
3952
|
+
return true;
|
|
3953
|
+
},
|
|
3755
3954
|
consumeReady() {
|
|
3756
3955
|
if (!steering) return false;
|
|
3757
3956
|
steering = false;
|
|
@@ -3860,6 +4059,7 @@ class LaunchModeGate {
|
|
|
3860
4059
|
}
|
|
3861
4060
|
}
|
|
3862
4061
|
|
|
4062
|
+
const ABORT_INTERRUPT_GRACE_MS = 4e3;
|
|
3863
4063
|
async function claudeRemoteLauncher(session, onPermissionModeChange, onEffectivePermissionMode, onPromptFinalized) {
|
|
3864
4064
|
persistence.logger.debug("[claudeRemoteLauncher] Starting remote launcher");
|
|
3865
4065
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
@@ -3898,6 +4098,8 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
3898
4098
|
let abortController = null;
|
|
3899
4099
|
let abortFuture = null;
|
|
3900
4100
|
const turnSteering = createTurnSteeringController();
|
|
4101
|
+
const runtimeControls = createRuntimeControls(() => session.thinking);
|
|
4102
|
+
session.client.rpcHandlerManager.registerHandler("claude-runtime-control", async (input) => runtimeControls.request(input));
|
|
3901
4103
|
async function abort() {
|
|
3902
4104
|
if (abortController && !abortController.signal.aborted) {
|
|
3903
4105
|
abortController.abort();
|
|
@@ -3906,9 +4108,29 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
3906
4108
|
}
|
|
3907
4109
|
async function doAbort() {
|
|
3908
4110
|
persistence.logger.debug("[remote]: doAbort");
|
|
4111
|
+
session.markAborting();
|
|
3909
4112
|
session.onAbort();
|
|
4113
|
+
if (session.thinking) {
|
|
4114
|
+
const interrupted = await turnSteering.interruptTurn().catch((e) => {
|
|
4115
|
+
persistence.logger.debug("[remote]: interruptTurn threw, falling back to hard abort", e);
|
|
4116
|
+
return false;
|
|
4117
|
+
});
|
|
4118
|
+
if (interrupted && await waitForThinkingToStop(ABORT_INTERRUPT_GRACE_MS)) {
|
|
4119
|
+
persistence.logger.debug("[remote]: interrupt stopped the turn gracefully; query kept alive");
|
|
4120
|
+
return;
|
|
4121
|
+
}
|
|
4122
|
+
persistence.logger.debug("[remote]: interrupt did not stop the turn in time; escalating to hard abort");
|
|
4123
|
+
}
|
|
3910
4124
|
await abort();
|
|
3911
4125
|
}
|
|
4126
|
+
async function waitForThinkingToStop(graceMs) {
|
|
4127
|
+
const deadline = Date.now() + graceMs;
|
|
4128
|
+
while (Date.now() < deadline) {
|
|
4129
|
+
if (!session.thinking) return true;
|
|
4130
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4131
|
+
}
|
|
4132
|
+
return !session.thinking;
|
|
4133
|
+
}
|
|
3912
4134
|
async function doSwitch() {
|
|
3913
4135
|
persistence.logger.debug("[remote]: doSwitch");
|
|
3914
4136
|
if (!exitReason) {
|
|
@@ -3958,6 +4180,8 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
3958
4180
|
let ongoingToolCalls = /* @__PURE__ */ new Map();
|
|
3959
4181
|
let notifiedQuestionToolCalls = /* @__PURE__ */ new Set();
|
|
3960
4182
|
function onMessage(message) {
|
|
4183
|
+
runtimeControls.observe(message);
|
|
4184
|
+
if (isRuntimeCheckpointReplay(message)) return;
|
|
3961
4185
|
if (message.type === "user" && isClaudeInterruptSentinelContent(message.message.content)) {
|
|
3962
4186
|
sdkToLogConverter.convert(message);
|
|
3963
4187
|
return;
|
|
@@ -4180,6 +4404,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4180
4404
|
if (metadata.permissionMode) onEffectivePermissionMode?.(metadata.permissionMode);
|
|
4181
4405
|
},
|
|
4182
4406
|
onQueryReady: (q) => {
|
|
4407
|
+
runtimeControls.setQuery(q);
|
|
4183
4408
|
turnSteering.setInterrupt(q.interrupt);
|
|
4184
4409
|
session.setSteerHandler(async (input) => {
|
|
4185
4410
|
if (!modeGate.matches(input.mode)) return false;
|
|
@@ -4269,6 +4494,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4269
4494
|
continue;
|
|
4270
4495
|
}
|
|
4271
4496
|
} finally {
|
|
4497
|
+
runtimeControls.setQuery(null);
|
|
4272
4498
|
turnSteering.reset();
|
|
4273
4499
|
session.setSteerHandler(null);
|
|
4274
4500
|
livePermissionModeHandler = null;
|
|
@@ -5319,6 +5545,80 @@ ${typeLabels[type] || type}:`));
|
|
|
5319
5545
|
console.log(chalk.green("\n\u2705 Doctor diagnosis complete!\n"));
|
|
5320
5546
|
}
|
|
5321
5547
|
|
|
5548
|
+
const AGENT_CHECK_INTERVAL = 6 * 60 * 60 * 1e3;
|
|
5549
|
+
function stableVersion(value) {
|
|
5550
|
+
const match = value.trim().match(/(?:^|\s|v)(\d+\.\d+\.\d+)([-+][\w.-]+)?(?=\s|$)/);
|
|
5551
|
+
return match && !match[2] ? match[1] : null;
|
|
5552
|
+
}
|
|
5553
|
+
function newerVersion(current, latest) {
|
|
5554
|
+
const a = current.split(".").map(Number), b = latest.split(".").map(Number);
|
|
5555
|
+
for (let i = 0; i < 3; i++) {
|
|
5556
|
+
if (b[i] !== a[i]) return b[i] > a[i];
|
|
5557
|
+
}
|
|
5558
|
+
return false;
|
|
5559
|
+
}
|
|
5560
|
+
const runVersionCommand = (command, args, shell) => new Promise((resolve, reject) => {
|
|
5561
|
+
node_child_process.execFile(
|
|
5562
|
+
command,
|
|
5563
|
+
args,
|
|
5564
|
+
{ timeout: 5e3, maxBuffer: 32 * 1024, windowsHide: true, shell },
|
|
5565
|
+
(error, stdout) => error ? reject(error) : resolve(stdout)
|
|
5566
|
+
);
|
|
5567
|
+
});
|
|
5568
|
+
async function probeAgentVersion(command, platform = process.platform, run = runVersionCommand) {
|
|
5569
|
+
if (platform === "win32") {
|
|
5570
|
+
try {
|
|
5571
|
+
await run("where.exe", [command], false);
|
|
5572
|
+
} catch (error) {
|
|
5573
|
+
return { version: null, missing: error.code === 1 };
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
try {
|
|
5577
|
+
return { version: stableVersion(await run(command, ["--version"], platform === "win32")), missing: false };
|
|
5578
|
+
} catch (error) {
|
|
5579
|
+
return { version: null, missing: error.code === "ENOENT" };
|
|
5580
|
+
}
|
|
5581
|
+
}
|
|
5582
|
+
async function registryVersion(name) {
|
|
5583
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`, { signal: AbortSignal.timeout(5e3) });
|
|
5584
|
+
if (!response.ok) throw new Error("Registry unavailable");
|
|
5585
|
+
const data = await response.json();
|
|
5586
|
+
return typeof data.version === "string" ? stableVersion(data.version) : null;
|
|
5587
|
+
}
|
|
5588
|
+
async function detectAgentVersions(deps = { probe: probeAgentVersion, registryVersion }, now = Date.now()) {
|
|
5589
|
+
const agents = await Promise.all(api.AGENT_VERSION_SOURCES.map(async (source) => {
|
|
5590
|
+
const local = source.command ? await deps.probe(source.command).catch(() => ({ version: null, missing: false })) : { version: stableVersion(persistence.packageJson.dependencies["@anthropic-ai/claude-agent-sdk"]), missing: false };
|
|
5591
|
+
if (local.missing) return { id: source.id, installed: null, latest: null, status: "not-installed" };
|
|
5592
|
+
const latest = await deps.registryVersion(source.package).catch(() => null);
|
|
5593
|
+
const installed = local.version;
|
|
5594
|
+
const status = !installed || !latest ? "unknown" : newerVersion(installed, latest) ? "update-available" : "current";
|
|
5595
|
+
return { id: source.id, installed, latest, status };
|
|
5596
|
+
}));
|
|
5597
|
+
return { checkedAt: now, daemonPid: process.pid, agents };
|
|
5598
|
+
}
|
|
5599
|
+
function startAgentVersionChecks(publish, onError, detect = detectAgentVersions) {
|
|
5600
|
+
let stopped = false, pending = false;
|
|
5601
|
+
async function refresh() {
|
|
5602
|
+
if (stopped || pending) return;
|
|
5603
|
+
pending = true;
|
|
5604
|
+
try {
|
|
5605
|
+
const snapshot = await detect();
|
|
5606
|
+
if (!stopped) await publish(snapshot);
|
|
5607
|
+
} catch (error) {
|
|
5608
|
+
onError(error);
|
|
5609
|
+
} finally {
|
|
5610
|
+
pending = false;
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5613
|
+
void refresh();
|
|
5614
|
+
const timer = setInterval(() => void refresh(), AGENT_CHECK_INTERVAL);
|
|
5615
|
+
timer.unref();
|
|
5616
|
+
return () => {
|
|
5617
|
+
stopped = true;
|
|
5618
|
+
clearInterval(timer);
|
|
5619
|
+
};
|
|
5620
|
+
}
|
|
5621
|
+
|
|
5322
5622
|
function sanitizeSpawnModel(value) {
|
|
5323
5623
|
if (typeof value !== "string") return null;
|
|
5324
5624
|
return /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}(\[1m\])?$/i.test(value) ? value : null;
|
|
@@ -7246,10 +7546,13 @@ function buildResumeLaunch(session, options = {}) {
|
|
|
7246
7546
|
const { metadata } = session;
|
|
7247
7547
|
const flavor = resolveFlavor(metadata);
|
|
7248
7548
|
if (flavor === "codex") {
|
|
7249
|
-
|
|
7250
|
-
|
|
7549
|
+
const args = ["codex"];
|
|
7550
|
+
if (options.freshConversation) ; else {
|
|
7551
|
+
if (!metadata.codexThreadId) {
|
|
7552
|
+
throw new Error(`Happy session ${session.id} is missing its Codex thread ID.`);
|
|
7553
|
+
}
|
|
7554
|
+
args.push("--resume", metadata.codexThreadId);
|
|
7251
7555
|
}
|
|
7252
|
-
const args = ["codex", "--resume", metadata.codexThreadId];
|
|
7253
7556
|
if (options.startedBy) {
|
|
7254
7557
|
args.push("--started-by", options.startedBy);
|
|
7255
7558
|
}
|
|
@@ -7259,9 +7562,6 @@ function buildResumeLaunch(session, options = {}) {
|
|
|
7259
7562
|
};
|
|
7260
7563
|
}
|
|
7261
7564
|
if (flavor === "claude") {
|
|
7262
|
-
if (!metadata.claudeSessionId) {
|
|
7263
|
-
throw new Error(`Happy session ${session.id} is missing its Claude session ID.`);
|
|
7264
|
-
}
|
|
7265
7565
|
const args = ["claude"];
|
|
7266
7566
|
if (options.claudeStartingMode) {
|
|
7267
7567
|
args.push("--happy-starting-mode", options.claudeStartingMode);
|
|
@@ -7269,7 +7569,12 @@ function buildResumeLaunch(session, options = {}) {
|
|
|
7269
7569
|
if (options.startedBy) {
|
|
7270
7570
|
args.push("--started-by", options.startedBy);
|
|
7271
7571
|
}
|
|
7272
|
-
|
|
7572
|
+
if (options.freshConversation) ; else {
|
|
7573
|
+
if (!metadata.claudeSessionId) {
|
|
7574
|
+
throw new Error(`Happy session ${session.id} is missing its Claude session ID.`);
|
|
7575
|
+
}
|
|
7576
|
+
args.push("--resume", metadata.claudeSessionId);
|
|
7577
|
+
}
|
|
7273
7578
|
return {
|
|
7274
7579
|
cwd: metadata.path,
|
|
7275
7580
|
args
|
|
@@ -7750,7 +8055,7 @@ function parseJsonObject(text) {
|
|
|
7750
8055
|
return null;
|
|
7751
8056
|
}
|
|
7752
8057
|
}
|
|
7753
|
-
function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
8058
|
+
function resolveSdkClaudeBinary(platform = process.platform, arch = process.arch, resolver = (s) => node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-8bwU9d-4.cjs', document.baseURI).href))).resolve(s)) {
|
|
7754
8059
|
const platformArch = `${platform}-${arch}`;
|
|
7755
8060
|
const candidates = [
|
|
7756
8061
|
`@anthropic-ai/claude-agent-sdk-${platformArch}/claude`,
|
|
@@ -10568,12 +10873,18 @@ async function startDaemon() {
|
|
|
10568
10873
|
cwdExists: (p) => fs$1.existsSync(p),
|
|
10569
10874
|
conversationExists: (cwd, claudeSessionId) => fs$1.existsSync(path$1.join(api.getProjectPath(cwd), `${claudeSessionId}.jsonl`))
|
|
10570
10875
|
});
|
|
10876
|
+
let freshConversation = false;
|
|
10571
10877
|
if (!precheck.ok) {
|
|
10572
|
-
|
|
10878
|
+
if (precheck.reason === "conversation-missing") {
|
|
10879
|
+
persistence.logger.debug(`[DAEMON RUN] resume: transcript for ${happySessionId} is gone (${precheck.detail}); starting a fresh conversation in ${metadata.path}`);
|
|
10880
|
+
freshConversation = true;
|
|
10881
|
+
} else {
|
|
10882
|
+
return { type: "error", errorMessage: `resume-precheck:${precheck.reason}: ${precheck.detail}` };
|
|
10883
|
+
}
|
|
10573
10884
|
}
|
|
10574
10885
|
const launch = buildResumeLaunch(
|
|
10575
10886
|
{ id: happySessionId, active: true, metadata },
|
|
10576
|
-
{ startedBy: "daemon", claudeStartingMode: "remote" }
|
|
10887
|
+
{ startedBy: "daemon", claudeStartingMode: "remote", freshConversation }
|
|
10577
10888
|
);
|
|
10578
10889
|
const model = sanitizeResumeModel(options?.model);
|
|
10579
10890
|
if (model) launch.args.push("--model", model);
|
|
@@ -10991,6 +11302,10 @@ async function startDaemon() {
|
|
|
10991
11302
|
persistence.logger.warn(`[DAEMON RUN] Ignoring invalid HAPPY_CLI_UPDATE_CHECK_INTERVAL; using ${api.DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS}ms`);
|
|
10992
11303
|
}
|
|
10993
11304
|
const cliUpdateInterval = setInterval(() => void refreshCliUpdate(), cliUpdateIntervalMs);
|
|
11305
|
+
const stopAgentVersionChecks = startAgentVersionChecks(
|
|
11306
|
+
(snapshot) => apiMachine.updateDaemonState((state) => ({ ...state, status: state?.status ?? "running", agentVersions: snapshot })),
|
|
11307
|
+
(error) => persistence.logger.debug("[DAEMON RUN] Agent version check failed", error)
|
|
11308
|
+
);
|
|
10994
11309
|
const heartbeatIntervalMs = parseInt(process.env.HAPPY_DAEMON_HEARTBEAT_INTERVAL || "60000");
|
|
10995
11310
|
const restartOnStaleVersionAndHeartbeat = setInterval(serialTask(async () => {
|
|
10996
11311
|
try {
|
|
@@ -11031,6 +11346,7 @@ async function startDaemon() {
|
|
|
11031
11346
|
teamWorker?.stop();
|
|
11032
11347
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
11033
11348
|
clearInterval(cliUpdateInterval);
|
|
11349
|
+
stopAgentVersionChecks();
|
|
11034
11350
|
claudeAuthService.stop();
|
|
11035
11351
|
apiMachine.shutdown();
|
|
11036
11352
|
await stopControlServer();
|
|
@@ -11092,6 +11408,7 @@ async function startDaemon() {
|
|
|
11092
11408
|
persistence.logger.debug("[DAEMON RUN] Health check interval cleared");
|
|
11093
11409
|
}
|
|
11094
11410
|
clearInterval(cliUpdateInterval);
|
|
11411
|
+
stopAgentVersionChecks();
|
|
11095
11412
|
await apiMachine.updateDaemonState((state) => ({
|
|
11096
11413
|
...state,
|
|
11097
11414
|
status: "shutting-down",
|
|
@@ -12181,7 +12498,7 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12181
12498
|
const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
|
|
12182
12499
|
const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
|
|
12183
12500
|
const run = deps.run ?? (async (input) => {
|
|
12184
|
-
const { query } = await Promise.resolve().then(function () { return require('./index-
|
|
12501
|
+
const { query } = await Promise.resolve().then(function () { return require('./index-BqbtKX0T.cjs'); });
|
|
12185
12502
|
return runSideQuestion(query, input);
|
|
12186
12503
|
});
|
|
12187
12504
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12283,7 +12600,7 @@ const MAX_TITLE_CHARS = 60;
|
|
|
12283
12600
|
const GENERATION_TIMEOUT_MS = 3e4;
|
|
12284
12601
|
function resolveClaudeBinary() {
|
|
12285
12602
|
try {
|
|
12286
|
-
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
12603
|
+
const require$1 = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-8bwU9d-4.cjs', document.baseURI).href)));
|
|
12287
12604
|
const utilsPath = path.resolve(path.join(api.projectPath(), "scripts", "claude_version_utils.cjs"));
|
|
12288
12605
|
const { getClaudeCliPath } = require$1(utilsPath);
|
|
12289
12606
|
const path$1 = getClaudeCliPath();
|
|
@@ -12744,7 +13061,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
12744
13061
|
// a brand-new session cannot pick a PDF until after sending once.
|
|
12745
13062
|
attachmentKinds: [...CLAUDE_ATTACHMENT_KINDS],
|
|
12746
13063
|
queueCancellation: true,
|
|
12747
|
-
capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2", "claude-btw-v1"],
|
|
13064
|
+
capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2", "claude-btw-v1", "claude-runtime-controls-v1"],
|
|
12748
13065
|
// Effective mode this process enforces. Kept current by
|
|
12749
13066
|
// publishPermissionMode below; the web renders it instead of guessing.
|
|
12750
13067
|
permissionMode: mapToClaudeMode(initialPermissionMode ?? "default"),
|
|
@@ -13323,10 +13640,18 @@ async function runClaude(credentials, options = {}) {
|
|
|
13323
13640
|
});
|
|
13324
13641
|
process.on("uncaughtException", (error) => {
|
|
13325
13642
|
persistence.logger.debug("[START] Uncaught exception:", error);
|
|
13643
|
+
if (currentSession?.isAborting()) {
|
|
13644
|
+
persistence.logger.debug("[START] Uncaught exception during abort teardown \u2014 swallowing, not archiving");
|
|
13645
|
+
return;
|
|
13646
|
+
}
|
|
13326
13647
|
void cleanup({ archive: true });
|
|
13327
13648
|
});
|
|
13328
13649
|
process.on("unhandledRejection", (reason) => {
|
|
13329
13650
|
persistence.logger.debug("[START] Unhandled rejection:", reason);
|
|
13651
|
+
if (currentSession?.isAborting()) {
|
|
13652
|
+
persistence.logger.debug("[START] Unhandled rejection during abort teardown \u2014 swallowing, not archiving");
|
|
13653
|
+
return;
|
|
13654
|
+
}
|
|
13330
13655
|
void cleanup({ archive: true });
|
|
13331
13656
|
});
|
|
13332
13657
|
registerKillSessionHandler(session.rpcHandlerManager, () => cleanup({ archive: true }), session);
|
|
@@ -14501,9 +14826,9 @@ function resolveLocalSignupBootstrap(configuredMode, configuredInviteCodes, gene
|
|
|
14501
14826
|
};
|
|
14502
14827
|
}
|
|
14503
14828
|
|
|
14504
|
-
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
14829
|
+
const __filename$1 = node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-8bwU9d-4.cjs', document.baseURI).href)));
|
|
14505
14830
|
const __dirname$1 = path.dirname(__filename$1);
|
|
14506
|
-
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-
|
|
14831
|
+
const require_ = node_module.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index-8bwU9d-4.cjs', document.baseURI).href)));
|
|
14507
14832
|
const PRISMA_QUERY_ENGINE_FILES = {
|
|
14508
14833
|
"arm64-darwin": "libquery_engine-darwin-arm64.dylib.node",
|
|
14509
14834
|
"x64-darwin": "libquery_engine-darwin.dylib.node",
|
|
@@ -14925,1269 +15250,1325 @@ async function ensureDaemonRunning() {
|
|
|
14925
15250
|
persistence.logger.debug(`Happy background service did not become ready within ${DAEMON_READY_TIMEOUT_MS}ms; continuing anyway`);
|
|
14926
15251
|
}
|
|
14927
15252
|
|
|
14928
|
-
function
|
|
14929
|
-
|
|
14930
|
-
const attachment = fileEvent.content.data.ev;
|
|
14931
|
-
const download = (async () => {
|
|
14932
|
-
try {
|
|
14933
|
-
const data = await session.downloadAndDecryptAttachment(attachment.ref);
|
|
14934
|
-
if (!data) throw new Error("Attachment decryption failed");
|
|
14935
|
-
return { data, mimeType: attachment.mimeType ?? "image/jpeg", name: attachment.name };
|
|
14936
|
-
} catch {
|
|
14937
|
-
session.sendSessionEvent({ type: "message", message: `Could not download attachment: ${attachment.name}. Please attach it again.` });
|
|
14938
|
-
return null;
|
|
14939
|
-
}
|
|
14940
|
-
})();
|
|
14941
|
-
session.trackAttachmentDownload(download);
|
|
14942
|
-
});
|
|
15253
|
+
function getStartedSubagents(state) {
|
|
15254
|
+
return state.startedSubagents ?? /* @__PURE__ */ new Set();
|
|
14943
15255
|
}
|
|
14944
|
-
|
|
14945
|
-
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
15256
|
+
function getActiveSubagents(state) {
|
|
15257
|
+
return state.activeSubagents ?? /* @__PURE__ */ new Set();
|
|
15258
|
+
}
|
|
15259
|
+
function getProviderSubagentToSessionSubagent(state) {
|
|
15260
|
+
return state.providerSubagentToSessionSubagent ?? /* @__PURE__ */ new Map();
|
|
15261
|
+
}
|
|
15262
|
+
function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes) {
|
|
15263
|
+
if (!subagent || startedSubagents.has(subagent)) {
|
|
15264
|
+
return;
|
|
14952
15265
|
}
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
|
|
14957
|
-
|
|
14958
|
-
|
|
14959
|
-
|
|
14960
|
-
|
|
15266
|
+
envelopes.push(api.createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
|
|
15267
|
+
startedSubagents.add(subagent);
|
|
15268
|
+
activeSubagents.add(subagent);
|
|
15269
|
+
}
|
|
15270
|
+
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15271
|
+
const envelopes = [];
|
|
15272
|
+
for (const subagent of activeSubagents) {
|
|
15273
|
+
envelopes.push(api.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
14961
15274
|
}
|
|
14962
|
-
|
|
14963
|
-
|
|
14964
|
-
|
|
14965
|
-
|
|
14966
|
-
|
|
14967
|
-
|
|
14968
|
-
|
|
14969
|
-
|
|
14970
|
-
|
|
14971
|
-
|
|
14972
|
-
|
|
14973
|
-
|
|
14974
|
-
|
|
14975
|
-
|
|
14976
|
-
|
|
14977
|
-
|
|
14978
|
-
const request = currentState.requests?.[response.id];
|
|
14979
|
-
if (!request) return currentState;
|
|
14980
|
-
const { [response.id]: _, ...remainingRequests } = currentState.requests || {};
|
|
14981
|
-
let res = {
|
|
14982
|
-
...currentState,
|
|
14983
|
-
requests: remainingRequests,
|
|
14984
|
-
completedRequests: {
|
|
14985
|
-
...currentState.completedRequests,
|
|
14986
|
-
[response.id]: {
|
|
14987
|
-
...request,
|
|
14988
|
-
completedAt: Date.now(),
|
|
14989
|
-
status: response.approved ? "approved" : "denied",
|
|
14990
|
-
decision: result.decision
|
|
14991
|
-
}
|
|
14992
|
-
}
|
|
14993
|
-
};
|
|
14994
|
-
return res;
|
|
14995
|
-
});
|
|
14996
|
-
persistence.logger.debug(`${this.getLogPrefix()} Permission ${response.approved ? "approved" : "denied"} for ${pending.toolName}`);
|
|
14997
|
-
}
|
|
14998
|
-
);
|
|
15275
|
+
activeSubagents.clear();
|
|
15276
|
+
startedSubagents.clear();
|
|
15277
|
+
return envelopes;
|
|
15278
|
+
}
|
|
15279
|
+
function buildEnvelopeOptions(currentTurnId, subagent) {
|
|
15280
|
+
return {
|
|
15281
|
+
...currentTurnId ? { turn: currentTurnId } : {},
|
|
15282
|
+
...subagent ? { subagent } : {}
|
|
15283
|
+
};
|
|
15284
|
+
}
|
|
15285
|
+
function pickProviderSubagent(message) {
|
|
15286
|
+
const candidates = [message.subagent, message.parent_call_id, message.parentCallId];
|
|
15287
|
+
for (const candidate of candidates) {
|
|
15288
|
+
if (typeof candidate === "string" && candidate.length > 0) {
|
|
15289
|
+
return candidate;
|
|
15290
|
+
}
|
|
14999
15291
|
}
|
|
15000
|
-
|
|
15001
|
-
|
|
15002
|
-
|
|
15003
|
-
|
|
15004
|
-
|
|
15005
|
-
|
|
15006
|
-
requests: {
|
|
15007
|
-
...currentState.requests,
|
|
15008
|
-
[toolCallId]: {
|
|
15009
|
-
tool: toolName,
|
|
15010
|
-
arguments: input,
|
|
15011
|
-
createdAt: Date.now()
|
|
15012
|
-
}
|
|
15013
|
-
}
|
|
15014
|
-
}));
|
|
15292
|
+
return void 0;
|
|
15293
|
+
}
|
|
15294
|
+
function resolveSessionSubagent(message, providerSubagentToSessionSubagent) {
|
|
15295
|
+
const providerSubagent = pickProviderSubagent(message);
|
|
15296
|
+
if (!providerSubagent) {
|
|
15297
|
+
return void 0;
|
|
15015
15298
|
}
|
|
15016
|
-
|
|
15017
|
-
|
|
15018
|
-
|
|
15019
|
-
* causing the approval response to send 'cancel' to the provider. This is used when the
|
|
15020
|
-
* user presses the abort/stop button — it unblocks any pending tool approval so the provider
|
|
15021
|
-
* can process the turn cancellation.
|
|
15022
|
-
*/
|
|
15023
|
-
abortAll() {
|
|
15024
|
-
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15025
|
-
if (pendingSnapshot.length === 0) return;
|
|
15026
|
-
this.pendingRequests.clear();
|
|
15027
|
-
for (const [id, pending] of pendingSnapshot) {
|
|
15028
|
-
try {
|
|
15029
|
-
pending.resolve({ decision: "abort" });
|
|
15030
|
-
} catch (err) {
|
|
15031
|
-
persistence.logger.debug(`${this.getLogPrefix()} Error resolving aborted request ${id}:`, err);
|
|
15032
|
-
}
|
|
15033
|
-
}
|
|
15034
|
-
this.session.updateAgentState((currentState) => {
|
|
15035
|
-
const pendingRequests = currentState.requests || {};
|
|
15036
|
-
const completedRequests = { ...currentState.completedRequests };
|
|
15037
|
-
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15038
|
-
completedRequests[id] = {
|
|
15039
|
-
...request,
|
|
15040
|
-
completedAt: Date.now(),
|
|
15041
|
-
status: "canceled",
|
|
15042
|
-
reason: "Aborted by user"
|
|
15043
|
-
};
|
|
15044
|
-
}
|
|
15045
|
-
return {
|
|
15046
|
-
...currentState,
|
|
15047
|
-
requests: {},
|
|
15048
|
-
completedRequests
|
|
15049
|
-
};
|
|
15050
|
-
});
|
|
15051
|
-
persistence.logger.debug(`${this.getLogPrefix()} Aborted ${pendingSnapshot.length} pending permission(s)`);
|
|
15299
|
+
const existing = providerSubagentToSessionSubagent.get(providerSubagent);
|
|
15300
|
+
if (existing) {
|
|
15301
|
+
return existing;
|
|
15052
15302
|
}
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
15061
|
-
}
|
|
15062
|
-
this.isResetting = true;
|
|
15063
|
-
try {
|
|
15064
|
-
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15065
|
-
this.pendingRequests.clear();
|
|
15066
|
-
for (const [id, pending] of pendingSnapshot) {
|
|
15067
|
-
try {
|
|
15068
|
-
pending.reject(new Error("Session reset"));
|
|
15069
|
-
} catch (err) {
|
|
15070
|
-
persistence.logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
15071
|
-
}
|
|
15072
|
-
}
|
|
15073
|
-
this.session.updateAgentState((currentState) => {
|
|
15074
|
-
const pendingRequests = currentState.requests || {};
|
|
15075
|
-
const completedRequests = { ...currentState.completedRequests };
|
|
15076
|
-
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15077
|
-
completedRequests[id] = {
|
|
15078
|
-
...request,
|
|
15079
|
-
completedAt: Date.now(),
|
|
15080
|
-
status: "canceled",
|
|
15081
|
-
reason
|
|
15082
|
-
};
|
|
15083
|
-
}
|
|
15084
|
-
return {
|
|
15085
|
-
...currentState,
|
|
15086
|
-
requests: {},
|
|
15087
|
-
completedRequests
|
|
15088
|
-
};
|
|
15089
|
-
});
|
|
15090
|
-
persistence.logger.debug(`${this.getLogPrefix()} Permission handler reset`);
|
|
15091
|
-
} finally {
|
|
15092
|
-
this.isResetting = false;
|
|
15093
|
-
}
|
|
15303
|
+
const created = cuid2.createId();
|
|
15304
|
+
providerSubagentToSessionSubagent.set(providerSubagent, created);
|
|
15305
|
+
return created;
|
|
15306
|
+
}
|
|
15307
|
+
function pickCallId(message) {
|
|
15308
|
+
const callId = message.call_id ?? message.callId;
|
|
15309
|
+
if (typeof callId === "string" && callId.length > 0) {
|
|
15310
|
+
return callId;
|
|
15094
15311
|
}
|
|
15312
|
+
return node_crypto.randomUUID();
|
|
15095
15313
|
}
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
// form (used by Codex elicitation messages like `tool "change_title"`)
|
|
15100
|
-
// and the MCP-qualified form for defense in depth.
|
|
15101
|
-
static ALWAYS_AUTO_APPROVE_NAMES = /* @__PURE__ */ new Set([
|
|
15102
|
-
"change_title",
|
|
15103
|
-
"mcp__happy__change_title"
|
|
15104
|
-
]);
|
|
15105
|
-
// Tool-call IDs that should auto-approve when they exactly match one of
|
|
15106
|
-
// these values or start with `<name>-` (e.g. `change_title-1765385846663`).
|
|
15107
|
-
// Substring matching was a bypass vector — any tool whose ID happened to
|
|
15108
|
-
// contain `change_title` as a substring would be silently approved.
|
|
15109
|
-
static ALWAYS_AUTO_APPROVE_ID_PREFIXES = [
|
|
15110
|
-
"change_title"
|
|
15111
|
-
];
|
|
15112
|
-
constructor(session) {
|
|
15113
|
-
super(session);
|
|
15314
|
+
function summarizeCommand(command) {
|
|
15315
|
+
if (typeof command === "string" && command.trim().length > 0) {
|
|
15316
|
+
return command;
|
|
15114
15317
|
}
|
|
15115
|
-
|
|
15116
|
-
|
|
15318
|
+
if (Array.isArray(command)) {
|
|
15319
|
+
const cmd = command.map((v) => String(v)).join(" ").trim();
|
|
15320
|
+
return cmd.length > 0 ? cmd : null;
|
|
15117
15321
|
}
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
|
|
15122
|
-
|
|
15123
|
-
if (toolCallId === prefix || toolCallId.startsWith(`${prefix}-`)) {
|
|
15124
|
-
return true;
|
|
15125
|
-
}
|
|
15126
|
-
}
|
|
15127
|
-
return false;
|
|
15322
|
+
return null;
|
|
15323
|
+
}
|
|
15324
|
+
function commandToTitle(command) {
|
|
15325
|
+
if (!command) {
|
|
15326
|
+
return "Run command";
|
|
15128
15327
|
}
|
|
15129
|
-
|
|
15130
|
-
|
|
15131
|
-
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15135
|
-
|
|
15136
|
-
|
|
15137
|
-
|
|
15138
|
-
|
|
15139
|
-
|
|
15140
|
-
|
|
15141
|
-
|
|
15142
|
-
|
|
15143
|
-
[toolCallId]: {
|
|
15144
|
-
tool: toolName,
|
|
15145
|
-
arguments: input,
|
|
15146
|
-
createdAt: Date.now(),
|
|
15147
|
-
completedAt: Date.now(),
|
|
15148
|
-
status: "approved",
|
|
15149
|
-
decision: "approved"
|
|
15150
|
-
}
|
|
15151
|
-
}
|
|
15152
|
-
}));
|
|
15153
|
-
return { decision: "approved" };
|
|
15154
|
-
}
|
|
15155
|
-
return new Promise((resolve, reject) => {
|
|
15156
|
-
this.pendingRequests.set(toolCallId, {
|
|
15157
|
-
resolve,
|
|
15158
|
-
reject,
|
|
15159
|
-
toolName,
|
|
15160
|
-
input
|
|
15161
|
-
});
|
|
15162
|
-
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
15163
|
-
persistence.logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})`);
|
|
15164
|
-
});
|
|
15328
|
+
const short = command.length > 80 ? `${command.slice(0, 77)}...` : command;
|
|
15329
|
+
return `Run \`${short}\``;
|
|
15330
|
+
}
|
|
15331
|
+
function turnTimestampMs(turn) {
|
|
15332
|
+
const seconds = turn.startedAt ?? turn.completedAt;
|
|
15333
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
|
|
15334
|
+
}
|
|
15335
|
+
function completedTimestampMs(turn) {
|
|
15336
|
+
const seconds = turn.completedAt ?? turn.startedAt;
|
|
15337
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
|
|
15338
|
+
}
|
|
15339
|
+
function textFromInputItems(items) {
|
|
15340
|
+
if (!Array.isArray(items)) {
|
|
15341
|
+
return null;
|
|
15165
15342
|
}
|
|
15343
|
+
const text = items.filter((item) => Boolean(item) && typeof item === "object" && item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n").trim();
|
|
15344
|
+
return text.length > 0 ? text : null;
|
|
15166
15345
|
}
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15346
|
+
function reasoningText(item) {
|
|
15347
|
+
const summary = item.summary;
|
|
15348
|
+
const content = item.content;
|
|
15349
|
+
const parts = [
|
|
15350
|
+
...Array.isArray(summary) ? summary : [],
|
|
15351
|
+
...Array.isArray(content) ? content : []
|
|
15352
|
+
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
15353
|
+
const text = parts.join("\n").trim();
|
|
15354
|
+
return text.length > 0 ? text : null;
|
|
15355
|
+
}
|
|
15356
|
+
function turnStatus(turn) {
|
|
15357
|
+
const status = typeof turn.status === "string" ? turn.status : null;
|
|
15358
|
+
if (status === "failed") {
|
|
15359
|
+
return "failed";
|
|
15181
15360
|
}
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
*/
|
|
15185
|
-
setMessageCallback(callback) {
|
|
15186
|
-
this.onMessage = callback;
|
|
15361
|
+
if (status === "cancelled" || status === "canceled" || status === "aborted" || status === "interrupted") {
|
|
15362
|
+
return "cancelled";
|
|
15187
15363
|
}
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15364
|
+
return "completed";
|
|
15365
|
+
}
|
|
15366
|
+
function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
|
|
15367
|
+
const time = turnTimestampMs(turn);
|
|
15368
|
+
const opts = { turn: turn.id, time, codexItemId: item.id };
|
|
15369
|
+
envelopes.push(api.createEnvelope("agent", {
|
|
15370
|
+
t: "tool-call-start",
|
|
15371
|
+
call: item.id,
|
|
15372
|
+
name,
|
|
15373
|
+
title,
|
|
15374
|
+
description: title,
|
|
15375
|
+
args
|
|
15376
|
+
}, {
|
|
15377
|
+
...opts,
|
|
15378
|
+
id: `${item.id}:start`
|
|
15379
|
+
}));
|
|
15380
|
+
if (output && output.trim().length > 0) {
|
|
15381
|
+
envelopes.push(api.createEnvelope("agent", {
|
|
15382
|
+
t: "text",
|
|
15383
|
+
text: output,
|
|
15384
|
+
thinking: true
|
|
15385
|
+
}, {
|
|
15386
|
+
...opts,
|
|
15387
|
+
id: `${item.id}:output`
|
|
15388
|
+
}));
|
|
15195
15389
|
}
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
|
|
15211
|
-
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
}
|
|
15390
|
+
if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
|
|
15391
|
+
envelopes.push(api.createEnvelope("agent", {
|
|
15392
|
+
t: "tool-call-end",
|
|
15393
|
+
call: item.id
|
|
15394
|
+
}, {
|
|
15395
|
+
...opts,
|
|
15396
|
+
id: `${item.id}:end`,
|
|
15397
|
+
time: completedTimestampMs(turn)
|
|
15398
|
+
}));
|
|
15399
|
+
}
|
|
15400
|
+
function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
15401
|
+
if (item.type === "subAgentActivity" && typeof item.agentThreadId === "string") {
|
|
15402
|
+
item = { ...item, type: "collabAgentToolCall", tool: item.kind, status: "completed", receiverThreadIds: [item.agentThreadId] };
|
|
15403
|
+
}
|
|
15404
|
+
if (item.type !== "collabAgentToolCall" || typeof item.id !== "string" || typeof item.tool !== "string") return [];
|
|
15405
|
+
const args = {
|
|
15406
|
+
operation: item.tool,
|
|
15407
|
+
...typeof item.prompt === "string" ? { prompt: item.prompt } : {},
|
|
15408
|
+
...typeof item.model === "string" ? { model: item.model } : {},
|
|
15409
|
+
receiverThreadIds: Array.isArray(item.receiverThreadIds) ? item.receiverThreadIds : [],
|
|
15410
|
+
agentsStates: item.agentsStates ?? {}
|
|
15411
|
+
};
|
|
15412
|
+
const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
|
|
15413
|
+
const envelopes = [api.createEnvelope("agent", {
|
|
15414
|
+
t: "tool-call-start",
|
|
15415
|
+
call: item.id,
|
|
15416
|
+
name: "CodexCollaboration",
|
|
15417
|
+
title: `Codex \xB7 ${item.tool}`,
|
|
15418
|
+
description: `Codex \xB7 ${item.tool}`,
|
|
15419
|
+
args
|
|
15420
|
+
}, { ...opts, id: startId })];
|
|
15421
|
+
if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
|
|
15422
|
+
envelopes.push(api.createEnvelope("agent", {
|
|
15423
|
+
t: "tool-call-end",
|
|
15424
|
+
call: item.id,
|
|
15425
|
+
result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
|
|
15426
|
+
}, { ...opts, id: `${item.id}:end` }));
|
|
15233
15427
|
}
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
|
|
15269
|
-
|
|
15270
|
-
|
|
15428
|
+
return envelopes;
|
|
15429
|
+
}
|
|
15430
|
+
function mapCodexThreadToSessionEnvelopes(thread) {
|
|
15431
|
+
const envelopes = [];
|
|
15432
|
+
for (const turn of thread.turns ?? []) {
|
|
15433
|
+
const startedAt = turnTimestampMs(turn);
|
|
15434
|
+
const completedAt = completedTimestampMs(turn);
|
|
15435
|
+
envelopes.push(api.createEnvelope("agent", { t: "turn-start" }, {
|
|
15436
|
+
id: `${turn.id}:start`,
|
|
15437
|
+
turn: turn.id,
|
|
15438
|
+
time: startedAt
|
|
15439
|
+
}));
|
|
15440
|
+
for (const item of turn.items ?? []) {
|
|
15441
|
+
switch (item.type) {
|
|
15442
|
+
case "subAgentActivity":
|
|
15443
|
+
case "collabAgentToolCall": {
|
|
15444
|
+
envelopes.push(...mapCodexCollaborationItem(item, { turn: turn.id, time: startedAt, codexItemId: item.id }));
|
|
15445
|
+
break;
|
|
15446
|
+
}
|
|
15447
|
+
case "userMessage": {
|
|
15448
|
+
const text = textFromInputItems(item.content);
|
|
15449
|
+
if (text) {
|
|
15450
|
+
envelopes.push(api.createEnvelope("user", { t: "text", text }, {
|
|
15451
|
+
id: item.id,
|
|
15452
|
+
time: startedAt,
|
|
15453
|
+
codexItemId: item.id
|
|
15454
|
+
}));
|
|
15455
|
+
}
|
|
15456
|
+
break;
|
|
15457
|
+
}
|
|
15458
|
+
case "agentMessage": {
|
|
15459
|
+
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15460
|
+
if (text.length > 0) {
|
|
15461
|
+
envelopes.push(api.createEnvelope("agent", { t: "text", text }, {
|
|
15462
|
+
id: item.id,
|
|
15463
|
+
turn: turn.id,
|
|
15464
|
+
time: completedAt,
|
|
15465
|
+
codexItemId: item.id
|
|
15466
|
+
}));
|
|
15467
|
+
}
|
|
15468
|
+
break;
|
|
15469
|
+
}
|
|
15470
|
+
case "reasoning": {
|
|
15471
|
+
const text = reasoningText(item);
|
|
15472
|
+
if (text) {
|
|
15473
|
+
envelopes.push(api.createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15474
|
+
id: item.id,
|
|
15475
|
+
turn: turn.id,
|
|
15476
|
+
time: startedAt,
|
|
15477
|
+
codexItemId: item.id
|
|
15478
|
+
}));
|
|
15479
|
+
}
|
|
15480
|
+
break;
|
|
15481
|
+
}
|
|
15482
|
+
case "commandExecution": {
|
|
15483
|
+
const command = typeof item.command === "string" ? item.command : "";
|
|
15484
|
+
emitHistoricalToolCall(
|
|
15485
|
+
envelopes,
|
|
15486
|
+
turn,
|
|
15487
|
+
item,
|
|
15488
|
+
"CodexBash",
|
|
15489
|
+
commandToTitle(command),
|
|
15490
|
+
{ command, cwd: item.cwd },
|
|
15491
|
+
typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : null
|
|
15492
|
+
);
|
|
15493
|
+
break;
|
|
15494
|
+
}
|
|
15495
|
+
case "fileChange": {
|
|
15496
|
+
const title = "Apply patch";
|
|
15497
|
+
emitHistoricalToolCall(
|
|
15498
|
+
envelopes,
|
|
15499
|
+
turn,
|
|
15500
|
+
item,
|
|
15501
|
+
"CodexPatch",
|
|
15502
|
+
title,
|
|
15503
|
+
{ changes: item.changes, status: item.status },
|
|
15504
|
+
null
|
|
15505
|
+
);
|
|
15506
|
+
break;
|
|
15507
|
+
}
|
|
15508
|
+
case "mcpToolCall": {
|
|
15509
|
+
const title = `${item.server}.${item.tool}`;
|
|
15510
|
+
const output = item.error !== void 0 && item.error !== null ? String(item.error) : item.result !== void 0 && item.result !== null ? String(item.result) : null;
|
|
15511
|
+
emitHistoricalToolCall(
|
|
15512
|
+
envelopes,
|
|
15513
|
+
turn,
|
|
15514
|
+
item,
|
|
15515
|
+
"McpTool",
|
|
15516
|
+
title,
|
|
15517
|
+
{
|
|
15518
|
+
server: item.server,
|
|
15519
|
+
tool: item.tool,
|
|
15520
|
+
arguments: item.arguments
|
|
15521
|
+
},
|
|
15522
|
+
output
|
|
15523
|
+
);
|
|
15524
|
+
break;
|
|
15525
|
+
}
|
|
15271
15526
|
}
|
|
15272
15527
|
}
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
|
|
15276
|
-
|
|
15277
|
-
}
|
|
15278
|
-
if (this.toolCallStarted && this.currentCallId) {
|
|
15279
|
-
const toolResult = {
|
|
15280
|
-
type: "tool-call-result",
|
|
15281
|
-
callId: this.currentCallId,
|
|
15282
|
-
output: {
|
|
15283
|
-
content,
|
|
15284
|
-
status: "completed"
|
|
15285
|
-
},
|
|
15286
|
-
id: node_crypto.randomUUID()
|
|
15287
|
-
};
|
|
15288
|
-
persistence.logger.debug(`${this.getLogPrefix()} Sending tool call result`);
|
|
15289
|
-
this.onMessage?.(toolResult);
|
|
15290
|
-
} else if (content.trim()) {
|
|
15291
|
-
const reasoningMessage = {
|
|
15292
|
-
type: "reasoning",
|
|
15293
|
-
message: content,
|
|
15294
|
-
id: node_crypto.randomUUID()
|
|
15295
|
-
};
|
|
15296
|
-
persistence.logger.debug(`${this.getLogPrefix()} Sending reasoning message`);
|
|
15297
|
-
this.onMessage?.(reasoningMessage);
|
|
15298
|
-
}
|
|
15299
|
-
this.resetState();
|
|
15300
|
-
return true;
|
|
15528
|
+
envelopes.push(api.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15529
|
+
id: `${turn.id}:end`,
|
|
15530
|
+
turn: turn.id,
|
|
15531
|
+
time: completedAt
|
|
15532
|
+
}));
|
|
15301
15533
|
}
|
|
15302
|
-
|
|
15303
|
-
|
|
15304
|
-
|
|
15305
|
-
|
|
15306
|
-
|
|
15307
|
-
this.finishCurrentToolCall("canceled");
|
|
15308
|
-
this.resetState();
|
|
15534
|
+
return envelopes;
|
|
15535
|
+
}
|
|
15536
|
+
function patchDescription(changes) {
|
|
15537
|
+
if (!changes || typeof changes !== "object") {
|
|
15538
|
+
return "Applying patch";
|
|
15309
15539
|
}
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
reset() {
|
|
15314
|
-
this.finishCurrentToolCall("canceled");
|
|
15315
|
-
this.resetState();
|
|
15540
|
+
const fileCount = Object.keys(changes).length;
|
|
15541
|
+
if (fileCount === 1) {
|
|
15542
|
+
return "Applying patch to 1 file";
|
|
15316
15543
|
}
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
type: "tool-call-result",
|
|
15324
|
-
callId: this.currentCallId,
|
|
15325
|
-
output: {
|
|
15326
|
-
content: this.contentBuffer || "",
|
|
15327
|
-
status
|
|
15328
|
-
},
|
|
15329
|
-
id: node_crypto.randomUUID()
|
|
15330
|
-
};
|
|
15331
|
-
persistence.logger.debug(`${this.getLogPrefix()} Sending tool call result with status: ${status}`);
|
|
15332
|
-
this.onMessage?.(toolResult);
|
|
15333
|
-
}
|
|
15544
|
+
return `Applying patch to ${fileCount} files`;
|
|
15545
|
+
}
|
|
15546
|
+
function pickTurnEndStatus(message, type) {
|
|
15547
|
+
const rawStatus = message.status;
|
|
15548
|
+
if (rawStatus === "completed" || rawStatus === "failed" || rawStatus === "cancelled") {
|
|
15549
|
+
return rawStatus;
|
|
15334
15550
|
}
|
|
15335
|
-
|
|
15336
|
-
|
|
15337
|
-
*/
|
|
15338
|
-
resetState() {
|
|
15339
|
-
this.accumulator = "";
|
|
15340
|
-
this.inTitleCapture = false;
|
|
15341
|
-
this.titleBuffer = "";
|
|
15342
|
-
this.contentBuffer = "";
|
|
15343
|
-
this.hasTitle = false;
|
|
15344
|
-
this.currentCallId = null;
|
|
15345
|
-
this.toolCallStarted = false;
|
|
15346
|
-
this.currentTitle = null;
|
|
15551
|
+
if (rawStatus === "canceled") {
|
|
15552
|
+
return "cancelled";
|
|
15347
15553
|
}
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15554
|
+
if (type === "turn_aborted") {
|
|
15555
|
+
const reason = message.reason;
|
|
15556
|
+
const error = message.error;
|
|
15557
|
+
if (typeof reason === "string" && /(fail|error)/i.test(reason) || typeof error === "string" && error.length > 0 || error !== void 0 && error !== null && typeof error === "object") {
|
|
15558
|
+
return "failed";
|
|
15559
|
+
}
|
|
15560
|
+
return "cancelled";
|
|
15353
15561
|
}
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
*/
|
|
15357
|
-
hasStartedToolCall() {
|
|
15358
|
-
return this.toolCallStarted;
|
|
15562
|
+
if (message.error !== void 0 && message.error !== null) {
|
|
15563
|
+
return "failed";
|
|
15359
15564
|
}
|
|
15565
|
+
return "completed";
|
|
15360
15566
|
}
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15567
|
+
function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
15568
|
+
const type = message.type;
|
|
15569
|
+
const startedSubagents = getStartedSubagents(state);
|
|
15570
|
+
const activeSubagents = getActiveSubagents(state);
|
|
15571
|
+
const providerSubagentToSessionSubagent = getProviderSubagentToSessionSubagent(state);
|
|
15572
|
+
if (type === "collab_agent_item") {
|
|
15573
|
+
return {
|
|
15574
|
+
currentTurnId: state.currentTurnId,
|
|
15575
|
+
startedSubagents,
|
|
15576
|
+
activeSubagents,
|
|
15577
|
+
providerSubagentToSessionSubagent,
|
|
15578
|
+
envelopes: mapCodexCollaborationItem(message.item, buildEnvelopeOptions(state.currentTurnId), "live")
|
|
15579
|
+
};
|
|
15365
15580
|
}
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15581
|
+
if (type === "task_started") {
|
|
15582
|
+
const turnId = cuid2.createId();
|
|
15583
|
+
const turnStart = api.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15584
|
+
startedSubagents.clear();
|
|
15585
|
+
activeSubagents.clear();
|
|
15586
|
+
providerSubagentToSessionSubagent.clear();
|
|
15587
|
+
return {
|
|
15588
|
+
currentTurnId: turnId,
|
|
15589
|
+
startedSubagents,
|
|
15590
|
+
activeSubagents,
|
|
15591
|
+
providerSubagentToSessionSubagent,
|
|
15592
|
+
envelopes: [turnStart]
|
|
15593
|
+
};
|
|
15374
15594
|
}
|
|
15375
|
-
|
|
15376
|
-
|
|
15377
|
-
|
|
15378
|
-
|
|
15379
|
-
|
|
15595
|
+
if (type === "task_complete" || type === "turn_aborted") {
|
|
15596
|
+
if (!state.currentTurnId) {
|
|
15597
|
+
return {
|
|
15598
|
+
currentTurnId: null,
|
|
15599
|
+
startedSubagents,
|
|
15600
|
+
activeSubagents,
|
|
15601
|
+
providerSubagentToSessionSubagent,
|
|
15602
|
+
envelopes: []
|
|
15603
|
+
};
|
|
15604
|
+
}
|
|
15605
|
+
const lifecycleOpts = { turn: state.currentTurnId };
|
|
15606
|
+
providerSubagentToSessionSubagent.clear();
|
|
15607
|
+
return {
|
|
15608
|
+
currentTurnId: null,
|
|
15609
|
+
startedSubagents,
|
|
15610
|
+
activeSubagents,
|
|
15611
|
+
providerSubagentToSessionSubagent,
|
|
15612
|
+
envelopes: [
|
|
15613
|
+
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
15614
|
+
api.createEnvelope("agent", {
|
|
15615
|
+
t: "turn-end",
|
|
15616
|
+
status: pickTurnEndStatus(message, type)
|
|
15617
|
+
}, lifecycleOpts)
|
|
15618
|
+
]
|
|
15619
|
+
};
|
|
15380
15620
|
}
|
|
15381
|
-
|
|
15382
|
-
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
|
|
15387
|
-
|
|
15621
|
+
if (type === "token_count") {
|
|
15622
|
+
return {
|
|
15623
|
+
currentTurnId: state.currentTurnId,
|
|
15624
|
+
startedSubagents,
|
|
15625
|
+
activeSubagents,
|
|
15626
|
+
providerSubagentToSessionSubagent,
|
|
15627
|
+
envelopes: []
|
|
15628
|
+
};
|
|
15388
15629
|
}
|
|
15389
|
-
|
|
15390
|
-
|
|
15391
|
-
|
|
15392
|
-
|
|
15393
|
-
|
|
15394
|
-
|
|
15395
|
-
|
|
15396
|
-
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
callId,
|
|
15400
|
-
input: {
|
|
15401
|
-
unified_diff: unifiedDiff
|
|
15402
|
-
},
|
|
15403
|
-
id: node_crypto.randomUUID()
|
|
15630
|
+
const subagent = resolveSessionSubagent(message, providerSubagentToSessionSubagent);
|
|
15631
|
+
const opts = buildEnvelopeOptions(state.currentTurnId, subagent);
|
|
15632
|
+
if (type === "agent_message") {
|
|
15633
|
+
if (typeof message.message !== "string") {
|
|
15634
|
+
return {
|
|
15635
|
+
currentTurnId: state.currentTurnId,
|
|
15636
|
+
startedSubagents,
|
|
15637
|
+
activeSubagents,
|
|
15638
|
+
providerSubagentToSessionSubagent,
|
|
15639
|
+
envelopes: []
|
|
15404
15640
|
};
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15409
|
-
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15641
|
+
}
|
|
15642
|
+
const envelopes = [];
|
|
15643
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15644
|
+
envelopes.push(api.createEnvelope("agent", { t: "text", text: message.message }, opts));
|
|
15645
|
+
return {
|
|
15646
|
+
currentTurnId: state.currentTurnId,
|
|
15647
|
+
startedSubagents,
|
|
15648
|
+
activeSubagents,
|
|
15649
|
+
providerSubagentToSessionSubagent,
|
|
15650
|
+
envelopes
|
|
15651
|
+
};
|
|
15652
|
+
}
|
|
15653
|
+
if (type === "agent_reasoning" || type === "agent_reasoning_delta") {
|
|
15654
|
+
const text = typeof message.text === "string" ? message.text : typeof message.delta === "string" ? message.delta : null;
|
|
15655
|
+
if (!text) {
|
|
15656
|
+
return {
|
|
15657
|
+
currentTurnId: state.currentTurnId,
|
|
15658
|
+
startedSubagents,
|
|
15659
|
+
activeSubagents,
|
|
15660
|
+
providerSubagentToSessionSubagent,
|
|
15661
|
+
envelopes: []
|
|
15413
15662
|
};
|
|
15414
|
-
this.onMessage?.(toolResult);
|
|
15415
15663
|
}
|
|
15416
|
-
|
|
15417
|
-
|
|
15664
|
+
const envelopes = [];
|
|
15665
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15666
|
+
envelopes.push(api.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
15667
|
+
return {
|
|
15668
|
+
currentTurnId: state.currentTurnId,
|
|
15669
|
+
startedSubagents,
|
|
15670
|
+
activeSubagents,
|
|
15671
|
+
providerSubagentToSessionSubagent,
|
|
15672
|
+
envelopes
|
|
15673
|
+
};
|
|
15418
15674
|
}
|
|
15419
|
-
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15675
|
+
if (type === "exec_command_begin") {
|
|
15676
|
+
const call = pickCallId(message);
|
|
15677
|
+
const { call_id: _callIdSnake, callId: _callIdCamel, type: _type, ...args } = message;
|
|
15678
|
+
const command = summarizeCommand(args.command);
|
|
15679
|
+
const description = typeof args.description === "string" ? args.description : command ?? "Execute command";
|
|
15680
|
+
const envelopes = [];
|
|
15681
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15682
|
+
envelopes.push(
|
|
15683
|
+
api.createEnvelope("agent", {
|
|
15684
|
+
t: "tool-call-start",
|
|
15685
|
+
call,
|
|
15686
|
+
name: "CodexBash",
|
|
15687
|
+
title: commandToTitle(command),
|
|
15688
|
+
description,
|
|
15689
|
+
args
|
|
15690
|
+
}, opts)
|
|
15691
|
+
);
|
|
15692
|
+
return {
|
|
15693
|
+
currentTurnId: state.currentTurnId,
|
|
15694
|
+
startedSubagents,
|
|
15695
|
+
activeSubagents,
|
|
15696
|
+
providerSubagentToSessionSubagent,
|
|
15697
|
+
envelopes
|
|
15698
|
+
};
|
|
15425
15699
|
}
|
|
15426
|
-
|
|
15427
|
-
|
|
15428
|
-
|
|
15429
|
-
|
|
15430
|
-
|
|
15700
|
+
if (type === "exec_command_end") {
|
|
15701
|
+
const call = pickCallId(message);
|
|
15702
|
+
const envelopes = [];
|
|
15703
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15704
|
+
envelopes.push(api.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15705
|
+
return {
|
|
15706
|
+
currentTurnId: state.currentTurnId,
|
|
15707
|
+
startedSubagents,
|
|
15708
|
+
activeSubagents,
|
|
15709
|
+
providerSubagentToSessionSubagent,
|
|
15710
|
+
envelopes
|
|
15711
|
+
};
|
|
15431
15712
|
}
|
|
15432
|
-
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15713
|
+
if (type === "patch_apply_begin") {
|
|
15714
|
+
const call = pickCallId(message);
|
|
15715
|
+
const autoApproved = message.auto_approved;
|
|
15716
|
+
const changes = message.changes;
|
|
15717
|
+
const envelopes = [];
|
|
15718
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15719
|
+
envelopes.push(
|
|
15720
|
+
api.createEnvelope("agent", {
|
|
15721
|
+
t: "tool-call-start",
|
|
15722
|
+
call,
|
|
15723
|
+
name: "CodexPatch",
|
|
15724
|
+
title: "Apply patch",
|
|
15725
|
+
description: patchDescription(changes),
|
|
15726
|
+
args: {
|
|
15727
|
+
auto_approved: autoApproved,
|
|
15728
|
+
changes
|
|
15729
|
+
}
|
|
15730
|
+
}, opts)
|
|
15731
|
+
);
|
|
15732
|
+
return {
|
|
15733
|
+
currentTurnId: state.currentTurnId,
|
|
15734
|
+
startedSubagents,
|
|
15735
|
+
activeSubagents,
|
|
15736
|
+
providerSubagentToSessionSubagent,
|
|
15737
|
+
envelopes
|
|
15738
|
+
};
|
|
15437
15739
|
}
|
|
15438
|
-
|
|
15439
|
-
|
|
15440
|
-
const
|
|
15441
|
-
|
|
15442
|
-
|
|
15443
|
-
|
|
15444
|
-
|
|
15445
|
-
|
|
15446
|
-
|
|
15447
|
-
|
|
15448
|
-
|
|
15449
|
-
setMessages(messageBuffer.getMessages());
|
|
15450
|
-
const unsubscribe = messageBuffer.onUpdate((newMessages) => {
|
|
15451
|
-
setMessages(newMessages);
|
|
15452
|
-
});
|
|
15453
|
-
return () => {
|
|
15454
|
-
unsubscribe();
|
|
15455
|
-
if (confirmationTimeoutRef.current) {
|
|
15456
|
-
clearTimeout(confirmationTimeoutRef.current);
|
|
15457
|
-
}
|
|
15740
|
+
if (type === "patch_apply_end") {
|
|
15741
|
+
const call = pickCallId(message);
|
|
15742
|
+
const envelopes = [];
|
|
15743
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15744
|
+
envelopes.push(api.createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15745
|
+
return {
|
|
15746
|
+
currentTurnId: state.currentTurnId,
|
|
15747
|
+
startedSubagents,
|
|
15748
|
+
activeSubagents,
|
|
15749
|
+
providerSubagentToSessionSubagent,
|
|
15750
|
+
envelopes
|
|
15458
15751
|
};
|
|
15459
|
-
}
|
|
15460
|
-
const resetConfirmation = React.useCallback(() => {
|
|
15461
|
-
setConfirmationMode(false);
|
|
15462
|
-
if (confirmationTimeoutRef.current) {
|
|
15463
|
-
clearTimeout(confirmationTimeoutRef.current);
|
|
15464
|
-
confirmationTimeoutRef.current = null;
|
|
15465
|
-
}
|
|
15466
|
-
}, []);
|
|
15467
|
-
const setConfirmationWithTimeout = React.useCallback(() => {
|
|
15468
|
-
setConfirmationMode(true);
|
|
15469
|
-
if (confirmationTimeoutRef.current) {
|
|
15470
|
-
clearTimeout(confirmationTimeoutRef.current);
|
|
15471
|
-
}
|
|
15472
|
-
confirmationTimeoutRef.current = setTimeout(() => {
|
|
15473
|
-
resetConfirmation();
|
|
15474
|
-
}, 15e3);
|
|
15475
|
-
}, [resetConfirmation]);
|
|
15476
|
-
ink.useInput(React.useCallback(async (input, key) => {
|
|
15477
|
-
if (actionInProgress) return;
|
|
15478
|
-
if (key.ctrl && input === "c") {
|
|
15479
|
-
if (confirmationMode) {
|
|
15480
|
-
resetConfirmation();
|
|
15481
|
-
setActionInProgress(true);
|
|
15482
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
15483
|
-
onExit?.();
|
|
15484
|
-
} else {
|
|
15485
|
-
setConfirmationWithTimeout();
|
|
15486
|
-
}
|
|
15487
|
-
return;
|
|
15488
|
-
}
|
|
15489
|
-
if (confirmationMode) {
|
|
15490
|
-
resetConfirmation();
|
|
15491
|
-
}
|
|
15492
|
-
}, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
|
|
15493
|
-
const getMessageColor = (type) => {
|
|
15494
|
-
switch (type) {
|
|
15495
|
-
case "user":
|
|
15496
|
-
return "magenta";
|
|
15497
|
-
case "assistant":
|
|
15498
|
-
return "cyan";
|
|
15499
|
-
case "system":
|
|
15500
|
-
return "blue";
|
|
15501
|
-
case "tool":
|
|
15502
|
-
return "yellow";
|
|
15503
|
-
case "result":
|
|
15504
|
-
return "green";
|
|
15505
|
-
case "status":
|
|
15506
|
-
return "gray";
|
|
15507
|
-
default:
|
|
15508
|
-
return "white";
|
|
15509
|
-
}
|
|
15510
|
-
};
|
|
15511
|
-
const formatMessage = (msg) => {
|
|
15512
|
-
const lines = msg.content.split("\n");
|
|
15513
|
-
const maxLineLength = terminalWidth - 10;
|
|
15514
|
-
return lines.map((line) => {
|
|
15515
|
-
if (line.length <= maxLineLength) return line;
|
|
15516
|
-
const chunks = [];
|
|
15517
|
-
for (let i = 0; i < line.length; i += maxLineLength) {
|
|
15518
|
-
chunks.push(line.slice(i, i + maxLineLength));
|
|
15519
|
-
}
|
|
15520
|
-
return chunks.join("\n");
|
|
15521
|
-
}).join("\n");
|
|
15522
|
-
};
|
|
15523
|
-
return /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", width: terminalWidth, height: terminalHeight }, /* @__PURE__ */ React.createElement(
|
|
15524
|
-
ink.Box,
|
|
15525
|
-
{
|
|
15526
|
-
flexDirection: "column",
|
|
15527
|
-
width: terminalWidth,
|
|
15528
|
-
height: terminalHeight - 4,
|
|
15529
|
-
borderStyle: "round",
|
|
15530
|
-
borderColor: "gray",
|
|
15531
|
-
paddingX: 1,
|
|
15532
|
-
overflow: "hidden"
|
|
15533
|
-
},
|
|
15534
|
-
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "\u{1F916} Codex Agent Messages"), /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "\u2500".repeat(Math.min(terminalWidth - 4, 60)))),
|
|
15535
|
-
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", height: terminalHeight - 10, overflow: "hidden" }, messages.length === 0 ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Waiting for messages...") : (
|
|
15536
|
-
// Show only the last messages that fit in the available space
|
|
15537
|
-
messages.slice(-Math.max(1, terminalHeight - 10)).map((msg) => /* @__PURE__ */ React.createElement(ink.Box, { key: msg.id, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { color: getMessageColor(msg.type), dimColor: true }, formatMessage(msg))))
|
|
15538
|
-
))
|
|
15539
|
-
), /* @__PURE__ */ React.createElement(
|
|
15540
|
-
ink.Box,
|
|
15541
|
-
{
|
|
15542
|
-
width: terminalWidth,
|
|
15543
|
-
borderStyle: "round",
|
|
15544
|
-
borderColor: actionInProgress ? "gray" : confirmationMode ? "red" : "green",
|
|
15545
|
-
paddingX: 2,
|
|
15546
|
-
justifyContent: "center",
|
|
15547
|
-
alignItems: "center",
|
|
15548
|
-
flexDirection: "column"
|
|
15549
|
-
},
|
|
15550
|
-
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", alignItems: "center" }, actionInProgress ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode ? /* @__PURE__ */ React.createElement(ink.Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ink.Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
|
|
15551
|
-
));
|
|
15552
|
-
};
|
|
15553
|
-
|
|
15554
|
-
function createOfflineSessionStub(sessionTag) {
|
|
15752
|
+
}
|
|
15555
15753
|
return {
|
|
15556
|
-
|
|
15557
|
-
|
|
15558
|
-
|
|
15559
|
-
|
|
15560
|
-
|
|
15561
|
-
sendAgentUsageSnapshot: () => false,
|
|
15562
|
-
sendClaudeSessionMessage: () => {
|
|
15563
|
-
},
|
|
15564
|
-
keepAlive: () => {
|
|
15565
|
-
},
|
|
15566
|
-
sendSessionEvent: () => {
|
|
15567
|
-
},
|
|
15568
|
-
sendSessionDeath: () => {
|
|
15569
|
-
},
|
|
15570
|
-
updateLifecycleState: () => {
|
|
15571
|
-
},
|
|
15572
|
-
requestControlTransfer: async () => {
|
|
15573
|
-
},
|
|
15574
|
-
flush: async () => {
|
|
15575
|
-
},
|
|
15576
|
-
close: async () => {
|
|
15577
|
-
},
|
|
15578
|
-
updateMetadata: () => {
|
|
15579
|
-
},
|
|
15580
|
-
updateAgentState: () => {
|
|
15581
|
-
},
|
|
15582
|
-
onUserMessage: () => {
|
|
15583
|
-
},
|
|
15584
|
-
onFileEvent: () => {
|
|
15585
|
-
},
|
|
15586
|
-
trackAttachmentDownload: () => {
|
|
15587
|
-
},
|
|
15588
|
-
drainAttachmentsForUserMessage: async () => [],
|
|
15589
|
-
rpcHandlerManager: {
|
|
15590
|
-
registerHandler: () => {
|
|
15591
|
-
}
|
|
15592
|
-
}
|
|
15754
|
+
currentTurnId: state.currentTurnId,
|
|
15755
|
+
startedSubagents,
|
|
15756
|
+
activeSubagents,
|
|
15757
|
+
providerSubagentToSessionSubagent,
|
|
15758
|
+
envelopes: []
|
|
15593
15759
|
};
|
|
15594
15760
|
}
|
|
15761
|
+
function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
15762
|
+
const toolLikeMessage = message;
|
|
15763
|
+
const opts = buildEnvelopeOptions(state.currentTurnId);
|
|
15764
|
+
if (message.type === "reasoning") {
|
|
15765
|
+
return [api.createEnvelope("agent", {
|
|
15766
|
+
t: "text",
|
|
15767
|
+
text: message.message,
|
|
15768
|
+
thinking: true
|
|
15769
|
+
}, opts)];
|
|
15770
|
+
}
|
|
15771
|
+
if (message.type === "tool-call") {
|
|
15772
|
+
const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
|
|
15773
|
+
return [api.createEnvelope("agent", {
|
|
15774
|
+
t: "tool-call-start",
|
|
15775
|
+
call: toolLikeMessage.callId,
|
|
15776
|
+
name: toolLikeMessage.name || "unknown",
|
|
15777
|
+
title,
|
|
15778
|
+
description: title,
|
|
15779
|
+
args: toolLikeMessage.input && typeof toolLikeMessage.input === "object" ? toolLikeMessage.input : {}
|
|
15780
|
+
}, opts)];
|
|
15781
|
+
}
|
|
15782
|
+
if (message.type === "tool-call-result") {
|
|
15783
|
+
const envelopes = [];
|
|
15784
|
+
const content = toolLikeMessage.output?.content;
|
|
15785
|
+
if (typeof content === "string" && content.trim().length > 0) {
|
|
15786
|
+
envelopes.push(api.createEnvelope("agent", {
|
|
15787
|
+
t: "text",
|
|
15788
|
+
text: content,
|
|
15789
|
+
thinking: true
|
|
15790
|
+
}, opts));
|
|
15791
|
+
}
|
|
15792
|
+
envelopes.push(api.createEnvelope("agent", {
|
|
15793
|
+
t: "tool-call-end",
|
|
15794
|
+
call: toolLikeMessage.callId
|
|
15795
|
+
}, opts));
|
|
15796
|
+
return envelopes;
|
|
15797
|
+
}
|
|
15798
|
+
return [];
|
|
15799
|
+
}
|
|
15595
15800
|
|
|
15596
|
-
function
|
|
15597
|
-
|
|
15598
|
-
|
|
15599
|
-
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15606
|
-
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15801
|
+
async function readChildThread(client, childId) {
|
|
15802
|
+
if (!client.threadId || typeof childId !== "string" || !childId) throw new Error("Invalid child thread");
|
|
15803
|
+
const parentId = client.threadId;
|
|
15804
|
+
const { thread: parent } = await client.readThread({ threadId: parentId, includeTurns: true });
|
|
15805
|
+
const belongs = parent.id === parentId && parent.turns?.some((turn) => turn.items.some((item) => item.type === "collabAgentToolCall" && Array.isArray(item.receiverThreadIds) && item.receiverThreadIds.includes(childId) || item.type === "subAgentActivity" && item.agentThreadId === childId));
|
|
15806
|
+
if (!belongs) throw new Error("Thread is not a child of this session");
|
|
15807
|
+
const { thread } = await client.readThread({ threadId: childId, includeTurns: true });
|
|
15808
|
+
if (thread.id !== childId) throw new Error("Child thread mismatch");
|
|
15809
|
+
return { envelopes: mapCodexThreadToSessionEnvelopes(thread) };
|
|
15810
|
+
}
|
|
15811
|
+
|
|
15812
|
+
function registerAgentAttachmentDownloads(session) {
|
|
15813
|
+
session.onFileEvent((fileEvent) => {
|
|
15814
|
+
const attachment = fileEvent.content.data.ev;
|
|
15815
|
+
const download = (async () => {
|
|
15816
|
+
try {
|
|
15817
|
+
const data = await session.downloadAndDecryptAttachment(attachment.ref);
|
|
15818
|
+
if (!data) throw new Error("Attachment decryption failed");
|
|
15819
|
+
return { data, mimeType: attachment.mimeType ?? "image/jpeg", name: attachment.name };
|
|
15820
|
+
} catch {
|
|
15821
|
+
session.sendSessionEvent({ type: "message", message: `Could not download attachment: ${attachment.name}. Please attach it again.` });
|
|
15822
|
+
return null;
|
|
15613
15823
|
}
|
|
15614
|
-
});
|
|
15615
|
-
|
|
15616
|
-
}
|
|
15617
|
-
session = api$1.sessionSyncClient(response, opts.sessionClientOptions);
|
|
15618
|
-
return { session, reconnectionHandle: null, isOffline: false };
|
|
15619
|
-
}
|
|
15824
|
+
})();
|
|
15825
|
+
session.trackAttachmentDownload(download);
|
|
15826
|
+
});
|
|
15620
15827
|
}
|
|
15621
15828
|
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15829
|
+
class BasePermissionHandler {
|
|
15830
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
15831
|
+
session;
|
|
15832
|
+
isResetting = false;
|
|
15833
|
+
constructor(session) {
|
|
15834
|
+
this.session = session;
|
|
15835
|
+
this.setupRpcHandler();
|
|
15628
15836
|
}
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
|
|
15633
|
-
|
|
15634
|
-
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
|
|
15655
|
-
|
|
15837
|
+
/**
|
|
15838
|
+
* Update the session reference (used after offline reconnection swaps sessions).
|
|
15839
|
+
* This is critical for avoiding stale session references after onSessionSwap.
|
|
15840
|
+
*/
|
|
15841
|
+
updateSession(newSession) {
|
|
15842
|
+
persistence.logger.debug(`${this.getLogPrefix()} Session reference updated`);
|
|
15843
|
+
this.session = newSession;
|
|
15844
|
+
this.setupRpcHandler();
|
|
15845
|
+
}
|
|
15846
|
+
/**
|
|
15847
|
+
* Setup RPC handler for permission responses.
|
|
15848
|
+
*/
|
|
15849
|
+
setupRpcHandler() {
|
|
15850
|
+
this.session.rpcHandlerManager.registerHandler(
|
|
15851
|
+
"permission",
|
|
15852
|
+
async (response) => {
|
|
15853
|
+
const pending = this.pendingRequests.get(response.id);
|
|
15854
|
+
if (!pending) {
|
|
15855
|
+
persistence.logger.debug(`${this.getLogPrefix()} Permission request not found or already resolved`);
|
|
15856
|
+
return;
|
|
15857
|
+
}
|
|
15858
|
+
this.pendingRequests.delete(response.id);
|
|
15859
|
+
const result = response.approved ? { decision: response.decision === "approved_for_session" ? "approved_for_session" : "approved" } : { decision: response.decision === "denied" ? "denied" : "abort" };
|
|
15860
|
+
pending.resolve(result);
|
|
15861
|
+
this.session.updateAgentState((currentState) => {
|
|
15862
|
+
const request = currentState.requests?.[response.id];
|
|
15863
|
+
if (!request) return currentState;
|
|
15864
|
+
const { [response.id]: _, ...remainingRequests } = currentState.requests || {};
|
|
15865
|
+
let res = {
|
|
15866
|
+
...currentState,
|
|
15867
|
+
requests: remainingRequests,
|
|
15868
|
+
completedRequests: {
|
|
15869
|
+
...currentState.completedRequests,
|
|
15870
|
+
[response.id]: {
|
|
15871
|
+
...request,
|
|
15872
|
+
completedAt: Date.now(),
|
|
15873
|
+
status: response.approved ? "approved" : "denied",
|
|
15874
|
+
decision: result.decision
|
|
15875
|
+
}
|
|
15876
|
+
}
|
|
15877
|
+
};
|
|
15878
|
+
return res;
|
|
15879
|
+
});
|
|
15880
|
+
persistence.logger.debug(`${this.getLogPrefix()} Permission ${response.approved ? "approved" : "denied"} for ${pending.toolName}`);
|
|
15881
|
+
}
|
|
15882
|
+
);
|
|
15883
|
+
}
|
|
15884
|
+
/**
|
|
15885
|
+
* Add a pending request to the agent state.
|
|
15886
|
+
*/
|
|
15887
|
+
addPendingRequestToState(toolCallId, toolName, input) {
|
|
15888
|
+
this.session.updateAgentState((currentState) => ({
|
|
15889
|
+
...currentState,
|
|
15890
|
+
requests: {
|
|
15891
|
+
...currentState.requests,
|
|
15892
|
+
[toolCallId]: {
|
|
15893
|
+
tool: toolName,
|
|
15894
|
+
arguments: input,
|
|
15895
|
+
createdAt: Date.now()
|
|
15896
|
+
}
|
|
15897
|
+
}
|
|
15898
|
+
}));
|
|
15899
|
+
}
|
|
15900
|
+
/**
|
|
15901
|
+
* Abort all pending permission requests.
|
|
15902
|
+
* Unlike reset(), this resolves (not rejects) pending promises with { decision: 'abort' },
|
|
15903
|
+
* causing the approval response to send 'cancel' to the provider. This is used when the
|
|
15904
|
+
* user presses the abort/stop button — it unblocks any pending tool approval so the provider
|
|
15905
|
+
* can process the turn cancellation.
|
|
15906
|
+
*/
|
|
15907
|
+
abortAll() {
|
|
15908
|
+
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15909
|
+
if (pendingSnapshot.length === 0) return;
|
|
15910
|
+
this.pendingRequests.clear();
|
|
15911
|
+
for (const [id, pending] of pendingSnapshot) {
|
|
15912
|
+
try {
|
|
15913
|
+
pending.resolve({ decision: "abort" });
|
|
15914
|
+
} catch (err) {
|
|
15915
|
+
persistence.logger.debug(`${this.getLogPrefix()} Error resolving aborted request ${id}:`, err);
|
|
15916
|
+
}
|
|
15656
15917
|
}
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
|
|
15662
|
-
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
|
|
15680
|
-
|
|
15681
|
-
|
|
15682
|
-
|
|
15683
|
-
|
|
15684
|
-
|
|
15918
|
+
this.session.updateAgentState((currentState) => {
|
|
15919
|
+
const pendingRequests = currentState.requests || {};
|
|
15920
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
15921
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15922
|
+
completedRequests[id] = {
|
|
15923
|
+
...request,
|
|
15924
|
+
completedAt: Date.now(),
|
|
15925
|
+
status: "canceled",
|
|
15926
|
+
reason: "Aborted by user"
|
|
15927
|
+
};
|
|
15928
|
+
}
|
|
15929
|
+
return {
|
|
15930
|
+
...currentState,
|
|
15931
|
+
requests: {},
|
|
15932
|
+
completedRequests
|
|
15933
|
+
};
|
|
15934
|
+
});
|
|
15935
|
+
persistence.logger.debug(`${this.getLogPrefix()} Aborted ${pendingSnapshot.length} pending permission(s)`);
|
|
15936
|
+
}
|
|
15937
|
+
/**
|
|
15938
|
+
* Reset state for new sessions.
|
|
15939
|
+
* This method is idempotent - safe to call multiple times.
|
|
15940
|
+
*/
|
|
15941
|
+
reset(reason = "Session reset") {
|
|
15942
|
+
if (this.isResetting) {
|
|
15943
|
+
persistence.logger.debug(`${this.getLogPrefix()} Reset already in progress, skipping`);
|
|
15944
|
+
return;
|
|
15945
|
+
}
|
|
15946
|
+
this.isResetting = true;
|
|
15947
|
+
try {
|
|
15948
|
+
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15949
|
+
this.pendingRequests.clear();
|
|
15950
|
+
for (const [id, pending] of pendingSnapshot) {
|
|
15951
|
+
try {
|
|
15952
|
+
pending.reject(new Error("Session reset"));
|
|
15953
|
+
} catch (err) {
|
|
15954
|
+
persistence.logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
15955
|
+
}
|
|
15956
|
+
}
|
|
15957
|
+
this.session.updateAgentState((currentState) => {
|
|
15958
|
+
const pendingRequests = currentState.requests || {};
|
|
15959
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
15960
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15961
|
+
completedRequests[id] = {
|
|
15962
|
+
...request,
|
|
15963
|
+
completedAt: Date.now(),
|
|
15964
|
+
status: "canceled",
|
|
15965
|
+
reason
|
|
15966
|
+
};
|
|
15967
|
+
}
|
|
15968
|
+
return {
|
|
15969
|
+
...currentState,
|
|
15970
|
+
requests: {},
|
|
15971
|
+
completedRequests
|
|
15972
|
+
};
|
|
15973
|
+
});
|
|
15974
|
+
persistence.logger.debug(`${this.getLogPrefix()} Permission handler reset`);
|
|
15975
|
+
} finally {
|
|
15976
|
+
this.isResetting = false;
|
|
15685
15977
|
}
|
|
15686
|
-
}
|
|
15687
|
-
return { approvalPolicy, sandbox };
|
|
15978
|
+
}
|
|
15688
15979
|
}
|
|
15689
15980
|
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
|
|
15981
|
+
class CodexPermissionHandler extends BasePermissionHandler {
|
|
15982
|
+
// Exact tool names that should always be auto-approved. Include the bare
|
|
15983
|
+
// form (used by Codex elicitation messages like `tool "change_title"`)
|
|
15984
|
+
// and the MCP-qualified form for defense in depth.
|
|
15985
|
+
static ALWAYS_AUTO_APPROVE_NAMES = /* @__PURE__ */ new Set([
|
|
15986
|
+
"change_title",
|
|
15987
|
+
"mcp__happy__change_title"
|
|
15988
|
+
]);
|
|
15989
|
+
// Tool-call IDs that should auto-approve when they exactly match one of
|
|
15990
|
+
// these values or start with `<name>-` (e.g. `change_title-1765385846663`).
|
|
15991
|
+
// Substring matching was a bypass vector — any tool whose ID happened to
|
|
15992
|
+
// contain `change_title` as a substring would be silently approved.
|
|
15993
|
+
static ALWAYS_AUTO_APPROVE_ID_PREFIXES = [
|
|
15994
|
+
"change_title"
|
|
15995
|
+
];
|
|
15996
|
+
constructor(session) {
|
|
15997
|
+
super(session);
|
|
15702
15998
|
}
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
activeSubagents.add(subagent);
|
|
15706
|
-
}
|
|
15707
|
-
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15708
|
-
const envelopes = [];
|
|
15709
|
-
for (const subagent of activeSubagents) {
|
|
15710
|
-
envelopes.push(api.createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
15999
|
+
getLogPrefix() {
|
|
16000
|
+
return "[Codex]";
|
|
15711
16001
|
}
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
}
|
|
15716
|
-
function buildEnvelopeOptions(currentTurnId, subagent) {
|
|
15717
|
-
return {
|
|
15718
|
-
...currentTurnId ? { turn: currentTurnId } : {},
|
|
15719
|
-
...subagent ? { subagent } : {}
|
|
15720
|
-
};
|
|
15721
|
-
}
|
|
15722
|
-
function pickProviderSubagent(message) {
|
|
15723
|
-
const candidates = [message.subagent, message.parent_call_id, message.parentCallId];
|
|
15724
|
-
for (const candidate of candidates) {
|
|
15725
|
-
if (typeof candidate === "string" && candidate.length > 0) {
|
|
15726
|
-
return candidate;
|
|
16002
|
+
shouldAutoApprove(toolName, toolCallId) {
|
|
16003
|
+
if (CodexPermissionHandler.ALWAYS_AUTO_APPROVE_NAMES.has(toolName)) {
|
|
16004
|
+
return true;
|
|
15727
16005
|
}
|
|
16006
|
+
for (const prefix of CodexPermissionHandler.ALWAYS_AUTO_APPROVE_ID_PREFIXES) {
|
|
16007
|
+
if (toolCallId === prefix || toolCallId.startsWith(`${prefix}-`)) {
|
|
16008
|
+
return true;
|
|
16009
|
+
}
|
|
16010
|
+
}
|
|
16011
|
+
return false;
|
|
15728
16012
|
}
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
15732
|
-
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
15736
|
-
|
|
15737
|
-
|
|
15738
|
-
|
|
16013
|
+
/**
|
|
16014
|
+
* Handle a tool permission request
|
|
16015
|
+
* @param toolCallId - The unique ID of the tool call
|
|
16016
|
+
* @param toolName - The name of the tool being called
|
|
16017
|
+
* @param input - The input parameters for the tool
|
|
16018
|
+
* @returns Promise resolving to permission result
|
|
16019
|
+
*/
|
|
16020
|
+
async handleToolCall(toolCallId, toolName, input) {
|
|
16021
|
+
if (this.shouldAutoApprove(toolName, toolCallId)) {
|
|
16022
|
+
persistence.logger.debug(`${this.getLogPrefix()} Auto-approving tool ${toolName} (${toolCallId})`);
|
|
16023
|
+
this.session.updateAgentState((currentState) => ({
|
|
16024
|
+
...currentState,
|
|
16025
|
+
completedRequests: {
|
|
16026
|
+
...currentState.completedRequests,
|
|
16027
|
+
[toolCallId]: {
|
|
16028
|
+
tool: toolName,
|
|
16029
|
+
arguments: input,
|
|
16030
|
+
createdAt: Date.now(),
|
|
16031
|
+
completedAt: Date.now(),
|
|
16032
|
+
status: "approved",
|
|
16033
|
+
decision: "approved"
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
}));
|
|
16037
|
+
return { decision: "approved" };
|
|
16038
|
+
}
|
|
16039
|
+
return new Promise((resolve, reject) => {
|
|
16040
|
+
this.pendingRequests.set(toolCallId, {
|
|
16041
|
+
resolve,
|
|
16042
|
+
reject,
|
|
16043
|
+
toolName,
|
|
16044
|
+
input
|
|
16045
|
+
});
|
|
16046
|
+
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
16047
|
+
persistence.logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})`);
|
|
16048
|
+
});
|
|
15739
16049
|
}
|
|
15740
|
-
const created = cuid2.createId();
|
|
15741
|
-
providerSubagentToSessionSubagent.set(providerSubagent, created);
|
|
15742
|
-
return created;
|
|
15743
16050
|
}
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
16051
|
+
|
|
16052
|
+
class BaseReasoningProcessor {
|
|
16053
|
+
accumulator = "";
|
|
16054
|
+
inTitleCapture = false;
|
|
16055
|
+
titleBuffer = "";
|
|
16056
|
+
contentBuffer = "";
|
|
16057
|
+
hasTitle = false;
|
|
16058
|
+
currentCallId = null;
|
|
16059
|
+
toolCallStarted = false;
|
|
16060
|
+
currentTitle = null;
|
|
16061
|
+
onMessage = null;
|
|
16062
|
+
constructor(onMessage) {
|
|
16063
|
+
this.onMessage = onMessage || null;
|
|
16064
|
+
this.reset();
|
|
15748
16065
|
}
|
|
15749
|
-
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
16066
|
+
/**
|
|
16067
|
+
* Set the message callback for sending messages directly.
|
|
16068
|
+
*/
|
|
16069
|
+
setMessageCallback(callback) {
|
|
16070
|
+
this.onMessage = callback;
|
|
15754
16071
|
}
|
|
15755
|
-
|
|
15756
|
-
|
|
15757
|
-
|
|
16072
|
+
/**
|
|
16073
|
+
* Process a reasoning section break - indicates a new reasoning section is starting.
|
|
16074
|
+
*/
|
|
16075
|
+
handleSectionBreak() {
|
|
16076
|
+
this.finishCurrentToolCall("canceled");
|
|
16077
|
+
this.resetState();
|
|
16078
|
+
persistence.logger.debug(`${this.getLogPrefix()} Section break - reset state`);
|
|
15758
16079
|
}
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
16080
|
+
/**
|
|
16081
|
+
* Process a reasoning delta/chunk and accumulate content.
|
|
16082
|
+
*/
|
|
16083
|
+
processInput(input) {
|
|
16084
|
+
this.accumulator += input;
|
|
16085
|
+
if (!this.inTitleCapture && !this.hasTitle && !this.contentBuffer) {
|
|
16086
|
+
if (this.accumulator.startsWith("**")) {
|
|
16087
|
+
this.inTitleCapture = true;
|
|
16088
|
+
this.titleBuffer = this.accumulator.substring(2);
|
|
16089
|
+
persistence.logger.debug(`${this.getLogPrefix()} Started title capture`);
|
|
16090
|
+
} else if (this.accumulator.length > 0) {
|
|
16091
|
+
this.contentBuffer = this.accumulator;
|
|
16092
|
+
}
|
|
16093
|
+
} else if (this.inTitleCapture) {
|
|
16094
|
+
this.titleBuffer = this.accumulator.substring(2);
|
|
16095
|
+
const titleEndIndex = this.titleBuffer.indexOf("**");
|
|
16096
|
+
if (titleEndIndex !== -1) {
|
|
16097
|
+
const title = this.titleBuffer.substring(0, titleEndIndex);
|
|
16098
|
+
const afterTitle = this.titleBuffer.substring(titleEndIndex + 2);
|
|
16099
|
+
this.hasTitle = true;
|
|
16100
|
+
this.inTitleCapture = false;
|
|
16101
|
+
this.currentTitle = title;
|
|
16102
|
+
this.contentBuffer = afterTitle;
|
|
16103
|
+
this.currentCallId = node_crypto.randomUUID();
|
|
16104
|
+
persistence.logger.debug(`${this.getLogPrefix()} Title captured: "${title}"`);
|
|
16105
|
+
this.sendToolCallStart(title);
|
|
16106
|
+
}
|
|
16107
|
+
} else if (this.hasTitle) {
|
|
16108
|
+
const titleStartIndex = this.accumulator.indexOf("**");
|
|
16109
|
+
if (titleStartIndex !== -1) {
|
|
16110
|
+
this.contentBuffer = this.accumulator.substring(
|
|
16111
|
+
titleStartIndex + 2 + this.currentTitle.length + 2
|
|
16112
|
+
);
|
|
16113
|
+
}
|
|
16114
|
+
} else {
|
|
16115
|
+
this.contentBuffer = this.accumulator;
|
|
16116
|
+
}
|
|
15764
16117
|
}
|
|
15765
|
-
|
|
15766
|
-
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
15770
|
-
|
|
15771
|
-
}
|
|
15772
|
-
|
|
15773
|
-
|
|
15774
|
-
|
|
15775
|
-
|
|
15776
|
-
|
|
15777
|
-
|
|
15778
|
-
|
|
16118
|
+
/**
|
|
16119
|
+
* Send the tool call start message.
|
|
16120
|
+
*/
|
|
16121
|
+
sendToolCallStart(title) {
|
|
16122
|
+
if (!this.currentCallId || this.toolCallStarted) {
|
|
16123
|
+
return;
|
|
16124
|
+
}
|
|
16125
|
+
const toolCall = {
|
|
16126
|
+
type: "tool-call",
|
|
16127
|
+
name: this.getToolName(),
|
|
16128
|
+
callId: this.currentCallId,
|
|
16129
|
+
input: {
|
|
16130
|
+
title
|
|
16131
|
+
},
|
|
16132
|
+
id: node_crypto.randomUUID()
|
|
16133
|
+
};
|
|
16134
|
+
persistence.logger.debug(`${this.getLogPrefix()} Sending tool call start for: "${title}"`);
|
|
16135
|
+
this.onMessage?.(toolCall);
|
|
16136
|
+
this.toolCallStarted = true;
|
|
15779
16137
|
}
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
|
|
15784
|
-
|
|
15785
|
-
|
|
15786
|
-
|
|
15787
|
-
|
|
15788
|
-
|
|
15789
|
-
|
|
15790
|
-
|
|
15791
|
-
|
|
15792
|
-
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
|
|
16138
|
+
/**
|
|
16139
|
+
* Complete the reasoning section.
|
|
16140
|
+
* Returns true if reasoning was completed, false if there was nothing to complete.
|
|
16141
|
+
*/
|
|
16142
|
+
completeReasoning(fullText) {
|
|
16143
|
+
const text = fullText ?? this.accumulator;
|
|
16144
|
+
if (!text.trim() && !this.toolCallStarted) {
|
|
16145
|
+
persistence.logger.debug(`${this.getLogPrefix()} Complete called but no content accumulated, skipping`);
|
|
16146
|
+
return false;
|
|
16147
|
+
}
|
|
16148
|
+
let title;
|
|
16149
|
+
let content = text;
|
|
16150
|
+
if (text.startsWith("**")) {
|
|
16151
|
+
const titleEndIndex = text.indexOf("**", 2);
|
|
16152
|
+
if (titleEndIndex !== -1) {
|
|
16153
|
+
title = text.substring(2, titleEndIndex);
|
|
16154
|
+
content = text.substring(titleEndIndex + 2).trim();
|
|
16155
|
+
}
|
|
16156
|
+
}
|
|
16157
|
+
persistence.logger.debug(`${this.getLogPrefix()} Complete reasoning - Title: "${title}", Has content: ${content.length > 0}`);
|
|
16158
|
+
if (title && !this.toolCallStarted) {
|
|
16159
|
+
this.currentCallId = this.currentCallId || node_crypto.randomUUID();
|
|
16160
|
+
this.sendToolCallStart(title);
|
|
16161
|
+
}
|
|
16162
|
+
if (this.toolCallStarted && this.currentCallId) {
|
|
16163
|
+
const toolResult = {
|
|
16164
|
+
type: "tool-call-result",
|
|
16165
|
+
callId: this.currentCallId,
|
|
16166
|
+
output: {
|
|
16167
|
+
content,
|
|
16168
|
+
status: "completed"
|
|
16169
|
+
},
|
|
16170
|
+
id: node_crypto.randomUUID()
|
|
16171
|
+
};
|
|
16172
|
+
persistence.logger.debug(`${this.getLogPrefix()} Sending tool call result`);
|
|
16173
|
+
this.onMessage?.(toolResult);
|
|
16174
|
+
} else if (content.trim()) {
|
|
16175
|
+
const reasoningMessage = {
|
|
16176
|
+
type: "reasoning",
|
|
16177
|
+
message: content,
|
|
16178
|
+
id: node_crypto.randomUUID()
|
|
16179
|
+
};
|
|
16180
|
+
persistence.logger.debug(`${this.getLogPrefix()} Sending reasoning message`);
|
|
16181
|
+
this.onMessage?.(reasoningMessage);
|
|
16182
|
+
}
|
|
16183
|
+
this.resetState();
|
|
16184
|
+
return true;
|
|
15797
16185
|
}
|
|
15798
|
-
|
|
15799
|
-
|
|
16186
|
+
/**
|
|
16187
|
+
* Abort the current reasoning section.
|
|
16188
|
+
*/
|
|
16189
|
+
abort() {
|
|
16190
|
+
persistence.logger.debug(`${this.getLogPrefix()} Abort called`);
|
|
16191
|
+
this.finishCurrentToolCall("canceled");
|
|
16192
|
+
this.resetState();
|
|
15800
16193
|
}
|
|
15801
|
-
|
|
15802
|
-
|
|
15803
|
-
|
|
15804
|
-
|
|
15805
|
-
|
|
15806
|
-
|
|
15807
|
-
t: "tool-call-start",
|
|
15808
|
-
call: item.id,
|
|
15809
|
-
name,
|
|
15810
|
-
title,
|
|
15811
|
-
description: title,
|
|
15812
|
-
args
|
|
15813
|
-
}, {
|
|
15814
|
-
...opts,
|
|
15815
|
-
id: `${item.id}:start`
|
|
15816
|
-
}));
|
|
15817
|
-
if (output && output.trim().length > 0) {
|
|
15818
|
-
envelopes.push(api.createEnvelope("agent", {
|
|
15819
|
-
t: "text",
|
|
15820
|
-
text: output,
|
|
15821
|
-
thinking: true
|
|
15822
|
-
}, {
|
|
15823
|
-
...opts,
|
|
15824
|
-
id: `${item.id}:output`
|
|
15825
|
-
}));
|
|
16194
|
+
/**
|
|
16195
|
+
* Reset the processor state.
|
|
16196
|
+
*/
|
|
16197
|
+
reset() {
|
|
16198
|
+
this.finishCurrentToolCall("canceled");
|
|
16199
|
+
this.resetState();
|
|
15826
16200
|
}
|
|
15827
|
-
|
|
15828
|
-
|
|
15829
|
-
|
|
15830
|
-
|
|
15831
|
-
|
|
15832
|
-
|
|
15833
|
-
|
|
15834
|
-
|
|
15835
|
-
|
|
15836
|
-
|
|
15837
|
-
|
|
15838
|
-
|
|
15839
|
-
|
|
15840
|
-
|
|
15841
|
-
|
|
15842
|
-
|
|
15843
|
-
turn: turn.id,
|
|
15844
|
-
time: startedAt
|
|
15845
|
-
}));
|
|
15846
|
-
for (const item of turn.items ?? []) {
|
|
15847
|
-
switch (item.type) {
|
|
15848
|
-
case "userMessage": {
|
|
15849
|
-
const text = textFromInputItems(item.content);
|
|
15850
|
-
if (text) {
|
|
15851
|
-
envelopes.push(api.createEnvelope("user", { t: "text", text }, {
|
|
15852
|
-
id: item.id,
|
|
15853
|
-
time: startedAt,
|
|
15854
|
-
codexItemId: item.id
|
|
15855
|
-
}));
|
|
15856
|
-
}
|
|
15857
|
-
break;
|
|
15858
|
-
}
|
|
15859
|
-
case "agentMessage": {
|
|
15860
|
-
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15861
|
-
if (text.length > 0) {
|
|
15862
|
-
envelopes.push(api.createEnvelope("agent", { t: "text", text }, {
|
|
15863
|
-
id: item.id,
|
|
15864
|
-
turn: turn.id,
|
|
15865
|
-
time: completedAt,
|
|
15866
|
-
codexItemId: item.id
|
|
15867
|
-
}));
|
|
15868
|
-
}
|
|
15869
|
-
break;
|
|
15870
|
-
}
|
|
15871
|
-
case "reasoning": {
|
|
15872
|
-
const text = reasoningText(item);
|
|
15873
|
-
if (text) {
|
|
15874
|
-
envelopes.push(api.createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15875
|
-
id: item.id,
|
|
15876
|
-
turn: turn.id,
|
|
15877
|
-
time: startedAt,
|
|
15878
|
-
codexItemId: item.id
|
|
15879
|
-
}));
|
|
15880
|
-
}
|
|
15881
|
-
break;
|
|
15882
|
-
}
|
|
15883
|
-
case "commandExecution": {
|
|
15884
|
-
const command = typeof item.command === "string" ? item.command : "";
|
|
15885
|
-
emitHistoricalToolCall(
|
|
15886
|
-
envelopes,
|
|
15887
|
-
turn,
|
|
15888
|
-
item,
|
|
15889
|
-
"CodexBash",
|
|
15890
|
-
commandToTitle(command),
|
|
15891
|
-
{ command, cwd: item.cwd },
|
|
15892
|
-
typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : null
|
|
15893
|
-
);
|
|
15894
|
-
break;
|
|
15895
|
-
}
|
|
15896
|
-
case "fileChange": {
|
|
15897
|
-
const title = "Apply patch";
|
|
15898
|
-
emitHistoricalToolCall(
|
|
15899
|
-
envelopes,
|
|
15900
|
-
turn,
|
|
15901
|
-
item,
|
|
15902
|
-
"CodexPatch",
|
|
15903
|
-
title,
|
|
15904
|
-
{ changes: item.changes, status: item.status },
|
|
15905
|
-
null
|
|
15906
|
-
);
|
|
15907
|
-
break;
|
|
15908
|
-
}
|
|
15909
|
-
case "mcpToolCall": {
|
|
15910
|
-
const title = `${item.server}.${item.tool}`;
|
|
15911
|
-
const output = item.error !== void 0 && item.error !== null ? String(item.error) : item.result !== void 0 && item.result !== null ? String(item.result) : null;
|
|
15912
|
-
emitHistoricalToolCall(
|
|
15913
|
-
envelopes,
|
|
15914
|
-
turn,
|
|
15915
|
-
item,
|
|
15916
|
-
"McpTool",
|
|
15917
|
-
title,
|
|
15918
|
-
{
|
|
15919
|
-
server: item.server,
|
|
15920
|
-
tool: item.tool,
|
|
15921
|
-
arguments: item.arguments
|
|
15922
|
-
},
|
|
15923
|
-
output
|
|
15924
|
-
);
|
|
15925
|
-
break;
|
|
15926
|
-
}
|
|
15927
|
-
}
|
|
16201
|
+
/**
|
|
16202
|
+
* Finish current tool call if one is in progress.
|
|
16203
|
+
*/
|
|
16204
|
+
finishCurrentToolCall(status) {
|
|
16205
|
+
if (this.toolCallStarted && this.currentCallId) {
|
|
16206
|
+
const toolResult = {
|
|
16207
|
+
type: "tool-call-result",
|
|
16208
|
+
callId: this.currentCallId,
|
|
16209
|
+
output: {
|
|
16210
|
+
content: this.contentBuffer || "",
|
|
16211
|
+
status
|
|
16212
|
+
},
|
|
16213
|
+
id: node_crypto.randomUUID()
|
|
16214
|
+
};
|
|
16215
|
+
persistence.logger.debug(`${this.getLogPrefix()} Sending tool call result with status: ${status}`);
|
|
16216
|
+
this.onMessage?.(toolResult);
|
|
15928
16217
|
}
|
|
15929
|
-
envelopes.push(api.createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15930
|
-
id: `${turn.id}:end`,
|
|
15931
|
-
turn: turn.id,
|
|
15932
|
-
time: completedAt
|
|
15933
|
-
}));
|
|
15934
16218
|
}
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
16219
|
+
/**
|
|
16220
|
+
* Reset internal state.
|
|
16221
|
+
*/
|
|
16222
|
+
resetState() {
|
|
16223
|
+
this.accumulator = "";
|
|
16224
|
+
this.inTitleCapture = false;
|
|
16225
|
+
this.titleBuffer = "";
|
|
16226
|
+
this.contentBuffer = "";
|
|
16227
|
+
this.hasTitle = false;
|
|
16228
|
+
this.currentCallId = null;
|
|
16229
|
+
this.toolCallStarted = false;
|
|
16230
|
+
this.currentTitle = null;
|
|
15940
16231
|
}
|
|
15941
|
-
|
|
15942
|
-
|
|
15943
|
-
|
|
16232
|
+
/**
|
|
16233
|
+
* Get the current call ID for tool result matching.
|
|
16234
|
+
*/
|
|
16235
|
+
getCurrentCallId() {
|
|
16236
|
+
return this.currentCallId;
|
|
16237
|
+
}
|
|
16238
|
+
/**
|
|
16239
|
+
* Check if a tool call has been started.
|
|
16240
|
+
*/
|
|
16241
|
+
hasStartedToolCall() {
|
|
16242
|
+
return this.toolCallStarted;
|
|
15944
16243
|
}
|
|
15945
|
-
return `Applying patch to ${fileCount} files`;
|
|
15946
16244
|
}
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
return
|
|
16245
|
+
|
|
16246
|
+
class ReasoningProcessor extends BaseReasoningProcessor {
|
|
16247
|
+
getToolName() {
|
|
16248
|
+
return "CodexReasoning";
|
|
15951
16249
|
}
|
|
15952
|
-
|
|
15953
|
-
return "
|
|
16250
|
+
getLogPrefix() {
|
|
16251
|
+
return "[ReasoningProcessor]";
|
|
15954
16252
|
}
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
}
|
|
15961
|
-
return "cancelled";
|
|
16253
|
+
/**
|
|
16254
|
+
* Process a reasoning delta and accumulate content.
|
|
16255
|
+
*/
|
|
16256
|
+
processDelta(delta) {
|
|
16257
|
+
this.processInput(delta);
|
|
15962
16258
|
}
|
|
15963
|
-
|
|
15964
|
-
|
|
16259
|
+
/**
|
|
16260
|
+
* Complete the reasoning section with final text.
|
|
16261
|
+
*/
|
|
16262
|
+
complete(fullText) {
|
|
16263
|
+
this.completeReasoning(fullText);
|
|
15965
16264
|
}
|
|
15966
|
-
return "completed";
|
|
15967
16265
|
}
|
|
15968
|
-
|
|
15969
|
-
|
|
15970
|
-
|
|
15971
|
-
|
|
15972
|
-
|
|
15973
|
-
|
|
15974
|
-
const turnId = cuid2.createId();
|
|
15975
|
-
const turnStart = api.createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15976
|
-
startedSubagents.clear();
|
|
15977
|
-
activeSubagents.clear();
|
|
15978
|
-
providerSubagentToSessionSubagent.clear();
|
|
15979
|
-
return {
|
|
15980
|
-
currentTurnId: turnId,
|
|
15981
|
-
startedSubagents,
|
|
15982
|
-
activeSubagents,
|
|
15983
|
-
providerSubagentToSessionSubagent,
|
|
15984
|
-
envelopes: [turnStart]
|
|
15985
|
-
};
|
|
15986
|
-
}
|
|
15987
|
-
if (type === "task_complete" || type === "turn_aborted") {
|
|
15988
|
-
if (!state.currentTurnId) {
|
|
15989
|
-
return {
|
|
15990
|
-
currentTurnId: null,
|
|
15991
|
-
startedSubagents,
|
|
15992
|
-
activeSubagents,
|
|
15993
|
-
providerSubagentToSessionSubagent,
|
|
15994
|
-
envelopes: []
|
|
15995
|
-
};
|
|
15996
|
-
}
|
|
15997
|
-
const lifecycleOpts = { turn: state.currentTurnId };
|
|
15998
|
-
providerSubagentToSessionSubagent.clear();
|
|
15999
|
-
return {
|
|
16000
|
-
currentTurnId: null,
|
|
16001
|
-
startedSubagents,
|
|
16002
|
-
activeSubagents,
|
|
16003
|
-
providerSubagentToSessionSubagent,
|
|
16004
|
-
envelopes: [
|
|
16005
|
-
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
16006
|
-
api.createEnvelope("agent", {
|
|
16007
|
-
t: "turn-end",
|
|
16008
|
-
status: pickTurnEndStatus(message, type)
|
|
16009
|
-
}, lifecycleOpts)
|
|
16010
|
-
]
|
|
16011
|
-
};
|
|
16012
|
-
}
|
|
16013
|
-
if (type === "token_count") {
|
|
16014
|
-
return {
|
|
16015
|
-
currentTurnId: state.currentTurnId,
|
|
16016
|
-
startedSubagents,
|
|
16017
|
-
activeSubagents,
|
|
16018
|
-
providerSubagentToSessionSubagent,
|
|
16019
|
-
envelopes: []
|
|
16020
|
-
};
|
|
16266
|
+
|
|
16267
|
+
class DiffProcessor {
|
|
16268
|
+
previousDiff = null;
|
|
16269
|
+
onMessage = null;
|
|
16270
|
+
constructor(onMessage) {
|
|
16271
|
+
this.onMessage = onMessage || null;
|
|
16021
16272
|
}
|
|
16022
|
-
|
|
16023
|
-
|
|
16024
|
-
|
|
16025
|
-
|
|
16026
|
-
|
|
16027
|
-
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16273
|
+
/**
|
|
16274
|
+
* Process a turn_diff message and check if the unified_diff has changed
|
|
16275
|
+
*/
|
|
16276
|
+
processDiff(unifiedDiff) {
|
|
16277
|
+
if (this.previousDiff !== unifiedDiff) {
|
|
16278
|
+
persistence.logger.debug("[DiffProcessor] Unified diff changed, sending CodexDiff tool call");
|
|
16279
|
+
const callId = node_crypto.randomUUID();
|
|
16280
|
+
const toolCall = {
|
|
16281
|
+
type: "tool-call",
|
|
16282
|
+
name: "CodexDiff",
|
|
16283
|
+
callId,
|
|
16284
|
+
input: {
|
|
16285
|
+
unified_diff: unifiedDiff
|
|
16286
|
+
},
|
|
16287
|
+
id: node_crypto.randomUUID()
|
|
16032
16288
|
};
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16039
|
-
|
|
16040
|
-
|
|
16041
|
-
providerSubagentToSessionSubagent,
|
|
16042
|
-
envelopes
|
|
16043
|
-
};
|
|
16044
|
-
}
|
|
16045
|
-
if (type === "agent_reasoning" || type === "agent_reasoning_delta") {
|
|
16046
|
-
const text = typeof message.text === "string" ? message.text : typeof message.delta === "string" ? message.delta : null;
|
|
16047
|
-
if (!text) {
|
|
16048
|
-
return {
|
|
16049
|
-
currentTurnId: state.currentTurnId,
|
|
16050
|
-
startedSubagents,
|
|
16051
|
-
activeSubagents,
|
|
16052
|
-
providerSubagentToSessionSubagent,
|
|
16053
|
-
envelopes: []
|
|
16289
|
+
this.onMessage?.(toolCall);
|
|
16290
|
+
const toolResult = {
|
|
16291
|
+
type: "tool-call-result",
|
|
16292
|
+
callId,
|
|
16293
|
+
output: {
|
|
16294
|
+
status: "completed"
|
|
16295
|
+
},
|
|
16296
|
+
id: node_crypto.randomUUID()
|
|
16054
16297
|
};
|
|
16298
|
+
this.onMessage?.(toolResult);
|
|
16055
16299
|
}
|
|
16056
|
-
|
|
16057
|
-
|
|
16058
|
-
envelopes.push(api.createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
16059
|
-
return {
|
|
16060
|
-
currentTurnId: state.currentTurnId,
|
|
16061
|
-
startedSubagents,
|
|
16062
|
-
activeSubagents,
|
|
16063
|
-
providerSubagentToSessionSubagent,
|
|
16064
|
-
envelopes
|
|
16065
|
-
};
|
|
16300
|
+
this.previousDiff = unifiedDiff;
|
|
16301
|
+
persistence.logger.debug("[DiffProcessor] Updated stored diff");
|
|
16066
16302
|
}
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
16074
|
-
envelopes.push(
|
|
16075
|
-
api.createEnvelope("agent", {
|
|
16076
|
-
t: "tool-call-start",
|
|
16077
|
-
call,
|
|
16078
|
-
name: "CodexBash",
|
|
16079
|
-
title: commandToTitle(command),
|
|
16080
|
-
description,
|
|
16081
|
-
args
|
|
16082
|
-
}, opts)
|
|
16083
|
-
);
|
|
16084
|
-
return {
|
|
16085
|
-
currentTurnId: state.currentTurnId,
|
|
16086
|
-
startedSubagents,
|
|
16087
|
-
activeSubagents,
|
|
16088
|
-
providerSubagentToSessionSubagent,
|
|
16089
|
-
envelopes
|
|
16090
|
-
};
|
|
16303
|
+
/**
|
|
16304
|
+
* Reset the processor state (called on task_complete or turn_aborted)
|
|
16305
|
+
*/
|
|
16306
|
+
reset() {
|
|
16307
|
+
persistence.logger.debug("[DiffProcessor] Resetting diff state");
|
|
16308
|
+
this.previousDiff = null;
|
|
16091
16309
|
}
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16097
|
-
return {
|
|
16098
|
-
currentTurnId: state.currentTurnId,
|
|
16099
|
-
startedSubagents,
|
|
16100
|
-
activeSubagents,
|
|
16101
|
-
providerSubagentToSessionSubagent,
|
|
16102
|
-
envelopes
|
|
16103
|
-
};
|
|
16310
|
+
/**
|
|
16311
|
+
* Set the message callback for sending messages directly
|
|
16312
|
+
*/
|
|
16313
|
+
setMessageCallback(callback) {
|
|
16314
|
+
this.onMessage = callback;
|
|
16104
16315
|
}
|
|
16105
|
-
|
|
16106
|
-
|
|
16107
|
-
|
|
16108
|
-
|
|
16109
|
-
|
|
16110
|
-
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
16111
|
-
envelopes.push(
|
|
16112
|
-
api.createEnvelope("agent", {
|
|
16113
|
-
t: "tool-call-start",
|
|
16114
|
-
call,
|
|
16115
|
-
name: "CodexPatch",
|
|
16116
|
-
title: "Apply patch",
|
|
16117
|
-
description: patchDescription(changes),
|
|
16118
|
-
args: {
|
|
16119
|
-
auto_approved: autoApproved,
|
|
16120
|
-
changes
|
|
16121
|
-
}
|
|
16122
|
-
}, opts)
|
|
16123
|
-
);
|
|
16124
|
-
return {
|
|
16125
|
-
currentTurnId: state.currentTurnId,
|
|
16126
|
-
startedSubagents,
|
|
16127
|
-
activeSubagents,
|
|
16128
|
-
providerSubagentToSessionSubagent,
|
|
16129
|
-
envelopes
|
|
16130
|
-
};
|
|
16316
|
+
/**
|
|
16317
|
+
* Get the current diff value
|
|
16318
|
+
*/
|
|
16319
|
+
getCurrentDiff() {
|
|
16320
|
+
return this.previousDiff;
|
|
16131
16321
|
}
|
|
16132
|
-
|
|
16133
|
-
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16140
|
-
|
|
16141
|
-
|
|
16142
|
-
|
|
16322
|
+
}
|
|
16323
|
+
|
|
16324
|
+
const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
|
|
16325
|
+
const [messages, setMessages] = React.useState([]);
|
|
16326
|
+
const [confirmationMode, setConfirmationMode] = React.useState(false);
|
|
16327
|
+
const [actionInProgress, setActionInProgress] = React.useState(false);
|
|
16328
|
+
const confirmationTimeoutRef = React.useRef(null);
|
|
16329
|
+
const { stdout } = ink.useStdout();
|
|
16330
|
+
const terminalWidth = stdout.columns || 80;
|
|
16331
|
+
const terminalHeight = stdout.rows || 24;
|
|
16332
|
+
React.useEffect(() => {
|
|
16333
|
+
setMessages(messageBuffer.getMessages());
|
|
16334
|
+
const unsubscribe = messageBuffer.onUpdate((newMessages) => {
|
|
16335
|
+
setMessages(newMessages);
|
|
16336
|
+
});
|
|
16337
|
+
return () => {
|
|
16338
|
+
unsubscribe();
|
|
16339
|
+
if (confirmationTimeoutRef.current) {
|
|
16340
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
16341
|
+
}
|
|
16143
16342
|
};
|
|
16144
|
-
}
|
|
16343
|
+
}, [messageBuffer]);
|
|
16344
|
+
const resetConfirmation = React.useCallback(() => {
|
|
16345
|
+
setConfirmationMode(false);
|
|
16346
|
+
if (confirmationTimeoutRef.current) {
|
|
16347
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
16348
|
+
confirmationTimeoutRef.current = null;
|
|
16349
|
+
}
|
|
16350
|
+
}, []);
|
|
16351
|
+
const setConfirmationWithTimeout = React.useCallback(() => {
|
|
16352
|
+
setConfirmationMode(true);
|
|
16353
|
+
if (confirmationTimeoutRef.current) {
|
|
16354
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
16355
|
+
}
|
|
16356
|
+
confirmationTimeoutRef.current = setTimeout(() => {
|
|
16357
|
+
resetConfirmation();
|
|
16358
|
+
}, 15e3);
|
|
16359
|
+
}, [resetConfirmation]);
|
|
16360
|
+
ink.useInput(React.useCallback(async (input, key) => {
|
|
16361
|
+
if (actionInProgress) return;
|
|
16362
|
+
if (key.ctrl && input === "c") {
|
|
16363
|
+
if (confirmationMode) {
|
|
16364
|
+
resetConfirmation();
|
|
16365
|
+
setActionInProgress(true);
|
|
16366
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
16367
|
+
onExit?.();
|
|
16368
|
+
} else {
|
|
16369
|
+
setConfirmationWithTimeout();
|
|
16370
|
+
}
|
|
16371
|
+
return;
|
|
16372
|
+
}
|
|
16373
|
+
if (confirmationMode) {
|
|
16374
|
+
resetConfirmation();
|
|
16375
|
+
}
|
|
16376
|
+
}, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
|
|
16377
|
+
const getMessageColor = (type) => {
|
|
16378
|
+
switch (type) {
|
|
16379
|
+
case "user":
|
|
16380
|
+
return "magenta";
|
|
16381
|
+
case "assistant":
|
|
16382
|
+
return "cyan";
|
|
16383
|
+
case "system":
|
|
16384
|
+
return "blue";
|
|
16385
|
+
case "tool":
|
|
16386
|
+
return "yellow";
|
|
16387
|
+
case "result":
|
|
16388
|
+
return "green";
|
|
16389
|
+
case "status":
|
|
16390
|
+
return "gray";
|
|
16391
|
+
default:
|
|
16392
|
+
return "white";
|
|
16393
|
+
}
|
|
16394
|
+
};
|
|
16395
|
+
const formatMessage = (msg) => {
|
|
16396
|
+
const lines = msg.content.split("\n");
|
|
16397
|
+
const maxLineLength = terminalWidth - 10;
|
|
16398
|
+
return lines.map((line) => {
|
|
16399
|
+
if (line.length <= maxLineLength) return line;
|
|
16400
|
+
const chunks = [];
|
|
16401
|
+
for (let i = 0; i < line.length; i += maxLineLength) {
|
|
16402
|
+
chunks.push(line.slice(i, i + maxLineLength));
|
|
16403
|
+
}
|
|
16404
|
+
return chunks.join("\n");
|
|
16405
|
+
}).join("\n");
|
|
16406
|
+
};
|
|
16407
|
+
return /* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", width: terminalWidth, height: terminalHeight }, /* @__PURE__ */ React.createElement(
|
|
16408
|
+
ink.Box,
|
|
16409
|
+
{
|
|
16410
|
+
flexDirection: "column",
|
|
16411
|
+
width: terminalWidth,
|
|
16412
|
+
height: terminalHeight - 4,
|
|
16413
|
+
borderStyle: "round",
|
|
16414
|
+
borderColor: "gray",
|
|
16415
|
+
paddingX: 1,
|
|
16416
|
+
overflow: "hidden"
|
|
16417
|
+
},
|
|
16418
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "\u{1F916} Codex Agent Messages"), /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "\u2500".repeat(Math.min(terminalWidth - 4, 60)))),
|
|
16419
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", height: terminalHeight - 10, overflow: "hidden" }, messages.length === 0 ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Waiting for messages...") : (
|
|
16420
|
+
// Show only the last messages that fit in the available space
|
|
16421
|
+
messages.slice(-Math.max(1, terminalHeight - 10)).map((msg) => /* @__PURE__ */ React.createElement(ink.Box, { key: msg.id, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(ink.Text, { color: getMessageColor(msg.type), dimColor: true }, formatMessage(msg))))
|
|
16422
|
+
))
|
|
16423
|
+
), /* @__PURE__ */ React.createElement(
|
|
16424
|
+
ink.Box,
|
|
16425
|
+
{
|
|
16426
|
+
width: terminalWidth,
|
|
16427
|
+
borderStyle: "round",
|
|
16428
|
+
borderColor: actionInProgress ? "gray" : confirmationMode ? "red" : "green",
|
|
16429
|
+
paddingX: 2,
|
|
16430
|
+
justifyContent: "center",
|
|
16431
|
+
alignItems: "center",
|
|
16432
|
+
flexDirection: "column"
|
|
16433
|
+
},
|
|
16434
|
+
/* @__PURE__ */ React.createElement(ink.Box, { flexDirection: "column", alignItems: "center" }, actionInProgress ? /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode ? /* @__PURE__ */ React.createElement(ink.Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ink.Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(ink.Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
|
|
16435
|
+
));
|
|
16436
|
+
};
|
|
16437
|
+
|
|
16438
|
+
function createOfflineSessionStub(sessionTag) {
|
|
16145
16439
|
return {
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16440
|
+
sessionId: `offline-${sessionTag}`,
|
|
16441
|
+
sendCodexMessage: () => {
|
|
16442
|
+
},
|
|
16443
|
+
sendAgentMessage: () => {
|
|
16444
|
+
},
|
|
16445
|
+
sendAgentUsageSnapshot: () => false,
|
|
16446
|
+
sendClaudeSessionMessage: () => {
|
|
16447
|
+
},
|
|
16448
|
+
keepAlive: () => {
|
|
16449
|
+
},
|
|
16450
|
+
sendSessionEvent: () => {
|
|
16451
|
+
},
|
|
16452
|
+
sendSessionDeath: () => {
|
|
16453
|
+
},
|
|
16454
|
+
updateLifecycleState: () => {
|
|
16455
|
+
},
|
|
16456
|
+
requestControlTransfer: async () => {
|
|
16457
|
+
},
|
|
16458
|
+
flush: async () => {
|
|
16459
|
+
},
|
|
16460
|
+
close: async () => {
|
|
16461
|
+
},
|
|
16462
|
+
updateMetadata: () => {
|
|
16463
|
+
},
|
|
16464
|
+
updateAgentState: () => {
|
|
16465
|
+
},
|
|
16466
|
+
onUserMessage: () => {
|
|
16467
|
+
},
|
|
16468
|
+
onFileEvent: () => {
|
|
16469
|
+
},
|
|
16470
|
+
trackAttachmentDownload: () => {
|
|
16471
|
+
},
|
|
16472
|
+
drainAttachmentsForUserMessage: async () => [],
|
|
16473
|
+
rpcHandlerManager: {
|
|
16474
|
+
registerHandler: () => {
|
|
16475
|
+
}
|
|
16476
|
+
}
|
|
16151
16477
|
};
|
|
16152
16478
|
}
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
const
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
|
|
16479
|
+
|
|
16480
|
+
function setupOfflineReconnection(opts) {
|
|
16481
|
+
const { api: api$1, sessionTag, metadata, state, response, onSessionSwap } = opts;
|
|
16482
|
+
let session;
|
|
16483
|
+
let reconnectionHandle = null;
|
|
16484
|
+
if (!response) {
|
|
16485
|
+
session = createOfflineSessionStub(sessionTag);
|
|
16486
|
+
reconnectionHandle = api.startOfflineReconnection({
|
|
16487
|
+
serverUrl: persistence.configuration.serverUrl,
|
|
16488
|
+
onReconnected: async () => {
|
|
16489
|
+
const resp = await api$1.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
16490
|
+
if (!resp) throw new Error("Server unavailable");
|
|
16491
|
+
const realSession = api$1.sessionSyncClient(resp);
|
|
16492
|
+
onSessionSwap(realSession);
|
|
16493
|
+
return realSession;
|
|
16494
|
+
},
|
|
16495
|
+
onNotify: (msg) => {
|
|
16496
|
+
console.log(msg);
|
|
16497
|
+
}
|
|
16498
|
+
});
|
|
16499
|
+
return { session, reconnectionHandle, isOffline: true };
|
|
16500
|
+
} else {
|
|
16501
|
+
session = api$1.sessionSyncClient(response, opts.sessionClientOptions);
|
|
16502
|
+
return { session, reconnectionHandle: null, isOffline: false };
|
|
16162
16503
|
}
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
|
|
16171
|
-
args: toolLikeMessage.input && typeof toolLikeMessage.input === "object" ? toolLikeMessage.input : {}
|
|
16172
|
-
}, opts)];
|
|
16504
|
+
}
|
|
16505
|
+
|
|
16506
|
+
function resolveCodexExecutionPolicy(permissionMode, sandboxManagedByHappy) {
|
|
16507
|
+
if (sandboxManagedByHappy) {
|
|
16508
|
+
return {
|
|
16509
|
+
approvalPolicy: "never",
|
|
16510
|
+
sandbox: "danger-full-access"
|
|
16511
|
+
};
|
|
16173
16512
|
}
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
|
|
16179
|
-
|
|
16180
|
-
|
|
16181
|
-
|
|
16182
|
-
|
|
16513
|
+
const approvalPolicy = (() => {
|
|
16514
|
+
switch (permissionMode) {
|
|
16515
|
+
// Codex native modes
|
|
16516
|
+
case "default":
|
|
16517
|
+
return "untrusted";
|
|
16518
|
+
// Ask for non-trusted commands
|
|
16519
|
+
case "read-only":
|
|
16520
|
+
return "never";
|
|
16521
|
+
// Never ask, read-only enforced by sandbox
|
|
16522
|
+
case "safe-yolo":
|
|
16523
|
+
return "on-request";
|
|
16524
|
+
// Workspace auto-run; request approval for escalation (0.153 removed on-failure)
|
|
16525
|
+
case "yolo":
|
|
16526
|
+
return "never";
|
|
16527
|
+
// Full YOLO: never interrupt for approvals
|
|
16528
|
+
// Defensive fallback for Claude-specific modes (backward compatibility)
|
|
16529
|
+
case "bypassPermissions":
|
|
16530
|
+
return "never";
|
|
16531
|
+
// Full access: map to yolo behavior
|
|
16532
|
+
case "acceptEdits":
|
|
16533
|
+
return "on-request";
|
|
16534
|
+
// Let model decide (closest to auto-approve edits)
|
|
16535
|
+
case "plan":
|
|
16536
|
+
return "untrusted";
|
|
16537
|
+
// Conservative: ask for non-trusted
|
|
16538
|
+
default:
|
|
16539
|
+
return "untrusted";
|
|
16183
16540
|
}
|
|
16184
|
-
|
|
16185
|
-
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
|
|
16541
|
+
})();
|
|
16542
|
+
const sandbox = (() => {
|
|
16543
|
+
switch (permissionMode) {
|
|
16544
|
+
// Codex native modes
|
|
16545
|
+
case "default":
|
|
16546
|
+
return "workspace-write";
|
|
16547
|
+
// Can write in workspace
|
|
16548
|
+
case "read-only":
|
|
16549
|
+
return "read-only";
|
|
16550
|
+
// Read-only filesystem
|
|
16551
|
+
case "safe-yolo":
|
|
16552
|
+
return "workspace-write";
|
|
16553
|
+
// Can write in workspace
|
|
16554
|
+
case "yolo":
|
|
16555
|
+
return "danger-full-access";
|
|
16556
|
+
// Full system access
|
|
16557
|
+
// Defensive fallback for Claude-specific modes
|
|
16558
|
+
case "bypassPermissions":
|
|
16559
|
+
return "danger-full-access";
|
|
16560
|
+
// Full access: map to yolo
|
|
16561
|
+
case "acceptEdits":
|
|
16562
|
+
return "workspace-write";
|
|
16563
|
+
// Can edit files in workspace
|
|
16564
|
+
case "plan":
|
|
16565
|
+
return "workspace-write";
|
|
16566
|
+
// Can write for planning
|
|
16567
|
+
default:
|
|
16568
|
+
return "workspace-write";
|
|
16569
|
+
}
|
|
16570
|
+
})();
|
|
16571
|
+
return { approvalPolicy, sandbox };
|
|
16191
16572
|
}
|
|
16192
16573
|
|
|
16193
16574
|
async function resumeExistingThread(opts) {
|
|
@@ -16659,6 +17040,13 @@ async function runCodex(opts) {
|
|
|
16659
17040
|
process.exit(1);
|
|
16660
17041
|
}
|
|
16661
17042
|
};
|
|
17043
|
+
session.rpcHandlerManager.registerHandler("codex-child-read", async (args) => {
|
|
17044
|
+
try {
|
|
17045
|
+
return await readChildThread(client, args?.threadId);
|
|
17046
|
+
} catch (error) {
|
|
17047
|
+
return { error: error instanceof Error ? error.message : "Child read failed" };
|
|
17048
|
+
}
|
|
17049
|
+
});
|
|
16662
17050
|
session.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
16663
17051
|
const disposeTermination = registerKillSessionHandler(session.rpcHandlerManager, handleKillSession, session, { processSignals: true });
|
|
16664
17052
|
const messageBuffer = new MessageBuffer();
|
|
@@ -17199,7 +17587,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17199
17587
|
process.exit(0);
|
|
17200
17588
|
} else if (subcommand === "install-terminal-hooks") {
|
|
17201
17589
|
try {
|
|
17202
|
-
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-
|
|
17590
|
+
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await Promise.resolve().then(function () { return require('./installTerminalHooks-2POBqVlu.cjs'); });
|
|
17203
17591
|
const command = parseTerminalHooksArgs(args.slice(1));
|
|
17204
17592
|
if (command.action === "help") {
|
|
17205
17593
|
console.log(TERMINAL_HOOKS_HELP);
|
|
@@ -17216,7 +17604,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17216
17604
|
}
|
|
17217
17605
|
} else if (subcommand === "spawn") {
|
|
17218
17606
|
try {
|
|
17219
|
-
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-
|
|
17607
|
+
const { handleSpawnCommand } = await Promise.resolve().then(function () { return require('./spawn-CMjhlwlD.cjs'); });
|
|
17220
17608
|
await handleSpawnCommand(args.slice(1));
|
|
17221
17609
|
} catch (error) {
|
|
17222
17610
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17228,7 +17616,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17228
17616
|
return;
|
|
17229
17617
|
} else if (subcommand === "sessions") {
|
|
17230
17618
|
try {
|
|
17231
|
-
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-
|
|
17619
|
+
const { handleSessionsCommand } = await Promise.resolve().then(function () { return require('./sessions-Q4BR02dG.cjs'); });
|
|
17232
17620
|
await handleSessionsCommand(args.slice(1));
|
|
17233
17621
|
} catch (error) {
|
|
17234
17622
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17240,7 +17628,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17240
17628
|
return;
|
|
17241
17629
|
} else if (subcommand === "send") {
|
|
17242
17630
|
try {
|
|
17243
|
-
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-
|
|
17631
|
+
const { handleSendCommand } = await Promise.resolve().then(function () { return require('./send-DP6n2J9x.cjs'); });
|
|
17244
17632
|
await handleSendCommand(args.slice(1));
|
|
17245
17633
|
} catch (error) {
|
|
17246
17634
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17346,9 +17734,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17346
17734
|
if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
|
|
17347
17735
|
const projectId = args[3];
|
|
17348
17736
|
try {
|
|
17349
|
-
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config
|
|
17350
|
-
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-
|
|
17351
|
-
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-
|
|
17737
|
+
const { saveGoogleCloudProjectToConfig } = await Promise.resolve().then(function () { return require('./config--cxxmkOd.cjs'); });
|
|
17738
|
+
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await Promise.resolve().then(function () { return require('./limits-DRrdJ7gr.cjs'); }).then(function (n) { return n.persistence; });
|
|
17739
|
+
const { ApiClient: ApiClient2 } = await Promise.resolve().then(function () { return require('./types-DbBPNIwf.cjs'); }).then(function (n) { return n.api; });
|
|
17352
17740
|
let userEmail = void 0;
|
|
17353
17741
|
try {
|
|
17354
17742
|
const credentials = await readCredentialsForConfiguredRelay2();
|
|
@@ -17379,7 +17767,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17379
17767
|
}
|
|
17380
17768
|
if (geminiSubcommand === "project" && args[2] === "get") {
|
|
17381
17769
|
try {
|
|
17382
|
-
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config
|
|
17770
|
+
const { readGeminiLocalConfig } = await Promise.resolve().then(function () { return require('./config--cxxmkOd.cjs'); });
|
|
17383
17771
|
const config = readGeminiLocalConfig();
|
|
17384
17772
|
if (config.googleCloudProject) {
|
|
17385
17773
|
console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
|
|
@@ -17419,7 +17807,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17419
17807
|
process.exit(0);
|
|
17420
17808
|
}
|
|
17421
17809
|
try {
|
|
17422
|
-
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-
|
|
17810
|
+
const { runGemini } = await Promise.resolve().then(function () { return require('./runGemini-B9Rc-H0G.cjs'); });
|
|
17423
17811
|
let startedBy = void 0;
|
|
17424
17812
|
for (let i = 1; i < args.length; i++) {
|
|
17425
17813
|
if (args[i] === "--started-by") {
|
|
@@ -17449,7 +17837,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17449
17837
|
}
|
|
17450
17838
|
};
|
|
17451
17839
|
try {
|
|
17452
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
17840
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BPVQFcKW.cjs'); });
|
|
17453
17841
|
const parsed = parsePiRunnerArgs(args.slice(1));
|
|
17454
17842
|
const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
|
|
17455
17843
|
const { credentials } = await authAndSetupMachineIfNeeded();
|
|
@@ -17477,7 +17865,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17477
17865
|
return;
|
|
17478
17866
|
} else if (subcommand === "acp") {
|
|
17479
17867
|
try {
|
|
17480
|
-
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-
|
|
17868
|
+
const { runAcp, resolveAcpAgentConfig } = await Promise.resolve().then(function () { return require('./index-BPVQFcKW.cjs'); });
|
|
17481
17869
|
let startedBy = void 0;
|
|
17482
17870
|
let verbose = false;
|
|
17483
17871
|
const acpArgs = [];
|
|
@@ -17517,7 +17905,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17517
17905
|
return;
|
|
17518
17906
|
} else if (subcommand === "openclaw") {
|
|
17519
17907
|
try {
|
|
17520
|
-
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-
|
|
17908
|
+
const { runOpenClaw } = await Promise.resolve().then(function () { return require('./runOpenClaw-BsRRKDvF.cjs'); });
|
|
17521
17909
|
let startedBy = void 0;
|
|
17522
17910
|
let verbose = false;
|
|
17523
17911
|
let gatewayUrl;
|
|
@@ -17568,7 +17956,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17568
17956
|
return;
|
|
17569
17957
|
} else if (subcommand === "todo") {
|
|
17570
17958
|
try {
|
|
17571
|
-
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-
|
|
17959
|
+
const { handleTodoCommand } = await Promise.resolve().then(function () { return require('./todo-CaYedmtq.cjs'); });
|
|
17572
17960
|
await handleTodoCommand(args.slice(1));
|
|
17573
17961
|
} catch (error) {
|
|
17574
17962
|
console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
|
|
@@ -17577,7 +17965,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17577
17965
|
return;
|
|
17578
17966
|
} else if (subcommand === "teams") {
|
|
17579
17967
|
try {
|
|
17580
|
-
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-
|
|
17968
|
+
const { handleTeamsCommand } = await Promise.resolve().then(function () { return require('./teams-CkdUH10-.cjs'); });
|
|
17581
17969
|
await handleTeamsCommand(args.slice(1));
|
|
17582
17970
|
} catch (error) {
|
|
17583
17971
|
console.error(error instanceof Error ? error.message : "Teams command failed");
|
|
@@ -17586,7 +17974,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17586
17974
|
return;
|
|
17587
17975
|
} else if (subcommand === "mcp" && args.length === 1) {
|
|
17588
17976
|
try {
|
|
17589
|
-
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-
|
|
17977
|
+
const { handleMcpCommand } = await Promise.resolve().then(function () { return require('./mcp-C1JQsM1s.cjs'); });
|
|
17590
17978
|
await handleMcpCommand();
|
|
17591
17979
|
} catch (error) {
|
|
17592
17980
|
process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
|