@openclaw/codex 2026.9.2 → 2026.9.4

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 (120) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +29 -26
  3. package/dist/apply-CvT8tW5i.js +356 -0
  4. package/dist/{attempt-client-cleanup-Bp1ZOnk2.js → attempt-client-cleanup-DKHzbwt5.js} +38 -214
  5. package/dist/{attempt-context-CSazkWns.js → attempt-context-D44yeDII.js} +9 -7
  6. package/dist/{event-projector-usage-ZqEVbAEY.js → attempt-notifications-C9XufG6l.js} +3 -106
  7. package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-CWlKVccJ.js} +2 -2
  8. package/dist/auth-cache-key-7orQae_j.js +498 -0
  9. package/dist/auth-profile-CwzO4_RG.js +43 -0
  10. package/dist/auth-profile-selection-DFu9e167.js +45 -0
  11. package/dist/binding-connection-D3udKbIE.js +60 -0
  12. package/dist/{bounded-turn-DxGJRYbY.js → bounded-turn-C_Hneydj.js} +28 -125
  13. package/dist/build-state-BCmg9-5p.js +19 -0
  14. package/dist/{capabilities-B1mgvpP8.js → capabilities-8vx68WLH.js} +1 -1
  15. package/dist/cli-metadata.js +1 -1
  16. package/dist/client-B57KWPQx.js +4005 -0
  17. package/dist/command-dispatch-BpduNGVf.js +21 -0
  18. package/dist/{command-formatters-DSbGMXA0.js → command-formatters-lxlkNZgU.js} +4 -3
  19. package/dist/{command-handlers-DOJvGv6-.js → command-handlers-tiL9iWj7.js} +788 -135
  20. package/dist/command-rpc-CR5EOEZX.js +233 -0
  21. package/dist/{compact-Y_xBqrHN.js → compact-B_H3AOd7.js} +19 -14
  22. package/dist/config-oIORaQ5T.js +106 -0
  23. package/dist/{config-CyapYSji.js → config-options-CXMq1T-C.js} +205 -593
  24. package/dist/config-runtime-D0zGNIHu.js +11 -0
  25. package/dist/{config-utils-C57JiJrU.js → config-utils-DujwEnhg.js} +2 -4
  26. package/dist/conversation-binding-DsB4-O7U.js +900 -0
  27. package/dist/{plugin-marketplace-discovery-B7gHXJ3d.js → conversation-control-CtUu1b3m.js} +11 -143
  28. package/dist/doctor-contract-api.js +5 -5
  29. package/dist/{dynamic-tools-BwpvTxaX.js → dynamic-tools-CmN5RAip.js} +133 -76
  30. package/dist/{effective-mcp-catalog-CY9Jn4bA.js → effective-mcp-catalog-Bd73bqA8.js} +3 -3
  31. package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
  32. package/dist/ephemeral-turn-D78_VmzO.js +101 -0
  33. package/dist/{event-projector-assistant-message-B9kp6iGC.js → event-projector-assistant-message-BmBM4qBV.js} +12 -25
  34. package/dist/event-projector-items-5VhsECCO.js +200 -0
  35. package/dist/event-projector-usage-BfXmpOec.js +102 -0
  36. package/dist/{harness-Czh2Sdoc.js → harness-C7rRa7bm.js} +48 -24
  37. package/dist/harness.js +1 -1
  38. package/dist/history-rejection-B4nOXNXF.js +13 -0
  39. package/dist/incognito-session-uhrBF6wJ.js +15 -0
  40. package/dist/index.js +406 -2915
  41. package/dist/inference-context-Dr_mzvk4.js +94 -0
  42. package/dist/inference-proxy-HGXKUIre.js +311 -0
  43. package/dist/{isolated-completion-CRh-51AK.js → isolated-completion-DWVb5U2J.js} +7 -4
  44. package/dist/launch-args-RXQwn8zV.js +90 -0
  45. package/dist/{managed-binary-DcpnJF9x.js → managed-binary-D22rNEcR.js} +13 -103
  46. package/dist/managed-thread-store-D1t5jh3G.js +66 -0
  47. package/dist/media-understanding-provider.js +4 -1
  48. package/dist/migration-provider-api.js +2 -0
  49. package/dist/{model-catalog-DT5vo6qo.js → model-catalog-Dq2n8f_p.js} +6 -5
  50. package/dist/{models-CcrgSMQR.js → models-PCKOim-h.js} +3 -3
  51. package/dist/native-execution-policy-umd0tgh4.js +128 -0
  52. package/dist/native-subagent-history-DjtPloaU.js +140 -0
  53. package/dist/native-subagent-history-owner-Pcp7JuVF.js +34 -0
  54. package/dist/{native-subagent-monitor-B3bvQGcg.js → native-subagent-monitor-fQ7eIXEf.js} +115 -460
  55. package/dist/native-subagent-task-ids-BzTdT0Uw.js +387 -0
  56. package/dist/node-cli-sessions-DKaOmVUh.js +527 -0
  57. package/dist/{node-exec-server.runtime-nfbUOtUB.js → node-exec-server.runtime-Cl2vapEi.js} +55 -24
  58. package/dist/plan-7uG-Fp36.js +947 -0
  59. package/dist/plugin-activation-BYQculil.js +138 -0
  60. package/dist/{thread-resume-BLgqkUc1.js → plugin-app-cache-key-d0eKxy7b.js} +4 -52
  61. package/dist/{plugin-approval-roundtrip-aQVdyL5g.js → plugin-approval-roundtrip-Bz3FT8W5.js} +1 -1
  62. package/dist/plugin-inventory-oL4Na1ZG.js +328 -0
  63. package/dist/plugin-marketplace-discovery-P-Vndfqq.js +137 -0
  64. package/dist/{plugin-metadata-cache-BwCLTY_U.js → plugin-metadata-cache-ClPJpRio.js} +17 -9
  65. package/dist/protocol-5bh1G-H7.js +275 -0
  66. package/dist/provider-MQ8PVkuE.js +50 -0
  67. package/dist/{provider-capabilities-BpG9Ltc5.js → provider-capabilities-BvCUL69A.js} +2 -2
  68. package/dist/{rate-limits-Fx_l4eNa.js → rate-limits-CBMmWBr9.js} +1 -1
  69. package/dist/request-B7hEkBGq.js +212 -0
  70. package/dist/rpc-error-ttN_QjEm.js +30 -0
  71. package/dist/{run-attempt-BzthFQSM.js → run-attempt-CvsAAGa0.js} +664 -377
  72. package/dist/{runtime-artifact-Bh_gf6-t.js → runtime-artifact-BJ0rXBZ_.js} +27 -27
  73. package/dist/sandbox-guard-BxgyoM8M.js +166 -0
  74. package/dist/{session-binding-4Ki04sW-.js → session-binding-CI8UuilT.js} +30 -48
  75. package/dist/{session-binding-record-CyaXZvTe.js → session-binding-record-Bcvslnhf.js} +12 -26
  76. package/dist/session-catalog-adoption-tcZi1IfX.js +251 -0
  77. package/dist/session-catalog-archive-CwKg8Xz2.js +56 -0
  78. package/dist/session-catalog-ct-AH01I.js +1477 -0
  79. package/dist/session-catalog-home-id-CwhLdC03.js +18 -0
  80. package/dist/session-catalog-node-adoption-B1tgDW-m.js +182 -0
  81. package/dist/session-catalog-node-continue-Ci-DXyJP.js +238 -0
  82. package/dist/session-catalog-provenance-D-dIfRAH.js +96 -0
  83. package/dist/{session-cli-BjLhpmS1.js → session-cli-DNN_jhGc.js} +3 -2
  84. package/dist/{session-history-DWmMqiqW.js → session-history-Cz-tOOYj.js} +6 -3
  85. package/dist/{session-upstream-marker-DReTpGz6.js → session-history-import-DwyijFI6.js} +5 -50
  86. package/dist/{session-history-read-LHldwvyh.js → session-history-read-cxPUxyVV.js} +21 -7
  87. package/dist/session-history-worker-runtime.js +18 -6
  88. package/dist/session-history.worker-BeK1YOyT.js +378 -0
  89. package/dist/session-history.worker.js +1 -1
  90. package/dist/{session-initialization-BCFVnLL4.js → session-initialization-Ct0_Hzmg.js} +2 -2
  91. package/dist/{session-permission-policy-BaueHmSj.js → session-permission-policy-BBmXA8Eg.js} +4 -7
  92. package/dist/{session-retirement-DNdmYdFc.js → session-retirement-B2YfbAAT.js} +6 -5
  93. package/dist/session-upstream-marker-D15C9NHp.js +46 -0
  94. package/dist/{settled-turn-context-BOM3Y1pl.js → settled-turn-context-Du6gdUDd.js} +9 -9
  95. package/dist/{settled-turn-finalizer-BI-b1sry.js → settled-turn-finalizer-Dn91stmc.js} +8 -7
  96. package/dist/shared-client-CscigXXL.js +3109 -0
  97. package/dist/{side-question-B6DWAw1A.js → side-question-BGMJQf4U.js} +65 -170
  98. package/dist/source-CElXU0Oy.js +518 -0
  99. package/dist/thread-archive-guard-DfpBr-Sz.js +53 -0
  100. package/dist/thread-history-page-D0JIp48V.js +361 -0
  101. package/dist/{thread-lifecycle-DPK30jad.js → thread-lifecycle-DXl1-I6b.js} +187 -639
  102. package/dist/{thread-ownership-CbucAmmp.js → thread-ownership-BsDRTsyp.js} +13 -15
  103. package/dist/thread-policy-BuEQfE0g.js +54 -0
  104. package/dist/thread-resume-DsDOZpFg.js +52 -0
  105. package/dist/timeout-BVw8aaM1.js +33 -0
  106. package/dist/transcript-history-projection-v17wXgdK.js +238 -0
  107. package/dist/{transcript-mirror-_njEB84z.js → transcript-mirror-C5CKgNy6.js} +50 -260
  108. package/dist/{transcript-mirror-attestation-Byl34PC-.js → transcript-mirror-attestation-DibE0krH.js} +17 -4
  109. package/dist/{transport-stdio-CzTngV0j.js → transport-stdio-BGjHoYLr.js} +73 -43
  110. package/dist/{upstream-session-fork-1fwbNKzQ.js → upstream-session-fork-BZerMte_.js} +27 -19
  111. package/dist/{usage-i9fv5-4m.js → usage-tCk-mg_x.js} +4 -3
  112. package/dist/version-omm6IPI3.js +12 -0
  113. package/dist/{web-search-provider.runtime-DKQn67A7.js → web-search-provider.runtime-B7bGbGWq.js} +3 -3
  114. package/openclaw.plugin.json +23 -1
  115. package/package.json +4 -4
  116. package/dist/request-C8H22rPi.js +0 -475
  117. package/dist/session-catalog-B47uwekb.js +0 -3272
  118. package/dist/session-history.worker-zZVcx0ME.js +0 -267
  119. package/dist/shared-client-CKhX2kFt.js +0 -7505
  120. package/dist/thread-policy-CTalhWFy.js +0 -105
