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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import chalk from 'chalk';
|
|
2
2
|
import os, { homedir, userInfo, tmpdir } from 'node:os';
|
|
3
|
-
import { randomUUID, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
|
|
4
|
-
import { j as getProjectPath, g as BUILTIN_TODO_DISCOVERY, p as projectPath, k as initializeSandbox, w as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, h as delay, l as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, m as commandOnPath, n as deriveLocalCliUpdateSummary, o as
|
|
5
|
-
import { z as readDaemonState, l as logger, c as configuration, A as clearDaemonState, B as ensurePrivateDirectory, D as hardenPrivateFile, p as packageJson, g as readSettings, E as readCredentials, s as credentialRelayProblem, i as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, m as decodeBase64, I as writeCredentialsLegacy, J as writeCredentialsDataKey, d as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, K as decryptWithDataKey, L as decryptLegacy, M as persistSession, k as decrypt, f as ensurePrivateDirectorySync, N as acquireDaemonLock, O as deletePersistedSessions, P as writeDaemonState, Q as writeSettings, R as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES, S as clearCredentials, T as clearMachineId, U as SandboxConfigSchema, V as getLatestDaemonLog } from './limits-
|
|
3
|
+
import { randomUUID, randomInt, randomBytes, createHash as createHash$1, timingSafeEqual } from 'node:crypto';
|
|
4
|
+
import { j as getProjectPath, g as BUILTIN_TODO_DISCOVERY, p as projectPath, k as initializeSandbox, w as wrapCommand, d as contentLogMetadata, I as InvalidateSync, R as RawJSONLinesSchema, h as delay, l as AsyncLock, S as STREAM_DELTA_MAX_CHARS, M as MAX_CHAT_ATTACHMENT_SOURCE_BYTES, m as commandOnPath, n as deriveLocalCliUpdateSummary, o as AGENT_VERSION_SOURCES, q as isClaudeSessionId, r as getLocalHappyAgentCredentialPath, t as readLocalHappyAgentCredentials, u as parseExactVersion, v as compareExactVersions, x as extractCompleteLines, y as decideBackfill, z as parseTerminalHookPayload, C as tmuxArgs, D as scrubTmuxClientEnv, E as decideMirrorBinding, F as MIRROR_BACKFILL_LINES_DEFAULT, G as mirrorLineKey, H as mirrorLocalId, J as detectCLIAvailability, K as detectResumeSupport, L as summarizeSpawnSessionForLog, N as shellescape, A as ApiClient, O as fetchCliUpdateState, P as resolveCliUpdateCheckInterval, Q as DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS, i as isValidSessionId, T as listVhTerminals, U as isValidTerminalId, V as readVhTerminal, W as sendToVhTerminal, X as ContextUsageSchema, Y as TITLE_PROMPT_PREFIX, Z as BOARD_ANALYZER_PROMPT_PREFIX, f as connectionState, _ as startOfflineReconnection, c as createEnvelope, $ as CodexAppServerClient } from './types-BKrZubg8.mjs';
|
|
5
|
+
import { z as readDaemonState, l as logger, c as configuration, A as clearDaemonState, B as ensurePrivateDirectory, D as hardenPrivateFile, p as packageJson, g as readSettings, E as readCredentials, s as credentialRelayProblem, i as encodeBase64, F as readSettingsOutcome, G as updateSettings, H as encodeBase64Url, m as decodeBase64, I as writeCredentialsLegacy, J as writeCredentialsDataKey, d as readCredentialsForConfiguredRelay, j as encrypt, r as readPersistedSessions, w as writePrivateFileSync, K as decryptWithDataKey, L as decryptLegacy, M as persistSession, k as decrypt, f as ensurePrivateDirectorySync, N as acquireDaemonLock, O as deletePersistedSessions, P as writeDaemonState, Q as writeSettings, R as releaseDaemonLock, C as CLIPBOARD_TOOL_NAME, a as CLIPBOARD_TOOL_TITLE, b as CLIPBOARD_TOOL_DESCRIPTION, e as CLIPBOARD_MAX_BYTES, S as clearCredentials, T as clearMachineId, U as SandboxConfigSchema, V as getLatestDaemonLog } from './limits-kjHZUlSp.mjs';
|
|
6
6
|
import spawn$1, { spawn } from 'cross-spawn';
|
|
7
7
|
import path, { join, resolve, basename, isAbsolute, dirname, sep, delimiter, normalize } from 'node:path';
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
9
9
|
import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, unlinkSync, renameSync, writeFileSync, accessSync, constants, openSync, fsyncSync, closeSync, rmSync as rmSync$1, chmodSync as chmodSync$1 } from 'node:fs';
|
|
10
|
-
import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-
|
|
10
|
+
import { f as trimIdent, A as AGENT_GUIDANCE, t as teamScopePath, T as TeamConnectionPreflightError, e as createTeamsClient, P as PREVIEW_TOOL_NAME, R as REPORT_PROGRESS_TOOL_NAME, g as TEAM_TOOL_NAMES, r as registerTeamsTools, a as PREVIEW_TOOL_TITLE, b as PREVIEW_TOOL_DESCRIPTION, h as REPORT_PROGRESS_TOOL_TITLE, i as REPORT_PROGRESS_TOOL_DESCRIPTION } from './agentGuidance-ASxKHxBP.mjs';
|
|
11
11
|
import { readFile, writeFile, rename, unlink, mkdir, symlink, rm, readlink, lstat, stat, open as open$1, appendFile } from 'node:fs/promises';
|
|
12
12
|
import fs, { watch, access } from 'fs/promises';
|
|
13
13
|
import { useStdout, useInput, Box, Text, render } from 'ink';
|
|
@@ -20,7 +20,7 @@ import tweetnacl from 'tweetnacl';
|
|
|
20
20
|
import os$1, { homedir as homedir$1 } from 'os';
|
|
21
21
|
import { spawn as spawn$2, execSync as execSync$1, exec as exec$1 } from 'child_process';
|
|
22
22
|
import { createId } from '@paralleldrive/cuid2';
|
|
23
|
-
import { execSync, spawnSync, spawn as spawn$3,
|
|
23
|
+
import { execSync, spawnSync, execFile, spawn as spawn$3, execFileSync } from 'node:child_process';
|
|
24
24
|
import { existsSync as existsSync$1, statSync as statSync$1, mkdtempSync, rmSync, writeFileSync as writeFileSync$1, chmodSync, unlinkSync as unlinkSync$1 } from 'fs';
|
|
25
25
|
import { join as join$1 } from 'path';
|
|
26
26
|
import '@anthropic-ai/sandbox-runtime';
|
|
@@ -320,6 +320,20 @@ class Session {
|
|
|
320
320
|
sessionId;
|
|
321
321
|
mode = "local";
|
|
322
322
|
thinking = false;
|
|
323
|
+
/**
|
|
324
|
+
* Window after a user-initiated abort during which stray promise rejections
|
|
325
|
+
* from tearing down the SDK query (MCP transports, in-flight tool calls,
|
|
326
|
+
* the aborted streaming generator) are benign teardown noise — NOT a real
|
|
327
|
+
* crash. The process-level unhandledRejection/uncaughtException handlers
|
|
328
|
+
* must not treat them as fatal and force-archive the session (the reported
|
|
329
|
+
* "点终止就 archive" bug). Self-expiring so a genuine later crash still
|
|
330
|
+
* archives normally.
|
|
331
|
+
*/
|
|
332
|
+
abortingUntil = 0;
|
|
333
|
+
markAborting = (windowMs = 8e3) => {
|
|
334
|
+
this.abortingUntil = Date.now() + windowMs;
|
|
335
|
+
};
|
|
336
|
+
isAborting = () => Date.now() < this.abortingUntil;
|
|
323
337
|
/**
|
|
324
338
|
* Account-encrypted notification producer. Set lazily once we have a
|
|
325
339
|
* server session id (notifications are addressed per session). May be
|
|
@@ -1331,6 +1345,152 @@ async function claudeLocalLauncher(session) {
|
|
|
1331
1345
|
return exitReason || { type: "exit", code: 0 };
|
|
1332
1346
|
}
|
|
1333
1347
|
|
|
1348
|
+
const text = (value) => typeof value === "string" && value.length > 0 && value.length <= 1024 && !value.includes("\0") ? value : null;
|
|
1349
|
+
const publicMcpStatus = (servers) => servers.map(({ name, status, serverInfo, scope, tools }) => ({ name, status, serverInfo, scope, tools: tools?.map(({ name: name2 }) => ({ name: name2 })) }));
|
|
1350
|
+
const fingerprint = (result) => JSON.stringify([result.canRewind, [...result.filesChanged ?? []].sort(), result.insertions, result.deletions]);
|
|
1351
|
+
function isRuntimeCheckpointReplay(message) {
|
|
1352
|
+
const m = message;
|
|
1353
|
+
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"));
|
|
1354
|
+
}
|
|
1355
|
+
function createRuntimeControls(isThinking) {
|
|
1356
|
+
let query = null, generation = randomInt(0, 2 ** 40), activityRevision = 0;
|
|
1357
|
+
const operations = /* @__PURE__ */ new Map();
|
|
1358
|
+
const confirmations = /* @__PURE__ */ new Map();
|
|
1359
|
+
const tasks = /* @__PURE__ */ new Map();
|
|
1360
|
+
const checkpoints = /* @__PURE__ */ new Map();
|
|
1361
|
+
let pending = false;
|
|
1362
|
+
const setQuery = (next) => {
|
|
1363
|
+
for (const operation of operations.values()) if (operation.status === "running") {
|
|
1364
|
+
operation.status = "failed";
|
|
1365
|
+
operation.error = "Claude query ended before this operation completed";
|
|
1366
|
+
}
|
|
1367
|
+
query = next;
|
|
1368
|
+
generation++;
|
|
1369
|
+
pending = false;
|
|
1370
|
+
confirmations.clear();
|
|
1371
|
+
tasks.clear();
|
|
1372
|
+
checkpoints.clear();
|
|
1373
|
+
};
|
|
1374
|
+
function observe(message) {
|
|
1375
|
+
const m = message;
|
|
1376
|
+
if (m.type === "user" || m.type === "assistant") {
|
|
1377
|
+
activityRevision++;
|
|
1378
|
+
confirmations.clear();
|
|
1379
|
+
}
|
|
1380
|
+
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(" ") : "";
|
|
1381
|
+
if (isRuntimeCheckpointReplay(message) && text(m.uuid) && content) {
|
|
1382
|
+
checkpoints.set(m.uuid, { id: m.uuid, title: content.slice(0, 120) });
|
|
1383
|
+
if (checkpoints.size > 100) checkpoints.delete(checkpoints.keys().next().value);
|
|
1384
|
+
}
|
|
1385
|
+
if (m.type === "system" && text(m.task_id) && ["task_started", "task_updated", "task_progress", "task_notification"].includes(m.subtype)) {
|
|
1386
|
+
const previous = tasks.get(m.task_id);
|
|
1387
|
+
if (m.subtype !== "task_notification" && ["completed", "failed", "stopped", "cancelled"].includes(String(previous?.status))) return;
|
|
1388
|
+
tasks.set(m.task_id, {
|
|
1389
|
+
...previous,
|
|
1390
|
+
id: m.task_id,
|
|
1391
|
+
...text(m.tool_use_id) ? { toolUseId: m.tool_use_id } : {},
|
|
1392
|
+
...text(m.description) ? { description: m.description } : {},
|
|
1393
|
+
...text(m.task_type) ? { type: m.task_type } : {},
|
|
1394
|
+
status: m.subtype === "task_notification" ? m.status ?? "completed" : "running"
|
|
1395
|
+
});
|
|
1396
|
+
if (tasks.size > 100) tasks.delete(tasks.keys().next().value);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
function request(input) {
|
|
1400
|
+
const r = input && typeof input === "object" ? input : {};
|
|
1401
|
+
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) };
|
|
1402
|
+
if (r.action === "operation") {
|
|
1403
|
+
const op2 = operations.get(String(r.operationId));
|
|
1404
|
+
return op2 ? { ...op2 } : { error: "Operation not found" };
|
|
1405
|
+
}
|
|
1406
|
+
const q = query;
|
|
1407
|
+
if (!q) return { error: "No active Claude SDK query" };
|
|
1408
|
+
if (pending || !q.canControl()) return { error: "Claude is waiting for an interaction or another control operation; retry after it finishes" };
|
|
1409
|
+
const action = String(r.action), server = text(r.serverName), task = text(r.taskId), target = text(r.userMessageId);
|
|
1410
|
+
const runGeneration = generation, runRevision = activityRevision;
|
|
1411
|
+
let execute;
|
|
1412
|
+
switch (action) {
|
|
1413
|
+
case "reload-skills":
|
|
1414
|
+
execute = () => q.reloadSkills();
|
|
1415
|
+
break;
|
|
1416
|
+
case "reload-plugins":
|
|
1417
|
+
execute = async () => {
|
|
1418
|
+
const result = await q.reloadPlugins();
|
|
1419
|
+
return { ...result, mcpServers: publicMcpStatus(result.mcpServers ?? []) };
|
|
1420
|
+
};
|
|
1421
|
+
break;
|
|
1422
|
+
case "mcp-status":
|
|
1423
|
+
execute = async () => publicMcpStatus(await q.mcpServerStatus());
|
|
1424
|
+
break;
|
|
1425
|
+
case "mcp-reconnect":
|
|
1426
|
+
if (!server) return { error: "Invalid server name" };
|
|
1427
|
+
execute = () => q.reconnectMcpServer(server);
|
|
1428
|
+
break;
|
|
1429
|
+
case "mcp-toggle":
|
|
1430
|
+
if (!server || typeof r.enabled !== "boolean") return { error: "Invalid server or enabled flag" };
|
|
1431
|
+
execute = () => q.toggleMcpServer(server, r.enabled);
|
|
1432
|
+
break;
|
|
1433
|
+
case "stop-task":
|
|
1434
|
+
if (!task || !tasks.has(task)) return { error: "Unknown task in this query" };
|
|
1435
|
+
execute = () => q.stopTask(task);
|
|
1436
|
+
break;
|
|
1437
|
+
case "background-tasks":
|
|
1438
|
+
if (r.toolUseId !== void 0 && !text(r.toolUseId)) return { error: "Invalid tool use ID" };
|
|
1439
|
+
execute = async () => ({ backgrounded: await q.backgroundTasks(r.toolUseId) });
|
|
1440
|
+
break;
|
|
1441
|
+
case "rewind-preview":
|
|
1442
|
+
if (isThinking() || !target || !checkpoints.has(target)) return { error: "Rewind requires an idle query and a known user checkpoint" };
|
|
1443
|
+
execute = async () => {
|
|
1444
|
+
const result = await q.rewindFiles(target, { dryRun: true });
|
|
1445
|
+
if (!result.canRewind) return result;
|
|
1446
|
+
if (query !== q || generation !== runGeneration || activityRevision !== runRevision || isThinking() || !q.canControl()) throw Error("Query changed; preview again");
|
|
1447
|
+
const token = randomUUID();
|
|
1448
|
+
confirmations.set(token, { generation, revision: activityRevision, userMessageId: target, fingerprint: fingerprint(result), expires: Date.now() + 3e5 });
|
|
1449
|
+
return { ...result, confirmationToken: token };
|
|
1450
|
+
};
|
|
1451
|
+
break;
|
|
1452
|
+
case "rewind-apply": {
|
|
1453
|
+
const confirmation = confirmations.get(String(r.confirmationToken));
|
|
1454
|
+
if (isThinking() || !confirmation || confirmation.generation !== generation || confirmation.expires < Date.now()) return { error: "Preview expired or query is running; preview again" };
|
|
1455
|
+
confirmations.delete(String(r.confirmationToken));
|
|
1456
|
+
execute = async () => {
|
|
1457
|
+
const fresh = await q.rewindFiles(confirmation.userMessageId, { dryRun: true });
|
|
1458
|
+
if (query !== q || generation !== runGeneration || activityRevision !== confirmation.revision || isThinking() || !q.canControl() || fingerprint(fresh) !== confirmation.fingerprint) throw Error("Files or query changed; preview again");
|
|
1459
|
+
return q.rewindFiles(confirmation.userMessageId, { dryRun: false });
|
|
1460
|
+
};
|
|
1461
|
+
break;
|
|
1462
|
+
}
|
|
1463
|
+
default:
|
|
1464
|
+
return { error: "Unknown Claude control action" };
|
|
1465
|
+
}
|
|
1466
|
+
pending = true;
|
|
1467
|
+
const op = { id: randomUUID(), queryGeneration: runGeneration, action, status: "running" };
|
|
1468
|
+
operations.set(op.id, op);
|
|
1469
|
+
if (operations.size > 50) for (const [id, old] of operations) {
|
|
1470
|
+
if (old.status !== "running") {
|
|
1471
|
+
operations.delete(id);
|
|
1472
|
+
break;
|
|
1473
|
+
}
|
|
1474
|
+
}
|
|
1475
|
+
void Promise.resolve().then(() => {
|
|
1476
|
+
if (query !== q || generation !== runGeneration || !q.canControl()) throw Error("Claude query changed or is waiting for an interaction");
|
|
1477
|
+
return execute();
|
|
1478
|
+
}).then((result) => {
|
|
1479
|
+
if (generation !== runGeneration || op.status !== "running") return;
|
|
1480
|
+
op.status = "completed";
|
|
1481
|
+
op.result = result ?? null;
|
|
1482
|
+
}, (error) => {
|
|
1483
|
+
if (generation !== runGeneration || op.status !== "running") return;
|
|
1484
|
+
op.status = "failed";
|
|
1485
|
+
op.error = error instanceof Error ? error.message : "Claude control failed";
|
|
1486
|
+
}).finally(() => {
|
|
1487
|
+
if (generation === runGeneration) pending = false;
|
|
1488
|
+
});
|
|
1489
|
+
return { operationId: op.id };
|
|
1490
|
+
}
|
|
1491
|
+
return { setQuery, observe, request };
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1334
1494
|
class MessageBuffer {
|
|
1335
1495
|
messages = [];
|
|
1336
1496
|
listeners = [];
|
|
@@ -1600,6 +1760,9 @@ function query(params) {
|
|
|
1600
1760
|
tools: opts?.tools,
|
|
1601
1761
|
persistSession: opts?.persistSession,
|
|
1602
1762
|
includePartialMessages: opts?.includePartialMessages,
|
|
1763
|
+
forwardSubagentText: true,
|
|
1764
|
+
enableFileCheckpointing: true,
|
|
1765
|
+
extraArgs: { "replay-user-messages": null },
|
|
1603
1766
|
onElicitation: opts?.onElicitation,
|
|
1604
1767
|
onUserDialog: opts?.onUserDialog,
|
|
1605
1768
|
supportedDialogKinds: opts?.onUserDialog ? opts.supportedDialogKinds : void 0
|
|
@@ -2042,6 +2205,20 @@ async function claudeRemote(opts) {
|
|
|
2042
2205
|
content: initial.message
|
|
2043
2206
|
}
|
|
2044
2207
|
});
|
|
2208
|
+
let callbackDepth = 0;
|
|
2209
|
+
const guardCallback = (callback) => (async (...args) => {
|
|
2210
|
+
callbackDepth++;
|
|
2211
|
+
try {
|
|
2212
|
+
return await callback(...args);
|
|
2213
|
+
} finally {
|
|
2214
|
+
setImmediate(() => {
|
|
2215
|
+
callbackDepth--;
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
if (sdkOptions.canCallTool) sdkOptions.canCallTool = guardCallback(sdkOptions.canCallTool);
|
|
2220
|
+
if (sdkOptions.onElicitation) sdkOptions.onElicitation = guardCallback(sdkOptions.onElicitation);
|
|
2221
|
+
if (sdkOptions.onUserDialog) sdkOptions.onUserDialog = guardCallback(sdkOptions.onUserDialog);
|
|
2045
2222
|
const response = query({
|
|
2046
2223
|
prompt: messages,
|
|
2047
2224
|
options: sdkOptions
|
|
@@ -2062,6 +2239,15 @@ async function claudeRemote(opts) {
|
|
|
2062
2239
|
}
|
|
2063
2240
|
if (opts.onQueryReady) {
|
|
2064
2241
|
opts.onQueryReady({
|
|
2242
|
+
canControl: () => callbackDepth === 0,
|
|
2243
|
+
reloadSkills: () => response.reloadSkills(),
|
|
2244
|
+
reloadPlugins: () => response.reloadPlugins(),
|
|
2245
|
+
mcpServerStatus: () => response.mcpServerStatus(),
|
|
2246
|
+
reconnectMcpServer: (name) => response.reconnectMcpServer(name),
|
|
2247
|
+
toggleMcpServer: (name, enabled) => response.toggleMcpServer(name, enabled),
|
|
2248
|
+
stopTask: (id) => response.stopTask(id),
|
|
2249
|
+
backgroundTasks: (id) => response.backgroundTasks(id),
|
|
2250
|
+
rewindFiles: (id, options) => response.rewindFiles(id, options),
|
|
2065
2251
|
setPermissionMode: (mode2) => response.setPermissionMode(mode2),
|
|
2066
2252
|
setModel: (model) => response.setModel(modelTarget(model)),
|
|
2067
2253
|
interrupt: async () => {
|
|
@@ -2705,7 +2891,7 @@ class PermissionHandler {
|
|
|
2705
2891
|
} else if (pending.kind === "elicitation") {
|
|
2706
2892
|
pending.resolve(message.approved ? { action: "accept", content: elicitationContent(message.updatedInput) } : { action: message.decision === "abort" ? "cancel" : "decline" });
|
|
2707
2893
|
} else {
|
|
2708
|
-
pending.resolve(message.approved ? { behavior: "completed", result:
|
|
2894
|
+
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" });
|
|
2709
2895
|
}
|
|
2710
2896
|
this.recordCompletedRequest(id, effectiveResponse);
|
|
2711
2897
|
this.scheduleDeferredPermissionModeUpdate();
|
|
@@ -3167,6 +3353,7 @@ class SDKToLogConverter {
|
|
|
3167
3353
|
}
|
|
3168
3354
|
}
|
|
3169
3355
|
|
|
3356
|
+
const transcriptSystemSubtypes = /* @__PURE__ */ new Set(["task_started", "task_updated", "task_progress", "task_notification"]);
|
|
3170
3357
|
class OutgoingMessageQueue {
|
|
3171
3358
|
constructor(sendFunction) {
|
|
3172
3359
|
this.sendFunction = sendFunction;
|
|
@@ -3248,7 +3435,7 @@ class OutgoingMessageQueue {
|
|
|
3248
3435
|
break;
|
|
3249
3436
|
}
|
|
3250
3437
|
if (!item.sent) {
|
|
3251
|
-
if (item.logMessage.type !== "system") {
|
|
3438
|
+
if (item.logMessage.type !== "system" || transcriptSystemSubtypes.has(item.logMessage.subtype)) {
|
|
3252
3439
|
this.sendFunction(item.logMessage);
|
|
3253
3440
|
}
|
|
3254
3441
|
item.sent = true;
|
|
@@ -3730,6 +3917,18 @@ function createTurnSteeringController() {
|
|
|
3730
3917
|
throw error;
|
|
3731
3918
|
}
|
|
3732
3919
|
},
|
|
3920
|
+
/**
|
|
3921
|
+
* Interrupt the current turn WITHOUT injecting a follow-up message —
|
|
3922
|
+
* used by the stop button. Like steer it keeps the streaming query
|
|
3923
|
+
* alive (same process, same claudeSessionId); unlike steer it does not
|
|
3924
|
+
* set the steering flag, so the turn simply ends and the query waits
|
|
3925
|
+
* for the next user message. Returns false when no query is attached.
|
|
3926
|
+
*/
|
|
3927
|
+
async interruptTurn() {
|
|
3928
|
+
if (!interrupt) return false;
|
|
3929
|
+
await interrupt();
|
|
3930
|
+
return true;
|
|
3931
|
+
},
|
|
3733
3932
|
consumeReady() {
|
|
3734
3933
|
if (!steering) return false;
|
|
3735
3934
|
steering = false;
|
|
@@ -3838,6 +4037,7 @@ class LaunchModeGate {
|
|
|
3838
4037
|
}
|
|
3839
4038
|
}
|
|
3840
4039
|
|
|
4040
|
+
const ABORT_INTERRUPT_GRACE_MS = 4e3;
|
|
3841
4041
|
async function claudeRemoteLauncher(session, onPermissionModeChange, onEffectivePermissionMode, onPromptFinalized) {
|
|
3842
4042
|
logger.debug("[claudeRemoteLauncher] Starting remote launcher");
|
|
3843
4043
|
const hasTTY = process.stdout.isTTY && process.stdin.isTTY;
|
|
@@ -3876,6 +4076,8 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
3876
4076
|
let abortController = null;
|
|
3877
4077
|
let abortFuture = null;
|
|
3878
4078
|
const turnSteering = createTurnSteeringController();
|
|
4079
|
+
const runtimeControls = createRuntimeControls(() => session.thinking);
|
|
4080
|
+
session.client.rpcHandlerManager.registerHandler("claude-runtime-control", async (input) => runtimeControls.request(input));
|
|
3879
4081
|
async function abort() {
|
|
3880
4082
|
if (abortController && !abortController.signal.aborted) {
|
|
3881
4083
|
abortController.abort();
|
|
@@ -3884,9 +4086,29 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
3884
4086
|
}
|
|
3885
4087
|
async function doAbort() {
|
|
3886
4088
|
logger.debug("[remote]: doAbort");
|
|
4089
|
+
session.markAborting();
|
|
3887
4090
|
session.onAbort();
|
|
4091
|
+
if (session.thinking) {
|
|
4092
|
+
const interrupted = await turnSteering.interruptTurn().catch((e) => {
|
|
4093
|
+
logger.debug("[remote]: interruptTurn threw, falling back to hard abort", e);
|
|
4094
|
+
return false;
|
|
4095
|
+
});
|
|
4096
|
+
if (interrupted && await waitForThinkingToStop(ABORT_INTERRUPT_GRACE_MS)) {
|
|
4097
|
+
logger.debug("[remote]: interrupt stopped the turn gracefully; query kept alive");
|
|
4098
|
+
return;
|
|
4099
|
+
}
|
|
4100
|
+
logger.debug("[remote]: interrupt did not stop the turn in time; escalating to hard abort");
|
|
4101
|
+
}
|
|
3888
4102
|
await abort();
|
|
3889
4103
|
}
|
|
4104
|
+
async function waitForThinkingToStop(graceMs) {
|
|
4105
|
+
const deadline = Date.now() + graceMs;
|
|
4106
|
+
while (Date.now() < deadline) {
|
|
4107
|
+
if (!session.thinking) return true;
|
|
4108
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
4109
|
+
}
|
|
4110
|
+
return !session.thinking;
|
|
4111
|
+
}
|
|
3890
4112
|
async function doSwitch() {
|
|
3891
4113
|
logger.debug("[remote]: doSwitch");
|
|
3892
4114
|
if (!exitReason) {
|
|
@@ -3936,6 +4158,8 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
3936
4158
|
let ongoingToolCalls = /* @__PURE__ */ new Map();
|
|
3937
4159
|
let notifiedQuestionToolCalls = /* @__PURE__ */ new Set();
|
|
3938
4160
|
function onMessage(message) {
|
|
4161
|
+
runtimeControls.observe(message);
|
|
4162
|
+
if (isRuntimeCheckpointReplay(message)) return;
|
|
3939
4163
|
if (message.type === "user" && isClaudeInterruptSentinelContent(message.message.content)) {
|
|
3940
4164
|
sdkToLogConverter.convert(message);
|
|
3941
4165
|
return;
|
|
@@ -4158,6 +4382,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4158
4382
|
if (metadata.permissionMode) onEffectivePermissionMode?.(metadata.permissionMode);
|
|
4159
4383
|
},
|
|
4160
4384
|
onQueryReady: (q) => {
|
|
4385
|
+
runtimeControls.setQuery(q);
|
|
4161
4386
|
turnSteering.setInterrupt(q.interrupt);
|
|
4162
4387
|
session.setSteerHandler(async (input) => {
|
|
4163
4388
|
if (!modeGate.matches(input.mode)) return false;
|
|
@@ -4247,6 +4472,7 @@ async function claudeRemoteLauncher(session, onPermissionModeChange, onEffective
|
|
|
4247
4472
|
continue;
|
|
4248
4473
|
}
|
|
4249
4474
|
} finally {
|
|
4475
|
+
runtimeControls.setQuery(null);
|
|
4250
4476
|
turnSteering.reset();
|
|
4251
4477
|
session.setSteerHandler(null);
|
|
4252
4478
|
livePermissionModeHandler = null;
|
|
@@ -5297,6 +5523,80 @@ ${typeLabels[type] || type}:`));
|
|
|
5297
5523
|
console.log(chalk.green("\n\u2705 Doctor diagnosis complete!\n"));
|
|
5298
5524
|
}
|
|
5299
5525
|
|
|
5526
|
+
const AGENT_CHECK_INTERVAL = 6 * 60 * 60 * 1e3;
|
|
5527
|
+
function stableVersion(value) {
|
|
5528
|
+
const match = value.trim().match(/(?:^|\s|v)(\d+\.\d+\.\d+)([-+][\w.-]+)?(?=\s|$)/);
|
|
5529
|
+
return match && !match[2] ? match[1] : null;
|
|
5530
|
+
}
|
|
5531
|
+
function newerVersion(current, latest) {
|
|
5532
|
+
const a = current.split(".").map(Number), b = latest.split(".").map(Number);
|
|
5533
|
+
for (let i = 0; i < 3; i++) {
|
|
5534
|
+
if (b[i] !== a[i]) return b[i] > a[i];
|
|
5535
|
+
}
|
|
5536
|
+
return false;
|
|
5537
|
+
}
|
|
5538
|
+
const runVersionCommand = (command, args, shell) => new Promise((resolve, reject) => {
|
|
5539
|
+
execFile(
|
|
5540
|
+
command,
|
|
5541
|
+
args,
|
|
5542
|
+
{ timeout: 5e3, maxBuffer: 32 * 1024, windowsHide: true, shell },
|
|
5543
|
+
(error, stdout) => error ? reject(error) : resolve(stdout)
|
|
5544
|
+
);
|
|
5545
|
+
});
|
|
5546
|
+
async function probeAgentVersion(command, platform = process.platform, run = runVersionCommand) {
|
|
5547
|
+
if (platform === "win32") {
|
|
5548
|
+
try {
|
|
5549
|
+
await run("where.exe", [command], false);
|
|
5550
|
+
} catch (error) {
|
|
5551
|
+
return { version: null, missing: error.code === 1 };
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
try {
|
|
5555
|
+
return { version: stableVersion(await run(command, ["--version"], platform === "win32")), missing: false };
|
|
5556
|
+
} catch (error) {
|
|
5557
|
+
return { version: null, missing: error.code === "ENOENT" };
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
async function registryVersion(name) {
|
|
5561
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(name)}/latest`, { signal: AbortSignal.timeout(5e3) });
|
|
5562
|
+
if (!response.ok) throw new Error("Registry unavailable");
|
|
5563
|
+
const data = await response.json();
|
|
5564
|
+
return typeof data.version === "string" ? stableVersion(data.version) : null;
|
|
5565
|
+
}
|
|
5566
|
+
async function detectAgentVersions(deps = { probe: probeAgentVersion, registryVersion }, now = Date.now()) {
|
|
5567
|
+
const agents = await Promise.all(AGENT_VERSION_SOURCES.map(async (source) => {
|
|
5568
|
+
const local = source.command ? await deps.probe(source.command).catch(() => ({ version: null, missing: false })) : { version: stableVersion(packageJson.dependencies["@anthropic-ai/claude-agent-sdk"]), missing: false };
|
|
5569
|
+
if (local.missing) return { id: source.id, installed: null, latest: null, status: "not-installed" };
|
|
5570
|
+
const latest = await deps.registryVersion(source.package).catch(() => null);
|
|
5571
|
+
const installed = local.version;
|
|
5572
|
+
const status = !installed || !latest ? "unknown" : newerVersion(installed, latest) ? "update-available" : "current";
|
|
5573
|
+
return { id: source.id, installed, latest, status };
|
|
5574
|
+
}));
|
|
5575
|
+
return { checkedAt: now, daemonPid: process.pid, agents };
|
|
5576
|
+
}
|
|
5577
|
+
function startAgentVersionChecks(publish, onError, detect = detectAgentVersions) {
|
|
5578
|
+
let stopped = false, pending = false;
|
|
5579
|
+
async function refresh() {
|
|
5580
|
+
if (stopped || pending) return;
|
|
5581
|
+
pending = true;
|
|
5582
|
+
try {
|
|
5583
|
+
const snapshot = await detect();
|
|
5584
|
+
if (!stopped) await publish(snapshot);
|
|
5585
|
+
} catch (error) {
|
|
5586
|
+
onError(error);
|
|
5587
|
+
} finally {
|
|
5588
|
+
pending = false;
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5591
|
+
void refresh();
|
|
5592
|
+
const timer = setInterval(() => void refresh(), AGENT_CHECK_INTERVAL);
|
|
5593
|
+
timer.unref();
|
|
5594
|
+
return () => {
|
|
5595
|
+
stopped = true;
|
|
5596
|
+
clearInterval(timer);
|
|
5597
|
+
};
|
|
5598
|
+
}
|
|
5599
|
+
|
|
5300
5600
|
function sanitizeSpawnModel(value) {
|
|
5301
5601
|
if (typeof value !== "string") return null;
|
|
5302
5602
|
return /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}(\[1m\])?$/i.test(value) ? value : null;
|
|
@@ -7224,10 +7524,13 @@ function buildResumeLaunch(session, options = {}) {
|
|
|
7224
7524
|
const { metadata } = session;
|
|
7225
7525
|
const flavor = resolveFlavor(metadata);
|
|
7226
7526
|
if (flavor === "codex") {
|
|
7227
|
-
|
|
7228
|
-
|
|
7527
|
+
const args = ["codex"];
|
|
7528
|
+
if (options.freshConversation) ; else {
|
|
7529
|
+
if (!metadata.codexThreadId) {
|
|
7530
|
+
throw new Error(`Happy session ${session.id} is missing its Codex thread ID.`);
|
|
7531
|
+
}
|
|
7532
|
+
args.push("--resume", metadata.codexThreadId);
|
|
7229
7533
|
}
|
|
7230
|
-
const args = ["codex", "--resume", metadata.codexThreadId];
|
|
7231
7534
|
if (options.startedBy) {
|
|
7232
7535
|
args.push("--started-by", options.startedBy);
|
|
7233
7536
|
}
|
|
@@ -7237,9 +7540,6 @@ function buildResumeLaunch(session, options = {}) {
|
|
|
7237
7540
|
};
|
|
7238
7541
|
}
|
|
7239
7542
|
if (flavor === "claude") {
|
|
7240
|
-
if (!metadata.claudeSessionId) {
|
|
7241
|
-
throw new Error(`Happy session ${session.id} is missing its Claude session ID.`);
|
|
7242
|
-
}
|
|
7243
7543
|
const args = ["claude"];
|
|
7244
7544
|
if (options.claudeStartingMode) {
|
|
7245
7545
|
args.push("--happy-starting-mode", options.claudeStartingMode);
|
|
@@ -7247,7 +7547,12 @@ function buildResumeLaunch(session, options = {}) {
|
|
|
7247
7547
|
if (options.startedBy) {
|
|
7248
7548
|
args.push("--started-by", options.startedBy);
|
|
7249
7549
|
}
|
|
7250
|
-
|
|
7550
|
+
if (options.freshConversation) ; else {
|
|
7551
|
+
if (!metadata.claudeSessionId) {
|
|
7552
|
+
throw new Error(`Happy session ${session.id} is missing its Claude session ID.`);
|
|
7553
|
+
}
|
|
7554
|
+
args.push("--resume", metadata.claudeSessionId);
|
|
7555
|
+
}
|
|
7251
7556
|
return {
|
|
7252
7557
|
cwd: metadata.path,
|
|
7253
7558
|
args
|
|
@@ -10546,12 +10851,18 @@ async function startDaemon() {
|
|
|
10546
10851
|
cwdExists: (p) => existsSync$1(p),
|
|
10547
10852
|
conversationExists: (cwd, claudeSessionId) => existsSync$1(join$1(getProjectPath(cwd), `${claudeSessionId}.jsonl`))
|
|
10548
10853
|
});
|
|
10854
|
+
let freshConversation = false;
|
|
10549
10855
|
if (!precheck.ok) {
|
|
10550
|
-
|
|
10856
|
+
if (precheck.reason === "conversation-missing") {
|
|
10857
|
+
logger.debug(`[DAEMON RUN] resume: transcript for ${happySessionId} is gone (${precheck.detail}); starting a fresh conversation in ${metadata.path}`);
|
|
10858
|
+
freshConversation = true;
|
|
10859
|
+
} else {
|
|
10860
|
+
return { type: "error", errorMessage: `resume-precheck:${precheck.reason}: ${precheck.detail}` };
|
|
10861
|
+
}
|
|
10551
10862
|
}
|
|
10552
10863
|
const launch = buildResumeLaunch(
|
|
10553
10864
|
{ id: happySessionId, active: true, metadata },
|
|
10554
|
-
{ startedBy: "daemon", claudeStartingMode: "remote" }
|
|
10865
|
+
{ startedBy: "daemon", claudeStartingMode: "remote", freshConversation }
|
|
10555
10866
|
);
|
|
10556
10867
|
const model = sanitizeResumeModel(options?.model);
|
|
10557
10868
|
if (model) launch.args.push("--model", model);
|
|
@@ -10969,6 +11280,10 @@ async function startDaemon() {
|
|
|
10969
11280
|
logger.warn(`[DAEMON RUN] Ignoring invalid HAPPY_CLI_UPDATE_CHECK_INTERVAL; using ${DEFAULT_CLI_UPDATE_CHECK_INTERVAL_MS}ms`);
|
|
10970
11281
|
}
|
|
10971
11282
|
const cliUpdateInterval = setInterval(() => void refreshCliUpdate(), cliUpdateIntervalMs);
|
|
11283
|
+
const stopAgentVersionChecks = startAgentVersionChecks(
|
|
11284
|
+
(snapshot) => apiMachine.updateDaemonState((state) => ({ ...state, status: state?.status ?? "running", agentVersions: snapshot })),
|
|
11285
|
+
(error) => logger.debug("[DAEMON RUN] Agent version check failed", error)
|
|
11286
|
+
);
|
|
10972
11287
|
const heartbeatIntervalMs = parseInt(process.env.HAPPY_DAEMON_HEARTBEAT_INTERVAL || "60000");
|
|
10973
11288
|
const restartOnStaleVersionAndHeartbeat = setInterval(serialTask(async () => {
|
|
10974
11289
|
try {
|
|
@@ -11009,6 +11324,7 @@ async function startDaemon() {
|
|
|
11009
11324
|
teamWorker?.stop();
|
|
11010
11325
|
clearInterval(restartOnStaleVersionAndHeartbeat);
|
|
11011
11326
|
clearInterval(cliUpdateInterval);
|
|
11327
|
+
stopAgentVersionChecks();
|
|
11012
11328
|
claudeAuthService.stop();
|
|
11013
11329
|
apiMachine.shutdown();
|
|
11014
11330
|
await stopControlServer();
|
|
@@ -11070,6 +11386,7 @@ async function startDaemon() {
|
|
|
11070
11386
|
logger.debug("[DAEMON RUN] Health check interval cleared");
|
|
11071
11387
|
}
|
|
11072
11388
|
clearInterval(cliUpdateInterval);
|
|
11389
|
+
stopAgentVersionChecks();
|
|
11073
11390
|
await apiMachine.updateDaemonState((state) => ({
|
|
11074
11391
|
...state,
|
|
11075
11392
|
status: "shutting-down",
|
|
@@ -12159,7 +12476,7 @@ function registerSideQuestionHandler(rpc, deps) {
|
|
|
12159
12476
|
const retainMs = deps.retainMs ?? SIDE_QUESTION_RETAIN_MS;
|
|
12160
12477
|
const maxRunMs = deps.maxRunMs ?? SIDE_QUESTION_MAX_RUN_MS;
|
|
12161
12478
|
const run = deps.run ?? (async (input) => {
|
|
12162
|
-
const { query } = await import('./index-
|
|
12479
|
+
const { query } = await import('./index-vN4Xf0zB.mjs');
|
|
12163
12480
|
return runSideQuestion(query, input);
|
|
12164
12481
|
});
|
|
12165
12482
|
const slots = /* @__PURE__ */ new Map();
|
|
@@ -12722,7 +13039,7 @@ async function runClaude(credentials, options = {}) {
|
|
|
12722
13039
|
// a brand-new session cannot pick a PDF until after sending once.
|
|
12723
13040
|
attachmentKinds: [...CLAUDE_ATTACHMENT_KINDS],
|
|
12724
13041
|
queueCancellation: true,
|
|
12725
|
-
capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2", "claude-btw-v1"],
|
|
13042
|
+
capabilities: ["claude-steer-v1", "claude-live-permission-v1", "claude-live-permission-v2", "claude-btw-v1", "claude-runtime-controls-v1"],
|
|
12726
13043
|
// Effective mode this process enforces. Kept current by
|
|
12727
13044
|
// publishPermissionMode below; the web renders it instead of guessing.
|
|
12728
13045
|
permissionMode: mapToClaudeMode(initialPermissionMode ?? "default"),
|
|
@@ -13301,10 +13618,18 @@ async function runClaude(credentials, options = {}) {
|
|
|
13301
13618
|
});
|
|
13302
13619
|
process.on("uncaughtException", (error) => {
|
|
13303
13620
|
logger.debug("[START] Uncaught exception:", error);
|
|
13621
|
+
if (currentSession?.isAborting()) {
|
|
13622
|
+
logger.debug("[START] Uncaught exception during abort teardown \u2014 swallowing, not archiving");
|
|
13623
|
+
return;
|
|
13624
|
+
}
|
|
13304
13625
|
void cleanup({ archive: true });
|
|
13305
13626
|
});
|
|
13306
13627
|
process.on("unhandledRejection", (reason) => {
|
|
13307
13628
|
logger.debug("[START] Unhandled rejection:", reason);
|
|
13629
|
+
if (currentSession?.isAborting()) {
|
|
13630
|
+
logger.debug("[START] Unhandled rejection during abort teardown \u2014 swallowing, not archiving");
|
|
13631
|
+
return;
|
|
13632
|
+
}
|
|
13308
13633
|
void cleanup({ archive: true });
|
|
13309
13634
|
});
|
|
13310
13635
|
registerKillSessionHandler(session.rpcHandlerManager, () => cleanup({ archive: true }), session);
|
|
@@ -14903,1269 +15228,1325 @@ async function ensureDaemonRunning() {
|
|
|
14903
15228
|
logger.debug(`Happy background service did not become ready within ${DAEMON_READY_TIMEOUT_MS}ms; continuing anyway`);
|
|
14904
15229
|
}
|
|
14905
15230
|
|
|
14906
|
-
function
|
|
14907
|
-
|
|
14908
|
-
const attachment = fileEvent.content.data.ev;
|
|
14909
|
-
const download = (async () => {
|
|
14910
|
-
try {
|
|
14911
|
-
const data = await session.downloadAndDecryptAttachment(attachment.ref);
|
|
14912
|
-
if (!data) throw new Error("Attachment decryption failed");
|
|
14913
|
-
return { data, mimeType: attachment.mimeType ?? "image/jpeg", name: attachment.name };
|
|
14914
|
-
} catch {
|
|
14915
|
-
session.sendSessionEvent({ type: "message", message: `Could not download attachment: ${attachment.name}. Please attach it again.` });
|
|
14916
|
-
return null;
|
|
14917
|
-
}
|
|
14918
|
-
})();
|
|
14919
|
-
session.trackAttachmentDownload(download);
|
|
14920
|
-
});
|
|
15231
|
+
function getStartedSubagents(state) {
|
|
15232
|
+
return state.startedSubagents ?? /* @__PURE__ */ new Set();
|
|
14921
15233
|
}
|
|
14922
|
-
|
|
14923
|
-
|
|
14924
|
-
|
|
14925
|
-
|
|
14926
|
-
|
|
14927
|
-
|
|
14928
|
-
|
|
14929
|
-
|
|
15234
|
+
function getActiveSubagents(state) {
|
|
15235
|
+
return state.activeSubagents ?? /* @__PURE__ */ new Set();
|
|
15236
|
+
}
|
|
15237
|
+
function getProviderSubagentToSessionSubagent(state) {
|
|
15238
|
+
return state.providerSubagentToSessionSubagent ?? /* @__PURE__ */ new Map();
|
|
15239
|
+
}
|
|
15240
|
+
function maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes) {
|
|
15241
|
+
if (!subagent || startedSubagents.has(subagent)) {
|
|
15242
|
+
return;
|
|
14930
15243
|
}
|
|
14931
|
-
|
|
14932
|
-
|
|
14933
|
-
|
|
14934
|
-
|
|
14935
|
-
|
|
14936
|
-
|
|
14937
|
-
|
|
14938
|
-
|
|
15244
|
+
envelopes.push(createEnvelope("agent", { t: "start" }, { ...opts, subagent }));
|
|
15245
|
+
startedSubagents.add(subagent);
|
|
15246
|
+
activeSubagents.add(subagent);
|
|
15247
|
+
}
|
|
15248
|
+
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15249
|
+
const envelopes = [];
|
|
15250
|
+
for (const subagent of activeSubagents) {
|
|
15251
|
+
envelopes.push(createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
14939
15252
|
}
|
|
14940
|
-
|
|
14941
|
-
|
|
14942
|
-
|
|
14943
|
-
|
|
14944
|
-
|
|
14945
|
-
|
|
14946
|
-
|
|
14947
|
-
|
|
14948
|
-
|
|
14949
|
-
|
|
14950
|
-
|
|
14951
|
-
|
|
14952
|
-
|
|
14953
|
-
|
|
14954
|
-
|
|
14955
|
-
|
|
14956
|
-
const request = currentState.requests?.[response.id];
|
|
14957
|
-
if (!request) return currentState;
|
|
14958
|
-
const { [response.id]: _, ...remainingRequests } = currentState.requests || {};
|
|
14959
|
-
let res = {
|
|
14960
|
-
...currentState,
|
|
14961
|
-
requests: remainingRequests,
|
|
14962
|
-
completedRequests: {
|
|
14963
|
-
...currentState.completedRequests,
|
|
14964
|
-
[response.id]: {
|
|
14965
|
-
...request,
|
|
14966
|
-
completedAt: Date.now(),
|
|
14967
|
-
status: response.approved ? "approved" : "denied",
|
|
14968
|
-
decision: result.decision
|
|
14969
|
-
}
|
|
14970
|
-
}
|
|
14971
|
-
};
|
|
14972
|
-
return res;
|
|
14973
|
-
});
|
|
14974
|
-
logger.debug(`${this.getLogPrefix()} Permission ${response.approved ? "approved" : "denied"} for ${pending.toolName}`);
|
|
14975
|
-
}
|
|
14976
|
-
);
|
|
15253
|
+
activeSubagents.clear();
|
|
15254
|
+
startedSubagents.clear();
|
|
15255
|
+
return envelopes;
|
|
15256
|
+
}
|
|
15257
|
+
function buildEnvelopeOptions(currentTurnId, subagent) {
|
|
15258
|
+
return {
|
|
15259
|
+
...currentTurnId ? { turn: currentTurnId } : {},
|
|
15260
|
+
...subagent ? { subagent } : {}
|
|
15261
|
+
};
|
|
15262
|
+
}
|
|
15263
|
+
function pickProviderSubagent(message) {
|
|
15264
|
+
const candidates = [message.subagent, message.parent_call_id, message.parentCallId];
|
|
15265
|
+
for (const candidate of candidates) {
|
|
15266
|
+
if (typeof candidate === "string" && candidate.length > 0) {
|
|
15267
|
+
return candidate;
|
|
15268
|
+
}
|
|
14977
15269
|
}
|
|
14978
|
-
|
|
14979
|
-
|
|
14980
|
-
|
|
14981
|
-
|
|
14982
|
-
|
|
14983
|
-
|
|
14984
|
-
requests: {
|
|
14985
|
-
...currentState.requests,
|
|
14986
|
-
[toolCallId]: {
|
|
14987
|
-
tool: toolName,
|
|
14988
|
-
arguments: input,
|
|
14989
|
-
createdAt: Date.now()
|
|
14990
|
-
}
|
|
14991
|
-
}
|
|
14992
|
-
}));
|
|
15270
|
+
return void 0;
|
|
15271
|
+
}
|
|
15272
|
+
function resolveSessionSubagent(message, providerSubagentToSessionSubagent) {
|
|
15273
|
+
const providerSubagent = pickProviderSubagent(message);
|
|
15274
|
+
if (!providerSubagent) {
|
|
15275
|
+
return void 0;
|
|
14993
15276
|
}
|
|
14994
|
-
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
* causing the approval response to send 'cancel' to the provider. This is used when the
|
|
14998
|
-
* user presses the abort/stop button — it unblocks any pending tool approval so the provider
|
|
14999
|
-
* can process the turn cancellation.
|
|
15000
|
-
*/
|
|
15001
|
-
abortAll() {
|
|
15002
|
-
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15003
|
-
if (pendingSnapshot.length === 0) return;
|
|
15004
|
-
this.pendingRequests.clear();
|
|
15005
|
-
for (const [id, pending] of pendingSnapshot) {
|
|
15006
|
-
try {
|
|
15007
|
-
pending.resolve({ decision: "abort" });
|
|
15008
|
-
} catch (err) {
|
|
15009
|
-
logger.debug(`${this.getLogPrefix()} Error resolving aborted request ${id}:`, err);
|
|
15010
|
-
}
|
|
15011
|
-
}
|
|
15012
|
-
this.session.updateAgentState((currentState) => {
|
|
15013
|
-
const pendingRequests = currentState.requests || {};
|
|
15014
|
-
const completedRequests = { ...currentState.completedRequests };
|
|
15015
|
-
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15016
|
-
completedRequests[id] = {
|
|
15017
|
-
...request,
|
|
15018
|
-
completedAt: Date.now(),
|
|
15019
|
-
status: "canceled",
|
|
15020
|
-
reason: "Aborted by user"
|
|
15021
|
-
};
|
|
15022
|
-
}
|
|
15023
|
-
return {
|
|
15024
|
-
...currentState,
|
|
15025
|
-
requests: {},
|
|
15026
|
-
completedRequests
|
|
15027
|
-
};
|
|
15028
|
-
});
|
|
15029
|
-
logger.debug(`${this.getLogPrefix()} Aborted ${pendingSnapshot.length} pending permission(s)`);
|
|
15277
|
+
const existing = providerSubagentToSessionSubagent.get(providerSubagent);
|
|
15278
|
+
if (existing) {
|
|
15279
|
+
return existing;
|
|
15030
15280
|
}
|
|
15031
|
-
|
|
15032
|
-
|
|
15033
|
-
|
|
15034
|
-
|
|
15035
|
-
|
|
15036
|
-
|
|
15037
|
-
|
|
15038
|
-
|
|
15039
|
-
}
|
|
15040
|
-
this.isResetting = true;
|
|
15041
|
-
try {
|
|
15042
|
-
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15043
|
-
this.pendingRequests.clear();
|
|
15044
|
-
for (const [id, pending] of pendingSnapshot) {
|
|
15045
|
-
try {
|
|
15046
|
-
pending.reject(new Error("Session reset"));
|
|
15047
|
-
} catch (err) {
|
|
15048
|
-
logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
15049
|
-
}
|
|
15050
|
-
}
|
|
15051
|
-
this.session.updateAgentState((currentState) => {
|
|
15052
|
-
const pendingRequests = currentState.requests || {};
|
|
15053
|
-
const completedRequests = { ...currentState.completedRequests };
|
|
15054
|
-
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15055
|
-
completedRequests[id] = {
|
|
15056
|
-
...request,
|
|
15057
|
-
completedAt: Date.now(),
|
|
15058
|
-
status: "canceled",
|
|
15059
|
-
reason
|
|
15060
|
-
};
|
|
15061
|
-
}
|
|
15062
|
-
return {
|
|
15063
|
-
...currentState,
|
|
15064
|
-
requests: {},
|
|
15065
|
-
completedRequests
|
|
15066
|
-
};
|
|
15067
|
-
});
|
|
15068
|
-
logger.debug(`${this.getLogPrefix()} Permission handler reset`);
|
|
15069
|
-
} finally {
|
|
15070
|
-
this.isResetting = false;
|
|
15071
|
-
}
|
|
15281
|
+
const created = createId();
|
|
15282
|
+
providerSubagentToSessionSubagent.set(providerSubagent, created);
|
|
15283
|
+
return created;
|
|
15284
|
+
}
|
|
15285
|
+
function pickCallId(message) {
|
|
15286
|
+
const callId = message.call_id ?? message.callId;
|
|
15287
|
+
if (typeof callId === "string" && callId.length > 0) {
|
|
15288
|
+
return callId;
|
|
15072
15289
|
}
|
|
15290
|
+
return randomUUID();
|
|
15073
15291
|
}
|
|
15074
|
-
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
// form (used by Codex elicitation messages like `tool "change_title"`)
|
|
15078
|
-
// and the MCP-qualified form for defense in depth.
|
|
15079
|
-
static ALWAYS_AUTO_APPROVE_NAMES = /* @__PURE__ */ new Set([
|
|
15080
|
-
"change_title",
|
|
15081
|
-
"mcp__happy__change_title"
|
|
15082
|
-
]);
|
|
15083
|
-
// Tool-call IDs that should auto-approve when they exactly match one of
|
|
15084
|
-
// these values or start with `<name>-` (e.g. `change_title-1765385846663`).
|
|
15085
|
-
// Substring matching was a bypass vector — any tool whose ID happened to
|
|
15086
|
-
// contain `change_title` as a substring would be silently approved.
|
|
15087
|
-
static ALWAYS_AUTO_APPROVE_ID_PREFIXES = [
|
|
15088
|
-
"change_title"
|
|
15089
|
-
];
|
|
15090
|
-
constructor(session) {
|
|
15091
|
-
super(session);
|
|
15292
|
+
function summarizeCommand(command) {
|
|
15293
|
+
if (typeof command === "string" && command.trim().length > 0) {
|
|
15294
|
+
return command;
|
|
15092
15295
|
}
|
|
15093
|
-
|
|
15094
|
-
|
|
15296
|
+
if (Array.isArray(command)) {
|
|
15297
|
+
const cmd = command.map((v) => String(v)).join(" ").trim();
|
|
15298
|
+
return cmd.length > 0 ? cmd : null;
|
|
15095
15299
|
}
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
if (toolCallId === prefix || toolCallId.startsWith(`${prefix}-`)) {
|
|
15102
|
-
return true;
|
|
15103
|
-
}
|
|
15104
|
-
}
|
|
15105
|
-
return false;
|
|
15300
|
+
return null;
|
|
15301
|
+
}
|
|
15302
|
+
function commandToTitle(command) {
|
|
15303
|
+
if (!command) {
|
|
15304
|
+
return "Run command";
|
|
15106
15305
|
}
|
|
15107
|
-
|
|
15108
|
-
|
|
15109
|
-
|
|
15110
|
-
|
|
15111
|
-
|
|
15112
|
-
|
|
15113
|
-
|
|
15114
|
-
|
|
15115
|
-
|
|
15116
|
-
|
|
15117
|
-
|
|
15118
|
-
|
|
15119
|
-
|
|
15120
|
-
|
|
15121
|
-
[toolCallId]: {
|
|
15122
|
-
tool: toolName,
|
|
15123
|
-
arguments: input,
|
|
15124
|
-
createdAt: Date.now(),
|
|
15125
|
-
completedAt: Date.now(),
|
|
15126
|
-
status: "approved",
|
|
15127
|
-
decision: "approved"
|
|
15128
|
-
}
|
|
15129
|
-
}
|
|
15130
|
-
}));
|
|
15131
|
-
return { decision: "approved" };
|
|
15132
|
-
}
|
|
15133
|
-
return new Promise((resolve, reject) => {
|
|
15134
|
-
this.pendingRequests.set(toolCallId, {
|
|
15135
|
-
resolve,
|
|
15136
|
-
reject,
|
|
15137
|
-
toolName,
|
|
15138
|
-
input
|
|
15139
|
-
});
|
|
15140
|
-
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
15141
|
-
logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})`);
|
|
15142
|
-
});
|
|
15306
|
+
const short = command.length > 80 ? `${command.slice(0, 77)}...` : command;
|
|
15307
|
+
return `Run \`${short}\``;
|
|
15308
|
+
}
|
|
15309
|
+
function turnTimestampMs(turn) {
|
|
15310
|
+
const seconds = turn.startedAt ?? turn.completedAt;
|
|
15311
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
|
|
15312
|
+
}
|
|
15313
|
+
function completedTimestampMs(turn) {
|
|
15314
|
+
const seconds = turn.completedAt ?? turn.startedAt;
|
|
15315
|
+
return typeof seconds === "number" && Number.isFinite(seconds) ? seconds * 1e3 : Date.now();
|
|
15316
|
+
}
|
|
15317
|
+
function textFromInputItems(items) {
|
|
15318
|
+
if (!Array.isArray(items)) {
|
|
15319
|
+
return null;
|
|
15143
15320
|
}
|
|
15321
|
+
const text = items.filter((item) => Boolean(item) && typeof item === "object" && item.type === "text" && typeof item.text === "string").map((item) => item.text).join("\n").trim();
|
|
15322
|
+
return text.length > 0 ? text : null;
|
|
15144
15323
|
}
|
|
15145
|
-
|
|
15146
|
-
|
|
15147
|
-
|
|
15148
|
-
|
|
15149
|
-
|
|
15150
|
-
|
|
15151
|
-
|
|
15152
|
-
|
|
15153
|
-
|
|
15154
|
-
|
|
15155
|
-
|
|
15156
|
-
|
|
15157
|
-
|
|
15158
|
-
|
|
15324
|
+
function reasoningText(item) {
|
|
15325
|
+
const summary = item.summary;
|
|
15326
|
+
const content = item.content;
|
|
15327
|
+
const parts = [
|
|
15328
|
+
...Array.isArray(summary) ? summary : [],
|
|
15329
|
+
...Array.isArray(content) ? content : []
|
|
15330
|
+
].filter((value) => typeof value === "string" && value.trim().length > 0);
|
|
15331
|
+
const text = parts.join("\n").trim();
|
|
15332
|
+
return text.length > 0 ? text : null;
|
|
15333
|
+
}
|
|
15334
|
+
function turnStatus(turn) {
|
|
15335
|
+
const status = typeof turn.status === "string" ? turn.status : null;
|
|
15336
|
+
if (status === "failed") {
|
|
15337
|
+
return "failed";
|
|
15159
15338
|
}
|
|
15160
|
-
|
|
15161
|
-
|
|
15162
|
-
*/
|
|
15163
|
-
setMessageCallback(callback) {
|
|
15164
|
-
this.onMessage = callback;
|
|
15339
|
+
if (status === "cancelled" || status === "canceled" || status === "aborted" || status === "interrupted") {
|
|
15340
|
+
return "cancelled";
|
|
15165
15341
|
}
|
|
15166
|
-
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15170
|
-
|
|
15171
|
-
|
|
15172
|
-
|
|
15342
|
+
return "completed";
|
|
15343
|
+
}
|
|
15344
|
+
function emitHistoricalToolCall(envelopes, turn, item, name, title, args, output) {
|
|
15345
|
+
const time = turnTimestampMs(turn);
|
|
15346
|
+
const opts = { turn: turn.id, time, codexItemId: item.id };
|
|
15347
|
+
envelopes.push(createEnvelope("agent", {
|
|
15348
|
+
t: "tool-call-start",
|
|
15349
|
+
call: item.id,
|
|
15350
|
+
name,
|
|
15351
|
+
title,
|
|
15352
|
+
description: title,
|
|
15353
|
+
args
|
|
15354
|
+
}, {
|
|
15355
|
+
...opts,
|
|
15356
|
+
id: `${item.id}:start`
|
|
15357
|
+
}));
|
|
15358
|
+
if (output && output.trim().length > 0) {
|
|
15359
|
+
envelopes.push(createEnvelope("agent", {
|
|
15360
|
+
t: "text",
|
|
15361
|
+
text: output,
|
|
15362
|
+
thinking: true
|
|
15363
|
+
}, {
|
|
15364
|
+
...opts,
|
|
15365
|
+
id: `${item.id}:output`
|
|
15366
|
+
}));
|
|
15173
15367
|
}
|
|
15174
|
-
|
|
15175
|
-
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15180
|
-
|
|
15181
|
-
|
|
15182
|
-
|
|
15183
|
-
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
15208
|
-
|
|
15209
|
-
|
|
15210
|
-
}
|
|
15368
|
+
if ("status" in item && (item.status === "inProgress" || item.status === "running")) return;
|
|
15369
|
+
envelopes.push(createEnvelope("agent", {
|
|
15370
|
+
t: "tool-call-end",
|
|
15371
|
+
call: item.id
|
|
15372
|
+
}, {
|
|
15373
|
+
...opts,
|
|
15374
|
+
id: `${item.id}:end`,
|
|
15375
|
+
time: completedTimestampMs(turn)
|
|
15376
|
+
}));
|
|
15377
|
+
}
|
|
15378
|
+
function mapCodexCollaborationItem(item, opts = {}, source = "history") {
|
|
15379
|
+
if (item.type === "subAgentActivity" && typeof item.agentThreadId === "string") {
|
|
15380
|
+
item = { ...item, type: "collabAgentToolCall", tool: item.kind, status: "completed", receiverThreadIds: [item.agentThreadId] };
|
|
15381
|
+
}
|
|
15382
|
+
if (item.type !== "collabAgentToolCall" || typeof item.id !== "string" || typeof item.tool !== "string") return [];
|
|
15383
|
+
const args = {
|
|
15384
|
+
operation: item.tool,
|
|
15385
|
+
...typeof item.prompt === "string" ? { prompt: item.prompt } : {},
|
|
15386
|
+
...typeof item.model === "string" ? { model: item.model } : {},
|
|
15387
|
+
receiverThreadIds: Array.isArray(item.receiverThreadIds) ? item.receiverThreadIds : [],
|
|
15388
|
+
agentsStates: item.agentsStates ?? {}
|
|
15389
|
+
};
|
|
15390
|
+
const startId = source === "live" ? `${item.id}:${item.status ?? "unknown"}:start` : `${item.id}:start`;
|
|
15391
|
+
const envelopes = [createEnvelope("agent", {
|
|
15392
|
+
t: "tool-call-start",
|
|
15393
|
+
call: item.id,
|
|
15394
|
+
name: "CodexCollaboration",
|
|
15395
|
+
title: `Codex \xB7 ${item.tool}`,
|
|
15396
|
+
description: `Codex \xB7 ${item.tool}`,
|
|
15397
|
+
args
|
|
15398
|
+
}, { ...opts, id: startId })];
|
|
15399
|
+
if (item.status === "completed" || item.status === "failed" || item.status === "interrupted") {
|
|
15400
|
+
envelopes.push(createEnvelope("agent", {
|
|
15401
|
+
t: "tool-call-end",
|
|
15402
|
+
call: item.id,
|
|
15403
|
+
result: { text: JSON.stringify(args, null, 2), isError: item.status !== "completed" }
|
|
15404
|
+
}, { ...opts, id: `${item.id}:end` }));
|
|
15211
15405
|
}
|
|
15212
|
-
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
15217
|
-
|
|
15218
|
-
|
|
15219
|
-
|
|
15220
|
-
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
|
|
15235
|
-
|
|
15236
|
-
|
|
15237
|
-
|
|
15238
|
-
|
|
15239
|
-
|
|
15240
|
-
|
|
15241
|
-
|
|
15242
|
-
|
|
15243
|
-
|
|
15244
|
-
|
|
15245
|
-
|
|
15246
|
-
|
|
15247
|
-
|
|
15248
|
-
|
|
15406
|
+
return envelopes;
|
|
15407
|
+
}
|
|
15408
|
+
function mapCodexThreadToSessionEnvelopes(thread) {
|
|
15409
|
+
const envelopes = [];
|
|
15410
|
+
for (const turn of thread.turns ?? []) {
|
|
15411
|
+
const startedAt = turnTimestampMs(turn);
|
|
15412
|
+
const completedAt = completedTimestampMs(turn);
|
|
15413
|
+
envelopes.push(createEnvelope("agent", { t: "turn-start" }, {
|
|
15414
|
+
id: `${turn.id}:start`,
|
|
15415
|
+
turn: turn.id,
|
|
15416
|
+
time: startedAt
|
|
15417
|
+
}));
|
|
15418
|
+
for (const item of turn.items ?? []) {
|
|
15419
|
+
switch (item.type) {
|
|
15420
|
+
case "subAgentActivity":
|
|
15421
|
+
case "collabAgentToolCall": {
|
|
15422
|
+
envelopes.push(...mapCodexCollaborationItem(item, { turn: turn.id, time: startedAt, codexItemId: item.id }));
|
|
15423
|
+
break;
|
|
15424
|
+
}
|
|
15425
|
+
case "userMessage": {
|
|
15426
|
+
const text = textFromInputItems(item.content);
|
|
15427
|
+
if (text) {
|
|
15428
|
+
envelopes.push(createEnvelope("user", { t: "text", text }, {
|
|
15429
|
+
id: item.id,
|
|
15430
|
+
time: startedAt,
|
|
15431
|
+
codexItemId: item.id
|
|
15432
|
+
}));
|
|
15433
|
+
}
|
|
15434
|
+
break;
|
|
15435
|
+
}
|
|
15436
|
+
case "agentMessage": {
|
|
15437
|
+
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15438
|
+
if (text.length > 0) {
|
|
15439
|
+
envelopes.push(createEnvelope("agent", { t: "text", text }, {
|
|
15440
|
+
id: item.id,
|
|
15441
|
+
turn: turn.id,
|
|
15442
|
+
time: completedAt,
|
|
15443
|
+
codexItemId: item.id
|
|
15444
|
+
}));
|
|
15445
|
+
}
|
|
15446
|
+
break;
|
|
15447
|
+
}
|
|
15448
|
+
case "reasoning": {
|
|
15449
|
+
const text = reasoningText(item);
|
|
15450
|
+
if (text) {
|
|
15451
|
+
envelopes.push(createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15452
|
+
id: item.id,
|
|
15453
|
+
turn: turn.id,
|
|
15454
|
+
time: startedAt,
|
|
15455
|
+
codexItemId: item.id
|
|
15456
|
+
}));
|
|
15457
|
+
}
|
|
15458
|
+
break;
|
|
15459
|
+
}
|
|
15460
|
+
case "commandExecution": {
|
|
15461
|
+
const command = typeof item.command === "string" ? item.command : "";
|
|
15462
|
+
emitHistoricalToolCall(
|
|
15463
|
+
envelopes,
|
|
15464
|
+
turn,
|
|
15465
|
+
item,
|
|
15466
|
+
"CodexBash",
|
|
15467
|
+
commandToTitle(command),
|
|
15468
|
+
{ command, cwd: item.cwd },
|
|
15469
|
+
typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : null
|
|
15470
|
+
);
|
|
15471
|
+
break;
|
|
15472
|
+
}
|
|
15473
|
+
case "fileChange": {
|
|
15474
|
+
const title = "Apply patch";
|
|
15475
|
+
emitHistoricalToolCall(
|
|
15476
|
+
envelopes,
|
|
15477
|
+
turn,
|
|
15478
|
+
item,
|
|
15479
|
+
"CodexPatch",
|
|
15480
|
+
title,
|
|
15481
|
+
{ changes: item.changes, status: item.status },
|
|
15482
|
+
null
|
|
15483
|
+
);
|
|
15484
|
+
break;
|
|
15485
|
+
}
|
|
15486
|
+
case "mcpToolCall": {
|
|
15487
|
+
const title = `${item.server}.${item.tool}`;
|
|
15488
|
+
const output = item.error !== void 0 && item.error !== null ? String(item.error) : item.result !== void 0 && item.result !== null ? String(item.result) : null;
|
|
15489
|
+
emitHistoricalToolCall(
|
|
15490
|
+
envelopes,
|
|
15491
|
+
turn,
|
|
15492
|
+
item,
|
|
15493
|
+
"McpTool",
|
|
15494
|
+
title,
|
|
15495
|
+
{
|
|
15496
|
+
server: item.server,
|
|
15497
|
+
tool: item.tool,
|
|
15498
|
+
arguments: item.arguments
|
|
15499
|
+
},
|
|
15500
|
+
output
|
|
15501
|
+
);
|
|
15502
|
+
break;
|
|
15503
|
+
}
|
|
15249
15504
|
}
|
|
15250
15505
|
}
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
}
|
|
15256
|
-
if (this.toolCallStarted && this.currentCallId) {
|
|
15257
|
-
const toolResult = {
|
|
15258
|
-
type: "tool-call-result",
|
|
15259
|
-
callId: this.currentCallId,
|
|
15260
|
-
output: {
|
|
15261
|
-
content,
|
|
15262
|
-
status: "completed"
|
|
15263
|
-
},
|
|
15264
|
-
id: randomUUID()
|
|
15265
|
-
};
|
|
15266
|
-
logger.debug(`${this.getLogPrefix()} Sending tool call result`);
|
|
15267
|
-
this.onMessage?.(toolResult);
|
|
15268
|
-
} else if (content.trim()) {
|
|
15269
|
-
const reasoningMessage = {
|
|
15270
|
-
type: "reasoning",
|
|
15271
|
-
message: content,
|
|
15272
|
-
id: randomUUID()
|
|
15273
|
-
};
|
|
15274
|
-
logger.debug(`${this.getLogPrefix()} Sending reasoning message`);
|
|
15275
|
-
this.onMessage?.(reasoningMessage);
|
|
15276
|
-
}
|
|
15277
|
-
this.resetState();
|
|
15278
|
-
return true;
|
|
15506
|
+
envelopes.push(createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15507
|
+
id: `${turn.id}:end`,
|
|
15508
|
+
turn: turn.id,
|
|
15509
|
+
time: completedAt
|
|
15510
|
+
}));
|
|
15279
15511
|
}
|
|
15280
|
-
|
|
15281
|
-
|
|
15282
|
-
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
this.finishCurrentToolCall("canceled");
|
|
15286
|
-
this.resetState();
|
|
15512
|
+
return envelopes;
|
|
15513
|
+
}
|
|
15514
|
+
function patchDescription(changes) {
|
|
15515
|
+
if (!changes || typeof changes !== "object") {
|
|
15516
|
+
return "Applying patch";
|
|
15287
15517
|
}
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
reset() {
|
|
15292
|
-
this.finishCurrentToolCall("canceled");
|
|
15293
|
-
this.resetState();
|
|
15518
|
+
const fileCount = Object.keys(changes).length;
|
|
15519
|
+
if (fileCount === 1) {
|
|
15520
|
+
return "Applying patch to 1 file";
|
|
15294
15521
|
}
|
|
15295
|
-
|
|
15296
|
-
|
|
15297
|
-
|
|
15298
|
-
|
|
15299
|
-
|
|
15300
|
-
|
|
15301
|
-
type: "tool-call-result",
|
|
15302
|
-
callId: this.currentCallId,
|
|
15303
|
-
output: {
|
|
15304
|
-
content: this.contentBuffer || "",
|
|
15305
|
-
status
|
|
15306
|
-
},
|
|
15307
|
-
id: randomUUID()
|
|
15308
|
-
};
|
|
15309
|
-
logger.debug(`${this.getLogPrefix()} Sending tool call result with status: ${status}`);
|
|
15310
|
-
this.onMessage?.(toolResult);
|
|
15311
|
-
}
|
|
15522
|
+
return `Applying patch to ${fileCount} files`;
|
|
15523
|
+
}
|
|
15524
|
+
function pickTurnEndStatus(message, type) {
|
|
15525
|
+
const rawStatus = message.status;
|
|
15526
|
+
if (rawStatus === "completed" || rawStatus === "failed" || rawStatus === "cancelled") {
|
|
15527
|
+
return rawStatus;
|
|
15312
15528
|
}
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
*/
|
|
15316
|
-
resetState() {
|
|
15317
|
-
this.accumulator = "";
|
|
15318
|
-
this.inTitleCapture = false;
|
|
15319
|
-
this.titleBuffer = "";
|
|
15320
|
-
this.contentBuffer = "";
|
|
15321
|
-
this.hasTitle = false;
|
|
15322
|
-
this.currentCallId = null;
|
|
15323
|
-
this.toolCallStarted = false;
|
|
15324
|
-
this.currentTitle = null;
|
|
15529
|
+
if (rawStatus === "canceled") {
|
|
15530
|
+
return "cancelled";
|
|
15325
15531
|
}
|
|
15326
|
-
|
|
15327
|
-
|
|
15328
|
-
|
|
15329
|
-
|
|
15330
|
-
|
|
15532
|
+
if (type === "turn_aborted") {
|
|
15533
|
+
const reason = message.reason;
|
|
15534
|
+
const error = message.error;
|
|
15535
|
+
if (typeof reason === "string" && /(fail|error)/i.test(reason) || typeof error === "string" && error.length > 0 || error !== void 0 && error !== null && typeof error === "object") {
|
|
15536
|
+
return "failed";
|
|
15537
|
+
}
|
|
15538
|
+
return "cancelled";
|
|
15331
15539
|
}
|
|
15332
|
-
|
|
15333
|
-
|
|
15334
|
-
*/
|
|
15335
|
-
hasStartedToolCall() {
|
|
15336
|
-
return this.toolCallStarted;
|
|
15540
|
+
if (message.error !== void 0 && message.error !== null) {
|
|
15541
|
+
return "failed";
|
|
15337
15542
|
}
|
|
15543
|
+
return "completed";
|
|
15338
15544
|
}
|
|
15339
|
-
|
|
15340
|
-
|
|
15341
|
-
|
|
15342
|
-
|
|
15545
|
+
function mapCodexMcpMessageToSessionEnvelopes(message, state) {
|
|
15546
|
+
const type = message.type;
|
|
15547
|
+
const startedSubagents = getStartedSubagents(state);
|
|
15548
|
+
const activeSubagents = getActiveSubagents(state);
|
|
15549
|
+
const providerSubagentToSessionSubagent = getProviderSubagentToSessionSubagent(state);
|
|
15550
|
+
if (type === "collab_agent_item") {
|
|
15551
|
+
return {
|
|
15552
|
+
currentTurnId: state.currentTurnId,
|
|
15553
|
+
startedSubagents,
|
|
15554
|
+
activeSubagents,
|
|
15555
|
+
providerSubagentToSessionSubagent,
|
|
15556
|
+
envelopes: mapCodexCollaborationItem(message.item, buildEnvelopeOptions(state.currentTurnId), "live")
|
|
15557
|
+
};
|
|
15343
15558
|
}
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15559
|
+
if (type === "task_started") {
|
|
15560
|
+
const turnId = createId();
|
|
15561
|
+
const turnStart = createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15562
|
+
startedSubagents.clear();
|
|
15563
|
+
activeSubagents.clear();
|
|
15564
|
+
providerSubagentToSessionSubagent.clear();
|
|
15565
|
+
return {
|
|
15566
|
+
currentTurnId: turnId,
|
|
15567
|
+
startedSubagents,
|
|
15568
|
+
activeSubagents,
|
|
15569
|
+
providerSubagentToSessionSubagent,
|
|
15570
|
+
envelopes: [turnStart]
|
|
15571
|
+
};
|
|
15352
15572
|
}
|
|
15353
|
-
|
|
15354
|
-
|
|
15355
|
-
|
|
15356
|
-
|
|
15357
|
-
|
|
15573
|
+
if (type === "task_complete" || type === "turn_aborted") {
|
|
15574
|
+
if (!state.currentTurnId) {
|
|
15575
|
+
return {
|
|
15576
|
+
currentTurnId: null,
|
|
15577
|
+
startedSubagents,
|
|
15578
|
+
activeSubagents,
|
|
15579
|
+
providerSubagentToSessionSubagent,
|
|
15580
|
+
envelopes: []
|
|
15581
|
+
};
|
|
15582
|
+
}
|
|
15583
|
+
const lifecycleOpts = { turn: state.currentTurnId };
|
|
15584
|
+
providerSubagentToSessionSubagent.clear();
|
|
15585
|
+
return {
|
|
15586
|
+
currentTurnId: null,
|
|
15587
|
+
startedSubagents,
|
|
15588
|
+
activeSubagents,
|
|
15589
|
+
providerSubagentToSessionSubagent,
|
|
15590
|
+
envelopes: [
|
|
15591
|
+
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
15592
|
+
createEnvelope("agent", {
|
|
15593
|
+
t: "turn-end",
|
|
15594
|
+
status: pickTurnEndStatus(message, type)
|
|
15595
|
+
}, lifecycleOpts)
|
|
15596
|
+
]
|
|
15597
|
+
};
|
|
15358
15598
|
}
|
|
15359
|
-
|
|
15360
|
-
|
|
15361
|
-
|
|
15362
|
-
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15599
|
+
if (type === "token_count") {
|
|
15600
|
+
return {
|
|
15601
|
+
currentTurnId: state.currentTurnId,
|
|
15602
|
+
startedSubagents,
|
|
15603
|
+
activeSubagents,
|
|
15604
|
+
providerSubagentToSessionSubagent,
|
|
15605
|
+
envelopes: []
|
|
15606
|
+
};
|
|
15366
15607
|
}
|
|
15367
|
-
|
|
15368
|
-
|
|
15369
|
-
|
|
15370
|
-
|
|
15371
|
-
|
|
15372
|
-
|
|
15373
|
-
|
|
15374
|
-
|
|
15375
|
-
|
|
15376
|
-
|
|
15377
|
-
callId,
|
|
15378
|
-
input: {
|
|
15379
|
-
unified_diff: unifiedDiff
|
|
15380
|
-
},
|
|
15381
|
-
id: randomUUID()
|
|
15608
|
+
const subagent = resolveSessionSubagent(message, providerSubagentToSessionSubagent);
|
|
15609
|
+
const opts = buildEnvelopeOptions(state.currentTurnId, subagent);
|
|
15610
|
+
if (type === "agent_message") {
|
|
15611
|
+
if (typeof message.message !== "string") {
|
|
15612
|
+
return {
|
|
15613
|
+
currentTurnId: state.currentTurnId,
|
|
15614
|
+
startedSubagents,
|
|
15615
|
+
activeSubagents,
|
|
15616
|
+
providerSubagentToSessionSubagent,
|
|
15617
|
+
envelopes: []
|
|
15382
15618
|
};
|
|
15383
|
-
|
|
15384
|
-
|
|
15385
|
-
|
|
15386
|
-
|
|
15387
|
-
|
|
15388
|
-
|
|
15389
|
-
|
|
15390
|
-
|
|
15619
|
+
}
|
|
15620
|
+
const envelopes = [];
|
|
15621
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15622
|
+
envelopes.push(createEnvelope("agent", { t: "text", text: message.message }, opts));
|
|
15623
|
+
return {
|
|
15624
|
+
currentTurnId: state.currentTurnId,
|
|
15625
|
+
startedSubagents,
|
|
15626
|
+
activeSubagents,
|
|
15627
|
+
providerSubagentToSessionSubagent,
|
|
15628
|
+
envelopes
|
|
15629
|
+
};
|
|
15630
|
+
}
|
|
15631
|
+
if (type === "agent_reasoning" || type === "agent_reasoning_delta") {
|
|
15632
|
+
const text = typeof message.text === "string" ? message.text : typeof message.delta === "string" ? message.delta : null;
|
|
15633
|
+
if (!text) {
|
|
15634
|
+
return {
|
|
15635
|
+
currentTurnId: state.currentTurnId,
|
|
15636
|
+
startedSubagents,
|
|
15637
|
+
activeSubagents,
|
|
15638
|
+
providerSubagentToSessionSubagent,
|
|
15639
|
+
envelopes: []
|
|
15391
15640
|
};
|
|
15392
|
-
this.onMessage?.(toolResult);
|
|
15393
15641
|
}
|
|
15394
|
-
|
|
15395
|
-
|
|
15642
|
+
const envelopes = [];
|
|
15643
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15644
|
+
envelopes.push(createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
15645
|
+
return {
|
|
15646
|
+
currentTurnId: state.currentTurnId,
|
|
15647
|
+
startedSubagents,
|
|
15648
|
+
activeSubagents,
|
|
15649
|
+
providerSubagentToSessionSubagent,
|
|
15650
|
+
envelopes
|
|
15651
|
+
};
|
|
15396
15652
|
}
|
|
15397
|
-
|
|
15398
|
-
|
|
15399
|
-
|
|
15400
|
-
|
|
15401
|
-
|
|
15402
|
-
|
|
15653
|
+
if (type === "exec_command_begin") {
|
|
15654
|
+
const call = pickCallId(message);
|
|
15655
|
+
const { call_id: _callIdSnake, callId: _callIdCamel, type: _type, ...args } = message;
|
|
15656
|
+
const command = summarizeCommand(args.command);
|
|
15657
|
+
const description = typeof args.description === "string" ? args.description : command ?? "Execute command";
|
|
15658
|
+
const envelopes = [];
|
|
15659
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15660
|
+
envelopes.push(
|
|
15661
|
+
createEnvelope("agent", {
|
|
15662
|
+
t: "tool-call-start",
|
|
15663
|
+
call,
|
|
15664
|
+
name: "CodexBash",
|
|
15665
|
+
title: commandToTitle(command),
|
|
15666
|
+
description,
|
|
15667
|
+
args
|
|
15668
|
+
}, opts)
|
|
15669
|
+
);
|
|
15670
|
+
return {
|
|
15671
|
+
currentTurnId: state.currentTurnId,
|
|
15672
|
+
startedSubagents,
|
|
15673
|
+
activeSubagents,
|
|
15674
|
+
providerSubagentToSessionSubagent,
|
|
15675
|
+
envelopes
|
|
15676
|
+
};
|
|
15403
15677
|
}
|
|
15404
|
-
|
|
15405
|
-
|
|
15406
|
-
|
|
15407
|
-
|
|
15408
|
-
|
|
15678
|
+
if (type === "exec_command_end") {
|
|
15679
|
+
const call = pickCallId(message);
|
|
15680
|
+
const envelopes = [];
|
|
15681
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15682
|
+
envelopes.push(createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15683
|
+
return {
|
|
15684
|
+
currentTurnId: state.currentTurnId,
|
|
15685
|
+
startedSubagents,
|
|
15686
|
+
activeSubagents,
|
|
15687
|
+
providerSubagentToSessionSubagent,
|
|
15688
|
+
envelopes
|
|
15689
|
+
};
|
|
15409
15690
|
}
|
|
15410
|
-
|
|
15411
|
-
|
|
15412
|
-
|
|
15413
|
-
|
|
15414
|
-
|
|
15691
|
+
if (type === "patch_apply_begin") {
|
|
15692
|
+
const call = pickCallId(message);
|
|
15693
|
+
const autoApproved = message.auto_approved;
|
|
15694
|
+
const changes = message.changes;
|
|
15695
|
+
const envelopes = [];
|
|
15696
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15697
|
+
envelopes.push(
|
|
15698
|
+
createEnvelope("agent", {
|
|
15699
|
+
t: "tool-call-start",
|
|
15700
|
+
call,
|
|
15701
|
+
name: "CodexPatch",
|
|
15702
|
+
title: "Apply patch",
|
|
15703
|
+
description: patchDescription(changes),
|
|
15704
|
+
args: {
|
|
15705
|
+
auto_approved: autoApproved,
|
|
15706
|
+
changes
|
|
15707
|
+
}
|
|
15708
|
+
}, opts)
|
|
15709
|
+
);
|
|
15710
|
+
return {
|
|
15711
|
+
currentTurnId: state.currentTurnId,
|
|
15712
|
+
startedSubagents,
|
|
15713
|
+
activeSubagents,
|
|
15714
|
+
providerSubagentToSessionSubagent,
|
|
15715
|
+
envelopes
|
|
15716
|
+
};
|
|
15415
15717
|
}
|
|
15416
|
-
|
|
15417
|
-
|
|
15418
|
-
const
|
|
15419
|
-
|
|
15420
|
-
|
|
15421
|
-
|
|
15422
|
-
|
|
15423
|
-
|
|
15424
|
-
|
|
15425
|
-
|
|
15426
|
-
|
|
15427
|
-
setMessages(messageBuffer.getMessages());
|
|
15428
|
-
const unsubscribe = messageBuffer.onUpdate((newMessages) => {
|
|
15429
|
-
setMessages(newMessages);
|
|
15430
|
-
});
|
|
15431
|
-
return () => {
|
|
15432
|
-
unsubscribe();
|
|
15433
|
-
if (confirmationTimeoutRef.current) {
|
|
15434
|
-
clearTimeout(confirmationTimeoutRef.current);
|
|
15435
|
-
}
|
|
15718
|
+
if (type === "patch_apply_end") {
|
|
15719
|
+
const call = pickCallId(message);
|
|
15720
|
+
const envelopes = [];
|
|
15721
|
+
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
15722
|
+
envelopes.push(createEnvelope("agent", { t: "tool-call-end", call }, opts));
|
|
15723
|
+
return {
|
|
15724
|
+
currentTurnId: state.currentTurnId,
|
|
15725
|
+
startedSubagents,
|
|
15726
|
+
activeSubagents,
|
|
15727
|
+
providerSubagentToSessionSubagent,
|
|
15728
|
+
envelopes
|
|
15436
15729
|
};
|
|
15437
|
-
}
|
|
15438
|
-
const resetConfirmation = useCallback(() => {
|
|
15439
|
-
setConfirmationMode(false);
|
|
15440
|
-
if (confirmationTimeoutRef.current) {
|
|
15441
|
-
clearTimeout(confirmationTimeoutRef.current);
|
|
15442
|
-
confirmationTimeoutRef.current = null;
|
|
15443
|
-
}
|
|
15444
|
-
}, []);
|
|
15445
|
-
const setConfirmationWithTimeout = useCallback(() => {
|
|
15446
|
-
setConfirmationMode(true);
|
|
15447
|
-
if (confirmationTimeoutRef.current) {
|
|
15448
|
-
clearTimeout(confirmationTimeoutRef.current);
|
|
15449
|
-
}
|
|
15450
|
-
confirmationTimeoutRef.current = setTimeout(() => {
|
|
15451
|
-
resetConfirmation();
|
|
15452
|
-
}, 15e3);
|
|
15453
|
-
}, [resetConfirmation]);
|
|
15454
|
-
useInput(useCallback(async (input, key) => {
|
|
15455
|
-
if (actionInProgress) return;
|
|
15456
|
-
if (key.ctrl && input === "c") {
|
|
15457
|
-
if (confirmationMode) {
|
|
15458
|
-
resetConfirmation();
|
|
15459
|
-
setActionInProgress(true);
|
|
15460
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
15461
|
-
onExit?.();
|
|
15462
|
-
} else {
|
|
15463
|
-
setConfirmationWithTimeout();
|
|
15464
|
-
}
|
|
15465
|
-
return;
|
|
15466
|
-
}
|
|
15467
|
-
if (confirmationMode) {
|
|
15468
|
-
resetConfirmation();
|
|
15469
|
-
}
|
|
15470
|
-
}, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
|
|
15471
|
-
const getMessageColor = (type) => {
|
|
15472
|
-
switch (type) {
|
|
15473
|
-
case "user":
|
|
15474
|
-
return "magenta";
|
|
15475
|
-
case "assistant":
|
|
15476
|
-
return "cyan";
|
|
15477
|
-
case "system":
|
|
15478
|
-
return "blue";
|
|
15479
|
-
case "tool":
|
|
15480
|
-
return "yellow";
|
|
15481
|
-
case "result":
|
|
15482
|
-
return "green";
|
|
15483
|
-
case "status":
|
|
15484
|
-
return "gray";
|
|
15485
|
-
default:
|
|
15486
|
-
return "white";
|
|
15487
|
-
}
|
|
15488
|
-
};
|
|
15489
|
-
const formatMessage = (msg) => {
|
|
15490
|
-
const lines = msg.content.split("\n");
|
|
15491
|
-
const maxLineLength = terminalWidth - 10;
|
|
15492
|
-
return lines.map((line) => {
|
|
15493
|
-
if (line.length <= maxLineLength) return line;
|
|
15494
|
-
const chunks = [];
|
|
15495
|
-
for (let i = 0; i < line.length; i += maxLineLength) {
|
|
15496
|
-
chunks.push(line.slice(i, i + maxLineLength));
|
|
15497
|
-
}
|
|
15498
|
-
return chunks.join("\n");
|
|
15499
|
-
}).join("\n");
|
|
15500
|
-
};
|
|
15501
|
-
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: terminalWidth, height: terminalHeight }, /* @__PURE__ */ React.createElement(
|
|
15502
|
-
Box,
|
|
15503
|
-
{
|
|
15504
|
-
flexDirection: "column",
|
|
15505
|
-
width: terminalWidth,
|
|
15506
|
-
height: terminalHeight - 4,
|
|
15507
|
-
borderStyle: "round",
|
|
15508
|
-
borderColor: "gray",
|
|
15509
|
-
paddingX: 1,
|
|
15510
|
-
overflow: "hidden"
|
|
15511
|
-
},
|
|
15512
|
-
/* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", bold: true }, "\u{1F916} Codex Agent Messages"), /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "\u2500".repeat(Math.min(terminalWidth - 4, 60)))),
|
|
15513
|
-
/* @__PURE__ */ React.createElement(Box, { flexDirection: "column", height: terminalHeight - 10, overflow: "hidden" }, messages.length === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "Waiting for messages...") : (
|
|
15514
|
-
// Show only the last messages that fit in the available space
|
|
15515
|
-
messages.slice(-Math.max(1, terminalHeight - 10)).map((msg) => /* @__PURE__ */ React.createElement(Box, { key: msg.id, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: getMessageColor(msg.type), dimColor: true }, formatMessage(msg))))
|
|
15516
|
-
))
|
|
15517
|
-
), /* @__PURE__ */ React.createElement(
|
|
15518
|
-
Box,
|
|
15519
|
-
{
|
|
15520
|
-
width: terminalWidth,
|
|
15521
|
-
borderStyle: "round",
|
|
15522
|
-
borderColor: actionInProgress ? "gray" : confirmationMode ? "red" : "green",
|
|
15523
|
-
paddingX: 2,
|
|
15524
|
-
justifyContent: "center",
|
|
15525
|
-
alignItems: "center",
|
|
15526
|
-
flexDirection: "column"
|
|
15527
|
-
},
|
|
15528
|
-
/* @__PURE__ */ React.createElement(Box, { flexDirection: "column", alignItems: "center" }, actionInProgress ? /* @__PURE__ */ React.createElement(Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode ? /* @__PURE__ */ React.createElement(Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
|
|
15529
|
-
));
|
|
15530
|
-
};
|
|
15531
|
-
|
|
15532
|
-
function createOfflineSessionStub(sessionTag) {
|
|
15730
|
+
}
|
|
15533
15731
|
return {
|
|
15534
|
-
|
|
15535
|
-
|
|
15536
|
-
|
|
15537
|
-
|
|
15538
|
-
|
|
15539
|
-
sendAgentUsageSnapshot: () => false,
|
|
15540
|
-
sendClaudeSessionMessage: () => {
|
|
15541
|
-
},
|
|
15542
|
-
keepAlive: () => {
|
|
15543
|
-
},
|
|
15544
|
-
sendSessionEvent: () => {
|
|
15545
|
-
},
|
|
15546
|
-
sendSessionDeath: () => {
|
|
15547
|
-
},
|
|
15548
|
-
updateLifecycleState: () => {
|
|
15549
|
-
},
|
|
15550
|
-
requestControlTransfer: async () => {
|
|
15551
|
-
},
|
|
15552
|
-
flush: async () => {
|
|
15553
|
-
},
|
|
15554
|
-
close: async () => {
|
|
15555
|
-
},
|
|
15556
|
-
updateMetadata: () => {
|
|
15557
|
-
},
|
|
15558
|
-
updateAgentState: () => {
|
|
15559
|
-
},
|
|
15560
|
-
onUserMessage: () => {
|
|
15561
|
-
},
|
|
15562
|
-
onFileEvent: () => {
|
|
15563
|
-
},
|
|
15564
|
-
trackAttachmentDownload: () => {
|
|
15565
|
-
},
|
|
15566
|
-
drainAttachmentsForUserMessage: async () => [],
|
|
15567
|
-
rpcHandlerManager: {
|
|
15568
|
-
registerHandler: () => {
|
|
15569
|
-
}
|
|
15570
|
-
}
|
|
15732
|
+
currentTurnId: state.currentTurnId,
|
|
15733
|
+
startedSubagents,
|
|
15734
|
+
activeSubagents,
|
|
15735
|
+
providerSubagentToSessionSubagent,
|
|
15736
|
+
envelopes: []
|
|
15571
15737
|
};
|
|
15572
15738
|
}
|
|
15739
|
+
function mapCodexProcessorMessageToSessionEnvelopes(message, state) {
|
|
15740
|
+
const toolLikeMessage = message;
|
|
15741
|
+
const opts = buildEnvelopeOptions(state.currentTurnId);
|
|
15742
|
+
if (message.type === "reasoning") {
|
|
15743
|
+
return [createEnvelope("agent", {
|
|
15744
|
+
t: "text",
|
|
15745
|
+
text: message.message,
|
|
15746
|
+
thinking: true
|
|
15747
|
+
}, opts)];
|
|
15748
|
+
}
|
|
15749
|
+
if (message.type === "tool-call") {
|
|
15750
|
+
const title = typeof toolLikeMessage.input?.title === "string" ? toolLikeMessage.input.title : `${toolLikeMessage.name || "Tool"} call`;
|
|
15751
|
+
return [createEnvelope("agent", {
|
|
15752
|
+
t: "tool-call-start",
|
|
15753
|
+
call: toolLikeMessage.callId,
|
|
15754
|
+
name: toolLikeMessage.name || "unknown",
|
|
15755
|
+
title,
|
|
15756
|
+
description: title,
|
|
15757
|
+
args: toolLikeMessage.input && typeof toolLikeMessage.input === "object" ? toolLikeMessage.input : {}
|
|
15758
|
+
}, opts)];
|
|
15759
|
+
}
|
|
15760
|
+
if (message.type === "tool-call-result") {
|
|
15761
|
+
const envelopes = [];
|
|
15762
|
+
const content = toolLikeMessage.output?.content;
|
|
15763
|
+
if (typeof content === "string" && content.trim().length > 0) {
|
|
15764
|
+
envelopes.push(createEnvelope("agent", {
|
|
15765
|
+
t: "text",
|
|
15766
|
+
text: content,
|
|
15767
|
+
thinking: true
|
|
15768
|
+
}, opts));
|
|
15769
|
+
}
|
|
15770
|
+
envelopes.push(createEnvelope("agent", {
|
|
15771
|
+
t: "tool-call-end",
|
|
15772
|
+
call: toolLikeMessage.callId
|
|
15773
|
+
}, opts));
|
|
15774
|
+
return envelopes;
|
|
15775
|
+
}
|
|
15776
|
+
return [];
|
|
15777
|
+
}
|
|
15573
15778
|
|
|
15574
|
-
function
|
|
15575
|
-
|
|
15576
|
-
|
|
15577
|
-
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15779
|
+
async function readChildThread(client, childId) {
|
|
15780
|
+
if (!client.threadId || typeof childId !== "string" || !childId) throw new Error("Invalid child thread");
|
|
15781
|
+
const parentId = client.threadId;
|
|
15782
|
+
const { thread: parent } = await client.readThread({ threadId: parentId, includeTurns: true });
|
|
15783
|
+
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));
|
|
15784
|
+
if (!belongs) throw new Error("Thread is not a child of this session");
|
|
15785
|
+
const { thread } = await client.readThread({ threadId: childId, includeTurns: true });
|
|
15786
|
+
if (thread.id !== childId) throw new Error("Child thread mismatch");
|
|
15787
|
+
return { envelopes: mapCodexThreadToSessionEnvelopes(thread) };
|
|
15788
|
+
}
|
|
15789
|
+
|
|
15790
|
+
function registerAgentAttachmentDownloads(session) {
|
|
15791
|
+
session.onFileEvent((fileEvent) => {
|
|
15792
|
+
const attachment = fileEvent.content.data.ev;
|
|
15793
|
+
const download = (async () => {
|
|
15794
|
+
try {
|
|
15795
|
+
const data = await session.downloadAndDecryptAttachment(attachment.ref);
|
|
15796
|
+
if (!data) throw new Error("Attachment decryption failed");
|
|
15797
|
+
return { data, mimeType: attachment.mimeType ?? "image/jpeg", name: attachment.name };
|
|
15798
|
+
} catch {
|
|
15799
|
+
session.sendSessionEvent({ type: "message", message: `Could not download attachment: ${attachment.name}. Please attach it again.` });
|
|
15800
|
+
return null;
|
|
15591
15801
|
}
|
|
15592
|
-
});
|
|
15593
|
-
|
|
15594
|
-
}
|
|
15595
|
-
session = api.sessionSyncClient(response, opts.sessionClientOptions);
|
|
15596
|
-
return { session, reconnectionHandle: null, isOffline: false };
|
|
15597
|
-
}
|
|
15802
|
+
})();
|
|
15803
|
+
session.trackAttachmentDownload(download);
|
|
15804
|
+
});
|
|
15598
15805
|
}
|
|
15599
15806
|
|
|
15600
|
-
|
|
15601
|
-
|
|
15602
|
-
|
|
15603
|
-
|
|
15604
|
-
|
|
15605
|
-
|
|
15807
|
+
class BasePermissionHandler {
|
|
15808
|
+
pendingRequests = /* @__PURE__ */ new Map();
|
|
15809
|
+
session;
|
|
15810
|
+
isResetting = false;
|
|
15811
|
+
constructor(session) {
|
|
15812
|
+
this.session = session;
|
|
15813
|
+
this.setupRpcHandler();
|
|
15606
15814
|
}
|
|
15607
|
-
|
|
15608
|
-
|
|
15609
|
-
|
|
15610
|
-
|
|
15611
|
-
|
|
15612
|
-
|
|
15613
|
-
|
|
15614
|
-
|
|
15615
|
-
|
|
15616
|
-
|
|
15617
|
-
|
|
15618
|
-
|
|
15619
|
-
|
|
15620
|
-
|
|
15621
|
-
|
|
15622
|
-
|
|
15623
|
-
|
|
15624
|
-
|
|
15625
|
-
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
|
|
15633
|
-
|
|
15815
|
+
/**
|
|
15816
|
+
* Update the session reference (used after offline reconnection swaps sessions).
|
|
15817
|
+
* This is critical for avoiding stale session references after onSessionSwap.
|
|
15818
|
+
*/
|
|
15819
|
+
updateSession(newSession) {
|
|
15820
|
+
logger.debug(`${this.getLogPrefix()} Session reference updated`);
|
|
15821
|
+
this.session = newSession;
|
|
15822
|
+
this.setupRpcHandler();
|
|
15823
|
+
}
|
|
15824
|
+
/**
|
|
15825
|
+
* Setup RPC handler for permission responses.
|
|
15826
|
+
*/
|
|
15827
|
+
setupRpcHandler() {
|
|
15828
|
+
this.session.rpcHandlerManager.registerHandler(
|
|
15829
|
+
"permission",
|
|
15830
|
+
async (response) => {
|
|
15831
|
+
const pending = this.pendingRequests.get(response.id);
|
|
15832
|
+
if (!pending) {
|
|
15833
|
+
logger.debug(`${this.getLogPrefix()} Permission request not found or already resolved`);
|
|
15834
|
+
return;
|
|
15835
|
+
}
|
|
15836
|
+
this.pendingRequests.delete(response.id);
|
|
15837
|
+
const result = response.approved ? { decision: response.decision === "approved_for_session" ? "approved_for_session" : "approved" } : { decision: response.decision === "denied" ? "denied" : "abort" };
|
|
15838
|
+
pending.resolve(result);
|
|
15839
|
+
this.session.updateAgentState((currentState) => {
|
|
15840
|
+
const request = currentState.requests?.[response.id];
|
|
15841
|
+
if (!request) return currentState;
|
|
15842
|
+
const { [response.id]: _, ...remainingRequests } = currentState.requests || {};
|
|
15843
|
+
let res = {
|
|
15844
|
+
...currentState,
|
|
15845
|
+
requests: remainingRequests,
|
|
15846
|
+
completedRequests: {
|
|
15847
|
+
...currentState.completedRequests,
|
|
15848
|
+
[response.id]: {
|
|
15849
|
+
...request,
|
|
15850
|
+
completedAt: Date.now(),
|
|
15851
|
+
status: response.approved ? "approved" : "denied",
|
|
15852
|
+
decision: result.decision
|
|
15853
|
+
}
|
|
15854
|
+
}
|
|
15855
|
+
};
|
|
15856
|
+
return res;
|
|
15857
|
+
});
|
|
15858
|
+
logger.debug(`${this.getLogPrefix()} Permission ${response.approved ? "approved" : "denied"} for ${pending.toolName}`);
|
|
15859
|
+
}
|
|
15860
|
+
);
|
|
15861
|
+
}
|
|
15862
|
+
/**
|
|
15863
|
+
* Add a pending request to the agent state.
|
|
15864
|
+
*/
|
|
15865
|
+
addPendingRequestToState(toolCallId, toolName, input) {
|
|
15866
|
+
this.session.updateAgentState((currentState) => ({
|
|
15867
|
+
...currentState,
|
|
15868
|
+
requests: {
|
|
15869
|
+
...currentState.requests,
|
|
15870
|
+
[toolCallId]: {
|
|
15871
|
+
tool: toolName,
|
|
15872
|
+
arguments: input,
|
|
15873
|
+
createdAt: Date.now()
|
|
15874
|
+
}
|
|
15875
|
+
}
|
|
15876
|
+
}));
|
|
15877
|
+
}
|
|
15878
|
+
/**
|
|
15879
|
+
* Abort all pending permission requests.
|
|
15880
|
+
* Unlike reset(), this resolves (not rejects) pending promises with { decision: 'abort' },
|
|
15881
|
+
* causing the approval response to send 'cancel' to the provider. This is used when the
|
|
15882
|
+
* user presses the abort/stop button — it unblocks any pending tool approval so the provider
|
|
15883
|
+
* can process the turn cancellation.
|
|
15884
|
+
*/
|
|
15885
|
+
abortAll() {
|
|
15886
|
+
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15887
|
+
if (pendingSnapshot.length === 0) return;
|
|
15888
|
+
this.pendingRequests.clear();
|
|
15889
|
+
for (const [id, pending] of pendingSnapshot) {
|
|
15890
|
+
try {
|
|
15891
|
+
pending.resolve({ decision: "abort" });
|
|
15892
|
+
} catch (err) {
|
|
15893
|
+
logger.debug(`${this.getLogPrefix()} Error resolving aborted request ${id}:`, err);
|
|
15894
|
+
}
|
|
15634
15895
|
}
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
|
|
15662
|
-
|
|
15896
|
+
this.session.updateAgentState((currentState) => {
|
|
15897
|
+
const pendingRequests = currentState.requests || {};
|
|
15898
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
15899
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15900
|
+
completedRequests[id] = {
|
|
15901
|
+
...request,
|
|
15902
|
+
completedAt: Date.now(),
|
|
15903
|
+
status: "canceled",
|
|
15904
|
+
reason: "Aborted by user"
|
|
15905
|
+
};
|
|
15906
|
+
}
|
|
15907
|
+
return {
|
|
15908
|
+
...currentState,
|
|
15909
|
+
requests: {},
|
|
15910
|
+
completedRequests
|
|
15911
|
+
};
|
|
15912
|
+
});
|
|
15913
|
+
logger.debug(`${this.getLogPrefix()} Aborted ${pendingSnapshot.length} pending permission(s)`);
|
|
15914
|
+
}
|
|
15915
|
+
/**
|
|
15916
|
+
* Reset state for new sessions.
|
|
15917
|
+
* This method is idempotent - safe to call multiple times.
|
|
15918
|
+
*/
|
|
15919
|
+
reset(reason = "Session reset") {
|
|
15920
|
+
if (this.isResetting) {
|
|
15921
|
+
logger.debug(`${this.getLogPrefix()} Reset already in progress, skipping`);
|
|
15922
|
+
return;
|
|
15923
|
+
}
|
|
15924
|
+
this.isResetting = true;
|
|
15925
|
+
try {
|
|
15926
|
+
const pendingSnapshot = Array.from(this.pendingRequests.entries());
|
|
15927
|
+
this.pendingRequests.clear();
|
|
15928
|
+
for (const [id, pending] of pendingSnapshot) {
|
|
15929
|
+
try {
|
|
15930
|
+
pending.reject(new Error("Session reset"));
|
|
15931
|
+
} catch (err) {
|
|
15932
|
+
logger.debug(`${this.getLogPrefix()} Error rejecting pending request ${id}:`, err);
|
|
15933
|
+
}
|
|
15934
|
+
}
|
|
15935
|
+
this.session.updateAgentState((currentState) => {
|
|
15936
|
+
const pendingRequests = currentState.requests || {};
|
|
15937
|
+
const completedRequests = { ...currentState.completedRequests };
|
|
15938
|
+
for (const [id, request] of Object.entries(pendingRequests)) {
|
|
15939
|
+
completedRequests[id] = {
|
|
15940
|
+
...request,
|
|
15941
|
+
completedAt: Date.now(),
|
|
15942
|
+
status: "canceled",
|
|
15943
|
+
reason
|
|
15944
|
+
};
|
|
15945
|
+
}
|
|
15946
|
+
return {
|
|
15947
|
+
...currentState,
|
|
15948
|
+
requests: {},
|
|
15949
|
+
completedRequests
|
|
15950
|
+
};
|
|
15951
|
+
});
|
|
15952
|
+
logger.debug(`${this.getLogPrefix()} Permission handler reset`);
|
|
15953
|
+
} finally {
|
|
15954
|
+
this.isResetting = false;
|
|
15663
15955
|
}
|
|
15664
|
-
}
|
|
15665
|
-
return { approvalPolicy, sandbox };
|
|
15956
|
+
}
|
|
15666
15957
|
}
|
|
15667
15958
|
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
|
|
15959
|
+
class CodexPermissionHandler extends BasePermissionHandler {
|
|
15960
|
+
// Exact tool names that should always be auto-approved. Include the bare
|
|
15961
|
+
// form (used by Codex elicitation messages like `tool "change_title"`)
|
|
15962
|
+
// and the MCP-qualified form for defense in depth.
|
|
15963
|
+
static ALWAYS_AUTO_APPROVE_NAMES = /* @__PURE__ */ new Set([
|
|
15964
|
+
"change_title",
|
|
15965
|
+
"mcp__happy__change_title"
|
|
15966
|
+
]);
|
|
15967
|
+
// Tool-call IDs that should auto-approve when they exactly match one of
|
|
15968
|
+
// these values or start with `<name>-` (e.g. `change_title-1765385846663`).
|
|
15969
|
+
// Substring matching was a bypass vector — any tool whose ID happened to
|
|
15970
|
+
// contain `change_title` as a substring would be silently approved.
|
|
15971
|
+
static ALWAYS_AUTO_APPROVE_ID_PREFIXES = [
|
|
15972
|
+
"change_title"
|
|
15973
|
+
];
|
|
15974
|
+
constructor(session) {
|
|
15975
|
+
super(session);
|
|
15680
15976
|
}
|
|
15681
|
-
|
|
15682
|
-
|
|
15683
|
-
activeSubagents.add(subagent);
|
|
15684
|
-
}
|
|
15685
|
-
function emitSubagentStops(opts, startedSubagents, activeSubagents) {
|
|
15686
|
-
const envelopes = [];
|
|
15687
|
-
for (const subagent of activeSubagents) {
|
|
15688
|
-
envelopes.push(createEnvelope("agent", { t: "stop" }, { ...opts, subagent }));
|
|
15977
|
+
getLogPrefix() {
|
|
15978
|
+
return "[Codex]";
|
|
15689
15979
|
}
|
|
15690
|
-
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
}
|
|
15694
|
-
function buildEnvelopeOptions(currentTurnId, subagent) {
|
|
15695
|
-
return {
|
|
15696
|
-
...currentTurnId ? { turn: currentTurnId } : {},
|
|
15697
|
-
...subagent ? { subagent } : {}
|
|
15698
|
-
};
|
|
15699
|
-
}
|
|
15700
|
-
function pickProviderSubagent(message) {
|
|
15701
|
-
const candidates = [message.subagent, message.parent_call_id, message.parentCallId];
|
|
15702
|
-
for (const candidate of candidates) {
|
|
15703
|
-
if (typeof candidate === "string" && candidate.length > 0) {
|
|
15704
|
-
return candidate;
|
|
15980
|
+
shouldAutoApprove(toolName, toolCallId) {
|
|
15981
|
+
if (CodexPermissionHandler.ALWAYS_AUTO_APPROVE_NAMES.has(toolName)) {
|
|
15982
|
+
return true;
|
|
15705
15983
|
}
|
|
15984
|
+
for (const prefix of CodexPermissionHandler.ALWAYS_AUTO_APPROVE_ID_PREFIXES) {
|
|
15985
|
+
if (toolCallId === prefix || toolCallId.startsWith(`${prefix}-`)) {
|
|
15986
|
+
return true;
|
|
15987
|
+
}
|
|
15988
|
+
}
|
|
15989
|
+
return false;
|
|
15706
15990
|
}
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
15716
|
-
|
|
15991
|
+
/**
|
|
15992
|
+
* Handle a tool permission request
|
|
15993
|
+
* @param toolCallId - The unique ID of the tool call
|
|
15994
|
+
* @param toolName - The name of the tool being called
|
|
15995
|
+
* @param input - The input parameters for the tool
|
|
15996
|
+
* @returns Promise resolving to permission result
|
|
15997
|
+
*/
|
|
15998
|
+
async handleToolCall(toolCallId, toolName, input) {
|
|
15999
|
+
if (this.shouldAutoApprove(toolName, toolCallId)) {
|
|
16000
|
+
logger.debug(`${this.getLogPrefix()} Auto-approving tool ${toolName} (${toolCallId})`);
|
|
16001
|
+
this.session.updateAgentState((currentState) => ({
|
|
16002
|
+
...currentState,
|
|
16003
|
+
completedRequests: {
|
|
16004
|
+
...currentState.completedRequests,
|
|
16005
|
+
[toolCallId]: {
|
|
16006
|
+
tool: toolName,
|
|
16007
|
+
arguments: input,
|
|
16008
|
+
createdAt: Date.now(),
|
|
16009
|
+
completedAt: Date.now(),
|
|
16010
|
+
status: "approved",
|
|
16011
|
+
decision: "approved"
|
|
16012
|
+
}
|
|
16013
|
+
}
|
|
16014
|
+
}));
|
|
16015
|
+
return { decision: "approved" };
|
|
16016
|
+
}
|
|
16017
|
+
return new Promise((resolve, reject) => {
|
|
16018
|
+
this.pendingRequests.set(toolCallId, {
|
|
16019
|
+
resolve,
|
|
16020
|
+
reject,
|
|
16021
|
+
toolName,
|
|
16022
|
+
input
|
|
16023
|
+
});
|
|
16024
|
+
this.addPendingRequestToState(toolCallId, toolName, input);
|
|
16025
|
+
logger.debug(`${this.getLogPrefix()} Permission request sent for tool: ${toolName} (${toolCallId})`);
|
|
16026
|
+
});
|
|
15717
16027
|
}
|
|
15718
|
-
const created = createId();
|
|
15719
|
-
providerSubagentToSessionSubagent.set(providerSubagent, created);
|
|
15720
|
-
return created;
|
|
15721
16028
|
}
|
|
15722
|
-
|
|
15723
|
-
|
|
15724
|
-
|
|
15725
|
-
|
|
16029
|
+
|
|
16030
|
+
class BaseReasoningProcessor {
|
|
16031
|
+
accumulator = "";
|
|
16032
|
+
inTitleCapture = false;
|
|
16033
|
+
titleBuffer = "";
|
|
16034
|
+
contentBuffer = "";
|
|
16035
|
+
hasTitle = false;
|
|
16036
|
+
currentCallId = null;
|
|
16037
|
+
toolCallStarted = false;
|
|
16038
|
+
currentTitle = null;
|
|
16039
|
+
onMessage = null;
|
|
16040
|
+
constructor(onMessage) {
|
|
16041
|
+
this.onMessage = onMessage || null;
|
|
16042
|
+
this.reset();
|
|
15726
16043
|
}
|
|
15727
|
-
|
|
15728
|
-
|
|
15729
|
-
|
|
15730
|
-
|
|
15731
|
-
|
|
16044
|
+
/**
|
|
16045
|
+
* Set the message callback for sending messages directly.
|
|
16046
|
+
*/
|
|
16047
|
+
setMessageCallback(callback) {
|
|
16048
|
+
this.onMessage = callback;
|
|
15732
16049
|
}
|
|
15733
|
-
|
|
15734
|
-
|
|
15735
|
-
|
|
16050
|
+
/**
|
|
16051
|
+
* Process a reasoning section break - indicates a new reasoning section is starting.
|
|
16052
|
+
*/
|
|
16053
|
+
handleSectionBreak() {
|
|
16054
|
+
this.finishCurrentToolCall("canceled");
|
|
16055
|
+
this.resetState();
|
|
16056
|
+
logger.debug(`${this.getLogPrefix()} Section break - reset state`);
|
|
15736
16057
|
}
|
|
15737
|
-
|
|
15738
|
-
|
|
15739
|
-
|
|
15740
|
-
|
|
15741
|
-
|
|
16058
|
+
/**
|
|
16059
|
+
* Process a reasoning delta/chunk and accumulate content.
|
|
16060
|
+
*/
|
|
16061
|
+
processInput(input) {
|
|
16062
|
+
this.accumulator += input;
|
|
16063
|
+
if (!this.inTitleCapture && !this.hasTitle && !this.contentBuffer) {
|
|
16064
|
+
if (this.accumulator.startsWith("**")) {
|
|
16065
|
+
this.inTitleCapture = true;
|
|
16066
|
+
this.titleBuffer = this.accumulator.substring(2);
|
|
16067
|
+
logger.debug(`${this.getLogPrefix()} Started title capture`);
|
|
16068
|
+
} else if (this.accumulator.length > 0) {
|
|
16069
|
+
this.contentBuffer = this.accumulator;
|
|
16070
|
+
}
|
|
16071
|
+
} else if (this.inTitleCapture) {
|
|
16072
|
+
this.titleBuffer = this.accumulator.substring(2);
|
|
16073
|
+
const titleEndIndex = this.titleBuffer.indexOf("**");
|
|
16074
|
+
if (titleEndIndex !== -1) {
|
|
16075
|
+
const title = this.titleBuffer.substring(0, titleEndIndex);
|
|
16076
|
+
const afterTitle = this.titleBuffer.substring(titleEndIndex + 2);
|
|
16077
|
+
this.hasTitle = true;
|
|
16078
|
+
this.inTitleCapture = false;
|
|
16079
|
+
this.currentTitle = title;
|
|
16080
|
+
this.contentBuffer = afterTitle;
|
|
16081
|
+
this.currentCallId = randomUUID();
|
|
16082
|
+
logger.debug(`${this.getLogPrefix()} Title captured: "${title}"`);
|
|
16083
|
+
this.sendToolCallStart(title);
|
|
16084
|
+
}
|
|
16085
|
+
} else if (this.hasTitle) {
|
|
16086
|
+
const titleStartIndex = this.accumulator.indexOf("**");
|
|
16087
|
+
if (titleStartIndex !== -1) {
|
|
16088
|
+
this.contentBuffer = this.accumulator.substring(
|
|
16089
|
+
titleStartIndex + 2 + this.currentTitle.length + 2
|
|
16090
|
+
);
|
|
16091
|
+
}
|
|
16092
|
+
} else {
|
|
16093
|
+
this.contentBuffer = this.accumulator;
|
|
16094
|
+
}
|
|
15742
16095
|
}
|
|
15743
|
-
|
|
15744
|
-
|
|
15745
|
-
|
|
15746
|
-
|
|
15747
|
-
|
|
15748
|
-
|
|
15749
|
-
}
|
|
15750
|
-
|
|
15751
|
-
|
|
15752
|
-
|
|
15753
|
-
|
|
15754
|
-
|
|
15755
|
-
|
|
15756
|
-
|
|
16096
|
+
/**
|
|
16097
|
+
* Send the tool call start message.
|
|
16098
|
+
*/
|
|
16099
|
+
sendToolCallStart(title) {
|
|
16100
|
+
if (!this.currentCallId || this.toolCallStarted) {
|
|
16101
|
+
return;
|
|
16102
|
+
}
|
|
16103
|
+
const toolCall = {
|
|
16104
|
+
type: "tool-call",
|
|
16105
|
+
name: this.getToolName(),
|
|
16106
|
+
callId: this.currentCallId,
|
|
16107
|
+
input: {
|
|
16108
|
+
title
|
|
16109
|
+
},
|
|
16110
|
+
id: randomUUID()
|
|
16111
|
+
};
|
|
16112
|
+
logger.debug(`${this.getLogPrefix()} Sending tool call start for: "${title}"`);
|
|
16113
|
+
this.onMessage?.(toolCall);
|
|
16114
|
+
this.toolCallStarted = true;
|
|
15757
16115
|
}
|
|
15758
|
-
|
|
15759
|
-
|
|
15760
|
-
|
|
15761
|
-
|
|
15762
|
-
|
|
15763
|
-
|
|
15764
|
-
|
|
15765
|
-
|
|
15766
|
-
|
|
15767
|
-
|
|
15768
|
-
|
|
15769
|
-
|
|
15770
|
-
|
|
15771
|
-
|
|
15772
|
-
|
|
15773
|
-
|
|
15774
|
-
|
|
16116
|
+
/**
|
|
16117
|
+
* Complete the reasoning section.
|
|
16118
|
+
* Returns true if reasoning was completed, false if there was nothing to complete.
|
|
16119
|
+
*/
|
|
16120
|
+
completeReasoning(fullText) {
|
|
16121
|
+
const text = fullText ?? this.accumulator;
|
|
16122
|
+
if (!text.trim() && !this.toolCallStarted) {
|
|
16123
|
+
logger.debug(`${this.getLogPrefix()} Complete called but no content accumulated, skipping`);
|
|
16124
|
+
return false;
|
|
16125
|
+
}
|
|
16126
|
+
let title;
|
|
16127
|
+
let content = text;
|
|
16128
|
+
if (text.startsWith("**")) {
|
|
16129
|
+
const titleEndIndex = text.indexOf("**", 2);
|
|
16130
|
+
if (titleEndIndex !== -1) {
|
|
16131
|
+
title = text.substring(2, titleEndIndex);
|
|
16132
|
+
content = text.substring(titleEndIndex + 2).trim();
|
|
16133
|
+
}
|
|
16134
|
+
}
|
|
16135
|
+
logger.debug(`${this.getLogPrefix()} Complete reasoning - Title: "${title}", Has content: ${content.length > 0}`);
|
|
16136
|
+
if (title && !this.toolCallStarted) {
|
|
16137
|
+
this.currentCallId = this.currentCallId || randomUUID();
|
|
16138
|
+
this.sendToolCallStart(title);
|
|
16139
|
+
}
|
|
16140
|
+
if (this.toolCallStarted && this.currentCallId) {
|
|
16141
|
+
const toolResult = {
|
|
16142
|
+
type: "tool-call-result",
|
|
16143
|
+
callId: this.currentCallId,
|
|
16144
|
+
output: {
|
|
16145
|
+
content,
|
|
16146
|
+
status: "completed"
|
|
16147
|
+
},
|
|
16148
|
+
id: randomUUID()
|
|
16149
|
+
};
|
|
16150
|
+
logger.debug(`${this.getLogPrefix()} Sending tool call result`);
|
|
16151
|
+
this.onMessage?.(toolResult);
|
|
16152
|
+
} else if (content.trim()) {
|
|
16153
|
+
const reasoningMessage = {
|
|
16154
|
+
type: "reasoning",
|
|
16155
|
+
message: content,
|
|
16156
|
+
id: randomUUID()
|
|
16157
|
+
};
|
|
16158
|
+
logger.debug(`${this.getLogPrefix()} Sending reasoning message`);
|
|
16159
|
+
this.onMessage?.(reasoningMessage);
|
|
16160
|
+
}
|
|
16161
|
+
this.resetState();
|
|
16162
|
+
return true;
|
|
15775
16163
|
}
|
|
15776
|
-
|
|
15777
|
-
|
|
16164
|
+
/**
|
|
16165
|
+
* Abort the current reasoning section.
|
|
16166
|
+
*/
|
|
16167
|
+
abort() {
|
|
16168
|
+
logger.debug(`${this.getLogPrefix()} Abort called`);
|
|
16169
|
+
this.finishCurrentToolCall("canceled");
|
|
16170
|
+
this.resetState();
|
|
15778
16171
|
}
|
|
15779
|
-
|
|
15780
|
-
|
|
15781
|
-
|
|
15782
|
-
|
|
15783
|
-
|
|
15784
|
-
|
|
15785
|
-
t: "tool-call-start",
|
|
15786
|
-
call: item.id,
|
|
15787
|
-
name,
|
|
15788
|
-
title,
|
|
15789
|
-
description: title,
|
|
15790
|
-
args
|
|
15791
|
-
}, {
|
|
15792
|
-
...opts,
|
|
15793
|
-
id: `${item.id}:start`
|
|
15794
|
-
}));
|
|
15795
|
-
if (output && output.trim().length > 0) {
|
|
15796
|
-
envelopes.push(createEnvelope("agent", {
|
|
15797
|
-
t: "text",
|
|
15798
|
-
text: output,
|
|
15799
|
-
thinking: true
|
|
15800
|
-
}, {
|
|
15801
|
-
...opts,
|
|
15802
|
-
id: `${item.id}:output`
|
|
15803
|
-
}));
|
|
16172
|
+
/**
|
|
16173
|
+
* Reset the processor state.
|
|
16174
|
+
*/
|
|
16175
|
+
reset() {
|
|
16176
|
+
this.finishCurrentToolCall("canceled");
|
|
16177
|
+
this.resetState();
|
|
15804
16178
|
}
|
|
15805
|
-
|
|
15806
|
-
|
|
15807
|
-
|
|
15808
|
-
|
|
15809
|
-
|
|
15810
|
-
|
|
15811
|
-
|
|
15812
|
-
|
|
15813
|
-
|
|
15814
|
-
|
|
15815
|
-
|
|
15816
|
-
|
|
15817
|
-
|
|
15818
|
-
|
|
15819
|
-
|
|
15820
|
-
|
|
15821
|
-
turn: turn.id,
|
|
15822
|
-
time: startedAt
|
|
15823
|
-
}));
|
|
15824
|
-
for (const item of turn.items ?? []) {
|
|
15825
|
-
switch (item.type) {
|
|
15826
|
-
case "userMessage": {
|
|
15827
|
-
const text = textFromInputItems(item.content);
|
|
15828
|
-
if (text) {
|
|
15829
|
-
envelopes.push(createEnvelope("user", { t: "text", text }, {
|
|
15830
|
-
id: item.id,
|
|
15831
|
-
time: startedAt,
|
|
15832
|
-
codexItemId: item.id
|
|
15833
|
-
}));
|
|
15834
|
-
}
|
|
15835
|
-
break;
|
|
15836
|
-
}
|
|
15837
|
-
case "agentMessage": {
|
|
15838
|
-
const text = typeof item.text === "string" ? item.text.trim() : "";
|
|
15839
|
-
if (text.length > 0) {
|
|
15840
|
-
envelopes.push(createEnvelope("agent", { t: "text", text }, {
|
|
15841
|
-
id: item.id,
|
|
15842
|
-
turn: turn.id,
|
|
15843
|
-
time: completedAt,
|
|
15844
|
-
codexItemId: item.id
|
|
15845
|
-
}));
|
|
15846
|
-
}
|
|
15847
|
-
break;
|
|
15848
|
-
}
|
|
15849
|
-
case "reasoning": {
|
|
15850
|
-
const text = reasoningText(item);
|
|
15851
|
-
if (text) {
|
|
15852
|
-
envelopes.push(createEnvelope("agent", { t: "text", text, thinking: true }, {
|
|
15853
|
-
id: item.id,
|
|
15854
|
-
turn: turn.id,
|
|
15855
|
-
time: startedAt,
|
|
15856
|
-
codexItemId: item.id
|
|
15857
|
-
}));
|
|
15858
|
-
}
|
|
15859
|
-
break;
|
|
15860
|
-
}
|
|
15861
|
-
case "commandExecution": {
|
|
15862
|
-
const command = typeof item.command === "string" ? item.command : "";
|
|
15863
|
-
emitHistoricalToolCall(
|
|
15864
|
-
envelopes,
|
|
15865
|
-
turn,
|
|
15866
|
-
item,
|
|
15867
|
-
"CodexBash",
|
|
15868
|
-
commandToTitle(command),
|
|
15869
|
-
{ command, cwd: item.cwd },
|
|
15870
|
-
typeof item.aggregatedOutput === "string" ? item.aggregatedOutput : null
|
|
15871
|
-
);
|
|
15872
|
-
break;
|
|
15873
|
-
}
|
|
15874
|
-
case "fileChange": {
|
|
15875
|
-
const title = "Apply patch";
|
|
15876
|
-
emitHistoricalToolCall(
|
|
15877
|
-
envelopes,
|
|
15878
|
-
turn,
|
|
15879
|
-
item,
|
|
15880
|
-
"CodexPatch",
|
|
15881
|
-
title,
|
|
15882
|
-
{ changes: item.changes, status: item.status },
|
|
15883
|
-
null
|
|
15884
|
-
);
|
|
15885
|
-
break;
|
|
15886
|
-
}
|
|
15887
|
-
case "mcpToolCall": {
|
|
15888
|
-
const title = `${item.server}.${item.tool}`;
|
|
15889
|
-
const output = item.error !== void 0 && item.error !== null ? String(item.error) : item.result !== void 0 && item.result !== null ? String(item.result) : null;
|
|
15890
|
-
emitHistoricalToolCall(
|
|
15891
|
-
envelopes,
|
|
15892
|
-
turn,
|
|
15893
|
-
item,
|
|
15894
|
-
"McpTool",
|
|
15895
|
-
title,
|
|
15896
|
-
{
|
|
15897
|
-
server: item.server,
|
|
15898
|
-
tool: item.tool,
|
|
15899
|
-
arguments: item.arguments
|
|
15900
|
-
},
|
|
15901
|
-
output
|
|
15902
|
-
);
|
|
15903
|
-
break;
|
|
15904
|
-
}
|
|
15905
|
-
}
|
|
16179
|
+
/**
|
|
16180
|
+
* Finish current tool call if one is in progress.
|
|
16181
|
+
*/
|
|
16182
|
+
finishCurrentToolCall(status) {
|
|
16183
|
+
if (this.toolCallStarted && this.currentCallId) {
|
|
16184
|
+
const toolResult = {
|
|
16185
|
+
type: "tool-call-result",
|
|
16186
|
+
callId: this.currentCallId,
|
|
16187
|
+
output: {
|
|
16188
|
+
content: this.contentBuffer || "",
|
|
16189
|
+
status
|
|
16190
|
+
},
|
|
16191
|
+
id: randomUUID()
|
|
16192
|
+
};
|
|
16193
|
+
logger.debug(`${this.getLogPrefix()} Sending tool call result with status: ${status}`);
|
|
16194
|
+
this.onMessage?.(toolResult);
|
|
15906
16195
|
}
|
|
15907
|
-
envelopes.push(createEnvelope("agent", { t: "turn-end", status: turnStatus(turn) }, {
|
|
15908
|
-
id: `${turn.id}:end`,
|
|
15909
|
-
turn: turn.id,
|
|
15910
|
-
time: completedAt
|
|
15911
|
-
}));
|
|
15912
16196
|
}
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
16197
|
+
/**
|
|
16198
|
+
* Reset internal state.
|
|
16199
|
+
*/
|
|
16200
|
+
resetState() {
|
|
16201
|
+
this.accumulator = "";
|
|
16202
|
+
this.inTitleCapture = false;
|
|
16203
|
+
this.titleBuffer = "";
|
|
16204
|
+
this.contentBuffer = "";
|
|
16205
|
+
this.hasTitle = false;
|
|
16206
|
+
this.currentCallId = null;
|
|
16207
|
+
this.toolCallStarted = false;
|
|
16208
|
+
this.currentTitle = null;
|
|
15918
16209
|
}
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
|
|
16210
|
+
/**
|
|
16211
|
+
* Get the current call ID for tool result matching.
|
|
16212
|
+
*/
|
|
16213
|
+
getCurrentCallId() {
|
|
16214
|
+
return this.currentCallId;
|
|
16215
|
+
}
|
|
16216
|
+
/**
|
|
16217
|
+
* Check if a tool call has been started.
|
|
16218
|
+
*/
|
|
16219
|
+
hasStartedToolCall() {
|
|
16220
|
+
return this.toolCallStarted;
|
|
15922
16221
|
}
|
|
15923
|
-
return `Applying patch to ${fileCount} files`;
|
|
15924
16222
|
}
|
|
15925
|
-
|
|
15926
|
-
|
|
15927
|
-
|
|
15928
|
-
return
|
|
16223
|
+
|
|
16224
|
+
class ReasoningProcessor extends BaseReasoningProcessor {
|
|
16225
|
+
getToolName() {
|
|
16226
|
+
return "CodexReasoning";
|
|
15929
16227
|
}
|
|
15930
|
-
|
|
15931
|
-
return "
|
|
16228
|
+
getLogPrefix() {
|
|
16229
|
+
return "[ReasoningProcessor]";
|
|
15932
16230
|
}
|
|
15933
|
-
|
|
15934
|
-
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
}
|
|
15939
|
-
return "cancelled";
|
|
16231
|
+
/**
|
|
16232
|
+
* Process a reasoning delta and accumulate content.
|
|
16233
|
+
*/
|
|
16234
|
+
processDelta(delta) {
|
|
16235
|
+
this.processInput(delta);
|
|
15940
16236
|
}
|
|
15941
|
-
|
|
15942
|
-
|
|
16237
|
+
/**
|
|
16238
|
+
* Complete the reasoning section with final text.
|
|
16239
|
+
*/
|
|
16240
|
+
complete(fullText) {
|
|
16241
|
+
this.completeReasoning(fullText);
|
|
15943
16242
|
}
|
|
15944
|
-
return "completed";
|
|
15945
16243
|
}
|
|
15946
|
-
|
|
15947
|
-
|
|
15948
|
-
|
|
15949
|
-
|
|
15950
|
-
|
|
15951
|
-
|
|
15952
|
-
const turnId = createId();
|
|
15953
|
-
const turnStart = createEnvelope("agent", { t: "turn-start" }, { turn: turnId });
|
|
15954
|
-
startedSubagents.clear();
|
|
15955
|
-
activeSubagents.clear();
|
|
15956
|
-
providerSubagentToSessionSubagent.clear();
|
|
15957
|
-
return {
|
|
15958
|
-
currentTurnId: turnId,
|
|
15959
|
-
startedSubagents,
|
|
15960
|
-
activeSubagents,
|
|
15961
|
-
providerSubagentToSessionSubagent,
|
|
15962
|
-
envelopes: [turnStart]
|
|
15963
|
-
};
|
|
15964
|
-
}
|
|
15965
|
-
if (type === "task_complete" || type === "turn_aborted") {
|
|
15966
|
-
if (!state.currentTurnId) {
|
|
15967
|
-
return {
|
|
15968
|
-
currentTurnId: null,
|
|
15969
|
-
startedSubagents,
|
|
15970
|
-
activeSubagents,
|
|
15971
|
-
providerSubagentToSessionSubagent,
|
|
15972
|
-
envelopes: []
|
|
15973
|
-
};
|
|
15974
|
-
}
|
|
15975
|
-
const lifecycleOpts = { turn: state.currentTurnId };
|
|
15976
|
-
providerSubagentToSessionSubagent.clear();
|
|
15977
|
-
return {
|
|
15978
|
-
currentTurnId: null,
|
|
15979
|
-
startedSubagents,
|
|
15980
|
-
activeSubagents,
|
|
15981
|
-
providerSubagentToSessionSubagent,
|
|
15982
|
-
envelopes: [
|
|
15983
|
-
...emitSubagentStops(lifecycleOpts, startedSubagents, activeSubagents),
|
|
15984
|
-
createEnvelope("agent", {
|
|
15985
|
-
t: "turn-end",
|
|
15986
|
-
status: pickTurnEndStatus(message, type)
|
|
15987
|
-
}, lifecycleOpts)
|
|
15988
|
-
]
|
|
15989
|
-
};
|
|
15990
|
-
}
|
|
15991
|
-
if (type === "token_count") {
|
|
15992
|
-
return {
|
|
15993
|
-
currentTurnId: state.currentTurnId,
|
|
15994
|
-
startedSubagents,
|
|
15995
|
-
activeSubagents,
|
|
15996
|
-
providerSubagentToSessionSubagent,
|
|
15997
|
-
envelopes: []
|
|
15998
|
-
};
|
|
16244
|
+
|
|
16245
|
+
class DiffProcessor {
|
|
16246
|
+
previousDiff = null;
|
|
16247
|
+
onMessage = null;
|
|
16248
|
+
constructor(onMessage) {
|
|
16249
|
+
this.onMessage = onMessage || null;
|
|
15999
16250
|
}
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
|
|
16003
|
-
|
|
16004
|
-
|
|
16005
|
-
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
|
|
16251
|
+
/**
|
|
16252
|
+
* Process a turn_diff message and check if the unified_diff has changed
|
|
16253
|
+
*/
|
|
16254
|
+
processDiff(unifiedDiff) {
|
|
16255
|
+
if (this.previousDiff !== unifiedDiff) {
|
|
16256
|
+
logger.debug("[DiffProcessor] Unified diff changed, sending CodexDiff tool call");
|
|
16257
|
+
const callId = randomUUID();
|
|
16258
|
+
const toolCall = {
|
|
16259
|
+
type: "tool-call",
|
|
16260
|
+
name: "CodexDiff",
|
|
16261
|
+
callId,
|
|
16262
|
+
input: {
|
|
16263
|
+
unified_diff: unifiedDiff
|
|
16264
|
+
},
|
|
16265
|
+
id: randomUUID()
|
|
16010
16266
|
};
|
|
16011
|
-
|
|
16012
|
-
|
|
16013
|
-
|
|
16014
|
-
|
|
16015
|
-
|
|
16016
|
-
|
|
16017
|
-
|
|
16018
|
-
|
|
16019
|
-
providerSubagentToSessionSubagent,
|
|
16020
|
-
envelopes
|
|
16021
|
-
};
|
|
16022
|
-
}
|
|
16023
|
-
if (type === "agent_reasoning" || type === "agent_reasoning_delta") {
|
|
16024
|
-
const text = typeof message.text === "string" ? message.text : typeof message.delta === "string" ? message.delta : null;
|
|
16025
|
-
if (!text) {
|
|
16026
|
-
return {
|
|
16027
|
-
currentTurnId: state.currentTurnId,
|
|
16028
|
-
startedSubagents,
|
|
16029
|
-
activeSubagents,
|
|
16030
|
-
providerSubagentToSessionSubagent,
|
|
16031
|
-
envelopes: []
|
|
16267
|
+
this.onMessage?.(toolCall);
|
|
16268
|
+
const toolResult = {
|
|
16269
|
+
type: "tool-call-result",
|
|
16270
|
+
callId,
|
|
16271
|
+
output: {
|
|
16272
|
+
status: "completed"
|
|
16273
|
+
},
|
|
16274
|
+
id: randomUUID()
|
|
16032
16275
|
};
|
|
16276
|
+
this.onMessage?.(toolResult);
|
|
16033
16277
|
}
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
envelopes.push(createEnvelope("agent", { t: "text", text, thinking: true }, opts));
|
|
16037
|
-
return {
|
|
16038
|
-
currentTurnId: state.currentTurnId,
|
|
16039
|
-
startedSubagents,
|
|
16040
|
-
activeSubagents,
|
|
16041
|
-
providerSubagentToSessionSubagent,
|
|
16042
|
-
envelopes
|
|
16043
|
-
};
|
|
16278
|
+
this.previousDiff = unifiedDiff;
|
|
16279
|
+
logger.debug("[DiffProcessor] Updated stored diff");
|
|
16044
16280
|
}
|
|
16045
|
-
|
|
16046
|
-
|
|
16047
|
-
|
|
16048
|
-
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
16052
|
-
envelopes.push(
|
|
16053
|
-
createEnvelope("agent", {
|
|
16054
|
-
t: "tool-call-start",
|
|
16055
|
-
call,
|
|
16056
|
-
name: "CodexBash",
|
|
16057
|
-
title: commandToTitle(command),
|
|
16058
|
-
description,
|
|
16059
|
-
args
|
|
16060
|
-
}, opts)
|
|
16061
|
-
);
|
|
16062
|
-
return {
|
|
16063
|
-
currentTurnId: state.currentTurnId,
|
|
16064
|
-
startedSubagents,
|
|
16065
|
-
activeSubagents,
|
|
16066
|
-
providerSubagentToSessionSubagent,
|
|
16067
|
-
envelopes
|
|
16068
|
-
};
|
|
16281
|
+
/**
|
|
16282
|
+
* Reset the processor state (called on task_complete or turn_aborted)
|
|
16283
|
+
*/
|
|
16284
|
+
reset() {
|
|
16285
|
+
logger.debug("[DiffProcessor] Resetting diff state");
|
|
16286
|
+
this.previousDiff = null;
|
|
16069
16287
|
}
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
return {
|
|
16076
|
-
currentTurnId: state.currentTurnId,
|
|
16077
|
-
startedSubagents,
|
|
16078
|
-
activeSubagents,
|
|
16079
|
-
providerSubagentToSessionSubagent,
|
|
16080
|
-
envelopes
|
|
16081
|
-
};
|
|
16288
|
+
/**
|
|
16289
|
+
* Set the message callback for sending messages directly
|
|
16290
|
+
*/
|
|
16291
|
+
setMessageCallback(callback) {
|
|
16292
|
+
this.onMessage = callback;
|
|
16082
16293
|
}
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
maybeEmitSubagentStart(subagent, opts, startedSubagents, activeSubagents, envelopes);
|
|
16089
|
-
envelopes.push(
|
|
16090
|
-
createEnvelope("agent", {
|
|
16091
|
-
t: "tool-call-start",
|
|
16092
|
-
call,
|
|
16093
|
-
name: "CodexPatch",
|
|
16094
|
-
title: "Apply patch",
|
|
16095
|
-
description: patchDescription(changes),
|
|
16096
|
-
args: {
|
|
16097
|
-
auto_approved: autoApproved,
|
|
16098
|
-
changes
|
|
16099
|
-
}
|
|
16100
|
-
}, opts)
|
|
16101
|
-
);
|
|
16102
|
-
return {
|
|
16103
|
-
currentTurnId: state.currentTurnId,
|
|
16104
|
-
startedSubagents,
|
|
16105
|
-
activeSubagents,
|
|
16106
|
-
providerSubagentToSessionSubagent,
|
|
16107
|
-
envelopes
|
|
16108
|
-
};
|
|
16294
|
+
/**
|
|
16295
|
+
* Get the current diff value
|
|
16296
|
+
*/
|
|
16297
|
+
getCurrentDiff() {
|
|
16298
|
+
return this.previousDiff;
|
|
16109
16299
|
}
|
|
16110
|
-
|
|
16111
|
-
|
|
16112
|
-
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
|
|
16117
|
-
|
|
16118
|
-
|
|
16119
|
-
|
|
16120
|
-
|
|
16300
|
+
}
|
|
16301
|
+
|
|
16302
|
+
const CodexDisplay = ({ messageBuffer, logPath, onExit }) => {
|
|
16303
|
+
const [messages, setMessages] = useState([]);
|
|
16304
|
+
const [confirmationMode, setConfirmationMode] = useState(false);
|
|
16305
|
+
const [actionInProgress, setActionInProgress] = useState(false);
|
|
16306
|
+
const confirmationTimeoutRef = useRef(null);
|
|
16307
|
+
const { stdout } = useStdout();
|
|
16308
|
+
const terminalWidth = stdout.columns || 80;
|
|
16309
|
+
const terminalHeight = stdout.rows || 24;
|
|
16310
|
+
useEffect(() => {
|
|
16311
|
+
setMessages(messageBuffer.getMessages());
|
|
16312
|
+
const unsubscribe = messageBuffer.onUpdate((newMessages) => {
|
|
16313
|
+
setMessages(newMessages);
|
|
16314
|
+
});
|
|
16315
|
+
return () => {
|
|
16316
|
+
unsubscribe();
|
|
16317
|
+
if (confirmationTimeoutRef.current) {
|
|
16318
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
16319
|
+
}
|
|
16121
16320
|
};
|
|
16122
|
-
}
|
|
16321
|
+
}, [messageBuffer]);
|
|
16322
|
+
const resetConfirmation = useCallback(() => {
|
|
16323
|
+
setConfirmationMode(false);
|
|
16324
|
+
if (confirmationTimeoutRef.current) {
|
|
16325
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
16326
|
+
confirmationTimeoutRef.current = null;
|
|
16327
|
+
}
|
|
16328
|
+
}, []);
|
|
16329
|
+
const setConfirmationWithTimeout = useCallback(() => {
|
|
16330
|
+
setConfirmationMode(true);
|
|
16331
|
+
if (confirmationTimeoutRef.current) {
|
|
16332
|
+
clearTimeout(confirmationTimeoutRef.current);
|
|
16333
|
+
}
|
|
16334
|
+
confirmationTimeoutRef.current = setTimeout(() => {
|
|
16335
|
+
resetConfirmation();
|
|
16336
|
+
}, 15e3);
|
|
16337
|
+
}, [resetConfirmation]);
|
|
16338
|
+
useInput(useCallback(async (input, key) => {
|
|
16339
|
+
if (actionInProgress) return;
|
|
16340
|
+
if (key.ctrl && input === "c") {
|
|
16341
|
+
if (confirmationMode) {
|
|
16342
|
+
resetConfirmation();
|
|
16343
|
+
setActionInProgress(true);
|
|
16344
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
16345
|
+
onExit?.();
|
|
16346
|
+
} else {
|
|
16347
|
+
setConfirmationWithTimeout();
|
|
16348
|
+
}
|
|
16349
|
+
return;
|
|
16350
|
+
}
|
|
16351
|
+
if (confirmationMode) {
|
|
16352
|
+
resetConfirmation();
|
|
16353
|
+
}
|
|
16354
|
+
}, [confirmationMode, actionInProgress, onExit, setConfirmationWithTimeout, resetConfirmation]));
|
|
16355
|
+
const getMessageColor = (type) => {
|
|
16356
|
+
switch (type) {
|
|
16357
|
+
case "user":
|
|
16358
|
+
return "magenta";
|
|
16359
|
+
case "assistant":
|
|
16360
|
+
return "cyan";
|
|
16361
|
+
case "system":
|
|
16362
|
+
return "blue";
|
|
16363
|
+
case "tool":
|
|
16364
|
+
return "yellow";
|
|
16365
|
+
case "result":
|
|
16366
|
+
return "green";
|
|
16367
|
+
case "status":
|
|
16368
|
+
return "gray";
|
|
16369
|
+
default:
|
|
16370
|
+
return "white";
|
|
16371
|
+
}
|
|
16372
|
+
};
|
|
16373
|
+
const formatMessage = (msg) => {
|
|
16374
|
+
const lines = msg.content.split("\n");
|
|
16375
|
+
const maxLineLength = terminalWidth - 10;
|
|
16376
|
+
return lines.map((line) => {
|
|
16377
|
+
if (line.length <= maxLineLength) return line;
|
|
16378
|
+
const chunks = [];
|
|
16379
|
+
for (let i = 0; i < line.length; i += maxLineLength) {
|
|
16380
|
+
chunks.push(line.slice(i, i + maxLineLength));
|
|
16381
|
+
}
|
|
16382
|
+
return chunks.join("\n");
|
|
16383
|
+
}).join("\n");
|
|
16384
|
+
};
|
|
16385
|
+
return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: terminalWidth, height: terminalHeight }, /* @__PURE__ */ React.createElement(
|
|
16386
|
+
Box,
|
|
16387
|
+
{
|
|
16388
|
+
flexDirection: "column",
|
|
16389
|
+
width: terminalWidth,
|
|
16390
|
+
height: terminalHeight - 4,
|
|
16391
|
+
borderStyle: "round",
|
|
16392
|
+
borderColor: "gray",
|
|
16393
|
+
paddingX: 1,
|
|
16394
|
+
overflow: "hidden"
|
|
16395
|
+
},
|
|
16396
|
+
/* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "gray", bold: true }, "\u{1F916} Codex Agent Messages"), /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "\u2500".repeat(Math.min(terminalWidth - 4, 60)))),
|
|
16397
|
+
/* @__PURE__ */ React.createElement(Box, { flexDirection: "column", height: terminalHeight - 10, overflow: "hidden" }, messages.length === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "Waiting for messages...") : (
|
|
16398
|
+
// Show only the last messages that fit in the available space
|
|
16399
|
+
messages.slice(-Math.max(1, terminalHeight - 10)).map((msg) => /* @__PURE__ */ React.createElement(Box, { key: msg.id, flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: getMessageColor(msg.type), dimColor: true }, formatMessage(msg))))
|
|
16400
|
+
))
|
|
16401
|
+
), /* @__PURE__ */ React.createElement(
|
|
16402
|
+
Box,
|
|
16403
|
+
{
|
|
16404
|
+
width: terminalWidth,
|
|
16405
|
+
borderStyle: "round",
|
|
16406
|
+
borderColor: actionInProgress ? "gray" : confirmationMode ? "red" : "green",
|
|
16407
|
+
paddingX: 2,
|
|
16408
|
+
justifyContent: "center",
|
|
16409
|
+
alignItems: "center",
|
|
16410
|
+
flexDirection: "column"
|
|
16411
|
+
},
|
|
16412
|
+
/* @__PURE__ */ React.createElement(Box, { flexDirection: "column", alignItems: "center" }, actionInProgress ? /* @__PURE__ */ React.createElement(Text, { color: "gray", bold: true }, "Exiting agent...") : confirmationMode ? /* @__PURE__ */ React.createElement(Text, { color: "red", bold: true }, "\u26A0\uFE0F Press Ctrl-C again to exit the agent") : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: "green", bold: true }, "\u{1F916} Codex Agent Running \u2022 Ctrl-C to exit")), process.env.DEBUG && logPath && /* @__PURE__ */ React.createElement(Text, { color: "gray", dimColor: true }, "Debug logs: ", logPath))
|
|
16413
|
+
));
|
|
16414
|
+
};
|
|
16415
|
+
|
|
16416
|
+
function createOfflineSessionStub(sessionTag) {
|
|
16123
16417
|
return {
|
|
16124
|
-
|
|
16125
|
-
|
|
16126
|
-
|
|
16127
|
-
|
|
16128
|
-
|
|
16418
|
+
sessionId: `offline-${sessionTag}`,
|
|
16419
|
+
sendCodexMessage: () => {
|
|
16420
|
+
},
|
|
16421
|
+
sendAgentMessage: () => {
|
|
16422
|
+
},
|
|
16423
|
+
sendAgentUsageSnapshot: () => false,
|
|
16424
|
+
sendClaudeSessionMessage: () => {
|
|
16425
|
+
},
|
|
16426
|
+
keepAlive: () => {
|
|
16427
|
+
},
|
|
16428
|
+
sendSessionEvent: () => {
|
|
16429
|
+
},
|
|
16430
|
+
sendSessionDeath: () => {
|
|
16431
|
+
},
|
|
16432
|
+
updateLifecycleState: () => {
|
|
16433
|
+
},
|
|
16434
|
+
requestControlTransfer: async () => {
|
|
16435
|
+
},
|
|
16436
|
+
flush: async () => {
|
|
16437
|
+
},
|
|
16438
|
+
close: async () => {
|
|
16439
|
+
},
|
|
16440
|
+
updateMetadata: () => {
|
|
16441
|
+
},
|
|
16442
|
+
updateAgentState: () => {
|
|
16443
|
+
},
|
|
16444
|
+
onUserMessage: () => {
|
|
16445
|
+
},
|
|
16446
|
+
onFileEvent: () => {
|
|
16447
|
+
},
|
|
16448
|
+
trackAttachmentDownload: () => {
|
|
16449
|
+
},
|
|
16450
|
+
drainAttachmentsForUserMessage: async () => [],
|
|
16451
|
+
rpcHandlerManager: {
|
|
16452
|
+
registerHandler: () => {
|
|
16453
|
+
}
|
|
16454
|
+
}
|
|
16129
16455
|
};
|
|
16130
16456
|
}
|
|
16131
|
-
|
|
16132
|
-
|
|
16133
|
-
const
|
|
16134
|
-
|
|
16135
|
-
|
|
16136
|
-
|
|
16137
|
-
|
|
16138
|
-
|
|
16139
|
-
|
|
16457
|
+
|
|
16458
|
+
function setupOfflineReconnection(opts) {
|
|
16459
|
+
const { api, sessionTag, metadata, state, response, onSessionSwap } = opts;
|
|
16460
|
+
let session;
|
|
16461
|
+
let reconnectionHandle = null;
|
|
16462
|
+
if (!response) {
|
|
16463
|
+
session = createOfflineSessionStub(sessionTag);
|
|
16464
|
+
reconnectionHandle = startOfflineReconnection({
|
|
16465
|
+
serverUrl: configuration.serverUrl,
|
|
16466
|
+
onReconnected: async () => {
|
|
16467
|
+
const resp = await api.getOrCreateSession({ tag: sessionTag, metadata, state });
|
|
16468
|
+
if (!resp) throw new Error("Server unavailable");
|
|
16469
|
+
const realSession = api.sessionSyncClient(resp);
|
|
16470
|
+
onSessionSwap(realSession);
|
|
16471
|
+
return realSession;
|
|
16472
|
+
},
|
|
16473
|
+
onNotify: (msg) => {
|
|
16474
|
+
console.log(msg);
|
|
16475
|
+
}
|
|
16476
|
+
});
|
|
16477
|
+
return { session, reconnectionHandle, isOffline: true };
|
|
16478
|
+
} else {
|
|
16479
|
+
session = api.sessionSyncClient(response, opts.sessionClientOptions);
|
|
16480
|
+
return { session, reconnectionHandle: null, isOffline: false };
|
|
16140
16481
|
}
|
|
16141
|
-
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
args: toolLikeMessage.input && typeof toolLikeMessage.input === "object" ? toolLikeMessage.input : {}
|
|
16150
|
-
}, opts)];
|
|
16482
|
+
}
|
|
16483
|
+
|
|
16484
|
+
function resolveCodexExecutionPolicy(permissionMode, sandboxManagedByHappy) {
|
|
16485
|
+
if (sandboxManagedByHappy) {
|
|
16486
|
+
return {
|
|
16487
|
+
approvalPolicy: "never",
|
|
16488
|
+
sandbox: "danger-full-access"
|
|
16489
|
+
};
|
|
16151
16490
|
}
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16491
|
+
const approvalPolicy = (() => {
|
|
16492
|
+
switch (permissionMode) {
|
|
16493
|
+
// Codex native modes
|
|
16494
|
+
case "default":
|
|
16495
|
+
return "untrusted";
|
|
16496
|
+
// Ask for non-trusted commands
|
|
16497
|
+
case "read-only":
|
|
16498
|
+
return "never";
|
|
16499
|
+
// Never ask, read-only enforced by sandbox
|
|
16500
|
+
case "safe-yolo":
|
|
16501
|
+
return "on-request";
|
|
16502
|
+
// Workspace auto-run; request approval for escalation (0.153 removed on-failure)
|
|
16503
|
+
case "yolo":
|
|
16504
|
+
return "never";
|
|
16505
|
+
// Full YOLO: never interrupt for approvals
|
|
16506
|
+
// Defensive fallback for Claude-specific modes (backward compatibility)
|
|
16507
|
+
case "bypassPermissions":
|
|
16508
|
+
return "never";
|
|
16509
|
+
// Full access: map to yolo behavior
|
|
16510
|
+
case "acceptEdits":
|
|
16511
|
+
return "on-request";
|
|
16512
|
+
// Let model decide (closest to auto-approve edits)
|
|
16513
|
+
case "plan":
|
|
16514
|
+
return "untrusted";
|
|
16515
|
+
// Conservative: ask for non-trusted
|
|
16516
|
+
default:
|
|
16517
|
+
return "untrusted";
|
|
16161
16518
|
}
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
16519
|
+
})();
|
|
16520
|
+
const sandbox = (() => {
|
|
16521
|
+
switch (permissionMode) {
|
|
16522
|
+
// Codex native modes
|
|
16523
|
+
case "default":
|
|
16524
|
+
return "workspace-write";
|
|
16525
|
+
// Can write in workspace
|
|
16526
|
+
case "read-only":
|
|
16527
|
+
return "read-only";
|
|
16528
|
+
// Read-only filesystem
|
|
16529
|
+
case "safe-yolo":
|
|
16530
|
+
return "workspace-write";
|
|
16531
|
+
// Can write in workspace
|
|
16532
|
+
case "yolo":
|
|
16533
|
+
return "danger-full-access";
|
|
16534
|
+
// Full system access
|
|
16535
|
+
// Defensive fallback for Claude-specific modes
|
|
16536
|
+
case "bypassPermissions":
|
|
16537
|
+
return "danger-full-access";
|
|
16538
|
+
// Full access: map to yolo
|
|
16539
|
+
case "acceptEdits":
|
|
16540
|
+
return "workspace-write";
|
|
16541
|
+
// Can edit files in workspace
|
|
16542
|
+
case "plan":
|
|
16543
|
+
return "workspace-write";
|
|
16544
|
+
// Can write for planning
|
|
16545
|
+
default:
|
|
16546
|
+
return "workspace-write";
|
|
16547
|
+
}
|
|
16548
|
+
})();
|
|
16549
|
+
return { approvalPolicy, sandbox };
|
|
16169
16550
|
}
|
|
16170
16551
|
|
|
16171
16552
|
async function resumeExistingThread(opts) {
|
|
@@ -16637,6 +17018,13 @@ async function runCodex(opts) {
|
|
|
16637
17018
|
process.exit(1);
|
|
16638
17019
|
}
|
|
16639
17020
|
};
|
|
17021
|
+
session.rpcHandlerManager.registerHandler("codex-child-read", async (args) => {
|
|
17022
|
+
try {
|
|
17023
|
+
return await readChildThread(client, args?.threadId);
|
|
17024
|
+
} catch (error) {
|
|
17025
|
+
return { error: error instanceof Error ? error.message : "Child read failed" };
|
|
17026
|
+
}
|
|
17027
|
+
});
|
|
16640
17028
|
session.rpcHandlerManager.registerHandler("abort", handleAbort);
|
|
16641
17029
|
const disposeTermination = registerKillSessionHandler(session.rpcHandlerManager, handleKillSession, session, { processSignals: true });
|
|
16642
17030
|
const messageBuffer = new MessageBuffer();
|
|
@@ -17177,7 +17565,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17177
17565
|
process.exit(0);
|
|
17178
17566
|
} else if (subcommand === "install-terminal-hooks") {
|
|
17179
17567
|
try {
|
|
17180
|
-
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-
|
|
17568
|
+
const { installTerminalHooks, parseTerminalHooksArgs, TERMINAL_HOOKS_HELP } = await import('./installTerminalHooks-D0YRu_p-.mjs');
|
|
17181
17569
|
const command = parseTerminalHooksArgs(args.slice(1));
|
|
17182
17570
|
if (command.action === "help") {
|
|
17183
17571
|
console.log(TERMINAL_HOOKS_HELP);
|
|
@@ -17194,7 +17582,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17194
17582
|
}
|
|
17195
17583
|
} else if (subcommand === "spawn") {
|
|
17196
17584
|
try {
|
|
17197
|
-
const { handleSpawnCommand } = await import('./spawn-
|
|
17585
|
+
const { handleSpawnCommand } = await import('./spawn-DuQXydqI.mjs');
|
|
17198
17586
|
await handleSpawnCommand(args.slice(1));
|
|
17199
17587
|
} catch (error) {
|
|
17200
17588
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17206,7 +17594,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17206
17594
|
return;
|
|
17207
17595
|
} else if (subcommand === "sessions") {
|
|
17208
17596
|
try {
|
|
17209
|
-
const { handleSessionsCommand } = await import('./sessions-
|
|
17597
|
+
const { handleSessionsCommand } = await import('./sessions-BtrLzv5U.mjs');
|
|
17210
17598
|
await handleSessionsCommand(args.slice(1));
|
|
17211
17599
|
} catch (error) {
|
|
17212
17600
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17218,7 +17606,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17218
17606
|
return;
|
|
17219
17607
|
} else if (subcommand === "send") {
|
|
17220
17608
|
try {
|
|
17221
|
-
const { handleSendCommand } = await import('./send-
|
|
17609
|
+
const { handleSendCommand } = await import('./send-BHquPaeG.mjs');
|
|
17222
17610
|
await handleSendCommand(args.slice(1));
|
|
17223
17611
|
} catch (error) {
|
|
17224
17612
|
console.error(chalk.red("Error:"), error instanceof Error ? error.message : "Unknown error");
|
|
@@ -17324,9 +17712,9 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17324
17712
|
if (geminiSubcommand === "project" && args[2] === "set" && args[3]) {
|
|
17325
17713
|
const projectId = args[3];
|
|
17326
17714
|
try {
|
|
17327
|
-
const { saveGoogleCloudProjectToConfig } = await import('./config-
|
|
17328
|
-
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-
|
|
17329
|
-
const { ApiClient: ApiClient2 } = await import('./types-
|
|
17715
|
+
const { saveGoogleCloudProjectToConfig } = await import('./config-VqHQ67d8.mjs');
|
|
17716
|
+
const { readCredentialsForConfiguredRelay: readCredentialsForConfiguredRelay2 } = await import('./limits-kjHZUlSp.mjs').then(function (n) { return n.W; });
|
|
17717
|
+
const { ApiClient: ApiClient2 } = await import('./types-BKrZubg8.mjs').then(function (n) { return n.a0; });
|
|
17330
17718
|
let userEmail = void 0;
|
|
17331
17719
|
try {
|
|
17332
17720
|
const credentials = await readCredentialsForConfiguredRelay2();
|
|
@@ -17357,7 +17745,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17357
17745
|
}
|
|
17358
17746
|
if (geminiSubcommand === "project" && args[2] === "get") {
|
|
17359
17747
|
try {
|
|
17360
|
-
const { readGeminiLocalConfig } = await import('./config-
|
|
17748
|
+
const { readGeminiLocalConfig } = await import('./config-VqHQ67d8.mjs');
|
|
17361
17749
|
const config = readGeminiLocalConfig();
|
|
17362
17750
|
if (config.googleCloudProject) {
|
|
17363
17751
|
console.log(`Current Google Cloud Project: ${config.googleCloudProject}`);
|
|
@@ -17397,7 +17785,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17397
17785
|
process.exit(0);
|
|
17398
17786
|
}
|
|
17399
17787
|
try {
|
|
17400
|
-
const { runGemini } = await import('./runGemini-
|
|
17788
|
+
const { runGemini } = await import('./runGemini-DufGnnl2.mjs');
|
|
17401
17789
|
let startedBy = void 0;
|
|
17402
17790
|
for (let i = 1; i < args.length; i++) {
|
|
17403
17791
|
if (args[i] === "--started-by") {
|
|
@@ -17427,7 +17815,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17427
17815
|
}
|
|
17428
17816
|
};
|
|
17429
17817
|
try {
|
|
17430
|
-
const { runAcp, resolveAcpAgentConfig } = await import('./index-
|
|
17818
|
+
const { runAcp, resolveAcpAgentConfig } = await import('./index-D3vlFjXy.mjs');
|
|
17431
17819
|
const parsed = parsePiRunnerArgs(args.slice(1));
|
|
17432
17820
|
const resolved = resolveAcpAgentConfig(["pi", ...parsed.passthrough]);
|
|
17433
17821
|
const { credentials } = await authAndSetupMachineIfNeeded();
|
|
@@ -17455,7 +17843,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17455
17843
|
return;
|
|
17456
17844
|
} else if (subcommand === "acp") {
|
|
17457
17845
|
try {
|
|
17458
|
-
const { runAcp, resolveAcpAgentConfig } = await import('./index-
|
|
17846
|
+
const { runAcp, resolveAcpAgentConfig } = await import('./index-D3vlFjXy.mjs');
|
|
17459
17847
|
let startedBy = void 0;
|
|
17460
17848
|
let verbose = false;
|
|
17461
17849
|
const acpArgs = [];
|
|
@@ -17495,7 +17883,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17495
17883
|
return;
|
|
17496
17884
|
} else if (subcommand === "openclaw") {
|
|
17497
17885
|
try {
|
|
17498
|
-
const { runOpenClaw } = await import('./runOpenClaw-
|
|
17886
|
+
const { runOpenClaw } = await import('./runOpenClaw-DAl2nnjB.mjs');
|
|
17499
17887
|
let startedBy = void 0;
|
|
17500
17888
|
let verbose = false;
|
|
17501
17889
|
let gatewayUrl;
|
|
@@ -17546,7 +17934,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17546
17934
|
return;
|
|
17547
17935
|
} else if (subcommand === "todo") {
|
|
17548
17936
|
try {
|
|
17549
|
-
const { handleTodoCommand } = await import('./todo-
|
|
17937
|
+
const { handleTodoCommand } = await import('./todo-BiLIZH4t.mjs');
|
|
17550
17938
|
await handleTodoCommand(args.slice(1));
|
|
17551
17939
|
} catch (error) {
|
|
17552
17940
|
console.error(JSON.stringify({ error: error instanceof Error ? error.message : "Todo command failed" }));
|
|
@@ -17555,7 +17943,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17555
17943
|
return;
|
|
17556
17944
|
} else if (subcommand === "teams") {
|
|
17557
17945
|
try {
|
|
17558
|
-
const { handleTeamsCommand } = await import('./teams-
|
|
17946
|
+
const { handleTeamsCommand } = await import('./teams-D5HxnyaW.mjs');
|
|
17559
17947
|
await handleTeamsCommand(args.slice(1));
|
|
17560
17948
|
} catch (error) {
|
|
17561
17949
|
console.error(error instanceof Error ? error.message : "Teams command failed");
|
|
@@ -17564,7 +17952,7 @@ Conversation history is preserved on the server, but in-flight tool calls are in
|
|
|
17564
17952
|
return;
|
|
17565
17953
|
} else if (subcommand === "mcp" && args.length === 1) {
|
|
17566
17954
|
try {
|
|
17567
|
-
const { handleMcpCommand } = await import('./mcp-
|
|
17955
|
+
const { handleMcpCommand } = await import('./mcp-CgjEHlky.mjs');
|
|
17568
17956
|
await handleMcpCommand();
|
|
17569
17957
|
} catch (error) {
|
|
17570
17958
|
process.stderr.write(`[very-happy mcp] Fatal: ${error instanceof Error ? error.message : String(error)}
|