@openclaw/codex 2026.7.1-beta.1 → 2026.7.1-beta.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 (39) hide show
  1. package/dist/{app-server-policy-xi0ZmBGO.js → app-server-policy-BUk0GLMy.js} +5 -5
  2. package/dist/{attempt-notifications-l9LElGOU.js → attempt-notifications-BGsEIIDI.js} +5 -40
  3. package/dist/{command-handlers-vZy8xX5X.js → command-handlers-UVniG5Hl.js} +267 -232
  4. package/dist/command-rpc-BjgEPh19.js +784 -0
  5. package/dist/compact-CZzbcygx.js +603 -0
  6. package/dist/{computer-use-FPGG-mtX.js → computer-use-DDeySrnb.js} +3 -3
  7. package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
  8. package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
  9. package/dist/doctor-contract-api.js +439 -22
  10. package/dist/harness.js +29 -10
  11. package/dist/index.js +1119 -28
  12. package/dist/{media-understanding-provider-C5yYzx_6.js → media-understanding-provider-DgqRvU2_.js} +13 -8
  13. package/dist/media-understanding-provider.js +1 -1
  14. package/dist/{models-Ccy1_7nN.js → models-DDmO1zwd.js} +2 -2
  15. package/dist/notification-correlation-Bo7KB3ks.js +35 -0
  16. package/dist/{plugin-app-cache-key-CDioyur-.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
  17. package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
  18. package/dist/{protocol-validators-CB4mtyrs.js → protocol-validators-dZQ-UTOa.js} +117 -62
  19. package/dist/{provider-capabilities-CE5k8cmO.js → provider-capabilities-gTCwjfmh.js} +2920 -114
  20. package/dist/{provider-DosqwRb6.js → provider-cc_62eQE.js} +19 -10
  21. package/dist/provider.js +2 -2
  22. package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
  23. package/dist/{run-attempt-O3Zs32KI.js → run-attempt-8SPLiDlT.js} +1016 -2877
  24. package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
  25. package/dist/session-binding-Dc03iwRF.js +595 -0
  26. package/dist/session-binding-meta-B7aEMU7g.js +6 -0
  27. package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
  28. package/dist/{side-question-DDVgFmpv.js → side-question-DkjXvYv7.js} +132 -140
  29. package/dist/{thread-lifecycle-DeSMZSXr.js → thread-lifecycle-BskXnNP-.js} +609 -444
  30. package/dist/{web-search-provider.runtime-Q89rQhsL.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
  31. package/npm-shrinkwrap.json +34 -34
  32. package/openclaw.plugin.json +20 -0
  33. package/package.json +9 -6
  34. package/dist/client-IcTBKEqA.js +0 -732
  35. package/dist/client-factory-CwjdQQ4j.js +0 -18
  36. package/dist/compact-BzB9AqN1.js +0 -278
  37. package/dist/conversation-binding-CEE2UyYb.js +0 -1570
  38. package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
  39. package/dist/session-binding-Cm0Gg7Z1.js +0 -339
@@ -1,1570 +0,0 @@
1
- import { n as isJsonObject } from "./protocol-dh-ETiNd.js";
2
- import { H as readCodexNotificationThreadId, U as readCodexNotificationTurnId, d as resolveCodexAppServerRequestModelSelection, p as resolveCodexBindingModelProviderFallback, t as CODEX_NATIVE_PERSONALITY_NONE } from "./thread-lifecycle-DeSMZSXr.js";
3
- import { d as resolveCodexAppServerRuntimeOptions, h as resolveOpenClawExecPolicyForCodexAppServer, i as codexSandboxPolicyForTurn, n as canUseCodexModelBackedApprovalsReviewerForModel, o as isCodexFastServiceTier, p as resolveCodexModelBackedReviewerPolicyContext } from "./config-C_whBx3M.js";
4
- import { r as assertCodexThreadStartResponse } from "./protocol-validators-CB4mtyrs.js";
5
- import { a as normalizeCodexAppServerBindingModelProvider, i as isCodexAppServerNativeAuthProfile, l as writeCodexAppServerBinding, n as clearCodexAppServerBinding, o as readCodexAppServerBinding } from "./session-binding-Cm0Gg7Z1.js";
6
- import { r as CODEX_CONTROL_METHODS } from "./plugin-app-cache-key-CDioyur-.js";
7
- import { a as formatCodexDisplayText, t as resolveCodexAppServerForModelProvider } from "./app-server-policy-xi0ZmBGO.js";
8
- import { a as getLeasedSharedCodexAppServerClient, o as releaseLeasedSharedCodexAppServerClient, p as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-rR6TDJA3.js";
9
- import { n as resolveCodexNativeExecutionBlock, r as resolveCodexNativeSandboxBlock } from "./sandbox-guard-pTY2COoC.js";
10
- import fs from "node:fs/promises";
11
- import path from "node:path";
12
- import { resolveTimerTimeoutMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
13
- import { resolvePreferredOpenClawTmpDir } from "openclaw/plugin-sdk/temp-path";
14
- import { asOptionalRecord, isRecord, normalizeSingleOrTrimmedStringList } from "openclaw/plugin-sdk/string-coerce-runtime";
15
- import os from "node:os";
16
- import { resolveSessionAgentIds } from "openclaw/plugin-sdk/agent-runtime";
17
- import { loadExecApprovals } from "openclaw/plugin-sdk/exec-approvals-runtime";
18
- import { materializeWindowsSpawnProgram, resolveWindowsSpawnProgram } from "openclaw/plugin-sdk/windows-spawn";
19
- import { formatErrorMessage, resolveSandboxContext } from "openclaw/plugin-sdk/agent-harness-runtime";
20
- import { spawn } from "node:child_process";
21
- import { getSessionEntry, resolveStorePath } from "openclaw/plugin-sdk/session-store-runtime";
22
- import { fileURLToPath } from "node:url";
23
- import process$1 from "node:process";
24
- import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
25
- //#region extensions/codex/src/command-authorization.ts
26
- const CODEX_NATIVE_EXECUTION_AUTH_ERROR = "Only an owner or operator.admin can control Codex native execution.";
27
- function canMutateCodexHost(ctx) {
28
- return ctx.senderIsOwner === true || ctx.gatewayClientScopes?.includes("operator.admin") === true;
29
- }
30
- //#endregion
31
- //#region extensions/codex/src/conversation-binding-data.ts
32
- const BINDING_DATA_VERSION = 1;
33
- function createCodexConversationBindingData(params) {
34
- const agentDir = params.agentDir?.trim();
35
- const agentId = params.agentId?.trim();
36
- return {
37
- kind: "codex-app-server-session",
38
- version: BINDING_DATA_VERSION,
39
- sessionFile: params.sessionFile,
40
- workspaceDir: params.workspaceDir,
41
- ...agentDir ? { agentDir } : {},
42
- ...agentId ? { agentId } : {}
43
- };
44
- }
45
- function createCodexCliNodeConversationBindingData(params) {
46
- const cwd = params.cwd?.trim();
47
- return {
48
- kind: "codex-cli-node-session",
49
- version: BINDING_DATA_VERSION,
50
- nodeId: params.nodeId,
51
- sessionId: params.sessionId,
52
- ...cwd ? { cwd } : {}
53
- };
54
- }
55
- function readCodexConversationBindingData(binding) {
56
- const data = binding?.data;
57
- if (!data || typeof data !== "object" || Array.isArray(data)) return;
58
- return readCodexConversationBindingDataRecord(data);
59
- }
60
- function readCodexConversationBindingDataRecord(data) {
61
- if (data.kind === "codex-cli-node-session") {
62
- if (data.version !== BINDING_DATA_VERSION || typeof data.nodeId !== "string" || !data.nodeId.trim() || typeof data.sessionId !== "string" || !data.sessionId.trim()) return;
63
- return {
64
- kind: "codex-cli-node-session",
65
- version: BINDING_DATA_VERSION,
66
- nodeId: data.nodeId.trim(),
67
- sessionId: data.sessionId.trim(),
68
- cwd: typeof data.cwd === "string" && data.cwd.trim() ? data.cwd.trim() : void 0
69
- };
70
- }
71
- if (data.kind !== "codex-app-server-session") return;
72
- if (data.version !== BINDING_DATA_VERSION || typeof data.sessionFile !== "string" || !data.sessionFile.trim()) return;
73
- return {
74
- kind: "codex-app-server-session",
75
- version: BINDING_DATA_VERSION,
76
- sessionFile: data.sessionFile,
77
- workspaceDir: typeof data.workspaceDir === "string" && data.workspaceDir.trim() ? data.workspaceDir : process$1.cwd(),
78
- agentDir: typeof data.agentDir === "string" && data.agentDir.trim() ? data.agentDir : void 0,
79
- agentId: typeof data.agentId === "string" && data.agentId.trim() ? data.agentId : void 0
80
- };
81
- }
82
- function resolveCodexDefaultWorkspaceDir(pluginConfig) {
83
- return readString$1(asOptionalRecord(asOptionalRecord(pluginConfig)?.appServer), "defaultWorkspaceDir") ?? process$1.cwd();
84
- }
85
- function readString$1(record, key) {
86
- const value = record?.[key];
87
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
88
- }
89
- //#endregion
90
- //#region extensions/codex/src/conversation-control.ts
91
- const CODEX_CONVERSATION_CONTROL_STATE = Symbol.for("openclaw.codex.conversationControl");
92
- function getActiveTurns() {
93
- const globalState = globalThis;
94
- globalState[CODEX_CONVERSATION_CONTROL_STATE] ??= /* @__PURE__ */ new Map();
95
- return globalState[CODEX_CONVERSATION_CONTROL_STATE];
96
- }
97
- function trackCodexConversationActiveTurn(active) {
98
- const activeTurns = getActiveTurns();
99
- activeTurns.set(active.sessionFile, active);
100
- return () => {
101
- if (activeTurns.get(active.sessionFile)?.turnId === active.turnId) activeTurns.delete(active.sessionFile);
102
- };
103
- }
104
- function readCodexConversationActiveTurn(sessionFile) {
105
- return getActiveTurns().get(sessionFile);
106
- }
107
- async function stopCodexConversationTurn(params) {
108
- const active = readCodexConversationActiveTurn(params.sessionFile);
109
- if (!active) return {
110
- stopped: false,
111
- message: "No active Codex run to stop."
112
- };
113
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
114
- const lookup = buildBindingLookup(params);
115
- const binding = await readCodexAppServerBinding(params.sessionFile, lookup);
116
- const client = await getLeasedSharedCodexAppServerClient({
117
- startOptions: runtime.start,
118
- timeoutMs: runtime.requestTimeoutMs,
119
- authProfileId: binding?.authProfileId,
120
- ...lookup
121
- });
122
- try {
123
- await client.request("turn/interrupt", {
124
- threadId: active.threadId,
125
- turnId: active.turnId
126
- }, { timeoutMs: runtime.requestTimeoutMs });
127
- } finally {
128
- releaseLeasedSharedCodexAppServerClient(client);
129
- }
130
- return {
131
- stopped: true,
132
- message: "Codex stop requested."
133
- };
134
- }
135
- async function steerCodexConversationTurn(params) {
136
- const active = readCodexConversationActiveTurn(params.sessionFile);
137
- const text = params.message.trim();
138
- if (!text) return {
139
- steered: false,
140
- message: "Usage: /codex steer <message>"
141
- };
142
- if (!active) return {
143
- steered: false,
144
- message: "No active Codex run to steer."
145
- };
146
- const runtime = resolveCodexAppServerRuntimeOptions({ pluginConfig: params.pluginConfig });
147
- const lookup = buildBindingLookup(params);
148
- const binding = await readCodexAppServerBinding(params.sessionFile, lookup);
149
- const client = await getLeasedSharedCodexAppServerClient({
150
- startOptions: runtime.start,
151
- timeoutMs: runtime.requestTimeoutMs,
152
- authProfileId: binding?.authProfileId,
153
- ...lookup
154
- });
155
- try {
156
- await client.request("turn/steer", {
157
- threadId: active.threadId,
158
- expectedTurnId: active.turnId,
159
- input: [{
160
- type: "text",
161
- text,
162
- text_elements: []
163
- }]
164
- }, { timeoutMs: runtime.requestTimeoutMs });
165
- } finally {
166
- releaseLeasedSharedCodexAppServerClient(client);
167
- }
168
- return {
169
- steered: true,
170
- message: "Sent steer message to Codex."
171
- };
172
- }
173
- async function setCodexConversationModel(params) {
174
- const model = params.model.trim();
175
- if (!model) return "Usage: /codex model <model>";
176
- const lookup = buildBindingLookup(params);
177
- const binding = await requireThreadBinding(params.sessionFile, lookup);
178
- const reviewerPolicyContext = resolveCodexModelBackedReviewerPolicyContext({
179
- provider: "codex",
180
- model,
181
- bindingModelProvider: binding.modelProvider,
182
- bindingModel: binding.model,
183
- nativeAuthProfile: isCodexAppServerNativeAuthProfile({
184
- authProfileId: binding.authProfileId,
185
- ...lookup
186
- })
187
- });
188
- const runtime = resolveCodexAppServerRuntimeOptions({
189
- pluginConfig: params.pluginConfig,
190
- modelProvider: reviewerPolicyContext.modelProvider,
191
- model: reviewerPolicyContext.model,
192
- config: params.config,
193
- agentDir: params.agentDir
194
- });
195
- const modelSelection = resolveCodexAppServerRequestModelSelection({
196
- model,
197
- modelProvider: resolveConversationControlModelProvider({
198
- authProfileId: binding.authProfileId,
199
- bindingModel: binding.model,
200
- bindingModelProvider: binding.modelProvider,
201
- currentModel: model,
202
- ...lookup
203
- }),
204
- authProfileId: binding.authProfileId,
205
- ...lookup
206
- });
207
- const response = await resumeThreadWithOverrides({
208
- runtime,
209
- threadId: binding.threadId,
210
- authProfileId: binding.authProfileId,
211
- ...lookup,
212
- model: modelSelection.model,
213
- modelProvider: modelSelection.modelProvider
214
- });
215
- await writeCodexAppServerBinding(params.sessionFile, {
216
- ...binding,
217
- cwd: response.thread.cwd ?? binding.cwd,
218
- model: response.model ?? modelSelection.model,
219
- modelProvider: response.modelProvider ?? modelSelection.modelProvider,
220
- approvalPolicy: binding.approvalPolicy,
221
- sandbox: binding.sandbox,
222
- serviceTier: binding.serviceTier ?? runtime.serviceTier ?? void 0
223
- }, lookup);
224
- return `Codex model set to ${formatCodexDisplayText(response.model ?? model)}.`;
225
- }
226
- async function setCodexConversationFastMode(params) {
227
- const lookup = buildBindingLookup(params);
228
- const binding = await requireThreadBinding(params.sessionFile, lookup);
229
- if (params.enabled == null) return `Codex fast mode: ${isCodexFastServiceTier(binding.serviceTier) ? "on" : "off"}.`;
230
- const serviceTier = params.enabled ? "priority" : "flex";
231
- await writeCodexAppServerBinding(params.sessionFile, {
232
- ...binding,
233
- serviceTier
234
- }, lookup);
235
- return `Codex fast mode ${params.enabled ? "enabled" : "disabled"}.`;
236
- }
237
- async function setCodexConversationPermissions(params) {
238
- const lookup = buildBindingLookup(params);
239
- const binding = await requireThreadBinding(params.sessionFile, lookup);
240
- if (!params.mode) return `Codex permissions: ${formatPermissionsMode(binding)}.`;
241
- const policy = permissionsForMode(params.mode);
242
- await writeCodexAppServerBinding(params.sessionFile, {
243
- ...binding,
244
- approvalPolicy: policy.approvalPolicy,
245
- sandbox: policy.sandbox
246
- }, lookup);
247
- return `Codex permissions set to ${params.mode === "yolo" ? "full access" : "default"}.`;
248
- }
249
- function parseCodexFastModeArg(arg) {
250
- const normalized = arg?.trim().toLowerCase();
251
- if (!normalized || normalized === "status") return;
252
- if (normalized === "on" || normalized === "true" || normalized === "fast") return true;
253
- if (normalized === "off" || normalized === "false" || normalized === "flex") return false;
254
- }
255
- function parseCodexPermissionsModeArg(arg) {
256
- const normalized = arg?.trim().toLowerCase();
257
- if (!normalized || normalized === "status") return;
258
- if (normalized === "yolo" || normalized === "full" || normalized === "full-access") return "yolo";
259
- if (normalized === "default" || normalized === "guardian") return "default";
260
- }
261
- function formatPermissionsMode(binding) {
262
- return binding.approvalPolicy === "never" && binding.sandbox === "danger-full-access" ? "full access" : "default";
263
- }
264
- async function requireThreadBinding(sessionFile, lookup = {}) {
265
- const binding = await readCodexAppServerBinding(sessionFile, lookup);
266
- if (!binding?.threadId) throw new Error("No Codex thread is attached to this OpenClaw session yet.");
267
- return binding;
268
- }
269
- async function resumeThreadWithOverrides(params) {
270
- const runtime = params.runtime;
271
- const client = await getLeasedSharedCodexAppServerClient({
272
- startOptions: runtime.start,
273
- timeoutMs: runtime.requestTimeoutMs,
274
- authProfileId: params.authProfileId,
275
- ...buildBindingLookup(params)
276
- });
277
- try {
278
- return await client.request(CODEX_CONTROL_METHODS.resumeThread, {
279
- threadId: params.threadId,
280
- ...params.model ? { model: params.model } : {},
281
- ...params.modelProvider ? { modelProvider: params.modelProvider } : {},
282
- approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
283
- sandbox: params.sandbox ?? runtime.sandbox,
284
- approvalsReviewer: runtime.approvalsReviewer,
285
- ...params.serviceTier ? { serviceTier: params.serviceTier } : {},
286
- persistExtendedHistory: true
287
- }, { timeoutMs: runtime.requestTimeoutMs });
288
- } finally {
289
- releaseLeasedSharedCodexAppServerClient(client);
290
- }
291
- }
292
- function buildBindingLookup(params) {
293
- const agentDir = params.agentDir?.trim();
294
- return {
295
- ...agentDir ? { agentDir } : {},
296
- ...params.config ? { config: params.config } : {}
297
- };
298
- }
299
- function resolveConversationControlModelProvider(params) {
300
- const modelProvider = resolveCodexBindingModelProviderFallback({
301
- currentModel: params.currentModel,
302
- bindingModel: params.bindingModel,
303
- bindingModelProvider: params.bindingModelProvider
304
- })?.trim();
305
- if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
306
- if (isCodexAppServerNativeAuthProfile(params) && modelProvider.toLowerCase() === "openai") return;
307
- return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
308
- }
309
- function permissionsForMode(mode) {
310
- return mode === "yolo" ? {
311
- approvalPolicy: "never",
312
- sandbox: "danger-full-access"
313
- } : {
314
- approvalPolicy: "on-request",
315
- sandbox: "workspace-write"
316
- };
317
- }
318
- //#endregion
319
- //#region extensions/codex/src/conversation-turn-collector.ts
320
- const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
321
- function createCodexConversationTurnCollector(threadId) {
322
- let turnId;
323
- let completed = false;
324
- let failedError;
325
- let timeout;
326
- const assistantTextByItem = /* @__PURE__ */ new Map();
327
- const assistantOrder = [];
328
- const pendingNotificationsByTurnId = /* @__PURE__ */ new Map();
329
- let resolveCompletion;
330
- let rejectCompletion;
331
- const rememberItem = (itemId) => {
332
- if (!assistantOrder.includes(itemId)) assistantOrder.push(itemId);
333
- };
334
- const collectReplyText = () => {
335
- return assistantOrder.map((itemId) => assistantTextByItem.get(itemId)?.trim()).filter((text) => Boolean(text)).at(-1) ?? "";
336
- };
337
- const clearWaitState = () => {
338
- if (timeout) {
339
- clearTimeout(timeout);
340
- timeout = void 0;
341
- }
342
- resolveCompletion = void 0;
343
- rejectCompletion = void 0;
344
- };
345
- const finish = () => {
346
- if (completed) return;
347
- completed = true;
348
- if (failedError) rejectCompletion?.(new Error(failedError));
349
- else resolveCompletion?.({ replyText: collectReplyText() });
350
- clearWaitState();
351
- };
352
- const handleNotification = (notification) => {
353
- const params = isJsonObject(notification.params) ? notification.params : void 0;
354
- if (!params || readCodexNotificationThreadId(params) !== threadId) return;
355
- if (!turnId) {
356
- const pendingTurnId = readNotificationTurnId(params);
357
- if (pendingTurnId) {
358
- const pending = pendingNotificationsByTurnId.get(pendingTurnId) ?? [];
359
- if (pending.length < MAX_PENDING_NOTIFICATIONS_PER_TURN) {
360
- pending.push(notification);
361
- pendingNotificationsByTurnId.set(pendingTurnId, pending);
362
- }
363
- }
364
- return;
365
- }
366
- if (!isNotificationForTurn(params, threadId, turnId)) return;
367
- if (notification.method === "item/agentMessage/delta") {
368
- const itemId = readString(params, "itemId") ?? readString(params, "id") ?? "assistant";
369
- const delta = readTextString(params, "delta");
370
- if (!delta) return;
371
- rememberItem(itemId);
372
- assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
373
- return;
374
- }
375
- if (notification.method === "item/completed") {
376
- const item = isJsonObject(params.item) ? params.item : void 0;
377
- if (item?.type === "agentMessage") {
378
- const itemId = readString(item, "id") ?? readString(params, "itemId") ?? "assistant";
379
- const text = readTextString(item, "text");
380
- if (text) {
381
- rememberItem(itemId);
382
- assistantTextByItem.set(itemId, text);
383
- }
384
- }
385
- return;
386
- }
387
- if (notification.method === "turn/completed") {
388
- const turn = isJsonObject(params.turn) ? params.turn : void 0;
389
- if (readString(turn, "status") === "failed") failedError = readString(asOptionalRecord(turn?.error), "message") ?? "codex app-server turn failed";
390
- const items = Array.isArray(turn?.items) ? turn.items : [];
391
- for (const item of items) {
392
- if (!isJsonObject(item) || item.type !== "agentMessage") continue;
393
- const itemId = readString(item, "id") ?? `assistant-${assistantOrder.length + 1}`;
394
- const text = readTextString(item, "text");
395
- if (text) {
396
- rememberItem(itemId);
397
- assistantTextByItem.set(itemId, text);
398
- }
399
- }
400
- finish();
401
- }
402
- };
403
- return {
404
- setTurnId(nextTurnId) {
405
- turnId = nextTurnId;
406
- const pending = pendingNotificationsByTurnId.get(nextTurnId) ?? [];
407
- pendingNotificationsByTurnId.clear();
408
- for (const notification of pending) handleNotification(notification);
409
- },
410
- handleNotification,
411
- wait(params) {
412
- if (completed) return failedError ? Promise.reject(new Error(failedError)) : Promise.resolve({ replyText: collectReplyText() });
413
- return new Promise((resolve, reject) => {
414
- resolveCompletion = resolve;
415
- rejectCompletion = reject;
416
- timeout = setTimeout(() => {
417
- completed = true;
418
- reject(/* @__PURE__ */ new Error("codex app-server bound turn timed out"));
419
- clearWaitState();
420
- }, resolveTimerTimeoutMs(params.timeoutMs, 100, 100));
421
- timeout.unref?.();
422
- });
423
- }
424
- };
425
- }
426
- function isNotificationForTurn(params, threadId, turnId) {
427
- if (readCodexNotificationThreadId(params) !== threadId) return false;
428
- if (!turnId) return true;
429
- const directTurnId = readString(params, "turnId");
430
- if (directTurnId) return directTurnId === turnId;
431
- return readString(isJsonObject(params.turn) ? params.turn : void 0, "id") === turnId;
432
- }
433
- function readNotificationTurnId(params) {
434
- return readCodexNotificationTurnId(params);
435
- }
436
- function readString(record, key) {
437
- const value = record?.[key];
438
- return typeof value === "string" && value.trim() ? value.trim() : void 0;
439
- }
440
- function readTextString(record, key) {
441
- const value = record?.[key];
442
- return typeof value === "string" && value.length > 0 ? value : void 0;
443
- }
444
- //#endregion
445
- //#region extensions/codex/src/conversation-turn-input.ts
446
- const IMAGE_EXTENSIONS = new Set([
447
- ".avif",
448
- ".gif",
449
- ".jpeg",
450
- ".jpg",
451
- ".png",
452
- ".webp"
453
- ]);
454
- function buildCodexConversationTurnInput(params) {
455
- return [{
456
- type: "text",
457
- text: params.prompt,
458
- text_elements: []
459
- }, ...extractInboundMedia(params.event).map(toCodexImageInput).filter((item) => item !== void 0)];
460
- }
461
- function extractInboundMedia(event) {
462
- const metadata = event.metadata ?? {};
463
- const paths = normalizeSingleOrTrimmedStringList(metadata.mediaPaths).concat(normalizeSingleOrTrimmedStringList(metadata.mediaPath));
464
- const urls = normalizeSingleOrTrimmedStringList(metadata.mediaUrls).concat(normalizeSingleOrTrimmedStringList(metadata.mediaUrl));
465
- const mimeTypes = normalizeSingleOrTrimmedStringList(metadata.mediaTypes).concat(normalizeSingleOrTrimmedStringList(metadata.mediaType));
466
- const count = Math.max(paths.length, urls.length, mimeTypes.length);
467
- const media = [];
468
- for (let index = 0; index < count; index += 1) media.push({
469
- path: paths[index],
470
- url: urls[index],
471
- mimeType: mimeTypes[index] ?? mimeTypes[0]
472
- });
473
- return media;
474
- }
475
- function toCodexImageInput(media) {
476
- if (!isImageMedia(media)) return;
477
- const localPath = media.path ?? readLocalMediaPath(media.url);
478
- if (localPath) {
479
- const normalized = normalizeFileUrl(localPath);
480
- return normalized ? {
481
- type: "localImage",
482
- path: normalized
483
- } : void 0;
484
- }
485
- return media.url ? {
486
- type: "image",
487
- url: media.url
488
- } : void 0;
489
- }
490
- function isImageMedia(media) {
491
- if (media.mimeType?.toLowerCase().startsWith("image/")) return true;
492
- const candidate = media.path ?? media.url;
493
- if (!candidate) return false;
494
- return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
495
- }
496
- function normalizeFileUrl(value) {
497
- if (!value.startsWith("file://")) return value;
498
- try {
499
- return fileURLToPath(value);
500
- } catch {
501
- return;
502
- }
503
- }
504
- function readLocalMediaPath(value) {
505
- if (!value) return;
506
- if (value.startsWith("file://")) return value;
507
- if (value.startsWith("//")) return;
508
- if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
509
- return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
510
- }
511
- //#endregion
512
- //#region extensions/codex/src/node-cli-sessions.ts
513
- const CODEX_CLI_SESSIONS_LIST_COMMAND = "codex.cli.sessions.list";
514
- const CODEX_CLI_SESSION_RESUME_COMMAND = "codex.cli.session.resume";
515
- const DEFAULT_SESSION_LIMIT = 10;
516
- const MAX_SESSION_LIMIT = 50;
517
- const DEFAULT_RESUME_TIMEOUT_MS = 20 * 6e4;
518
- const SESSION_ID_PATTERN = /^[A-Za-z0-9._:-]{1,128}$/;
519
- const activeResumeSessions = /* @__PURE__ */ new Set();
520
- const DEFAULT_RESUME_SPAWN_RUNTIME = {
521
- platform: process$1.platform,
522
- env: process$1.env,
523
- execPath: process$1.execPath
524
- };
525
- function createCodexCliSessionNodeHostCommands() {
526
- return [{
527
- command: CODEX_CLI_SESSIONS_LIST_COMMAND,
528
- cap: "codex-cli-sessions",
529
- handle: listLocalCodexCliSessions
530
- }, {
531
- command: CODEX_CLI_SESSION_RESUME_COMMAND,
532
- cap: "codex-cli-sessions",
533
- dangerous: true,
534
- handle: resumeLocalCodexCliSession
535
- }];
536
- }
537
- function createCodexCliSessionNodeInvokePolicies() {
538
- return [{
539
- commands: [CODEX_CLI_SESSIONS_LIST_COMMAND],
540
- defaultPlatforms: [
541
- "macos",
542
- "linux",
543
- "windows"
544
- ],
545
- handle: (ctx) => ctx.invokeNode()
546
- }, {
547
- commands: [CODEX_CLI_SESSION_RESUME_COMMAND],
548
- dangerous: true,
549
- handle: (ctx) => ctx.invokeNode()
550
- }];
551
- }
552
- async function listCodexCliSessionsOnNode(params) {
553
- const node = await resolveCodexCliNode({
554
- runtime: params.runtime,
555
- requestedNode: params.requestedNode,
556
- command: CODEX_CLI_SESSIONS_LIST_COMMAND
557
- });
558
- return {
559
- node,
560
- result: parseCodexCliSessionsListResult(await params.runtime.nodes.invoke({
561
- nodeId: readNodeId(node),
562
- command: CODEX_CLI_SESSIONS_LIST_COMMAND,
563
- params: {
564
- limit: params.limit,
565
- filter: params.filter
566
- },
567
- timeoutMs: 15e3
568
- }))
569
- };
570
- }
571
- async function resolveCodexCliSessionForBindingOnNode(params) {
572
- const listing = await listCodexCliSessionsOnNode({
573
- runtime: params.runtime,
574
- requestedNode: params.requestedNode,
575
- filter: params.sessionId,
576
- limit: MAX_SESSION_LIMIT
577
- });
578
- if (!listing.node.commands?.includes("codex.cli.session.resume")) throw new Error(`Node ${formatNodeLabel(listing.node)} does not expose ${CODEX_CLI_SESSION_RESUME_COMMAND}.`);
579
- return {
580
- node: listing.node,
581
- session: listing.result.sessions.find((session) => session.sessionId === params.sessionId)
582
- };
583
- }
584
- async function resumeCodexCliSessionOnNode(params) {
585
- const payload = unwrapNodeInvokePayload(await params.runtime.nodes.invoke({
586
- nodeId: params.nodeId,
587
- command: CODEX_CLI_SESSION_RESUME_COMMAND,
588
- params: {
589
- sessionId: params.sessionId,
590
- prompt: params.prompt,
591
- cwd: params.cwd,
592
- timeoutMs: params.timeoutMs
593
- },
594
- timeoutMs: (params.timeoutMs ?? DEFAULT_RESUME_TIMEOUT_MS) + 5e3
595
- }));
596
- if (!isRecord(payload) || payload.ok !== true || typeof payload.text !== "string") throw new Error("Codex CLI resume returned an invalid payload.");
597
- return {
598
- ok: true,
599
- sessionId: typeof payload.sessionId === "string" ? payload.sessionId : params.sessionId,
600
- text: payload.text
601
- };
602
- }
603
- function formatCodexCliSessions(params) {
604
- if (params.result.sessions.length === 0) return `No Codex CLI sessions returned from ${formatCodexDisplayText(formatNodeLabel(params.node))}.`;
605
- return [`Codex CLI sessions on ${formatCodexDisplayText(formatNodeLabel(params.node))}:`, ...params.result.sessions.map((session) => {
606
- const details = [session.cwd, session.updatedAt].filter((value) => Boolean(value));
607
- 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`;
608
- })].join("\n");
609
- }
610
- async function listLocalCodexCliSessions(paramsJSON) {
611
- const params = readRecordParam(paramsJSON);
612
- const limit = normalizeLimit(params.limit);
613
- const filter = typeof params.filter === "string" ? params.filter.trim().toLowerCase() : "";
614
- const codexHome = resolveCodexHome();
615
- const summaries = await readHistorySessions(codexHome);
616
- await hydrateSessionFiles(codexHome, summaries);
617
- await hydrateSessionsFromSessionFiles(codexHome, summaries);
618
- const sessions = [...summaries.values()].filter((session) => {
619
- if (!filter) return true;
620
- return [
621
- session.sessionId,
622
- session.cwd,
623
- session.lastMessage
624
- ].some((value) => value?.toLowerCase().includes(filter));
625
- }).toSorted((a, b) => compareOptionalStringsDesc(a.updatedAt, b.updatedAt)).slice(0, limit);
626
- return JSON.stringify({
627
- sessions,
628
- codexHome
629
- });
630
- }
631
- async function resumeLocalCodexCliSession(paramsJSON) {
632
- const params = readRecordParam(paramsJSON);
633
- const sessionId = typeof params.sessionId === "string" ? params.sessionId.trim() : "";
634
- const prompt = typeof params.prompt === "string" ? params.prompt.trim() : "";
635
- if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) throw new Error("Missing or invalid Codex CLI session id.");
636
- if (!prompt) throw new Error("Missing Codex CLI prompt.");
637
- if (activeResumeSessions.has(sessionId)) throw new Error(`Codex CLI session ${sessionId} already has an active resume turn.`);
638
- activeResumeSessions.add(sessionId);
639
- try {
640
- const text = await runCodexExecResume({
641
- sessionId,
642
- prompt,
643
- cwd: typeof params.cwd === "string" && params.cwd.trim() ? params.cwd.trim() : void 0,
644
- timeoutMs: normalizeTimeoutMs(params.timeoutMs)
645
- });
646
- return JSON.stringify({
647
- ok: true,
648
- sessionId,
649
- text: text.trim() || "Codex completed without a text reply."
650
- });
651
- } finally {
652
- activeResumeSessions.delete(sessionId);
653
- }
654
- }
655
- async function runCodexExecResume(params) {
656
- const outputPath = path.join(await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "openclaw-codex-cli-")), "last-message.txt");
657
- try {
658
- const invocation = resolveCodexCliResumeSpawnInvocation([
659
- "exec",
660
- "resume",
661
- "--skip-git-repo-check",
662
- "--output-last-message",
663
- outputPath,
664
- params.sessionId,
665
- "-"
666
- ], {
667
- platform: process$1.platform,
668
- env: process$1.env,
669
- execPath: process$1.execPath
670
- });
671
- const child = spawn(invocation.command, invocation.args, {
672
- cwd: params.cwd || process$1.cwd(),
673
- stdio: [
674
- "pipe",
675
- "pipe",
676
- "pipe"
677
- ],
678
- env: process$1.env,
679
- shell: invocation.shell,
680
- windowsHide: invocation.windowsHide
681
- });
682
- const stdout = [];
683
- const stderr = [];
684
- let timedOut = false;
685
- let forceKillTimeout;
686
- const timeout = setTimeout(() => {
687
- timedOut = true;
688
- child.kill("SIGTERM");
689
- forceKillTimeout = setTimeout(() => child.kill("SIGKILL"), 2e3);
690
- forceKillTimeout.unref?.();
691
- }, params.timeoutMs);
692
- child.stdout.on("data", (chunk) => stdout.push(chunk));
693
- child.stderr.on("data", (chunk) => stderr.push(chunk));
694
- child.stdin.end(params.prompt);
695
- const exitCode = await new Promise((resolve, reject) => {
696
- child.on("error", reject);
697
- child.on("exit", (code) => resolve(code));
698
- }).finally(() => {
699
- clearTimeout(timeout);
700
- if (forceKillTimeout) clearTimeout(forceKillTimeout);
701
- });
702
- if (timedOut) throw new Error(`codex exec resume timed out after ${String(params.timeoutMs)}ms`);
703
- if (exitCode !== 0) {
704
- const message = Buffer.concat(stderr).toString("utf8").trim() || Buffer.concat(stdout).toString("utf8").trim() || `codex exec resume exited with code ${String(exitCode)}`;
705
- throw new Error(message);
706
- }
707
- return await fs.readFile(outputPath, "utf8");
708
- } finally {
709
- await fs.rm(path.dirname(outputPath), {
710
- recursive: true,
711
- force: true
712
- });
713
- }
714
- }
715
- function resolveCodexCliResumeSpawnInvocation(args, runtime = DEFAULT_RESUME_SPAWN_RUNTIME) {
716
- const resolved = materializeWindowsSpawnProgram(resolveWindowsSpawnProgram({
717
- command: "codex",
718
- platform: runtime.platform,
719
- env: runtime.env,
720
- execPath: runtime.execPath,
721
- packageName: "@openai/codex"
722
- }), args);
723
- return {
724
- command: resolved.command,
725
- args: resolved.argv,
726
- shell: resolved.shell,
727
- windowsHide: resolved.windowsHide
728
- };
729
- }
730
- async function readHistorySessions(codexHome) {
731
- const summaries = /* @__PURE__ */ new Map();
732
- const content = await readFileIfExists(path.join(codexHome, "history.jsonl"));
733
- if (!content) return summaries;
734
- for (const line of content.split(/\r?\n/u)) {
735
- const trimmed = line.trim();
736
- if (!trimmed) continue;
737
- let parsed;
738
- try {
739
- parsed = JSON.parse(trimmed);
740
- } catch {
741
- continue;
742
- }
743
- if (!isRecord(parsed) || typeof parsed.session_id !== "string") continue;
744
- const sessionId = parsed.session_id.trim();
745
- if (!sessionId) continue;
746
- const entry = summaries.get(sessionId) ?? {
747
- sessionId,
748
- messageCount: 0
749
- };
750
- entry.messageCount += 1;
751
- if (typeof parsed.text === "string" && parsed.text.trim()) entry.lastMessage = truncateText(parsed.text.trim(), 140);
752
- if (typeof parsed.ts === "number") entry.updatedAt = timestampMsToIsoString(parsed.ts * 1e3) ?? entry.updatedAt;
753
- summaries.set(sessionId, entry);
754
- }
755
- return summaries;
756
- }
757
- async function hydrateSessionFiles(codexHome, summaries) {
758
- if (summaries.size === 0) return;
759
- const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
760
- const pending = new Set(summaries.keys());
761
- for (const file of files) {
762
- const basename = path.basename(file);
763
- const sessionId = [...pending].find((id) => basename.includes(id));
764
- if (!sessionId) continue;
765
- const entry = summaries.get(sessionId);
766
- if (!entry) continue;
767
- entry.sessionFile = file;
768
- const cwd = readSessionMetaCwd(await readFirstLine(file) ?? "");
769
- if (cwd) entry.cwd = cwd;
770
- pending.delete(sessionId);
771
- if (pending.size === 0) return;
772
- }
773
- }
774
- async function hydrateSessionsFromSessionFiles(codexHome, summaries) {
775
- const files = await findSessionFiles(path.join(codexHome, "sessions"), 4);
776
- for (const file of files) {
777
- const summary = await readSessionFileSummary(file);
778
- if (!summary) continue;
779
- const existing = summaries.get(summary.sessionId);
780
- summaries.set(summary.sessionId, {
781
- ...summary,
782
- ...existing,
783
- cwd: existing?.cwd ?? summary.cwd,
784
- sessionFile: existing?.sessionFile ?? summary.sessionFile,
785
- updatedAt: existing?.updatedAt ?? summary.updatedAt,
786
- lastMessage: existing?.lastMessage ?? summary.lastMessage,
787
- messageCount: existing?.messageCount ?? summary.messageCount
788
- });
789
- }
790
- }
791
- async function readSessionFileSummary(file) {
792
- const content = await readFileIfExists(file);
793
- if (!content) return null;
794
- let sessionId = "";
795
- let cwd;
796
- let updatedAt;
797
- let lastMessage;
798
- let messageCount = 0;
799
- for (const line of content.split(/\r?\n/u)) {
800
- const trimmed = line.trim();
801
- if (!trimmed) continue;
802
- let parsed;
803
- try {
804
- parsed = JSON.parse(trimmed);
805
- } catch {
806
- continue;
807
- }
808
- if (!isRecord(parsed)) continue;
809
- if (typeof parsed.timestamp === "string" && parsed.timestamp.trim()) updatedAt = parsed.timestamp.trim();
810
- if (parsed.type === "session_meta" && isRecord(parsed.payload)) {
811
- if (typeof parsed.payload.id === "string" && parsed.payload.id.trim()) sessionId = parsed.payload.id.trim();
812
- if (typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim()) cwd = parsed.payload.cwd.trim();
813
- continue;
814
- }
815
- const messageText = readResponseItemMessageText(parsed);
816
- if (messageText) {
817
- messageCount += 1;
818
- lastMessage = truncateText(messageText, 140);
819
- }
820
- }
821
- if (!sessionId) sessionId = readSessionIdFromFilename(file) ?? "";
822
- if (!sessionId) return null;
823
- return {
824
- sessionId,
825
- updatedAt: updatedAt ?? await readFileMtimeIso(file),
826
- lastMessage,
827
- cwd,
828
- sessionFile: file,
829
- messageCount
830
- };
831
- }
832
- async function findSessionFiles(dir, maxDepth) {
833
- if (maxDepth < 0) return [];
834
- let entries;
835
- try {
836
- entries = await fs.readdir(dir, { withFileTypes: true });
837
- } catch {
838
- return [];
839
- }
840
- const files = [];
841
- for (const entry of entries) {
842
- const entryPath = path.join(dir, entry.name);
843
- if (entry.isDirectory()) files.push(...await findSessionFiles(entryPath, maxDepth - 1));
844
- else if (entry.isFile() && entry.name.endsWith(".jsonl")) files.push(entryPath);
845
- }
846
- return files;
847
- }
848
- function readSessionMetaCwd(line) {
849
- try {
850
- const parsed = JSON.parse(line);
851
- if (!isRecord(parsed) || parsed.type !== "session_meta" || !isRecord(parsed.payload)) return;
852
- return typeof parsed.payload.cwd === "string" && parsed.payload.cwd.trim() ? parsed.payload.cwd.trim() : void 0;
853
- } catch {
854
- return;
855
- }
856
- }
857
- function readResponseItemMessageText(parsed) {
858
- if (parsed.type !== "response_item" || !isRecord(parsed.payload)) return;
859
- if (parsed.payload.type !== "message") return;
860
- if ((typeof parsed.payload.role === "string" ? parsed.payload.role : "") !== "user") return;
861
- const parts = (Array.isArray(parsed.payload.content) ? parsed.payload.content : []).flatMap((entry) => {
862
- if (!isRecord(entry)) return [];
863
- const text = typeof entry.text === "string" ? entry.text : typeof entry.input_text === "string" ? entry.input_text : void 0;
864
- return text?.trim() ? [text.trim()] : [];
865
- });
866
- return parts.length > 0 ? parts.join(" ") : void 0;
867
- }
868
- function readSessionIdFromFilename(file) {
869
- return path.basename(file).match(/[0-9a-f]{8}-[0-9a-f-]{27,}/iu)?.[0];
870
- }
871
- async function resolveCodexCliNode(params) {
872
- const list = await params.runtime.nodes.list(params.requestedNode ? void 0 : { connected: true });
873
- const requested = params.requestedNode?.trim();
874
- const candidates = list.nodes.filter((node) => {
875
- if (requested) return [
876
- node.nodeId,
877
- node.displayName,
878
- node.remoteIp
879
- ].some((value) => value === requested);
880
- return node.connected === true && node.commands?.includes(params.command);
881
- });
882
- if (candidates.length === 0) throw new Error(requested ? `Codex CLI node ${requested} was not found.` : "No connected node exposes Codex CLI session commands.");
883
- const usable = candidates.filter((node) => node.commands?.includes(params.command));
884
- if (usable.length === 0) throw new Error(`Node ${requested ?? "candidate"} does not expose ${params.command}.`);
885
- if (usable.length > 1) throw new Error("Multiple Codex CLI-capable nodes connected. Pass --host <node-id>.");
886
- return usable[0];
887
- }
888
- function parseCodexCliSessionsListResult(raw) {
889
- const payload = unwrapNodeInvokePayload(raw);
890
- if (!isRecord(payload) || !Array.isArray(payload.sessions)) throw new Error("Codex CLI session list returned an invalid payload.");
891
- return {
892
- codexHome: typeof payload.codexHome === "string" ? payload.codexHome : "",
893
- sessions: payload.sessions.flatMap((entry) => {
894
- if (!isRecord(entry) || typeof entry.sessionId !== "string") return [];
895
- return [{
896
- sessionId: entry.sessionId,
897
- updatedAt: typeof entry.updatedAt === "string" ? entry.updatedAt : void 0,
898
- lastMessage: typeof entry.lastMessage === "string" ? entry.lastMessage : void 0,
899
- cwd: typeof entry.cwd === "string" ? entry.cwd : void 0,
900
- sessionFile: typeof entry.sessionFile === "string" ? entry.sessionFile : void 0,
901
- messageCount: typeof entry.messageCount === "number" && Number.isFinite(entry.messageCount) ? entry.messageCount : 0
902
- }];
903
- })
904
- };
905
- }
906
- function unwrapNodeInvokePayload(raw) {
907
- const record = isRecord(raw) ? raw : {};
908
- if (typeof record.payloadJSON === "string" && record.payloadJSON.trim()) try {
909
- return JSON.parse(record.payloadJSON);
910
- } catch (error) {
911
- throw new Error("Codex CLI node command returned malformed payloadJSON.", { cause: error });
912
- }
913
- if ("payload" in record) return record.payload;
914
- return raw;
915
- }
916
- function readRecordParam(paramsJSON) {
917
- if (!paramsJSON?.trim()) return {};
918
- try {
919
- const parsed = JSON.parse(paramsJSON);
920
- return isRecord(parsed) ? parsed : {};
921
- } catch {
922
- return {};
923
- }
924
- }
925
- function resolveCodexHome() {
926
- return process$1.env.CODEX_HOME?.trim() || path.join(os.homedir(), ".codex");
927
- }
928
- async function readFileIfExists(file) {
929
- try {
930
- return await fs.readFile(file, "utf8");
931
- } catch {
932
- return;
933
- }
934
- }
935
- async function readFirstLine(file) {
936
- return (await readFileIfExists(file))?.split(/\r?\n/u)[0];
937
- }
938
- async function readFileMtimeIso(file) {
939
- try {
940
- return (await fs.stat(file)).mtime.toISOString();
941
- } catch {
942
- return;
943
- }
944
- }
945
- function normalizeLimit(value) {
946
- return typeof value === "number" && Number.isFinite(value) ? Math.min(MAX_SESSION_LIMIT, Math.max(1, Math.floor(value))) : DEFAULT_SESSION_LIMIT;
947
- }
948
- function normalizeTimeoutMs(value) {
949
- return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.min(60 * 6e4, Math.floor(value)) : DEFAULT_RESUME_TIMEOUT_MS;
950
- }
951
- function truncateText(value, max) {
952
- if (value.length <= max) return value;
953
- return `${truncateUtf16Safe(value, Math.max(0, max - 3))}...`;
954
- }
955
- function compareOptionalStringsDesc(a, b) {
956
- return (b ?? "").localeCompare(a ?? "");
957
- }
958
- function readNodeId(node) {
959
- if (!node.nodeId) throw new Error("Codex CLI node did not include a node id.");
960
- return node.nodeId;
961
- }
962
- function formatNodeLabel(node) {
963
- return [
964
- node.displayName,
965
- node.nodeId,
966
- node.remoteIp
967
- ].filter(Boolean).join(" / ") || "node";
968
- }
969
- //#endregion
970
- //#region extensions/codex/src/conversation-binding.ts
971
- const DEFAULT_BOUND_TURN_TIMEOUT_MS = 20 * 6e4;
972
- const DEFAULT_AGENT_ID = "main";
973
- const VALID_AGENT_ID_PATTERN = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
974
- const INVALID_AGENT_ID_CHARS_PATTERN = /[^a-z0-9_-]+/g;
975
- const LEADING_DASH_PATTERN = /^-+/;
976
- const TRAILING_DASH_PATTERN = /-+$/;
977
- const NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE = "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow.";
978
- async function resolveConversationAppServerRuntime(params) {
979
- const execPolicy = resolveConversationExecPolicy({
980
- config: params.config,
981
- agentId: params.agentId,
982
- sessionKey: params.sessionKey
983
- });
984
- const sandboxForPolicy = execPolicy.touched && execPolicy.security === "full" && execPolicy.ask !== "off" ? await resolveSandboxContext({
985
- config: params.config,
986
- sessionKey: params.sessionKey,
987
- workspaceDir: params.workspaceDir
988
- }) : void 0;
989
- return {
990
- execPolicy,
991
- runtime: resolveCodexAppServerRuntimeOptions({
992
- pluginConfig: params.pluginConfig,
993
- execPolicy,
994
- modelProvider: params.modelProvider,
995
- model: params.model,
996
- config: params.config,
997
- agentDir: params.agentDir,
998
- openClawSandboxActive: Boolean(sandboxForPolicy?.enabled)
999
- })
1000
- };
1001
- }
1002
- const CODEX_CONVERSATION_GLOBAL_STATE = Symbol.for("openclaw.codex.conversationBinding");
1003
- const CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS = "This Codex thread is bound to an OpenClaw conversation. Answer normally; OpenClaw will deliver your final response back to the conversation.";
1004
- function getGlobalState() {
1005
- const globalState = globalThis;
1006
- globalState[CODEX_CONVERSATION_GLOBAL_STATE] ??= { queues: /* @__PURE__ */ new Map() };
1007
- return globalState[CODEX_CONVERSATION_GLOBAL_STATE];
1008
- }
1009
- async function startCodexConversationThread(params) {
1010
- const workspaceDir = params.workspaceDir?.trim() || resolveCodexDefaultWorkspaceDir(params.pluginConfig);
1011
- const agentDir = params.agentDir?.trim();
1012
- const agentLookup = buildAgentLookup({
1013
- agentDir,
1014
- config: params.config
1015
- });
1016
- const existingBinding = await readCodexAppServerBinding(params.sessionFile, { ...agentLookup });
1017
- const authProfileId = resolveCodexAppServerAuthProfileIdForAgent({
1018
- authProfileId: params.authProfileId ?? existingBinding?.authProfileId,
1019
- ...agentLookup
1020
- });
1021
- if (params.threadId?.trim()) await attachExistingThread({
1022
- pluginConfig: params.pluginConfig,
1023
- sessionFile: params.sessionFile,
1024
- threadId: params.threadId.trim(),
1025
- workspaceDir,
1026
- ...agentDir ? { agentDir } : {},
1027
- model: params.model,
1028
- modelProvider: params.modelProvider,
1029
- authProfileId,
1030
- approvalPolicy: params.approvalPolicy,
1031
- sandbox: params.sandbox,
1032
- serviceTier: params.serviceTier,
1033
- config: params.config,
1034
- sessionKey: params.sessionKey,
1035
- agentId: params.agentId
1036
- });
1037
- else await createThread({
1038
- pluginConfig: params.pluginConfig,
1039
- sessionFile: params.sessionFile,
1040
- workspaceDir,
1041
- ...agentDir ? { agentDir } : {},
1042
- model: params.model,
1043
- modelProvider: params.modelProvider,
1044
- authProfileId,
1045
- approvalPolicy: params.approvalPolicy,
1046
- sandbox: params.sandbox,
1047
- serviceTier: params.serviceTier,
1048
- config: params.config,
1049
- sessionKey: params.sessionKey,
1050
- agentId: params.agentId
1051
- });
1052
- return createCodexConversationBindingData({
1053
- sessionFile: params.sessionFile,
1054
- workspaceDir,
1055
- ...agentDir ? { agentDir } : {},
1056
- agentId: params.agentId
1057
- });
1058
- }
1059
- async function handleCodexConversationInboundClaim(event, ctx, options = {}) {
1060
- const data = readCodexConversationBindingData(ctx.pluginBinding);
1061
- if (!data) return;
1062
- if (event.commandAuthorized !== true) return { handled: true };
1063
- const prompt = event.bodyForAgent?.trim() || event.content?.trim() || "";
1064
- if (!prompt) return { handled: true };
1065
- if (!canMutateCodexHost(event)) return {
1066
- handled: true,
1067
- reply: { text: CODEX_NATIVE_EXECUTION_AUTH_ERROR }
1068
- };
1069
- const nativeExecutionBlock = data.kind === "codex-cli-node-session" ? resolveCodexNativeSandboxBlock({
1070
- config: options.config,
1071
- sessionKey: event.sessionKey ?? ctx.sessionKey,
1072
- surface: "Codex CLI node conversation binding"
1073
- }) : resolveCodexNativeExecutionBlock({
1074
- config: options.config,
1075
- sessionKey: event.sessionKey ?? ctx.sessionKey,
1076
- agentId: data.agentId,
1077
- surface: "Codex app-server conversation binding"
1078
- });
1079
- if (nativeExecutionBlock) return {
1080
- handled: true,
1081
- reply: { text: nativeExecutionBlock }
1082
- };
1083
- if (data.kind === "codex-cli-node-session") {
1084
- const resume = options.resumeCodexCliSessionOnNode;
1085
- if (!resume) return {
1086
- handled: true,
1087
- reply: { text: "Codex CLI node binding is unavailable because Gateway node runtime is not attached." }
1088
- };
1089
- try {
1090
- return {
1091
- handled: true,
1092
- reply: (await enqueueBoundTurn(`${data.nodeId}:${data.sessionId}`, async () => {
1093
- return { reply: { text: (await resume({
1094
- nodeId: data.nodeId,
1095
- sessionId: data.sessionId,
1096
- prompt,
1097
- cwd: data.cwd,
1098
- timeoutMs: options.timeoutMs
1099
- })).text.trim() || "Codex completed without a text reply." } };
1100
- })).reply
1101
- };
1102
- } catch (error) {
1103
- return {
1104
- handled: true,
1105
- reply: { text: `Codex CLI node turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
1106
- };
1107
- }
1108
- }
1109
- try {
1110
- return {
1111
- handled: true,
1112
- reply: (await enqueueBoundTurn(data.sessionFile, () => runBoundTurnWithMissingThreadRecovery({
1113
- data,
1114
- prompt,
1115
- event,
1116
- config: options.config,
1117
- sessionKey: event.sessionKey ?? ctx.sessionKey,
1118
- pluginConfig: options.pluginConfig,
1119
- timeoutMs: options.timeoutMs
1120
- }))).reply
1121
- };
1122
- } catch (error) {
1123
- return {
1124
- handled: true,
1125
- reply: { text: `Codex app-server turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
1126
- };
1127
- }
1128
- }
1129
- async function handleCodexConversationBindingResolved(event) {
1130
- if (event.status !== "denied") return;
1131
- const data = readCodexConversationBindingDataRecord(event.request.data ?? {});
1132
- if (!data || data.kind !== "codex-app-server-session") return;
1133
- await clearCodexAppServerBinding(data.sessionFile);
1134
- }
1135
- async function resolveThreadBindingRuntime(params) {
1136
- const agentLookup = buildAgentLookup({
1137
- agentDir: params.agentDir,
1138
- config: params.config
1139
- });
1140
- const modelProvider = resolveThreadRequestModelProvider({
1141
- authProfileId: params.authProfileId,
1142
- modelProvider: params.modelProvider,
1143
- ...agentLookup
1144
- });
1145
- const modelSelection = resolveOptionalThreadRequestModelSelection({
1146
- model: params.model,
1147
- modelProvider,
1148
- authProfileId: params.authProfileId,
1149
- ...agentLookup
1150
- });
1151
- const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
1152
- authProfileId: params.authProfileId,
1153
- modelProvider: params.modelProvider,
1154
- ...agentLookup
1155
- });
1156
- const { execPolicy, runtime } = await resolveConversationAppServerRuntime({
1157
- pluginConfig: params.pluginConfig,
1158
- config: params.config,
1159
- agentId: params.agentId,
1160
- sessionKey: params.sessionKey,
1161
- workspaceDir: params.workspaceDir,
1162
- modelProvider: reviewerModelProvider,
1163
- model: params.model,
1164
- agentDir: params.agentDir
1165
- });
1166
- const modelScopedRuntime = resolveCodexAppServerForModelProvider({
1167
- appServer: runtime,
1168
- provider: reviewerModelProvider,
1169
- model: params.model,
1170
- config: params.config,
1171
- env: process.env,
1172
- agentDir: params.agentDir
1173
- });
1174
- assertNativeConversationApprovalPolicySupported({
1175
- execPolicy,
1176
- approvalPolicy: execPolicy?.touched ? modelScopedRuntime.approvalPolicy : params.approvalPolicy ?? modelScopedRuntime.approvalPolicy,
1177
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
1178
- modelBackedApprovalsReviewerUnavailable: !canUseCodexModelBackedApprovalsReviewerForModel({
1179
- modelProvider: reviewerModelProvider,
1180
- model: params.model,
1181
- config: params.config,
1182
- env: process.env,
1183
- agentDir: params.agentDir
1184
- })
1185
- });
1186
- const client = await getLeasedSharedCodexAppServerClient({
1187
- startOptions: runtime.start,
1188
- timeoutMs: runtime.requestTimeoutMs,
1189
- authProfileId: params.authProfileId,
1190
- ...agentLookup
1191
- });
1192
- return {
1193
- execPolicy,
1194
- runtime: modelScopedRuntime,
1195
- agentLookup,
1196
- model: modelSelection?.model,
1197
- modelProvider: modelSelection?.modelProvider ?? modelProvider,
1198
- client
1199
- };
1200
- }
1201
- function buildThreadRequestRuntimeOptions(params, resolved) {
1202
- const serviceTier = params.serviceTier ?? resolved.runtime.serviceTier;
1203
- const sandbox = resolved.execPolicy?.touched ? resolved.runtime.sandbox : params.sandbox ?? resolved.runtime.sandbox;
1204
- return {
1205
- approvalPolicy: resolved.execPolicy?.touched ? resolved.runtime.approvalPolicy : params.approvalPolicy ?? resolved.runtime.approvalPolicy,
1206
- approvalsReviewer: resolved.runtime.approvalsReviewer,
1207
- ...codexConversationSandboxOrPermissions(resolved.runtime, sandbox),
1208
- ...serviceTier ? { serviceTier } : {}
1209
- };
1210
- }
1211
- function codexConversationSandboxOrPermissions(runtime, sandbox) {
1212
- const networkProxy = runtime.networkProxy;
1213
- if (networkProxy) return { config: networkProxy.configPatch };
1214
- return { sandbox };
1215
- }
1216
- async function requestNewConversationBindingThread(params, resolved) {
1217
- return await resolved.client.request("thread/start", {
1218
- cwd: params.workspaceDir,
1219
- ...resolved.model ? { model: resolved.model } : {},
1220
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
1221
- personality: CODEX_NATIVE_PERSONALITY_NONE,
1222
- ...buildThreadRequestRuntimeOptions(params, resolved),
1223
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
1224
- experimentalRawEvents: true,
1225
- persistExtendedHistory: true
1226
- }, { timeoutMs: resolved.runtime.requestTimeoutMs });
1227
- }
1228
- async function writeThreadBindingFromResponse(params, resolved, response) {
1229
- const runtimeApprovalPolicy = typeof resolved.runtime.approvalPolicy === "string" ? resolved.runtime.approvalPolicy : void 0;
1230
- await writeCodexAppServerBinding(params.sessionFile, {
1231
- threadId: response.thread.id,
1232
- cwd: response.thread.cwd ?? params.workspaceDir,
1233
- authProfileId: params.authProfileId,
1234
- model: response.model ?? resolved.model ?? params.model,
1235
- modelProvider: normalizeCodexAppServerBindingModelProvider({
1236
- authProfileId: params.authProfileId,
1237
- modelProvider: response.modelProvider ?? resolved.modelProvider ?? params.modelProvider,
1238
- ...resolved.agentLookup
1239
- }),
1240
- approvalPolicy: resolved.execPolicy?.touched ? runtimeApprovalPolicy : params.approvalPolicy ?? runtimeApprovalPolicy,
1241
- sandbox: resolved.execPolicy?.touched ? resolved.runtime.sandbox : params.sandbox ?? resolved.runtime.sandbox,
1242
- serviceTier: params.serviceTier ?? resolved.runtime.serviceTier ?? void 0,
1243
- networkProxyProfileName: resolved.runtime.networkProxy?.profileName,
1244
- networkProxyConfigFingerprint: resolved.runtime.networkProxy?.configFingerprint
1245
- }, { ...resolved.agentLookup });
1246
- }
1247
- async function attachExistingThread(params) {
1248
- const resolved = await resolveThreadBindingRuntime(params);
1249
- try {
1250
- await writeThreadBindingFromResponse(params, resolved, resolved.runtime.networkProxy ? await requestNewConversationBindingThread(params, resolved) : await resolved.client.request(CODEX_CONTROL_METHODS.resumeThread, {
1251
- threadId: params.threadId,
1252
- ...resolved.model ? { model: resolved.model } : {},
1253
- ...resolved.modelProvider ? { modelProvider: resolved.modelProvider } : {},
1254
- personality: CODEX_NATIVE_PERSONALITY_NONE,
1255
- ...buildThreadRequestRuntimeOptions(params, resolved),
1256
- persistExtendedHistory: true
1257
- }, { timeoutMs: resolved.runtime.requestTimeoutMs }));
1258
- } finally {
1259
- releaseLeasedSharedCodexAppServerClient(resolved.client);
1260
- }
1261
- }
1262
- async function createThread(params) {
1263
- const resolved = await resolveThreadBindingRuntime(params);
1264
- try {
1265
- await writeThreadBindingFromResponse(params, resolved, await requestNewConversationBindingThread(params, resolved));
1266
- } finally {
1267
- releaseLeasedSharedCodexAppServerClient(resolved.client);
1268
- }
1269
- }
1270
- async function runBoundTurn(params) {
1271
- const agentLookup = buildAgentLookup({
1272
- agentDir: params.data.agentDir,
1273
- config: params.config
1274
- });
1275
- const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
1276
- if (!binding?.threadId) throw new Error("bound Codex conversation has no thread binding");
1277
- let threadId = binding.threadId;
1278
- const workspaceDir = binding.cwd || params.data.workspaceDir;
1279
- const reviewerModelProvider = resolveModelBackedReviewerPolicyProvider({
1280
- authProfileId: binding.authProfileId,
1281
- modelProvider: binding.modelProvider,
1282
- ...agentLookup
1283
- });
1284
- const { execPolicy, runtime } = await resolveConversationAppServerRuntime({
1285
- pluginConfig: params.pluginConfig,
1286
- config: params.config,
1287
- agentId: params.data.agentId,
1288
- sessionKey: params.sessionKey,
1289
- workspaceDir,
1290
- modelProvider: reviewerModelProvider,
1291
- model: binding.model,
1292
- agentDir: params.data.agentDir
1293
- });
1294
- const modelScopedRuntime = resolveCodexAppServerForModelProvider({
1295
- appServer: runtime,
1296
- provider: reviewerModelProvider,
1297
- model: binding.model,
1298
- config: params.config,
1299
- env: process.env,
1300
- agentDir: params.data.agentDir
1301
- });
1302
- const modelBackedApprovalsReviewerUnavailable = !canUseCodexModelBackedApprovalsReviewerForModel({
1303
- modelProvider: reviewerModelProvider,
1304
- model: binding.model,
1305
- config: params.config,
1306
- env: process.env,
1307
- agentDir: params.data.agentDir
1308
- });
1309
- const useModelScopedPolicy = execPolicy?.touched === true || modelBackedApprovalsReviewerUnavailable;
1310
- const approvalPolicy = useModelScopedPolicy ? modelScopedRuntime.approvalPolicy : binding.approvalPolicy ?? modelScopedRuntime.approvalPolicy;
1311
- const sandbox = useModelScopedPolicy ? modelScopedRuntime.sandbox : binding.sandbox ?? modelScopedRuntime.sandbox;
1312
- const permissionProfile = modelScopedRuntime.networkProxy?.profileName;
1313
- const networkProxyConfigFingerprint = modelScopedRuntime.networkProxy?.configFingerprint;
1314
- const networkProxyBindingChanged = binding.networkProxyProfileName !== permissionProfile || binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint;
1315
- const serviceTier = binding.serviceTier ?? runtime.serviceTier;
1316
- let useStickyNetworkProfile = permissionProfile !== void 0 && binding.networkProxyProfileName === permissionProfile && binding.networkProxyConfigFingerprint === networkProxyConfigFingerprint;
1317
- assertNativeConversationApprovalPolicySupported({
1318
- execPolicy,
1319
- approvalPolicy,
1320
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
1321
- modelBackedApprovalsReviewerUnavailable
1322
- });
1323
- const modelSelection = binding.model ? resolveCodexAppServerRequestModelSelection({
1324
- model: binding.model,
1325
- modelProvider: binding.modelProvider,
1326
- authProfileId: binding.authProfileId,
1327
- ...agentLookup
1328
- }) : void 0;
1329
- const client = await getLeasedSharedCodexAppServerClient({
1330
- startOptions: runtime.start,
1331
- timeoutMs: runtime.requestTimeoutMs,
1332
- authProfileId: binding.authProfileId,
1333
- ...agentLookup
1334
- });
1335
- let notificationCleanup = () => void 0;
1336
- let requestCleanup = () => void 0;
1337
- try {
1338
- if (networkProxyBindingChanged) {
1339
- const response = assertCodexThreadStartResponse(await client.request("thread/start", {
1340
- cwd: workspaceDir,
1341
- ...modelSelection?.model ? { model: modelSelection.model } : {},
1342
- ...modelSelection?.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
1343
- personality: CODEX_NATIVE_PERSONALITY_NONE,
1344
- approvalPolicy,
1345
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
1346
- ...modelScopedRuntime.networkProxy ? { config: modelScopedRuntime.networkProxy.configPatch } : { sandbox },
1347
- ...serviceTier ? { serviceTier } : {},
1348
- developerInstructions: CODEX_CONVERSATION_THREAD_DEVELOPER_INSTRUCTIONS,
1349
- experimentalRawEvents: true,
1350
- persistExtendedHistory: true
1351
- }, { timeoutMs: runtime.requestTimeoutMs }));
1352
- threadId = response.thread.id;
1353
- await writeCodexAppServerBinding(params.data.sessionFile, {
1354
- threadId,
1355
- cwd: response.thread.cwd ?? workspaceDir,
1356
- authProfileId: binding.authProfileId,
1357
- model: response.model ?? modelSelection?.model ?? binding.model,
1358
- modelProvider: normalizeCodexAppServerBindingModelProvider({
1359
- authProfileId: binding.authProfileId,
1360
- modelProvider: response.modelProvider ?? modelSelection?.modelProvider ?? binding.modelProvider,
1361
- ...agentLookup
1362
- }),
1363
- approvalPolicy: typeof approvalPolicy === "string" ? approvalPolicy : void 0,
1364
- sandbox,
1365
- serviceTier: serviceTier ?? void 0,
1366
- networkProxyProfileName: modelScopedRuntime.networkProxy?.profileName,
1367
- networkProxyConfigFingerprint: modelScopedRuntime.networkProxy?.configFingerprint
1368
- }, agentLookup);
1369
- useStickyNetworkProfile = modelScopedRuntime.networkProxy !== void 0;
1370
- }
1371
- const collector = createCodexConversationTurnCollector(threadId);
1372
- notificationCleanup = client.addNotificationHandler((notification) => collector.handleNotification(notification));
1373
- requestCleanup = client.addRequestHandler(async (request) => {
1374
- if (request.method === "item/tool/call") return {
1375
- contentItems: [{
1376
- type: "inputText",
1377
- text: "OpenClaw native Codex conversation binding does not expose dynamic OpenClaw tools yet."
1378
- }],
1379
- success: false
1380
- };
1381
- if (request.method === "item/commandExecution/requestApproval" || request.method === "item/fileChange/requestApproval") return {
1382
- decision: "decline",
1383
- reason: "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
1384
- };
1385
- if (request.method === "item/permissions/requestApproval") return {
1386
- permissions: {},
1387
- scope: "turn"
1388
- };
1389
- if (request.method.includes("requestApproval")) return {
1390
- decision: "decline",
1391
- reason: "OpenClaw native Codex conversation binding cannot route interactive approvals yet; use the Codex harness or explicit /acp spawn codex for that workflow."
1392
- };
1393
- });
1394
- const turnId = (await client.request("turn/start", {
1395
- threadId,
1396
- input: buildCodexConversationTurnInput({
1397
- prompt: params.prompt,
1398
- event: params.event
1399
- }),
1400
- cwd: workspaceDir,
1401
- approvalPolicy,
1402
- approvalsReviewer: modelScopedRuntime.approvalsReviewer,
1403
- ...useStickyNetworkProfile ? {} : { sandboxPolicy: codexSandboxPolicyForTurn(sandbox, workspaceDir) },
1404
- ...modelSelection?.model ? { model: modelSelection.model } : {},
1405
- personality: CODEX_NATIVE_PERSONALITY_NONE,
1406
- ...serviceTier ? { serviceTier } : {}
1407
- }, { timeoutMs: runtime.requestTimeoutMs })).turn.id;
1408
- const activeCleanup = trackCodexConversationActiveTurn({
1409
- sessionFile: params.data.sessionFile,
1410
- threadId,
1411
- turnId
1412
- });
1413
- collector.setTurnId(turnId);
1414
- return { reply: { text: (await collector.wait({ timeoutMs: params.timeoutMs ?? DEFAULT_BOUND_TURN_TIMEOUT_MS }).finally(activeCleanup)).replyText.trim() || "Codex completed without a text reply." } };
1415
- } finally {
1416
- notificationCleanup();
1417
- requestCleanup();
1418
- releaseLeasedSharedCodexAppServerClient(client);
1419
- }
1420
- }
1421
- function assertNativeConversationApprovalPolicySupported(params) {
1422
- if (params.approvalPolicy !== "never" && (params.execPolicy?.touched === true || params.modelBackedApprovalsReviewerUnavailable && params.approvalsReviewer === "user")) throw new Error(NATIVE_CONVERSATION_INTERACTIVE_APPROVALS_UNAVAILABLE);
1423
- }
1424
- async function runBoundTurnWithMissingThreadRecovery(params) {
1425
- try {
1426
- return await runBoundTurn(params);
1427
- } catch (error) {
1428
- if (!isCodexThreadNotFoundError(error)) throw error;
1429
- const agentLookup = buildAgentLookup({
1430
- agentDir: params.data.agentDir,
1431
- config: params.config
1432
- });
1433
- const binding = await readCodexAppServerBinding(params.data.sessionFile, agentLookup);
1434
- const useCurrentRuntimePolicy = resolveConversationExecPolicy({
1435
- config: params.config,
1436
- agentId: params.data.agentId,
1437
- sessionKey: params.sessionKey
1438
- }).touched;
1439
- await startCodexConversationThread({
1440
- pluginConfig: params.pluginConfig,
1441
- sessionFile: params.data.sessionFile,
1442
- workspaceDir: binding?.cwd || params.data.workspaceDir,
1443
- ...agentLookup,
1444
- model: binding?.model,
1445
- modelProvider: binding?.modelProvider,
1446
- authProfileId: binding?.authProfileId,
1447
- approvalPolicy: useCurrentRuntimePolicy ? void 0 : binding?.approvalPolicy,
1448
- sandbox: useCurrentRuntimePolicy ? void 0 : binding?.sandbox,
1449
- serviceTier: binding?.serviceTier,
1450
- config: params.config,
1451
- sessionKey: params.sessionKey,
1452
- agentId: params.data.agentId
1453
- });
1454
- return await runBoundTurn(params);
1455
- }
1456
- }
1457
- function resolveConversationExecPolicy(params) {
1458
- const agentId = params.agentId ?? (params.config ? resolveSessionAgentIds({
1459
- sessionKey: params.sessionKey,
1460
- config: params.config
1461
- }).sessionAgentId : void 0);
1462
- return resolveOpenClawExecPolicyForCodexAppServer({
1463
- config: params.config,
1464
- agentId,
1465
- execOverrides: readSessionExecOverrides({
1466
- config: params.config,
1467
- agentId,
1468
- sessionKey: params.sessionKey
1469
- }),
1470
- approvals: loadExecApprovals()
1471
- });
1472
- }
1473
- function readSessionExecOverrides(params) {
1474
- const sessionKey = params.sessionKey?.trim();
1475
- if (!params.config || !sessionKey) return;
1476
- if (!canReadSessionExecOverrides({
1477
- config: params.config,
1478
- agentId: params.agentId,
1479
- sessionKey
1480
- })) return;
1481
- const entry = getSessionEntry({
1482
- storePath: resolveStorePath(params.config.session?.store, { agentId: params.agentId }),
1483
- sessionKey,
1484
- readConsistency: "latest"
1485
- });
1486
- if (!entry?.execSecurity && !entry?.execAsk) return;
1487
- return {
1488
- security: entry.execSecurity,
1489
- ask: entry.execAsk
1490
- };
1491
- }
1492
- function canReadSessionExecOverrides(params) {
1493
- const agentId = normalizeAgentIdOrDefault(params.agentId);
1494
- if (!agentId) return true;
1495
- const sessionAgentId = parseAgentIdFromSessionKey(params.sessionKey);
1496
- if (!sessionAgentId) return isDefaultAgentSessionKeyForAgent({
1497
- config: params.config,
1498
- agentId
1499
- });
1500
- return sessionAgentId === agentId;
1501
- }
1502
- function parseAgentIdFromSessionKey(sessionKey) {
1503
- const raw = sessionKey?.trim();
1504
- if (!raw) return;
1505
- const parts = raw.toLowerCase().split(":").filter(Boolean);
1506
- if (parts.length < 3 || parts[0] !== "agent" || !parts[2]) return;
1507
- return normalizeAgentIdOrDefault(parts[1]);
1508
- }
1509
- function isDefaultAgentSessionKeyForAgent(params) {
1510
- return normalizeAgentId(params.agentId) === resolveDefaultPolicyAgentId(params.config);
1511
- }
1512
- function resolveDefaultPolicyAgentId(config) {
1513
- const agents = (config.agents?.list ?? []).filter((entry) => entry !== null && typeof entry === "object");
1514
- return normalizeAgentId((agents.find((entry) => entry?.default) ?? agents[0])?.id);
1515
- }
1516
- function normalizeAgentIdOrDefault(value) {
1517
- const normalized = normalizeAgentId(value);
1518
- return normalized === DEFAULT_AGENT_ID && !(value ?? "").trim() ? void 0 : normalized;
1519
- }
1520
- function normalizeAgentId(value) {
1521
- const trimmed = (value ?? "").trim();
1522
- if (!trimmed) return DEFAULT_AGENT_ID;
1523
- const normalized = trimmed.toLowerCase();
1524
- if (VALID_AGENT_ID_PATTERN.test(trimmed)) return normalized;
1525
- return normalized.replace(INVALID_AGENT_ID_CHARS_PATTERN, "-").replace(LEADING_DASH_PATTERN, "").replace(TRAILING_DASH_PATTERN, "").slice(0, 64) || DEFAULT_AGENT_ID;
1526
- }
1527
- function isCodexThreadNotFoundError(error) {
1528
- const message = formatErrorMessage(error);
1529
- return /\bthread not found:/iu.test(message) || /\bbound Codex conversation has no thread binding\b/u.test(message);
1530
- }
1531
- function enqueueBoundTurn(key, run) {
1532
- const state = getGlobalState();
1533
- const next = (state.queues.get(key) ?? Promise.resolve()).then(run, run);
1534
- const queued = next.then(() => void 0, () => void 0);
1535
- state.queues.set(key, queued);
1536
- next.finally(() => {
1537
- if (state.queues.get(key) === queued) state.queues.delete(key);
1538
- }).catch(() => void 0);
1539
- return next;
1540
- }
1541
- function resolveThreadRequestModelProvider(params) {
1542
- const modelProvider = params.modelProvider?.trim();
1543
- if (!modelProvider || modelProvider.toLowerCase() === "codex") return;
1544
- if (isCodexAppServerNativeAuthProfile(params) && modelProvider.toLowerCase() === "openai") return;
1545
- return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
1546
- }
1547
- function resolveOptionalThreadRequestModelSelection(params) {
1548
- if (!params.model?.trim()) return;
1549
- return resolveCodexAppServerRequestModelSelection({
1550
- model: params.model,
1551
- modelProvider: params.modelProvider,
1552
- authProfileId: params.authProfileId,
1553
- agentDir: params.agentDir,
1554
- config: params.config
1555
- });
1556
- }
1557
- function resolveModelBackedReviewerPolicyProvider(params) {
1558
- const modelProvider = params.modelProvider?.trim();
1559
- if (modelProvider && modelProvider.toLowerCase() !== "codex") return modelProvider.toLowerCase() === "openai" ? "openai" : modelProvider;
1560
- return isCodexAppServerNativeAuthProfile(params) ? "openai" : void 0;
1561
- }
1562
- function buildAgentLookup(params) {
1563
- const agentDir = params.agentDir?.trim();
1564
- return {
1565
- ...agentDir ? { agentDir } : {},
1566
- ...params.config ? { config: params.config } : {}
1567
- };
1568
- }
1569
- //#endregion
1570
- export { canMutateCodexHost as C, CODEX_NATIVE_EXECUTION_AUTH_ERROR as S, steerCodexConversationTurn as _, createCodexCliSessionNodeInvokePolicies as a, readCodexConversationBindingData as b, resolveCodexCliSessionForBindingOnNode as c, parseCodexFastModeArg as d, parseCodexPermissionsModeArg as f, setCodexConversationPermissions as g, setCodexConversationModel as h, createCodexCliSessionNodeHostCommands as i, resumeCodexCliSessionOnNode as l, setCodexConversationFastMode as m, handleCodexConversationInboundClaim as n, formatCodexCliSessions as o, readCodexConversationActiveTurn as p, startCodexConversationThread as r, listCodexCliSessionsOnNode as s, handleCodexConversationBindingResolved as t, formatPermissionsMode as u, stopCodexConversationTurn as v, resolveCodexDefaultWorkspaceDir as x, createCodexCliNodeConversationBindingData as y };