@openclaw/codex 2026.7.2-beta.5 → 2026.7.33

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 (66) hide show
  1. package/README.md +3 -27
  2. package/dist/{command-formatters-ico4ZFnk.js → .setup/app-server-policy-C_qTMEMy.mjs} +51 -9
  3. package/dist/{attempt-notifications-CMR2_fmR.js → .setup/attempt-notifications-F8krwWL4.mjs} +6 -3
  4. package/dist/{command-handlers-CRmnwI2N.js → .setup/command-handlers-DSRFfor4.mjs} +1156 -1319
  5. package/dist/.setup/command-rpc-BvYsfQ7G.mjs +786 -0
  6. package/dist/{compact-dEN49UH6.js → .setup/compact-BNgh7I4t.mjs} +10 -40
  7. package/dist/.setup/computer-use-C3QkmSO3.mjs +377 -0
  8. package/dist/.setup/config-CYEDnLJ2.mjs +1138 -0
  9. package/dist/{conversation-binding-data-BHRpYbX3.js → .setup/conversation-binding-data-CrRxtDBu.mjs} +1 -1
  10. package/dist/{bounded-turn-FEbUXoYT.js → .setup/media-understanding-provider-EbJCyDqe.mjs} +193 -92
  11. package/dist/{models-BA01NEez.js → .setup/models-C_aZk03A.mjs} +11 -6
  12. package/dist/.setup/notification-correlation-iYs4WieS.mjs +35 -0
  13. package/dist/.setup/plugin-app-cache-key-DR8jV9oS.mjs +99 -0
  14. package/dist/.setup/protocol-2POPqAY4.mjs +13 -0
  15. package/dist/{protocol-validators-Dm4eXZmt.js → .setup/protocol-validators-dZQ-UTOa.mjs} +111 -334
  16. package/dist/{rate-limits-S_PXs0ul.js → .setup/provider-DGxDs6g8.mjs} +269 -13
  17. package/dist/{dynamic-tools-LIt9ojep.js → .setup/provider-capabilities-DS2N_zc1.mjs} +5465 -3399
  18. package/dist/.setup/request-DFR0JyoV.mjs +36 -0
  19. package/dist/.setup/run-attempt-C7xEKFVB.mjs +8180 -0
  20. package/dist/{sandbox-guard-D1Wky__S.js → .setup/sandbox-guard-DA2TQfZW.mjs} +1 -10
  21. package/dist/.setup/session-binding-DuihlMJc.mjs +628 -0
  22. package/dist/.setup/shared-client-B2KmLPRw.mjs +1854 -0
  23. package/dist/{side-question-CO12sHxo.js → .setup/side-question-Cv6JXWCS.mjs} +84 -159
  24. package/dist/{thread-lifecycle-DRg_FdzC.js → .setup/thread-lifecycle-u6CZdOOH.mjs} +1159 -1799
  25. package/dist/{web-search-provider.runtime-CaQMYDbp.js → .setup/web-search-provider.runtime-Ce2zOHxW.mjs} +3 -3
  26. package/dist/doctor-contract-api.js +122 -88
  27. package/dist/harness.js +20 -85
  28. package/dist/index.js +381 -1537
  29. package/dist/media-understanding-provider.js +1 -173
  30. package/dist/prompt-overlay.js +15 -0
  31. package/dist/provider-catalog.js +79 -0
  32. package/dist/provider-discovery.js +34 -0
  33. package/dist/provider.js +2 -0
  34. package/dist/web-search-contract-api.js +1 -1
  35. package/openclaw.plugin.json +178 -221
  36. package/package.json +8 -10
  37. package/dist/app-server-policy-BmDgseo_.js +0 -49
  38. package/dist/attempt-client-cleanup-Ia52yRWc.js +0 -127
  39. package/dist/auth-binding-B4FfbcCg.js +0 -62
  40. package/dist/capabilities-BHRSI6Cn.js +0 -36
  41. package/dist/cli-metadata.js +0 -20
  42. package/dist/computer-use-BjGylO-E.js +0 -695
  43. package/dist/conversation-control-DE7d9DOj.js +0 -287
  44. package/dist/event-projector-assistant-message-DTgnPPVF.js +0 -101
  45. package/dist/event-projector-usage-tvgb1NCX.js +0 -69
  46. package/dist/incognito-session-ET_ufDH-.js +0 -2
  47. package/dist/notification-correlation-Bj_Ak_jI.js +0 -67
  48. package/dist/plugin-app-cache-key-DSXX-rXO.js +0 -315
  49. package/dist/realtime-voice-api-BPcA1uN9.js +0 -494
  50. package/dist/realtime-voice-api.js +0 -2
  51. package/dist/request-CXpPennK.js +0 -157
  52. package/dist/run-attempt-C5GTPJOE.js +0 -11935
  53. package/dist/runtime-artifact-C2ITXdhM.js +0 -488
  54. package/dist/session-binding-DlVQ-Pxs.js +0 -2161
  55. package/dist/session-catalog-CP2oCcEb.js +0 -2444
  56. package/dist/session-cli-CqZ0Fp2a.js +0 -175
  57. package/dist/session-upstream-marker-QE-m5QFG.js +0 -82
  58. package/dist/settled-turn-finalizer-CA_2QXIE.js +0 -303
  59. package/dist/shared-client-BVKE40c-.js +0 -2952
  60. package/dist/transcript-mirror-uKA-A4tB.js +0 -550
  61. package/dist/transport-stdio-DXgGZ7Ib.js +0 -88
  62. package/dist/upstream-session-fork-_Vrz4WJr.js +0 -320
  63. package/dist/usage-UpVbdX5G.js +0 -25
  64. /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
  65. /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
  66. /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
