@openclaw/codex 2026.5.4-beta.1 → 2026.5.4-beta.3

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.
@@ -1,10 +1,11 @@
1
- import { i as resolveCodexAppServerRuntimeOptions, n as codexSandboxPolicyForTurn } from "./config-CkkoMeqF.js";
1
+ import { i as resolveCodexAppServerRuntimeOptions, n as codexSandboxPolicyForTurn } from "./config-ByrA30No.js";
2
2
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
3
- import { n as getSharedCodexAppServerClient, s as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-B7LbV3PF.js";
4
- import { t as CODEX_CONTROL_METHODS } from "./capabilities-BmhO5h8O.js";
3
+ import { d as CODEX_CONTROL_METHODS, r as formatCodexDisplayText } from "./command-formatters-PiJcdUbu.js";
4
+ import { n as getSharedCodexAppServerClient, s as resolveCodexAppServerAuthProfileIdForAgent } from "./shared-client-Dfk3Enm-.js";
5
5
  import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, r as normalizeCodexAppServerBindingModelProvider, t as clearCodexAppServerBinding } from "./session-binding-DuJYTJQy.js";
6
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
7
7
  import path from "node:path";
8
+ import { fileURLToPath } from "node:url";
8
9
  import process from "node:process";
9
10
  //#region extensions/codex/src/conversation-binding-data.ts
10
11
  const BINDING_DATA_VERSION = 1;
@@ -130,7 +131,7 @@ async function setCodexConversationModel(params) {
130
131
  sandbox: binding.sandbox,
131
132
  serviceTier: binding.serviceTier ?? runtime.serviceTier
132
133
  });
133
- return `Codex model set to ${response.model ?? model}.`;
134
+ return `Codex model set to ${formatCodexDisplayText(response.model ?? model)}.`;
134
135
  }