@@ -0,0 +1,527 @@
1
+ import { o as formatCodexDisplayText } from "./command-formatters-lxlkNZgU.js";
2
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
4
+ import { runCommandBuffered } from "openclaw/plugin-sdk/process-runtime";
5
+ import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
6
+ import path from "node:path";
7
+ import os from "node:os";
8
+ import fs from "node:fs/promises";
9
+ import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
10
+ import process from "node:process";
11
+ import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
12
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
13
+ //#region extensions/codex/src/jsonl-lines.ts
14
+ const JSONL_STREAM_THRESHOLD_BYTES = 4194304;
15
+ const JSONL_READ_CHUNK_BYTES = 1048576;
16
+ async function visitJsonlLines(file, visitor, chunkBytes = JSONL_READ_CHUNK_BYTES) {
17
+ let size;
18
+ try {
19
+ size = (await fs.stat(file)).size;
20
+ } catch {
21
+ return {
22
+ ok: false,
23
+ lineCount: 0
24
+ };
25
+ }
26
+ if (size <= JSONL_STREAM_THRESHOLD_BYTES) {
27
+ let content;
28
+ try {
29
+ content = await fs.readFile(file, "utf8");
30
+ } catch {
31
+ return {
32
+ ok: false,
33
+ lineCount: 0
34
+ };
35
+ }
36
+ if (content.length === 0) return {
37
+ ok: true,
38
+ lineCount: 0
39
+ };
40
+ let lineCount = 0;
41
+ for (const line of content.split(/\r?\n/u)) {
42
+ lineCount += 1;
43
+ if (visitor(line) === false) break;
44
+ }
45
+ return {
46
+ ok: true,
47
+ lineCount
48
+ };
49
+ }
50
+ let handle;
51
+ try {
52
+ handle = await fs.open(file, "r");
53
+ } catch {
54
+ return {
55
+ ok: false,
56
+ lineCount: 0
57
+ };
58
+ }
59
+ const buffer = Buffer.allocUnsafe(chunkBytes);
60
+ const decoder = new TextDecoder();
61
+ let pendingFragments = [];
62
+ let lineCount = 0;
63
+ try {
64
+ while (true) {
65
+ const { bytesRead } = await handle.read(buffer, 0, buffer.length, null);
66
+ if (bytesRead === 0) break;
67
+ const content = decoder.decode(buffer.subarray(0, bytesRead), { stream: true });
68
+ let lineStart = 0;
69
+ while (true) {
70
+ const newline = content.indexOf("\n", lineStart);
71
+ if (newline === -1) break;
72
+ let rawLine = content.slice(lineStart, newline);
73
+ if (pendingFragments.length > 0) {
74
+ pendingFragments.push(rawLine);
75
+ rawLine = pendingFragments.join("");
76
+ pendingFragments = [];
77
+ }
78
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
79
+ lineCount += 1;
80
+ if (visitor(line) === false) return {
81
+ ok: true,
82
+ lineCount
83
+ };
84
+ lineStart = newline + 1;
85
+ }
86
+ if (lineStart < content.length) pendingFragments.push(content.slice(lineStart));
87
+ }
88
+ const decoderTail = decoder.decode();
89
+ if (decoderTail.length > 0) pendingFragments.push(decoderTail);
90
+ if (pendingFragments.length > 0) {
91
+ const rawLine = pendingFragments.join("");
92
+ const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
93
+ lineCount += 1;
94
+ visitor(line);
95
+ }
96
+ return {
97
+ ok: true,
98
+ lineCount
99
+ };
100
+ } catch {
101
+ return {
102
+ ok: false,
103
+ lineCount: 0
104
+ };
105
+ } finally {
106
+ await handle.close().catch(() => void 0);
107
+ }
108
+ }
109
+ //#endregion
110
+ //#region extensions/codex/src/node-cli-sessions.ts
111
+ const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
112
+ const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
113
+ const DEFAULT_SESSION_LIMIT = 10;
114
+ const MAX_SESSION_LIMIT = 50;
115
+ const DEFAULT_RESUME_TIMEOUT_MS = 12e5;
116
+ const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
117
+ const activeResumeSessions = /* @__PURE__ */ new Set();
118
+ function createCodexCliSessionNodeHostCommands() {
119
+ return [{
120
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND,
121
+ cap: "codex-cli-sessions",
122
+ handle: listLocalCodexCliSessions
123
+ }, {
124
+ command: CODEX_CLI_SESSION_RESUME_COMMAND,
125
+ cap: "codex-cli-sessions",
126
+ dangerous: true,
127
+ handle: resumeLocalCodexCliSession
128
+ }];
129
+ }
130
+ function createCodexCliSessionNodeInvokePolicies() {
131
+ return [{
132
+ commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
133
+ defaultPlatforms: [
134
+ "macos",
135
+ "linux",
136
+ "windows"
137
+ ],
138
+ handle: (ctx) => ctx.invokeNode()
139
+ }, {
140
+ commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
141
+ dangerous: true,
142
+ handle: (ctx) => ctx.invokeNode()
143
+ }];
144
+ }
145
+ async function listCodexCliSessionsOnNode(params) {
146
+ const node = await resolveCodexCliNode({
147
+ runtime: params.runtime,
148
+ requestedNode: params.requestedNode,
149
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND
150
+ });
151
+ return {
152
+ node,
153
+ result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
154
+ nodeId: readNodeId(node),
155
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND,
156
+ params: {
157
+ limit: params.limit,
158
+ filter: params.filter
159
+ },
160
+ timeoutMs: 15e3,
161
+ scopes: ["operator.write"]
162
+ }))
163
+ };
164
+ }
165
+ async function resolveCodexCliSessionForBindingOnNode(params) {
166
+ const listing = await listCodexCliSessionsOnNode({
167
+ runtime: params.runtime,
168
+ requestedNode: params.requestedNode,
169
+ filter: params.sessionId,
170
+ limit: MAX_SESSION_LIMIT
171
+ });
172
+ if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
173
+ return {
174
+ node: listing.node,
175
+ session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
176
+ };
177
+ }
178
+ async function resumeCodexCliSessionOnNode(params) {
179
+ const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
180
+ nodeId: params.nodeId,
181
+ command: CODEX_CLI_SESSION_RESUME_COMMAND,
182
+ params: {
183
+ sessionId: params.sessionId,
184
+ prompt: params.prompt,
185
+ cwd: params.cwd,
186
+ timeoutMs: params.timeoutMs
187
+ },
188
+ timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3,
189
+ scopes: ["operator.write"]
190
+ }));
191
+ if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
192
+ return {
193
+ ok: true,
194
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
195
+ text: payload.text
196
+ };
197
+ }
198
+ function formatCodexCliSessions(params) {
199
+ if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
200
+ return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
201
+ const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
202
+ return `- ${formatCodexDisplayText(session.sessionId)}${session.lastMessage ? ` - ${formatCodexDisplayText(session.lastMessage)}` : ""}${details.length > 0 ? ` (${details.map(formatCodexDisplayText).join(", ")})` : ""}\n Bind: /codex resume ${formatCodexDisplayText(session.sessionId)} --host ${formatCodexDisplayText(readNodeId(params.node))} --bind here`;
203
+ })].join("\n");
204
+ }
205
+ async function listLocalCodexCliSessions(paramsJSON) {
206
+ const params = readRecordParam(paramsJSON);
207
+ const limit = normalizeLimit(params.limit);
208
+ const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
209
+ const codexHome = resolveCodexHome();
210
+ const summaries = await readHistorySessions(codexHome);
211
+ await hydrateSessionFiles(codexHome, summaries);
212
+ await hydrateSessionsFromSessionFiles(codexHome, summaries);
213
+ const sessions = [...summaries.values()].filter((session) => {
214
+ if (!filter) return true;
215
+ return [
216
+ session.sessionId,
217
+ session.cwd,
218
+ session.lastMessage
219
+ ].some((value) => value?.toLowerCase().includes(filter));
220
+ }).toSorted((a, b) => compareOptionalStringsDesc(a.updatedAt, b.updatedAt)).slice(0, limit);
221
+ return JSON.stringify({
222
+ sessions,
223
+ codexHome
224
+ });
225
+ }
226
+ async function resumeLocalCodexCliSession(paramsJSON) {
227
+ const params = readRecordParam(paramsJSON);
228
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
229
+ const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
230
+ if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
231
+ 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);
234
+ try {
235
+ const text = await runCodexExecResume({
236
+ sessionId,
237
+ prompt,
238
+ cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
239
+ timeoutMs: normalizeTimeoutMs(params.timeoutMs)
240
+ });
241
+ return JSON.stringify({
242
+ ok: true,
243
+ sessionId,
244
+ text: text.trim() || "Codex completed without a text reply."
245
+ });
246
+ } finally {
247
+ activeResumeSessions.delete(sessionId);
248
+ }
249
+ }
250
+ async function runCodexExecResume(params) {
251
+ const outputPath = path.join(await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "openclaw-codex-cli-")), "last-message.txt");
252
+ try {
253
+ const args = [
254
+ "exec",
255
+ "resume",
256
+ "--skip-git-repo-check",
257
+ "--output-last-message",
258
+ outputPath,
259
+ params.sessionId,
260
+ "-"
261
+ ];
262
+ const invocation = materializeWindowsSpawnProgram(resolveWindowsSpawnProgram({
263
+ command: "codex",
264
+ platform: process.platform,
265
+ env: process.env,
266
+ execPath: process.execPath,
267
+ packageName: "@openai/codex"
268
+ }), 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
+ });
278
+ if (result.termination === "timeout") throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
279
+ if (result.termination === "error" && result.error) throw result.error;
280
+ if (result.code !== 0) {
281
+ const message = result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim() || `codex exec resume exited with code ${String(result.code)}`;
282
+ throw new Error(message);
283
+ }
284
+ return await fs.readFile(outputPath, "utf8");
285
+ } finally {
286
+ await fs.rm(path.dirname(outputPath), {
287
+ recursive: true,
288
+ force: true
289
+ });
290
+ }
291
+ }
292
+ async function readHistorySessions(codexHome) {
293
+ const summaries = /* @__PURE__ */ new Map();
294
+ 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;
304
+ const sessionId = parsed.session_id.trim();
305
+ if (!sessionId) return;
306
+ const entry = summaries.get(sessionId) ?? {
307
+ sessionId,
308
+ messageCount: 0
309
+ };
310
+ entry.messageCount += 1;
311
+ if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
312
+ if (typeof parsed.ts === "number") entry.updatedAt = timestampMsToIsoString(parsed.ts * 1e3) ?? entry.updatedAt;
313
+ summaries.set(sessionId, entry);
314
+ })).ok) return /* @__PURE__ */ new Map();
315
+ return summaries;
316
+ }
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
+ async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
335
+ const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
336
+ for (const file of files) {
337
+ const summary = await readSessionFileSummary(file);
338
+ if (!summary) continue;
339
+ const existing = summaries.get(summary.sessionId);
340
+ summaries.set(summary.sessionId, {
341
+ ...summary,
342
+ ...existing,
343
+ cwd: existing?.cwd ?? summary.cwd,
344
+ sessionFile: existing?.sessionFile ?? summary.sessionFile,
345
+ updatedAt: existing?.updatedAt ?? summary.updatedAt,
346
+ lastMessage: existing?.lastMessage ?? summary.lastMessage,
347
+ messageCount: existing?.messageCount ?? summary.messageCount
348
+ });
349
+ }
350
+ }
351
+ async function readSessionFileSummary(file) {
352
+ let sessionId = "";
353
+ let cwd;
354
+ let updatedAt;
355
+ let lastMessage;
356
+ let messageCount = 0;
357
+ 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;
367
+ if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
368
+ if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
369
+ if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
370
+ if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
371
+ return;
372
+ }
373
+ const messageText = readResponseItemMessageText(parsed);
374
+ if (messageText) {
375
+ messageCount += 1;
376
+ lastMessage = truncateText(messageText, 140);
377
+ }
378
+ });
379
+ if (!result.ok) return null;
380
+ if (result.lineCount === 0) return null;
381
+ if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
382
+ if (!sessionId) return null;
383
+ return {
384
+ sessionId,
385
+ updatedAt: updatedAt ?? await readFileMtimeIso(file),
386
+ lastMessage,
387
+ cwd,
388
+ sessionFile: file,
389
+ messageCount
390
+ };
391
+ }
392
+ async function findSessionFiles(dir, maxDepth) {
393
+ if (maxDepth < 0) return [];
394
+ let entries;
395
+ try {
396
+ entries = await fs.readdir(dir, { withFileTypes: true });
397
+ } catch {
398
+ return [];
399
+ }
400
+ const files = [];
401
+ for (const entry of entries) {
402
+ const entryPath = path.join(dir, entry.name);
403
+ if (entry.isDirectory()) files.push(...await findSessionFiles(entryPath, maxDepth - 1));
404
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(entryPath);
405
+ }
406
+ return files;
407
+ }
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
+ 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;
421
+ const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
422
+ if (!isRecord(entry)) return [];
423
+ const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
424
+ return text?.trim() ? [text.trim()] : [];
425
+ });
426
+ return parts.length > 0 ? parts.join(" ") : void 0;
427
+ }
428
+ function readSessionIdFromFilename(file) {
429
+ return path.basename(file).match(/[0-9a-f]{8}-[0-9a-f-]{27,}/iu)?.[0];
430
+ }
431
+ async function resolveCodexCliNode(params) {
432
+ const list = await params.runtime.nodes.list(params.requestedNode ? void 0 : { connected: true });
433
+ const requested = params.requestedNode?.trim();
434
+ const candidates = list.nodes.filter((node) => {
435
+ if (requested) return [
436
+ node.nodeId,
437
+ node.displayName,
438
+ node.remoteIp
439
+ ].some((value) => value === requested);
440
+ return node.connected === true && node.commands?.includes(params.command);
441
+ });
442
+ if (candidates.length === 0) throw new Error(requested ? `Codex CLI node ${requested} was not found.` : "No connected node exposes Codex CLI session commands.");
443
+ const usable = candidates.filter((node) => node.commands?.includes(params.command));
444
+ if (usable.length === 0) throw new Error(`Node ${requested ?? "candidate"} does not expose ${params.command}.`);
445
+ if (usable.length > 1) throw new Error("Multiple Codex CLI-capable nodes connected. Pass --host <node-id>.");
446
+ return expectDefined(usable[0], "single usable Codex CLI node");
447
+ }
448
+ function parseCodexCliSessionsListResult(raw) {
449
+ const payload = unwrapNodeInvokePayload(raw);
450
+ if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
451
+ return {
452
+ codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
453
+ sessions: payload.sessions.flatMap((entry) => {
454
+ if (!isRecord(entry) || typeof entry.sessionId !== "string") return [];
455
+ return [{
456
+ sessionId: entry.sessionId,
457
+ updatedAt: typeof entry.updatedAt === "string" ? entry.updatedAt : void 0,
458
+ lastMessage: typeof entry.lastMessage === "string" ? entry.lastMessage : void 0,
459
+ cwd: typeof entry.cwd === "string" ? entry.cwd : void 0,
460
+ sessionFile: typeof entry.sessionFile === "string" ? entry.sessionFile : void 0,
461
+ messageCount: typeof entry.messageCount === "number" && Number.isFinite(entry.messageCount) ? entry.messageCount : 0
462
+ }];
463
+ })
464
+ };
465
+ }
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
+ if (!paramsJSON?.trim()) return {};
478
+ try {
479
+ const parsed = JSON.parse(paramsJSON);
480
+ return isRecord(parsed) ? parsed : {};
481
+ } catch {
482
+ return {};
483
+ }
484
+ }
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
+ async function readFileMtimeIso(file) {
496
+ try {
497
+ return (await fs.stat(file)).mtime.toISOString();
498
+ } catch {
499
+ return;
500
+ }
501
+ }
502
+ function normalizeLimit(value) {
503
+ return typeof value === "number" && Number.isFinite(value) ? Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(value))) : DEFAULT_SESSION_LIMIT;
504
+ }
505
+ function normalizeTimeoutMs(value) {
506
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(36e5, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
507
+ }
508
+ function truncateText(value, max) {
509
+ if (value.length <= max) return value;
510
+ return `${truncateUtf16Safe(value, Math.max(0, max - 3))}...`;
511
+ }
512
+ function compareOptionalStringsDesc(a, b) {
513
+ return (b ?? "").localeCompare(a ?? "");
514
+ }
515
+ function readNodeId(node) {
516
+ if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
517
+ return node.nodeId;
518
+ }
519
+ function formatNodeLabel(node) {
520
+ return [
521
+ node.displayName,
522
+ node.nodeId,
523
+ node.remoteIp
524
+ ].filter(Boolean).join(" / ") || "node";
525
+ }
526
+ //#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 };
@@ -1,14 +1,19 @@
1
- import { n as resolveManagedCodexAppServerStartOptions, r as resolveManagedCodexNativeCommand, t as isManagedCodexDesktopCommand } from "./managed-binary-DcpnJF9x.js";
2
- import { i as closeCodexAppServerTransportAndWait, t as createStdioTransport } from "./transport-stdio-CzTngV0j.js";
1
+ import { i as resolveManagedCodexNativeCommand, r as resolveManagedCodexAppServerStartOptions, t as isManagedCodexDesktopCommand } from "./managed-binary-D22rNEcR.js";
2
+ import { i as closeCodexAppServerTransportAndWait, t as createStdioTransport } from "./transport-stdio-BGjHoYLr.js";
3
3
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import { killProcessTree } from "openclaw/plugin-sdk/process-runtime";
5
- import { mkdir } from "node:fs/promises";
6
5
  import path from "node:path";
7
- import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
6
+ import { mkdir } from "node:fs/promises";
8
7
  import { once } from "node:events";
8
+ import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plugin-sdk/temp-path";
9
+ import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
10
+ import { stripVTControlCharacters } from "node:util";
11
+ import { StringDecoder } from "node:string_decoder";
9
12
  import { sanitizeEnvVars } from "openclaw/plugin-sdk/sandbox";
10
13
  //#region extensions/codex/src/node-exec-server.runtime.ts
11
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";
12
17
  const CODEX_EXEC_SERVER_TERMINATION_GRACE_MS = 1e3;
13
18
  const CODEX_EXEC_SERVER_REAP_TIMEOUT_MS = 5e3;
14
19
  const NODE_EXEC_SERVER_PLATFORM_ENVIRONMENT = /^(?:SYSTEMROOT|WINDIR|COMSPEC|PATHEXT|TEMP|TMP|TMPDIR)$/iu;
@@ -70,10 +75,11 @@ function createNodeExecServerProcessOwner(child, closed) {
70
75
  let termination;
71
76
  return { terminate: () => termination ??= (async () => {
72
77
  if (process.platform === "win32" && child.pid) killProcessTree(child.pid, { graceMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS });
73
- if (!await closeCodexAppServerTransportAndWait(child, {
78
+ const { exited } = await closeCodexAppServerTransportAndWait(child, {
74
79
  forceKillDelayMs: CODEX_EXEC_SERVER_TERMINATION_GRACE_MS,
75
80
  exitTimeoutMs: CODEX_EXEC_SERVER_REAP_TIMEOUT_MS
76
- })) throw new Error("Codex node exec-server process tree did not terminate.");
81
+ });
82
+ if (!exited) throw new Error("Codex node exec-server process tree did not terminate.");
77
83
  await closed;
78
84
  })() };
79
85
  }
@@ -125,6 +131,12 @@ async function runCodexNodeExecServer(params) {
125
131
  }).allowed;