@@ -0,0 +1,786 @@
1
+ import { d as resolveCodexAppServerRuntimeOptions, m as resolveCodexModelBackedReviewerPolicyContext, o as isCodexFastServiceTier } from "./config-CYEDnLJ2.mjs";
2
+ import { n as listCodexAppServerModels } from "./models-C_aZk03A.mjs";
3
+ import { f as resolveCodexAppServerRequestModelSelection, m as resolveCodexBindingModelProviderFallback } from "./thread-lifecycle-u6CZdOOH.mjs";
4
+ import { a as getLeasedSharedCodexAppServerClient, o as releaseLeasedSharedCodexAppServerClient } from "./shared-client-B2KmLPRw.mjs";
5
+ import { a as normalizeCodexAppServerBindingModelProvider, i as isCodexAppServerNativeAuthProfile, n as bindingStoreKey } from "./session-binding-DuihlMJc.mjs";
6
+ import { i as describeControlFailure, r as CODEX_CONTROL_METHODS } from "./plugin-app-cache-key-DR8jV9oS.mjs";
7
+ import { a as formatCodexDisplayText } from "./app-server-policy-C_qTMEMy.mjs";
8
+ import { t as requestCodexAppServerJson } from "./request-DFR0JyoV.mjs";
9
+ import fs from "node:fs/promises";
10
+ import path from "node:path";
11
+ import { timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
12
+ import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
13
+ import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
14
+ import os from "node:os";
15
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
16
+ import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
17
+ import { spawn } from "node:child_process";
18
+ import process from "node:process";
19
+ //#region extensions/codex/src/command-authorization.ts
20
+ const CODEX_NATIVE_EXECUTION_AUTH_ERROR = "Only an owner or operator.admin can control Codex native execution.";
21
+ function canMutateCodexHost(ctx) {
22
+ return ctx.senderIsOwner === true || ctx.gatewayClientScopes?.includes("operator.admin") === true;
23
+ }
24
+ //#endregion
25
+ //#region extensions/codex/src/conversation-control.ts
26
+ const CODEX_CONVERSATION_CONTROL_STATE = Symbol.for("openclaw.codex.conversationControl");
27
+ function getActiveTurns() {
28
+ const globalState = globalThis;
29
+ globalState[CODEX_CONVERSATION_CONTROL_STATE] ??= /* @__PURE__ */ new Map();
30
+ return globalState[CODEX_CONVERSATION_CONTROL_STATE];
31
+ }
32
+ function trackCodexConversationActiveTurn(active) {
33
+ const activeTurns = getActiveTurns();
34
+ const key = bindingStoreKey(active.identity);
35
+ activeTurns.set(key, active);
36
+ return () => {
37
+ if (activeTurns.get(key)?.turnId === active.turnId) activeTurns.delete(key);
38
+ };
39
+ }
40
+ function readCodexConversationActiveTurn(identity) {
41
+ return getActiveTurns().get(bindingStoreKey(identity));
42
+ }
43
+ async function stopCodexConversationTurn(params) {
44
+ const active = readCodexConversationActiveTurn(params.identity);
45
+ if (!active) return {
46
+ stopped: false,
47
+ message: "No active Codex run to stop."
48
+ };
49
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
50
+ const lookup = buildBindingLookup(params);
51
+ const binding = await params.bindingStore.read(params.identity);
52
+ const client = await getLeasedSharedCodexAppServerClient({
53
+ startOptions: runtime.start,
54
+ timeoutMs: runtime.requestTimeoutMs,
55
+ authProfileId: binding?.authProfileId,
56
+ ...lookup
57
+ });
58
+ try {
59
+ await client.request("turn/interrupt", {
60
+ threadId: active.threadId,
61
+ turnId: active.turnId
62
+ }, { timeoutMs: runtime.requestTimeoutMs });
63
+ } finally {
64
+ releaseLeasedSharedCodexAppServerClient(client);
65
+ }
66
+ return {
67
+ stopped: true,
68
+ message: "Codex stop requested."
69
+ };
70
+ }
71
+ async function steerCodexConversationTurn(params) {
72
+ const active = readCodexConversationActiveTurn(params.identity);
73
+ const text = params.message.trim();
74
+ if (!text) return {
75
+ steered: false,
76
+ message: "Usage: /codex steer <message>"
77
+ };
78
+ if (!active) return {
79
+ steered: false,
80
+ message: "No active Codex run to steer."
81
+ };
82
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
83
+ const lookup = buildBindingLookup(params);
84
+ const binding = await params.bindingStore.read(params.identity);
85
+ const client = await getLeasedSharedCodexAppServerClient({
86
+ startOptions: runtime.start,
87
+ timeoutMs: runtime.requestTimeoutMs,
88
+ authProfileId: binding?.authProfileId,
89
+ ...lookup
90
+ });
91
+ try {
92
+ await client.request("turn/steer", {
93
+ threadId: active.threadId,
94
+ expectedTurnId: active.turnId,
95
+ input: [{
96
+ type: "text",
97
+ text,
98
+ text_elements: []
99
+ }]
100
+ }, { timeoutMs: runtime.requestTimeoutMs });
101
+ } finally {
102
+ releaseLeasedSharedCodexAppServerClient(client);
103
+ }
104
+ return {
105
+ steered: true,
106
+ message: "Sent steer message to Codex."
107
+ };
108
+ }
109
+ async function setCodexConversationModel(params) {
110
+ const model = params.model.trim();
111
+ if (!model) return "Usage: /codex model <model>";
112
+ const lookup = buildBindingLookup(params);
113
+ const binding = await requireThreadBinding(params.bindingStore, params.identity);
114
+ const reviewerPolicyContext = resolveCodexModelBackedReviewerPolicyContext({
115
+ provider: "codex",
116
+ model,
117
+ bindingModelProvider: binding.modelProvider,
118
+ bindingModel: binding.model,
119
+ nativeAuthProfile: isCodexAppServerNativeAuthProfile({
120
+ authProfileId: binding.authProfileId,
121
+ ...lookup
122
+ })
123
+ });
124
+ const runtime = resolveCodexAppServerRuntimeOptions({
125
+ pluginConfig: params.pluginConfig,
126
+ modelProvider: reviewerPolicyContext.modelProvider,
127
+ model: reviewerPolicyContext.model,
128
+ config: params.config,
129
+ agentDir: params.agentDir
130
+ });
131
+ const modelProvider = resolveConversationControlModelProvider({
132
+ authProfileId: binding.authProfileId,
133
+ bindingModel: binding.model,
134
+ bindingModelProvider: binding.modelProvider,
135
+ currentModel: model,
136
+ ...lookup
137
+ });
138
+ const modelSelection = resolveCodexAppServerRequestModelSelection({
139
+ model,
140
+ modelProvider,
141
+ authProfileId: binding.authProfileId,
142
+ ...lookup
143
+ });
144
+ const response = await resumeThreadWithOverrides({
145
+ runtime,
146
+ threadId: binding.threadId,
147
+ authProfileId: binding.authProfileId,
148
+ ...lookup,
149
+ model: modelSelection.model,
150
+ modelProvider: modelSelection.modelProvider
151
+ });
152
+ const nextModel = response.model ?? modelSelection.model;
153
+ const nextModelProvider = normalizeCodexAppServerBindingModelProvider({
154
+ authProfileId: binding.authProfileId,
155
+ modelProvider: response.modelProvider ?? modelSelection.modelProvider,
156
+ ...lookup
157
+ });
158
+ const modelChanged = nextModel !== binding.model || nextModelProvider !== binding.modelProvider;
159
+ await patchThreadBinding(params.bindingStore, params.identity, binding.threadId, {
160
+ cwd: response.thread.cwd ?? binding.cwd,
161
+ model: nextModel,
162
+ modelProvider: nextModelProvider,
163
+ ...modelChanged && binding.contextEngine?.projection ? { contextEngine: {
164
+ ...binding.contextEngine,
165
+ projection: void 0
166
+ } } : {},
167
+ approvalPolicy: binding.approvalPolicy,
168
+ sandbox: binding.sandbox,
169
+ serviceTier: binding.serviceTier ?? runtime.serviceTier ?? void 0
170
+ });
171
+ return `Codex model set to ${formatCodexDisplayText(response.model ?? model)}.`;
172
+ }
173
+ async function setCodexConversationFastMode(params) {
174
+ const binding = await requireThreadBinding(params.bindingStore, params.identity);
175
+ if (params.enabled == null) return `Codex fast mode: ${isCodexFastServiceTier(binding.serviceTier) ? "on" : "off"}.`;
176
+ const serviceTier = params.enabled ? "priority" : "flex";
177
+ await patchThreadBinding(params.bindingStore, params.identity, binding.threadId, { serviceTier });
178
+ return `Codex fast mode ${params.enabled ? "enabled" : "disabled"}.`;
179
+ }
180
+ async function setCodexConversationPermissions(params) {
181
+ const binding = await requireThreadBinding(params.bindingStore, params.identity);
182
+ if (!params.mode) return `Codex permissions: ${formatPermissionsMode(binding)}.`;
183
+ const policy = permissionsForMode(params.mode);
184
+ await patchThreadBinding(params.bindingStore, params.identity, binding.threadId, {
185
+ approvalPolicy: policy.approvalPolicy,
186
+ sandbox: policy.sandbox
187
+ });
188
+ return `Codex permissions set to ${params.mode === "yolo" ? "full access" : "default"}.`;
189
+ }
190
+ function parseCodexFastModeArg(arg) {
191
+ const normalized = arg?.trim().toLowerCase();
192
+ if (!normalized || normalized === "status") return;
193
+ if (normalized === "on" || normalized === "true" || normalized === "fast") return true;
194
+ if (normalized === "off" || normalized === "false" || normalized === "flex") return false;
195
+ }
196
+ function parseCodexPermissionsModeArg(arg) {
197
+ const normalized = arg?.trim().toLowerCase();
198
+ if (!normalized || normalized === "status") return;
199
+ if (normalized === "yolo" || normalized === "full" || normalized === "full-access") return "yolo";
200
+ if (normalized === "default" || normalized === "guardian") return "default";
201
+ }
202
+ function formatPermissionsMode(binding) {
203
+ return binding.approvalPolicy === "never" && binding.sandbox === "danger-full-access" ? "full access" : "default";
204
+ }
205
+ async function requireThreadBinding(bindingStore, identity) {
206
+ const binding = await bindingStore.read(identity);
207
+ if (!binding?.threadId) throw new Error("No Codex thread is attached to this OpenClaw session yet.");
208
+ return binding;
209
+ }
210
+ async function patchThreadBinding(bindingStore, identity, threadId, patch) {
211
+ if (!await bindingStore.mutate(identity, {
212
+ kind: "patch",
213
+ threadId,
214
+ patch
215
+ })) throw new Error("Codex thread binding changed while applying the control update.");
216
+ }
217
+ async function resumeThreadWithOverrides(params) {
218
+ const runtime = params.runtime;
219
+ const client = await getLeasedSharedCodexAppServerClient({
220
+ startOptions: runtime.start,
221
+ timeoutMs: runtime.requestTimeoutMs,
222
+ authProfileId: params.authProfileId,
223
+ ...buildBindingLookup(params)
224
+ });
225
+ try {
226
+ return await client.request(CODEX_CONTROL_METHODS.resumeThread, {
227
+ threadId: params.threadId,
228
+ ...params.model ? { model: params.model } : {},
229
+ ...params.modelProvider ? { modelProvider: params.modelProvider } : {},
230
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
231
+ sandbox: params.sandbox ?? runtime.sandbox,
232
+ approvalsReviewer: runtime.approvalsReviewer,
233
+ ...params.serviceTier ? { serviceTier: params.serviceTier } : {}
234
+ }, { timeoutMs: runtime.requestTimeoutMs });
235
+ } finally {
236
+ releaseLeasedSharedCodexAppServerClient(client);
237
+ }
238
+ }
239
+ function buildBindingLookup(params) {
240
+ const agentDir = params.agentDir?.trim();
241
+ return {
242
+ ...agentDir ? { agentDir } : {},
243
+ ...params.config ? { config: params.config } : {}
244
+ };
245
+ }
246
+ function resolveConversationControlModelProvider(params) {
247
+ const modelProvider = resolveCodexBindingModelProviderFallback({
248
+ currentModel: params.currentModel,
249
+ bindingModel: params.bindingModel,
250
+ bindingModelProvider: params.bindingModelProvider
251
+ })?.trim();
252
+ if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
253
+ if (isCodexAppServerNativeAuthProfile(params) && modelProvider.toLowerCase() === "openai") return;
254
+ return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
255
+ }
256
+ function permissionsForMode(mode) {
257
+ return mode === "yolo" ? {
258
+ approvalPolicy: "never",
259
+ sandbox: "danger-full-access"
260
+ } : {
261
+ approvalPolicy: "on-request",
262
+ sandbox: "workspace-write"
263
+ };
264
+ }
265
+ //#endregion
266
+ //#region extensions/codex/src/node-cli-sessions.ts
267
+ const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
268
+ const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
269
+ const DEFAULT_SESSION_LIMIT = 10;
270
+ const MAX_SESSION_LIMIT = 50;
271
+ const DEFAULT_RESUME_TIMEOUT_MS = 12e5;
272
+ const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
273
+ const activeResumeSessions = /* @__PURE__ */ new Set();
274
+ const DEFAULT_RESUME_SPAWN_RUNTIME = {
275
+ platform: process.platform,
276
+ env: process.env,
277
+ execPath: process.execPath
278
+ };
279
+ function createCodexCliSessionNodeHostCommands() {
280
+ return [{
281
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND,
282
+ cap: "codex-cli-sessions",
283
+ handle: listLocalCodexCliSessions
284
+ }, {
285
+ command: CODEX_CLI_SESSION_RESUME_COMMAND,
286
+ cap: "codex-cli-sessions",
287
+ dangerous: true,
288
+ handle: resumeLocalCodexCliSession
289
+ }];
290
+ }
291
+ function createCodexCliSessionNodeInvokePolicies() {
292
+ return [{
293
+ commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
294
+ defaultPlatforms: [
295
+ "macos",
296
+ "linux",
297
+ "windows"
298
+ ],
299
+ handle: (ctx) => ctx.invokeNode()
300
+ }, {
301
+ commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
302
+ dangerous: true,
303
+ handle: (ctx) => ctx.invokeNode()
304
+ }];
305
+ }
306
+ async function listCodexCliSessionsOnNode(params) {
307
+ const node = await resolveCodexCliNode({
308
+ runtime: params.runtime,
309
+ requestedNode: params.requestedNode,
310
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND
311
+ });
312
+ return {
313
+ node,
314
+ result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
315
+ nodeId: readNodeId(node),
316
+ command: CODEX_CLI_SESSIONS_LIST_COMMAND,
317
+ params: {
318
+ limit: params.limit,
319
+ filter: params.filter
320
+ },
321
+ timeoutMs: 15e3
322
+ }))
323
+ };
324
+ }
325
+ async function resolveCodexCliSessionForBindingOnNode(params) {
326
+ const listing = await listCodexCliSessionsOnNode({
327
+ runtime: params.runtime,
328
+ requestedNode: params.requestedNode,
329
+ filter: params.sessionId,
330
+ limit: MAX_SESSION_LIMIT
331
+ });
332
+ if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
333
+ return {
334
+ node: listing.node,
335
+ session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
336
+ };
337
+ }
338
+ async function resumeCodexCliSessionOnNode(params) {
339
+ const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
340
+ nodeId: params.nodeId,
341
+ command: CODEX_CLI_SESSION_RESUME_COMMAND,
342
+ params: {
343
+ sessionId: params.sessionId,
344
+ prompt: params.prompt,
345
+ cwd: params.cwd,
346
+ timeoutMs: params.timeoutMs
347
+ },
348
+ timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3
349
+ }));
350
+ if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
351
+ return {
352
+ ok: true,
353
+ sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
354
+ text: payload.text
355
+ };
356
+ }
357
+ function formatCodexCliSessions(params) {
358
+ if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
359
+ return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
360
+ const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
361
+ 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`;
362
+ })].join("\n");
363
+ }
364
+ async function listLocalCodexCliSessions(paramsJSON) {
365
+ const params = readRecordParam(paramsJSON);
366
+ const limit = normalizeLimit(params.limit);
367
+ const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
368
+ const codexHome = resolveCodexHome();
369
+ const summaries = await readHistorySessions(codexHome);
370
+ await hydrateSessionFiles(codexHome, summaries);
371
+ await hydrateSessionsFromSessionFiles(codexHome, summaries);
372
+ const sessions = [...summaries.values()].filter((session) => {
373
+ if (!filter) return true;
374
+ return [
375
+ session.sessionId,
376
+ session.cwd,
377
+ session.lastMessage
378
+ ].some((value) => value?.toLowerCase().includes(filter));
379
+ }).toSorted((a, b) => compareOptionalStringsDesc(a.updatedAt, b.updatedAt)).slice(0, limit);
380
+ return JSON.stringify({
381
+ sessions,
382
+ codexHome
383
+ });
384
+ }
385
+ async function resumeLocalCodexCliSession(paramsJSON) {
386
+ const params = readRecordParam(paramsJSON);
387
+ const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
388
+ const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
389
+ if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
390
+ if (!prompt) throw new Error("Missing Codex CLI prompt.");
391
+ if (activeResumeSessions.has(sessionId)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
392
+ activeResumeSessions.add(sessionId);
393
+ try {
394
+ const text = await runCodexExecResume({
395
+ sessionId,
396
+ prompt,
397
+ cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
398
+ timeoutMs: normalizeTimeoutMs(params.timeoutMs)
399
+ });
400
+ return JSON.stringify({
401
+ ok: true,
402
+ sessionId,
403
+ text: text.trim() || "Codex completed without a text reply."
404
+ });
405
+ } finally {
406
+ activeResumeSessions.delete(sessionId);
407
+ }
408
+ }
409
+ async function runCodexExecResume(params) {
410
+ const outputPath = path.join(await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "openclaw-codex-cli-")), "last-message.txt");
411
+ try {
412
+ const invocation = resolveCodexCliResumeSpawnInvocation([
413
+ "exec",
414
+ "resume",
415
+ "--skip-git-repo-check",
416
+ "--output-last-message",
417
+ outputPath,
418
+ params.sessionId,
419
+ "-"
420
+ ], {
421
+ platform: process.platform,
422
+ env: process.env,
423
+ execPath: process.execPath
424
+ });
425
+ const child = spawn(invocation.command, invocation.args, {
426
+ cwd: params.cwd || process.cwd(),
427
+ stdio: [
428
+ "pipe",
429
+ "pipe",
430
+ "pipe"
431
+ ],
432
+ env: process.env,
433
+ shell: invocation.shell,
434
+ windowsHide: invocation.windowsHide
435
+ });
436
+ const stdout = [];
437
+ const stderr = [];
438
+ let timedOut = false;
439
+ let forceKillTimeout;
440
+ const timeout = setTimeout(() => {
441
+ timedOut = true;
442
+ child.kill("SIGTERM");
443
+ forceKillTimeout = setTimeout(() => child.kill("SIGKILL"), 2e3);
444
+ forceKillTimeout.unref?.();
445
+ }, params.timeoutMs);
446
+ child.stdout.on("data", (chunk) => stdout.push(chunk));
447
+ child.stderr.on("data", (chunk) => stderr.push(chunk));
448
+ child.stdin.end(params.prompt);
449
+ const exitCode = await new Promise((resolve, reject) => {
450
+ child.on("error", reject);
451
+ child.on("exit", (code) => resolve(code));
452
+ }).finally(() => {
453
+ clearTimeout(timeout);
454
+ if (forceKillTimeout) clearTimeout(forceKillTimeout);
455
+ });
456
+ if (timedOut) throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
457
+ if (exitCode !== 0) {
458
+ const message = Buffer.concat(stderr).toString("utf8").trim() || Buffer.concat(stdout).toString("utf8").trim() || `codex exec resume exited with code ${String(exitCode)}`;
459
+ throw new Error(message);
460
+ }
461
+ return await fs.readFile(outputPath, "utf8");
462
+ } finally {
463
+ await fs.rm(path.dirname(outputPath), {
464
+ recursive: true,
465
+ force: true
466
+ });
467
+ }
468
+ }
469
+ function resolveCodexCliResumeSpawnInvocation(args, runtime = DEFAULT_RESUME_SPAWN_RUNTIME) {
470
+ const program = resolveWindowsSpawnProgram({
471
+ command: "codex",
472
+ platform: runtime.platform,
473
+ env: runtime.env,
474
+ execPath: runtime.execPath,
475
+ packageName: "@openai/codex"
476
+ });
477
+ const resolved = materializeWindowsSpawnProgram(program, args);
478
+ return {
479
+ command: resolved.command,
480
+ args: resolved.argv,
481
+ shell: resolved.shell,
482
+ windowsHide: resolved.windowsHide
483
+ };
484
+ }
485
+ async function readHistorySessions(codexHome) {
486
+ const summaries = /* @__PURE__ */ new Map();
487
+ const content = await readFileIfExists(path.join(codexHome, "history.jsonl"));
488
+ if (!content) return summaries;
489
+ for (const line of content.split(/\r?\n/u)) {
490
+ const trimmed = line.trim();
491
+ if (!trimmed) continue;
492
+ let parsed;
493
+ try {
494
+ parsed = JSON.parse(trimmed);
495
+ } catch {
496
+ continue;
497
+ }
498
+ if (!isRecord(parsed) || typeof parsed.session_id !== "string") continue;
499
+ const sessionId = parsed.session_id.trim();
500
+ if (!sessionId) continue;
501
+ const entry = summaries.get(sessionId) ?? {
502
+ sessionId,
503
+ messageCount: 0
504
+ };
505
+ entry.messageCount += 1;
506
+ if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
507
+ if (typeof parsed.ts === "number") entry.updatedAt = timestampMsToIsoString(parsed.ts * 1e3) ?? entry.updatedAt;
508
+ summaries.set(sessionId, entry);
509
+ }
510
+ return summaries;
511
+ }
512
+ async function hydrateSessionFiles(codexHome, summaries) {
513
+ if (summaries.size === 0) return;
514
+ const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
515
+ const pending = new Set(summaries.keys());
516
+ for (const file of files) {
517
+ const basename = path.basename(file);
518
+ const sessionId = [...pending].find((id) => basename.includes(id));
519
+ if (!sessionId) continue;
520
+ const entry = summaries.get(sessionId);
521
+ if (!entry) continue;
522
+ entry.sessionFile = file;
523
+ const cwd = readSessionMetaCwd(await readFirstLine(file) ?? "");
524
+ if (cwd) entry.cwd = cwd;
525
+ pending.delete(sessionId);
526
+ if (pending.size === 0) return;
527
+ }
528
+ }
529
+ async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
530
+ const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
531
+ for (const file of files) {
532
+ const summary = await readSessionFileSummary(file);
533
+ if (!summary) continue;
534
+ const existing = summaries.get(summary.sessionId);
535
+ summaries.set(summary.sessionId, {
536
+ ...summary,
537
+ ...existing,
538
+ cwd: existing?.cwd ?? summary.cwd,
539
+ sessionFile: existing?.sessionFile ?? summary.sessionFile,
540
+ updatedAt: existing?.updatedAt ?? summary.updatedAt,
541
+ lastMessage: existing?.lastMessage ?? summary.lastMessage,
542
+ messageCount: existing?.messageCount ?? summary.messageCount
543
+ });
544
+ }
545
+ }
546
+ async function readSessionFileSummary(file) {
547
+ const content = await readFileIfExists(file);
548
+ if (!content) return null;
549
+ let sessionId = "";
550
+ let cwd;
551
+ let updatedAt;
552
+ let lastMessage;
553
+ let messageCount = 0;
554
+ for (const line of content.split(/\r?\n/u)) {
555
+ const trimmed = line.trim();
556
+ if (!trimmed) continue;
557
+ let parsed;
558
+ try {
559
+ parsed = JSON.parse(trimmed);
560
+ } catch {
561
+ continue;
562
+ }
563
+ if (!isRecord(parsed)) continue;
564
+ if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
565
+ if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
566
+ if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
567
+ if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
568
+ continue;
569
+ }
570
+ const messageText = readResponseItemMessageText(parsed);
571
+ if (messageText) {
572
+ messageCount += 1;
573
+ lastMessage = truncateText(messageText, 140);
574
+ }
575
+ }
576
+ if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
577
+ if (!sessionId) return null;
578
+ return {
579
+ sessionId,
580
+ updatedAt: updatedAt ?? await readFileMtimeIso(file),
581
+ lastMessage,
582
+ cwd,
583
+ sessionFile: file,
584
+ messageCount
585
+ };
586
+ }
587
+ async function findSessionFiles(dir, maxDepth) {
588
+ if (maxDepth < 0) return [];
589
+ let entries;
590
+ try {
591
+ entries = await fs.readdir(dir, { withFileTypes: true });
592
+ } catch {
593
+ return [];
594
+ }
595
+ const files = [];
596
+ for (const entry of entries) {
597
+ const entryPath = path.join(dir, entry.name);
598
+ if (entry.isDirectory()) files.push(...await findSessionFiles(entryPath, maxDepth - 1));
599
+ else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(entryPath);
600
+ }
601
+ return files;
602
+ }
603
+ function readSessionMetaCwd(line) {
604
+ try {
605
+ const parsed = JSON.parse(line);
606
+ if (!isRecord(parsed) || parsed.type !== "session_meta" || !isRecord(parsed.payload)) return;
607
+ return typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim() ? parsed.payload.cwd.trim() : void 0;
608
+ } catch {
609
+ return;
610
+ }
611
+ }
612
+ function readResponseItemMessageText(parsed) {
613
+ if (parsed.type !== "response_item" || !isRecord(parsed.payload)) return;
614
+ if (parsed.payload.type !== "message") return;
615
+ if ((typeof parsed.payload.role === "string" ? parsed.payload.role : "") !== "user") return;
616
+ const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
617
+ if (!isRecord(entry)) return [];
618
+ const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
619
+ return text?.trim() ? [text.trim()] : [];
620
+ });
621
+ return parts.length > 0 ? parts.join(" ") : void 0;
622
+ }
623
+ function readSessionIdFromFilename(file) {
624
+ return path.basename(file).match(/[0-9a-f]{8}-[0-9a-f-]{27,}/iu)?.[0];
625
+ }
626
+ async function resolveCodexCliNode(params) {
627
+ const list = await params.runtime.nodes.list(params.requestedNode ? void 0 : { connected: true });
628
+ const requested = params.requestedNode?.trim();
629
+ const candidates = list.nodes.filter((node) => {
630
+ if (requested) return [
631
+ node.nodeId,
632
+ node.displayName,
633
+ node.remoteIp
634
+ ].some((value) => value === requested);
635
+ return node.connected === true && node.commands?.includes(params.command);
636
+ });
637
+ if (candidates.length === 0) throw new Error(requested ? `Codex CLI node ${requested} was not found.` : "No connected node exposes Codex CLI session commands.");
638
+ const usable = candidates.filter((node) => node.commands?.includes(params.command));
639
+ if (usable.length === 0) throw new Error(`Node ${requested ?? "candidate"} does not expose ${params.command}.`);
640
+ if (usable.length > 1) throw new Error("Multiple Codex CLI-capable nodes connected. Pass --host <node-id>.");
641
+ return usable[0];
642
+ }
643
+ function parseCodexCliSessionsListResult(raw) {
644
+ const payload = unwrapNodeInvokePayload(raw);
645
+ if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
646
+ return {
647
+ codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
648
+ sessions: payload.sessions.flatMap((entry) => {
649
+ if (!isRecord(entry) || typeof entry.sessionId !== "string") return [];
650
+ return [{
651
+ sessionId: entry.sessionId,
652
+ updatedAt: typeof entry.updatedAt === "string" ? entry.updatedAt : void 0,
653
+ lastMessage: typeof entry.lastMessage === "string" ? entry.lastMessage : void 0,
654
+ cwd: typeof entry.cwd === "string" ? entry.cwd : void 0,
655
+ sessionFile: typeof entry.sessionFile === "string" ? entry.sessionFile : void 0,
656
+ messageCount: typeof entry.messageCount === "number" && Number.isFinite(entry.messageCount) ? entry.messageCount : 0
657
+ }];
658
+ })
659
+ };
660
+ }
661
+ function unwrapNodeInvokePayload(raw) {
662
+ const record = isRecord(raw) ? raw : {};
663
+ if (typeof record.payloadJSON === "string" && record.payloadJSON.trim()) try {
664
+ return JSON.parse(record.payloadJSON);
665
+ } catch (error) {
666
+ throw new Error("Codex CLI node command returned malformed payloadJSON.", { cause: error });
667
+ }
668
+ if ("payload" in record) return record.payload;
669
+ return raw;
670
+ }
671
+ function readRecordParam(paramsJSON) {
672
+ if (!paramsJSON?.trim()) return {};
673
+ try {
674
+ const parsed = JSON.parse(paramsJSON);
675
+ return isRecord(parsed) ? parsed : {};
676
+ } catch {
677
+ return {};
678
+ }
679
+ }
680
+ function resolveCodexHome() {
681
+ return process.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
682
+ }
683
+ async function readFileIfExists(file) {
684
+ try {
685
+ return await fs.readFile(file, "utf8");
686
+ } catch {
687
+ return;
688
+ }
689
+ }
690
+ async function readFirstLine(file) {
691
+ return (await readFileIfExists(file))?.split(/\r?\n/u)[0];
692
+ }
693
+ async function readFileMtimeIso(file) {
694
+ try {
695
+ return (await fs.stat(file)).mtime.toISOString();
696
+ } catch {
697
+ return;
698
+ }
699
+ }
700
+ function normalizeLimit(value) {
701
+ return typeof value === "number" && Number.isFinite(value) ? Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(value))) : DEFAULT_SESSION_LIMIT;
702
+ }
703
+ function normalizeTimeoutMs(value) {
704
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(36e5, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
705
+ }
706
+ function truncateText(value, max) {
707
+ if (value.length <= max) return value;
708
+ return `${truncateUtf16Safe(value, Math.max(0, max - 3))}...`;
709
+ }
710
+ function compareOptionalStringsDesc(a, b) {
711
+ return (b ?? "").localeCompare(a ?? "");
712
+ }
713
+ function readNodeId(node) {
714
+ if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
715
+ return node.nodeId;
716
+ }
717
+ function formatNodeLabel(node) {
718
+ return [
719
+ node.displayName,
720
+ node.nodeId,
721
+ node.remoteIp
722
+ ].filter(Boolean).join(" / ") || "node";
723
+ }
724
+ //#endregion
725
+ //#region extensions/codex/src/command-rpc.ts
726
+ function requestOptions(pluginConfig, limit, config) {
727
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
728
+ return {
729
+ limit,
730
+ timeoutMs: runtime.requestTimeoutMs,
731
+ startOptions: runtime.start,
732
+ config
733
+ };
734
+ }
735
+ async function codexControlRequest(pluginConfig, method, requestParams, options = {}) {
736
+ const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig });
737
+ return await requestCodexAppServerJson({
738
+ method,
739
+ requestParams,
740
+ timeoutMs: runtime.requestTimeoutMs,
741
+ startOptions: runtime.start,
742
+ config: options.config,
743
+ sessionKey: options.sessionKey,
744
+ sessionId: options.sessionId,
745
+ authProfileId: options.authProfileId,
746
+ agentDir: options.agentDir,
747
+ isolated: options.isolated
748
+ });
749
+ }
750
+ async function safeCodexControlRequest(pluginConfig, method, requestParams, options = {}) {
751
+ return await safeValue(async () => await codexControlRequest(pluginConfig, method, requestParams, options));
752
+ }
753
+ async function safeCodexModelList(pluginConfig, limit, config) {
754
+ return await safeValue(async () => await listCodexAppServerModels(requestOptions(pluginConfig, limit, config)));
755
+ }
756
+ async function readCodexStatusProbes(pluginConfig, config) {
757
+ const [models, account, limits, mcps, skills] = await Promise.all([
758
+ safeCodexModelList(pluginConfig, 20, config),
759
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.account, { refreshToken: false }, { config }),
760
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.rateLimits, void 0, { config }),
761
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listMcpServers, { limit: 100 }, { config }),
762
+ safeCodexControlRequest(pluginConfig, CODEX_CONTROL_METHODS.listSkills, {}, { config })
763
+ ]);
764
+ return {
765
+ models,
766
+ account,
767
+ limits,
768
+ mcps,
769
+ skills
770
+ };
771
+ }
772
+ async function safeValue(read) {
773
+ try {
774
+ return {
775
+ ok: true,
776
+ value: await read()
777
+ };
778
+ } catch (error) {
779
+ return {
780
+ ok: false,
781
+ error: describeControlFailure(error)
782
+ };
783
+ }
784
+ }
785
+ //#endregion
786
+ export { canMutateCodexHost as S, setCodexConversationPermissions as _, createCodexCliSessionNodeHostCommands as a, trackCodexConversationActiveTurn as b, listCodexCliSessionsOnNode as c, formatPermissionsMode as d, parseCodexFastModeArg as f, setCodexConversationModel as g, setCodexConversationFastMode as h, safeCodexControlRequest as i, resolveCodexCliSessionForBindingOnNode as l, readCodexConversationActiveTurn as m, readCodexStatusProbes as n, createCodexCliSessionNodeInvokePolicies as o, parseCodexPermissionsModeArg as p, requestOptions as r, formatCodexCliSessions as s, codexControlRequest as t, resumeCodexCliSessionOnNode as u, steerCodexConversationTurn as v, CODEX_NATIVE_EXECUTION_AUTH_ERROR as x, stopCodexConversationTurn as y };