@openclaw/codex 2026.5.7-beta.1 → 2026.5.9-beta.1

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,12 @@
1
- import { n as codexSandboxPolicyForTurn } from "./config-ByrA30No.js";
2
- import { n as assertCodexThreadStartResponse, t as assertCodexThreadResumeResponse } from "./protocol-validators-Dky2yV4W.js";
1
+ import { l as resolveCodexPluginsPolicy, r as codexSandboxPolicyForTurn } from "./config-BZiM7rhH.js";
2
+ import { n as assertCodexThreadStartResponse, t as assertCodexThreadResumeResponse } from "./protocol-validators-CbqWfY5M.js";
3
3
  import { t as isJsonObject } from "./protocol-C9UWI98H.js";
4
4
  import { CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY, renderCodexPromptOverlay } from "./prompt-overlay.js";
5
5
  import { isModernCodexModel } from "./provider.js";
6
- import { i as isCodexAppServerConnectionClosedError } from "./client-BGbqC7jk.js";
7
- import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-DuJYTJQy.js";
6
+ import { i as isCodexAppServerConnectionClosedError } from "./client-DYkCXAze.js";
7
+ import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-C_HDlZx8.js";
8
+ import { i as defaultCodexAppInventoryCache, n as readCodexPluginInventory, t as ensureCodexPluginActivation } from "./plugin-activation-D0uXssSN.js";
9
+ import crypto from "node:crypto";
8
10
  import { HEARTBEAT_RESPONSE_TOOL_NAME, createAgentToolResultMiddlewareRunner, createCodexAppServerToolResultExtensionRunner, embeddedAgentLog, extractToolResultMediaArtifact, filterToolResultMediaUrls, isMessagingTool, isMessagingToolSendAction, isToolWrappedWithBeforeToolCallHook, normalizeHeartbeatToolResponse, runAgentHarnessAfterToolCallHook, wrapToolWithBeforeToolCallHook } from "openclaw/plugin-sdk/agent-harness-runtime";
9
11
  //#region extensions/codex/src/app-server/dynamic-tool-profile.ts
10
12
  const CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES = [
@@ -16,17 +18,27 @@ const CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES = [
16
18
  "process",
17
19
  "update_plan"
18
20
  ];
21
+ const DYNAMIC_TOOL_NAME_ALIASES = {
22
+ bash: "exec",
23
+ "apply-patch": "apply_patch"
24
+ };
25
+ function normalizeCodexDynamicToolName(name) {
26
+ const normalized = name.trim().toLowerCase();
27
+ return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
28
+ }
19
29
  function applyCodexDynamicToolProfile(tools, config) {
20
30
  const excludes = /* @__PURE__ */ new Set();
21
31
  if ((config.codexDynamicToolsProfile ?? "native-first") === "native-first") for (const name of CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES) excludes.add(name);
22
32
  for (const name of config.codexDynamicToolsExclude ?? []) {
23
- const trimmed = name.trim();
33
+ const trimmed = normalizeCodexDynamicToolName(name);
24
34
  if (trimmed) excludes.add(trimmed);
25
35
  }
26
- return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(tool.name));
36
+ return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(normalizeCodexDynamicToolName(tool.name)));
27
37
  }
28
38
  //#endregion
29
39
  //#region extensions/codex/src/app-server/dynamic-tools.ts
40
+ const CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE = "openclaw";
41
+ const ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES = new Set(["sessions_yield"]);
30
42
  function createCodexDynamicToolBridge(params) {
31
43
  const toolResultHookContext = toToolResultHookContext(params.hookContext);
32
44
  const tools = params.tools.map((tool) => isToolWrappedWithBeforeToolCallHook(tool) ? tool : wrapToolWithBeforeToolCallHook(tool, params.hookContext));
@@ -44,11 +56,12 @@ function createCodexDynamicToolBridge(params) {
44
56
  ...toolResultHookContext
45
57
  });
46
58
  const legacyExtensionRunner = createCodexAppServerToolResultExtensionRunner(toolResultHookContext);
