@openclaw/codex 2026.9.1-beta.1 → 2026.9.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 (81) hide show
  1. package/dist/api.js +8 -9
  2. package/dist/{attempt-client-cleanup-bfsmcQ6m.js → attempt-client-cleanup-Bp1ZOnk2.js} +105 -75
  3. package/dist/attempt-context-CSazkWns.js +634 -0
  4. package/dist/{auth-binding-B4FfbcCg.js → auth-binding-Ds_QVm-X.js} +18 -3
  5. package/dist/{bounded-turn-B7iBUcuP.js → bounded-turn-DxGJRYbY.js} +53 -57
  6. package/dist/{capabilities-BRMh0aIq.js → capabilities-B1mgvpP8.js} +2 -1
  7. package/dist/cli-metadata.js +1 -1
  8. package/dist/{command-formatters-DEsIufkt.js → command-formatters-DSbGMXA0.js} +5 -4
  9. package/dist/{command-handlers-D_-1_qRp.js → command-handlers-DOJvGv6-.js} +243 -148
  10. package/dist/{compact-BglNFZrQ.js → compact-Y_xBqrHN.js} +109 -65
  11. package/dist/{config-giW8X0QA.js → config-CyapYSji.js} +328 -374
  12. package/dist/config-utils-C57JiJrU.js +69 -0
  13. package/dist/{context-compaction-activity-BOeJf7yo.js → context-compaction-activity-CMla5FPJ.js} +4 -1
  14. package/dist/{conversation-binding-data-CW2GuupV.js → conversation-binding-data-BiodpIzq.js} +2 -1
  15. package/dist/doctor-contract-api.js +144 -14
  16. package/dist/{dynamic-tools-Dnlfa--w.js → dynamic-tools-BwpvTxaX.js} +1119 -1135
  17. package/dist/{effective-mcp-catalog-CTeGrnfO.js → effective-mcp-catalog-CY9Jn4bA.js} +4 -3
  18. package/dist/{event-projector-assistant-message-C5-Ds8mA.js → event-projector-assistant-message-B9kp6iGC.js} +15 -6
  19. package/dist/event-projector-usage-ZqEVbAEY.js +212 -0
  20. package/dist/{harness-Csz0F-DK.js → harness-Czh2Sdoc.js} +65 -52
  21. package/dist/harness.js +1 -1
  22. package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
  23. package/dist/index.js +495 -457
  24. package/dist/{isolated-completion-Cv6FqygM.js → isolated-completion-CRh-51AK.js} +9 -21
  25. package/dist/{managed-binary-DFYK4DWq.js → managed-binary-DcpnJF9x.js} +117 -80
  26. package/dist/media-understanding-provider.js +1 -1
  27. package/dist/model-catalog-DT5vo6qo.js +101 -0
  28. package/dist/{models-TS3HBU-q.js → models-CcrgSMQR.js} +26 -29
  29. package/dist/{native-subagent-monitor-4Mfq2HDs.js → native-subagent-monitor-B3bvQGcg.js} +63 -122
  30. package/dist/{node-exec-server.runtime-BpWqJO1V.js → node-exec-server.runtime-nfbUOtUB.js} +10 -7
  31. package/dist/{plugin-approval-roundtrip-bUm777tR.js → plugin-approval-roundtrip-aQVdyL5g.js} +30 -3
  32. package/dist/{plugin-marketplace-discovery-B9DvaAG-.js → plugin-marketplace-discovery-B7gHXJ3d.js} +44 -30
  33. package/dist/{plugin-metadata-cache-CTCT3w5L.js → plugin-metadata-cache-BwCLTY_U.js} +6 -6
  34. package/dist/provider-capabilities-BpG9Ltc5.js +101 -0
  35. package/dist/{rate-limits-C5_D8Gmb.js → rate-limits-Fx_l4eNa.js} +3 -2
  36. package/dist/{sandbox-guard-kuWTn9C5.js → request-C8H22rPi.js} +195 -2
  37. package/dist/{run-attempt-DYc0QCBF.js → run-attempt-BzthFQSM.js} +1616 -2634
  38. package/dist/{runtime-artifact-BvyT1uyd.js → runtime-artifact-Bh_gf6-t.js} +22 -15
  39. package/dist/{session-binding-Bcynfd-_.js → session-binding-4Ki04sW-.js} +282 -399
  40. package/dist/session-binding-record-CyaXZvTe.js +270 -0
  41. package/dist/{session-catalog-DaLKVtpH.js → session-catalog-B47uwekb.js} +812 -504
  42. package/dist/{session-cli-DMc4oS2w.js → session-cli-BjLhpmS1.js} +1 -1
  43. package/dist/session-history-DWmMqiqW.js +93 -0
  44. package/dist/session-history-read-LHldwvyh.js +36 -0
  45. package/dist/session-history-worker-runtime.js +48 -0
  46. package/dist/session-history.worker-zZVcx0ME.js +267 -0
  47. package/dist/session-history.worker.js +2 -0
  48. package/dist/session-initialization-BCFVnLL4.js +75 -0
  49. package/dist/{session-permission-policy-BrNH1bjy.js → session-permission-policy-BaueHmSj.js} +2 -2
  50. package/dist/session-retirement-DNdmYdFc.js +97 -0
  51. package/dist/{session-upstream-marker-BIkusb95.js → session-upstream-marker-DReTpGz6.js} +20 -4
  52. package/dist/settled-turn-context-BOM3Y1pl.js +45 -0
  53. package/dist/settled-turn-finalizer-BI-b1sry.js +115 -0
  54. package/dist/{shared-client-DKz23hHY.js → shared-client-CKhX2kFt.js} +3659 -847
  55. package/dist/{side-question-DjA_jsNe.js → side-question-B6DWAw1A.js} +168 -81
  56. package/dist/{thread-lifecycle-L7oEkTlq.js → thread-lifecycle-DPK30jad.js} +2412 -2145
  57. package/dist/{thread-ownership-DZeuKH1j.js → thread-ownership-CbucAmmp.js} +27 -16
  58. package/dist/thread-policy-CTalhWFy.js +105 -0
  59. package/dist/{plugin-app-cache-key-C1CAdfcP.js → thread-resume-BLgqkUc1.js} +61 -9
  60. package/dist/{transcript-mirror-DsCMWBca.js → transcript-mirror-_njEB84z.js} +114 -110
  61. package/dist/transcript-mirror-attestation-Byl34PC-.js +76 -0
  62. package/dist/transport-stdio-CzTngV0j.js +728 -0
  63. package/dist/{upstream-prompt-provenance-CFmsKwr9.js → upstream-prompt-provenance-LphB8slG.js} +8 -4
  64. package/dist/upstream-session-fork-1fwbNKzQ.js +886 -0
  65. package/dist/{usage-ROIaW3wo.js → usage-i9fv5-4m.js} +3 -3
  66. package/dist/{web-search-provider.runtime-ClaNPV7N.js → web-search-provider.runtime-DKQn67A7.js} +3 -3
  67. package/openclaw.plugin.json +1 -30
  68. package/package.json +6 -6
  69. package/dist/binding-connection-BMvPX73o.js +0 -45
  70. package/dist/command-rpc-CCeCkf_v.js +0 -90
  71. package/dist/event-projector-usage-Bk1I90Bg.js +0 -328
  72. package/dist/incognito-session-ET_ufDH-.js +0 -2
  73. package/dist/model-catalog-i9rNBthX.js +0 -48
  74. package/dist/protocol-validators-VeJxxJ0U.js +0 -2421
  75. package/dist/request-DXXGHko0.js +0 -157
  76. package/dist/session-catalog-home-id-BNwAJ9D7.js +0 -18
  77. package/dist/session-retirement-CcNC3YSY.js +0 -36
  78. package/dist/settled-turn-finalizer-Dc3ApjU4.js +0 -328
  79. package/dist/thread-resume-BE9qNEW0.js +0 -30
  80. package/dist/transport-stdio-DhoGy33Z.js +0 -99
  81. package/dist/upstream-session-fork-DlaHxSLC.js +0 -326