126
132
  if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
127
133
  params.assertExecAuthorized();
134
+ const nativeReady = createDeferred();
135
+ const exit = createDeferred();
136
+ let stderr = Buffer.alloc(0);
137
+ let startupSettled = false;
138
+ let pendingLine = "";
139
+ const decoder = new StringDecoder("utf8");
128
140
  const child = await createStdioTransport({
129
141
  transport: "stdio",
130
142
  command: native,
@@ -133,31 +145,45 @@ async function runCodexNodeExecServer(params) {
133
145
  headers: {},
134
146
  cwd,
135
147
  env: {
136
- HOME: dir,
148
+ HOME: params.homeDir ?? dir,
137
149
  CODEX_HOME: codexHome,
138
- ...process.platform === "win32" ? { USERPROFILE: dir } : {}
150
+ RUST_LOG: "error,opentelemetry_sdk=off,opentelemetry_otlp=off,codex_exec_server::server::transport=info",
151
+ ...process.platform === "win32" ? { USERPROFILE: params.homeDir ?? dir } : {}
139
152
  },
140
153
  clearEnv: ["NODE_OPTIONS"]
141
154
  }, baseEnv, () => {
142
155
  if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
143
156
  params.assertExecAuthorized();
144
- });
145
- child.stdin.on("error", (error) => {
146
- rejectDisconnected(error);
147
- });
148
- let stderr = Buffer.alloc(0);
149
- child.stderr.on("data", (chunk) => {
150
- const bounded = (Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)).subarray(-4096);
151
- stderr = Buffer.concat([stderr, bounded]).subarray(-4096);
152
- });
153
- const closed = new Promise((resolve) => {
154
- child.once("close", (code, signal) => resolve({
157
+ }, (spawned) => {
158
+ spawned.once("close", (code, signal) => exit.resolve({
155
159
  code,
156
160
  signal
157
161
  }));
162
+ spawned.once("error", rejectDisconnected);
163
+ spawned.stdin.on("error", rejectDisconnected);
164
+ spawned.stderr.on("data", (chunk) => {
165
+ const next = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
166
+ stderr = Buffer.concat([stderr, next.subarray(-4096)]).subarray(-4096);
167
+ if (startupSettled) return;
168
+ const lines = (pendingLine + decoder.write(next)).split("\n");
169
+ pendingLine = lines.pop();
170
+ for (const line of [...lines, pendingLine]) if (Buffer.byteLength(line, "utf8") > MAX_CODEX_EXEC_SERVER_STDERR_BYTES) {
171
+ startupSettled = true;
172
+ pendingLine = "";
173
+ rejectDisconnected(/* @__PURE__ */ new Error("Codex node startup diagnostic line exceeded 4 KiB."));
174
+ return;
175
+ }
176
+ if (lines.some((line) => stripVTControlCharacters(line).trimEnd().endsWith(CODEX_EXEC_SERVER_READY_LINE))) {
177
+ startupSettled = true;
178
+ pendingLine = "";
179
+ nativeReady.resolve();
180
+ }
181
+ });
158
182
  });
159
- child.once("error", (error) => {
160
- rejectDisconnected(error);
183
+ const closed = exit.promise;
184
+ const stopped = closed.then((outcome) => {
185
+ const diagnostic = stderr.toString("utf8").trim();
186
+ throw new Error(`Codex node exec-server exited (code ${outcome.code ?? "none"}, signal ${outcome.signal ?? "none"})${diagnostic ? `: ${diagnostic}` : "."}`);
161
187
  });
162
188
  const owner = createNodeExecServerProcessOwner(child, closed);
163
189
  params.activeProcesses.add(owner.terminate);
@@ -166,7 +192,14 @@ async function runCodexNodeExecServer(params) {
166
192
  rejectDisconnected(error instanceof Error ? error : new Error(String(error)));
167
193
  });
168
194
  try {
195
+ await Promise.race([
196
+ nativeReady.promise,
197
+ stopped,
198
+ disconnected
199
+ ]);
169
200
  if (io.signal.aborted) throw nodeExecServerAbortError(io.signal);
201
+ if (child.exitCode !== null || child.signalCode !== null) await stopped;
202
+ params.assertExecAuthorized();
170
203
  params.onFrameReceiver((message) => {
171
204
  const encoded = validateNodeExecServerMessage(message);
172
205
  const operation = writes ? writes.then(() => writeNodeExecServerMessage(child, encoded, io.signal)) : writeNodeExecServerMessage(child, encoded, io.signal);
@@ -178,9 +211,7 @@ async function runCodexNodeExecServer(params) {
178
211
  });
179
212
  return operation;
180
213
  });
181
- const outcome = await Promise.race([closed, disconnected]);
182
- const diagnostic = stderr.toString("utf8").trim();
183
- throw new Error(`Codex node exec-server exited (code ${outcome.code ?? "none"}, signal ${outcome.signal ?? "none"})${diagnostic ? `: ${diagnostic}` : "."}`);
214
+ return await Promise.race([stopped, disconnected]);
184
215
  } finally {
185
216
  await owner.terminate();
186
217
  params.activeProcesses.delete(owner.terminate);