59
+ const directToolNames = new Set([...ALWAYS_DIRECT_DYNAMIC_TOOL_NAMES, ...params.directToolNames ?? []]);
47
60
  return {
48
- specs: tools.map((tool) => ({
49
- name: tool.name,
50
- description: tool.description,
51
- inputSchema: toJsonValue(tool.parameters)
61
+ specs: tools.map((tool) => createCodexDynamicToolSpec({
62
+ tool,
63
+ loading: params.loading ?? "searchable",
64
+ directToolNames
52
65
  })),
53
66
  telemetry,
54
67
  handleToolCall: async (call, options) => {
@@ -138,6 +151,19 @@ function createCodexDynamicToolBridge(params) {
138
151
  }
139
152
  };
140
153
  }
154
+ function createCodexDynamicToolSpec(params) {
155
+ const base = {
156
+ name: params.tool.name,
157
+ description: params.tool.description,
158
+ inputSchema: toJsonValue(params.tool.parameters)
159
+ };
160
+ if (params.loading === "direct" || params.directToolNames.has(params.tool.name)) return base;
161
+ return {
162
+ ...base,
163
+ namespace: CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE,
164
+ deferLoading: true
165
+ };
166
+ }
141
167
  function toToolResultHookContext(ctx) {
142
168
  const { agentId, sessionId, sessionKey, runId } = ctx ?? {};
143
169
  return {
@@ -271,15 +297,274 @@ function isCronAddAction(args) {
271
297
  return typeof action === "string" && action.trim().toLowerCase() === "add";
272
298
  }
273
299
  //#endregion
300
+ //#region extensions/codex/src/app-server/plugin-thread-config.ts
301
+ const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION = 1;
302
+ const CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION = 1;
303
+ function shouldBuildCodexPluginThreadConfig(pluginConfig) {
304
+ return resolveCodexPluginsPolicy(pluginConfig).configured;
305
+ }
306
+ function buildCodexPluginThreadConfigInputFingerprint(params) {
307
+ return fingerprintJson({
308
+ version: CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION,
309
+ policy: policyFingerprint(resolveCodexPluginsPolicy(params.pluginConfig)),
310
+ appCacheKey: params.appCacheKey ?? null
311
+ });
312
+ }
313
+ async function buildCodexPluginThreadConfig(params) {
314
+ const appCache = params.appCache ?? defaultCodexAppInventoryCache;
315
+ let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
316
+ pluginConfig: params.pluginConfig,
317
+ appCacheKey: params.appCacheKey
318
+ });
319
+ const policy = resolveCodexPluginsPolicy(params.pluginConfig);
320
+ if (!policy.enabled) return emptyPluginThreadConfig({
321
+ enabled: false,
322
+ inputFingerprint,
323
+ configPatch: buildDisabledAppsConfigPatch()
324
+ });
325
+ let inventory = await readCodexPluginInventory({
326
+ pluginConfig: params.pluginConfig,
327
+ policy,
328
+ request: params.request,
329
+ appCache,
330
+ appCacheKey: params.appCacheKey,
331
+ nowMs: params.nowMs
332
+ });
333
+ if (shouldWaitForInitialAppInventory(params, policy, inventory)) {
334
+ await refreshAppInventoryNow(params, appCache);
335
+ inventory = await readCodexPluginInventory({
336
+ pluginConfig: params.pluginConfig,
337
+ policy,
338
+ request: params.request,
339
+ appCache,
340
+ appCacheKey: params.appCacheKey,
341
+ nowMs: params.nowMs
342
+ });
343
+ inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
344
+ pluginConfig: params.pluginConfig,
345
+ appCacheKey: params.appCacheKey
346
+ });
347
+ }
348
+ const activationDiagnostics = [];
349
+ const activationResults = [];
350
+ for (const record of inventory.records) {
351
+ if (!record.activationRequired) continue;
352
+ const activation = await ensureCodexPluginActivation({
353
+ identity: record.policy,
354
+ request: params.request,
355
+ appCache,
356
+ appCacheKey: params.appCacheKey
357
+ });
358
+ activationResults.push(activation);
359
+ if (!activation.ok) activationDiagnostics.push({
360
+ code: "plugin_activation_failed",
361
+ plugin: record.policy,
362
+ message: activation.diagnostics.map((item) => item.message).join(" ") || activation.reason
363
+ });
364
+ }
365
+ if (activationResults.some((activation) => activation.ok && activation.installAttempted)) {
366
+ await refreshAppInventoryNow(params, appCache, { forceRefetch: true });
367
+ inventory = await readCodexPluginInventory({
368
+ pluginConfig: params.pluginConfig,
369
+ policy,
370
+ request: params.request,
371
+ appCache,
372
+ appCacheKey: params.appCacheKey,
373
+ nowMs: params.nowMs
374
+ });
375
+ inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
376
+ pluginConfig: params.pluginConfig,
377
+ appCacheKey: params.appCacheKey
378
+ });
379
+ }
380
+ const diagnostics = [...inventory.diagnostics, ...activationDiagnostics];
381
+ const apps = { _default: {
382
+ enabled: false,
383
+ destructive_enabled: false,
384
+ open_world_enabled: false
385
+ } };
386
+ const policyApps = {};
387
+ const pluginAppIds = {};
388
+ for (const record of inventory.records) {
389
+ if (record.activationRequired) {
390
+ if (!activationResults.find((item) => item.identity.configKey === record.policy.configKey)?.ok) continue;
391
+ }
392
+ if (record.appOwnership !== "proven") continue;
393
+ pluginAppIds[record.policy.configKey] = [...record.ownedAppIds].toSorted();
394
+ for (const app of record.apps) {
395
+ if (!app.accessible || !app.enabled) {
396
+ diagnostics.push({
397
+ code: "app_not_ready",
398
+ plugin: record.policy,
399
+ message: `${app.id} is not accessible or enabled for ${record.policy.pluginName}.`
400
+ });
401
+ continue;
402
+ }
403
+ const appConfig = {
404
+ enabled: true,
405
+ destructive_enabled: record.policy.allowDestructiveActions,
406
+ open_world_enabled: true,
407
+ default_tools_approval_mode: "prompt"
408
+ };
409
+ apps[app.id] = appConfig;
410
+ policyApps[app.id] = {
411
+ configKey: record.policy.configKey,
412
+ marketplaceName: record.policy.marketplaceName,
413
+ pluginName: record.policy.pluginName,
414
+ allowDestructiveActions: record.policy.allowDestructiveActions,
415
+ mcpServerNames: [...record.detail?.mcpServers ?? []].toSorted()
416
+ };
417
+ }
418
+ }
419
+ const configPatch = { apps };
420
+ const policyContext = buildPluginAppPolicyContext(policyApps, pluginAppIds);
421
+ return {
422
+ enabled: true,
423
+ configPatch,
424
+ fingerprint: fingerprintJson({
425
+ version: CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION,
426
+ inputFingerprint,
427
+ configPatch,
428
+ policyContext
429
+ }),
430
+ inputFingerprint,
431
+ policyContext,
432
+ inventory,
433
+ diagnostics
434
+ };
435
+ }
436
+ function mergeCodexThreadConfigs(...configs) {
437
+ let merged;
438
+ for (const config of configs) {
439
+ if (!config) continue;
440
+ merged = mergeJsonObjects(merged ?? {}, config);
441
+ }
442
+ return merged && Object.keys(merged).length > 0 ? merged : void 0;
443
+ }
444
+ function isCodexPluginThreadBindingStale(params) {
445
+ if (!params.codexPluginsEnabled) return Boolean(params.bindingFingerprint || params.bindingInputFingerprint || params.hasBindingPolicyContext);
446
+ if (!params.bindingFingerprint || !params.bindingInputFingerprint || !params.hasBindingPolicyContext) return true;
447
+ return params.bindingInputFingerprint !== params.currentInputFingerprint;
448
+ }
449
+ function emptyPluginThreadConfig(params) {
450
+ const policyContext = buildPluginAppPolicyContext({}, {});
451
+ return {
452
+ enabled: params.enabled,
453
+ fingerprint: fingerprintJson({
454
+ version: CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION,
455
+ inputFingerprint: params.inputFingerprint,
456
+ configPatch: params.configPatch ?? null,
457
+ policyContext
458
+ }),
459
+ inputFingerprint: params.inputFingerprint,
460
+ ...params.configPatch ? { configPatch: params.configPatch } : {},
461
+ policyContext,
462
+ diagnostics: []
463
+ };
464
+ }
465
+ function buildDisabledAppsConfigPatch() {
466
+ return { apps: { _default: {
467
+ enabled: false,
468
+ destructive_enabled: false,
469
+ open_world_enabled: false
470
+ } } };
471
+ }
472
+ function buildPluginAppPolicyContext(apps, pluginAppIds) {
473
+ return {
474
+ fingerprint: fingerprintJson({
475
+ version: 1,
476
+ apps,
477
+ pluginAppIds
478
+ }),
479
+ apps,
480
+ pluginAppIds
481
+ };
482
+ }
483
+ function shouldWaitForInitialAppInventory(params, policy, inventory) {
484
+ return Boolean(params.appCacheKey && policy.pluginPolicies.some((plugin) => plugin.enabled) && inventory.appInventory?.state === "missing");
485
+ }
486
+ async function refreshAppInventoryNow(params, appCache, options = {}) {
487
+ const appCacheKey = params.appCacheKey;
488
+ if (!appCacheKey) return;
489
+ const request = async (method, requestParams) => await params.request(method, requestParams);
490
+ try {
491
+ await appCache.refreshNow({
492
+ key: appCacheKey,
493
+ request,
494
+ nowMs: params.nowMs,
495
+ forceRefetch: options.forceRefetch
496
+ });
497
+ } catch {}
498
+ }
499
+ function policyFingerprint(policy) {
500
+ return {
501
+ enabled: policy.enabled,
502
+ allowDestructiveActions: policy.allowDestructiveActions,
503
+ plugins: policy.pluginPolicies.map((plugin) => ({
504
+ configKey: plugin.configKey,
505
+ marketplaceName: plugin.marketplaceName,
506
+ pluginName: plugin.pluginName,
507
+ enabled: plugin.enabled,
508
+ allowDestructiveActions: plugin.allowDestructiveActions
509
+ }))
510
+ };
511
+ }
512
+ function mergeJsonObjects(left, right) {
513
+ const merged = { ...left };
514
+ for (const [key, value] of Object.entries(right)) {
515
+ const existing = merged[key];
516
+ merged[key] = isPlainJsonObject(existing) && isPlainJsonObject(value) ? mergeJsonObjects(existing, value) : value;
517
+ }
518
+ return merged;
519
+ }
520
+ function isPlainJsonObject(value) {
521
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
522
+ }
523
+ function fingerprintJson(value) {
524
+ return crypto.createHash("sha256").update(stableStringify(value)).digest("hex");
525
+ }
526
+ function stableStringify(value) {
527
+ if (Array.isArray(value)) return `[${value.map((item) => stableStringify(item)).join(",")}]`;
528
+ if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
529
+ return JSON.stringify(value);
530
+ }
531
+ //#endregion
274
532
  //#region extensions/codex/src/app-server/thread-lifecycle.ts