@@ -1,40 +1,44 @@
1
- import { S as resolveCodexAppServerUserHomeDir, h as readCodexPluginConfig, s as resolveCodexSupervisionAppServerRuntimeOptions } from "./config-giW8X0QA.js";
2
- import { G as CodexAppServerRpcError, St as resolveCodexAppServerLocalHomeDir, U as resolveCodexAppServerClientInstanceId, ht as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, mt as CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES, o as getLeasedSharedCodexAppServerClient, p as releaseLeasedSharedCodexAppServerClient, rt as withTimeout, vt as isJsonObject, xt as resolveCodexAppServerHomeDir } from "./shared-client-DKz23hHY.js";
3
- import { c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity } from "./session-binding-Bcynfd-_.js";
4
- import { n as assertCodexThreadForkParams } from "./protocol-validators-VeJxxJ0U.js";
5
- import { i as replaceCodexCatalogConnectionHomes, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-C1CAdfcP.js";
6
- import { n as codexCatalogHomeId, t as canonicalCodexCatalogHome } from "./session-catalog-home-id-BNwAJ9D7.js";
7
- import { r as importCodexThreadHistoryToTranscript } from "./transcript-mirror-DsCMWBca.js";
8
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-BRMh0aIq.js";
9
- import { r as formatCodexDisplayText } from "./command-formatters-DEsIufkt.js";
10
- import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-CW2GuupV.js";
11
- import { n as requestCodexAppServerClientJson } from "./request-DXXGHko0.js";
12
- import { t as codexControlRequest } from "./command-rpc-CCeCkf_v.js";
13
- import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-BIkusb95.js";
1
+ import { l as sessionBindingIdentity } from "./session-binding-record-CyaXZvTe.js";
2
+ import { B as isJsonObject, I as CODEX_INTERACTIVE_CUSTOM_THREAD_SOURCES, L as CODEX_INTERACTIVE_THREAD_SOURCE_KINDS, M as resolveCodexAppServerHomeDir, N as resolveCodexAppServerLocalHomeDir, O as readCodexPluginConfig, P as resolveCodexAppServerUserHomeDir, i as resolveCodexAppServerRuntimeOptions, s as resolveCodexSupervisionAppServerRuntimeOptions } from "./config-CyapYSji.js";
3
+ import { $ as assertCodexThreadForkParams, Ft as withTimeout, J as isCodexThreadReadMissingError, K as resolveCodexAppServerClientInstanceId, Kt as resolveCodexAppServerPreparedAuthHandoff, Ut as resolveCodexAppServerAuthProfileStore, Vt as resolveCodexAppServerAuthProfileId, Xt as readCodexSessionMeta, Yt as isOpenClawManagedCodexThread, c as getLeasedSharedCodexAppServerClient, h as releaseLeasedSharedCodexAppServerClient, y as retireSharedCodexAppServerClientIfCurrent } from "./shared-client-CKhX2kFt.js";
4
+ import { r as createCodexSessionGenerationSupersededError, s as reclaimCurrentCodexSessionGeneration } from "./session-binding-4Ki04sW-.js";
5
+ import { a as closeCodexStartupClientBestEffort } from "./attempt-client-cleanup-Bp1ZOnk2.js";
6
+ import { n as listCodexAppServerModels } from "./models-CcrgSMQR.js";
7
+ import { a as replaceCodexCatalogConnectionHomes, n as buildCodexAppServerConnectionFingerprint, t as resumeCodexAppServerThread } from "./thread-resume-BLgqkUc1.js";
8
+ import { d as codexCatalogHomeId, l as projectCodexUserItemText, r as importCodexThreadHistoryToTranscript, u as canonicalCodexCatalogHome } from "./transcript-mirror-_njEB84z.js";
9
+ import { i as requestCodexAppServerJson, o as withCodexAppServerJsonClient, r as requestCodexAppServerClientJson } from "./request-C8H22rPi.js";
10
+ import { n as describeControlFailure, t as CODEX_CONTROL_METHODS } from "./capabilities-B1mgvpP8.js";
11
+ import { a as formatCodexDisplayText } from "./command-formatters-DSbGMXA0.js";
12
+ import { n as createCodexCliNodeConversationBindingData } from "./conversation-binding-data-BiodpIzq.js";
13
+ import { n as prepareCodexAppServerAuthBinding } from "./auth-binding-Ds_QVm-X.js";
14
+ import { i as createImportedCodexSession, n as codexUpstreamBaseline, r as codexUpstreamContinueResult, t as codexLastTerminalTurnId } from "./session-upstream-marker-DReTpGz6.js";
14
15
  import { asFiniteNumber, isRecord, normalizeBoundedOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
16
+ import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
17
+ import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
18
+ import { z } from "zod";
19
+ import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
20
+ import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
15
21
  import fs from "node:fs/promises";
16
22
  import path from "node:path";
17
- import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
18
23
  import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
24
+ import { prepareAgentRuntimeAuth } from "openclaw/plugin-sdk/agent-harness-runtime";
25
+ import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
19
26
  import fsSync from "node:fs";
20
27
  import os from "node:os";
21
- import { listAgentIds, resolveAgentDir, resolveAllowedModelRef, resolveDefaultAgentDir, resolveDefaultAgentId, resolveDefaultModelForAgent, resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
22
- import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
28
+ import { listAgentIds as listAgentIds$1, resolveAgentDir as resolveAgentDir$2, resolveAgentWorkspaceDir, resolveAllowedModelRef, resolveDefaultAgentDir, resolveDefaultAgentId as resolveDefaultAgentId$1, resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
23
29
  import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
24
- import { resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
25
30
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
26
31
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
27
32
  import { sanitizeTerminalText } from "openclaw/plugin-sdk/text-chunking";
28
33
  import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
29
- import { root } from "openclaw/plugin-sdk/file-access-runtime";
30
34
  import process$1 from "node:process";
31
- import { createSessionCatalogAdoptionCoordinator, listSessionCatalogEntries, sessionCatalogAdoptedSessionKey, sessionCatalogAdoptedSourceKey } from "openclaw/plugin-sdk/session-catalog";
32
- import { decodeNodePtyResumeParams, resolveNodeHostExecutable, runNodePtyCommand } from "openclaw/plugin-sdk/node-host";
33
- import { pruneMapToMaxSize } from "openclaw/plugin-sdk/collection-runtime";
34
- import { createZstdDecompress } from "node:zlib";
35
+ import { resolveSessionModelRef } from "openclaw/plugin-sdk/model-session-runtime";
36
+ import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/provider-auth-runtime";
37
+ import { createSessionCatalogAdoptionCoordinator, listSessionCatalogEntries, publishSessionCatalogHost, sessionCatalogAdoptedSessionKey, sessionCatalogAdoptedSourceKey, sessionCatalogPaging } from "openclaw/plugin-sdk/session-catalog";
38
+ import { decodeNodePtyResumeParams, decodeNodePtyStartParams, resolveNodeHostExecutable, runNodePtyCommand } from "openclaw/plugin-sdk/node-host";
35
39
  //#region extensions/codex/src/jsonl-lines.ts
36
- const JSONL_STREAM_THRESHOLD_BYTES = 4 * 1024 * 1024;
37
- const JSONL_READ_CHUNK_BYTES = 1024 * 1024;
40
+ const JSONL_STREAM_THRESHOLD_BYTES = 4194304;
41
+ const JSONL_READ_CHUNK_BYTES = 1048576;
38
42
  async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTES) {
39
43
  let size;
40
44
  try {
@@ -134,7 +138,7 @@ const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
134
138
  const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
135
139
  const DEFAULT_SESSION_LIMIT = 10;
136
140
  const MAX_SESSION_LIMIT = 50;
137
- const DEFAULT_RESUME_TIMEOUT_MS = 20 * 6e4;
141
+ const DEFAULT_RESUME_TIMEOUT_MS = 12e5;
138
142
  const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
139
143
  const activeResumeSessions = /* @__PURE__ */ new Set();
140
144
  function createCodexCliSessionNodeHostCommands() {
@@ -525,7 +529,7 @@ function normalizeLimit$1(value) {
525
529
  return typeof value === "number" && Number.isFinite(value) ? Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(value))) : DEFAULT_SESSION_LIMIT;
526
530
  }
527
531
  function normalizeTimeoutMs(value) {
528
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(60 * 6e4, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
532
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(36e5, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
529
533
  }
530
534
  function truncateText(value, max) {
531
535
  if (value.length <= max) return value;
@@ -546,6 +550,206 @@ function formatNodeLabel(node) {
546
550
  ].filter(Boolean).join(" / ") || "node";
547
551
  }
548
552
  //#endregion
553
+ //#region extensions/codex/src/command-rpc.ts
554
+ async function prepareControlAuth(options, startOptions) {
555
+ if (!options.onResponse) return {
556
+ authProfileId: options.authProfileId ?? void 0,
557
+ clientOptions: { authProfileId: options.authProfileId }
558
+ };
559
+ if (!options.config || !options.sessionKey || !options.sessionId) throw new Error("Codex control subscription requires admitted session authority.");
560
+ const config = options.config;
561
+ const { sessionAgentId } = resolveSessionAgentIdsStrict({
562
+ config,
563
+ sessionKey: options.sessionKey,
564
+ agentId: options.agentId
565
+ });
566
+ const agentDir = options.agentDir ?? resolveAgentDir$2(config, sessionAgentId);
567
+ const workspaceDir = resolveAgentWorkspaceDir(config, sessionAgentId);
568
+ const entry = getSessionEntry({
569
+ agentId: sessionAgentId,
570
+ storePath: options.storePath?.trim() || resolveStorePath(config.session?.store, { agentId: sessionAgentId }),
571
+ sessionKey: options.sessionKey,
572
+ hydrateSkillPromptRefs: false,
573
+ readConsistency: "latest"
574
+ });
575
+ if (entry?.sessionId !== options.sessionId) throw createCodexSessionGenerationSupersededError(options.sessionId);
576
+ if (options.authProfileId === null || startOptions.homeScope === "user") return {
577
+ authProfileId: options.authProfileId ?? void 0,
578
+ clientOptions: { authProfileId: options.authProfileId }
579
+ };
580
+ const model = resolveSessionModelRef(config, entry, sessionAgentId);
581
+ const authProfileId = entry?.authProfileOverride ?? options.authProfileId;
582
+ const store = resolveCodexAppServerAuthProfileStore({
583
+ agentDir,
584
+ config,
585
+ authProfileId
586
+ });
587
+ const { plan, attempts } = prepareAgentRuntimeAuth({
588
+ provider: model.provider,
589
+ modelId: model.model,
590
+ config,
591
+ agentDir,
592
+ workspaceDir,
593
+ authProfileStore: store,
594
+ sessionAuthProfileId: authProfileId,
595
+ sessionAuthProfileSource: entry?.authProfileOverrideSource,
596
+ harnessId: "codex",
597
+ harnessAuthBootstrap: "harness"
598
+ });
599
+ const route = plan.modelRoute;
600
+ const resolvedAuth = route ? await resolveApiKeyForProvider({
601
+ provider: route.provider,
602
+ modelId: route.modelId,
603
+ modelApi: route.api,
604
+ cfg: config,
605
+ agentDir,
606
+ workspaceDir,
607
+ store,
608
+ profileId: attempts[0]?.profileId,
609
+ lockedProfile: plan.forwardedAuthProfileSource === "user",
610
+ allowAuthProfileFallback: attempts[0]?.allowAuthProfileFallback,
611
+ skipSetupProviderFallback: true
612
+ }) : void 0;
613
+ const handoff = await resolveCodexAppServerPreparedAuthHandoff({
614
+ authRequirement: route?.authRequirement,
615
+ resolvedApiKey: resolvedAuth?.apiKey,
616
+ authProfileId: route ? plan.forwardedAuthProfileId : resolveCodexAppServerAuthProfileId({
617
+ authProfileId: plan.forwardedAuthProfileId,
618
+ store,
619
+ config
620
+ }),
621
+ authProfileStore: store,
622
+ agentDir,
623
+ homeScope: startOptions.homeScope ?? "agent",
624
+ config,
625
+ subscriptionProfileRequiredError: "Prepared Codex subscription route requires a forwarded OpenAI OAuth or token profile.",
626
+ subscriptionProfileUnusableError: "Prepared Codex subscription auth profile is unusable."
627
+ });
628
+ const binding = handoff.authProfileId ? await prepareCodexAppServerAuthBinding({
629
+ authProfileId: handoff.authProfileId,
630
+ authProfileStore: store,
631
+ agentDir,
632
+ config
633
+ }) : void 0;
634
+ return {
635
+ authProfileId: handoff.authProfileId,
636
+ clientOptions: {
637
+ ...handoff.preparedAuth ? { preparedAuth: handoff.preparedAuth } : { authProfileId: handoff.authProfileId },
638
+ authRequirement: route?.authRequirement,
639
+ authProfileStore: binding?.authProfileStore ?? store,
640
+ authBindingFingerprint: binding?.fingerprint,
641
+ agentDir
642
+ }
643
+ };
644
+ }
645
+ function requestOptions(pluginConfig, limit, config, agentDir) {
646
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
647
+ return {
648
+ limit,
649
+ timeoutMs: runtime.requestTimeoutMs,
650
+ startOptions: runtime.start,
651
+ config,
652
+ agentDir
653
+ };
654
+ }
655
+ async function codexControlRequest(pluginConfig, method, requestParams, options = {}) {
656
+ const runtime = options.startOptions ? resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig }) : resolveCodexAppServerRuntimeOptions({ pluginConfig });
657
+ const startOptions = options.startOptions ?? runtime.start;
658
+ const auth = await prepareControlAuth(options, startOptions);
659
+ const controlRequestOptions = {
660
+ timeoutMs: options.timeoutMs ?? runtime.requestTimeoutMs,
661
+ assertCurrent: options.assertCurrent,
662
+ startOptions,
663
+ config: options.config,
664
+ sessionKey: options.sessionKey,
665
+ sessionId: options.sessionId,
666
+ agentDir: options.agentDir,
667
+ isolated: options.isolated,
668
+ ...auth.clientOptions
669
+ };
670
+ if (options.onResponse || options.beforeRequest) return await withCodexAppServerJsonClient(controlRequestOptions, async (request, client, scope) => {
671
+ await options.beforeRequest?.(request, client, scope);
672
+ scope.assertCurrent();
673
+ let response;
674
+ if (method === "thread/resume") {
675
+ if (!isJsonObject(requestParams) || typeof requestParams.threadId !== "string") throw new Error("Codex thread/resume requires a thread id.");
676
+ response = await resumeCodexAppServerThread({
677
+ client,
678
+ request: {
679
+ ...requestParams,
680
+ threadId: requestParams.threadId
681
+ },
682
+ requestResume: () => request({
683
+ method,
684
+ requestParams
685
+ }),
686
+ abandonClient: () => closeCodexStartupClientBestEffort(client)
687
+ });
688
+ } else response = await request({
689
+ method,
690
+ requestParams
691
+ });
692
+ await options.onResponse?.(response, client, {
693
+ authProfileId: auth.authProfileId,
694
+ assertCurrent: scope.assertCurrent
695
+ });
696
+ scope.assertCurrent();
697
+ return response;
698
+ });
699
+ return await requestCodexAppServerJson({
700
+ method,
701
+ requestParams,
702
+ ...controlRequestOptions
703
+ });
704
+ }
705
+ async function safeCodexControlRequest(pluginConfig, method, requestParams, options = {}) {
706
+ return await safeValue(async () => await codexControlRequest(pluginConfig, method, requestParams, options));
707
+ }
708
+ async function safeCodexModelList(pluginConfig, limit, config, agentDir) {
709
+ return await safeValue(async () => await listCodexAppServerModels(requestOptions(pluginConfig, limit, config, agentDir)));
710
+ }
711
+ async function readCodexStatusProbes(pluginConfig, config, agentDir) {
712
+ const [models, account, limits, mcps, skills] = await Promise.all([
713
+ safeCodexModelList(pluginConfig, 20, config, agentDir),
714
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.account, { refreshToken: false }, {
715
+ config,
716
+ agentDir
717
+ }),
718
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, {
719
+ config,
720
+ agentDir
721
+ }),
722
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listMcpServers, { limit: 100 }, {
723
+ config,
724
+ agentDir
725
+ }),
726
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}, {
727
+ config,
728
+ agentDir
729
+ })
730
+ ]);
731
+ return {
732
+ models,
733
+ account,
734
+ limits,
735
+ mcps,
736
+ skills
737
+ };
738
+ }
739
+ async function safeValue(read) {
740
+ try {
741
+ return {
742
+ ok: true,
743
+ value: await read()
744
+ };
745
+ } catch (error) {
746
+ return {
747
+ ok: false,
748
+ error: describeControlFailure(error)
749
+ };
750
+ }
751
+ }
752
+ //#endregion
549
753
  //#region extensions/codex/src/app-server/thread-archive-guard.ts
550
754
  const DESCENDANT_PAGE_LIMIT = 100;
551
755
  const MAX_DESCENDANT_PAGES = 100;
@@ -601,6 +805,7 @@ const DEFAULT_PAGE_LIMIT = 50;
601
805
  const CODEX_APP_SERVER_THREADS_CAPABILITY = "codex-app-server-threads";
602
806
  const CODEX_APP_SERVER_THREADS_LIST_COMMAND = "codex.appServer.threads.list.v1";
603
807
  const CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND = "codex.appServer.thread.turns.list.v1";
808
+ const CODEX_CATALOG_TRANSCRIPT_READ_COMMAND = "codex.sessionCatalog.transcript.read.v1";
604
809
  const CODEX_LOCAL_SESSION_HOST_ID = "gateway:local";
605
810
  const NODE_INVOKE_TIMEOUT_MS = 65e3;
606
811
  const MAX_SEARCH_LENGTH = 500;
@@ -613,7 +818,7 @@ const MAX_SESSION_PREVIEW_LENGTH = 500;
613
818
  const MAX_SESSION_KEY_LENGTH = 1024;
614
819
  const MAX_METADATA_LENGTH = 500;
615
820
  const MAX_ACTIVE_FLAGS = 16;
616
- const MAX_TRANSCRIPT_PAGE_BYTES = 20 * 1024 * 1024;
821
+ const MAX_TRANSCRIPT_PAGE_BYTES = 20971520;
617
822
  var CatalogParamsError = class extends Error {};
618
823
  function readControlCursor(value, label) {
619
824
  if (value === void 0 || value === null) return;
@@ -863,7 +1068,7 @@ function parseTranscriptPage(value) {
863
1068
  ...nextCursor ? { nextCursor } : {},
864
1069
  ...backwardsCursor ? { backwardsCursor } : {}
865
1070
  };
866
- if (Buffer.byteLength(JSON.stringify(page), "utf8") > MAX_TRANSCRIPT_PAGE_BYTES) throw new Error("Codex app-server transcript page exceeds the safe response size");
1071
+ if (Buffer.byteLength(JSON.stringify(page), "utf8") > 20971520) throw new Error("Codex app-server transcript page exceeds the safe response size");
867
1072
  return page;
868
1073
  }
