@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.
Files changed (59) hide show
  1. package/README.md +2 -0
  2. package/dist/{app-server-policy-BmDgseo_.js → app-server-policy-zcBDc1JU.js} +1 -1
  3. package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
  4. package/dist/binding-connection-I5Bo-AsX.js +33 -0
  5. package/dist/{bounded-turn-FEbUXoYT.js → bounded-turn-qgZmzy_4.js} +71 -63
  6. package/dist/{capabilities-BHRSI6Cn.js → capabilities-CybGI1oz.js} +8 -2
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-ico4ZFnk.js → command-formatters-B1AEz8ZA.js} +18 -18
  9. package/dist/{command-handlers-CRmnwI2N.js → command-handlers-Db8pu_ds.js} +484 -95
  10. package/dist/{compact-dEN49UH6.js → compact-LspQaGfl.js} +213 -199
  11. package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
  12. package/dist/doctor-contract-api.js +40 -43
  13. package/dist/{dynamic-tools-LIt9ojep.js → dynamic-tools-BVdyWkUD.js} +2984 -1221
  14. package/dist/effective-mcp-catalog-DIIhAtOZ.js +119 -0
  15. package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
  16. package/dist/{attempt-notifications-CMR2_fmR.js → event-projector-usage-e5huIQWu.js} +113 -29
  17. package/dist/harness.js +83 -13
  18. package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
  19. package/dist/index.js +703 -329
  20. package/dist/isolated-completion-C58NbL40.js +75 -0
  21. package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
  22. package/dist/media-understanding-provider.js +5 -5
  23. package/dist/{models-BA01NEez.js → models-BFlpn1Ex.js} +12 -17
  24. package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
  25. package/dist/{plugin-app-cache-key-DSXX-rXO.js → plugin-app-cache-key-C3H2CVp6.js} +135 -42
  26. package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
  27. package/dist/{computer-use-BjGylO-E.js → plugin-metadata-cache-CNJPj4_b.js} +211 -113
  28. package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
  29. package/dist/{rate-limits-S_PXs0ul.js → rate-limits-DZXmOL8-.js} +17 -35
  30. package/dist/{request-CXpPennK.js → request-BRl5_UfX.js} +4 -4
  31. package/dist/{run-attempt-C5GTPJOE.js → run-attempt-P8BdxC1I.js} +1996 -5728
  32. package/dist/{runtime-artifact-C2ITXdhM.js → runtime-artifact-BVSPdd6g.js} +91 -40
  33. package/dist/{sandbox-guard-D1Wky__S.js → sandbox-guard-Cmwc9eIk.js} +10 -13
  34. package/dist/{session-binding-DlVQ-Pxs.js → session-binding-DPxIvMgE.js} +305 -271
  35. package/dist/{session-catalog-CP2oCcEb.js → session-catalog-xwNi_SDM.js} +418 -144
  36. package/dist/{session-cli-CqZ0Fp2a.js → session-cli-7hgXZDV7.js} +1 -1
  37. package/dist/session-retirement-DoMMJFKm.js +36 -0
  38. package/dist/{session-upstream-marker-QE-m5QFG.js → session-upstream-marker-CrIFX06J.js} +1 -1
  39. package/dist/{settled-turn-finalizer-CA_2QXIE.js → settled-turn-finalizer-BRWBO4MO.js} +36 -15
  40. package/dist/{shared-client-BVKE40c-.js → shared-client-kIBbGjk4.js} +721 -240
  41. package/dist/{side-question-CO12sHxo.js → side-question-9HAL26l9.js} +102 -53
  42. package/dist/{thread-lifecycle-DRg_FdzC.js → thread-lifecycle-Bu1BTDXr.js} +1355 -660
  43. package/dist/thread-ownership-6kOQHlyC.js +92 -0
  44. package/dist/thread-resume-CPrJNQVM.js +30 -0
  45. package/dist/{transcript-mirror-uKA-A4tB.js → transcript-mirror-B9gGgnJS.js} +202 -182
  46. package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
  47. package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
  48. package/dist/{upstream-session-fork-_Vrz4WJr.js → upstream-session-fork-CE-_LdnP.js} +5 -5
  49. package/dist/{usage-UpVbdX5G.js → usage-CHlV0e-o.js} +3 -3
  50. package/dist/{web-search-provider.runtime-CaQMYDbp.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
  51. package/openclaw.plugin.json +18 -3
  52. package/package.json +6 -7
  53. package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
  54. package/dist/conversation-control-DE7d9DOj.js +0 -287
  55. package/dist/event-projector-usage-tvgb1NCX.js +0 -69
  56. package/dist/notification-correlation-Bj_Ak_jI.js +0 -67
  57. package/dist/protocol-validators-Dm4eXZmt.js +0 -6337
  58. package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
  59. package/dist/realtime-voice-api.js +0 -2
@@ -1,32 +1,129 @@
1
- import { E as resolveCodexAppServerClientInstanceId, L as CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES, R as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, V as isJsonObject, _ as CodexAppServerRpcError, a as getLeasedSharedCodexAppServerClient, h as withTimeout, l as releaseLeasedSharedCodexAppServerClient } from "./shared-client-BVKE40c-.js";
2
- import { c as reclaimCurrentCodexSessionGeneration, g as resolveCodexSupervisionAppServerRuntimeOptions, l as sessionBindingIdentity, p as resolveCodexAppServerRuntimeOptions } from "./session-binding-DlVQ-Pxs.js";
3
- import { t as assertCodexThreadForkParams } from "./protocol-validators-Dm4eXZmt.js";
4
- import { n as listCodexAppServerModels } from "./models-BA01NEez.js";
5
- import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-DSXX-rXO.js";
6
- import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-uKA-A4tB.js";
7
- import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-BHRSI6Cn.js";
8
- import { r as formatCodexDisplayText } from "./command-formatters-ico4ZFnk.js";
9
- import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-BHRpYbX3.js";
10
- import { n as requestCodexAppServerClientJson, r as requestCodexAppServerJson } from "./request-CXpPennK.js";
11
- import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-QE-m5QFG.js";
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
- const content = await readFileIfExists(path.join(codexHome, "history.jsonl"));
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) continue;
351
+ if (!trimmed) return;
257
352
  let parsed;
