@openclaw/codex 2026.9.5 → 2026.9.7

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 (174) hide show
  1. package/README.md +2 -0
  2. package/assets/icon.png +0 -0
  3. package/dist/.setup/{account-usage-runtime-C2MOIvfq.mjs → account-usage-runtime-DFhbh1zz.mjs} +6 -6
  4. package/dist/.setup/{apply-CJ0ZfmBG.mjs → apply-D3GcSoSa.mjs} +35 -55
  5. package/dist/.setup/{attempt-client-cleanup-BpYKdTA0.mjs → attempt-client-cleanup-DOP-cW5j.mjs} +179 -73
  6. package/dist/.setup/attempt-workspace-context-C5HigshI.mjs +164 -0
  7. package/dist/.setup/{auth-binding-DhLWa_7K.mjs → auth-binding-BLWz7stm.mjs} +14 -1
  8. package/dist/.setup/{auth-cache-key-QirmgiCo.mjs → auth-cache-key-Dbvq-QgB.mjs} +41 -111
  9. package/dist/.setup/{auth-profile-WqZtZfXN.mjs → auth-profile-B0okYl2D.mjs} +1 -2
  10. package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
  11. package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
  12. package/dist/.setup/auth-start-options-BPIPeEX8.mjs +44 -0
  13. package/dist/.setup/{binding-connection-Bm38niE8.mjs → binding-connection-DTtN1KrX.mjs} +2 -2
  14. package/dist/.setup/{binding-connection.runtime-CnRMC0ta.mjs → binding-connection.runtime-6TpgD1M6.mjs} +5 -5
  15. package/dist/.setup/{bounded-turn-BtlZcivC.mjs → bounded-turn-CbjoN2OW.mjs} +140 -37
  16. package/dist/.setup/bounded-turn-isolation-B8K_ylqk.mjs +14 -0
  17. package/dist/.setup/{build-state-Xx9ybZqa.mjs → build-state-Da1_z0kd.mjs} +1 -1
  18. package/dist/.setup/{capabilities-_vXcZGSx.mjs → capabilities-B3WEZl4e.mjs} +2 -10
  19. package/dist/.setup/{client-vXs2929Q.mjs → client-N0JpgSzF.mjs} +635 -872
  20. package/dist/.setup/client-catalog-response-5HHLdcde.mjs +285 -0
  21. package/dist/.setup/{command-dispatch-naHIZwwF.mjs → command-dispatch-BeAoaovI.mjs} +9 -8
  22. package/dist/.setup/{command-formatters-CGEkdawF.mjs → command-formatters-R3ckE3Co.mjs} +36 -64
  23. package/dist/.setup/{command-handlers-BtAJEW1X.mjs → command-handlers-ylTMS00z.mjs} +301 -488
  24. package/dist/.setup/{command-rpc-DHFpn4le.mjs → command-rpc-CGJGUQ6Y.mjs} +40 -28
  25. package/dist/.setup/compact-C2WamavS.mjs +776 -0
  26. package/dist/.setup/{config-CedDWjM-.mjs → config-DKhXquav.mjs} +26 -28
  27. package/dist/.setup/{config-options-BsanJ8QG.mjs → config-options-CzyWLTJt.mjs} +68 -65
  28. package/dist/.setup/{config-parsing-CcB9iPoq.mjs → config-parsing-b088LyhT.mjs} +57 -60
  29. package/dist/.setup/{config-runtime-CeAhl_h4.mjs → config-runtime-C0NbLms1.mjs} +2 -2
  30. package/dist/.setup/{config-security-BRQ2mgW9.mjs → config-security-CE-FcGw9.mjs} +51 -215
  31. package/dist/.setup/{config-utils-DujwEnhg.mjs → config-utils-D0CXecki.mjs} +3 -13
  32. package/dist/.setup/context-engine-projection-PezSESth.mjs +387 -0
  33. package/dist/.setup/{conversation-binding-DhzrpTM6.mjs → conversation-binding-3Min8qvP.mjs} +71 -105
  34. package/dist/.setup/{conversation-binding-data-CrcyZk6O.mjs → conversation-binding-data-C746z5Pn.mjs} +8 -12
  35. package/dist/.setup/{conversation-control-DN53ns41.mjs → conversation-control-CEP8X5JC.mjs} +30 -69
  36. package/dist/.setup/{cyber-failover-DYwbmf1o.mjs → cyber-failover-XV0nNRZu.mjs} +1 -1
  37. package/dist/.setup/{cyber-failover-notice-e9G3J9-e.mjs → cyber-failover-notice-RNpGpTyP.mjs} +1 -1
  38. package/dist/.setup/{dynamic-tools-D16lOp67.mjs → dynamic-tools-DzlsLUuS.mjs} +3136 -3167
  39. package/dist/.setup/{effective-mcp-catalog-K6kLCN8X.mjs → effective-mcp-catalog-CxUtC-xG.mjs} +8 -17
  40. package/dist/.setup/{ephemeral-turn-R1gA8Sn6.mjs → ephemeral-turn-CDsPJOa2.mjs} +9 -6
  41. package/dist/.setup/event-projector-assistant-message-CigreO4K.mjs +69 -0
  42. package/dist/.setup/{event-projector-usage-C7Ntzs-f.mjs → event-projector-usage-03R0I2dM.mjs} +4 -26
  43. package/dist/.setup/event-projector-values-DJSz8BTk.mjs +106 -0
  44. package/dist/.setup/incognito-session-DWYapfwf.mjs +2 -0
  45. package/dist/.setup/{inference-context-fulhZuz8.mjs → inference-context-D-9gWlTR.mjs} +16 -18
  46. package/dist/.setup/inference-dispatch-CMZtjQyz.mjs +359 -0
  47. package/dist/.setup/inference-metadata-CgIf7sOx.mjs +81 -0
  48. package/dist/.setup/inference-proxy-DhcXOgUE.mjs +561 -0
  49. package/dist/.setup/{isolated-completion-B6srt48J.mjs → isolated-completion-Dm3PzxJk.mjs} +15 -11
  50. package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
  51. package/dist/.setup/{managed-binary-DP6Rib3P.mjs → managed-binary-kiWRhNRE.mjs} +16 -9
  52. package/dist/.setup/{model-catalog-DkvUuBey.mjs → model-catalog-PAuZgJC6.mjs} +14 -14
  53. package/dist/.setup/model-runtime-BdqzOMkU.mjs +11 -0
  54. package/dist/.setup/{models-BDFgm_G-.mjs → models-DHXZtVVk.mjs} +9 -18
  55. package/dist/.setup/{native-auth-BLZGqFjw.mjs → native-auth-CuG53tu4.mjs} +5 -5
  56. package/dist/.setup/{native-execution-policy-YBMHSzC4.mjs → native-execution-policy-BxZJy5mP.mjs} +6 -31
  57. package/dist/.setup/{native-subagent-monitor-B1hxzIOy.mjs → native-subagent-monitor-Dpsmf2wz.mjs} +4422 -3808
  58. package/dist/.setup/native-subagent-pending-assignments-DwkCzLvD.mjs +203 -0
  59. package/dist/.setup/native-task-assignments-DMQ0g3hu.mjs +194 -0
  60. package/dist/.setup/{node-cli-sessions-Cl8b2ZF3.mjs → node-cli-sessions-HC4W6FLI.mjs} +99 -111
  61. package/dist/.setup/node-exec-server.runtime-BgBfWQqo.mjs +244 -0
  62. package/dist/.setup/{plan-xD7WKYTM.mjs → plan-Y-QIvrKB.mjs} +118 -163
  63. package/dist/.setup/{plugin-activation-r8nq5Jzp.mjs → plugin-activation-cNHRfaxU.mjs} +10 -47
  64. package/dist/.setup/{plugin-app-cache-key-BOxKP7db.mjs → plugin-app-cache-key-BL9B5H0L.mjs} +9 -42
  65. package/dist/.setup/{plugin-inventory-DUle8DLz.mjs → plugin-inventory-Cky2XJUw.mjs} +32 -23
  66. package/dist/.setup/{plugin-marketplace-discovery-P-Vndfqq.mjs → plugin-marketplace-discovery-D_dV9FXa.mjs} +22 -25
  67. package/dist/.setup/{plugin-metadata-cache-DsS5buNa.mjs → plugin-metadata-cache-DsjsL1dr.mjs} +328 -318
  68. package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
  69. package/dist/.setup/provider-DH8NSzGt.mjs +51 -0
  70. package/dist/.setup/{provider-capabilities-B8J4Dy-O.mjs → provider-capabilities-CAxoF-X1.mjs} +35 -12
  71. package/dist/.setup/{rate-limits-DOEGYRNs.mjs → rate-limits-B7-X2TgG.mjs} +123 -166
  72. package/dist/.setup/{request-BioXJLYa.mjs → request-BHZ1nz8d.mjs} +25 -16
  73. package/dist/.setup/responses-oauth-twnuTaAa.mjs +93 -0
  74. package/dist/.setup/rpc-error-BlD2vuzn.mjs +31 -0
  75. package/dist/.setup/{run-attempt-rlA07pUv.mjs → run-attempt-D8RZUOzY.mjs} +2046 -1454
  76. package/dist/.setup/{run-attempt-lifecycle-B7JbY4-F.mjs → run-attempt-lifecycle-C-6O3Exn.mjs} +10 -24
  77. package/dist/.setup/{runtime-artifact-Dy38Z5iU.mjs → runtime-artifact-DDQ-HGXf.mjs} +43 -47
  78. package/dist/.setup/runtime-artifact-connection-D68qQn1m.mjs +78 -0
  79. package/dist/.setup/sandbox-guard-DjCz2eOw.mjs +126 -0
  80. package/dist/.setup/scope-DOC1Xjc8.mjs +6 -0
  81. package/dist/.setup/session-binding-DGCYAfvD.mjs +443 -0
  82. package/dist/.setup/{session-binding-record-TH3xA0zj.mjs → session-binding-record-Oe9L-CNY.mjs} +73 -132
  83. package/dist/.setup/{session-binding-sidecars-CKYEcfNn.mjs → session-binding-sidecars-jo6H8dFr.mjs} +242 -60
  84. package/dist/.setup/{session-catalog-fUdWDx-U.mjs → session-catalog-4sCdv5qx.mjs} +598 -452
  85. package/dist/.setup/{session-catalog-adoption-CR_whkEW.mjs → session-catalog-adoption-BU_pHWG7.mjs} +59 -26
  86. package/dist/.setup/{session-catalog-archive-CwZaPacf.mjs → session-catalog-archive-spHfihri.mjs} +7 -7
  87. package/dist/.setup/session-catalog-events-DePDzzjc.mjs +139 -0
  88. package/dist/.setup/session-catalog-home-id-vlih0B_g.mjs +22 -0
  89. package/dist/.setup/{session-catalog-index-B141u15s.mjs → session-catalog-index-CZvDzC6z.mjs} +286 -182
  90. package/dist/.setup/{session-catalog-index-order-Cc5BzlQr.mjs → session-catalog-index-order-BcQpHks3.mjs} +2 -2
  91. package/dist/.setup/{session-catalog-index-state-BAuDztHN.mjs → session-catalog-index-state-TeO7EyO-.mjs} +6 -2
  92. package/dist/.setup/session-catalog-limits-DDxOsQeC.mjs +14 -0
  93. package/dist/.setup/session-catalog-list-request-CvtxrYSe.mjs +170 -0
  94. package/dist/.setup/session-catalog-native-projection-DjhwGL6x.mjs +178 -0
  95. package/dist/.setup/{session-catalog-node-adoption-BVRsYOSh.mjs → session-catalog-node-adoption-DgXvSGu0.mjs} +30 -35
  96. package/dist/.setup/{session-catalog-node-continue-Dowv8osw.mjs → session-catalog-node-continue-DjKXHBbi.mjs} +25 -18
  97. package/dist/.setup/session-catalog-node-lookup-CtAXI5W5.mjs +56 -0
  98. package/dist/.setup/{session-catalog-native-projection-ChV3cCtm.mjs → session-catalog-parsing-CPGaPYr-.mjs} +14 -180
  99. package/dist/.setup/{session-catalog-projection-C1Bj9Jl-.mjs → session-catalog-projection-Cr5qP5Dj.mjs} +15 -5
  100. package/dist/.setup/session-catalog-provenance-DoAvktcj.mjs +95 -0
  101. package/dist/.setup/{session-cli-DIj-IE-E.mjs → session-cli-Cn_nCqfq.mjs} +11 -24
  102. package/dist/.setup/{session-history-C--SrLI8.mjs → session-history-D83lJWHG.mjs} +16 -23
  103. package/dist/.setup/{session-history-import-JHXT5Xfi.mjs → session-history-import-WhX-X33a.mjs} +7 -10
  104. package/dist/.setup/session-history-read-Ov3StuL4.mjs +133 -0
  105. package/dist/.setup/{session-history.worker-CpNj7H_P.mjs → session-history.worker-MnPKncqP.mjs} +14 -23
  106. package/dist/.setup/session-initialization-6aveB9qb.mjs +22 -0
  107. package/dist/.setup/{session-permission-policy-DhurXY0l.mjs → session-permission-policy-Ci_fVX6y.mjs} +4 -8
  108. package/dist/.setup/{session-retirement-CU87AOg9.mjs → session-retirement-qMTxkNx2.mjs} +31 -12
  109. package/dist/.setup/{settled-turn-context-fmwouT5I.mjs → settled-turn-context-D4yj4rxf.mjs} +1 -1
  110. package/dist/.setup/{settled-turn-finalizer-CiY2bLFK.mjs → settled-turn-finalizer-BsvEabkw.mjs} +17 -13
  111. package/dist/.setup/{shared-client-BpTtuLQ5.mjs → shared-client-DqyTMYug.mjs} +826 -485
  112. package/dist/.setup/shared-client-lifecycle-DJIgqgMw.mjs +153 -0
  113. package/dist/.setup/{side-question-DSCZyoBD.mjs → side-question-C4kCmQy1.mjs} +206 -334
  114. package/dist/.setup/{source-Bjp4UQij.mjs → source-CS_7GFJZ.mjs} +82 -139
  115. package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
  116. package/dist/.setup/{thread-archive-guard-jmecf6OJ.mjs → thread-archive-guard-C9frICzX.mjs} +10 -3
  117. package/dist/.setup/thread-lifecycle-DPflntEJ.mjs +2362 -0
  118. package/dist/.setup/{incognito-session-uhrBF6wJ.mjs → thread-ownership-queue-Dp24d7-F.mjs} +1 -2
  119. package/dist/.setup/{thread-ownership-skomxnIt.mjs → thread-ownership-roNP0l1c.mjs} +47 -10
  120. package/dist/.setup/thread-policy-DTd-DtkX.mjs +109 -0
  121. package/dist/.setup/thread-requests-B_bRnMGN.mjs +2102 -0
  122. package/dist/.setup/{thread-resume-BkNpj9Pz.mjs → thread-resume-CdDaoc7T.mjs} +6 -6
  123. package/dist/.setup/timeout-CXOfNoaX.mjs +46 -0
  124. package/dist/.setup/transcript-history-projection-DRSifI-1.mjs +201 -0
  125. package/dist/.setup/{transcript-mirror-attestation-CzM-94NC.mjs → transcript-mirror-attestation-YSegaktH.mjs} +3 -4
  126. package/dist/.setup/{transcript-mirror-D4qp_leP.mjs → transcript-mirror-kxmCYsuF.mjs} +224 -206
  127. package/dist/.setup/{transport-stdio-Cmx94Oiy.mjs → transport-stdio-Bdegpn2V.mjs} +138 -75
  128. package/dist/.setup/transport-websocket-BTBE-LD9.mjs +211 -0
  129. package/dist/.setup/{upstream-prompt-provenance-LphB8slG.mjs → upstream-prompt-provenance-fXkKbPVC.mjs} +3 -9
  130. package/dist/.setup/{upstream-session-fork-D3UKxgVD.mjs → upstream-session-fork-CBeVjBpT.mjs} +48 -40
  131. package/dist/.setup/{usage-BOgBEKso.mjs → usage-BMzNxeR_.mjs} +5 -4
  132. package/dist/.setup/{usage-limit-error-C0ow7DOc.mjs → usage-limit-error-QXerDo3i.mjs} +5 -20
  133. package/dist/.setup/{web-search-provider.runtime-CTGuJt6i.mjs → web-search-provider.runtime-P77OUeoo.mjs} +10 -20
  134. package/dist/api.js +25 -16
  135. package/dist/catalog-page-worker-entrypoint.js +12 -0
  136. package/dist/catalog-page.worker.js +9 -0
  137. package/dist/cli-metadata.js +1 -1
  138. package/dist/doctor-contract-api.js +50 -8
  139. package/dist/harness.js +344 -1
  140. package/dist/index.js +345 -346
  141. package/dist/media-understanding-provider.js +9 -22
  142. package/dist/migration-provider-api.js +1 -1
  143. package/dist/provider-discovery.js +1 -1
  144. package/dist/session-history-worker-runtime.js +16 -33
  145. package/dist/session-history.worker.js +1 -1
  146. package/openclaw.plugin.json +14 -2
  147. package/package.json +11 -7
  148. package/dist/.setup/attempt-context-VXVwTCfS.mjs +0 -636
  149. package/dist/.setup/compact-C3i3qlzj.mjs +0 -687
  150. package/dist/.setup/event-projector-assistant-message-DNUR1s0A.mjs +0 -105
  151. package/dist/.setup/event-projector-items-DyTQ_XqL.mjs +0 -200
  152. package/dist/.setup/harness-rxaaoN-o.mjs +0 -349
  153. package/dist/.setup/history-rejection-B4nOXNXF.mjs +0 -13
  154. package/dist/.setup/image-payload-sanitizer-tupAr-2o.mjs +0 -79
  155. package/dist/.setup/inference-proxy-CC15HuSi.mjs +0 -311
  156. package/dist/.setup/launch-args-DnMak49W.mjs +0 -111
  157. package/dist/.setup/local-runtime-attribution-B_2mnqhb.mjs +0 -19
  158. package/dist/.setup/native-subagent-history-YXaL6dMd.mjs +0 -135
  159. package/dist/.setup/native-subagent-task-ids-DsuOonKv.mjs +0 -411
  160. package/dist/.setup/node-exec-server.runtime-BaViBKNz.mjs +0 -226
  161. package/dist/.setup/plugin-approval-roundtrip-CjfzafAZ.mjs +0 -122
  162. package/dist/.setup/provider-fFh_-VR3.mjs +0 -50
  163. package/dist/.setup/rolldown-runtime-8H4AJuhK.mjs +0 -14
  164. package/dist/.setup/sandbox-guard-BBP-RbTf.mjs +0 -166
  165. package/dist/.setup/session-binding-_0W_fHRr.mjs +0 -721
  166. package/dist/.setup/session-catalog-events-jMcYfQyd.mjs +0 -250
  167. package/dist/.setup/session-history-read-B3ZfSUbF.mjs +0 -50
  168. package/dist/.setup/session-initialization-DH1z6fXn.mjs +0 -75
  169. package/dist/.setup/thread-history-page-BTx-4xsg.mjs +0 -83
  170. package/dist/.setup/thread-lifecycle-Cik28qWn.mjs +0 -4904
  171. package/dist/.setup/thread-policy-Cjs4B8xf.mjs +0 -54
  172. package/dist/.setup/timeout-DZF05XrR.mjs +0 -63
  173. package/dist/.setup/transcript-history-projection-DHNBDhCU.mjs +0 -243
  174. package/dist/.setup/version-CoqzXPgK.mjs +0 -12