869
1074
  function requireBoundThread(entry) {
@@ -948,10 +1153,16 @@ async function finalizeNodeAdoptedSession(params) {
948
1153
  update: (entry) => {
949
1154
  const current = readNodeSessionMarker(entry);
950
1155
  if (entry.sessionId?.trim() !== params.adopted.sessionId || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true || !current || current.sourceHostId !== params.marker.sourceHostId || current.sourceThreadId !== params.marker.sourceThreadId || current.nodeId !== params.marker.nodeId) throw changedError();
951
- if (current.initializing !== true) return { archivedAt: void 0 };
1156
+ if (current.initializing !== true) return {
1157
+ archivedAt: void 0,
1158
+ archivedBy: void 0,
1159
+ archiveReason: void 0
1160
+ };
952
1161
  const codex = isRecord(entry.pluginExtensions?.codex) ? entry.pluginExtensions.codex : {};
953
1162
  return {
954
1163
  archivedAt: void 0,
1164
+ archivedBy: void 0,
1165
+ archiveReason: void 0,
955
1166
  pluginExtensions: {
956
1167
  ...entry.pluginExtensions,
957
1168
  codex: {
@@ -997,7 +1208,7 @@ async function createOrReuseNodeAdoptedSession(params) {
997
1208
  key: nodeAdoptionSessionKey(params.hostId, params.record.threadId),
998
1209
  agentId: params.agentId,
999
1210
  recoverMatchingInitialEntry: true,
1000
- ...params.record.name?.trim() ? { label: params.record.name.trim() } : {},
1211
+ displayName: params.record.name ?? void 0,
1001
1212
  ...params.record.cwd?.trim() ? { spawnedCwd: params.record.cwd.trim() } : {},
1002
1213
  initialEntry: {
1003
1214
  agentHarnessId: "codex",
@@ -1040,182 +1251,6 @@ async function createOrReuseNodeAdoptedSession(params) {
1040
1251
  }
1041
1252
  }
1042
1253
  //#endregion
1043
- //#region extensions/codex/src/session-catalog-terminal.ts
1044
- const CODEX_TERMINAL_RESUME_COMMAND = "codex.terminal.resume.v1";
1045
- function resolveCodexCatalogTerminalHome(sources) {
1046
- const runtimeConfig = sources.getRuntimeConfig();
1047
- if (!runtimeConfig) throw new Error("OpenClaw runtime config is unavailable");
1048
- const agentDir = sources.source?.agentDir ?? (sources.agentId ? resolveAgentDir(runtimeConfig, sources.agentId) : resolveDefaultAgentDir(runtimeConfig));
1049
- return resolveCodexAppServerLocalHomeDir(sources.source?.appServer.start ?? resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig: sources.getPluginConfig() }).start, agentDir);
1050
- }
1051
- function resolveLocalCodexTerminalExecutable(env = process.env) {
1052
- return resolveLocalCodexTerminalResolution(env)?.executable;
1053
- }
1054
- function resolveLocalCodexTerminalResolution(env = process.env) {
1055
- return resolveNodeHostExecutable("codex", {
1056
- env,
1057
- pathEnv: env.PATH ?? env.Path ?? "",
1058
- strategy: "fallback"
1059
- });
1060
- }
1061
- function codexNodeTerminalCapability(node) {
1062
- const commands = node.invocableCommands ?? node.commands;
1063
- return node.connected === true && commands?.includes("codex.terminal.resume.v1") === true ? { canOpenTerminalCodex: true } : {};
1064
- }
1065
- async function requireCatalogEligibleThread(control, threadId) {
1066
- const cached = await findCatalogEligibleThread(control, threadId, false);
1067
- if (cached) return cached;
1068
- const refreshed = await findCatalogEligibleThread(control, threadId, true);
1069
- if (refreshed) return refreshed;
1070
- throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1071
- }
1072
- async function findCatalogEligibleThread(control, threadId, forceRefresh) {
1073
- let cursor;
1074
- const seenCursors = /* @__PURE__ */ new Set();
1075
- for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
1076
- const page = await control.listPage({
1077
- limit: 100,
1078
- ...cursor ? { cursor } : {},
1079
- ...forceRefresh ? { forceRefresh: true } : {}
1080
- });
1081
- const candidate = page.sessions.find((session) => session.threadId === threadId);
1082
- if (candidate) {
1083
- if (isInteractiveThreadSource(candidate.source)) return candidate;
1084
- throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1085
- }
1086
- const nextCursor = page.nextCursor?.trim();
1087
- if (!nextCursor) return;
1088
- if (seenCursors.has(nextCursor)) throw new CatalogParamsError("Codex session eligibility could not be verified");
1089
- seenCursors.add(nextCursor);
1090
- cursor = nextCursor;
1091
- }
1092
- throw new CatalogParamsError("Codex session eligibility could not be verified");
1093
- }
1094
- function createCodexTerminalNodeHostCommand(bindRequest, configSources) {
1095
- return {
1096
- command: CODEX_TERMINAL_RESUME_COMMAND,
1097
- cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
1098
- dangerous: false,
1099
- duplex: true,
1100
- isAvailable: ({ env }) => Boolean(resolveNodeHostExecutable("codex", {
1101
- env,
1102
- pathEnv: env.PATH ?? env.Path ?? "",
1103
- strategy: "direct"
1104
- })),
1105
- handle: async (paramsJSON, io) => {
1106
- if (!io) throw new Error("Codex terminal command requires duplex transport");
1107
- const request = bindRequest(paramsJSON);
1108
- const resume = decodeNodePtyResumeParams(request.paramsJSON, (value) => {
1109
- if (typeof value !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(value)) throw new CatalogParamsError("threadId must be a UUID");
1110
- return value;
1111
- });
1112
- const record = await requireCatalogEligibleThread(request.control, resume.threadId);
1113
- const resolution = resolveNodeHostExecutable("codex", {
1114
- env: process.env,
1115
- pathEnv: process.env.PATH ?? process.env.Path ?? "",
1116
- strategy: "direct"
1117
- });
1118
- if (!resolution) throw new Error("Codex CLI is unavailable");
1119
- return JSON.stringify(await runNodePtyCommand({
1120
- file: resolution.executable,
1121
- args: ["resume", resume.threadId],
1122
- cwd: record.cwd,
1123
- env: { CODEX_HOME: resolveCodexCatalogTerminalHome({
1124
- ...configSources,
1125
- agentId: request.agentId
1126
- }) },
1127
- cols: resume.cols,
1128
- rows: resume.rows
1129
- }, io));
1130
- }
1131
- };
1132
- }
1133
- async function resolveNodeCatalogEligibleThread(params) {
1134
- let cursor;
1135
- const seenCursors = /* @__PURE__ */ new Set();
1136
- for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
1137
- const raw = await params.runtime.nodes.invoke({
1138
- nodeId: params.nodeId,
1139
- command: CODEX_APP_SERVER_THREADS_LIST_COMMAND,
1140
- params: {
1141
- agentId: params.agentId,
1142
- limit: 100,
1143
- ...cursor ? { cursor } : {}
1144
- },
1145
- timeoutMs: NODE_INVOKE_TIMEOUT_MS,
1146
- scopes: ["operator.write"]
1147
- });
1148
- const page = params.parseCatalogPage(unwrapNodeInvokePayload(raw));
1149
- const record = page.sessions.find((candidate) => candidate.threadId === params.threadId);
1150
- if (record) {
1151
- if (isInteractiveThreadSource(record.source)) return record;
1152
- break;
1153
- }
1154
- const nextCursor = page.nextCursor?.trim();
1155
- if (!nextCursor || seenCursors.has(nextCursor)) break;
1156
- seenCursors.add(nextCursor);
1157
- cursor = nextCursor;
1158
- }
1159
- throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1160
- }
1161
- async function openCodexCatalogTerminal(params) {
1162
- const title = `codex resume ${params.threadId.slice(0, 8)}…`;
1163
- if (params.hostId === "gateway:local" || params.hostId.startsWith(`gateway:local:`)) {
1164
- const record = await requireCatalogEligibleThread(params.control, params.threadId);
1165
- const resolution = resolveLocalCodexTerminalResolution();
1166
- if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable");
1167
- return {
1168
- kind: "local",
1169
- argv: [
1170
- resolution.executable,
1171
- "resume",
1172
- params.threadId
1173
- ],
1174
- ...record.cwd ? { cwd: record.cwd } : {},
1175
- env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
1176
- ...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
1177
- title
1178
- };
1179
- }
1180
- if (!params.hostId.startsWith("node:")) throw new CatalogParamsError("hostId is invalid");
1181
- const nodeId = params.hostId.slice(5);
1182
- if (!(await params.api.runtime.nodes.list()).nodes.find((candidate) => {
1183
- const commands = candidate.invocableCommands ?? candidate.commands;
1184
- return candidate.nodeId === nodeId && candidate.connected === true && commands?.includes("codex.appServer.threads.list.v1") === true && commands.includes("codex.terminal.resume.v1");
1185
- })) throw new CatalogParamsError("paired-node Codex terminal is unavailable");
1186
- const record = await resolveNodeCatalogEligibleThread({
1187
- agentId: params.agentId,
1188
- runtime: params.api.runtime,
1189
- nodeId,
1190
- threadId: params.threadId,
1191
- parseCatalogPage: params.parseCatalogPage
1192
- });
1193
- return {
1194
- kind: "node",
1195
- nodeId,
1196
- command: CODEX_TERMINAL_RESUME_COMMAND,
1197
- paramsJSON: JSON.stringify({
1198
- agentId: params.agentId,
1199
- threadId: params.threadId
1200
- }),
1201
- ...record.cwd ? { cwd: record.cwd } : {},
1202
- title
1203
- };
1204
- }
1205
- async function startCodexCatalogTerminal(params) {
1206
- if (params.nodeId) throw new CatalogParamsError("Paired-node Codex terminal start is unavailable; omit hostId to start on the gateway host");
1207
- const resolution = resolveLocalCodexTerminalResolution();
1208
- if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable; install Codex or add codex to PATH, then try again");
1209
- return {
1210
- kind: "local",
1211
- argv: [resolution.executable, ...params.initialMessage !== void 0 ? ["--", params.initialMessage] : []],
1212
- cwd: params.cwd,
1213
- env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
1214
- ...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
1215
- title: "codex"
1216
- };
1217
- }
1218
- //#endregion
1219
1254
  //#region extensions/codex/src/session-catalog-adoption.ts
1220
1255
  const CODEX_SUPERVISION_SESSION_KEY_PREFIX = "harness:codex:supervision:";
1221
1256
  const boundCatalogSessionId = (value) => boundedCatalogString(value, 256);
@@ -1255,7 +1290,7 @@ async function listAdoptedSessionEntries(params) {
1255
1290
  if (!sessionKeyRest.startsWith(CODEX_SUPERVISION_SESSION_KEY_PREFIX) || !marker || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true) continue;
1256
1291
  const sessionId = entry.sessionId?.trim();
1257
1292
  if (!sessionId) continue;
1258
- const binding = await params.bindingStore.read(sessionBindingIdentity({
1293
+ const binding = params.bindingStore.read(sessionBindingIdentity({
1259
1294
  sessionId,
1260
1295
  sessionKey,
1261
1296
  config: params.config
@@ -1278,54 +1313,26 @@ async function findAdoptedSessionEntry(params) {
1278
1313
  const adopted = await listAdoptedSessionEntries(params);
1279
1314
  return adopted.get(sessionCatalogAdoptedSourceKey(params.sourceHomeId ?? "gateway:local", params.threadId)) ?? (params.sourceHomeId && params.allowLegacy === true ? adopted.get(sessionCatalogAdoptedSourceKey("gateway:local", params.threadId)) : void 0);
1280
1315
  }
1281
- async function clearCreatedAdoptionBinding(params) {
1282
- let cleared = false;
1283
- let clearError;
1284
- try {
1285
- cleared = await params.bindingStore.mutate(params.identity, {
1286
- kind: "clear",
1287
- threadId: params.sourceThreadId,
1288
- expectedPendingSupervisionBranch: params.expectedPending
1289
- });
1290
- } catch (error) {
1291
- clearError = error;
1292
- }
1293
- if (cleared) return;
1294
- let current;
1295
- try {
1296
- current = await params.bindingStore.read(params.identity);
1297
- } catch (readError) {
1298
- throw new AggregateError([
1299
- params.cause,
1300
- ...clearError ? [clearError] : [],
1301
- readError
1302
- ], `OpenClaw session creation failed and the Codex binding could not be verified for ${params.sourceThreadId}`, { cause: readError });
1303
- }
1304
- if (!matchesPendingSupervisionOwner(current, params.expectedPending)) return;
1305
- 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 });
1306
- }
1307
1316
  function matchesPendingAdoptionBinding(binding, expected) {
1308
1317
  const historyCoveredThrough = binding?.historyCoveredThrough;
1309
1318
  return binding?.threadId === expected.sourceThreadId && binding.connectionScope === "supervision" && binding.supervisionSourceThreadId === expected.sourceThreadId && binding.cwd === expected.cwd && binding.conversationSourceTransferComplete === true && binding.preserveNativeModel === true && binding.pendingSupervisionBranch?.sourceThreadId === expected.sourceThreadId && binding.pendingSupervisionBranch.connectionFingerprint === expected.connectionFingerprint && binding.pendingSupervisionBranch.lastTurnId === expected.lastTurnId && (binding.pendingSupervisionBranch.cleanupThreadIds?.length ?? 0) === 0 && typeof historyCoveredThrough === "string" && Number.isFinite(Date.parse(historyCoveredThrough));
1310
1319
  }
1311
- function matchesPendingSupervisionOwner(binding, expected) {
1312
- const pending = binding?.pendingSupervisionBranch;
1313
- const cleanupThreadIds = pending?.cleanupThreadIds ?? [];
1314
- const expectedCleanupThreadIds = expected.cleanupThreadIds ?? [];
1315
- return binding?.threadId === expected.sourceThreadId && binding.connectionScope === "supervision" && binding.supervisionSourceThreadId === expected.sourceThreadId && pending?.sourceThreadId === expected.sourceThreadId && pending.connectionFingerprint === expected.connectionFingerprint && pending.lastTurnId === expected.lastTurnId && cleanupThreadIds.length === expectedCleanupThreadIds.length && cleanupThreadIds.every((threadId, index) => threadId === expectedCleanupThreadIds[index]);
1316
- }
1317
1320
  async function ensurePendingAdoptionBinding(params) {
1318
1321
  const pending = {
1319
1322
  sourceThreadId: params.sourceThreadId,
1320
1323
  connectionFingerprint: params.connectionFingerprint,
1321
1324
  ...params.lastTurnId ? { lastTurnId: params.lastTurnId } : {}
1322
1325
  };
1323
- if (!await reclaimCurrentCodexSessionGeneration({
1326
+ const ownsGeneration = await reclaimCurrentCodexSessionGeneration({
1327
+ assertCurrent: params.initialization.assertCurrent,
1324
1328
  bindingStore: params.bindingStore,
1325
1329
  identity: params.identity,
1326
1330
  config: params.config
1327
- })) throw new Error(`failed to claim the OpenClaw session generation for ${params.sourceThreadId}`);
1328
- const existing = await params.bindingStore.read(params.identity);
1331
+ });
1332
+ params.initialization.assertCurrent();
1333
+ if (!ownsGeneration) throw new Error(`failed to claim the OpenClaw session generation for ${params.sourceThreadId}`);
1334
+ const existing = params.bindingStore.read(params.identity);
1335
+ params.initialization.assertCurrent();
1329
1336
  if (existing) {
1330
1337
  if (matchesPendingAdoptionBinding(existing, params)) return;
1331
1338
  throw new Error(`OpenClaw session is already bound to Codex thread ${existing.threadId}`);
@@ -1340,19 +1347,7 @@ async function ensurePendingAdoptionBinding(params) {
1340
1347
  preserveNativeModel: true,
1341
1348
  pendingSupervisionBranch: pending
1342
1349
  };
1343
- let stored;
1344
- try {
1345
- stored = await params.bindingStore.mutate(params.identity, {
1346
- kind: "set",
1347
- if: { kind: "absent" },
1348
- binding
1349
- });
1350
- } catch (error) {
1351
- if (matchesPendingAdoptionBinding(await params.bindingStore.read(params.identity), params)) return;
1352
- throw error;
1353
- }
1354
- if (stored) return;
1355
- if (!matchesPendingAdoptionBinding(await params.bindingStore.read(params.identity), params)) throw new Error(`failed to bind OpenClaw session to Codex thread ${params.sourceThreadId}`);
1350
+ await params.initialization.bind(binding);
1356
1351
  }
1357
1352
  async function createOrReuseAdoptedSession(params) {
1358
1353
  const runtime = params.api.runtime;
@@ -1363,8 +1358,6 @@ async function createOrReuseAdoptedSession(params) {
1363
1358
  };
1364
1359
  const existing = await findAdoptedSessionEntry(lookup);
1365
1360
  if (existing) return existing;
1366
- let createdBindingIdentity;
1367
- let createdPendingBinding;
1368
1361
  try {
1369
1362
  const spawnedCwd = params.sourceThread.cwd?.trim() || void 0;
1370
1363
  const pendingLastTurnId = codexLastTerminalTurnId(params.sourceThread, boundCatalogSessionId);
@@ -1374,9 +1367,11 @@ async function createOrReuseAdoptedSession(params) {
1374
1367
  };
1375
1368
  const created = await createImportedCodexSession({
1376
1369
  runtime: params.api.runtime,
1370
+ bindingStore: params.bindingStore,
1377
1371
  config: params.config,
1378
1372
  key: adoptionSessionKey(params.sourceThread.id, params.sourceHomeId),
1379
1373
  agentId: params.agentId,
1374
+ displayName: params.sourceThread.name ?? void 0,
1380
1375
  thread: params.sourceThread,
1381
1376
  throughTurnId: pendingLastTurnId ?? null,
1382
1377
  recoverMatchingInitialEntry: true,
@@ -1389,21 +1384,17 @@ async function createOrReuseAdoptedSession(params) {
1389
1384
  modelLocked: true
1390
1385
  } } }
1391
1386
  },
1392
- afterImport: async (entry) => {
1393
- createdBindingIdentity = sessionBindingIdentity({
1387
+ afterImport: async (entry, initialization) => {
1388
+ const identity = sessionBindingIdentity({
1394
1389
  sessionId: entry.sessionId,
1395
1390
  sessionKey: entry.key,
1396
1391
  config: params.config
1397
1392
  });
1398
- createdPendingBinding = {
1399
- sourceThreadId: params.sourceThread.id,
1400
- connectionFingerprint: params.connectionFingerprint,
1401
- ...pendingLastTurnId ? { lastTurnId: pendingLastTurnId } : {}
1402
- };
1403
1393
  await ensurePendingAdoptionBinding({
1404
1394
  bindingStore: params.bindingStore,
1405
1395
  config: params.config,
1406
- identity: createdBindingIdentity,
1396
+ identity,
1397
+ initialization,
1407
1398
  sourceThreadId: params.sourceThread.id,
1408
1399
  connectionFingerprint: params.connectionFingerprint,
1409
1400
  cwd: spawnedCwd ?? "",
@@ -1422,24 +1413,13 @@ async function createOrReuseAdoptedSession(params) {
1422
1413
  boundThreadId: params.sourceThread.id
1423
1414
  };
1424
1415
  } catch (error) {
1425
- let raced = await findAdoptedSessionEntry(lookup);
1416
+ const raced = await findAdoptedSessionEntry(lookup);
1426
1417
  if (raced) return raced;
1427
- if (createdBindingIdentity && createdPendingBinding) {
1428
- await clearCreatedAdoptionBinding({
1429
- bindingStore: params.bindingStore,
1430
- identity: createdBindingIdentity,
1431
- sourceThreadId: params.sourceThread.id,
1432
- expectedPending: createdPendingBinding,
1433
- cause: error
1434
- });
1435
- raced = await findAdoptedSessionEntry(lookup);
1436
- if (raced) return raced;
1437
- }
1438
1418
  throw error;
1439
1419
  }
1440
1420
  }
1441
1421
  async function continueLocalCodexSessionInner(params) {
1442
- await requireCatalogEligibleThread(params.control, params.threadId);
1422
+ await params.control.requireEligibleThread(params.threadId);
1443
1423
  const existing = await findAdoptedSessionEntry({
1444
1424
  ...params,
1445
1425
  runtime: params.api.runtime
@@ -1455,7 +1435,11 @@ async function continueLocalCodexSessionInner(params) {
1455
1435
  preserveActivity: true,
1456
1436
  update: (entry) => {
1457
1437
  if (entry.sessionId?.trim() !== existing.sessionId || entry.initializationPending === true || entry.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true) throw changedError();
1458
- return { archivedAt: void 0 };
1438
+ return {
1439
+ archivedAt: void 0,
1440
+ archivedBy: void 0,
1441
+ archiveReason: void 0
1442
+ };
1459
1443
  }
1460
1444
  })) throw changedError();
1461
1445
  const connectionFingerprint = params.control.connectionFingerprint;
@@ -1510,8 +1494,8 @@ async function continueLocalCodexSession(params) {
1510
1494
  }
1511
1495
  //#endregion
1512
1496
  //#region extensions/codex/src/session-catalog-archive.ts
1513
- async function assertNoPendingSupervisionBranch(params) {
1514
- const adoptedEntries = [params.agentId, ...listAgentIds(params.config).filter((agentId) => agentId !== params.agentId)].flatMap((agentId) => params.runtime.agent.session.listSessionEntries({
1497
+ function assertNoPendingSupervisionBranch(params) {
1498
+ const adoptedEntries = [params.agentId, ...listAgentIds$1(params.config).filter((agentId) => agentId !== params.agentId)].flatMap((agentId) => params.runtime.agent.session.listSessionEntries({
1515
1499
  agentId,
1516
1500
  readOnly: true
1517
1501
  })).filter((candidate) => isAdoptionSessionKeyForThread(candidate.sessionKey, params.threadId, params.sourceHomeId) || params.sourceHomeId !== void 0 && params.allowLegacy === true && isAdoptionSessionKeyForThread(candidate.sessionKey, params.threadId));
@@ -1519,7 +1503,7 @@ async function assertNoPendingSupervisionBranch(params) {
1519
1503
  if (adopted.entry.initializationPending === true) throw new CatalogParamsError("Codex session cannot be archived while its OpenClaw branch is initializing");
1520
1504
  const sessionId = adopted.entry.sessionId?.trim();
1521
1505
  if (!sessionId) continue;
1522
- const binding = await params.bindingStore.read(sessionBindingIdentity({
1506
+ const binding = params.bindingStore.read(sessionBindingIdentity({
1523
1507
  sessionId,
1524
1508
  sessionKey: adopted.sessionKey,
1525
1509
  config: params.config
@@ -1532,8 +1516,8 @@ async function archiveLocalCodexSession(params) {
1532
1516
  return await runSessionActionExclusive(sessionCatalogAdoptedSourceKey(params.hostId ?? "gateway:local", params.threadId), async () => {
1533
1517
  return await params.bindingStore.withThreadArchiveFence(async () => {
1534
1518
  const run = async (control) => {
1535
- await requireCatalogEligibleThread(control, params.threadId);
1536
- await assertNoPendingSupervisionBranch(params);
1519
+ assertNoPendingSupervisionBranch(params);
1520
+ await control.requireEligibleThread(params.threadId);
1537
1521
  const thread = await control.readThread(params.threadId, false);
1538
1522
  if (thread.id !== params.threadId) throw new Error("Codex app-server returned a different thread than requested");
1539
1523
  requireIdleThread(thread, "archive");
@@ -1561,7 +1545,7 @@ const CODEX_AGENT_RUNTIME_ID = "codex";
1561
1545
  const CODEX_CATALOG_DEFAULT_MODEL_REF = "openai/gpt-5.6-sol";
1562
1546
  function resolveCodexCatalogCreateSession(config, requestedAgentId) {
1563
1547
  if (!config) return;
1564
- const agentId = requestedAgentId ?? resolveDefaultAgentId(config);
1548
+ const agentId = requestedAgentId ?? resolveDefaultAgentId$1(config);
1565
1549
  const defaultModel = resolveDefaultModelForAgent({
1566
1550
  cfg: config,
1567
1551
  agentId
@@ -1607,7 +1591,8 @@ async function listPairedNode(params) {
1607
1591
  label: nodeLabel(params.node),
1608
1592
  kind: "node",
1609
1593
  nodeId: params.node.nodeId,
1610
- canContinueCodex: canContinueCodexOnNode(params.node)
1594
+ canContinueCodex: canContinueCodexOnNode(params.node),
1595
+ ...params.terminalCapabilities
1611
1596
  };
1612
1597
  if (params.node.connected !== true) {
1613
1598
  const host = {
@@ -1622,6 +1607,15 @@ async function listPairedNode(params) {
1622
1607
  params.onHost?.(host);
1623
1608
  return host;
1624
1609
  }
1610
+ if (!params.node.commands?.includes("codex.appServer.threads.list.v1")) {
1611
+ const host = {
1612
+ ...common,
1613
+ connected: true,
1614
+ sessions: []
1615
+ };
1616
+ params.onHost?.(host);
1617
+ return host;
1618
+ }
1625
1619
  const eventualHost = Promise.resolve().then(async () => {
1626
1620
  const page = filterCatalogPageByTitle(parseCatalogPage(unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
1627
1621
  nodeId: params.node.nodeId,
@@ -1633,7 +1627,8 @@ async function listPairedNode(params) {
1633
1627
  searchTerm: params.query.search
1634
1628
  },
1635
1629
  timeoutMs: NODE_INVOKE_TIMEOUT_MS,
1636
- scopes: ["operator.write"]
1630
+ scopes: ["operator.write"],
1631
+ signal: params.signal
1637
1632
  }))), params.query.search);
1638
1633
  return {
1639
1634
  ...common,
@@ -1650,7 +1645,7 @@ async function listPairedNode(params) {
1650
1645
  sessions: [],
1651
1646
  error: catalogError("NODE_INVOKE_FAILED", error)
1652
1647
  }));
1653
- if (params.onHost) eventualHost.then(params.onHost).catch(() => void 0);
1648
+ publishSessionCatalogHost(params, eventualHost);
1654
1649
  try {
1655
1650
  return await withTimeout(eventualHost, NODE_CATALOG_LIST_RESPONSE_TIMEOUT_MS, "paired node Codex session catalog timed out");
1656
1651
  } catch (error) {
@@ -1800,7 +1795,7 @@ async function continueNodeCodexSession(params) {
1800
1795
  if (params.clientScopes?.includes("operator.admin") !== true) throw new CatalogParamsError("continuing a paired-node Codex session requires operator.admin");
1801
1796
  const nodeId = params.hostId.slice(5).trim();
1802
1797
  if (!nodeId || params.hostId !== `node:${nodeId}`) throw new CatalogParamsError("Codex session catalog hostId is invalid");
1803
- const agentId = resolveSessionAgentIds({
1798
+ const agentId = resolveSessionAgentIdsStrict({
1804
1799
  config: params.config,
1805
1800
  agentId: params.agentId
1806
1801
  }).sessionAgentId;
@@ -1817,15 +1812,342 @@ async function continueNodeCodexSession(params) {
1817
1812
  });
1818
1813
  }
1819
1814
  //#endregion
1820
- //#region extensions/codex/src/session-catalog-listing.ts
1821
- async function listVisiblePage(params) {
1822
- const excluded = params.excludedThreadIds;
1823
- const sessions = [];
1824
- let cursor = params.cursor;
1825
- let nextCursor;
1826
- let backwardsCursor;
1815
+ //#region extensions/codex/src/session-catalog-terminal.ts
1816
+ const CODEX_TERMINAL_RESUME_COMMAND = "codex.terminal.resume.v1";
1817
+ const CODEX_TERMINAL_START_COMMAND = "codex.terminal.start.v1";
1818
+ function createCodexTerminalStartNodeHostCommand() {
1819
+ return {
1820
+ command: CODEX_TERMINAL_START_COMMAND,
1821
+ cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
1822
+ dangerous: false,
1823
+ duplex: true,
1824
+ isAvailable: ({ env }) => Boolean(resolveNodeHostExecutable("codex", {
1825
+ env,
1826
+ strategy: "direct"
1827
+ })),
1828
+ handle: async (paramsJSON, io) => {
1829
+ if (!io) throw new Error("Codex terminal command requires duplex transport");
1830
+ const params = decodeNodePtyStartParams(paramsJSON);
1831
+ const resolution = resolveNodeHostExecutable("codex", { strategy: "direct" });
1832
+ if (!resolution) throw new Error("Codex CLI is unavailable; install codex on this node and reconnect");
1833
+ return JSON.stringify(await runNodePtyCommand({
1834
+ file: resolution.executable,
1835
+ args: params.initialMessage !== void 0 ? ["--", params.initialMessage] : [],
1836
+ cwd: params.cwd,
1837
+ requiredCwd: true,
1838
+ cols: params.cols,
1839
+ rows: params.rows
1840
+ }, io));
1841
+ }
1842
+ };
1843
+ }
1844
+ function resolveCodexCatalogTerminalHome(sources) {
1845
+ const runtimeConfig = sources.getRuntimeConfig();
1846
+ if (!runtimeConfig) throw new Error("OpenClaw runtime config is unavailable");
1847
+ const agentDir = sources.source?.agentDir ?? (sources.agentId ? resolveAgentDir$2(runtimeConfig, sources.agentId) : resolveDefaultAgentDir(runtimeConfig));
1848
+ const startOptions = sources.source?.appServer.start ?? resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig: sources.getPluginConfig() }).start;
1849
+ return resolveCodexAppServerLocalHomeDir(startOptions, agentDir);
1850
+ }
1851
+ function resolveLocalCodexTerminalExecutable(env = process.env) {
1852
+ return resolveLocalCodexTerminalResolution(env)?.executable;
1853
+ }
1854
+ function resolveLocalCodexTerminalResolution(env = process.env) {
1855
+ return resolveNodeHostExecutable("codex", {
1856
+ env,
1857
+ pathEnv: env.PATH ?? env.Path ?? "",
1858
+ strategy: "fallback"
1859
+ });
1860
+ }
1861
+ function codexNodeTerminalCapability(node) {
1862
+ const commands = node.invocableCommands ?? node.commands;
1863
+ return {
1864
+ canOpenTerminalCodex: node.connected === true && commands?.includes("codex.terminal.resume.v1") === true,
1865
+ canStartTerminal: node.connected === true && node.invocableCommands?.includes("codex.terminal.start.v1") === true
1866
+ };
1867
+ }
1868
+ function createCodexTerminalNodeHostCommand(bindRequest, configSources) {
1869
+ return {
1870
+ command: CODEX_TERMINAL_RESUME_COMMAND,
1871
+ cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
1872
+ dangerous: false,
1873
+ duplex: true,
1874
+ isAvailable: ({ env }) => Boolean(resolveNodeHostExecutable("codex", {
1875
+ env,
1876
+ pathEnv: env.PATH ?? env.Path ?? "",
1877
+ strategy: "direct"
1878
+ })),
1879
+ handle: async (paramsJSON, io) => {
1880
+ if (!io) throw new Error("Codex terminal command requires duplex transport");
1881
+ const request = bindRequest(paramsJSON);
1882
+ const resume = decodeNodePtyResumeParams(request.paramsJSON, (value) => {
1883
+ if (typeof value !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(value)) throw new CatalogParamsError("threadId must be a UUID");
1884
+ return value;
1885
+ });
1886
+ const record = await request.control.requireEligibleThread(resume.threadId);
1887
+ const resolution = resolveNodeHostExecutable("codex", {
1888
+ env: process.env,
1889
+ pathEnv: process.env.PATH ?? process.env.Path ?? "",
1890
+ strategy: "direct"
1891
+ });
1892
+ if (!resolution) throw new Error("Codex CLI is unavailable");
1893
+ return JSON.stringify(await runNodePtyCommand({
1894
+ file: resolution.executable,
1895
+ args: ["resume", resume.threadId],
1896
+ ...record.cwd ? { cwd: record.cwd } : {},
1897
+ env: { CODEX_HOME: resolveCodexCatalogTerminalHome({
1898
+ ...configSources,
1899
+ agentId: request.agentId
1900
+ }) },
1901
+ cols: resume.cols,
1902
+ rows: resume.rows
1903
+ }, io));
1904
+ }
1905
+ };
1906
+ }
1907
+ async function resolveNodeCatalogEligibleThread(params) {
1908
+ let cursor;
1909
+ const seenCursors = /* @__PURE__ */ new Set();
1910
+ for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
1911
+ const raw = await params.runtime.nodes.invoke({
1912
+ nodeId: params.nodeId,
1913
+ command: CODEX_APP_SERVER_THREADS_LIST_COMMAND,
1914
+ params: {
1915
+ agentId: params.agentId,
1916
+ limit: 100,
1917
+ ...cursor ? { cursor } : {}
1918
+ },
1919
+ timeoutMs: NODE_INVOKE_TIMEOUT_MS,
1920
+ scopes: ["operator.write"]
1921
+ });
1922
+ const page = params.parseCatalogPage(unwrapNodeInvokePayload(raw));
1923
+ const record = page.sessions.find((candidate) => candidate.threadId === params.threadId);
1924
+ if (record) {
1925
+ if (isInteractiveThreadSource(record.source)) return record;
1926
+ break;
1927
+ }
1928
+ const nextCursor = page.nextCursor?.trim();
1929
+ if (!nextCursor || seenCursors.has(nextCursor)) break;
1930
+ seenCursors.add(nextCursor);
1931
+ cursor = nextCursor;
1932
+ }
1933
+ throw new CatalogParamsError("Codex session is not a non-archived interactive Codex session");
1934
+ }
1935
+ async function openCodexCatalogTerminal(params) {
1936
+ const title = `codex resume ${params.threadId.slice(0, 8)}…`;
1937
+ if (params.hostId === "gateway:local" || params.hostId.startsWith(`gateway:local:`)) {
1938
+ const record = await params.control.requireEligibleThread(params.threadId);
1939
+ const resolution = resolveLocalCodexTerminalResolution();
1940
+ if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable");
1941
+ return {
1942
+ kind: "local",
1943
+ argv: [
1944
+ resolution.executable,
1945
+ "resume",
1946
+ params.threadId
1947
+ ],
1948
+ ...record.cwd ? { cwd: record.cwd } : {},
1949
+ env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
1950
+ ...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
1951
+ title
1952
+ };
1953
+ }
1954
+ if (!params.hostId.startsWith("node:")) throw new CatalogParamsError("hostId is invalid");
1955
+ const nodeId = params.hostId.slice(5);
1956
+ if (!(await params.api.runtime.nodes.list()).nodes.find((candidate) => {
1957
+ const commands = candidate.invocableCommands ?? candidate.commands;
1958
+ return candidate.nodeId === nodeId && candidate.connected === true && commands?.includes("codex.appServer.threads.list.v1") === true && commands.includes("codex.terminal.resume.v1");
1959
+ })) throw new CatalogParamsError("paired-node Codex terminal is unavailable");
1960
+ const record = await resolveNodeCatalogEligibleThread({
1961
+ agentId: params.agentId,
1962
+ runtime: params.api.runtime,
1963
+ nodeId,
1964
+ threadId: params.threadId,
1965
+ parseCatalogPage: params.parseCatalogPage
1966
+ });
1967
+ return {
1968
+ kind: "node",
1969
+ nodeId,
1970
+ command: CODEX_TERMINAL_RESUME_COMMAND,
1971
+ paramsJSON: JSON.stringify({
1972
+ agentId: params.agentId,
1973
+ threadId: params.threadId
1974
+ }),
1975
+ ...record.cwd ? { cwd: record.cwd } : {},
1976
+ title
1977
+ };
1978
+ }
1979
+ async function startCodexCatalogTerminal(params) {
1980
+ if (params.nodeId) return {
1981
+ kind: "node",
1982
+ nodeId: params.nodeId,
1983
+ command: CODEX_TERMINAL_START_COMMAND,
1984
+ paramsJSON: JSON.stringify({
1985
+ cwd: params.cwd,
1986
+ initialMessage: params.initialMessage
1987
+ }),
1988
+ cwd: params.cwd,
1989
+ title: "codex"
1990
+ };
1991
+ const resolution = resolveLocalCodexTerminalResolution();
1992
+ if (!resolution) throw new CatalogParamsError("Codex CLI is unavailable; install Codex or add codex to PATH, then try again");
1993
+ return {
1994
+ kind: "local",
1995
+ argv: [resolution.executable, ...params.initialMessage !== void 0 ? ["--", params.initialMessage] : []],
1996
+ cwd: params.cwd,
1997
+ env: { CODEX_HOME: resolveCodexCatalogTerminalHome(params) },
1998
+ ...resolution.pathEnv ? { pathEnv: resolution.pathEnv } : {},
1999
+ title: "codex"
2000
+ };
2001
+ }
2002
+ //#endregion
2003
+ //#region extensions/codex/src/session-catalog-transcript-item.ts
2004
+ const CODEX_MESSAGE_TYPES = /* @__PURE__ */ new Map([
2005
+ ["userMessage", "userMessage"],
2006
+ ["agentMessage", "agentMessage"],
2007
+ ["reasoning", "reasoning"]
2008
+ ]);
2009
+ const CODEX_TOOL_TYPES = /* @__PURE__ */ new Set([
2010
+ "commandExecution",
2011
+ "fileChange",
2012
+ "mcpToolCall",
2013
+ "dynamicToolCall",
2014
+ "collabAgentToolCall",
2015
+ "webSearch",
2016
+ "imageView",
2017
+ "imageGeneration"
2018
+ ]);
2019
+ function toGenericTranscriptItem(item) {
2020
+ let type = CODEX_MESSAGE_TYPES.get(item.type);
2021
+ if (!type && CODEX_TOOL_TYPES.has(item.type)) type = item.result !== void 0 || Boolean(item.aggregatedOutput) ? "toolResult" : "toolCall";
2022
+ type ??= "other";
2023
+ const fallback = item.title ?? item.name ?? item.tool ?? item.command ?? item.query ?? void 0;
2024
+ const resultText = item.aggregatedOutput || (item.result === void 0 ? void 0 : JSON.stringify(item.result, null, 2));
2025
+ const changesText = Array.isArray(item.changes) ? item.changes.map((change) => `${change.kind}: ${change.path}`).join("\n") || void 0 : void 0;
2026
+ const text = item.type === "userMessage" ? projectCodexUserItemText(item) : item.text || resultText || changesText || fallback;
2027
+ return {
2028
+ id: item.id,
2029
+ type,
2030
+ ...text ? { text } : {},
2031
+ raw: item
2032
+ };
2033
+ }
2034
+ //#endregion
2035
+ //#region extensions/codex/src/session-catalog-transcript.ts
2036
+ const TURN_ITEM_CURSOR_PREFIX = "turn-item:";
2037
+ const transcriptPageSchema = z.strictObject({
2038
+ items: z.array(z.strictObject({
2039
+ id: z.string(),
2040
+ type: z.enum([
2041
+ "userMessage",
2042
+ "agentMessage",
2043
+ "reasoning",
2044
+ "toolCall",
2045
+ "toolResult",
2046
+ "other"
2047
+ ]),
2048
+ text: z.string().optional(),
2049
+ raw: z.record(z.string(), z.json()).optional(),
2050
+ truncated: z.boolean().optional()
2051
+ })),
2052
+ nextCursor: z.string().optional()
2053
+ });
2054
+ function parseCodexCatalogTranscriptPage(value) {
2055
+ return transcriptPageSchema.parse(value);
2056
+ }
2057
+ function encodeTurnItemCursor(turnCursor, itemId) {
2058
+ return TURN_ITEM_CURSOR_PREFIX + Buffer.from(JSON.stringify([turnCursor, itemId])).toString("base64url");
2059
+ }
2060
+ function decodeTurnItemCursor(cursor) {
2061
+ if (!cursor?.startsWith(TURN_ITEM_CURSOR_PREFIX)) return { turnCursor: cursor };
2062
+ let value;
2063
+ try {
2064
+ value = JSON.parse(Buffer.from(cursor.slice(10), "base64url").toString());
2065
+ } catch {
2066
+ throw new CatalogParamsError("invalid Codex transcript item cursor");
2067
+ }
2068
+ if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== "string" || !value[0] || typeof value[1] !== "string" || !value[1]) throw new CatalogParamsError("invalid Codex transcript item cursor");
2069
+ return {
2070
+ turnCursor: value[0],
2071
+ itemId: value[1]
2072
+ };
2073
+ }
2074
+ function projectTranscriptPage(items, limit) {
2075
+ const projected = items.map(toGenericTranscriptItem);
2076
+ const page = sessionCatalogPaging.boundTranscriptPage(projected.toReversed(), limit, 0).items;
2077
+ for (const [index, item] of page.entries()) if (item.text !== projected[index]?.text && projected[index]?.text) item.truncated = true;
2078
+ return page;
2079
+ }
2080
+ function pageFitsNodeTransport(page) {
2081
+ return Buffer.byteLength(JSON.stringify({ payloadJSON: JSON.stringify(page) }), "utf8") <= MAX_TRANSCRIPT_PAGE_BYTES;
2082
+ }
2083
+ /** The legacy API can anchor a turn, but cannot continue within that turn. */
2084
+ async function readLegacyCodexTranscriptPage(readTurns, request) {
2085
+ const { turnCursor, itemId } = decodeTurnItemCursor(request.cursor);
2086
+ const page = parseTranscriptPage(await readTurns({
2087
+ threadId: request.threadId,
2088
+ limit: 1,
2089
+ sortDirection: "desc",
2090
+ itemsView: "full",
2091
+ ...turnCursor ? { cursor: turnCursor } : {}
2092
+ }));
2093
+ const source = page.data.flatMap((turn) => turn.items.toReversed());
2094
+ const anchorIndex = itemId ? source.findIndex((item) => item.id === itemId) : -1;
2095
+ if (itemId && anchorIndex < 0) throw new CatalogParamsError("Codex transcript changed; refresh the session before loading older items");
2096
+ const remaining = source.slice(anchorIndex + 1);
2097
+ const items = projectTranscriptPage(remaining, request.limit);
2098
+ const result = () => {
2099
+ const lastSource = remaining[items.length - 1];
2100
+ const partial = lastSource !== void 0 && items.length < remaining.length;
2101
+ const nativeCursor = partial ? page.backwardsCursor : page.nextCursor;
2102
+ const cursor = nativeCursor ? partial ? encodeTurnItemCursor(nativeCursor, lastSource.id) : nativeCursor : void 0;
2103
+ if (partial && !cursor) throw new Error("Codex app-server did not provide a transcript continuation anchor");
2104
+ return {
2105
+ items,
2106
+ ...cursor ? { nextCursor: cursor } : {}
2107
+ };
2108
+ };
2109
+ let bounded = result();
2110
+ while (!pageFitsNodeTransport(bounded)) {
2111
+ if (items.length <= 1) throw new Error("Codex transcript item exceeds the safe response size");
2112
+ items.pop();
2113
+ bounded = result();
2114
+ }
2115
+ return bounded;
2116
+ }
2117
+ /** Uses the native store's item cursor whenever that store supports item history. */
2118
+ async function readCodexCatalogTranscriptPage(control, request) {
2119
+ if ((await control.requireEligibleThread(request.threadId)).historyMode !== "paginated") return readLegacyCodexTranscriptPage((params) => control.listTurnPage(params), request);
2120
+ let limit = request.limit;
2121
+ for (;;) {
2122
+ const page = await control.listItemPage({
2123
+ threadId: request.threadId,
2124
+ limit,
2125
+ sortDirection: "desc",
2126
+ ...request.cursor ? { cursor: request.cursor } : {}
2127
+ });
2128
+ const nextCursor = readControlCursor(page.nextCursor, "transcript next response");
2129
+ const items = projectTranscriptPage(page.data.map(({ item }) => item), limit);
2130
+ const result = {
2131
+ items,
2132
+ ...nextCursor ? { nextCursor } : {}
2133
+ };
2134
+ const fittingCount = items.length - (pageFitsNodeTransport(result) ? 0 : 1);
2135
+ if (fittingCount === page.data.length) return result;
2136
+ if (fittingCount < 1) throw new Error("Codex transcript item exceeds the safe response size");
2137
+ limit = fittingCount;
2138
+ }
2139
+ }
2140
+ //#endregion
2141
+ //#region extensions/codex/src/session-catalog-listing.ts
2142
+ async function listVisiblePage(params) {
2143
+ const excluded = params.excludedThreadIds;
2144
+ const sessions = [];
2145
+ let cursor = params.cursor;
2146
+ let nextCursor;
2147
+ let backwardsCursor;
1827
2148
  const seenCursors = /* @__PURE__ */ new Set();
1828
2149
  for (let pageIndex = 0; pageIndex < 20; pageIndex += 1) {
2150
+ params.signal?.throwIfAborted();
1829
2151
  let excludedFromPage = false;
1830
2152
  const rawPage = await params.control.listPage({
1831
2153
  limit: params.limit - sessions.length,
@@ -1833,10 +2155,12 @@ async function listVisiblePage(params) {
1833
2155
  ...params.searchTerm ? { searchTerm: params.searchTerm } : {},
1834
2156
  ...params.cwd ? { cwd: params.cwd } : {}
1835
2157
  });
2158
+ params.signal?.throwIfAborted();
1836
2159
  const page = filterCatalogPageByTitle(parseCatalogPage(rawPage), params.searchTerm);
1837
2160
  if (pageIndex === 0) backwardsCursor = page.backwardsCursor;
1838
2161
  for (const managed of rawPage.managedThreads ?? []) {
1839
2162
  excludedFromPage = true;
2163
+ params.signal?.throwIfAborted();
1840
2164
  await params.onExcludedThread?.(managed);
1841
2165
  }
1842
2166
  for (const session of page.sessions) {
@@ -1845,6 +2169,7 @@ async function listVisiblePage(params) {
1845
2169
  continue;
1846
2170
  }
1847
2171
  excludedFromPage = true;
2172
+ params.signal?.throwIfAborted();
1848
2173
  await params.onExcludedThread?.({ threadId: session.threadId });
1849
2174
  }
1850
2175
  nextCursor = page.nextCursor;
@@ -1870,8 +2195,10 @@ async function listGatewayHost(params) {
1870
2195
  excludedThreadIds: params.excludedThreadIds,
1871
2196
  limit: params.query.limitPerHost,
1872
2197
  onExcludedThread: params.onExcludedThread,
1873
- searchTerm: params.query.search
2198
+ searchTerm: params.query.search,
2199
+ signal: params.signal
1874
2200
  });
2201
+ params.signal?.throwIfAborted();
1875
2202
  const adoptedSessions = await listAdoptedSessionEntries({
1876
2203
  agentId: params.agentId,
1877
2204
  bindingStore: params.bindingStore,
@@ -1904,7 +2231,7 @@ async function listGatewayHost(params) {
1904
2231
  }
1905
2232
  /** Lists Gateway-local and paired-node Codex sessions with per-host failures. */
1906
2233
  async function listCodexSessionCatalog(params) {
1907
- const agentId = resolveSessionAgentIds({
2234
+ const agentId = resolveSessionAgentIdsStrict({
1908
2235
  config: params.config ?? {},
1909
2236
  agentId: params.agentId
1910
2237
  }).sessionAgentId;
@@ -1912,6 +2239,7 @@ async function listCodexSessionCatalog(params) {
1912
2239
  const requestedHostIds = query.hostIds ? new Set(query.hostIds) : void 0;
1913
2240
  const localSources = params.localHomes?.filter((source) => !requestedHostIds || requestedHostIds.has(source.hostId)) ?? (params.includeLocal !== false && (!requestedHostIds || requestedHostIds.has("gateway:local")) ? [void 0] : []);
1914
2241
  const managedThreads = await params.bindingStore.managedThreads?.snapshot();
2242
+ params.signal?.throwIfAborted();
1915
2243
  const fallbackSource = params.control.homesForAgent(agentId)[0];
1916
2244
  const localHosts = localSources.map((source) => (() => {
1917
2245
  const ownershipSource = source ?? fallbackSource;
@@ -1924,6 +2252,7 @@ async function listCodexSessionCatalog(params) {
1924
2252
  query,
1925
2253
  runtime: params.runtime,
1926
2254
  sessionEntries: params.sessionEntries,
2255
+ signal: params.signal,
1927
2256
  excludedThreadIds: managedThreadIds,
1928
2257
  ...ownershipSource && params.bindingStore.managedThreads ? { onExcludedThread: async ({ threadId, rolloutPath }) => {
1929
2258
  if (!managedThreadIds?.has(threadId)) await params.bindingStore.managedThreads?.mark({
@@ -1935,40 +2264,42 @@ async function listCodexSessionCatalog(params) {
1935
2264
  ...source ? { source } : {}
1936
2265
  });
1937
2266
  })());
1938
- for (const host of localHosts) if (params.onHost) host.then(params.onHost).catch(() => void 0);
2267
+ for (const host of localHosts) publishSessionCatalogHost(params, host);
1939
2268
  if (!(!requestedHostIds || query.hostIds?.some((hostId) => hostId.startsWith("node:")))) return { hosts: await Promise.all(localHosts) };
1940
2269
  let nodes;
1941
2270
  try {
1942
- 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);
2271
+ nodes = (await (params.listNodes?.() ?? params.runtime.nodes.list())).nodes.filter((node) => node.gatewayLocal !== true && (node.commands?.includes("codex.appServer.threads.list.v1") || codexNodeTerminalCapability(node).canStartTerminal) && (!requestedHostIds || requestedHostIds.has(`node:${node.nodeId}`))).slice(0, 100 - localHosts.length);
1943
2272
  } catch (error) {
1944
2273
  const registryHost = {
1945
2274
  hostId: "node:registry",
1946
2275
  label: "Paired nodes",
1947
2276
  kind: "node",
1948
2277
  connected: false,
2278
+ canStartTerminal: false,
1949
2279
  sessions: [],
1950
2280
  error: catalogError("NODE_LIST_FAILED", error)
1951
2281
  };
1952
2282
  params.onHost?.(registryHost);
1953
2283
  return { hosts: [...await Promise.all(localHosts), registryHost] };
1954
2284
  }
2285
+ params.signal?.throwIfAborted();
1955
2286
  const adoptedNodeSessions = listNodeAdoptedSessionEntries({
1956
2287
  agentId,
1957
2288
  config: params.config,
1958
2289
  runtime: params.runtime,
1959
2290
  sessionEntries: params.sessionEntries
1960
2291
  });
1961
- const nodeHosts = nodes.toSorted(compareNodeLabels).map(async (node) => {
1962
- const host = await listPairedNode({
1963
- agentId,
1964
- runtime: params.runtime,
1965
- node,
1966
- query,
1967
- adoptedSessions: adoptedNodeSessions,
1968
- ...params.onHost ? { onHost: params.onHost } : {}
1969
- });
1970
- return Object.assign(host, codexNodeTerminalCapability(node));
1971
- });
2292
+ const nodeHosts = nodes.toSorted(compareNodeLabels).map((node) => listPairedNode({
2293
+ agentId,
2294
+ runtime: params.runtime,
2295
+ node,
2296
+ query,
2297
+ adoptedSessions: adoptedNodeSessions,
2298
+ terminalCapabilities: codexNodeTerminalCapability(node),
2299
+ waitUntil: params.waitUntil,
2300
+ signal: params.signal,
2301
+ ...params.onHost ? { onHost: params.onHost } : {}
2302
+ }));
1972
2303
  return { hosts: await Promise.all([...localHosts, ...nodeHosts]) };
1973
2304
  }
1974
2305
  /** Builds the node-local read-only Codex app-server catalog command. */
@@ -1978,11 +2309,11 @@ function createCodexSessionCatalogNodeHostCommands(controlFactory, configSources
1978
2309
  if (!isRecord(parsed)) throw new CatalogParamsError("Codex session catalog parameters must be an object");
1979
2310
  const requestedAgentId = readBoundedOptionalString(parsed, "agentId", 256);
1980
2311
  const config = configSources.getRuntimeConfig() ?? {};
1981
- const agentId = resolveSessionAgentIds({
2312
+ const agentId = resolveSessionAgentIdsStrict({
1982
2313
  config,
1983
2314
  agentId: requestedAgentId
1984
2315
  }).sessionAgentId;
1985
- if (!listAgentIds(config).includes(agentId)) throw new CatalogParamsError(`unknown Codex session catalog agent: ${agentId}`);
2316
+ if (!listAgentIds$1(config).includes(agentId)) throw new CatalogParamsError(`unknown Codex session catalog agent: ${agentId}`);
1986
2317
  const request = { ...parsed };
1987
2318
  delete request.agentId;
1988
2319
  const source = controlFactory.homesForAgent(agentId)[0];
@@ -1994,7 +2325,7 @@ function createCodexSessionCatalogNodeHostCommands(controlFactory, configSources
1994
2325
  paramsJSON: JSON.stringify(request)
1995
2326
  };
1996
2327
  };
1997
- return [
2328
+ const commands = [
1998
2329
  {
1999
2330
  command: CODEX_APP_SERVER_THREADS_LIST_COMMAND,
2000
2331
  cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
@@ -2035,7 +2366,7 @@ function createCodexSessionCatalogNodeHostCommands(controlFactory, configSources
2035
2366
  const request = bindRequest(paramsJSON);
2036
2367
  const action = readNodeTranscriptParams(request.params);
2037
2368
  try {
2038
- await requireCatalogEligibleThread(request.control, action.threadId);
2369
+ await request.control.requireEligibleThread(action.threadId);
2039
2370
  const page = parseTranscriptPage(await request.control.listTurnPage({
2040
2371
  threadId: action.threadId,
2041
2372
  limit: action.limit,
@@ -2050,8 +2381,25 @@ function createCodexSessionCatalogNodeHostCommands(controlFactory, configSources
2050
2381
  }
2051
2382
  }
2052
2383
  },
2053
- createCodexTerminalNodeHostCommand(bindRequest, configSources)
2384
+ {
2385
+ command: CODEX_CATALOG_TRANSCRIPT_READ_COMMAND,
2386
+ cap: CODEX_APP_SERVER_THREADS_CAPABILITY,
2387
+ dangerous: false,
2388
+ handle: async (paramsJSON) => {
2389
+ const request = bindRequest(paramsJSON);
2390
+ const action = readNodeTranscriptParams(request.params);
2391
+ try {
2392
+ return JSON.stringify(await readCodexCatalogTranscriptPage(request.control, action));
2393
+ } catch (error) {
2394
+ if (error instanceof CatalogParamsError) throw error;
2395
+ throw new Error("Codex app-server transcript is unavailable", { cause: error });
2396
+ }
2397
+ }
2398
+ },
2399
+ createCodexTerminalNodeHostCommand(bindRequest, configSources),
2400
+ createCodexTerminalStartNodeHostCommand()
2054
2401
  ];
2402
+ return process.env.OPENCLAW_NODE_EXEC_HOST?.trim().toLowerCase() === "app" ? commands.filter(({ command }) => command !== CODEX_CATALOG_TRANSCRIPT_READ_COMMAND) : commands;
2055
2403
  }
2056
2404
  function readNodeTranscriptParams(value) {
2057
2405
  if (!isRecord(value)) throw new CatalogParamsError("Codex session read parameters must be an object");
@@ -2074,94 +2422,58 @@ function readBoundedLimit(value, key, fallback, max) {
2074
2422
  if (!Number.isInteger(value) || value < 1 || value > max) throw new CatalogParamsError(`${key} must be an integer from 1 to ${max}`);
2075
2423
  return value;
2076
2424
  }
2077
- function flattenTranscriptPageDesc(page) {
2078
- return page.data.flatMap((turn) => turn.items.toReversed());
2079
- }
2080
2425
  /** Reads the persisted transcript for a Gateway-local or paired-node Codex session. */
2081
2426
  async function readCodexSessionTranscript(params) {
2427
+ const cursor = readControlCursor(params.cursor, "transcript request");
2428
+ const limit = readBoundedLimit(params.limit, "limit", 20, 50);
2082
2429
  if (params.source || params.hostId === "gateway:local") {
2083
- await requireCatalogEligibleThread(params.control, params.threadId);
2084
- const listParams = {
2430
+ const page = await readCodexCatalogTranscriptPage(params.control, {
2085
2431
  threadId: params.threadId,
2086
- limit: params.limit,
2087
- sortDirection: "desc",
2088
- itemsView: "full",
2089
- ...params.cursor ? { cursor: params.cursor } : {}
2090
- };
2091
- const page = parseTranscriptPage(await params.control.listTurnPage(listParams));
2432
+ limit,
2433
+ cursor
2434
+ });
2092
2435
  return {
2093
2436
  hostId: params.hostId,
2094
2437
  label: params.source?.label ?? "Local Codex",
2095
2438
  threadId: params.threadId,
2096
- items: flattenTranscriptPageDesc(page),
2097
- ...page.nextCursor ? { nextCursor: page.nextCursor } : {},
2098
- ...page.backwardsCursor ? { backwardsCursor: page.backwardsCursor } : {}
2439
+ ...page
2099
2440
  };
2100
2441
  }
2101
2442
  const nodeId = params.hostId.slice(5);
2102
2443
  const node = (await params.runtime.nodes.list()).nodes.find((candidate) => candidate.nodeId === nodeId && candidate.connected === true && candidate.commands?.includes("codex.appServer.thread.turns.list.v1"));
2103
2444
  if (!node) throw new CatalogParamsError("paired-node Codex session host is offline or unavailable");
2104
- const page = parseTranscriptPage(unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
2445
+ const invoke = async (command, request) => unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
2105
2446
  nodeId,
2106
- command: CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND,
2447
+ command,
2107
2448
  params: {
2108
2449
  agentId: params.agentId,
2109
2450
  threadId: params.threadId,
2110
- limit: params.limit,
2111
- ...params.cursor ? { cursor: params.cursor } : {}
2451
+ ...request
2112
2452
  },
2113
2453
  timeoutMs: NODE_INVOKE_TIMEOUT_MS,
2114
2454
  scopes: ["operator.write"]
2115
- })));
2455
+ }));
2456
+ const page = node.commands?.includes("codex.sessionCatalog.transcript.read.v1") ? parseCodexCatalogTranscriptPage(await invoke(CODEX_CATALOG_TRANSCRIPT_READ_COMMAND, {
2457
+ cursor,
2458
+ limit
2459
+ })) : await readLegacyCodexTranscriptPage(async ({ cursor: turnCursor, limit: turnLimit }) => parseTranscriptPage(await invoke(CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND, {
2460
+ cursor: turnCursor,
2461
+ limit: turnLimit
2462
+ })), {
2463
+ threadId: params.threadId,
2464
+ cursor,
2465
+ limit
2466
+ });
2116
2467
  return {
2117
2468
  hostId: params.hostId,
2118
2469
  label: nodeLabel(node),
2119
2470
  threadId: params.threadId,
2120
- items: flattenTranscriptPageDesc(page),
2121
- ...page.nextCursor ? { nextCursor: page.nextCursor } : {},
2122
- ...page.backwardsCursor ? { backwardsCursor: page.backwardsCursor } : {}
2123
- };
2124
- }
2125
- //#endregion
2126
- //#region extensions/codex/src/session-catalog-transcript-item.ts
2127
- const CODEX_MESSAGE_TYPES = /* @__PURE__ */ new Map([
2128
- ["userMessage", "userMessage"],
2129
- ["agentMessage", "agentMessage"],
2130
- ["reasoning", "reasoning"]
2131
- ]);
2132
- const CODEX_TOOL_TYPES = /* @__PURE__ */ new Set([
2133
- "commandExecution",
2134
- "fileChange",
2135
- "mcpToolCall",
2136
- "dynamicToolCall",
2137
- "collabAgentToolCall",
2138
- "webSearch",
2139
- "imageView",
2140
- "imageGeneration"
2141
- ]);
2142
- function toGenericTranscriptItem(item) {
2143
- let type = CODEX_MESSAGE_TYPES.get(item.type);
2144
- if (!type && CODEX_TOOL_TYPES.has(item.type)) type = item.result !== void 0 || Boolean(item.aggregatedOutput) ? "toolResult" : "toolCall";
2145
- type ??= "other";
2146
- const fallback = item.title ?? item.name ?? item.tool ?? item.command ?? item.query ?? void 0;
2147
- const resultText = item.aggregatedOutput || (item.result === void 0 ? void 0 : JSON.stringify(item.result, null, 2));
2148
- const changesText = Array.isArray(item.changes) ? item.changes.map((change) => `${change.kind}: ${change.path}`).join("\n") || void 0 : void 0;
2149
- const text = item.text || resultText || changesText || fallback;
2150
- return {
2151
- id: item.id,
2152
- type,
2153
- ...text ? { text } : {},
2154
- raw: item
2471
+ ...page
2155
2472
  };
2156
2473
  }
2157
2474
  //#endregion
2158
2475
  //#region extensions/codex/src/session-upstream-activity.ts
2159
2476
  const CODEX_UPSTREAM_TURN_LIMIT = 100;
2160
- const CODEX_APP_SERVER_INVALID_REQUEST_CODE = -32600;
2161
- const CODEX_THREAD_NOT_LOADED_MESSAGE_PREFIX = "thread not loaded:";
2162
- function isCodexThreadGoneError(error) {
2163
- return error instanceof CodexAppServerRpcError && error.code === CODEX_APP_SERVER_INVALID_REQUEST_CODE && error.message.startsWith(CODEX_THREAD_NOT_LOADED_MESSAGE_PREFIX);
2164
- }
2165
2477
  function readMarker(probe) {
2166
2478
  if (!isRecord(probe.marker)) return;
2167
2479
  const turnId = probe.marker.turnId;
@@ -2224,14 +2536,14 @@ function normalizeUserMessageTexts(item) {
2224
2536
  const contentTexts = typed.content?.filter((input) => input.type === "text").map((input) => input.text.trim().replace(/\s+/g, " ")).filter(Boolean);
2225
2537
  return contentTexts?.length ? contentTexts : [(typed.text ?? "").trim().replace(/\s+/g, " ")];
2226
2538
  }
2227
- async function checkCodexUpstreamActivity(probes, control, resolveThreadId = async (probe) => probe.threadId) {
2539
+ async function checkCodexUpstreamActivity(probes, control, resolveThreadId = (probe) => probe.threadId) {
2228
2540
  return await control.withPinnedConnection(async (pinned) => {
2229
2541
  const activities = [];
2230
2542
  for (const probe of probes) {
2231
2543
  const fingerprint = upstreamConnectionFingerprint(probe);
2232
2544
  if (probe.upstreamKind !== "codex-app-server" || !fingerprint || fingerprint !== pinned.connectionFingerprint) continue;
2233
2545
  try {
2234
- const threadId = await resolveThreadId(probe);
2546
+ const threadId = resolveThreadId(probe);
2235
2547
  const page = await pinned.listTurnPage({
2236
2548
  threadId,
2237
2549
  limit: CODEX_UPSTREAM_TURN_LIMIT,
@@ -2243,7 +2555,7 @@ async function checkCodexUpstreamActivity(probes, control, resolveThreadId = asy
2243
2555
  try {
2244
2556
  await pinned.readThread(threadId, false);
2245
2557
  } catch (error) {
2246
- if (isCodexThreadGoneError(error)) activities.push({
2558
+ if (isCodexThreadReadMissingError(error, threadId)) activities.push({
2247
2559
  kind: "missing",
2248
2560
  sessionKey: probe.sessionKey
2249
2561
  });
@@ -2261,7 +2573,7 @@ async function checkCodexUpstreamActivity(probes, control, resolveThreadId = asy
2261
2573
  });
2262
2574
  }
2263
2575
  function createChecker(params) {
2264
- const resolveThreadId = async (probe) => {
2576
+ const resolveThreadId = (probe) => {
2265
2577
  const config = params.getRuntimeConfig();
2266
2578
  const sessionId = params.api.runtime.agent.session.getSessionEntry({
2267
2579
  agentId: probe.agentId,
@@ -2269,7 +2581,7 @@ function createChecker(params) {
2269
2581
  readConsistency: "latest"
2270
2582
  })?.sessionId?.trim();
2271
2583
  if (!sessionId) return probe.threadId;
2272
- const binding = await params.bindingStore.read(sessionBindingIdentity({
2584
+ const binding = params.bindingStore.read(sessionBindingIdentity({
2273
2585
  sessionId,
2274
2586
  sessionKey: probe.sessionKey,
2275
2587
  config
@@ -2311,7 +2623,7 @@ function existingCatalogHomeCandidates(value, label) {
2311
2623
  /** Resolves every local Codex store the operator already owns, without path disclosure. */
2312
2624
  function resolveCodexCatalogHomes(params) {
2313
2625
  const { config, env, ownerAgentId, pluginConfig } = params;
2314
- const ownerAgentDir = resolveAgentDir(config, ownerAgentId, env);
2626
+ const ownerAgentDir = resolveAgentDir$2(config, ownerAgentId, env);
2315
2627
  const configuredHomes = readCodexPluginConfig(pluginConfig).sessionCatalog?.homes ?? [];
2316
2628
  const base = resolveCodexSupervisionAppServerRuntimeOptions({
2317
2629
  pluginConfig,
@@ -2333,8 +2645,8 @@ function resolveCodexCatalogHomes(params) {
2333
2645
  label: "Local Codex · user",
2334
2646
  usesProcessHomeFallback: !processHomeConfigured
2335
2647
  });
2336
- const agentIds = listAgentIds(config).toSorted((left, right) => left === ownerAgentId ? -1 : right === ownerAgentId ? 1 : left.localeCompare(right));
2337
- candidates.push(...agentIds.flatMap((agentId) => existingCatalogHomeCandidates(resolveCodexAppServerHomeDir(resolveAgentDir(config, agentId, env)), agentId)), ...configuredHomes.flatMap((entry) => {
2648
+ const agentIds = listAgentIds$1(config).toSorted((left, right) => left === ownerAgentId ? -1 : right === ownerAgentId ? 1 : left.localeCompare(right));
2649
+ candidates.push(...agentIds.flatMap((agentId) => existingCatalogHomeCandidates(resolveCodexAppServerHomeDir(resolveAgentDir$2(config, agentId, env)), agentId)), ...configuredHomes.flatMap((entry) => {
2338
2650
  const { path: home, label } = typeof entry === "string" ? { path: entry } : entry;
2339
2651
  return existingCatalogHomeCandidates(home, label);
2340
2652
  }));
@@ -2375,7 +2687,7 @@ function createCodexCatalogHomeResolver(params) {
2375
2687
  const homesByConfig = /* @__PURE__ */ new WeakMap();
2376
2688
  const buildSnapshot = (config) => {
2377
2689
  const pluginConfig = params.getPluginConfig();
2378
- const homesByAgent = new Map(listAgentIds(config).map((agentId) => [agentId, resolveCodexCatalogHomes({
2690
+ const homesByAgent = new Map(listAgentIds$1(config).map((agentId) => [agentId, resolveCodexCatalogHomes({
2379
2691
  config,
2380
2692
  pluginConfig,
2381
2693
  ownerAgentId: agentId,
@@ -2400,96 +2712,6 @@ function createCodexCatalogHomeResolver(params) {
2400
2712
  } };
2401
2713
  }
2402
2714
  //#endregion
2403
- //#region extensions/codex/src/session-catalog-provenance.ts
2404
- const MAX_SESSION_META_BYTES = 1024 * 1024;
2405
- const SESSION_META_READ_CHUNK_BYTES = 64 * 1024;
2406
- const MAX_PROVENANCE_CACHE_ENTRIES = 2e4;
2407
- const provenanceByPath = /* @__PURE__ */ new Map();
2408
- function cacheProvenance(key, value) {
2409
- provenanceByPath.delete(key);
2410
- provenanceByPath.set(key, value);
2411
- while (provenanceByPath.size > MAX_PROVENANCE_CACHE_ENTRIES) {
2412
- const oldest = provenanceByPath.keys().next().value;
2413
- if (oldest === void 0) break;
2414
- provenanceByPath.delete(oldest);
2415
- }
2416
- }
2417
- /** Undefined means the metadata line is not durable enough to cache yet. */
2418
- async function readOpenClawOriginator(sessionsRoot, rolloutPath, threadId) {
2419
- let safeRoot;
2420
- try {
2421
- safeRoot = await root(sessionsRoot, {
2422
- hardlinks: "reject",
2423
- maxBytes: Number.MAX_SAFE_INTEGER,
2424
- symlinks: "reject"
2425
- });
2426
- } catch {
2427
- return;
2428
- }
2429
- const candidates = rolloutPath.endsWith(".zst") ? [rolloutPath, rolloutPath.slice(0, -4)] : [rolloutPath, `${rolloutPath}.zst`];
2430
- for (const candidate of candidates) {
2431
- let opened;
2432
- try {
2433
- opened = await safeRoot.open(path.relative(sessionsRoot, candidate));
2434
- } catch {
2435
- continue;
2436
- }
2437
- const input = opened.handle.createReadStream({
2438
- autoClose: false,
2439
- highWaterMark: SESSION_META_READ_CHUNK_BYTES
2440
- });
2441
- const reader = candidate.endsWith(".zst") ? input.pipe(createZstdDecompress()) : input;
2442
- try {
2443
- const chunks = [];
2444
- let bytesReadTotal = 0;
2445
- let line;
2446
- for await (const value of reader) {
2447
- const chunk = Buffer.isBuffer(value) ? value : Buffer.from(value);
2448
- const remaining = MAX_SESSION_META_BYTES - bytesReadTotal;
2449
- if (remaining <= 0) break;
2450
- const bounded = chunk.subarray(0, remaining);
2451
- bytesReadTotal += bounded.length;
2452
- const newline = bounded.indexOf(10);
2453
- chunks.push(newline >= 0 ? bounded.subarray(0, newline) : bounded);
2454
- if (newline >= 0) {
2455
- line = Buffer.concat(chunks).toString("utf8");
2456
- break;
2457
- }
2458
- }
2459
- if (!line) continue;
2460
- let parsed;
2461
- try {
2462
- parsed = JSON.parse(line);
2463
- } catch {
2464
- continue;
2465
- }
2466
- if (!isRecord(parsed) || parsed.type !== "session_meta" || !isRecord(parsed.payload)) return false;
2467
- const payload = parsed.payload;
2468
- return (payload.id ?? payload.session_id) === threadId && payload.originator === "openclaw";
2469
- } catch {
2470
- continue;
2471
- } finally {
2472
- reader.destroy();
2473
- input.destroy();
2474
- await opened.handle.close().catch(() => void 0);
2475
- }
2476
- }
2477
- }
2478
- /**
2479
- * Codex 0.147 reports OpenClaw app-server rollouts as `vscode`, so the rollout's
2480
- * immutable session metadata is the authoritative historical provenance.
2481
- */
2482
- async function isOpenClawManagedCodexThread(thread, localSessionsRoot) {
2483
- const rolloutPath = typeof thread.path === "string" ? thread.path.trim() : "";
2484
- if (!localSessionsRoot || !rolloutPath) return false;
2485
- const cacheKey = `${localSessionsRoot}\0${rolloutPath}`;
2486
- const cached = provenanceByPath.get(cacheKey);
2487
- if (cached !== void 0) return cached;
2488
- const managed = await readOpenClawOriginator(localSessionsRoot, rolloutPath, thread.id);
2489
- if (managed !== void 0) cacheProvenance(cacheKey, managed);
2490
- return managed ?? false;
2491
- }
2492
- //#endregion
2493
2715
  //#region extensions/codex/src/session-catalog-control.ts
2494
2716
  const CODEX_SESSION_CATALOG_LIST_TTL_MS = 32e3;
2495
2717
  const CODEX_SESSION_CATALOG_LIST_CACHE_MAX_ENTRIES = 32;
@@ -2508,21 +2730,76 @@ function createCodexCatalogRequestSnapshot(requestTimeoutMs, request) {
2508
2730
  requestTimeoutMs,
2509
2731
  listThreads: (params, timeoutMs) => request(CODEX_CONTROL_METHODS.listThreads, params, timeoutMs),
2510
2732
  listThreadTurns: (params) => request(CODEX_CONTROL_METHODS.listThreadTurns, params),
2511
- forkThread: (params) => request(CODEX_CONTROL_METHODS.forkThread, assertCodexThreadForkParams(params)),
2512
- readThread: async (threadId, includeTurns) => (await request(CODEX_CONTROL_METHODS.readThread, {
2733
+ listThreadItems: (params) => request(CODEX_CONTROL_METHODS.listThreadItems, params),
2734
+ forkThread: (params, assertCurrent) => request(CODEX_CONTROL_METHODS.forkThread, assertCodexThreadForkParams(params), void 0, assertCurrent),
2735
+ readThread: async (threadId, includeTurns, timeoutMs) => (await request(CODEX_CONTROL_METHODS.readThread, {
2513
2736
  threadId,
2514
2737
  includeTurns
2515
- })).thread,
2516
- archiveThread: async (threadId) => {
2517
- await request(CODEX_CONTROL_METHODS.archiveThread, { threadId });
2738
+ }, timeoutMs)).thread,
2739
+ archiveThread: async (threadId, assertCurrent) => {
2740
+ await request(CODEX_CONTROL_METHODS.archiveThread, { threadId }, void 0, assertCurrent);
2518
2741
  }
2519
2742
  };
2520
2743
  }
2521
2744
  function createCodexSessionCatalogControlFromRequests(params) {
2522
2745
  return {
2746
+ forkContext: params.forkContext,
2523
2747
  ...params.clientId ? { clientId: params.clientId } : {},
2524
2748
  ...params.connectionFingerprint ? { connectionFingerprint: params.connectionFingerprint } : {},
2525
2749
  withPinnedConnection: params.withPinnedConnection,
2750
+ async requireEligibleThread(threadId) {
2751
+ const requests = params.createRequestSnapshot();
2752
+ const deadline = params.now() + requests.requestTimeoutMs;
2753
+ const unverified = () => new CatalogParamsError("Codex session eligibility could not be verified. Refresh the catalog and verify the session in its native Codex home before retrying.");
2754
+ const remaining = () => {
2755
+ const timeoutMs = Math.ceil(deadline - params.now());
2756
+ if (timeoutMs <= 0) throw unverified();
2757
+ return timeoutMs;
2758
+ };
2759
+ const verify = async () => {
2760
+ if (params.sourceHomeId && await params.managedThreads?.has(params.sourceHomeId, threadId)) throw unverified();
2761
+ const root = params.localSessionsRoot;
2762
+ const thread = root ? await requests.readThread(threadId, false, remaining()) : void 0;
2763
+ if (root && (!thread || thread.id !== threadId || !isInteractiveThreadSource(thread.source))) throw unverified();
2764
+ let cursor;
2765
+ const seenCursors = /* @__PURE__ */ new Set();
2766
+ for (let pageIndex = 0; pageIndex < 100; pageIndex += 1) {
2767
+ const page = await requests.listThreads({
2768
+ archived: false,
2769
+ limit: 100,
2770
+ modelProviders: [],
2771
+ sortKey: root ? "recency_at" : "updated_at",
2772
+ sortDirection: "desc",
2773
+ ...root ? {
2774
+ useStateDbOnly: true,
2775
+ ...thread?.cwd ? { cwd: thread.cwd } : {}
2776
+ } : {},
2777
+ ...cursor ? { cursor } : {}
2778
+ }, remaining());
2779
+ remaining();
2780
+ const candidate = page.data.find((value) => value.id === threadId);
2781
+ if (candidate) {
2782
+ if (!isInteractiveThreadSource(candidate.source)) throw unverified();
2783
+ if (root && thread) {
2784
+ const rolloutPath = thread.path;
2785
+ if (!rolloutPath || !candidate.path || rolloutPath.replace(/\.zst$/u, "") !== candidate.path.replace(/\.zst$/u, "")) throw unverified();
2786
+ const metadata = await readCodexSessionMeta(root, rolloutPath, threadId);
2787
+ remaining();
2788
+ if (!metadata || !isInteractiveThreadSource(metadata.source) || metadata.originator === "openclaw") throw unverified();
2789
+ return thread;
2790
+ }
2791
+ return candidate;
2792
+ }
2793
+ const nextCursor = readControlCursor(page.nextCursor, "next response");
2794
+ if (!nextCursor || seenCursors.has(nextCursor)) throw unverified();
2795
+ seenCursors.add(nextCursor);
2796
+ cursor = nextCursor;
2797
+ }
2798
+ throw unverified();
2799
+ };
2800
+ return await withTimeout(verify(), requests.requestTimeoutMs, "Codex session eligibility could not be verified", unverified);
2801
+ },
2802
+ retireConnection: params.retireConnection,
2526
2803
  async listPage(pageParams) {
2527
2804
  const limit = normalizeLimit(pageParams.limit, "limit");
2528
2805
  const search = pageParams.searchTerm?.trim().toLocaleLowerCase() || void 0;
@@ -2584,11 +2861,12 @@ function createCodexSessionCatalogControlFromRequests(params) {
2584
2861
  async listTurnPage(listParams) {
2585
2862
  return await params.createRequestSnapshot().listThreadTurns(listParams);
2586
2863
  },
2587
- async forkThread(forkParams) {
2588
- return await params.createRequestSnapshot().forkThread(forkParams);
2864
+ listItemPage: (listParams) => params.createRequestSnapshot().listThreadItems(listParams),
2865
+ async forkThread(forkParams, assertCurrent) {
2866
+ return await params.createRequestSnapshot().forkThread(forkParams, assertCurrent);
2589
2867
  },
2590
- async archiveThread(threadId) {
2591
- await params.createRequestSnapshot().archiveThread(threadId);
2868
+ async archiveThread(threadId, assertCurrent) {
2869
+ await params.createRequestSnapshot().archiveThread(threadId, assertCurrent);
2592
2870
  }
2593
2871
  };
2594
2872
  }
@@ -2606,7 +2884,7 @@ function createCodexSessionCatalogControl(params) {
2606
2884
  const catalogPagesByConfig = /* @__PURE__ */ new WeakMap();
2607
2885
  const resolveRequestOptions = (startOptions, agentId, source) => {
2608
2886
  const runtimeConfig = params.getRuntimeConfig();
2609
- const agentDir = source?.agentDir ?? resolveAgentDir(runtimeConfig ?? {}, agentId);
2887
+ const agentDir = source?.agentDir ?? resolveAgentDir$2(runtimeConfig ?? {}, agentId);
2610
2888
  const resolvedStartOptions = source?.appServer.start ?? startOptions;
2611
2889
  if (!runtimeConfig) return {
2612
2890
  agentDir,
@@ -2633,8 +2911,9 @@ function createCodexSessionCatalogControl(params) {
2633
2911
  const pluginConfig = getPluginConfig();
2634
2912
  const runtime = source?.appServer ?? resolveCodexSupervisionAppServerRuntimeOptions({ pluginConfig });
2635
2913
  const requestOptions = resolveRequestOptions(runtime.start, agentId, source);
2636
- return createCodexCatalogRequestSnapshot(runtime.requestTimeoutMs, async (method, requestParams, timeoutMs) => await codexControlRequest(pluginConfig, method, requestParams, {
2914
+ return createCodexCatalogRequestSnapshot(runtime.requestTimeoutMs, async (method, requestParams, timeoutMs, assertCurrent) => await codexControlRequest(pluginConfig, method, requestParams, {
2637
2915
  ...requestOptions,
2916
+ assertCurrent,
2638
2917
  ...timeoutMs === void 0 ? {} : { timeoutMs }
2639
2918
  }));
2640
2919
  };
@@ -2650,18 +2929,31 @@ function createCodexSessionCatalogControl(params) {
2650
2929
  timeoutMs: runtime.requestTimeoutMs
2651
2930
  });
2652
2931
  try {
2653
- const requests = createCodexCatalogRequestSnapshot(runtime.requestTimeoutMs, async (method, requestParams, timeoutMs) => await requestCodexAppServerClientJson({
2932
+ const requests = createCodexCatalogRequestSnapshot(runtime.requestTimeoutMs, async (method, requestParams, timeoutMs, assertCurrent) => await requestCodexAppServerClientJson({
2654
2933
  client,
2655
2934
  method,
2656
2935
  requestParams,
2657
2936
  config: runtimeConfig,
2658
- timeoutMs: timeoutMs ?? runtime.requestTimeoutMs
2937
+ timeoutMs: timeoutMs ?? runtime.requestTimeoutMs,
2938
+ assertCurrent
2659
2939
  }));
2660
2940
  const pinnedControl = createCodexSessionCatalogControlFromRequests({
2941
+ forkContext: {
2942
+ client,
2943
+ appServer: runtime,
2944
+ pluginConfig,
2945
+ agentDir,
2946
+ localSessionsRoot: source?.localSessionsRoot
2947
+ },
2661
2948
  clientId: resolveCodexAppServerClientInstanceId(client),
2949
+ retireConnection: () => {
2950
+ retireSharedCodexAppServerClientIfCurrent(client);
2951
+ },
2662
2952
  connectionFingerprint: buildCodexAppServerConnectionFingerprint(runtime, agentDir),
2663
2953
  createRequestSnapshot: () => requests,
2664
2954
  ...source?.localSessionsRoot ? { localSessionsRoot: source.localSessionsRoot } : {},
2955
+ sourceHomeId: source?.sourceHomeId,
2956
+ managedThreads: params.managedThreads,
2665
2957
  now,
2666
2958
  withPinnedConnection: async (nestedRun) => await nestedRun(pinnedControl)
2667
2959
  });
@@ -2678,6 +2970,7 @@ function createCodexSessionCatalogControl(params) {
2678
2970
  });
2679
2971
  return {
2680
2972
  ...control,
2973
+ requireEligibleThread: (threadId) => withPinnedConnection((pinned) => pinned.requireEligibleThread(threadId)),
2681
2974
  async listPage(pageParams) {
2682
2975
  const runtimeConfig = params.getRuntimeConfig();
2683
2976
  if (!runtimeConfig) return await control.listPage(pageParams);
@@ -2688,7 +2981,7 @@ function createCodexSessionCatalogControl(params) {
2688
2981
  }
2689
2982
  const key = codexCatalogPageCacheKey(pageParams, agentId, source);
2690
2983
  const cached = cache.get(key);
2691
- if (pageParams.forceRefresh !== true && cached) {
2984
+ if (cached) {
2692
2985
  cache.delete(key);
2693
2986
  cache.set(key, cached);
2694
2987
  if (cached.expiresAt > now()) return cached.value ?? await cached.page;
@@ -2719,7 +3012,7 @@ function createCodexSessionCatalogControl(params) {
2719
3012
  });
2720
3013
  else cache.delete(key);
2721
3014
  };
2722
- if (pageParams.forceRefresh !== true && staleValue) {
3015
+ if (staleValue) {
2723
3016
  page.then(settle, restore);
2724
3017
  return staleValue;
2725
3018
  }
@@ -2751,14 +3044,22 @@ function createCodexSessionCatalogNodeInvokePolicies() {
2751
3044
  commands: [
2752
3045
  CODEX_APP_SERVER_THREADS_LIST_COMMAND,
2753
3046
  CODEX_APP_SERVER_THREAD_TURNS_LIST_COMMAND,
2754
- CODEX_TERMINAL_RESUME_COMMAND
3047
+ CODEX_CATALOG_TRANSCRIPT_READ_COMMAND,
3048
+ CODEX_TERMINAL_RESUME_COMMAND,
3049
+ CODEX_TERMINAL_START_COMMAND
2755
3050
  ],
2756
3051
  defaultPlatforms: [
2757
3052
  "macos",
2758
3053
  "linux",
2759
3054
  "windows"
2760
3055
  ],
2761
- handle: (context) => context.command === "codex.terminal.resume.v1" ? { ok: true } : context.invokeNode()
3056
+ handle: (context) => {
3057
+ if (context.command === "codex.terminal.start.v1") return context.client?.scopes?.includes("operator.admin") && context.config.gateway?.cliAgents?.enabled === true && context.config.gateway?.terminal?.enabled !== false ? { ok: true } : {
3058
+ ok: false,
3059
+ message: "Native terminal start requires operator.admin and enabled CLI agents and terminals"
3060
+ };
3061
+ return context.command === "codex.terminal.resume.v1" ? { ok: true } : context.invokeNode();
3062
+ }
2762
3063
  }];
2763
3064
  }
2764
3065
  function toGenericCatalogHost(host, localTerminalAvailable) {
@@ -2813,7 +3114,7 @@ function registerCodexSessionCatalog(params) {
2813
3114
  const homes = params.control.homesForAgent(agentId);
2814
3115
  return allowProcessHomeFallback === false ? homes.filter((home) => !home.usesProcessHomeFallback) : homes;
2815
3116
  };
2816
- const resolveRequestAgentId = (agentId) => resolveSessionAgentIds({
3117
+ const resolveRequestAgentId = (agentId) => resolveSessionAgentIdsStrict({
2817
3118
  config: params.getRuntimeConfig() ?? params.api.config,
2818
3119
  agentId
2819
3120
  }).sessionAgentId;
@@ -2846,10 +3147,13 @@ function registerCodexSessionCatalog(params) {
2846
3147
  resolveCreateSession: ({ agentId }) => resolveCodexCatalogCreateSession(params.getRuntimeConfig() ?? params.api.config, agentId),
2847
3148
  list: async (query) => {
2848
3149
  const localTerminalAvailable = resolveLocalCodexTerminalExecutable() !== void 0;
2849
- const { agentId: requestedAgentId, allowProcessHomeFallback, listNodes, onHost, sessionEntries, ...gatewayQuery } = query;
3150
+ const { agentId: requestedAgentId, allowProcessHomeFallback, listNodes, onHost, waitUntil, signal, sessionEntries, ...gatewayQuery } = query;
2850
3151
  const agentId = resolveRequestAgentId(requestedAgentId);
2851
- const mapHost = (host) => toGenericCatalogHost(host, localTerminalAvailable);
2852
3152
  const localHomes = [...catalogHomes(agentId, allowProcessHomeFallback)];
3153
+ const mapHost = (host) => ({
3154
+ ...toGenericCatalogHost(host, localTerminalAvailable),
3155
+ canStartTerminal: host.kind === "gateway" ? localTerminalAvailable && localHomes.some((home) => home.hostId === host.hostId && home.appServer.start.transport === "stdio") : host.canStartTerminal === true
3156
+ });
2853
3157
  return (await listCodexSessionCatalog({
2854
3158
  agentId,
2855
3159
  bindingStore: params.bindingStore,
@@ -2858,6 +3162,8 @@ function registerCodexSessionCatalog(params) {
2858
3162
  control: params.control,
2859
3163
  query: gatewayQuery,
2860
3164
  listNodes,
3165
+ waitUntil,
3166
+ signal,
2861
3167
  sessionEntries,
2862
3168
  localHomes,
2863
3169
  ...onHost ? { onHost: (host) => onHost(mapHost(host)) } : {}
@@ -2865,7 +3171,7 @@ function registerCodexSessionCatalog(params) {
2865
3171
  },
2866
3172
  read: async (request) => {
2867
3173
  const { agentId, source, control } = bindRequest(request);
2868
- const page = await readCodexSessionTranscript({
3174
+ return await readCodexSessionTranscript({
2869
3175
  agentId,
2870
3176
  runtime: params.api.runtime,
2871
3177
  control,
@@ -2875,10 +3181,6 @@ function registerCodexSessionCatalog(params) {
2875
3181
  limit: request.limit ?? 20,
2876
3182
  ...source ? { source } : {}
2877
3183
  });
2878
- return {
2879
- ...page,
2880
- items: page.items.map(toGenericTranscriptItem)
2881
- };
2882
3184
  },
2883
3185
  continueSession: async (request) => {
2884
3186
  const config = params.getRuntimeConfig();
@@ -2894,7 +3196,7 @@ function registerCodexSessionCatalog(params) {
2894
3196
  if (!isLocalCodexCatalogHost(request.hostId)) throw new CatalogParamsError("Codex session catalog hostId is invalid");
2895
3197
  const { agentId, source, control } = bindLocalRequest(request);
2896
3198
  let upstreamBaseline;
2897
- return codexUpstreamContinueResult((await continueLocalCodexSession({
3199
+ const continued = await continueLocalCodexSession({
2898
3200
  agentId,
2899
3201
  api: params.api,
2900
3202
  bindingStore: params.bindingStore,
@@ -2907,7 +3209,8 @@ function registerCodexSessionCatalog(params) {
2907
3209
  onContinued: (baseline) => {
2908
3210
  upstreamBaseline = baseline;
2909
3211
  }
2910
- })).sessionKey, request.threadId, upstreamBaseline);
3212
+ });
3213
+ return codexUpstreamContinueResult(continued.sessionKey, request.threadId, upstreamBaseline);
2911
3214
  },
2912
3215
  checkUpstreamActivity: (probes, policy) => checkUpstreamActivity(probes.filter((probe) => !isLocalCodexCatalogHost(probe.hostId) || policy?.allowProcessHomeFallback !== false || catalogHomes(probe.agentId, false).some((home) => home.hostId === probe.hostId))),
2913
3216
  archive: async (request) => {
@@ -2943,11 +3246,16 @@ function registerCodexSessionCatalog(params) {
2943
3246
  });
2944
3247
  },
2945
3248
  startTerminalSession: async (request) => {
2946
- if (!request.nodeId && catalogHomes(request.agentId, request.allowProcessHomeFallback).length === 0) throw new CatalogParamsError("local Codex sessions are unavailable in isolated state");
3249
+ const source = request.nodeId ? void 0 : resolveLocalCatalogHomeForThread({
3250
+ homes: [...catalogHomes(request.agentId, request.allowProcessHomeFallback)],
3251
+ hostId: request.hostId ?? "gateway:local"
3252
+ });
3253
+ if (source && source.appServer.start.transport !== "stdio") throw new CatalogParamsError("Native terminal start requires a local Codex source");
2947
3254
  return await startCodexCatalogTerminal({
2948
3255
  getPluginConfig: params.getPluginConfig,
2949
3256
  getRuntimeConfig: params.getRuntimeConfig,
2950
- ...request
3257
+ ...request,
3258
+ source
2951
3259
  });
2952
3260
  }
2953
3261
  });
@@ -2961,4 +3269,4 @@ const codexSessionCatalogRuntime = {
2961
3269
  archiveLocal: archiveLocalCodexSession
2962
3270
  };
2963
3271
  //#endregion
2964
- export { CODEX_LOCAL_SESSION_HOST_ID as a, createCodexCliSessionNodeInvokePolicies as c, resolveCodexCliSessionForBindingOnNode as d, resumeCodexCliSessionOnNode as f, createCodexSessionCatalogNodeHostCommands as i, formatCodexCliSessions as l, createCodexSessionCatalogNodeInvokePolicies as n, assertCodexArchiveDescendantsUnowned as o, createCodexSessionCatalogControl as r, createCodexCliSessionNodeHostCommands as s, codexSessionCatalogRuntime as t, listCodexCliSessionsOnNode as u };
3272
+ export { CODEX_LOCAL_SESSION_HOST_ID as a, readCodexStatusProbes as c, createCodexCliSessionNodeHostCommands as d, createCodexCliSessionNodeInvokePolicies as f, resumeCodexCliSessionOnNode as g, resolveCodexCliSessionForBindingOnNode as h, createCodexSessionCatalogNodeHostCommands as i, requestOptions as l, listCodexCliSessionsOnNode as m, createCodexSessionCatalogNodeInvokePolicies as n, assertCodexArchiveDescendantsUnowned as o, formatCodexCliSessions as p, createCodexSessionCatalogControl as r, codexControlRequest as s, codexSessionCatalogRuntime as t, safeCodexControlRequest as u };