258
353
  try {
259
354
  parsed = JSON.parse(trimmed);
260
355
  } catch {
261
- continue;
356
+ return;
262
357
  }
263
- if (!isRecord(parsed) || typeof parsed.session_id !== "string") continue;
358
+ if (!isRecord(parsed) || typeof parsed.session_id !== "string") return;
264
359
  const sessionId = parsed.session_id.trim();
265
- if (!sessionId) continue;
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
- for (const line of content.split(/\r?\n/u)) {
412
+ const result = await visitJsonlLines(file, (line) => {
320
413
  const trimmed = line.trim();
321
- if (!trimmed) continue;
414
+ if (!trimmed) return;
322
415
  let parsed;
323
416
  try {
324
417
  parsed = JSON.parse(trimmed);
325
418
  } catch {
326
- continue;
419
+ return;
327
420
  }
328
- if (!isRecord(parsed)) continue;
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
- continue;
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
- return (await readFileIfExists(file))?.split(/\r?\n/u)[0];
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
- return await requestCodexAppServerJson({
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 readOptionalString(params, key, maxLength) {
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 = readOptionalString(params, "cursor", MAX_CURSOR_LENGTH);
748
- const searchTerm = readOptionalString(params, "searchTerm", MAX_SEARCH_LENGTH);
749
- const cwd = readOptionalString(params, "cwd", MAX_CWD_LENGTH);
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 = readOptionalString(params, "search", MAX_SEARCH_LENGTH);
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 = readFiniteNumber(value.createdAt);
835
- const updatedAt = readFiniteNumber(value.updatedAt);
836
- const recencyAt = value.recencyAt === null ? null : readFiniteNumber(value.recencyAt);
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) throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
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 = structuredClone(params.getRuntimeConfig());
1864
+ const { config: runtimeConfig, startOptions } = resolveRequestOptions(runtime.start);
1717
1865
  const client = await getLeasedSharedCodexAppServerClient({
1718
1866
  config: runtimeConfig,
1719
- startOptions: structuredClone(runtime.start),
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
- return createCodexSessionCatalogControlFromRequests({
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 = readOptionalString(value, "threadId", 256);
2163
+ const threadId = readBoundedOptionalString(value, "threadId", 256);
1915
2164
  if (!threadId) throw new CatalogParamsError("threadId is required");
1916
- const cursor = readOptionalString(value, "cursor", MAX_CURSOR_LENGTH);
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 CodexAdoptionBindingCleanupError([
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 CodexAdoptionBindingCleanupError([params.cause, ...clearError ? [clearError] : []], `OpenClaw session creation failed and the Codex binding could not be cleared for ${params.sourceThreadId}`);
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 provider = {
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: request.hostId,
2379
- threadId: request.threadId,
2380
- cursor: request.cursor,
2381
- limit: request.limit ?? 20
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: createChecker(params),
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) => openCodexCatalogTerminal({
2427
- api: params.api,
2428
- control: params.control,
2429
- parseCatalogPage,
2430
- ...request
2431
- })
2432
- };
2433
- params.api.registerSessionCatalog(provider);
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, requestOptions as c, createCodexCliSessionNodeHostCommands as d, createCodexCliSessionNodeInvokePolicies as f, resumeCodexCliSessionOnNode as g, resolveCodexCliSessionForBindingOnNode as h, createCodexSessionCatalogNodeInvokePolicies as i, safeCodexControlRequest as l, listCodexCliSessionsOnNode as m, createCodexSessionCatalogControl as n, codexControlRequest as o, formatCodexCliSessions as p, createCodexSessionCatalogNodeHostCommands as r, readCodexStatusProbes as s, codexSessionCatalogRuntime as t, assertCodexArchiveDescendantsUnowned as u };
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 };