@@ -1,19 +1,22 @@
1
- import { o as formatCodexDisplayText } from "./command-formatters-CGEkdawF.mjs";
1
+ import { A as unwrapNodeInvokePayload, x as readBoundedOptionalString } from "./session-catalog-parsing-CPGaPYr-.mjs";
2
+ import { r as resolveCodexAppServerUserHomeDir } from "./auth-start-options-BPIPeEX8.mjs";
3
+ import { o as formatCodexDisplayText } from "./command-formatters-R3ckE3Co.mjs";
4
+ import { t as codexCatalogHomeId } from "./session-catalog-home-id-vlih0B_g.mjs";
2
5
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
6
+ import { runCommandBuffered, withCommandProcessScope } from "openclaw/plugin-sdk/process-runtime";
3
7
  import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
4
- import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
5
8
  import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
6
9
  import path from "node:path";
7
- import os from "node:os";
8
10
  import fs from "node:fs/promises";
9
11
  import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
10
12
  import process from "node:process";
13
+ import { parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
11
14
  import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
12
15
  import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
13
16
  //#region extensions/codex/src/jsonl-lines.ts
14
17
  const JSONL_STREAM_THRESHOLD_BYTES = 4194304;
15
18
  const JSONL_READ_CHUNK_BYTES = 1048576;
16
- async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTES) {
19
+ async function visitJsonlLines(file, visitor) {
17
20
  let size;
18
21
  try {
19
22
  size = (await fs.stat(file)).size;
@@ -40,7 +43,7 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
40
43
  let lineCount = 0;
41
44
  for (const line of content.split(/\r?\n/u)) {
42
45
  lineCount += 1;
43
- if (visitor(line) === false) break;
46
+ visitor(line);
44
47
  }
45
48
  return {
46
49
  ok: true,
@@ -56,7 +59,7 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
56
59
  lineCount: 0
57
60
  };
58
61
  }
59
- const buffer = Buffer.allocUnsafe(chunkBytes);
62
+ const buffer = Buffer.allocUnsafe(JSONL_READ_CHUNK_BYTES);
60
63
  const decoder = new TextDecoder();
61
64
  let pendingFragments = [];
62
65
  let lineCount = 0;
@@ -77,10 +80,7 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
77
80
  }
78
81
  const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
79
82
  lineCount += 1;
80
- if (visitor(line) === false) return {
81
- ok: true,
82
- lineCount
83
- };
83
+ visitor(line);
84
84
  lineStart = newline + 1;
85
85
  }