135
136
  async function setCodexConversationFastMode(params) {
136
137
  const binding = await requireThreadBinding(params.sessionFile);
@@ -200,6 +201,7 @@ function permissionsForMode(mode) {
200
201
  }
201
202
  //#endregion
202
203
  //#region extensions/codex/src/conversation-turn-collector.ts
204
+ const MAX_PENDING_NOTIFICATIONS_PER_TURN = 100;
203
205
  function createCodexConversationTurnCollector(threadId) {
204
206
  let turnId;
205
207
  let completed = false;
@@ -207,6 +209,7 @@ function createCodexConversationTurnCollector(threadId) {
207
209
  let timeout;
208
210
  const assistantTextByItem = /* @__PURE__ */ new Map();
209
211
  const assistantOrder = [];
212
+ const pendingNotificationsByTurnId = /* @__PURE__ */ new Map();
210
213
  let resolveCompletion;
211
214
  let rejectCompletion;
212
215
  const rememberItem = (itemId) => {
@@ -230,49 +233,65 @@ function createCodexConversationTurnCollector(threadId) {
230
233
  else resolveCompletion?.({ replyText: collectReplyText() });
231
234
  clearWaitState();
232
235
  };
233
- return {
234
- setTurnId(nextTurnId) {
235
- turnId = nextTurnId;
236
- },
237
- handleNotification(notification) {
238
- const params = isJsonObject(notification.params) ? notification.params : void 0;
239
- if (!params || !isNotificationForTurn(params, threadId, turnId)) return;
240
- if (notification.method === "item/agentMessage/delta") {
241
- const itemId = readString(params, "itemId") ?? readString(params, "id") ?? "assistant";
242
- const delta = readTextString(params, "delta");
243
- if (!delta) return;
244
- rememberItem(itemId);
245
- assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
246
- return;
236
+ const handleNotification = (notification) => {
237
+ const params = isJsonObject(notification.params) ? notification.params : void 0;
238
+ if (!params || readString(params, "threadId") !== threadId) return;
239
+ if (!turnId) {
240
+ const pendingTurnId = readNotificationTurnId(params);
241
+ if (pendingTurnId) {
242
+ const pending = pendingNotificationsByTurnId.get(pendingTurnId) ?? [];
243
+ if (pending.length < MAX_PENDING_NOTIFICATIONS_PER_TURN) {
244
+ pending.push(notification);
245
+ pendingNotificationsByTurnId.set(pendingTurnId, pending);
246
+ }
247
247
  }
248
- if (notification.method === "item/completed") {
249
- const item = isJsonObject(params.item) ? params.item : void 0;
250
- if (item?.type === "agentMessage") {
251
- const itemId = readString(item, "id") ?? readString(params, "itemId") ?? "assistant";
252
- const text = readTextString(item, "text");
253
- if (text) {
254
- rememberItem(itemId);
255
- assistantTextByItem.set(itemId, text);
256
- }
248
+ return;
249
+ }
250
+ if (!isNotificationForTurn(params, threadId, turnId)) return;
251
+ if (notification.method === "item/agentMessage/delta") {
252
+ const itemId = readString(params, "itemId") ?? readString(params, "id") ?? "assistant";
253
+ const delta = readTextString(params, "delta");
254
+ if (!delta) return;
255
+ rememberItem(itemId);
256
+ assistantTextByItem.set(itemId, `${assistantTextByItem.get(itemId) ?? ""}${delta}`);
257
+ return;
258
+ }
259
+ if (notification.method === "item/completed") {
260
+ const item = isJsonObject(params.item) ? params.item : void 0;
261
+ if (item?.type === "agentMessage") {
262
+ const itemId = readString(item, "id") ?? readString(params, "itemId") ?? "assistant";
263
+ const text = readTextString(item, "text");
264
+ if (text) {
265
+ rememberItem(itemId);
266
+ assistantTextByItem.set(itemId, text);
257
267
  }
258
- return;
259
268
  }
260
- if (notification.method === "turn/completed") {
261
- const turn = isJsonObject(params.turn) ? params.turn : void 0;
262
- if (readString(turn, "status") === "failed") failedError = readString(readRecord(turn?.error), "message") ?? "codex app-server turn failed";
263
- const items = Array.isArray(turn?.items) ? turn.items : [];
264
- for (const item of items) {
265
- if (!isJsonObject(item) || item.type !== "agentMessage") continue;
266
- const itemId = readString(item, "id") ?? `assistant-${assistantOrder.length + 1}`;
267
- const text = readTextString(item, "text");
268
- if (text) {
269
- rememberItem(itemId);
270
- assistantTextByItem.set(itemId, text);
271
- }
269
+ return;
270
+ }
271
+ if (notification.method === "turn/completed") {
272
+ const turn = isJsonObject(params.turn) ? params.turn : void 0;
273
+ if (readString(turn, "status") === "failed") failedError = readString(readRecord(turn?.error), "message") ?? "codex app-server turn failed";
274
+ const items = Array.isArray(turn?.items) ? turn.items : [];
275
+ for (const item of items) {
276
+ if (!isJsonObject(item) || item.type !== "agentMessage") continue;
277
+ const itemId = readString(item, "id") ?? `assistant-${assistantOrder.length + 1}`;
278
+ const text = readTextString(item, "text");
279
+ if (text) {
280
+ rememberItem(itemId);
281
+ assistantTextByItem.set(itemId, text);
272
282
  }
273
- finish();
274
283
  }
284
+ finish();
285
+ }
286
+ };
287
+ return {
288
+ setTurnId(nextTurnId) {
289
+ turnId = nextTurnId;
290
+ const pending = pendingNotificationsByTurnId.get(nextTurnId) ?? [];
291
+ pendingNotificationsByTurnId.clear();
292
+ for (const notification of pending) handleNotification(notification);
275
293
  },
294
+ handleNotification,
276
295
  wait(params) {
277
296
  if (completed) return failedError ? Promise.reject(new Error(failedError)) : Promise.resolve({ replyText: collectReplyText() });
278
297
  return new Promise((resolve, reject) => {
@@ -295,6 +314,9 @@ function isNotificationForTurn(params, threadId, turnId) {
295
314
  if (directTurnId) return directTurnId === turnId;
296
315
  return readString(isJsonObject(params.turn) ? params.turn : void 0, "id") === turnId;
297
316
  }
317
+ function readNotificationTurnId(params) {
318
+ return readString(params, "turnId") ?? readString(readRecord(params.turn), "id");
319
+ }
298
320
  function readRecord(value) {
299
321
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
300
322
  }
@@ -339,10 +361,14 @@ function extractInboundMedia(event) {
339
361
  }
340
362
  function toCodexImageInput(media) {
341
363
  if (!isImageMedia(media)) return;
342
- if (media.path) return {
343
- type: "localImage",
344
- path: normalizeFileUrl(media.path)
345
- };
364
+ const localPath = media.path ?? readLocalMediaPath(media.url);
365
+ if (localPath) {
366
+ const normalized = normalizeFileUrl(localPath);
367
+ return normalized ? {
368
+ type: "localImage",
369
+ path: normalized
370
+ } : void 0;
371
+ }
346
372
  return media.url ? {
347
373
  type: "image",
348
374
  url: media.url
@@ -355,7 +381,19 @@ function isImageMedia(media) {
355
381
  return IMAGE_EXTENSIONS.has(path.extname(candidate.split(/[?#]/, 1)[0] ?? "").toLowerCase());
356
382
  }
357
383
  function normalizeFileUrl(value) {
358
- return value.startsWith("file://") ? new URL(value).pathname : value;
384
+ if (!value.startsWith("file://")) return value;
385
+ try {
386
+ return fileURLToPath(value);
387
+ } catch {
388
+ return;
389
+ }
390
+ }
391
+ function readLocalMediaPath(value) {
392
+ if (!value) return;
393
+ if (value.startsWith("file://")) return value;
394
+ if (value.startsWith("//")) return;
395
+ if (path.isAbsolute(value) || path.win32.isAbsolute(value)) return value;
396
+ return /^[a-z][a-z0-9+.-]*:/i.test(value) ? void 0 : value;
359
397
  }
360
398
  function readStringArray(value) {
361
399
  if (typeof value === "string" && value.trim()) return [value.trim()];
@@ -386,6 +424,9 @@ async function startCodexConversationThread(params) {
386
424
  model: params.model,
387
425
  modelProvider: params.modelProvider,
388
426
  authProfileId,
427
+ approvalPolicy: params.approvalPolicy,
428
+ sandbox: params.sandbox,
429
+ serviceTier: params.serviceTier,
389
430
  config: params.config
390
431
  });
391
432
  else await createThread({
@@ -395,6 +436,9 @@ async function startCodexConversationThread(params) {
395
436
  model: params.model,
396
437
  modelProvider: params.modelProvider,
397
438
  authProfileId,
439
+ approvalPolicy: params.approvalPolicy,
440
+ sandbox: params.sandbox,
441
+ serviceTier: params.serviceTier,
398
442
  config: params.config
399
443
  });
400
444
  return createCodexConversationBindingData({
@@ -406,12 +450,12 @@ async function handleCodexConversationInboundClaim(event, ctx, options = {}) {
406
450
  const data = readCodexConversationBindingData(ctx.pluginBinding);
407
451
  if (!data) return;
408
452
  if (event.commandAuthorized !== true) return { handled: true };
409
- const prompt = (event.bodyForAgent ?? event.content ?? "").trim();
453
+ const prompt = event.bodyForAgent?.trim() || event.content?.trim() || "";
410
454
  if (!prompt) return { handled: true };
411
455
  try {
412
456
  return {
413
457
  handled: true,
414
- reply: (await enqueueBoundTurn(data.sessionFile, () => runBoundTurn({
458
+ reply: (await enqueueBoundTurn(data.sessionFile, () => runBoundTurnWithMissingThreadRecovery({
415
459
  data,
416
460
  prompt,
417
461
  event,
@@ -422,7 +466,7 @@ async function handleCodexConversationInboundClaim(event, ctx, options = {}) {
422
466
  } catch (error) {
423
467
  return {
424
468
  handled: true,
425
- reply: { text: `Codex app-server turn failed: ${formatErrorMessage(error)}` }
469
+ reply: { text: `Codex app-server turn failed: ${formatCodexDisplayText(formatErrorMessage(error))}` }
426
470
  };
427
471
  }
428
472
  }
@@ -447,10 +491,10 @@ async function attachExistingThread(params) {
447
491
  threadId: params.threadId,
448
492
  ...params.model ? { model: params.model } : {},
449
493
  ...modelProvider ? { modelProvider } : {},
450
- approvalPolicy: runtime.approvalPolicy,
494
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
451
495
  approvalsReviewer: runtime.approvalsReviewer,
452
- sandbox: runtime.sandbox,
453
- ...runtime.serviceTier ? { serviceTier: runtime.serviceTier } : {},
496
+ sandbox: params.sandbox ?? runtime.sandbox,
497
+ ...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
454
498
  persistExtendedHistory: true
455
499
  }, { timeoutMs: runtime.requestTimeoutMs });
456
500
  const thread = response.thread;
@@ -464,9 +508,9 @@ async function attachExistingThread(params) {
464
508
  authProfileId: params.authProfileId,
465
509
  modelProvider: response.modelProvider ?? params.modelProvider
466
510
  }),
467
- approvalPolicy: runtime.approvalPolicy,
468
- sandbox: runtime.sandbox,
469
- serviceTier: runtime.serviceTier
511
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
512
+ sandbox: params.sandbox ?? runtime.sandbox,
513
+ serviceTier: params.serviceTier ?? runtime.serviceTier
470
514
  }, { config: params.config });
471
515
  }
472
516
  async function createThread(params) {
@@ -484,10 +528,10 @@ async function createThread(params) {
484
528
  cwd: params.workspaceDir,
485
529
  ...params.model ? { model: params.model } : {},
486
530
  ...modelProvider ? { modelProvider } : {},
487
- approvalPolicy: runtime.approvalPolicy,
531
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
488
532
  approvalsReviewer: runtime.approvalsReviewer,
489
- sandbox: runtime.sandbox,
490
- ...runtime.serviceTier ? { serviceTier: runtime.serviceTier } : {},
533
+ sandbox: params.sandbox ?? runtime.sandbox,
534
+ ...params.serviceTier ?? runtime.serviceTier ? { serviceTier: params.serviceTier ?? runtime.serviceTier } : {},
491
535
  developerInstructions: "This Codex thread is bound to an OpenClaw conversation. Answer normally; OpenClaw will deliver your final response back to the conversation.",
492
536
  experimentalRawEvents: true,
493
537
  persistExtendedHistory: true
@@ -502,9 +546,9 @@ async function createThread(params) {
502
546
  authProfileId: params.authProfileId,
503
547
  modelProvider: response.modelProvider ?? params.modelProvider
504
548
  }),
505
- approvalPolicy: runtime.approvalPolicy,
506
- sandbox: runtime.sandbox,
507
- serviceTier: runtime.serviceTier
549
+ approvalPolicy: params.approvalPolicy ?? runtime.approvalPolicy,
550
+ sandbox: params.sandbox ?? runtime.sandbox,
551
+ serviceTier: params.serviceTier ?? runtime.serviceTier
508
552
  }, { config: params.config });
509
553
  }
510
554
  async function runBoundTurn(params) {
@@ -566,6 +610,29 @@ async function runBoundTurn(params) {
566
610
  requestCleanup();
567
611
  }
568
612
  }
613
+ async function runBoundTurnWithMissingThreadRecovery(params) {
614
+ try {
615
+ return await runBoundTurn(params);
616
+ } catch (error) {
617
+ if (!isCodexThreadNotFoundError(error)) throw error;
618
+ const binding = await readCodexAppServerBinding(params.data.sessionFile);
619
+ await startCodexConversationThread({
620
+ pluginConfig: params.pluginConfig,
621
+ sessionFile: params.data.sessionFile,
622
+ workspaceDir: binding?.cwd || params.data.workspaceDir,
623
+ model: binding?.model,
624
+ modelProvider: binding?.modelProvider,
625
+ authProfileId: binding?.authProfileId,
626
+ approvalPolicy: binding?.approvalPolicy,
627
+ sandbox: binding?.sandbox,
628
+ serviceTier: binding?.serviceTier
629
+ });
630
+ return await runBoundTurn(params);
631
+ }
632
+ }
633
+ function isCodexThreadNotFoundError(error) {
634
+ return /\bthread not found:/iu.test(formatErrorMessage(error));
635
+ }
569
636
  function enqueueBoundTurn(key, run) {
570
637
  const state = getGlobalState();
571
638
  const next = (state.queues.get(key) ?? Promise.resolve()).then(run, run);
@@ -0,0 +1,19 @@
1
+ //#region extensions/codex/doctor-contract-api.ts
2
+ const sessionRouteStateOwners = [{
3
+ id: "codex",
4
+ label: "Codex",
5
+ providerIds: [
6
+ "codex",
7
+ "codex-cli",
8
+ "openai-codex"
9
+ ],
10
+ runtimeIds: ["codex", "codex-cli"],
11
+ cliSessionKeys: ["codex-cli"],
12
+ authProfilePrefixes: [
13
+ "codex:",
14
+ "codex-cli:",
15
+ "openai-codex:"
16
+ ]
17
+ }];
18
+ //#endregion
19
+ export { sessionRouteStateOwners };
package/dist/harness.js CHANGED
@@ -18,11 +18,11 @@ function createCodexAppServerAgentHarness(options) {
18
18
  };
19
19
  },
20
20
  runAttempt: async (params) => {
21
- const { runCodexAppServerAttempt } = await import("./run-attempt-DdEBxgoj.js");
21
+ const { runCodexAppServerAttempt } = await import("./run-attempt-CFL1BFBl.js");
22
22
  return runCodexAppServerAttempt(params, { pluginConfig: options?.pluginConfig });
23
23
  },
24
24
  compact: async (params) => {
25
- const { maybeCompactCodexAppServerSession } = await import("./compact-CCKeE8zJ.js");
25
+ const { maybeCompactCodexAppServerSession } = await import("./compact-DcR5aTxd.js");
26
26
  return maybeCompactCodexAppServerSession(params, { pluginConfig: options?.pluginConfig });
27
27
  },
28
28
  reset: async (params) => {
@@ -32,7 +32,7 @@ function createCodexAppServerAgentHarness(options) {
32
32
  }
33
33
  },
34
34
  dispose: async () => {
35
- const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-B7LbV3PF.js").then((n) => n.r);
35
+ const { clearSharedCodexAppServerClientAndWait } = await import("./shared-client-Dfk3Enm-.js").then((n) => n.r);
36
36
  await clearSharedCodexAppServerClientAndWait();
37
37
  }
38
38
  };
package/dist/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { createCodexAppServerAgentHarness } from "./harness.js";
2
2
  import { buildCodexMediaUnderstandingProvider } from "./media-understanding-provider.js";
3
3
  import { buildCodexProvider } from "./provider.js";
4
- import { n as handleCodexConversationInboundClaim, t as handleCodexConversationBindingResolved } from "./conversation-binding-LXwwffvV.js";
4
+ import { f as describeControlFailure, r as formatCodexDisplayText } from "./command-formatters-PiJcdUbu.js";
5
+ import { n as handleCodexConversationInboundClaim, t as handleCodexConversationBindingResolved } from "./conversation-binding-CtHkMJfG.js";
5
6
  import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
6
7
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
7
8
  import fs from "node:fs/promises";
@@ -23,8 +24,12 @@ function createCodexCommand(options) {
23
24
  };
24
25
  }
25
26
  async function handleCodexCommand(ctx, options = {}) {
26
- const { handleCodexSubcommand } = await import("./command-handlers-DsknA9lT.js");
27
- return await handleCodexSubcommand(ctx, options);
27
+ const { handleCodexSubcommand } = await import("./command-handlers-DiH-D13x.js");
28
+ try {
29
+ return await handleCodexSubcommand(ctx, options);
30
+ } catch (error) {
31
+ return { text: `Codex command failed: ${formatCodexDisplayText(describeControlFailure(error))}` };
32
+ }
28
33
  }
29
34
  //#endregion
30
35
  //#region extensions/codex/src/migration/helpers.ts
@@ -1,7 +1,7 @@
1
1
  import { CODEX_PROVIDER_ID, FALLBACK_CODEX_MODELS } from "./provider-catalog.js";
2
- import { i as resolveCodexAppServerRuntimeOptions } from "./config-CkkoMeqF.js";
3
- import { a as readCodexErrorNotification, c as readCodexTurnCompletedNotification, n as assertCodexThreadStartResponse, r as assertCodexTurnStartResponse } from "./protocol-validators-Cpopom3_.js";
4
- import { i as readModelListResult } from "./models-B-1qT9nX.js";
2
+ import { i as resolveCodexAppServerRuntimeOptions } from "./config-ByrA30No.js";
3
+ import { a as readCodexErrorNotification, c as readCodexTurnCompletedNotification, n as assertCodexThreadStartResponse, r as assertCodexTurnStartResponse } from "./protocol-validators-Dky2yV4W.js";
4
+ import { i as readModelListResult } from "./models-CkowdYbm.js";
5
5
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
6
6
  //#region extensions/codex/media-understanding-provider.ts
7
7
  const DEFAULT_CODEX_IMAGE_MODEL = FALLBACK_CODEX_MODELS.find((model) => model.inputModalities.includes("image"))?.id ?? FALLBACK_CODEX_MODELS[0]?.id;
@@ -37,7 +37,7 @@ async function describeCodexImages(req, options) {
37
37
  const appServer = resolveCodexAppServerRuntimeOptions({ pluginConfig: options.pluginConfig });
38
38
  const timeoutMs = Math.max(100, req.timeoutMs);
39
39
  const ownsClient = !options.clientFactory;
40
- const client = options.clientFactory ? await options.clientFactory(appServer.start, req.profile) : await import("./shared-client-B7LbV3PF.js").then((n) => n.r).then(({ createIsolatedCodexAppServerClient }) => createIsolatedCodexAppServerClient({
40
+ const client = options.clientFactory ? await options.clientFactory(appServer.start, req.profile) : await import("./shared-client-Dfk3Enm-.js").then((n) => n.r).then(({ createIsolatedCodexAppServerClient }) => createIsolatedCodexAppServerClient({
41
41
  startOptions: appServer.start,
42
42
  timeoutMs,
43
43
  authProfileId: req.profile
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
- import { o as readCodexModelListResponse } from "./protocol-validators-Cpopom3_.js";
2
+ import { o as readCodexModelListResponse } from "./protocol-validators-Dky2yV4W.js";
3
3
  //#region extensions/codex/src/app-server/models.ts
4
4
  var models_exports = /* @__PURE__ */ __exportAll({
5
5
  listAllCodexAppServerModels: () => listAllCodexAppServerModels,
@@ -39,7 +39,7 @@ async function listAllCodexAppServerModels(options = {}) {
39
39
  async function withCodexAppServerModelClient(options, run) {
40
40
  const timeoutMs = options.timeoutMs ?? 2500;
41
41
  const useSharedClient = options.sharedClient !== false;
42
- const { createIsolatedCodexAppServerClient, getSharedCodexAppServerClient } = await import("./shared-client-B7LbV3PF.js").then((n) => n.r);
42
+ const { createIsolatedCodexAppServerClient, getSharedCodexAppServerClient } = await import("./shared-client-Dfk3Enm-.js").then((n) => n.r);
43
43
  const client = useSharedClient ? await getSharedCodexAppServerClient({
44
44
  startOptions: options.startOptions,
45
45
  timeoutMs,