@openclaw/codex 2026.5.7 → 2026.5.10-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.
- package/dist/{client-BGbqC7jk.js → client-CTzy3Y6M.js} +7 -7
- package/dist/{client-factory--cll1Fba.js → client-factory-BJL_efz4.js} +1 -1
- package/dist/{command-handlers-DiH-D13x.js → command-handlers-BJ0GpaPa.js} +7 -7
- package/dist/{compact-DcR5aTxd.js → compact-4FVe6NwI.js} +3 -3
- package/dist/config-CT01BBDc.js +496 -0
- package/dist/{conversation-binding-CtHkMJfG.js → conversation-binding-FqeYliIk.js} +10 -8
- package/dist/doctor-contract-api.js +35 -1
- package/dist/harness.js +4 -4
- package/dist/index.js +406 -33
- package/dist/media-understanding-provider.js +4 -4
- package/dist/{models-CkowdYbm.js → models-OtCFiaj_.js} +2 -2
- package/dist/plugin-activation-CweAZa7r.js +389 -0
- package/dist/{protocol-validators-Dky2yV4W.js → protocol-validators-CbqWfY5M.js} +147 -14
- package/dist/provider.js +2 -2
- package/dist/{rate-limit-cache-t6ebYmfS.js → rate-limit-cache-DbZvmrAD.js} +3 -17
- package/dist/{command-formatters-PiJcdUbu.js → request-DC1Dz3iZ.js} +16 -2
- package/dist/{run-attempt-BaT_FcTv.js → run-attempt-DsO-3wqp.js} +542 -136
- package/dist/{session-binding-DuJYTJQy.js → session-binding-B44KIZM2.js} +44 -5
- package/dist/{shared-client-Dfk3Enm-.js → shared-client-p-TvEiNL.js} +77 -22
- package/dist/test-api.js +8 -5
- package/dist/{thread-lifecycle-CzllX4PU.js → thread-lifecycle-DtD_qoMW.js} +329 -18
- package/openclaw.plugin.json +75 -9
- package/package.json +7 -8
- package/dist/config-ByrA30No.js +0 -242
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as assertCodexThreadStartResponse, t as assertCodexThreadResumeResponse } from "./protocol-validators-
|
|
1
|
+
import { l as resolveCodexPluginsPolicy, r as codexSandboxPolicyForTurn } from "./config-CT01BBDc.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-
|
|
7
|
-
import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-
|
|
6
|
+
import { i as isCodexAppServerConnectionClosedError } from "./client-CTzy3Y6M.js";
|
|
7
|
+
import { i as readCodexAppServerBinding, n as isCodexAppServerNativeAuthProfile, o as writeCodexAppServerBinding, t as clearCodexAppServerBinding } from "./session-binding-B44KIZM2.js";
|
|
8
|
+
import { i as defaultCodexAppInventoryCache, n as readCodexPluginInventory, t as ensureCodexPluginActivation } from "./plugin-activation-CweAZa7r.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
|
-
const
|
|
12
|
+
const CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES = [
|
|
11
13
|
"read",
|
|
12
14
|
"write",
|
|
13
15
|
"edit",
|
|
@@ -16,17 +18,27 @@ const CODEX_NATIVE_FIRST_DYNAMIC_TOOL_EXCLUDES = [
|
|
|
16
18
|
"process",
|
|
17
19
|
"update_plan"
|
|
18
20
|
];
|
|
19
|
-
|
|
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
|
+
}
|
|
29
|
+
function filterCodexDynamicTools(tools, config) {
|
|
20
30
|
const excludes = /* @__PURE__ */ new Set();
|
|
21
|
-
|
|
31
|
+
for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) excludes.add(name);
|
|
22
32
|
for (const name of config.codexDynamicToolsExclude ?? []) {
|
|
23
|
-
const trimmed = name
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
|
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 [
|
|
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,
|
|
849
|
+
export { buildTurnStartParams as a, buildCodexPluginThreadConfig as c, createCodexDynamicToolBridge as d, filterCodexDynamicTools 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 };
|
package/openclaw.plugin.json
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
|
-
"
|
|
18
|
+
"providerCatalogEntry": "./provider-discovery.ts",
|
|
19
19
|
"syntheticAuthRefs": ["codex"],
|
|
20
20
|
"nonSecretAuthMarkers": ["codex-app-server"],
|
|
21
21
|
"activation": {
|
|
@@ -33,10 +33,10 @@
|
|
|
33
33
|
"type": "object",
|
|
34
34
|
"additionalProperties": false,
|
|
35
35
|
"properties": {
|
|
36
|
-
"
|
|
36
|
+
"codexDynamicToolsLoading": {
|
|
37
37
|
"type": "string",
|
|
38
|
-
"enum": ["
|
|
39
|
-
"default": "
|
|
38
|
+
"enum": ["searchable", "direct"],
|
|
39
|
+
"default": "searchable"
|
|
40
40
|
},
|
|
41
41
|
"codexDynamicToolsExclude": {
|
|
42
42
|
"type": "array",
|
|
@@ -91,6 +91,42 @@
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
|
+
"codexPlugins": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"additionalProperties": false,
|
|
97
|
+
"properties": {
|
|
98
|
+
"enabled": {
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"default": false
|
|
101
|
+
},
|
|
102
|
+
"allow_destructive_actions": {
|
|
103
|
+
"type": "boolean",
|
|
104
|
+
"default": false
|
|
105
|
+
},
|
|
106
|
+
"plugins": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"additionalProperties": {
|
|
109
|
+
"type": "object",
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"properties": {
|
|
112
|
+
"enabled": {
|
|
113
|
+
"type": "boolean"
|
|
114
|
+
},
|
|
115
|
+
"marketplaceName": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"enum": ["openai-curated"]
|
|
118
|
+
},
|
|
119
|
+
"pluginName": {
|
|
120
|
+
"type": "string"
|
|
121
|
+
},
|
|
122
|
+
"allow_destructive_actions": {
|
|
123
|
+
"type": "boolean"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
},
|
|
94
130
|
"appServer": {
|
|
95
131
|
"type": "object",
|
|
96
132
|
"additionalProperties": false,
|
|
@@ -130,6 +166,11 @@
|
|
|
130
166
|
"minimum": 1,
|
|
131
167
|
"default": 60000
|
|
132
168
|
},
|
|
169
|
+
"turnCompletionIdleTimeoutMs": {
|
|
170
|
+
"type": "number",
|
|
171
|
+
"minimum": 1,
|
|
172
|
+
"default": 60000
|
|
173
|
+
},
|
|
133
174
|
"approvalPolicy": {
|
|
134
175
|
"type": "string",
|
|
135
176
|
"enum": ["never", "on-request", "on-failure", "untrusted"]
|
|
@@ -142,7 +183,7 @@
|
|
|
142
183
|
"type": "string",
|
|
143
184
|
"enum": ["user", "auto_review", "guardian_subagent"]
|
|
144
185
|
},
|
|
145
|
-
"serviceTier": { "type": ["string", "null"]
|
|
186
|
+
"serviceTier": { "type": ["string", "null"] },
|
|
146
187
|
"defaultWorkspaceDir": {
|
|
147
188
|
"type": "string"
|
|
148
189
|
}
|
|
@@ -151,9 +192,9 @@
|
|
|
151
192
|
}
|
|
152
193
|
},
|
|
153
194
|
"uiHints": {
|
|
154
|
-
"
|
|
155
|
-
"label": "Dynamic Tools
|
|
156
|
-
"help": "
|
|
195
|
+
"codexDynamicToolsLoading": {
|
|
196
|
+
"label": "Dynamic Tools Loading",
|
|
197
|
+
"help": "Use searchable to defer OpenClaw dynamic tools behind Codex tool search, or direct to expose them in the initial context.",
|
|
157
198
|
"advanced": true
|
|
158
199
|
},
|
|
159
200
|
"codexDynamicToolsExclude": {
|
|
@@ -219,6 +260,26 @@
|
|
|
219
260
|
"help": "MCP server name exposed by the Computer Use plugin.",
|
|
220
261
|
"advanced": true
|
|
221
262
|
},
|
|
263
|
+
"codexPlugins": {
|
|
264
|
+
"label": "Native Codex Plugins",
|
|
265
|
+
"help": "Controls native Codex plugin availability for Codex harness turns.",
|
|
266
|
+
"advanced": true
|
|
267
|
+
},
|
|
268
|
+
"codexPlugins.enabled": {
|
|
269
|
+
"label": "Enable Native Plugins",
|
|
270
|
+
"help": "Expose explicit migrated Codex plugin entries to Codex harness turns.",
|
|
271
|
+
"advanced": true
|
|
272
|
+
},
|
|
273
|
+
"codexPlugins.allow_destructive_actions": {
|
|
274
|
+
"label": "Allow Destructive Plugin Actions",
|
|
275
|
+
"help": "Default policy for plugin app write or destructive action elicitations. Defaults to false.",
|
|
276
|
+
"advanced": true
|
|
277
|
+
},
|
|
278
|
+
"codexPlugins.plugins": {
|
|
279
|
+
"label": "Migrated Plugin Entries",
|
|
280
|
+
"help": "Explicit migration-authored plugin entries. The wildcard key * is not supported.",
|
|
281
|
+
"advanced": true
|
|
282
|
+
},
|
|
222
283
|
"appServer": {
|
|
223
284
|
"label": "App Server",
|
|
224
285
|
"help": "Runtime controls for connecting to Codex app-server.",
|
|
@@ -270,6 +331,11 @@
|
|
|
270
331
|
"help": "Maximum time to wait for Codex app-server control-plane requests.",
|
|
271
332
|
"advanced": true
|
|
272
333
|
},
|
|
334
|
+
"appServer.turnCompletionIdleTimeoutMs": {
|
|
335
|
+
"label": "Turn Completion Idle Timeout",
|
|
336
|
+
"help": "Maximum quiet time after a turn-scoped Codex app-server request before OpenClaw interrupts the turn while waiting for turn/completed.",
|
|
337
|
+
"advanced": true
|
|
338
|
+
},
|
|
273
339
|
"appServer.approvalPolicy": {
|
|
274
340
|
"label": "Approval Policy",
|
|
275
341
|
"help": "Codex native approval policy sent to thread start, resume, and turns.",
|
|
@@ -287,7 +353,7 @@
|
|
|
287
353
|
},
|
|
288
354
|
"appServer.serviceTier": {
|
|
289
355
|
"label": "Service Tier",
|
|
290
|
-
"help": "Optional Codex app-server service tier. Use
|
|
356
|
+
"help": "Optional Codex app-server service tier. Use priority, flex, or null. Legacy fast is accepted as priority.",
|
|
291
357
|
"advanced": true
|
|
292
358
|
},
|
|
293
359
|
"appServer.defaultWorkspaceDir": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/codex",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.10-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.
|
|
12
|
-
"@openai/codex": "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.
|
|
29
|
+
"pluginApi": ">=2026.5.10-beta.1"
|
|
31
30
|
},
|
|
32
31
|
"build": {
|
|
33
|
-
"openclawVersion": "2026.5.
|
|
32
|
+
"openclawVersion": "2026.5.10-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.
|
|
47
|
+
"openclaw": ">=2026.5.10-beta.1"
|
|
49
48
|
},
|
|
50
49
|
"peerDependenciesMeta": {
|
|
51
50
|
"openclaw": {
|