86
86
  if (lineStart < content.length) pendingFragments.push(content.slice(lineStart));
@@ -110,21 +110,25 @@ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTE
110
110
  //#region extensions/codex/src/node-cli-sessions.ts
111
111
  const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
112
112
  const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
113
+ const CODEX_CLI_SESSION_SOURCE_CAPABILITY = "codex-cli-session-source";
114
+ const CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE = "Update the node and approve its refreshed capabilities before continuing this Codex catalog session.";
113
115
  const DEFAULT_SESSION_LIMIT = 10;
114
116
  const MAX_SESSION_LIMIT = 50;
115
117
  const DEFAULT_RESUME_TIMEOUT_MS = 12e5;
116
118
  const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
117
119
  const activeResumeSessions = /* @__PURE__ */ new Set();
118
- function createCodexCliSessionNodeHostCommands() {
120
+ function createCodexCliSessionNodeHostCommands(resolveCatalogSource) {
119
121
  return [{
120
122
  command: CODEX_CLI_SESSIONS_LIST_COMMAND,
121
123
  cap: "codex-cli-sessions",
124
+ hasActiveWork: () => false,
122
125
  handle: listLocalCodexCliSessions
123
126
  }, {
124
127
  command: CODEX_CLI_SESSION_RESUME_COMMAND,
125
- cap: "codex-cli-sessions",
128
+ cap: CODEX_CLI_SESSION_SOURCE_CAPABILITY,
126
129
  dangerous: true,
127
- handle: resumeLocalCodexCliSession
130
+ hasActiveWork: () => activeResumeSessions.size > 0,
131
+ handle: (paramsJSON, _io, context) => resumeLocalCodexCliSession(paramsJSON, resolveCatalogSource, context)
128
132
  }];
129
133
  }
130
134
  function createCodexCliSessionNodeInvokePolicies() {
@@ -139,7 +143,11 @@ function createCodexCliSessionNodeInvokePolicies() {
139
143
  }, {
140
144
  commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
141
145
  dangerous: true,
142
- handle: (ctx) => ctx.invokeNode()
146
+ handle: (ctx) => isRecord(ctx.params) && (ctx.params.agentId !== void 0 || ctx.params.sourceHomeId !== void 0) && !ctx.node?.caps?.includes("codex-cli-session-source") ? {
147
+ ok: false,
148
+ code: "CODEX_NODE_SOURCE_UNAVAILABLE",
149
+ message: CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE
150
+ } : ctx.invokeNode()
143
151
  }];
144
152
  }
