@openclaw/codex 2026.7.2-beta.5 → 2026.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{app-server-policy-BmDgseo_.js → app-server-policy-zcBDc1JU.js} +1 -1
- package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
- package/dist/binding-connection-I5Bo-AsX.js +33 -0
- package/dist/{bounded-turn-FEbUXoYT.js → bounded-turn-qgZmzy_4.js} +71 -63
- package/dist/{capabilities-BHRSI6Cn.js → capabilities-CybGI1oz.js} +8 -2
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-ico4ZFnk.js → command-formatters-B1AEz8ZA.js} +18 -18
- package/dist/{command-handlers-CRmnwI2N.js → command-handlers-Db8pu_ds.js} +484 -95
- package/dist/{compact-dEN49UH6.js → compact-LspQaGfl.js} +213 -199
- package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
- package/dist/doctor-contract-api.js +40 -43
- package/dist/{dynamic-tools-LIt9ojep.js → dynamic-tools-BVdyWkUD.js} +2984 -1221
- package/dist/effective-mcp-catalog-DIIhAtOZ.js +119 -0
- package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
- package/dist/{attempt-notifications-CMR2_fmR.js → event-projector-usage-e5huIQWu.js} +113 -29
- package/dist/harness.js +83 -13
- package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
- package/dist/index.js +703 -329
- package/dist/isolated-completion-C58NbL40.js +75 -0
- package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
- package/dist/media-understanding-provider.js +5 -5
- package/dist/{models-BA01NEez.js → models-BFlpn1Ex.js} +12 -17
- package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
- package/dist/{plugin-app-cache-key-DSXX-rXO.js → plugin-app-cache-key-C3H2CVp6.js} +135 -42
- package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
- package/dist/{computer-use-BjGylO-E.js → plugin-metadata-cache-CNJPj4_b.js} +211 -113
- package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
- package/dist/{rate-limits-S_PXs0ul.js → rate-limits-DZXmOL8-.js} +17 -35
- package/dist/{request-CXpPennK.js → request-BRl5_UfX.js} +4 -4
- package/dist/{run-attempt-C5GTPJOE.js → run-attempt-P8BdxC1I.js} +1996 -5728
- package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-BVSPdd6g.js} +91 -40
- package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-Cmwc9eIk.js} +10 -13
- package/dist/{session-binding-DlVQ-Pxs.js → session-binding-DPxIvMgE.js} +305 -271
- package/dist/{session-catalog-CP2oCcEb.js → session-catalog-xwNi_SDM.js} +418 -144
- package/dist/{session-cli-CqZ0Fp2a.js → session-cli-7hgXZDV7.js} +1 -1
- package/dist/session-retirement-DoMMJFKm.js +36 -0
- package/dist/{session-upstream-marker-QE-m5QFG.js → session-upstream-marker-CrIFX06J.js} +1 -1
- package/dist/{settled-turn-finalizer-CA_2QXIE.js → settled-turn-finalizer-BRWBO4MO.js} +36 -15
- package/dist/{shared-client-BVKE40c-.js → shared-client-kIBbGjk4.js} +721 -240
- package/dist/{side-question-CO12sHxo.js → side-question-9HAL26l9.js} +102 -53
- package/dist/{thread-lifecycle-DRg_FdzC.js → thread-lifecycle-Bu1BTDXr.js} +1355 -660
- package/dist/thread-ownership-6kOQHlyC.js +92 -0
- package/dist/thread-resume-CPrJNQVM.js +30 -0
- package/dist/{transcript-mirror-uKA-A4tB.js → transcript-mirror-B9gGgnJS.js} +202 -182
- package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
- package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
- package/dist/{upstream-session-fork-_Vrz4WJr.js → upstream-session-fork-CE-_LdnP.js} +5 -5
- package/dist/{usage-UpVbdX5G.js → usage-CHlV0e-o.js} +3 -3
- package/dist/{web-search-provider.runtime-CaQMYDbp.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
- package/openclaw.plugin.json +18 -3
- package/package.json +6 -7
- package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
- package/dist/conversation-control-DE7d9DOj.js +0 -287
- package/dist/event-projector-usage-tvgb1NCX.js +0 -69
- package/dist/notification-correlation-Bj_Ak_jI.js +0 -67
- package/dist/protocol-validators-Dm4eXZmt.js +0 -6337
- package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
- package/dist/realtime-voice-api.js +0 -2
|
@@ -1,32 +1,129 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as listCodexAppServerModels } from "./models-
|
|
5
|
-
import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-
|
|
6
|
-
import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-
|
|
7
|
-
import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-
|
|
8
|
-
import { r as formatCodexDisplayText } from "./command-formatters-
|
|
9
|
-
import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-
|
|
10
|
-
import { n as requestCodexAppServerClientJson, r as requestCodexAppServerJson } from "./request-
|
|
11
|
-
import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-
|
|
1
|
+
import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, g as resolveCodexAppServerRuntimeOptions, y as resolveCodexSupervisionAppServerRuntimeOptions } from "./session-binding-DPxIvMgE.js";
|
|
2
|
+
import { R as resolveCodexAppServerClientInstanceId, _ as withTimeout, a as getLeasedSharedCodexAppServerClient, at as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, ct as isJsonObject, dt as resolveCodexAppServerLocalHomeDir, it as CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES, l as releaseLeasedSharedCodexAppServerClient, z as CodexAppServerRpcError } from "./shared-client-kIBbGjk4.js";
|
|
3
|
+
import { n as assertCodexThreadForkParams } from "./protocol-validators-B4F7L8Rf.js";
|
|
4
|
+
import { n as listCodexAppServerModels } from "./models-BFlpn1Ex.js";
|
|
5
|
+
import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-C3H2CVp6.js";
|
|
6
|
+
import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-B9gGgnJS.js";
|
|
7
|
+
import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-CybGI1oz.js";
|
|
8
|
+
import { r as formatCodexDisplayText } from "./command-formatters-B1AEz8ZA.js";
|
|
9
|
+
import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-CW2GuupV.js";
|
|
10
|
+
import { i as withCodexAppServerJsonClient, n as requestCodexAppServerClientJson, r as requestCodexAppServerJson } from "./request-BRl5_UfX.js";
|
|
11
|
+
import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-CrIFX06J.js";
|
|
12
|
+
import { asFiniteNumber, isRecord, normalizeBoundedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
12
13
|
import fs from "node:fs/promises";
|
|
13
14
|
import path from "node:path";
|
|
14
15
|
import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
|
|
15
16
|
import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
|
|
16
17
|
import { createHash } from "node:crypto";
|
|
17
|
-
import { listAgentIds, resolveAllowedModelRef, resolveDefaultAgentDir, resolveDefaultAgentId, resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
|
|
18
18
|
import os from "node:os";
|
|
19
|
+
import { listAgentIds, resolveAllowedModelRef, resolveDefaultAgentDir, resolveDefaultAgentId, resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
|
|
19
20
|
import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
|
|
20
|
-
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
21
21
|
import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
|
|
22
22
|
import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
|
|
23
23
|
import { spawn } from "node:child_process";
|
|
24
24
|
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
25
25
|
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
26
|
+
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
26
27
|
import process$1 from "node:process";
|
|
27
28
|
import { listSessionCatalogEntries } from "openclaw/plugin-sdk/session-catalog";
|
|
28
|
-
import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
|
|
29
29
|
import { decodeNodePtyResumeParams, resolveNodeHostExecutable, runNodePtyCommand } from "openclaw/plugin-sdk/node-host";
|
|
30
|
+
//#region extensions/codex/src/jsonl-lines.ts
|
|
31
|
+
const JSONL_STREAM_THRESHOLD_BYTES = 4 * 1024 * 1024;
|
|
32
|
+
const JSONL_READ_CHUNK_BYTES = 1024 * 1024;
|
|
33
|
+
async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTES) {
|
|
34
|
+
let size;
|
|
35
|
+
try {
|
|
36
|
+
size = (await fs.stat(file)).size;
|
|
37
|
+
} catch {
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
lineCount: 0
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (size <= JSONL_STREAM_THRESHOLD_BYTES) {
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = await fs.readFile(file, "utf8");
|
|
47
|
+
} catch {
|
|
48
|
+
return {
|
|
49
|
+
ok: false,
|
|
50
|
+
lineCount: 0
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (content.length === 0) return {
|
|
54
|
+
ok: true,
|
|
55
|
+
lineCount: 0
|
|
56
|
+
};
|
|
57
|
+
let lineCount = 0;
|
|
58
|
+
for (const line of content.split(/\r?\n/u)) {
|
|
59
|
+
lineCount += 1;
|
|
60
|
+
if (visitor(line) === false) break;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
lineCount
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
let handle;
|
|
68
|
+
try {
|
|
69
|
+
handle = await fs.open(file, "r");
|
|
70
|
+
} catch {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
lineCount: 0
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const buffer = Buffer.allocUnsafe(chunkBytes);
|
|
77
|
+
const decoder = new TextDecoder();
|
|
78
|
+
let pendingFragments = [];
|
|
79
|
+
let lineCount = 0;
|
|
80
|
+
try {
|
|
81
|
+
while (true) {
|
|
82
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
|
|
83
|
+
if (bytesRead === 0) break;
|
|
84
|
+
const content = decoder.decode(buffer.subarray(0, bytesRead), { stream: true });
|
|
85
|
+
let lineStart = 0;
|
|
86
|
+
while (true) {
|
|
87
|
+
const newline = content.indexOf("\n", lineStart);
|
|
88
|
+
if (newline === -1) break;
|
|
89
|
+
let rawLine = content.slice(lineStart, newline);
|
|
90
|
+
if (pendingFragments.length > 0) {
|
|
91
|
+
pendingFragments.push(rawLine);
|
|
92
|
+
rawLine = pendingFragments.join("");
|
|
93
|
+
pendingFragments = [];
|
|
94
|
+
}
|
|
95
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
96
|
+
lineCount += 1;
|
|
97
|
+
if (visitor(line) === false) return {
|
|
98
|
+
ok: true,
|
|
99
|
+
lineCount
|
|
100
|
+
};
|
|
101
|
+
lineStart = newline + 1;
|
|
102
|
+
}
|
|
103
|
+
if (lineStart < content.length) pendingFragments.push(content.slice(lineStart));
|
|
104
|
+
}
|
|
105
|
+
const decoderTail = decoder.decode();
|
|
106
|
+
if (decoderTail.length > 0) pendingFragments.push(decoderTail);
|
|
107
|
+
if (pendingFragments.length > 0) {
|
|
108
|
+
const rawLine = pendingFragments.join("");
|
|
109
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
110
|
+
lineCount += 1;
|
|
111
|
+
visitor(line);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
lineCount
|
|
116
|
+
};
|
|
117
|
+
} catch {
|
|
118
|
+
return {
|
|
119
|
+
ok: false,
|
|
120
|
+
lineCount: 0
|
|
121
|
+
};
|
|
122
|
+
} finally {
|
|
123
|
+
await handle.close().catch(() => void 0);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
30
127
|
//#region extensions/codex/src/node-cli-sessions.ts
|
|
31
128
|
const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
|
|
32
129
|
const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
|
|
@@ -249,20 +346,18 @@ function resolveCodexCliResumeSpawnInvocation(args, runtime = DEFAULT_RESUME_SPA
|
|
|
249
346
|
}
|
|
250
347
|
async function readHistorySessions(codexHome) {
|
|
251
348
|
const summaries = /* @__PURE__ */ new Map();
|
|
252
|
-
|
|
253
|
-
if (!content) return summaries;
|
|
254
|
-
for (const line of content.split(/\r?\n/u)) {
|
|
349
|
+
if (!(await visitJsonlLines(path.join(codexHome, "history.jsonl"), (line) => {
|
|
255
350
|
const trimmed = line.trim();
|
|
256
|
-
if (!trimmed)
|
|
351
|
+
if (!trimmed) return;
|
|
257
352
|
let parsed;
|
|
258
353
|
try {
|
|
259
354
|
parsed = JSON.parse(trimmed);
|
|
260
355
|
} catch {
|
|
261
|
-
|
|
356
|
+
return;
|
|
262
357
|
}
|
|
263
|
-
if (!isRecord(parsed) || typeof parsed.session_id !== "string")
|
|
358
|
+
if (!isRecord(parsed) || typeof parsed.session_id !== "string") return;
|
|
264
359
|
const sessionId = parsed.session_id.trim();
|
|
265
|
-
if (!sessionId)
|
|
360
|
+
if (!sessionId) return;
|
|
266
361
|
const entry = summaries.get(sessionId) ?? {
|
|
267
362
|
sessionId,
|
|
268
363
|
messageCount: 0
|
|
@@ -271,7 +366,7 @@ async function readHistorySessions(codexHome) {
|
|
|
271
366
|
if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
|
|
272
367
|
if (typeof parsed.ts === "number") entry.updatedAt = timestampMsToIsoString(parsed.ts * 1e3) ?? entry.updatedAt;
|
|
273
368
|
summaries.set(sessionId, entry);
|
|
274
|
-
}
|
|
369
|
+
})).ok) return /* @__PURE__ */ new Map();
|
|
275
370
|
return summaries;
|
|
276
371
|
}
|
|
277
372
|
async function hydrateSessionFiles(codexHome, summaries) {
|
|
@@ -309,35 +404,35 @@ async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
|
|
|
309
404
|
}
|
|
310
405
|
}
|
|
311
406
|
async function readSessionFileSummary(file) {
|
|
312
|
-
const content = await readFileIfExists(file);
|
|
313
|
-
if (!content) return null;
|
|
314
407
|
let sessionId = "";
|
|
315
408
|
let cwd;
|
|
316
409
|
let updatedAt;
|
|
317
410
|
let lastMessage;
|
|
318
411
|
let messageCount = 0;
|
|
319
|
-
|
|
412
|
+
const result = await visitJsonlLines(file, (line) => {
|
|
320
413
|
const trimmed = line.trim();
|
|
321
|
-
if (!trimmed)
|
|
414
|
+
if (!trimmed) return;
|
|
322
415
|
let parsed;
|
|
323
416
|
try {
|
|
324
417
|
parsed = JSON.parse(trimmed);
|
|
325
418
|
} catch {
|
|
326
|
-
|
|
419
|
+
return;
|
|
327
420
|
}
|
|
328
|
-
if (!isRecord(parsed))
|
|
421
|
+
if (!isRecord(parsed)) return;
|
|
329
422
|
if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
|
|
330
423
|
if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
|
|
331
424
|
if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
|
|
332
425
|
if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
|
|
333
|
-
|
|
426
|
+
return;
|
|
334
427
|
}
|
|
335
428
|
const messageText = readResponseItemMessageText(parsed);
|
|
336
429
|
if (messageText) {
|
|
337
430
|
messageCount += 1;
|
|
338
431
|
lastMessage = truncateText(messageText, 140);
|
|
339
432
|
}
|
|
340
|
-
}
|
|
433
|
+
});
|
|
434
|
+
if (!result.ok) return null;
|
|
435
|
+
if (result.lineCount === 0) return null;
|
|
341
436
|
if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
|
|
342
437
|
if (!sessionId) return null;
|
|
343
438
|
return {
|
|
@@ -445,15 +540,12 @@ function readRecordParam(paramsJSON) {
|
|
|
445
540
|
function resolveCodexHome() {
|
|
446
541
|
return process$1.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
|
|
447
542
|
}
|
|
448
|
-
async function readFileIfExists(file) {
|
|
449
|
-
try {
|
|
450
|
-
return await fs.readFile(file, "utf8");
|
|
451
|
-
} catch {
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
543
|
async function readFirstLine(file) {
|
|
456
|
-
|
|
544
|
+
let firstLine;
|
|
545
|
+
return (await visitJsonlLines(file, (line) => {
|
|
546
|
+
firstLine = line;
|
|
547
|
+
return false;
|
|
548
|
+
}, 65536)).ok ? firstLine : void 0;
|
|
457
549
|
}
|
|
458
550
|
async function readFileMtimeIso(file) {
|
|
459
551
|
try {
|
|
@@ -487,61 +579,6 @@ function formatNodeLabel(node) {
|
|
|
487
579
|
].filter(Boolean).join(" / ") || "node";
|
|
488
580
|
}
|
|
489
581
|
//#endregion
|
|
490
|
-
//#region extensions/codex/src/app-server/thread-archive-guard.ts
|
|
491
|
-
const DESCENDANT_PAGE_LIMIT = 100;
|
|
492
|
-
const MAX_DESCENDANT_PAGES = 100;
|
|
493
|
-
const MAX_THREAD_ID_LENGTH = 256;
|
|
494
|
-
const MAX_CURSOR_LENGTH$1 = 4096;
|
|
495
|
-
function readBoundedId(value) {
|
|
496
|
-
if (typeof value !== "string") return;
|
|
497
|
-
const normalized = value.trim();
|
|
498
|
-
return normalized && normalized.length <= MAX_THREAD_ID_LENGTH ? normalized : void 0;
|
|
499
|
-
}
|
|
500
|
-
function readNextCursor(value) {
|
|
501
|
-
if (value === void 0 || value === null) return;
|
|
502
|
-
if (typeof value !== "string" || !value.trim() || value.length > MAX_CURSOR_LENGTH$1) throw new Error("Codex app-server returned an invalid descendant-list cursor");
|
|
503
|
-
return value;
|
|
504
|
-
}
|
|
505
|
-
/**
|
|
506
|
-
* Native archive includes the spawned subtree. Enumerate that same subtree first so an
|
|
507
|
-
* OpenClaw-owned descendant cannot be stopped as an undocumented side effect.
|
|
508
|
-
*/
|
|
509
|
-
async function assertCodexArchiveDescendantsUnowned(params) {
|
|
510
|
-
const ancestorThreadId = readBoundedId(params.threadId);
|
|
511
|
-
if (!ancestorThreadId) throw new Error("cannot verify Codex archive descendants for an invalid thread id");
|
|
512
|
-
const seenCursors = /* @__PURE__ */ new Set();
|
|
513
|
-
const seenThreadIds = /* @__PURE__ */ new Set([ancestorThreadId]);
|
|
514
|
-
let cursor;
|
|
515
|
-
for (let pageIndex = 0; pageIndex < MAX_DESCENDANT_PAGES; pageIndex += 1) {
|
|
516
|
-
const response = await params.listPage({
|
|
517
|
-
ancestorThreadId,
|
|
518
|
-
archived: false,
|
|
519
|
-
limit: DESCENDANT_PAGE_LIMIT,
|
|
520
|
-
sortKey: "created_at",
|
|
521
|
-
sortDirection: "desc",
|
|
522
|
-
useStateDbOnly: true,
|
|
523
|
-
...cursor ? { cursor } : {}
|
|
524
|
-
});
|
|
525
|
-
if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex app-server returned an invalid descendant-list response");
|
|
526
|
-
if (response.data.length > DESCENDANT_PAGE_LIMIT) throw new Error("Codex app-server exceeded the descendant-list page limit");
|
|
527
|
-
for (const value of response.data) {
|
|
528
|
-
if (!isJsonObject(value)) throw new Error("Codex app-server returned an invalid descendant thread");
|
|
529
|
-
const descendantThreadId = readBoundedId(value.id);
|
|
530
|
-
if (!descendantThreadId) throw new Error("Codex app-server returned a descendant without a valid thread id");
|
|
531
|
-
if (seenThreadIds.has(descendantThreadId)) throw new Error("Codex app-server returned a cyclic descendant thread list");
|
|
532
|
-
seenThreadIds.add(descendantThreadId);
|
|
533
|
-
await params.assertDescendantIdle(descendantThreadId);
|
|
534
|
-
if (await params.bindingStore.hasOtherThreadOwner(descendantThreadId)) throw new Error("cannot archive a Codex thread while a spawned descendant is owned by an OpenClaw session");
|
|
535
|
-
}
|
|
536
|
-
const nextCursor = readNextCursor(response.nextCursor);
|
|
537
|
-
if (!nextCursor) return;
|
|
538
|
-
if (seenCursors.has(nextCursor)) throw new Error("Codex app-server returned a repeated descendant-list cursor");
|
|
539
|
-
seenCursors.add(nextCursor);
|
|
540
|
-
cursor = nextCursor;
|
|
541
|
-
}
|
|
542
|
-
throw new Error("Codex descendant enumeration exceeded its safety limit");
|
|
543
|
-
}
|
|
544
|
-
//#endregion
|
|
545
582
|
//#region extensions/codex/src/command-rpc.ts
|
|
546
583
|
function requestOptions(pluginConfig, limit, config, agentDir) {
|
|
547
584
|
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
@@ -554,10 +591,8 @@ function requestOptions(pluginConfig, limit, config, agentDir) {
|
|
|
554
591
|
};
|
|
555
592
|
}
|
|
556
593
|
async function codexControlRequest(pluginConfig, method, requestParams, options = {}) {
|
|
557
|
-
const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
558
|
-
|
|
559
|
-
method,
|
|
560
|
-
requestParams,
|
|
594
|
+
const runtime = options.startOptions ? resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig }) : resolveCodexAppServerRuntimeOptions({ pluginConfig });
|
|
595
|
+
const controlRequestOptions = {
|
|
561
596
|
timeoutMs: options.timeoutMs ?? runtime.requestTimeoutMs,
|
|
562
597
|
startOptions: options.startOptions ?? runtime.start,
|
|
563
598
|
config: options.config,
|
|
@@ -566,6 +601,19 @@ async function codexControlRequest(pluginConfig, method, requestParams, options
|
|
|
566
601
|
authProfileId: options.authProfileId,
|
|
567
602
|
agentDir: options.agentDir,
|
|
568
603
|
isolated: options.isolated
|
|
604
|
+
};
|
|
605
|
+
if (options.onResponse) return await withCodexAppServerJsonClient(controlRequestOptions, async (request, client) => {
|
|
606
|
+
const response = await request({
|
|
607
|
+
method,
|
|
608
|
+
requestParams
|
|
609
|
+
});
|
|
610
|
+
await options.onResponse(response, client);
|
|
611
|
+
return response;
|
|
612
|
+
});
|
|
613
|
+
return await requestCodexAppServerJson({
|
|
614
|
+
method,
|
|
615
|
+
requestParams,
|
|
616
|
+
...controlRequestOptions
|
|
569
617
|
});
|
|
570
618
|
}
|
|
571
619
|
async function safeCodexControlRequest(pluginConfig, method, requestParams, options = {}) {
|
|
@@ -616,6 +664,56 @@ async function safeValue(read) {
|
|
|
616
664
|
}
|
|
617
665
|
}
|
|
618
666
|
//#endregion
|
|
667
|
+
//#region extensions/codex/src/app-server/thread-archive-guard.ts
|
|
668
|
+
const DESCENDANT_PAGE_LIMIT = 100;
|
|
669
|
+
const MAX_DESCENDANT_PAGES = 100;
|
|
670
|
+
const MAX_THREAD_ID_LENGTH = 256;
|
|
671
|
+
const MAX_CURSOR_LENGTH$1 = 4096;
|
|
672
|
+
function readNextCursor(value) {
|
|
673
|
+
if (value === void 0 || value === null) return;
|
|
674
|
+
if (typeof value !== "string" || !value.trim() || value.length > MAX_CURSOR_LENGTH$1) throw new Error("Codex app-server returned an invalid descendant-list cursor");
|
|
675
|
+
return value;
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* Native archive includes the spawned subtree. Enumerate that same subtree first so an
|
|
679
|
+
* OpenClaw-owned descendant cannot be stopped as an undocumented side effect.
|
|
680
|
+
*/
|
|
681
|
+
async function assertCodexArchiveDescendantsUnowned(params) {
|
|
682
|
+
const ancestorThreadId = normalizeBoundedOptionalString(params.threadId, MAX_THREAD_ID_LENGTH);
|
|
683
|
+
if (!ancestorThreadId) throw new Error("cannot verify Codex archive descendants for an invalid thread id");
|
|
684
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
685
|
+
const seenThreadIds = /* @__PURE__ */ new Set([ancestorThreadId]);
|
|
686
|
+
let cursor;
|
|
687
|
+
for (let pageIndex = 0; pageIndex < MAX_DESCENDANT_PAGES; pageIndex += 1) {
|
|
688
|
+
const response = await params.listPage({
|
|
689
|
+
ancestorThreadId,
|
|
690
|
+
archived: false,
|
|
691
|
+
limit: DESCENDANT_PAGE_LIMIT,
|
|
692
|
+
sortKey: "created_at",
|
|
693
|
+
sortDirection: "desc",
|
|
694
|
+
useStateDbOnly: true,
|
|
695
|
+
...cursor ? { cursor } : {}
|
|
696
|
+
});
|
|
697
|
+
if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex app-server returned an invalid descendant-list response");
|
|
698
|
+
if (response.data.length > DESCENDANT_PAGE_LIMIT) throw new Error("Codex app-server exceeded the descendant-list page limit");
|
|
699
|
+
for (const value of response.data) {
|
|
700
|
+
if (!isJsonObject(value)) throw new Error("Codex app-server returned an invalid descendant thread");
|
|
701
|
+
const descendantThreadId = normalizeBoundedOptionalString(value.id, MAX_THREAD_ID_LENGTH);
|
|
702
|
+
if (!descendantThreadId) throw new Error("Codex app-server returned a descendant without a valid thread id");
|
|
703
|
+
if (seenThreadIds.has(descendantThreadId)) throw new Error("Codex app-server returned a cyclic descendant thread list");
|
|
704
|
+
seenThreadIds.add(descendantThreadId);
|
|
705
|
+
await params.assertDescendantIdle(descendantThreadId);
|
|
706
|
+
if (await params.bindingStore.hasOtherThreadOwner(descendantThreadId)) throw new Error("cannot archive a Codex thread while a spawned descendant is owned by an OpenClaw session");
|
|
707
|
+
}
|
|
708
|
+
const nextCursor = readNextCursor(response.nextCursor);
|
|
709
|
+
if (!nextCursor) return;
|
|
710
|
+
if (seenCursors.has(nextCursor)) throw new Error("Codex app-server returned a repeated descendant-list cursor");
|
|
711
|
+
seenCursors.add(nextCursor);
|
|
712
|
+
cursor = nextCursor;
|
|
713
|
+
}
|
|
714
|
+
throw new Error("Codex descendant enumeration exceeded its safety limit");
|
|
715
|
+
}
|
|
716
|
+
//#endregion
|
|
619
717
|
//#region extensions/codex/src/session-catalog-create.ts
|
|
620
718
|
const CODEX_AGENT_RUNTIME_ID = "codex";
|
|
621
719
|
const CODEX_CATALOG_DEFAULT_MODEL_REF = "openai/gpt-5.6-sol";
|
|
@@ -722,7 +820,7 @@ function normalizeLimit(value, key) {
|
|
|
722
820
|
if (!Number.isInteger(value) || value < 1 || value > 100) throw new CatalogParamsError(`${key} must be an integer from 1 to 100`);
|
|
723
821
|
return value;
|
|
724
822
|
}
|
|
725
|
-
function
|
|
823
|
+
function readBoundedOptionalString(params, key, maxLength) {
|
|
726
824
|
const value = params[key];
|
|
727
825
|
if (value === void 0) return;
|
|
728
826
|
if (typeof value !== "string") throw new CatalogParamsError(`${key} must be a string`);
|
|
@@ -744,9 +842,9 @@ function readPageParams(value) {
|
|
|
744
842
|
"searchTerm",
|
|
745
843
|
"cwd"
|
|
746
844
|
]));
|
|
747
|
-
const cursor =
|
|
748
|
-
const searchTerm =
|
|
749
|
-
const cwd =
|
|
845
|
+
const cursor = readBoundedOptionalString(params, "cursor", MAX_CURSOR_LENGTH);
|
|
846
|
+
const searchTerm = readBoundedOptionalString(params, "searchTerm", MAX_SEARCH_LENGTH);
|
|
847
|
+
const cwd = readBoundedOptionalString(params, "cwd", MAX_CWD_LENGTH);
|
|
750
848
|
return {
|
|
751
849
|
limit: normalizeLimit(params.limit, "limit"),
|
|
752
850
|
...cursor ? { cursor } : {},
|
|
@@ -763,7 +861,7 @@ function readGatewayParams(value) {
|
|
|
763
861
|
"hostIds",
|
|
764
862
|
"cursors"
|
|
765
863
|
]));
|
|
766
|
-
const search =
|
|
864
|
+
const search = readBoundedOptionalString(params, "search", MAX_SEARCH_LENGTH);
|
|
767
865
|
let hostIds;
|
|
768
866
|
if (params.hostIds !== void 0) {
|
|
769
867
|
if (!Array.isArray(params.hostIds) || params.hostIds.length > 100) throw new CatalogParamsError(`hostIds must contain at most 100 host ids`);
|
|
@@ -803,9 +901,6 @@ function parseJsonParams(paramsJSON) {
|
|
|
803
901
|
throw new Error("Codex session catalog parameters must be valid JSON", { cause: error });
|
|
804
902
|
}
|
|
805
903
|
}
|
|
806
|
-
function readFiniteNumber(value) {
|
|
807
|
-
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
808
|
-
}
|
|
809
904
|
function parseOptionalCatalogString(value, field, maxLength) {
|
|
810
905
|
if (value === void 0) return;
|
|
811
906
|
if (typeof value !== "string" || value.length > maxLength) throw new Error(`Codex session catalog returned an invalid ${field}`);
|
|
@@ -831,9 +926,9 @@ function parseCatalogSession(value, options = {}) {
|
|
|
831
926
|
const cliVersion = parseOptionalCatalogString(value.cliVersion, "CLI version", MAX_METADATA_LENGTH);
|
|
832
927
|
const gitBranch = parseOptionalCatalogString(value.gitBranch, "Git branch", MAX_METADATA_LENGTH);
|
|
833
928
|
const sessionKey = options.allowSessionKey ? parseOptionalCatalogString(value.sessionKey, "OpenClaw session key", MAX_SESSION_KEY_LENGTH) : void 0;
|
|
834
|
-
const createdAt =
|
|
835
|
-
const updatedAt =
|
|
836
|
-
const recencyAt = value.recencyAt === null ? null :
|
|
929
|
+
const createdAt = asFiniteNumber(value.createdAt);
|
|
930
|
+
const updatedAt = asFiniteNumber(value.updatedAt);
|
|
931
|
+
const recencyAt = value.recencyAt === null ? null : asFiniteNumber(value.recencyAt);
|
|
837
932
|
return {
|
|
838
933
|
threadId: value.threadId,
|
|
839
934
|
status,
|
|
@@ -1324,6 +1419,12 @@ async function continueNodeCodexSession(params) {
|
|
|
1324
1419
|
//#endregion
|
|
1325
1420
|
//#region extensions/codex/src/session-catalog-terminal.ts
|
|
1326
1421
|
const CODEX_TERMINAL_RESUME_COMMAND = "codex.terminal.resume.v1";
|
|
1422
|
+
function resolveCodexCatalogTerminalHome(sources) {
|
|
1423
|
+
const runtimeConfig = sources.getRuntimeConfig();
|
|
1424
|
+
if (!runtimeConfig) throw new Error("OpenClaw runtime config is unavailable");
|
|
1425
|
+
const startOptions = resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig: sources.getPluginConfig() }).start;
|
|
1426
|
+
return resolveCodexAppServerLocalHomeDir(startOptions, resolveDefaultAgentDir(runtimeConfig));
|
|
1427
|
+
}
|
|
1327
1428
|
function resolveLocalCodexTerminalExecutable(env = process.env) {
|
|
1328
1429
|
return resolveLocalCodexTerminalResolution(env)?.executable;
|
|
1329
1430
|
}
|
|
@@ -1339,12 +1440,20 @@ function codexNodeTerminalCapability(node) {
|
|
|
1339
1440
|
return node.connected === true && commands?.includes("codex.terminal.resume.v1") === true ? { canOpenTerminalCodex: true } : {};
|
|
1340
1441
|
}
|
|
1341
1442
|
async function requireCatalogEligibleThread(control, threadId) {
|
|
1443
|
+
const cached = await findCatalogEligibleThread(control, threadId, false);
|
|
1444
|
+
if (cached) return cached;
|
|
1445
|
+
const refreshed = await findCatalogEligibleThread(control, threadId, true);
|
|
1446
|
+
if (refreshed) return refreshed;
|
|
1447
|
+
throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
|
|
1448
|
+
}
|
|
1449
|
+
async function findCatalogEligibleThread(control, threadId, forceRefresh) {
|
|
1342
1450
|
let cursor;
|
|
1343
1451
|
const seenCursors = /* @__PURE__ */ new Set();
|
|
1344
1452
|
for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
|
|
1345
1453
|
const page = await control.listPage({
|
|
1346
1454
|
limit: 100,
|
|
1347
|
-
...cursor ? { cursor } : {}
|
|
1455
|
+
...cursor ? { cursor } : {},
|
|
1456
|
+
...forceRefresh ? { forceRefresh: true } : {}
|
|
1348
1457
|
});
|
|
1349
1458
|
const candidate = page.sessions.find((session) => session.threadId === threadId);
|
|
1350
1459
|
if (candidate) {
|
|
@@ -1352,14 +1461,14 @@ async function requireCatalogEligibleThread(control, threadId) {
|
|
|
1352
1461
|
throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
|
|
1353
1462
|
}
|
|
1354
1463
|
const nextCursor = page.nextCursor?.trim();
|
|
1355
|
-
if (!nextCursor)
|
|
1464
|
+
if (!nextCursor) return;
|
|
1356
1465
|
if (seenCursors.has(nextCursor)) throw new CatalogParamsError("Codex session eligibility could not be verified");
|
|
1357
1466
|
seenCursors.add(nextCursor);
|
|
1358
1467
|
cursor = nextCursor;
|
|
1359
1468
|
}
|
|
1360
1469
|
throw new CatalogParamsError("Codex session eligibility could not be verified");
|
|
1361
1470
|
}
|
|
1362
|
-
function createCodexTerminalNodeHostCommand(control) {
|
|
1471
|
+
function createCodexTerminalNodeHostCommand(control, configSources) {
|
|
1363
1472
|
return {
|
|
1364
1473
|
command: CODEX_TERMINAL_RESUME_COMMAND,
|
|
1365
1474
|
cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
|
|
@@ -1387,6 +1496,7 @@ function createCodexTerminalNodeHostCommand(control) {
|
|
|
1387
1496
|
file: resolution.executable,
|
|
1388
1497
|
args: ["resume", resume.threadId],
|
|
1389
1498
|
cwd: record.cwd,
|
|
1499
|
+
env: { CODEX_HOME: resolveCodexCatalogTerminalHome(configSources) },
|
|
1390
1500
|
cols: resume.cols,
|
|
1391
1501
|
rows: resume.rows
|
|
1392
1502
|
}, io));
|
|
@@ -1434,6 +1544,7 @@ async function openCodexCatalogTerminal(params) {
|
|
|
1434
1544
|
params.threadId
|
|
1435
1545
|
],
|
|
1436
1546
|
...record.cwd ? { cwd: record.cwd } : {},
|
|
1547
|
+
env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
|
|
1437
1548
|
...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
|
|
1438
1549
|
title
|
|
1439
1550
|
};
|
|
@@ -1459,6 +1570,19 @@ async function openCodexCatalogTerminal(params) {
|
|
|
1459
1570
|
title
|
|
1460
1571
|
};
|
|
1461
1572
|
}
|
|
1573
|
+
async function startCodexCatalogTerminal(params) {
|
|
1574
|
+
if (params.nodeId) throw new CatalogParamsError("Paired-node Codex terminal start is unavailable; omit hostId to start on the gateway host");
|
|
1575
|
+
const resolution = resolveLocalCodexTerminalResolution();
|
|
1576
|
+
if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable; install Codex or add codex to PATH, then try again");
|
|
1577
|
+
return {
|
|
1578
|
+
kind: "local",
|
|
1579
|
+
argv: [resolution.executable, ...params.initialMessage !== void 0 ? ["--", params.initialMessage] : []],
|
|
1580
|
+
cwd: params.cwd,
|
|
1581
|
+
env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
|
|
1582
|
+
...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
|
|
1583
|
+
title: "codex"
|
|
1584
|
+
};
|
|
1585
|
+
}
|
|
1462
1586
|
//#endregion
|
|
1463
1587
|
//#region extensions/codex/src/session-catalog-transcript-item.ts
|
|
1464
1588
|
const CODEX_MESSAGE_TYPES = /* @__PURE__ */ new Map([
|
|
@@ -1618,6 +1742,16 @@ function createChecker(params) {
|
|
|
1618
1742
|
//#region extensions/codex/src/session-catalog.ts
|
|
1619
1743
|
const boundCatalogSessionId = (value) => boundedCatalogString(value, 256);
|
|
1620
1744
|
const CODEX_SUPERVISION_SESSION_KEY_PREFIX = "harness:codex:supervision:";
|
|
1745
|
+
const CODEX_SESSION_CATALOG_LIST_TTL_MS = 32e3;
|
|
1746
|
+
const CODEX_SESSION_CATALOG_LIST_CACHE_MAX_ENTRIES = 32;
|
|
1747
|
+
function codexCatalogPageCacheKey(params) {
|
|
1748
|
+
return JSON.stringify([
|
|
1749
|
+
params.cursor ?? null,
|
|
1750
|
+
params.limit ?? null,
|
|
1751
|
+
params.searchTerm?.trim().toLocaleLowerCase() || null,
|
|
1752
|
+
params.cwd?.trim() || null
|
|
1753
|
+
]);
|
|
1754
|
+
}
|
|
1621
1755
|
function createCodexSessionCatalogControlFromRequests(params) {
|
|
1622
1756
|
return {
|
|
1623
1757
|
...params.clientId ? { clientId: params.clientId } : {},
|
|
@@ -1687,13 +1821,27 @@ function createCodexSessionCatalogControlFromRequests(params) {
|
|
|
1687
1821
|
function createCodexSessionCatalogControl(params) {
|
|
1688
1822
|
const now = params.now ?? Date.now;
|
|
1689
1823
|
const getPluginConfig = () => params.getPluginConfig();
|
|
1824
|
+
const requestOptionsByConfig = /* @__PURE__ */ new WeakMap();
|
|
1825
|
+
const catalogPagesByConfig = /* @__PURE__ */ new WeakMap();
|
|
1826
|
+
const resolveRequestOptions = (startOptions) => {
|
|
1827
|
+
const runtimeConfig = params.getRuntimeConfig();
|
|
1828
|
+
if (!runtimeConfig) return {
|
|
1829
|
+
config: void 0,
|
|
1830
|
+
startOptions: structuredClone(startOptions)
|
|
1831
|
+
};
|
|
1832
|
+
const cached = requestOptionsByConfig.get(runtimeConfig);
|
|
1833
|
+
if (cached) return cached;
|
|
1834
|
+
const resolved = {
|
|
1835
|
+
config: structuredClone(runtimeConfig),
|
|
1836
|
+
startOptions: structuredClone(startOptions)
|
|
1837
|
+
};
|
|
1838
|
+
requestOptionsByConfig.set(runtimeConfig, resolved);
|
|
1839
|
+
return resolved;
|
|
1840
|
+
};
|
|
1690
1841
|
const createRequestSnapshot = () => {
|
|
1691
1842
|
const pluginConfig = getPluginConfig();
|
|
1692
1843
|
const runtime = resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig });
|
|
1693
|
-
const requestOptions =
|
|
1694
|
-
config: structuredClone(params.getRuntimeConfig()),
|
|
1695
|
-
startOptions: structuredClone(runtime.start)
|
|
1696
|
-
};
|
|
1844
|
+
const requestOptions = resolveRequestOptions(runtime.start);
|
|
1697
1845
|
return {
|
|
1698
1846
|
requestTimeoutMs: runtime.requestTimeoutMs,
|
|
1699
1847
|
listThreads: async (listParams, timeoutMs) => await codexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listThreads, listParams, {
|
|
@@ -1713,10 +1861,10 @@ function createCodexSessionCatalogControl(params) {
|
|
|
1713
1861
|
};
|
|
1714
1862
|
const withPinnedConnection = async (run) => {
|
|
1715
1863
|
const runtime = resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig: getPluginConfig() });
|
|
1716
|
-
const runtimeConfig =
|
|
1864
|
+
const { config: runtimeConfig, startOptions } = resolveRequestOptions(runtime.start);
|
|
1717
1865
|
const client = await getLeasedSharedCodexAppServerClient({
|
|
1718
1866
|
config: runtimeConfig,
|
|
1719
|
-
startOptions
|
|
1867
|
+
startOptions,
|
|
1720
1868
|
timeoutMs: runtime.requestTimeoutMs
|
|
1721
1869
|
});
|
|
1722
1870
|
try {
|
|
@@ -1775,11 +1923,112 @@ function createCodexSessionCatalogControl(params) {
|
|
|
1775
1923
|
releaseLeasedSharedCodexAppServerClient(client);
|
|
1776
1924
|
}
|
|
1777
1925
|
};
|
|
1778
|
-
|
|
1926
|
+
const control = createCodexSessionCatalogControlFromRequests({
|
|
1779
1927
|
createRequestSnapshot,
|
|
1780
1928
|
now,
|
|
1781
1929
|
withPinnedConnection
|
|
1782
1930
|
});
|
|
1931
|
+
return {
|
|
1932
|
+
...control,
|
|
1933
|
+
async listPage(pageParams) {
|
|
1934
|
+
const runtimeConfig = params.getRuntimeConfig();
|
|
1935
|
+
if (!runtimeConfig) return await control.listPage(pageParams);
|
|
1936
|
+
let cache = catalogPagesByConfig.get(runtimeConfig);
|
|
1937
|
+
if (!cache) {
|
|
1938
|
+
cache = /* @__PURE__ */ new Map();
|
|
1939
|
+
catalogPagesByConfig.set(runtimeConfig, cache);
|
|
1940
|
+
}
|
|
1941
|
+
const key = codexCatalogPageCacheKey(pageParams);
|
|
1942
|
+
const cached = cache.get(key);
|
|
1943
|
+
if (pageParams.forceRefresh !== true && cached) {
|
|
1944
|
+
cache.delete(key);
|
|
1945
|
+
cache.set(key, cached);
|
|
1946
|
+
if (cached.stalePage) return await cached.stalePage;
|
|
1947
|
+
if (cached.expiresAt > now()) return await cached.page;
|
|
1948
|
+
const stalePage = cached.settledPage;
|
|
1949
|
+
if (!stalePage) return await cached.page;
|
|
1950
|
+
const page = control.listPage(pageParams);
|
|
1951
|
+
const entry = {
|
|
1952
|
+
expiresAt: Number.POSITIVE_INFINITY,
|
|
1953
|
+
page,
|
|
1954
|
+
settledPage: stalePage,
|
|
1955
|
+
stalePage
|
|
1956
|
+
};
|
|
1957
|
+
cache.set(key, entry);
|
|
1958
|
+
page.then(() => {
|
|
1959
|
+
if (cache.get(key) === entry) {
|
|
1960
|
+
delete entry.stalePage;
|
|
1961
|
+
entry.settledPage = page;
|
|
1962
|
+
entry.expiresAt = now() + CODEX_SESSION_CATALOG_LIST_TTL_MS;
|
|
1963
|
+
}
|
|
1964
|
+
}, async () => {
|
|
1965
|
+
let stale;
|
|
1966
|
+
try {
|
|
1967
|
+
stale = await stalePage;
|
|
1968
|
+
} catch {}
|
|
1969
|
+
if (cache.get(key) !== entry) return;
|
|
1970
|
+
if (stale) {
|
|
1971
|
+
cache.delete(key);
|
|
1972
|
+
const settledPage = Promise.resolve(stale);
|
|
1973
|
+
cache.set(key, {
|
|
1974
|
+
expiresAt: now(),
|
|
1975
|
+
page: settledPage,
|
|
1976
|
+
settledPage
|
|
1977
|
+
});
|
|
1978
|
+
} else cache.delete(key);
|
|
1979
|
+
});
|
|
1980
|
+
return await stalePage;
|
|
1981
|
+
}
|
|
1982
|
+
if (cached) cache.delete(key);
|
|
1983
|
+
const serveStaleOnError = pageParams.forceRefresh !== true;
|
|
1984
|
+
const page = control.listPage(pageParams);
|
|
1985
|
+
const stalePage = cached?.settledPage;
|
|
1986
|
+
const entry = {
|
|
1987
|
+
expiresAt: Number.POSITIVE_INFINITY,
|
|
1988
|
+
page,
|
|
1989
|
+
...stalePage ? {
|
|
1990
|
+
stalePage,
|
|
1991
|
+
settledPage: stalePage
|
|
1992
|
+
} : {}
|
|
1993
|
+
};
|
|
1994
|
+
cache.set(key, entry);
|
|
1995
|
+
while (cache.size > CODEX_SESSION_CATALOG_LIST_CACHE_MAX_ENTRIES) {
|
|
1996
|
+
const oldest = cache.keys().next();
|
|
1997
|
+
if (oldest.done) break;
|
|
1998
|
+
cache.delete(oldest.value);
|
|
1999
|
+
}
|
|
2000
|
+
try {
|
|
2001
|
+
const result = await page;
|
|
2002
|
+
if (cache.get(key) === entry) {
|
|
2003
|
+
delete entry.stalePage;
|
|
2004
|
+
entry.settledPage = page;
|
|
2005
|
+
entry.expiresAt = now() + CODEX_SESSION_CATALOG_LIST_TTL_MS;
|
|
2006
|
+
}
|
|
2007
|
+
return result;
|
|
2008
|
+
} catch (error) {
|
|
2009
|
+
if (stalePage) {
|
|
2010
|
+
let stale;
|
|
2011
|
+
try {
|
|
2012
|
+
stale = await stalePage;
|
|
2013
|
+
} catch {}
|
|
2014
|
+
if (stale) {
|
|
2015
|
+
if (cache.get(key) === entry) {
|
|
2016
|
+
cache.delete(key);
|
|
2017
|
+
const settledPage = Promise.resolve(stale);
|
|
2018
|
+
cache.set(key, {
|
|
2019
|
+
expiresAt: now(),
|
|
2020
|
+
page: settledPage,
|
|
2021
|
+
settledPage
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
if (serveStaleOnError) return stale;
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
if (cache.get(key) === entry) cache.delete(key);
|
|
2028
|
+
throw error;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
};
|
|
1783
2032
|
}
|
|
1784
2033
|
async function listGatewayHost(params) {
|
|
1785
2034
|
try {
|
|
@@ -1820,7 +2069,7 @@ async function listGatewayHost(params) {
|
|
|
1820
2069
|
async function listCodexSessionCatalog(params) {
|
|
1821
2070
|
const query = readGatewayParams(params.query);
|
|
1822
2071
|
const requestedHostIds = query.hostIds ? new Set(query.hostIds) : void 0;
|
|
1823
|
-
const localHosts = !requestedHostIds || requestedHostIds.has("gateway:local") ? [listGatewayHost({
|
|
2072
|
+
const localHosts = params.includeLocal !== false && (!requestedHostIds || requestedHostIds.has("gateway:local")) ? [listGatewayHost({
|
|
1824
2073
|
bindingStore: params.bindingStore,
|
|
1825
2074
|
config: params.config,
|
|
1826
2075
|
control: params.control,
|
|
@@ -1832,7 +2081,7 @@ async function listCodexSessionCatalog(params) {
|
|
|
1832
2081
|
if (!(!requestedHostIds || query.hostIds?.some((hostId) => hostId.startsWith("node:")))) return { hosts: await Promise.all(localHosts) };
|
|
1833
2082
|
let nodes;
|
|
1834
2083
|
try {
|
|
1835
|
-
nodes = (await (params.listNodes?.() ?? params.runtime.nodes.list())).nodes.filter((node) => node.commands?.includes("codex.appServer.threads.list.v1") && (!requestedHostIds || requestedHostIds.has(`node:${node.nodeId}`))).slice(0, 100 - localHosts.length);
|
|
2084
|
+
nodes = (await (params.listNodes?.() ?? params.runtime.nodes.list())).nodes.filter((node) => node.gatewayLocal !== true && node.commands?.includes("codex.appServer.threads.list.v1") && (!requestedHostIds || requestedHostIds.has(`node:${node.nodeId}`))).slice(0, 100 - localHosts.length);
|
|
1836
2085
|
} catch (error) {
|
|
1837
2086
|
const registryHost = {
|
|
1838
2087
|
hostId: "node:registry",
|
|
@@ -1863,7 +2112,7 @@ async function listCodexSessionCatalog(params) {
|
|
|
1863
2112
|
return { hosts: await Promise.all([...localHosts, ...nodeHosts]) };
|
|
1864
2113
|
}
|
|
1865
2114
|
/** Builds the node-local read-only Codex app-server catalog command. */
|
|
1866
|
-
function createCodexSessionCatalogNodeHostCommands(control) {
|
|
2115
|
+
function createCodexSessionCatalogNodeHostCommands(control, configSources) {
|
|
1867
2116
|
return [
|
|
1868
2117
|
{
|
|
1869
2118
|
command: CODEX_APP_SERVER_THREADS_LIST_COMMAND,
|
|
@@ -1901,7 +2150,7 @@ function createCodexSessionCatalogNodeHostCommands(control) {
|
|
|
1901
2150
|
}
|
|
1902
2151
|
}
|
|
1903
2152
|
},
|
|
1904
|
-
createCodexTerminalNodeHostCommand(control)
|
|
2153
|
+
createCodexTerminalNodeHostCommand(control, configSources)
|
|
1905
2154
|
];
|
|
1906
2155
|
}
|
|
1907
2156
|
function readNodeTranscriptParams(value) {
|
|
@@ -1911,9 +2160,9 @@ function readNodeTranscriptParams(value) {
|
|
|
1911
2160
|
"cursor",
|
|
1912
2161
|
"limit"
|
|
1913
2162
|
]));
|
|
1914
|
-
const threadId =
|
|
2163
|
+
const threadId = readBoundedOptionalString(value, "threadId", 256);
|
|
1915
2164
|
if (!threadId) throw new CatalogParamsError("threadId is required");
|
|
1916
|
-
const cursor =
|
|
2165
|
+
const cursor = readBoundedOptionalString(value, "cursor", MAX_CURSOR_LENGTH);
|
|
1917
2166
|
return {
|
|
1918
2167
|
threadId,
|
|
1919
2168
|
limit: readBoundedLimit(value.limit, "limit", 20, 50),
|
|
@@ -2015,7 +2264,6 @@ async function listAdoptedSessionEntries(params) {
|
|
|
2015
2264
|
async function findAdoptedSessionEntry(params) {
|
|
2016
2265
|
return (await listAdoptedSessionEntries(params)).get(params.threadId);
|
|
2017
2266
|
}
|
|
2018
|
-
var CodexAdoptionBindingCleanupError = class extends AggregateError {};
|
|
2019
2267
|
async function clearCreatedAdoptionBinding(params) {
|
|
2020
2268
|
let cleared = false;
|
|
2021
2269
|
let clearError;
|
|
@@ -2033,14 +2281,14 @@ async function clearCreatedAdoptionBinding(params) {
|
|
|
2033
2281
|
try {
|
|
2034
2282
|
current = await params.bindingStore.read(params.identity);
|
|
2035
2283
|
} catch (readError) {
|
|
2036
|
-
throw new
|
|
2284
|
+
throw new AggregateError([
|
|
2037
2285
|
params.cause,
|
|
2038
2286
|
...clearError ? [clearError] : [],
|
|
2039
2287
|
readError
|
|
2040
|
-
], `OpenClaw session creation failed and the Codex binding could not be verified for ${params.sourceThreadId}
|
|
2288
|
+
], `OpenClaw session creation failed and the Codex binding could not be verified for ${params.sourceThreadId}`, { cause: readError });
|
|
2041
2289
|
}
|
|
2042
2290
|
if (!matchesPendingSupervisionOwner(current, params.expectedPending)) return;
|
|
2043
|
-
throw new
|
|
2291
|
+
throw new AggregateError([params.cause, ...clearError ? [clearError] : []], `OpenClaw session creation failed and the Codex binding could not be cleared for ${params.sourceThreadId}`, { cause: params.cause });
|
|
2044
2292
|
}
|
|
2045
2293
|
function matchesPendingAdoptionBinding(binding, expected) {
|
|
2046
2294
|
const historyCoveredThrough = binding?.historyCoveredThrough;
|
|
@@ -2352,13 +2600,22 @@ function toGenericCatalogHost(host, localTerminalAvailable) {
|
|
|
2352
2600
|
};
|
|
2353
2601
|
}
|
|
2354
2602
|
function registerCodexSessionCatalog(params) {
|
|
2355
|
-
const
|
|
2603
|
+
const usesProcessHomeFallback = () => {
|
|
2604
|
+
const start = resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig: params.getPluginConfig() }).start;
|
|
2605
|
+
return start.transport === "stdio" && start.homeScope === "user" && !process.env.CODEX_HOME?.trim();
|
|
2606
|
+
};
|
|
2607
|
+
const assertLocalAccess = (hostId, allowProcessHomeFallback) => {
|
|
2608
|
+
if (hostId === "gateway:local" && allowProcessHomeFallback === false && usesProcessHomeFallback()) throw new CatalogParamsError("local Codex sessions are unavailable in isolated state");
|
|
2609
|
+
};
|
|
2610
|
+
const checkUpstreamActivity = createChecker(params);
|
|
2611
|
+
params.api.registerSessionCatalog({
|
|
2356
2612
|
id: "codex",
|
|
2357
2613
|
label: "Codex",
|
|
2614
|
+
supportsProcessHomeIsolation: true,
|
|
2358
2615
|
resolveCreateSession: ({ agentId }) => resolveCodexCatalogCreateSession(params.getRuntimeConfig() ?? params.api.config, agentId),
|
|
2359
2616
|
list: async (query) => {
|
|
2360
2617
|
const localTerminalAvailable = resolveLocalCodexTerminalExecutable() !== void 0;
|
|
2361
|
-
const { listNodes, onHost, sessionEntries, ...gatewayQuery } = query;
|
|
2618
|
+
const { allowProcessHomeFallback, listNodes, onHost, sessionEntries, ...gatewayQuery } = query;
|
|
2362
2619
|
const mapHost = (host) => toGenericCatalogHost(host, localTerminalAvailable);
|
|
2363
2620
|
return (await listCodexSessionCatalog({
|
|
2364
2621
|
bindingStore: params.bindingStore,
|
|
@@ -2368,17 +2625,20 @@ function registerCodexSessionCatalog(params) {
|
|
|
2368
2625
|
query: gatewayQuery,
|
|
2369
2626
|
listNodes,
|
|
2370
2627
|
sessionEntries,
|
|
2628
|
+
includeLocal: allowProcessHomeFallback !== false || !usesProcessHomeFallback(),
|
|
2371
2629
|
...onHost ? { onHost: (host) => onHost(mapHost(host)) } : {}
|
|
2372
2630
|
})).hosts.map(mapHost);
|
|
2373
2631
|
},
|
|
2374
2632
|
read: async (request) => {
|
|
2633
|
+
const { allowProcessHomeFallback, ...catalogRequest } = request;
|
|
2634
|
+
assertLocalAccess(catalogRequest.hostId, allowProcessHomeFallback);
|
|
2375
2635
|
const page = await readCodexSessionTranscript({
|
|
2376
2636
|
runtime: params.api.runtime,
|
|
2377
2637
|
control: params.control,
|
|
2378
|
-
hostId:
|
|
2379
|
-
threadId:
|
|
2380
|
-
cursor:
|
|
2381
|
-
limit:
|
|
2638
|
+
hostId: catalogRequest.hostId,
|
|
2639
|
+
threadId: catalogRequest.threadId,
|
|
2640
|
+
cursor: catalogRequest.cursor,
|
|
2641
|
+
limit: catalogRequest.limit ?? 20
|
|
2382
2642
|
});
|
|
2383
2643
|
return {
|
|
2384
2644
|
...page,
|
|
@@ -2386,6 +2646,7 @@ function registerCodexSessionCatalog(params) {
|
|
|
2386
2646
|
};
|
|
2387
2647
|
},
|
|
2388
2648
|
continueSession: async (request) => {
|
|
2649
|
+
assertLocalAccess(request.hostId, request.allowProcessHomeFallback);
|
|
2389
2650
|
const config = params.getRuntimeConfig();
|
|
2390
2651
|
if (!config) throw new Error("OpenClaw runtime config is unavailable");
|
|
2391
2652
|
if (request.hostId.startsWith("node:")) return await continueNodeCodexSession({
|
|
@@ -2408,8 +2669,9 @@ function registerCodexSessionCatalog(params) {
|
|
|
2408
2669
|
}
|
|
2409
2670
|
})).sessionKey, request.threadId, upstreamBaseline);
|
|
2410
2671
|
},
|
|
2411
|
-
checkUpstreamActivity:
|
|
2672
|
+
checkUpstreamActivity: (probes, policy) => checkUpstreamActivity(probes.filter((probe) => probe.hostId !== "gateway:local" || policy?.allowProcessHomeFallback !== false || !usesProcessHomeFallback())),
|
|
2412
2673
|
archive: async (request) => {
|
|
2674
|
+
assertLocalAccess(request.hostId, request.allowProcessHomeFallback);
|
|
2413
2675
|
if (request.confirmNoOtherRunner !== true) throw new CatalogParamsError("archive requires confirmation that no other runner is active");
|
|
2414
2676
|
if (request.hostId !== "gateway:local") throw new CatalogParamsError("paired-node Codex sessions are view-only");
|
|
2415
2677
|
const config = params.getRuntimeConfig();
|
|
@@ -2423,14 +2685,26 @@ function registerCodexSessionCatalog(params) {
|
|
|
2423
2685
|
});
|
|
2424
2686
|
return { ok: true };
|
|
2425
2687
|
},
|
|
2426
|
-
openTerminal: (request) =>
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2688
|
+
openTerminal: async (request) => {
|
|
2689
|
+
assertLocalAccess(request.hostId, request.allowProcessHomeFallback);
|
|
2690
|
+
return await openCodexCatalogTerminal({
|
|
2691
|
+
api: params.api,
|
|
2692
|
+
control: params.control,
|
|
2693
|
+
getPluginConfig: params.getPluginConfig,
|
|
2694
|
+
getRuntimeConfig: params.getRuntimeConfig,
|
|
2695
|
+
parseCatalogPage,
|
|
2696
|
+
...request
|
|
2697
|
+
});
|
|
2698
|
+
},
|
|
2699
|
+
startTerminalSession: async (request) => {
|
|
2700
|
+
assertLocalAccess(request.nodeId ? `node:${request.nodeId}` : CODEX_LOCAL_SESSION_HOST_ID, request.allowProcessHomeFallback);
|
|
2701
|
+
return await startCodexCatalogTerminal({
|
|
2702
|
+
getPluginConfig: params.getPluginConfig,
|
|
2703
|
+
getRuntimeConfig: params.getRuntimeConfig,
|
|
2704
|
+
...request
|
|
2705
|
+
});
|
|
2706
|
+
}
|
|
2707
|
+
});
|
|
2434
2708
|
}
|
|
2435
2709
|
const codexSessionCatalogRuntime = {
|
|
2436
2710
|
register: registerCodexSessionCatalog,
|
|
@@ -2441,4 +2715,4 @@ const codexSessionCatalogRuntime = {
|
|
|
2441
2715
|
archiveLocal: archiveLocalCodexSession
|
|
2442
2716
|
};
|
|
2443
2717
|
//#endregion
|
|
2444
|
-
export { CODEX_LOCAL_SESSION_HOST_ID as a,
|
|
2718
|
+
export { CODEX_LOCAL_SESSION_HOST_ID as a, readCodexStatusProbes as c, createCodexCliSessionNodeHostCommands as d, createCodexCliSessionNodeInvokePolicies as f, resumeCodexCliSessionOnNode as g, resolveCodexCliSessionForBindingOnNode as h, createCodexSessionCatalogNodeInvokePolicies as i, requestOptions as l, listCodexCliSessionsOnNode as m, createCodexSessionCatalogControl as n, assertCodexArchiveDescendantsUnowned as o, formatCodexCliSessions as p, createCodexSessionCatalogNodeHostCommands as r, codexControlRequest as s, codexSessionCatalogRuntime as t, safeCodexControlRequest as u };
|