275
533
  async function startOrResumeThread(params) {
276
534
  const dynamicToolsFingerprint = fingerprintDynamicTools(params.dynamicTools);
277
- const binding = await readCodexAppServerBinding(params.params.sessionFile, {
535
+ let binding = await readCodexAppServerBinding(params.params.sessionFile, {
278
536
  authProfileStore: params.params.authProfileStore,
279
537
  agentDir: params.params.agentDir,
280
538
  config: params.params.config
281
539
  });
282
540
  let preserveExistingBinding = false;
541
+ let prebuiltPluginThreadConfig;
542
+ if (binding?.threadId) {
543
+ let pluginBindingStale = isCodexPluginThreadBindingStale({
544
+ codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
545
+ bindingFingerprint: binding.pluginAppsFingerprint,
546
+ bindingInputFingerprint: binding.pluginAppsInputFingerprint,
547
+ currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
548
+ hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
549
+ });
550
+ if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
551
+ binding,
552
+ pluginThreadConfig: params.pluginThreadConfig
553
+ })) try {
554
+ prebuiltPluginThreadConfig = await params.pluginThreadConfig?.build();
555
+ pluginBindingStale = prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
556
+ } catch (error) {
557
+ embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
558
+ error,
559
+ threadId: binding.threadId
560
+ });
561
+ }
562
+ if (pluginBindingStale) {
563
+ embeddedAgentLog.debug("codex app-server plugin app config changed; starting a new thread", { threadId: binding.threadId });
564
+ await clearCodexAppServerBinding(params.params.sessionFile);
565
+ binding = void 0;
566
+ }
567
+ }
283
568
  if (binding?.threadId) if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint)) {
284
569
  preserveExistingBinding = shouldStartTransientNoToolThread({
285
570
  previous: binding.dynamicToolsFingerprint,
@@ -314,6 +599,9 @@ async function startOrResumeThread(params) {
314
599
  model: params.params.modelId,
315
600
  modelProvider: response.modelProvider ?? fallbackModelProvider,
316
601
  dynamicToolsFingerprint,
602
+ pluginAppsFingerprint: binding.pluginAppsFingerprint,
603
+ pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
604
+ pluginAppPolicyContext: binding.pluginAppPolicyContext,
317
605
  createdAt: binding.createdAt
318
606
  }, {
319
607
  authProfileStore: params.params.authProfileStore,
@@ -327,19 +615,24 @@ async function startOrResumeThread(params) {
327
615
  authProfileId: boundAuthProfileId,
328
616
  model: params.params.modelId,
329
617
  modelProvider: response.modelProvider ?? fallbackModelProvider,
330
- dynamicToolsFingerprint
618
+ dynamicToolsFingerprint,
619
+ pluginAppsFingerprint: binding.pluginAppsFingerprint,
620
+ pluginAppsInputFingerprint: binding.pluginAppsInputFingerprint,
621
+ pluginAppPolicyContext: binding.pluginAppPolicyContext
331
622
  };
332
623
  } catch (error) {
333
624
  if (isCodexAppServerConnectionClosedError(error)) throw error;
334
625
  embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
335
626
  await clearCodexAppServerBinding(params.params.sessionFile);
336
627
  }
628
+ const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await params.pluginThreadConfig.build() : void 0;
629
+ const config = mergeCodexThreadConfigs(params.config, pluginThreadConfig?.configPatch);
337
630
  const response = assertCodexThreadStartResponse(await params.client.request("thread/start", buildThreadStartParams(params.params, {
338
631
  cwd: params.cwd,
339
632
  dynamicTools: params.dynamicTools,
340
633
  appServer: params.appServer,
341
634
  developerInstructions: params.developerInstructions,
342
- config: params.config
635
+ config
343
636
  })));
344
637
  const modelProvider = resolveCodexAppServerModelProvider({
345
638
  provider: params.params.provider,
@@ -356,6 +649,9 @@ async function startOrResumeThread(params) {
356
649
  model: response.model ?? params.params.modelId,
357
650
  modelProvider: response.modelProvider ?? modelProvider,
358
651
  dynamicToolsFingerprint,
652
+ pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
653
+ pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
654
+ pluginAppPolicyContext: pluginThreadConfig?.policyContext,
359
655
  createdAt
360
656
  }, {
361
657
  authProfileStore: params.params.authProfileStore,
@@ -371,10 +667,21 @@ async function startOrResumeThread(params) {
371
667
  model: response.model ?? params.params.modelId,
372
668
  modelProvider: response.modelProvider ?? modelProvider,
373
669
  dynamicToolsFingerprint,
670
+ pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
671
+ pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
672
+ pluginAppPolicyContext: pluginThreadConfig?.policyContext,
374
673
  createdAt,
375
674
  updatedAt: createdAt
376
675
  };
377
676
  }
677
+ function shouldRecheckRecoverablePluginBinding(params) {
678
+ if (!params.pluginThreadConfig?.enabled) return false;
679
+ if (!params.binding.pluginAppsFingerprint || !params.binding.pluginAppsInputFingerprint || params.binding.pluginAppsInputFingerprint !== params.pluginThreadConfig.inputFingerprint) return false;
680
+ const policyContext = params.binding.pluginAppPolicyContext;
681
+ if (!policyContext) return false;
682
+ const expectedPluginConfigKeys = params.pluginThreadConfig.enabledPluginConfigKeys ?? [];
683
+ return Object.keys(policyContext.apps).length === 0 || expectedPluginConfigKeys.length > 0;
684
+ }
378
685
  function buildThreadStartParams(params, options) {
379
686
  const modelProvider = resolveCodexAppServerModelProvider({
380
687
  provider: params.provider,
@@ -445,7 +752,11 @@ function buildTurnCollaborationMode(params) {
445
752
  };
446
753
  }
447
754
  function buildHeartbeatCollaborationInstructions() {
448
- return ["This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.", CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY].join("\n\n");
755
+ return [
756
+ "This is an OpenClaw heartbeat turn. Apply these instructions only to this heartbeat wake; ordinary chat turns should stay in Codex Default mode.",
757
+ "When you are ready to end the heartbeat, prefer the structured `heartbeat_respond` tool so OpenClaw can record the wake outcome and notification decision. If `heartbeat_respond` is not already available and `tool_search` is available, search for `heartbeat_respond`, load it, then call it. Use `notify=false` when nothing should visibly interrupt the user.",
758
+ CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY
759
+ ].join("\n\n");
449
760
  }
450
761
  function codexDynamicToolsFingerprint(dynamicTools) {
451
762
  return fingerprintDynamicTools(dynamicTools);
@@ -535,4 +846,4 @@ function resolveReasoningEffort(thinkLevel, modelId) {
535
846
  return null;
536
847
  }
537
848
  //#endregion
538
- export { buildTurnStartParams as a, createCodexDynamicToolBridge as c, buildThreadStartParams as i, applyCodexDynamicToolProfile as l, buildDeveloperInstructions as n, codexDynamicToolsFingerprint as o, buildThreadResumeParams as r, startOrResumeThread as s, areCodexDynamicToolFingerprintsCompatible as t };
849
+ export { buildTurnStartParams as a, buildCodexPluginThreadConfig as c, createCodexDynamicToolBridge as d, applyCodexDynamicToolProfile as f, buildThreadStartParams as i, buildCodexPluginThreadConfigInputFingerprint as l, buildDeveloperInstructions as n, codexDynamicToolsFingerprint as o, normalizeCodexDynamicToolName as p, buildThreadResumeParams as r, startOrResumeThread as s, areCodexDynamicToolFingerprintsCompatible as t, shouldBuildCodexPluginThreadConfig as u };
@@ -15,7 +15,7 @@
15
15
  }
16
16
  }
17
17
  },
18
- "providerDiscoveryEntry": "./provider-discovery.ts",
18
+ "providerCatalogEntry": "./provider-discovery.ts",
19
19
  "syntheticAuthRefs": ["codex"],
20
20
  "nonSecretAuthMarkers": ["codex-app-server"],
21
21
  "activation": {
@@ -38,6 +38,11 @@
38
38
  "enum": ["native-first", "openclaw-compat"],
39
39
  "default": "native-first"
40
40
  },
41
+ "codexDynamicToolsLoading": {
42
+ "type": "string",
43
+ "enum": ["searchable", "direct"],
44
+ "default": "searchable"
45
+ },
41
46
  "codexDynamicToolsExclude": {
42
47
  "type": "array",
43
48
  "items": { "type": "string" },
@@ -91,6 +96,42 @@
91
96
  }
92
97
  }
93
98
  },
99
+ "codexPlugins": {
100
+ "type": "object",
101
+ "additionalProperties": false,
102
+ "properties": {
103
+ "enabled": {
104
+ "type": "boolean",
105
+ "default": false
106
+ },
107
+ "allow_destructive_actions": {
108
+ "type": "boolean",
109
+ "default": false
110
+ },
111
+ "plugins": {
112
+ "type": "object",
113
+ "additionalProperties": {
114
+ "type": "object",
115
+ "additionalProperties": false,
116
+ "properties": {
117
+ "enabled": {
118
+ "type": "boolean"
119
+ },
120
+ "marketplaceName": {
121
+ "type": "string",
122
+ "enum": ["openai-curated"]
123
+ },
124
+ "pluginName": {
125
+ "type": "string"
126
+ },
127
+ "allow_destructive_actions": {
128
+ "type": "boolean"
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ },
94
135
  "appServer": {
95
136
  "type": "object",
96
137
  "additionalProperties": false,
@@ -130,6 +171,11 @@
130
171
  "minimum": 1,
131
172
  "default": 60000
132
173
  },
174
+ "turnCompletionIdleTimeoutMs": {
175
+ "type": "number",
176
+ "minimum": 1,
177
+ "default": 60000
178
+ },
133
179
  "approvalPolicy": {
134
180
  "type": "string",
135
181
  "enum": ["never", "on-request", "on-failure", "untrusted"]
@@ -142,7 +188,7 @@
142
188
  "type": "string",
143
189
  "enum": ["user", "auto_review", "guardian_subagent"]
144
190
  },
145
- "serviceTier": { "type": ["string", "null"], "enum": ["fast", "flex", null] },
191
+ "serviceTier": { "type": ["string", "null"] },
146
192
  "defaultWorkspaceDir": {
147
193
  "type": "string"
148
194
  }
@@ -156,6 +202,11 @@
156
202
  "help": "Select which OpenClaw dynamic tools are exposed to Codex app-server. native-first omits tools Codex already owns.",
157
203
  "advanced": true
158
204
  },
205
+ "codexDynamicToolsLoading": {
206
+ "label": "Dynamic Tools Loading",
207
+ "help": "Use searchable to defer OpenClaw dynamic tools behind Codex tool search, or direct to expose them in the initial context.",
208
+ "advanced": true
209
+ },
159
210
  "codexDynamicToolsExclude": {
160
211
  "label": "Dynamic Tool Excludes",
161
212
  "help": "Additional OpenClaw dynamic tool names to omit from Codex app-server turns.",
@@ -219,6 +270,26 @@
219
270
  "help": "MCP server name exposed by the Computer Use plugin.",
220
271
  "advanced": true
221
272
  },
273
+ "codexPlugins": {
274
+ "label": "Native Codex Plugins",
275
+ "help": "Controls native Codex plugin availability for Codex harness turns.",
276
+ "advanced": true
277
+ },
278
+ "codexPlugins.enabled": {
279
+ "label": "Enable Native Plugins",
280
+ "help": "Expose explicit migrated Codex plugin entries to Codex harness turns.",
281
+ "advanced": true
282
+ },
283
+ "codexPlugins.allow_destructive_actions": {
284
+ "label": "Allow Destructive Plugin Actions",
285
+ "help": "Default policy for plugin app write or destructive action elicitations. Defaults to false.",
286
+ "advanced": true
287
+ },
288
+ "codexPlugins.plugins": {
289
+ "label": "Migrated Plugin Entries",
290
+ "help": "Explicit migration-authored plugin entries. The wildcard key * is not supported.",
291
+ "advanced": true
292
+ },
222
293
  "appServer": {
223
294
  "label": "App Server",
224
295
  "help": "Runtime controls for connecting to Codex app-server.",
@@ -270,6 +341,11 @@
270
341
  "help": "Maximum time to wait for Codex app-server control-plane requests.",
271
342
  "advanced": true
272
343
  },
344
+ "appServer.turnCompletionIdleTimeoutMs": {
345
+ "label": "Turn Completion Idle Timeout",
346
+ "help": "Maximum quiet time after a turn-scoped Codex app-server request before OpenClaw interrupts the turn while waiting for turn/completed.",
347
+ "advanced": true
348
+ },
273
349
  "appServer.approvalPolicy": {
274
350
  "label": "Approval Policy",
275
351
  "help": "Codex native approval policy sent to thread start, resume, and turns.",
@@ -287,7 +363,7 @@
287
363
  },
288
364
  "appServer.serviceTier": {
289
365
  "label": "Service Tier",
290
- "help": "Optional Codex app-server service tier. Use fast, flex, or null.",
366
+ "help": "Optional Codex app-server service tier. Use priority, flex, or null. Legacy fast is accepted as priority.",
291
367
  "advanced": true
292
368
  },
293
369
  "appServer.defaultWorkspaceDir": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/codex",
3
- "version": "2026.5.7-beta.1",
3
+ "version": "2026.5.9-beta.1",
4
4
  "description": "OpenClaw Codex harness and model provider plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,11 +8,10 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "@mariozechner/pi-coding-agent": "0.73.0",
12
- "@openai/codex": "0.128.0",
11
+ "@mariozechner/pi-coding-agent": "0.73.1",
12
+ "@openai/codex": "0.130.0",
13
13
  "ajv": "^8.20.0",
14
- "ws": "^8.20.0",
15
- "zod": "^4.4.3"
14
+ "ws": "^8.20.0"
16
15
  },
17
16
  "devDependencies": {
18
17
  "@openclaw/plugin-sdk": "workspace:*"
@@ -27,10 +26,10 @@
27
26
  "minHostVersion": ">=2026.5.1-beta.1"
28
27
  },
29
28
  "compat": {
30
- "pluginApi": ">=2026.5.7-beta.1"
29
+ "pluginApi": ">=2026.5.9-beta.1"
31
30
  },
32
31
  "build": {
33
- "openclawVersion": "2026.5.7-beta.1"
32
+ "openclawVersion": "2026.5.9-beta.1"
34
33
  },
35
34
  "release": {
36
35
  "publishToClawHub": true,
@@ -45,7 +44,7 @@
45
44
  "openclaw.plugin.json"
46
45
  ],
47
46
  "peerDependencies": {
48
- "openclaw": ">=2026.5.7-beta.1"
47
+ "openclaw": ">=2026.5.9-beta.1"
49
48
  },
50
49
  "peerDependenciesMeta": {
51
50
  "openclaw": {