145
153
  async function listCodexCliSessionsOnNode(params) {
@@ -176,18 +184,38 @@ async function resolveCodexCliSessionForBindingOnNode(params) {
176
184
  };
177
185
  }
178
186
  async function resumeCodexCliSessionOnNode(params) {
179
- const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
187
+ let catalogAgentId;
188
+ let catalogHomeId;
189
+ if (params.sessionKey) {
190
+ const { adoptionSessionKeyRest, CODEX_NODE_SESSION_KEY_PREFIX, readNodeSessionMarker } = await import("./session-catalog-node-adoption-DgXvSGu0.mjs");
191
+ const entry = params.runtime.agent.session.getSessionEntry({
192
+ sessionKey: params.sessionKey,
193
+ readConsistency: "latest"
194
+ });
195
+ const codex = entry?.pluginExtensions?.codex;
196
+ if (adoptionSessionKeyRest(params.sessionKey).startsWith(CODEX_NODE_SESSION_KEY_PREFIX) || isRecord(codex) && codex.sessionCatalog !== void 0) {
197
+ const marker = entry ? readNodeSessionMarker(entry) : void 0;
198
+ catalogAgentId = params.agentId?.trim();
199
+ if (!catalogAgentId || parseAgentSessionKey(params.sessionKey)?.agentId !== catalogAgentId || !marker || marker.initializing === true || marker.nodeId !== params.nodeId || marker.sourceHostId !== `node:${params.nodeId}` || marker.sourceThreadId !== params.sessionId || entry?.initializationPending === true || entry?.agentHarnessId !== "codex" || entry.modelSelectionLocked !== true) throw new Error("Codex catalog session changed before its node turn could run.");
200
+ if (!marker.sourceHomeId) throw new Error("This Codex catalog session has no saved source home. Reopen it from the catalog to continue in a new chat.");
201
+ catalogHomeId = marker.sourceHomeId;
202
+ }
203
+ }
204
+ const raw = await params.runtime.nodes.invoke({
180
205
  nodeId: params.nodeId,
181
206
  command: CODEX_CLI_SESSION_RESUME_COMMAND,
182
207
  params: {
183
208
  sessionId: params.sessionId,
209
+ ...catalogAgentId ? { agentId: catalogAgentId } : {},
210
+ ...catalogHomeId ? { sourceHomeId: catalogHomeId } : {},
184
211
  prompt: params.prompt,
185
212
  cwd: params.cwd,
186
213
  timeoutMs: params.timeoutMs
187
214
  },
188
215
  timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3,
189
216
  scopes: ["operator.write"]
190
- }));
217
+ });
218
+ const payload = unwrapNodeInvokePayload(raw, "Codex CLI node command returned malformed payloadJSON.");
191
219
  if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
192
220
  return {
193
221
  ok: true,
@@ -203,12 +231,11 @@ function formatCodexCliSessions(params) {
203
231
  })].join("\n");
204
232
  }
205
233
  async function listLocalCodexCliSessions(paramsJSON) {
206
- const params = readRecordParam(paramsJSON);
234
+ const params = parseJsonRecord(paramsJSON);
207
235
  const limit = normalizeLimit(params.limit);
208
236
  const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
209
- const codexHome = resolveCodexHome();
237
+ const codexHome = resolveCodexAppServerUserHomeDir();
210
238
  const summaries = await readHistorySessions(codexHome);
211
- await hydrateSessionFiles(codexHome, summaries);
212
239
  await hydrateSessionsFromSessionFiles(codexHome, summaries);
213
240
  const sessions = [...summaries.values()].filter((session) => {
214
241
  if (!filter) return true;
@@ -217,26 +244,45 @@ async function listLocalCodexCliSessions(paramsJSON) {
217
244
  session.cwd,
218
245
  session.lastMessage
219
246
  ].some((value) => value?.toLowerCase().includes(filter));
220
- }).toSorted((a, b) => compareOptionalStringsDesc(a.updatedAt, b.updatedAt)).slice(0, limit);
247
+ }).toSorted((a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")).slice(0, limit);
221
248
  return JSON.stringify({
222
249
  sessions,
223
250
  codexHome
224
251
  });
225
252
  }
226
- async function resumeLocalCodexCliSession(paramsJSON) {
227
- const params = readRecordParam(paramsJSON);
253
+ async function resumeLocalCodexCliSession(paramsJSON, resolveCatalogSource, context) {
254
+ context?.signal?.throwIfAborted();
255
+ const params = parseJsonRecord(paramsJSON);
228
256
  const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
229
257
  const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
258
+ const expectedHomeId = readBoundedOptionalString(params, "sourceHomeId", 256);
230
259
  if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
231
260
  if (!prompt) throw new Error("Missing Codex CLI prompt.");
232
- if (activeResumeSessions.has(sessionId)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
233
- activeResumeSessions.add(sessionId);
261
+ let codexHome = resolveCodexAppServerUserHomeDir();
262
+ let sourceHomeId;
263
+ let assertCurrent;
264
+ if (params.agentId !== void 0) {
265
+ if (typeof params.agentId !== "string" || !params.agentId.trim()) throw new Error("Codex catalog agent id must be a nonempty string.");
266
+ const source = await resolveCatalogSource(params.agentId.trim());
267
+ if (source.transport !== "stdio") throw new Error("Codex CLI continuation requires a local Codex catalog source.");
268
+ codexHome = source.codexHome;
269
+ sourceHomeId = source.sourceHomeId;
270
+ assertCurrent = () => source.assertCurrent();
271
+ } else sourceHomeId = codexCatalogHomeId(codexHome);
272
+ if (expectedHomeId && expectedHomeId !== sourceHomeId) throw new Error("Codex catalog source home changed. Reopen the session from the catalog.");
273
+ context?.signal?.throwIfAborted();
274
+ const resumeKey = `${sourceHomeId}\0${sessionId}`;
275
+ if (activeResumeSessions.has(resumeKey)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
276
+ activeResumeSessions.add(resumeKey);
234
277
  try {
235
278
  const text = await runCodexExecResume({
236
279
  sessionId,
237
280
  prompt,
238
281
  cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
239
- timeoutMs: normalizeTimeoutMs(params.timeoutMs)
282
+ timeoutMs: normalizeTimeoutMs(params.timeoutMs),
283
+ codexHome,
284
+ assertCurrent,
285
+ signal: context?.signal
240
286
  });
241
287
  return JSON.stringify({
242
288
  ok: true,
@@ -244,7 +290,7 @@ async function resumeLocalCodexCliSession(paramsJSON) {
244
290
  text: text.trim() || "Codex completed without a text reply."
245
291
  });
246
292
  } finally {
247
- activeResumeSessions.delete(sessionId);
293
+ activeResumeSessions.delete(resumeKey);
248
294
  }
249
295
  }
250
296
  async function runCodexExecResume(params) {
@@ -266,22 +312,31 @@ async function runCodexExecResume(params) {
266
312
  execPath: process.execPath,
267
313
  packageName: "@openai/codex"
268
314
  }), args);
269
- const result = await runCommandBuffered([invocation.command, ...invocation.argv], {
270
- cwd: params.cwd || process.cwd(),
271
- input: params.prompt,
272
- env: process.env,
273
- killGraceMs: 2e3,
274
- killProcessTree: false,
275
- terminateOnOutputError: true,
276
- timeoutMs: params.timeoutMs
277
- });
315
+ const result = await withCommandProcessScope(() => {
316
+ params.assertCurrent?.();
317
+ return runCommandBuffered([invocation.command, ...invocation.argv], {
318
+ cwd: params.cwd || process.cwd(),
319
+ input: params.prompt,
320
+ env: {
321
+ ...process.env,
322
+ CODEX_HOME: params.codexHome
323
+ },
324
+ killGraceMs: 2e3,
325
+ signal: params.signal,
326
+ terminateOnOutputError: true,
327
+ timeoutMs: params.timeoutMs
328
+ });
329
+ }, params.signal);
330
+ params.signal?.throwIfAborted();
278
331
  if (result.termination === "timeout") throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
279
332
  if (result.termination === "error" && result.error) throw result.error;
280
333
  if (result.code !== 0) {
281
334
  const message = result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim() || `codex exec resume exited with code ${String(result.code)}`;
282
335
  throw new Error(message);
283
336
  }
284
- return await fs.readFile(outputPath, "utf8");
337
+ const text = await fs.readFile(outputPath, "utf8");
338
+ params.signal?.throwIfAborted();
339
+ return text;
285
340
  } finally {
286
341
  await fs.rm(path.dirname(outputPath), {
287
342
  recursive: true,
@@ -292,15 +347,8 @@ async function runCodexExecResume(params) {
292
347
  async function readHistorySessions(codexHome) {
293
348
  const summaries = /* @__PURE__ */ new Map();
294
349
  if (!(await visitJsonlLines(path.join(codexHome, "history.jsonl"), (line) => {
295
- const trimmed = line.trim();
296
- if (!trimmed) return;
297
- let parsed;
298
- try {
299
- parsed = JSON.parse(trimmed);
300
- } catch {
301
- return;
302
- }
303
- if (!isRecord(parsed) || typeof parsed.session_id !== "string") return;
350
+ const parsed = parseJsonRecord(line.trim());
351
+ if (typeof parsed.session_id !== "string") return;
304
352
  const sessionId = parsed.session_id.trim();
305
353
  if (!sessionId) return;
306
354
  const entry = summaries.get(sessionId) ?? {
@@ -314,23 +362,6 @@ async function readHistorySessions(codexHome) {
314
362
  })).ok) return /* @__PURE__ */ new Map();
315
363
  return summaries;
316
364
  }
317
- async function hydrateSessionFiles(codexHome, summaries) {
318
- if (summaries.size === 0) return;
319
- const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
320
- const pending = new Set(summaries.keys());
321
- for (const file of files) {
322
- const basename = path.basename(file);
323
- const sessionId = [...pending].find((id) => basename.includes(id));
324
- if (!sessionId) continue;
325
- const entry = summaries.get(sessionId);
326
- if (!entry) continue;
327
- entry.sessionFile = file;
328
- const cwd = readSessionMetaCwd(await readFirstLine(file) ?? "");
329
- if (cwd) entry.cwd = cwd;
330
- pending.delete(sessionId);
331
- if (pending.size === 0) return;
332
- }
333
- }
334
365
  async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
335
366
  const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
336
367
  for (const file of files) {
@@ -355,15 +386,7 @@ async function readSessionFileSummary(file) {
355
386
  let lastMessage;
356
387
  let messageCount = 0;
357
388
  const result = await visitJsonlLines(file, (line) => {
358
- const trimmed = line.trim();
359
- if (!trimmed) return;
360
- let parsed;
361
- try {
362
- parsed = JSON.parse(trimmed);
363
- } catch {
364
- return;
365
- }
366
- if (!isRecord(parsed)) return;
389
+ const parsed = parseJsonRecord(line.trim());
367
390
  if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
368
391
  if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
369
392
  if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
@@ -376,8 +399,7 @@ async function readSessionFileSummary(file) {
376
399
  lastMessage = truncateText(messageText, 140);
377
400
  }
378
401
  });
379
- if (!result.ok) return null;
380
- if (result.lineCount === 0) return null;
402
+ if (!result.ok || result.lineCount === 0) return null;
381
403
  if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
382
404
  if (!sessionId) return null;
383
405
  return {
@@ -405,19 +427,8 @@ async function findSessionFiles(dir, maxDepth) {
405
427
  }
406
428
  return files;
407
429
  }
408
- function readSessionMetaCwd(line) {
409
- try {
410
- const parsed = JSON.parse(line);
411
- if (!isRecord(parsed) || parsed.type !== "session_meta" || !isRecord(parsed.payload)) return;
412
- return typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim() ? parsed.payload.cwd.trim() : void 0;
413
- } catch {
414
- return;
415
- }
416
- }
417
430
  function readResponseItemMessageText(parsed) {
418
- if (parsed.type !== "response_item" || !isRecord(parsed.payload)) return;
419
- if (parsed.payload.type !== "message") return;
420
- if ((typeof parsed.payload.role === "string" ? parsed.payload.role : "") !== "user") return;
431
+ if (parsed.type !== "response_item" || !isRecord(parsed.payload) || parsed.payload.type !== "message" || parsed.payload.role !== "user") return;
421
432
  const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
422
433
  if (!isRecord(entry)) return [];
423
434
  const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
@@ -446,7 +457,7 @@ async function resolveCodexCliNode(params) {
446
457
  return expectDefined(usable[0], "single usable Codex CLI node");
447
458
  }
448
459
  function parseCodexCliSessionsListResult(raw) {
449
- const payload = unwrapNodeInvokePayload(raw);
460
+ const payload = unwrapNodeInvokePayload(raw, "Codex CLI node command returned malformed payloadJSON.");
450
461
  if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
451
462
  return {
452
463
  codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
@@ -463,17 +474,7 @@ function parseCodexCliSessionsListResult(raw) {
463
474
  })
464
475
  };
465
476
  }
466
- function unwrapNodeInvokePayload(raw) {
467
- const record = isRecord(raw) ? raw : {};
468
- if (typeof record.payloadJSON === "string" && record.payloadJSON.trim()) try {
469
- return JSON.parse(record.payloadJSON);
470
- } catch (error) {
471
- throw new Error("Codex CLI node command returned malformed payloadJSON.", { cause: error });
472
- }
473
- if ("payload" in record) return record.payload;
474
- return raw;
475
- }
476
- function readRecordParam(paramsJSON) {
477
+ function parseJsonRecord(paramsJSON) {
477
478
  if (!paramsJSON?.trim()) return {};
478
479
  try {
479
480
  const parsed = JSON.parse(paramsJSON);
@@ -482,16 +483,6 @@ function readRecordParam(paramsJSON) {
482
483
  return {};
483
484
  }
484
485
  }
485
- function resolveCodexHome() {
486
- return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
487
- }
488
- async function readFirstLine(file) {
489
- let firstLine;
490
- return (await visitJsonlLines(file, (line) => {
491
- firstLine = line;
492
- return false;
493
- }, 65536)).ok ? firstLine : void 0;
494
- }
495
486
  async function readFileMtimeIso(file) {
496
487
  try {
497
488
  return (await fs.stat(file)).mtime.toISOString();
@@ -509,9 +500,6 @@ function truncateText(value, max) {
509
500
  if (value.length <= max) return value;
510
501
  return `${truncateUtf16Safe(value, Math.max(0, max - 3))}...`;
511
502
  }
512
- function compareOptionalStringsDesc(a, b) {
513
- return (b ?? "").localeCompare(a ?? "");
514
- }
515
503
  function readNodeId(node) {
516
504
  if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
517
505
  return node.nodeId;
@@ -524,4 +512,4 @@ function formatNodeLabel(node) {
524
512
  ].filter(Boolean).join(" / ") || "node";
525
513
  }
526
514
  //#endregion
527
- export { listCodexCliSessionsOnNode as a, formatCodexCliSessions as i, createCodexCliSessionNodeHostCommands as n, resolveCodexCliSessionForBindingOnNode as o, createCodexCliSessionNodeInvokePolicies as r, resumeCodexCliSessionOnNode as s, CODEX_CLI_SESSION_RESUME_COMMAND as t };
515
+ export { createCodexCliSessionNodeInvokePolicies as a, resolveCodexCliSessionForBindingOnNode as c, createCodexCliSessionNodeHostCommands as i, resumeCodexCliSessionOnNode as l, CODEX_CLI_SESSION_SOURCE_CAPABILITY as n, formatCodexCliSessions as o, CODEX_CLI_SESSION_SOURCE_UPGRADE_MESSAGE as r, listCodexCliSessionsOnNode as s, CODEX_CLI_SESSION_RESUME_COMMAND as t };
@@ -0,0 +1,244 @@
1
+ import { i as resolveManagedCodexNativeCommand, r as resolveManagedCodexAppServerStartOptions, t as isManagedCodexDesktopCommand } from "./managed-binary-kiWRhNRE.mjs";
2
+ import { a as closeCodexAppServerTransportAndWait, t as createStdioTransport } from "./transport-stdio-Bdegpn2V.mjs";
3
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { stripVTControlCharacters } from "node:util";
5
+ import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
6
+ import path from "node:path";
7
+ import { mkdir } from "node:fs/promises";
8
+ import { once } from "node:events";
9
+ import { resolvePreferredOpenClawTmpDir, tempWorkspace } from "openclaw/plugin-sdk/temp-path";
10
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
11
+ import { StringDecoder } from "node:string_decoder";
12
+ import { sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
13
+ //#region extensions/codex/src/node-exec-server.runtime.ts
14
+ const MAX_CODEX_EXEC_SERVER_MESSAGE_BYTES = 67108864;
15
+ const MAX_CODEX_EXEC_SERVER_STDERR_BYTES = 4096;
16
+ const CODEX_EXEC_SERVER_READY_LINE = "codex_exec_server::server::transport: codex-exec-server listening on stdio";
17
+ const CODEX_EXEC_SERVER_TERMINATION_GRACE_MS = 1e3;
18
+ const CODEX_EXEC_SERVER_REAP_TIMEOUT_MS = 5e3;
19
+ const NODE_EXEC_SERVER_PLATFORM_ENVIRONMENT = /^(?:SYSTEMROOT|WINDIR|COMSPEC|PATHEXT|TEMP|TMP|TMPDIR)$/iu;
20
+ function validateNodeExecServerMessage(message) {
21
+ if (message.byteLength === 0 || message.byteLength > MAX_CODEX_EXEC_SERVER_MESSAGE_BYTES) throw new Error("Codex exec-server JSON-RPC message exceeds its 64 MiB limit.");
22
+ const encoded = Buffer.from(message.buffer, message.byteOffset, message.byteLength);
23
+ if (encoded.includes(10) || encoded.includes(13)) throw new Error("Codex exec-server JSON-RPC frames must contain exactly one message.");
24
+ let decoded;
25
+ try {
26
+ const text = new TextDecoder("utf-8", { fatal: true }).decode(encoded);
27
+ decoded = JSON.parse(text);
28
+ } catch {
29
+ throw new Error("Codex exec-server received malformed UTF-8 or JSON-RPC.");
30
+ }
31
+ if (!isRecord(decoded) || decoded.jsonrpc !== void 0 && decoded.jsonrpc !== "2.0" || typeof decoded.method !== "string" && !("id" in decoded && ("result" in decoded || "error" in decoded))) throw new Error("Codex exec-server received an invalid JSON-RPC message.");
32
+ return encoded;
33
+ }
34
+ function nodeExecServerAbortError(signal) {
35
+ return signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Codex node exec-server connection closed.");
36
+ }
37
+ function writeNodeExecServerMessage(child, message, signal) {
38
+ if (signal.aborted) throw nodeExecServerAbortError(signal);
39
+ const payload = Buffer.concat([message, Buffer.from("\n")]);
40
+ if (!child.stdin.write(payload)) return once(child.stdin, "drain", { signal }).then(() => void 0);
41
+ }
42
+ async function relayNodeExecServerOutput(child, send) {
43
+ let fragments = [];
44
+ let pendingBytes = 0;
45
+ for await (const rawChunk of child.stdout) {
46
+ const chunk = Buffer.isBuffer(rawChunk) ? rawChunk : Buffer.from(rawChunk);
47
+ let offset = 0;
48
+ while (offset < chunk.byteLength) {
49
+ const newline = chunk.indexOf(10, offset);
50
+ const fragment = chunk.subarray(offset, newline === -1 ? chunk.byteLength : newline);
51
+ const nextLength = pendingBytes + fragment.byteLength;
52
+ if (nextLength > 67108865) throw new Error("Codex exec-server stdout message exceeds its 64 MiB limit.");
53
+ if (fragment.byteLength > 0) fragments.push(fragment);
54
+ pendingBytes = nextLength;
55
+ if (newline === -1) {
56
+ if (pendingBytes > MAX_CODEX_EXEC_SERVER_MESSAGE_BYTES && fragment[fragment.byteLength - 1] !== 13) throw new Error("Codex exec-server stdout message exceeds its 64 MiB limit.");
57
+ break;
58
+ }
59
+ const trailing = fragments.at(-1);
60
+ if (trailing?.[trailing.byteLength - 1] === 13) {
61
+ pendingBytes -= 1;
62
+ if (trailing.byteLength === 1) fragments.pop();
63
+ else fragments[fragments.length - 1] = trailing.subarray(0, trailing.byteLength - 1);
64
+ }
65
+ const message = validateNodeExecServerMessage(fragments.length === 1 ? fragments[0] : Buffer.concat(fragments, pendingBytes));
66
+ fragments = [];
67
+ pendingBytes = 0;
68
+ await send(message);
69
+ offset = newline + 1;
70
+ }
71
+ }
72
+ if (pendingBytes > 0) throw new Error("Codex exec-server stdout ended with an unterminated JSON-RPC message.");
73
+ }
74
+ function createNodeExecServerProcessOwner(child, closed, releaseResources, activeProcesses) {
75
+ let termination;
76
+ let hasClosed = false;
77
+ const settled = closed.then(async () => {
78
+ hasClosed = true;
79
+ await termination?.catch(() => {});
80
+ await releaseResources();
81
+ activeProcesses.delete(terminate);
82
+ });
83
+ settled.catch(() => {});
84
+ const terminate = async () => {
85
+ if (!hasClosed) await (termination ??= (async () => {
86
+ if (process.platform === "win32" && child.pid) killProcessTree(child.pid, { graceMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS });
87
+ const { exited } = await closeCodexAppServerTransportAndWait(child, {
88
+ forceKillDelayMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS,
89
+ exitTimeoutMs: CODEX_EXEC_SERVER_REAP_TIMEOUT_MS
90
+ });
91
+ if (!exited) throw new Error("Codex node exec-server process tree did not terminate.");
92
+ })());
93
+ await settled;
94
+ };
95
+ activeProcesses.add(terminate);
96
+ return terminate;
97
+ }
98
+ /** Runs the one-connection paired-node exec-server after lightweight command admission. */
99
+ async function runCodexNodeExecServer(params) {
100
+ const { io, workspace } = params;
101
+ const frames = io.frames;
102
+ const cwd = workspace.workspaceDir;
103
+ let writes;
104
+ let output;
105
+ let temporaryHome;
106
+ let unsubscribe;
107
+ const releaseResources = async () => {
108
+ try {
109
+ await Promise.allSettled([output, writes]);
110
+ await temporaryHome?.cleanup();
111
+ } finally {
112
+ workspace.release();
113
+ }
114
+ };
115
+ let close = releaseResources;
116
+ const { promise: disconnected, reject: rejectDisconnected } = createDeferred();
117
+ disconnected.catch(() => {});
118
+ const onAbort = () => {
119
+ const error = nodeExecServerAbortError(io.signal);
120
+ rejectDisconnected(error);
121
+ };
122
+ io.signal.addEventListener("abort", onAbort, { once: true });
123
+ try {
124
+ if (!frames) throw new Error("Codex node exec-server requires duplex frames.");
125
+ if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
126
+ temporaryHome = await tempWorkspace({
127
+ rootDir: resolvePreferredOpenClawTmpDir(),
128
+ prefix: "codex-node-exec-server-"
129
+ });
130
+ const { dir } = temporaryHome;
131
+ const codexHome = path.join(dir, ".codex");
132
+ await mkdir(codexHome, {
133
+ recursive: true,
134
+ mode: 448
135
+ });
136
+ const resolved = await resolveManagedCodexAppServerStartOptions({
137
+ transport: "stdio",
138
+ command: "codex",
139
+ commandSource: "managed",
140
+ managedCommandOrder: "package-first",
141
+ args: [
142
+ "exec-server",
143
+ "--listen",
144
+ "stdio"
145
+ ],
146
+ headers: {}
147
+ });
148
+ const native = resolveManagedCodexNativeCommand(resolved.command);
149
+ if (!native || isManagedCodexDesktopCommand(resolved.command)) throw new Error("Codex node exec-server requires the pinned managed package binary.");
150
+ const baseEnv = sanitizeEnvVars(process.env, {
151
+ strictMode: true,
152
+ customAllowedPatterns: [NODE_EXEC_SERVER_PLATFORM_ENVIRONMENT]
153
+ }).allowed;
154
+ if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
155
+ params.assertExecAuthorized();
156
+ const nativeReady = createDeferred();
157
+ const exit = createDeferred();
158
+ let stderr = Buffer.alloc(0);
159
+ let startupSettled = false;
160
+ let pendingLine = "";
161
+ const decoder = new StringDecoder("utf8");
162
+ const child = await createStdioTransport({
163
+ transport: "stdio",
164
+ command: native,
165
+ commandSource: "resolved-managed",
166
+ args: resolved.args,
167
+ headers: {},
168
+ cwd,
169
+ env: {
170
+ HOME: workspace.homeDir ?? dir,
171
+ CODEX_HOME: codexHome,
172
+ RUST_LOG: "error,opentelemetry_sdk=off,opentelemetry_otlp=off,codex_exec_server::server::transport=info",
173
+ ...process.platform === "win32" ? { USERPROFILE: workspace.homeDir ?? dir } : {}
174
+ },
175
+ clearEnv: ["NODE_OPTIONS"]
176
+ }, baseEnv, () => {
177
+ if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
178
+ params.assertExecAuthorized();
179
+ }, (spawned) => {
180
+ spawned.once("close", (code, signal) => exit.resolve({
181
+ code,
182
+ signal
183
+ }));
184
+ close = createNodeExecServerProcessOwner(spawned, exit.promise, releaseResources, params.activeProcesses);
185
+ output = relayNodeExecServerOutput(spawned, frames.send.bind(frames));
186
+ output.catch((error) => {
187
+ rejectDisconnected(error instanceof Error ? error : new Error(String(error)));
188
+ });
189
+ spawned.once("error", rejectDisconnected);
190
+ spawned.stdin.on("error", rejectDisconnected);
191
+ spawned.stderr.on("data", (chunk) => {
192
+ const next = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
193
+ stderr = Buffer.concat([stderr, next.subarray(-4096)]).subarray(-4096);
194
+ if (startupSettled) return;
195
+ const lines = (pendingLine + decoder.write(next)).split("\n");
196
+ pendingLine = lines.pop();
197
+ for (const line of [...lines, pendingLine]) if (Buffer.byteLength(line, "utf8") > MAX_CODEX_EXEC_SERVER_STDERR_BYTES) {
198
+ startupSettled = true;
199
+ pendingLine = "";
200
+ rejectDisconnected(/* @__PURE__ */ new Error("Codex node startup diagnostic line exceeded 4 KiB."));
201
+ return;
202
+ }
203
+ if (lines.some((line) => stripVTControlCharacters(line).trimEnd().endsWith(CODEX_EXEC_SERVER_READY_LINE))) {
204
+ startupSettled = true;
205
+ pendingLine = "";
206
+ nativeReady.resolve();
207
+ }
208
+ });
209
+ });
210
+ const stopped = exit.promise.then((outcome) => {
211
+ const diagnostic = stderr.toString("utf8").trim();
212
+ throw new Error(`Codex node exec-server exited (code ${outcome.code ?? "none"}, signal ${outcome.signal ?? "none"})${diagnostic ? `: ${diagnostic}` : "."}`);
213
+ });
214
+ await Promise.race([
215
+ nativeReady.promise,
216
+ stopped,
217
+ disconnected
218
+ ]);
219
+ if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
220
+ if (child.exitCode !== null || child.signalCode !== null) await stopped;
221
+ params.assertExecAuthorized();
222
+ unsubscribe = frames.onMessage((message) => {
223
+ const encoded = validateNodeExecServerMessage(message);
224
+ const operation = writes ? writes.then(() => writeNodeExecServerMessage(child, encoded, io.signal)) : writeNodeExecServerMessage(child, encoded, io.signal);
225
+ if (!operation) return;
226
+ const observed = operation.catch(() => {});
227
+ writes = observed;
228
+ observed.then(() => {
229
+ if (writes === observed) writes = void 0;
230
+ });
231
+ return operation;
232
+ });
233
+ return await Promise.race([stopped, disconnected]);
234
+ } finally {
235
+ io.signal.removeEventListener("abort", onAbort);
236
+ try {
237
+ unsubscribe?.();
238
+ } finally {
239
+ await close();
240
+ }
241
+ }
242
+ }
243
+ //#endregion
244
+ export { runCodexNodeExecServer };