@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-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.
Files changed (68) hide show
  1. package/README.md +2 -0
  2. package/dist/api.js +147 -0
  3. package/dist/app-server-policy-bg5QPSVP.js +25 -0
  4. package/dist/attempt-client-cleanup-URjUlCHg.js +735 -0
  5. package/dist/binding-connection-DAvZwp96.js +45 -0
  6. package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-4BjrZEwW.js} +63 -56
  7. package/dist/{capabilities-BFDTVYr4.js → capabilities-CH3K57v4.js} +8 -2
  8. package/dist/cli-metadata.js +1 -1
  9. package/dist/{command-formatters-C336eRVS.js → command-formatters-D0nZ3qtd.js} +18 -18
  10. package/dist/{command-handlers-DAHUSry9.js → command-handlers-DC86lgT4.js} +507 -101
  11. package/dist/command-rpc-xUIIonsO.js +90 -0
  12. package/dist/{compact-D_uvHurS.js → compact-COqgBRny.js} +234 -214
  13. package/dist/{session-binding-BXpo9ymA.js → config-CpNeo5AE.js} +284 -1163
  14. package/dist/context-compaction-activity-BOeJf7yo.js +58 -0
  15. package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
  16. package/dist/doctor-contract-api.js +48 -50
  17. package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-fJZhl4CE.js} +3442 -1433
  18. package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-4YKn51aG.js} +20 -15
  19. package/dist/elicitation-input-DMkeki8d.js +71 -0
  20. package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
  21. package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-CLwNEJqW.js} +113 -29
  22. package/dist/harness-DoKQcF8n.js +258 -0
  23. package/dist/harness.js +2 -160
  24. package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
  25. package/dist/index.js +730 -256
  26. package/dist/isolated-completion-DPMtlH9r.js +75 -0
  27. package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
  28. package/dist/managed-binary-By4FZC6i.js +212 -0
  29. package/dist/media-understanding-provider.js +5 -5
  30. package/dist/model-catalog-CmGoqVKB.js +48 -0
  31. package/dist/{models-BBMKU4jA.js → models-CZw1wClT.js} +12 -17
  32. package/dist/native-subagent-monitor-GGq3RnOo.js +3225 -0
  33. package/dist/node-exec-server.runtime-Bsu1LdFZ.js +192 -0
  34. package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-DMNQ8Zi8.js} +18 -6
  35. package/dist/plugin-approval-roundtrip-C75Yiiwc.js +95 -0
  36. package/dist/plugin-marketplace-discovery-CSWDkMrP.js +399 -0
  37. package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-BXH5NF-4.js} +109 -6
  38. package/dist/protocol-validators-VAVyEnDp.js +2399 -0
  39. package/dist/{rate-limits-CU-gVCCS.js → rate-limits-BI1dMHDG.js} +17 -35
  40. package/dist/{request-qKh331yu.js → request-DSK0W6YI.js} +3 -3
  41. package/dist/{run-attempt-CY9WlDLc.js → run-attempt-BV3HzuCf.js} +3416 -7194
  42. package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BvyT1uyd.js} +11 -29
  43. package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-C1qEeZ5C.js} +29 -30
  44. package/dist/session-binding-BTrLf9Qm.js +874 -0
  45. package/dist/{session-catalog-DyOgR4WH.js → session-catalog-ezP_UcRM.js} +1584 -1289
  46. package/dist/session-catalog-home-id-BNwAJ9D7.js +18 -0
  47. package/dist/{session-cli-2PZaFLRh.js → session-cli-B1hKvfkI.js} +20 -7
  48. package/dist/session-retirement-CE4iMiLh.js +36 -0
  49. package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-D_2npHlA.js} +1 -1
  50. package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-Dx1onIqg.js} +40 -28
  51. package/dist/{shared-client-DTbvH-XV.js → shared-client-Dw-rzii0.js} +1315 -469
  52. package/dist/{side-question-_ulBk1AL.js → side-question-DbXkJZzg.js} +140 -69
  53. package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-DKdrnTMX.js} +927 -768
  54. package/dist/thread-ownership-i2NBwDUL.js +92 -0
  55. package/dist/thread-resume-CdJk2Ehf.js +30 -0
  56. package/dist/transcript-mirror-BU0_LKAL.js +588 -0
  57. package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DhoGy33Z.js} +13 -2
  58. package/dist/upstream-prompt-provenance-CFmsKwr9.js +39 -0
  59. package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-BDVJCLAD.js} +14 -8
  60. package/dist/{usage-CQ6GhD5-.js → usage-9dKJCgcy.js} +3 -3
  61. package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DCdx4DGu.js} +8 -9
  62. package/openclaw.plugin.json +49 -4
  63. package/package.json +6 -5
  64. package/dist/app-server-policy-BSaO2JIT.js +0 -49
  65. package/dist/conversation-control-xsIeXbo4.js +0 -287
  66. package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
  67. package/dist/notification-correlation-CU6_AnOb.js +0 -67
  68. package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
@@ -1,223 +1,33 @@
1
- import { D as resolveCodexAppServerClientInstanceId, O as consumeCodexAppServerLiveThread, S as isCodexAppServerConnectionClosedError, T as isCodexAppServerPrewriteRequestCancellationError, U as flattenCodexDynamicToolFunctions, W as isJsonObject, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, p as retireSharedCodexAppServerClientIfCurrent, s as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent, v as CodexAppServerRpcError, y as getCodexAppServerClientInstanceId } from "./shared-client-DTbvH-XV.js";
2
- import { M as CODEX_PLUGINS_MARKETPLACE_NAME, N as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME, a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, k as resolveCodexPluginsPolicy, m as codexSandboxPolicyForTurn, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-BXpo9ymA.js";
3
- import { _ as assertCodexThreadStartResponse, a as projectBoundedCodexThreadHistory, g as assertCodexThreadResumeResponse, h as assertCodexThreadForkResponse } from "./transcript-mirror-DzsdNCf5.js";
4
- import { a as serializeCodexAppInventoryError, i as defaultCodexAppInventoryCache, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-DztPafOA.js";
1
+ import { _ as CODEX_PLUGINS_MARKETPLACE_NAME, g as resolveCodexPluginsPolicy, n as codexSandboxPolicyForTurn, v as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME } from "./config-CpNeo5AE.js";
2
+ import { B as CodexAppServerRpcError, M as isCodexAppServerConnectionClosedError, R as resolveCodexAppServerClientInstanceId, U as consumeCodexAppServerLiveThread, d as retainSharedCodexAppServerClientByInstanceId, k as getCodexAppServerClientInstanceId, lt as flattenCodexDynamicToolFunctions, pt as resolveCodexAppServerLocalHomeDir, s as isCodexAppServerStartSelectionChangedError, ut as isJsonObject } from "./shared-client-Dw-rzii0.js";
3
+ import { a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-BTrLf9Qm.js";
4
+ import { a as closeCodexStartupClientBestEffort, c as isCodexAppServerUnsafeSubscriptionError, d as unsubscribeCodexThreadBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-URjUlCHg.js";
5
+ import { a as assertCodexThreadStartResponse, r as assertCodexThreadForkResponse } from "./protocol-validators-VAVyEnDp.js";
6
+ import { o as defaultCodexAppInventoryCache, s as serializeCodexAppInventoryError, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-DMNQ8Zi8.js";
5
7
  import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
8
+ import { n as codexCatalogHomeId } from "./session-catalog-home-id-BNwAJ9D7.js";
9
+ import { t as resumeCodexAppServerThread } from "./thread-resume-CdJk2Ehf.js";
10
+ import { u as projectBoundedCodexThreadHistory } from "./transcript-mirror-BU0_LKAL.js";
11
+ import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
12
+ import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
13
  import fs from "node:fs/promises";
7
14
  import path from "node:path";
8
- import { SKILL_WORKSHOP_TOOL_NAME, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive } from "openclaw/plugin-sdk/agent-harness-runtime";
15
+ import { SKILL_WORKSHOP_TOOL_NAME, TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode } from "openclaw/plugin-sdk/agent-harness-runtime";
9
16
  import * as crypto$1 from "node:crypto";
10
- import crypto from "node:crypto";
17
+ import crypto, { createHash } from "node:crypto";
11
18
  import os from "node:os";
12
- import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
19
+ import { resolveAgentDir as resolveAgentDir$1, resolveSessionAgentIds as resolveSessionAgentIds$1 } from "openclaw/plugin-sdk/agent-runtime";
20
+ import { z } from "zod";
21
+ import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
13
22
  import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
14
23
  import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
24
+ import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
15
25
  import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
16
26
  import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
27
+ import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
17
28
  import { buildCodexUserMcpServersThreadConfigPatchForRuntime } from "openclaw/plugin-sdk/codex-mcp-projection";
18
29
  import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
19
30
  import { GPT5_HEARTBEAT_PROMPT_OVERLAY } from "openclaw/plugin-sdk/provider-model-shared";
20
- import { INLINE_IMAGE_DATA_URL_PREFIX, sanitizeInlineImageDataUrl } from "openclaw/plugin-sdk/inline-image-data-url-runtime";
21
- //#region extensions/codex/src/app-server/attempt-client-cleanup.ts
22
- /**
23
- * Best-effort cleanup helpers for Codex app-server startup attempts and turns.
24
- */
25
- /** Timeout for best-effort app-server turn interruption during cleanup. */
26
- const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
27
- /** Timeout for best-effort thread unsubscribe during cleanup. */
28
- const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
29
- /** Raised when a thread subscription may be live on a client OpenClaw no longer controls. */
30
- var CodexAppServerUnsafeSubscriptionError = class extends Error {
31
- constructor(message, options) {
32
- super(message, options);
33
- this.name = "CodexAppServerUnsafeSubscriptionError";
34
- }
35
- };
36
- function isCodexAppServerUnsafeSubscriptionError(error) {
37
- return error instanceof CodexAppServerUnsafeSubscriptionError;
38
- }
39
- /** Asserts Codex resumed the exact thread this attempt subscribed to. */
40
- function assertCodexThreadResumeSubscription(requestedThreadId, returnedThreadId) {
41
- if (returnedThreadId !== requestedThreadId) throw new CodexAppServerUnsafeSubscriptionError(`Codex thread/resume returned ${returnedThreadId} for ${requestedThreadId}`);
42
- }
43
- async function closeClientAndWaitIfAvailable(client) {
44
- const closeable = client;
45
- if (typeof closeable.closeAndWait === "function") {
46
- await closeable.closeAndWait();
47
- return;
48
- }
49
- closeable.close?.();
50
- }
51
- async function closeCodexStartupClientBestEffort(client) {
52
- if (!client) return;
53
- const unclaimedSharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client);
54
- if (unclaimedSharedClient.closed) {
55
- await closeClientAndWaitIfAvailable(client);
56
- return;
57
- }
58
- if (unclaimedSharedClient.found) {
59
- if (retireSharedCodexAppServerClientIfCurrent(client)?.closed) await closeClientAndWaitIfAvailable(client);
60
- return;
61
- }
62
- const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
63
- if (retiredSharedClient) {
64
- if (retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
65
- return;
66
- }
67
- if (clearSharedCodexAppServerClientIfCurrent(client)) {
68
- await closeClientAndWaitIfAvailable(client);
69
- return;
70
- }
71
- await closeClientAndWaitIfAvailable(client);
72
- }
73
- /** Sends a turn interrupt without blocking abort cleanup on app-server errors. */
74
- function interruptCodexTurnBestEffort(client, params) {
75
- interruptCodexTurnAndWaitBestEffort(client, params);
76
- }
77
- /** Sends a bounded turn interrupt and waits for Codex to confirm terminal abort handling. */
78
- async function interruptCodexTurnAndWaitBestEffort(client, params) {
79
- const requestOptions = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? { timeoutMs: params.timeoutMs } : void 0;
80
- const requestParams = {
81
- threadId: params.threadId,
82
- turnId: params.turnId
83
- };
84
- try {
85
- await (requestOptions ? client.request("turn/interrupt", requestParams, requestOptions) : client.request("turn/interrupt", requestParams));
86
- } catch (error) {
87
- embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
88
- }
89
- }
90
- /** Unsubscribes from a thread while swallowing cleanup-only failures. */
91
- async function unsubscribeCodexThreadBestEffort(client, params) {
92
- try {
93
- await client.request("thread/unsubscribe", { threadId: params.threadId }, { timeoutMs: params.timeoutMs });
94
- return true;
95
- } catch (error) {
96
- embeddedAgentLog.debug("codex app-server thread unsubscribe cleanup failed", {
97
- threadId: params.threadId,
98
- error
99
- });
100
- return false;
101
- }
102
- }
103
- /**
104
- * Retires the shared client after a timed-out turn so later runs do not reuse a
105
- * potentially wedged app-server connection.
106
- */
107
- async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
108
- const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client, { failActiveLeases: params.suspectPhysicalClient });
109
- const detachedSharedClient = Boolean(retiredSharedClient);
110
- if (!(params.suspectPhysicalClient && (retiredSharedClient?.closed ?? false))) {
111
- interruptCodexTurnBestEffort(client, {
112
- threadId: params.threadId,
113
- turnId: params.turnId,
114
- timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
115
- });
116
- await unsubscribeCodexThreadBestEffort(client, {
117
- threadId: params.threadId,
118
- timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
119
- });
120
- }
121
- let closedClient = retiredSharedClient?.closed ?? false;
122
- if (!detachedSharedClient) {
123
- const close = client.close;
124
- if (typeof close === "function") try {
125
- close.call(client);
126
- closedClient = true;
127
- } catch (error) {
128
- embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
129
- threadId: params.threadId,
130
- turnId: params.turnId,
131
- error
132
- });
133
- }
134
- }
135
- embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
136
- threadId: params.threadId,
137
- turnId: params.turnId,
138
- reason: params.reason,
139
- detachedSharedClient,
140
- closedClient,
141
- activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0
142
- });
143
- }
144
- //#endregion
145
- //#region extensions/codex/src/app-server/event-projector-values.ts
146
- function readString(record, key) {
147
- const value = record[key];
148
- return typeof value === "string" ? value : void 0;
149
- }
150
- function normalizeNonEmptyString(value) {
151
- if (typeof value !== "string") return;
152
- return value.trim() || void 0;
153
- }
154
- function readNonEmptyString(record, key) {
155
- return normalizeNonEmptyString(record[key]);
156
- }
157
- function readNonEmptyStringArray(record, key) {
158
- const value = record[key];
159
- if (!Array.isArray(value)) return [];
160
- const entries = [];
161
- for (const entry of value) {
162
- const normalized = normalizeNonEmptyString(entry);
163
- if (normalized) entries.push(normalized);
164
- }
165
- return entries;
166
- }
167
- function readNullableString(record, key) {
168
- const value = record[key];
169
- if (value === null) return null;
170
- return typeof value === "string" ? value : void 0;
171
- }
172
- function readNumber(record, key) {
173
- const value = record[key];
174
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
175
- }
176
- function readNonNegativeInteger(record, key) {
177
- const value = readNumber(record, key);
178
- return value !== void 0 && Number.isInteger(value) && value >= 0 ? value : void 0;
179
- }
180
- function readCodexErrorNotificationMessage(record) {
181
- const error = record.error;
182
- return isJsonObject(error) ? readString(error, "message") : void 0;
183
- }
184
- function readHookOutputEntries(value) {
185
- if (!Array.isArray(value)) return [];
186
- return value.flatMap((entry) => {
187
- if (!isJsonObject(entry)) return [];
188
- const text = readString(entry, "text");
189
- if (!text) return [];
190
- const kind = readString(entry, "kind");
191
- return [{
192
- ...kind ? { kind } : {},
193
- text
194
- }];
195
- });
196
- }
197
- function splitPlanText(text) {
198
- return text.split(/\r?\n/).map((line) => line.trim().replace(/^[-*]\s+/, "")).filter((line) => line.length > 0);
199
- }
200
- function extractRawAssistantText(item) {
201
- return (Array.isArray(item.content) ? item.content : []).flatMap((entry) => {
202
- if (!isJsonObject(entry)) return [];
203
- const type = readString(entry, "type");
204
- if (type !== "output_text" && type !== "text") return [];
205
- const value = readString(entry, "text");
206
- return value ? [value] : [];
207
- }).join("").trim() || void 0;
208
- }
209
- function readItemString(item, key) {
210
- const value = item[key];
211
- return typeof value === "string" ? value : void 0;
212
- }
213
- function readItem(value) {
214
- if (!isJsonObject(value)) return;
215
- const type = typeof value.type === "string" ? value.type : void 0;
216
- const id = typeof value.id === "string" ? value.id : void 0;
217
- if (!type || !id) return;
218
- return value;
219
- }
220
- //#endregion
221
31
  //#region extensions/codex/src/app-server/plugin-inventory.ts
222
32
  /**
223
33
  * Reads Codex plugin marketplace state and app inventory to decide which
@@ -241,7 +51,7 @@ async function readCodexPluginInventory(params) {
241
51
  ...params,
242
52
  policy
243
53
  });
244
- let curatedCatalog;
54
+ const pluginCatalogs = /* @__PURE__ */ new Map();
245
55
  const diagnostics = [];
246
56
  const records = [];
247
57
  if (appInventory?.state === "missing") diagnostics.push({
@@ -255,13 +65,19 @@ async function readCodexPluginInventory(params) {
255
65
  for (const pluginPolicy of policy.pluginPolicies) {
256
66
  if (!pluginPolicy.enabled && !policy.allowAllPlugins) continue;
257
67
  let listed = installedPlugins;
258
- let resolvedPlugin = pluginPolicy.marketplaceName === "workspace-directory" ? findWorkspaceMarketplacePlugin(listed, pluginPolicy.pluginName) : findOpenAiCuratedMarketplacePlugin(listed, pluginPolicy.pluginName);
259
- if (!resolvedPlugin && pluginPolicy.enabled && pluginPolicy.marketplaceName === "openai-curated") {
260
- curatedCatalog ??= listCodexPluginMetadata(params);
261
- listed = await curatedCatalog;
262
- resolvedPlugin = findOpenAiCuratedMarketplacePlugin(listed, pluginPolicy.pluginName);
68
+ let resolvedPlugin = findConfiguredMarketplacePlugin(listed, pluginPolicy);
69
+ if (!resolvedPlugin && pluginPolicy.enabled && pluginPolicy.marketplaceName !== "workspace-directory") {
70
+ const requestParams = buildPluginCatalogRequestParams(params, pluginPolicy.marketplaceName);
71
+ const catalogKey = JSON.stringify([requestParams, pluginMetadataCatalogScope(pluginPolicy.marketplaceName)]);
72
+ let catalog = pluginCatalogs.get(catalogKey);
73
+ if (!catalog) {
74
+ catalog = listCodexPluginMetadata(params, pluginPolicy.marketplaceName);
75
+ pluginCatalogs.set(catalogKey, catalog);
76
+ }
77
+ listed = await catalog;
78
+ resolvedPlugin = findConfiguredMarketplacePlugin(listed, pluginPolicy);
263
79
  }
264
- if (!(pluginPolicy.marketplaceName === "workspace-directory" ? listed.marketplaces.some((entry) => entry.name === "workspace-directory") : listed.marketplaces.some(isOpenAiCuratedMarketplace))) {
80
+ if (!listed.marketplaces.some((marketplace) => marketplaceMatchesConfiguredName(marketplace, pluginPolicy.marketplaceName))) {
265
81
  diagnostics.push({
266
82
  code: "marketplace_missing",
267
83
  plugin: pluginPolicy,
@@ -278,6 +94,15 @@ async function readCodexPluginInventory(params) {
278
94
  continue;
279
95
  }
280
96
  const { summary } = resolvedPlugin;
97
+ const unavailableByMarketplacePolicy = summary.availability === "DISABLED_BY_ADMIN" || summary.installPolicy === "NOT_AVAILABLE";
98
+ if (unavailableByMarketplacePolicy) {
99
+ diagnostics.push({
100
+ code: "plugin_disabled",
101
+ plugin: pluginPolicy,
102
+ message: `${pluginPolicy.pluginName} is unavailable in ${pluginPolicy.marketplaceName}.`
103
+ });
104
+ if (!summary.installed) continue;
105
+ }
281
106
  const detail = await readPluginDetail(params, marketplaceRef(resolvedPlugin.marketplace, pluginPolicy.marketplaceName), pluginPolicy, summary, diagnostics);
282
107
  const ownedAppIds = detail?.apps.map((app) => app.id).filter(Boolean).toSorted() ?? [];
283
108
  const appOwnership = resolveAppOwnership({
@@ -304,7 +129,7 @@ async function readCodexPluginInventory(params) {
304
129
  policy: pluginPolicy,
305
130
  summary,
306
131
  ...detail ? { detail } : {},
307
- activationRequired: pluginPolicy.enabled && (!summary.installed || !summary.enabled),
132
+ activationRequired: pluginPolicy.enabled && (unavailableByMarketplacePolicy || !summary.installed || !summary.enabled),
308
133
  authRequired: apps.some((app) => app.needsAuth || !app.accessible),
309
134
  appOwnership,
310
135
  ownedAppIds,
@@ -318,11 +143,14 @@ async function readCodexPluginInventory(params) {
318
143
  ...appInventory ? { appInventory } : {}
319
144
  };
320
145
  }
321
- /** Finds one plugin summary in the OpenAI curated marketplace response. */
322
- function findOpenAiCuratedPluginSummary(listed, pluginName) {
323
- const resolved = findOpenAiCuratedMarketplacePlugin(listed, pluginName);
146
+ /** Finds a configured plugin only in its authorized marketplace identity. */
147
+ function findCodexMarketplacePluginSummary(listed, marketplaceName, pluginName) {
148
+ const resolved = findConfiguredMarketplacePlugin(listed, {
149
+ marketplaceName,
150
+ pluginName
151
+ });
324
152
  return resolved ? {
325
- marketplace: marketplaceRef(resolved.marketplace, CODEX_PLUGINS_MARKETPLACE_NAME),
153
+ marketplace: marketplaceRef(resolved.marketplace, marketplaceName),
326
154
  summary: resolved.summary
327
155
  } : void 0;
328
156
  }
@@ -343,15 +171,16 @@ function resolveRecoverableCodexPluginConfigKeys(params) {
343
171
  configCwd: params.configCwd
344
172
  })).map((pluginPolicy) => pluginPolicy.configKey).toSorted();
345
173
  }
346
- async function listCodexPluginMetadata(params) {
347
- const requestParams = {};
174
+ async function listCodexPluginMetadata(params, marketplaceName) {
175
+ const requestParams = buildPluginCatalogRequestParams(params, marketplaceName);
348
176
  if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/list", requestParams);
349
177
  return (await params.metadataCache.load({
350
178
  appCacheKey: params.appCacheKey,
351
179
  queryKind: "curated-global",
352
180
  requestParams,
181
+ catalogScope: pluginMetadataCatalogScope(marketplaceName),
353
182
  request: async (method, listedParams) => await params.request(method, listedParams),
354
- cacheable: (response) => response.marketplaces.some((marketplace) => isOpenAiCuratedMarketplace(marketplace))
183
+ cacheable: (response) => response.marketplaces.some((marketplace) => marketplaceMatchesConfiguredName(marketplace, marketplaceName))
355
184
  })).response;
356
185
  }
357
186
  async function readInstalledCodexPluginMetadata(params) {
@@ -364,17 +193,26 @@ async function readInstalledCodexPluginMetadata(params) {
364
193
  request: async (method, installedParams) => await params.request(method, installedParams),
365
194
  cacheable: (response) => params.policy.pluginPolicies.every((pluginPolicy) => {
366
195
  if (!pluginPolicy.enabled && !params.policy.allowAllPlugins) return true;
367
- return pluginPolicy.marketplaceName === "workspace-directory" ? findWorkspaceMarketplacePlugin(response, pluginPolicy.pluginName) !== void 0 : findOpenAiCuratedMarketplacePlugin(response, pluginPolicy.pluginName) !== void 0;
196
+ return Boolean(findConfiguredMarketplacePlugin(response, pluginPolicy));
368
197
  })
369
198
  })).response;
370
199
  }
371
200
  function isSettledMissingPluginPolicy(params) {
372
201
  const queryKind = params.pluginPolicy.marketplaceName === "workspace-directory" ? "installed" : "curated-global";
373
- const requestParams = queryKind === "installed" && params.configCwd ? { cwds: [params.configCwd] } : {};
374
- const listed = params.metadataCache.read(params.appCacheKey, queryKind, requestParams)?.response;
202
+ const requestParams = queryKind === "installed" ? params.configCwd ? { cwds: [params.configCwd] } : {} : buildPluginCatalogRequestParams(params, params.pluginPolicy.marketplaceName);
203
+ const listed = params.metadataCache.read(params.appCacheKey, queryKind, requestParams, queryKind === "curated-global" ? pluginMetadataCatalogScope(params.pluginPolicy.marketplaceName) : void 0)?.response;
375
204
  if (!listed) return false;
376
- if (queryKind === "installed") return !findWorkspaceMarketplacePlugin(listed, params.pluginPolicy.pluginName);
377
- return !findOpenAiCuratedMarketplacePlugin(listed, params.pluginPolicy.pluginName);
205
+ return !findConfiguredMarketplacePlugin(listed, params.pluginPolicy);
206
+ }
207
+ function pluginMetadataCatalogScope(marketplaceName) {
208
+ return isOpenAiCuratedMarketplaceName(marketplaceName) ? void 0 : marketplaceName;
209
+ }
210
+ function buildPluginCatalogRequestParams(params, marketplaceName) {
211
+ const marketplaceKinds = marketplaceName === "created-by-me-remote" ? ["created-by-me-remote"] : marketplaceName.startsWith("workspace-shared-with-me") ? ["shared-with-me"] : void 0;
212
+ return {
213
+ ...params.configCwd ? { cwds: [params.configCwd] } : {},
214
+ ...marketplaceKinds ? { marketplaceKinds: [...marketplaceKinds] } : {}
215
+ };
378
216
  }
379
217
  function readCachedAppInventory(params) {
380
218
  if (!params.appCache || !params.appCacheKey) return;
@@ -442,18 +280,25 @@ function resolveOwnedApps(params) {
442
280
  }).toSorted((left, right) => left.id.localeCompare(right.id));
443
281
  }
444
282
  function findPluginSummary(marketplace, pluginName) {
445
- return marketplace.plugins.find((plugin) => plugin.name === pluginName || plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}` || pluginNameFromPluginId(plugin.id, marketplace.name) === pluginName);
283
+ const exact = marketplace.plugins.find((plugin) => plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`);
284
+ if (exact) return exact;
285
+ const matches = marketplace.plugins.filter((plugin) => plugin.name === pluginName || pluginNameFromPluginId(plugin.id, marketplace.name) === pluginName);
286
+ return matches.length === 1 ? matches[0] : void 0;
446
287
  }
447
- function findOpenAiCuratedMarketplacePlugin(listed, pluginName) {
288
+ function findConfiguredMarketplacePlugin(listed, plugin) {
289
+ if (plugin.marketplaceName === "workspace-directory") return findWorkspaceMarketplacePlugin(listed, plugin.pluginName);
448
290
  for (const marketplace of listed.marketplaces) {
449
- if (!isOpenAiCuratedMarketplace(marketplace)) continue;
450
- const summary = findPluginSummary(marketplace, pluginName);
291
+ if (!marketplaceMatchesConfiguredName(marketplace, plugin.marketplaceName)) continue;
292
+ const summary = findPluginSummary(marketplace, plugin.pluginName);
451
293
  if (summary) return {
452
294
  marketplace,
453
295
  summary
454
296
  };
455
297
  }
456
298
  }
299
+ function marketplaceMatchesConfiguredName(marketplace, configuredMarketplaceName) {
300
+ return isOpenAiCuratedMarketplaceName(configuredMarketplaceName) ? isOpenAiCuratedMarketplace(marketplace) : marketplace.name === configuredMarketplaceName;
301
+ }
457
302
  function findWorkspaceMarketplacePlugin(listed, pluginName) {
458
303
  const marketplace = listed.marketplaces.find((entry) => entry.name === CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME);
459
304
  const summary = marketplace?.plugins.find((plugin) => plugin.id === pluginName);
@@ -477,20 +322,33 @@ function marketplaceRef(marketplace, name) {
477
322
  }
478
323
  /** True for any supported OpenAI curated marketplace wire name, matching Codex's own curated predicate. */
479
324
  function isOpenAiCuratedMarketplace(marketplace) {
480
- return marketplace.name === "openai-curated" || marketplace.name === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME || marketplace.name === CODEX_PLUGINS_API_MARKETPLACE_NAME;
325
+ return isOpenAiCuratedMarketplaceName(marketplace.name);
326
+ }
327
+ /** True for all Codex wire aliases of the same OpenAI-curated catalog. */
328
+ function isOpenAiCuratedMarketplaceName(marketplaceName) {
329
+ return marketplaceName === "openai-curated" || marketplaceName === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME || marketplaceName === CODEX_PLUGINS_API_MARKETPLACE_NAME;
481
330
  }
482
331
  //#endregion
483
332
  //#region extensions/codex/src/app-server/plugin-activation.ts
484
- /** Activates a curated plugin or rejects a workspace plugin that is not already active. */
333
+ /**
334
+ * Activates legacy curated Codex plugins while requiring owner-managed
335
+ * installation for every other marketplace.
336
+ */
337
+ /** Activates legacy curated plugins without granting install authority to other marketplaces. */
485
338
  async function ensureCodexPluginActivation(params) {
486
339
  if (params.identity.marketplaceName === "workspace-directory") return activationFailure(params.identity, "disabled", { message: "workspace-directory plugins must be installed and enabled outside OpenClaw before use." });
340
+ if (!isOpenAiCuratedMarketplaceName(params.identity.marketplaceName)) {
341
+ const target = params.identity.pluginName.endsWith(`@${params.identity.marketplaceName}`) ? params.identity.pluginName : `${params.identity.pluginName}@${params.identity.marketplaceName}`;
342
+ return activationFailure(params.identity, "disabled", { message: `${params.identity.marketplaceName} plugins must be installed and enabled by an owner before use. Run /codex plugins install ${target}.` });
343
+ }
487
344
  const listed = await listCuratedCodexPluginMetadata(params);
488
- const resolved = findOpenAiCuratedPluginSummary(listed, params.identity.pluginName);
345
+ const resolved = findCodexMarketplacePluginSummary(listed, params.identity.marketplaceName, params.identity.pluginName);
489
346
  if (!resolved) {
490
- if (!listed.marketplaces.some(isOpenAiCuratedMarketplace)) return activationFailure(params.identity, "marketplace_missing", { message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.` });
347
+ if (!listed.marketplaces.some((marketplace) => isOpenAiCuratedMarketplace(marketplace))) return activationFailure(params.identity, "marketplace_missing", { message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.` });
491
348
  return activationFailure(params.identity, "plugin_missing", { message: `${params.identity.pluginName} was not found in ${CODEX_PLUGINS_MARKETPLACE_NAME}.` });
492
349
  }
493
350
  if (resolved.marketplace.remoteMarketplaceName && !resolved.summary.remotePluginId) return activationFailure(params.identity, "plugin_missing", { message: `${params.identity.pluginName} detail unavailable: Codex did not return a remote plugin id.` });
351
+ if (resolved.summary.availability === "DISABLED_BY_ADMIN" || resolved.summary.installPolicy === "NOT_AVAILABLE") return activationFailure(params.identity, "disabled", { message: `${params.identity.pluginName} was disabled or made unavailable by its marketplace administrator.` });
494
352
  if (resolved.summary.installed && resolved.summary.enabled && !params.installEvenIfActive) return {
495
353
  identity: params.identity,
496
354
  ok: true,
@@ -499,7 +357,21 @@ async function ensureCodexPluginActivation(params) {
499
357
  marketplace: resolved.marketplace,
500
358
  diagnostics: []
501
359
  };
502
- const installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, resolved.marketplace.remoteMarketplaceName && resolved.summary.remotePluginId ? resolved.summary.remotePluginId : params.identity.pluginName));
360
+ const remotePluginId = resolved.marketplace.remoteMarketplaceName ? resolved.summary.remotePluginId : void 0;
361
+ let installResponse;
362
+ try {
363
+ installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, remotePluginId ?? params.identity.pluginName));
364
+ } catch (error) {
365
+ if (!(error instanceof CodexAppServerRpcError) || error.code !== -32600 || !remotePluginId || error.message !== `remote plugin ${remotePluginId} is disabled by admin` && error.message !== `remote plugin ${remotePluginId} is not available for install`) throw error;
366
+ return {
367
+ identity: params.identity,
368
+ ok: false,
369
+ reason: "install_failed",
370
+ installAttempted: true,
371
+ marketplace: resolved.marketplace,
372
+ diagnostics: [{ message: `Codex plugin install failed: ${coerceErrorMessage(error)}` }]
373
+ };
374
+ }
503
375
  if (params.metadataCache && params.appCacheKey) params.metadataCache.invalidate(params.appCacheKey);
504
376
  const refreshDiagnostics = [];
505
377
  let refreshFailed = false;
@@ -508,6 +380,7 @@ async function ensureCodexPluginActivation(params) {
508
380
  request: params.request,
509
381
  appCache: params.appCache,
510
382
  appCacheKey: params.appCacheKey,
383
+ configCwd: params.configCwd,
511
384
  metadataCache: params.metadataCache,
512
385
  deferAppInventoryRefresh: params.deferAppInventoryRefresh,
513
386
  targetAppIds: params.targetAppIds
@@ -515,7 +388,7 @@ async function ensureCodexPluginActivation(params) {
515
388
  refreshDiagnostics.push(...refreshResult.diagnostics);
516
389
  } catch (error) {
517
390
  refreshFailed = true;
518
- refreshDiagnostics.push({ message: `Codex plugin runtime refresh failed after install: ${error instanceof Error ? error.message : String(error)}` });
391
+ refreshDiagnostics.push({ message: `Codex plugin runtime refresh failed after install: ${coerceErrorMessage(error)}` });
519
392
  }
520
393
  const authRequired = installResponse.appsNeedingAuth.length > 0;
521
394
  return {
@@ -533,13 +406,13 @@ async function refreshCodexPluginRuntimeState(params) {
533
406
  const diagnostics = [];
534
407
  await listCuratedCodexPluginMetadata(params, { forceRefetch: true });
535
408
  await params.request("skills/list", {
536
- cwds: [],
409
+ cwds: params.configCwd ? [params.configCwd] : [],
537
410
  forceReload: true
538
411
  });
539
412
  try {
540
- await params.request("hooks/list", { cwds: [] });
413
+ await params.request("hooks/list", { cwds: params.configCwd ? [params.configCwd] : [] });
541
414
  } catch (error) {
542
- diagnostics.push({ message: `Codex hooks refresh skipped: ${error instanceof Error ? error.message : String(error)}` });
415
+ diagnostics.push({ message: `Codex hooks refresh skipped: ${coerceErrorMessage(error)}` });
543
416
  }
544
417
  await params.request("config/mcpServer/reload", void 0);
545
418
  if (params.appCache && params.appCacheKey) {
@@ -554,13 +427,16 @@ async function refreshCodexPluginRuntimeState(params) {
554
427
  targetAppIds: params.targetAppIds
555
428
  });
556
429
  } catch (error) {
557
- diagnostics.push({ message: `Codex app inventory refresh skipped: ${error instanceof Error ? error.message : String(error)}` });
430
+ diagnostics.push({ message: `Codex app inventory refresh skipped: ${coerceErrorMessage(error)}` });
558
431
  }
559
432
  }
560
433
  return { diagnostics };
561
434
  }
562
435
  async function listCuratedCodexPluginMetadata(params, options = {}) {
563
- const requestParams = options.forceRefetch ? { forceRefetch: true } : {};
436
+ const requestParams = {
437
+ ...params.configCwd ? { cwds: [params.configCwd] } : {},
438
+ ...options.forceRefetch ? { forceRefetch: true } : {}
439
+ };
564
440
  if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/list", requestParams);
565
441
  return (await params.metadataCache.load({
566
442
  appCacheKey: params.appCacheKey,
@@ -792,6 +668,7 @@ async function buildCodexPluginThreadConfig(params) {
792
668
  request: params.request,
793
669
  appCache,
794
670
  appCacheKey: params.appCacheKey,
671
+ configCwd: params.configCwd,
795
672
  metadataCache: params.metadataCache,
796
673
  deferAppInventoryRefresh: true,
797
674
  targetAppIds: record.ownedAppIds
@@ -867,7 +744,12 @@ async function buildCodexPluginThreadConfig(params) {
867
744
  inventory,
868
745
  accountApps: accountAppsResult.apps
869
746
  });
870
- const unresolvedDisabledPluginOwnership = policy.allowAllPlugins ? policy.pluginPolicies.find((pluginPolicy) => !pluginPolicy.enabled && !inventory.records.some((record) => record.policy.configKey === pluginPolicy.configKey && record.detail)) : void 0;
747
+ const unresolvedDisabledPluginOwnership = policy.allowAllPlugins ? policy.pluginPolicies.find((pluginPolicy) => {
748
+ const record = inventory.records.find((candidate) => candidate.policy.configKey === pluginPolicy.configKey);
749
+ const disabledByMarketplacePolicy = record?.summary.availability === "DISABLED_BY_ADMIN" || record?.summary.installPolicy === "NOT_AVAILABLE";
750
+ const unresolvedPluginIdentity = !record && inventory.diagnostics.some((diagnostic) => diagnostic.plugin?.configKey === pluginPolicy.configKey && (diagnostic.code === "plugin_disabled" || diagnostic.code === "plugin_missing" || diagnostic.code === "marketplace_missing"));
751
+ return (!pluginPolicy.enabled || disabledByMarketplacePolicy || unresolvedPluginIdentity) && !record?.detail;
752
+ }) : void 0;
871
753
  if (unresolvedDisabledPluginOwnership) diagnostics.push({
872
754
  code: "account_app_ownership_unavailable",
873
755
  plugin: unresolvedDisabledPluginOwnership,
@@ -907,6 +789,7 @@ async function buildCodexPluginThreadConfig(params) {
907
789
  marketplaceName: record.policy.marketplaceName,
908
790
  pluginName: record.policy.pluginName,
909
791
  allowDestructiveActions: record.policy.allowDestructiveActions,
792
+ allowOpenWorld: true,
910
793
  destructiveApprovalMode: record.policy.destructiveApprovalMode,
911
794
  mcpServerNames: [...record.detail?.mcpServers ?? []].toSorted()
912
795
  };
@@ -938,6 +821,7 @@ async function buildCodexPluginThreadConfig(params) {
938
821
  source: "account",
939
822
  appName: app.name,
940
823
  allowDestructiveActions: policy.allowDestructiveActions,
824
+ allowOpenWorld: true,
941
825
  destructiveApprovalMode: policy.destructiveApprovalMode,
942
826
  mcpServerNames: []
943
827
  };
@@ -1017,7 +901,7 @@ function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
1017
901
  for (const [appId, policy] of Object.entries(policyContext.apps).toSorted(([left], [right]) => left.localeCompare(right))) apps[appId] = {
1018
902
  enabled: true,
1019
903
  destructive_enabled: policy.allowDestructiveActions,
1020
- open_world_enabled: true,
904
+ open_world_enabled: policy.allowOpenWorld !== false,
1021
905
  default_tools_approval_mode: "auto",
1022
906
  ...policy.destructiveApprovalMode === "ask" ? { approvals_reviewer: "user" } : {}
1023
907
  };
@@ -1110,13 +994,10 @@ function mergeJsonObjects(left, right) {
1110
994
  const merged = { ...left };
1111
995
  for (const [key, value] of Object.entries(right)) {
1112
996
  const existing = merged[key];
1113
- merged[key] = isPlainJsonObject(existing) && isPlainJsonObject(value) ? mergeJsonObjects(existing, value) : value;
997
+ merged[key] = isJsonObject(existing) && isJsonObject(value) ? mergeJsonObjects(existing, value) : value;
1114
998
  }
1115
999
  return merged;
1116
1000
  }
1117
- function isPlainJsonObject(value) {
1118
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
1119
- }
1120
1001
  function fingerprintJson(value) {
1121
1002
  return crypto.createHash("sha256").update(stableStringify(value)).digest("hex");
1122
1003
  }
@@ -1142,15 +1023,18 @@ const CODEX_TURN_START_TEXT_INPUT_MAX_CHARS = 1 << 20;
1142
1023
  const DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS = 2e4;
1143
1024
  const MIN_PROMPT_BUDGET_RATIO = .5;
1144
1025
  const MIN_PROMPT_BUDGET_TOKENS = 8e3;
1026
+ function neutralizeCodexExplicitMentionSigils(text) {
1027
+ return text.replace(/\$(?=[A-Za-z0-9_:-])/gu, "$").replace(/\[@(?=[A-Za-z0-9_:-]+\]\()/gu, "[@");
1028
+ }
1145
1029
  /** Projects assembled OpenClaw context-engine messages into Codex prompt inputs. */
1146
1030
  function projectContextEngineAssemblyForCodex(params) {
1147
1031
  const prompt = params.prompt.trim();
1148
1032
  const contextMessages = dropDuplicateTrailingPrompt(params.assembledMessages, prompt);
1149
1033
  const maxRenderedContextChars = normalizeRenderedContextMaxChars(params.maxRenderedContextChars);
1150
- const renderedContext = renderMessagesForCodexContext(contextMessages, {
1034
+ const renderedContext = neutralizeCodexExplicitMentionSigils(renderMessagesForCodexContext(contextMessages, {
1151
1035
  maxTextPartChars: resolveTextPartMaxChars(maxRenderedContextChars),
1152
1036
  toolPayloadMode: params.toolPayloadMode ?? "elide"
1153
- });
1037
+ }));
1154
1038
  const boundedContext = renderedContext ? truncateOlderContext(renderedContext, maxRenderedContextChars) : void 0;
1155
1039
  const promptPrefix = boundedContext ? [
1156
1040
  CONTEXT_HEADER,
@@ -1185,6 +1069,32 @@ function resolveCodexContextEngineProjectionMaxChars(params) {
1185
1069
  function resolveCodexContextEngineProjectionReserveTokens() {
1186
1070
  return DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS;
1187
1071
  }
1072
+ const CONTINUITY_PROJECTION_RESERVE_RATIO = .5;
1073
+ const CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN = 3;
1074
+ const CONTINUITY_MIN_CHARS_PER_TOKEN = .5;
1075
+ const CONTINUITY_MAX_CHARS_PER_TOKEN = CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
1076
+ const CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS = 5e4;
1077
+ /** Builds a calibration sample from a completed turn, or undefined if unusable. */
1078
+ function buildCodexContinuityCalibration(params) {
1079
+ if (!Number.isFinite(params.promptChars) || !Number.isFinite(params.inputTokens) || params.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || params.inputTokens <= 0) return;
1080
+ return {
1081
+ promptChars: Math.floor(params.promptChars),
1082
+ inputTokens: Math.floor(params.inputTokens)
1083
+ };
1084
+ }
1085
+ function resolveContinuityCharsPerToken(calibration) {
1086
+ if (!calibration || !Number.isFinite(calibration.promptChars) || !Number.isFinite(calibration.inputTokens) || calibration.promptChars < CONTINUITY_CALIBRATION_MIN_PROMPT_CHARS || calibration.inputTokens <= 0) return CONTINUITY_EMPIRICAL_CHARS_PER_TOKEN;
1087
+ return Math.min(CONTINUITY_MAX_CHARS_PER_TOKEN, Math.max(CONTINUITY_MIN_CHARS_PER_TOKEN, calibration.promptChars / calibration.inputTokens));
1088
+ }
1089
+ /** Resolves rendered context size for no-engine continuity projections. */
1090
+ function resolveCodexContinuityProjectionMaxChars(params) {
1091
+ const contextTokenBudget = typeof params.contextTokenBudget === "number" && Number.isFinite(params.contextTokenBudget) ? Math.floor(params.contextTokenBudget) : void 0;
1092
+ if (!contextTokenBudget || contextTokenBudget <= 0) return DEFAULT_RENDERED_CONTEXT_CHARS;
1093
+ return normalizeRenderedContextMaxChars(resolveProjectionPromptBudgetTokens({
1094
+ contextTokenBudget,
1095
+ reserveTokens: Math.max(DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS, Math.floor(contextTokenBudget * CONTINUITY_PROJECTION_RESERVE_RATIO))
1096
+ }) * resolveContinuityCharsPerToken(params.calibration));
1097
+ }
1188
1098
  /** Fits projected context prompts under Codex app-server turn/start text limits. */
1189
1099
  function fitCodexProjectedContextForTurnStart(params) {
1190
1100
  const maxChars = typeof params.maxChars === "number" && Number.isFinite(params.maxChars) ? Math.max(0, Math.floor(params.maxChars)) : CODEX_TURN_START_TEXT_INPUT_MAX_CHARS;
@@ -1414,18 +1324,171 @@ function areContextEngineProjectionBindingsCompatible(previous, next) {
1414
1324
  return previous?.schemaVersion === next.schemaVersion && previous.mode === next.mode && previous.epoch === next.epoch && previous.fingerprint === next.fingerprint;
1415
1325
  }
1416
1326
  function resolveContextEngineCitationsMode(config) {
1417
- const rootConfig = isUnknownRecord(config) ? config : void 0;
1418
- const citations = (isUnknownRecord(rootConfig?.memory) ? rootConfig.memory : void 0)?.citations;
1327
+ const rootConfig = isRecord(config) ? config : void 0;
1328
+ const citations = (isRecord(rootConfig?.memory) ? rootConfig.memory : void 0)?.citations;
1419
1329
  return isJsonConfigValue(citations) ? citations : void 0;
1420
1330
  }
1421
- function isUnknownRecord(value) {
1422
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
1423
- }
1424
1331
  function isJsonConfigValue(value) {
1425
1332
  if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1426
1333
  if (typeof value === "number") return Number.isFinite(value);
1427
1334
  if (Array.isArray(value)) return value.every(isJsonConfigValue);
1428
- return isUnknownRecord(value) && Object.values(value).every(isJsonConfigValue);
1335
+ return isRecord(value) && Object.values(value).every(isJsonConfigValue);
1336
+ }
1337
+ //#endregion
1338
+ //#region extensions/codex/src/app-server/dynamic-tool-profile.ts
1339
+ /** Tool names owned by Codex app-server and normally excluded from OpenClaw dynamic tools. */
1340
+ const CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES = [
1341
+ "read",
1342
+ "write",
1343
+ "edit",
1344
+ "apply_patch",
1345
+ "exec",
1346
+ "process",
1347
+ "update_plan",
1348
+ "tool_call",
1349
+ "tool_describe",
1350
+ "tool_search",
1351
+ "tool_search_code"
1352
+ ];
1353
+ const CODEX_NATIVE_GOAL_TOOL_EXCLUDES = [
1354
+ "get_goal",
1355
+ "create_goal",
1356
+ "update_goal"
1357
+ ];
1358
+ const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set([
1359
+ "read",
1360
+ "write",
1361
+ "edit",
1362
+ "apply_patch",
1363
+ ...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
1364
+ ]);
1365
+ const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
1366
+ const DYNAMIC_TOOL_NAME_ALIASES = {
1367
+ bash: "exec",
1368
+ "apply-patch": "apply_patch"
1369
+ };
1370
+ /** Normalizes OpenClaw/Codex tool names before filtering and allowlist checks. */
1371
+ function normalizeCodexDynamicToolName(name) {
1372
+ const normalized = name.trim().toLowerCase();
1373
+ return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
1374
+ }
1375
+ /** True only for the host-scoped OpenClaw run's exact tool contract. */
1376
+ function isSystemAgentOnlyCodexDynamicToolAllowlist(toolsAllow) {
1377
+ return toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "openclaw";
1378
+ }
1379
+ /** True when a private source reply may use the message delivery tool only. */
1380
+ function isMessageOnlyCodexSourceReply(params) {
1381
+ return params.sourceReplyDeliveryMode === "message_tool_only" && params.toolsAllow?.length === 1 && normalizeCodexDynamicToolName(params.toolsAllow[0] ?? "") === "message";
1382
+ }
1383
+ /** Returns true for private QA runs that force the Codex runtime profile. */
1384
+ function isForcedPrivateQaCodexRuntime(env = process.env) {
1385
+ return env.OPENCLAW_BUILD_PRIVATE_QA === "1" && env.OPENCLAW_QA_FORCE_RUNTIME?.trim().toLowerCase() === "codex";
1386
+ }
1387
+ /** Resolves whether dynamic tools load directly or through Codex tool search. */
1388
+ function resolveCodexDynamicToolsLoading(config, env = process.env) {
1389
+ return isForcedPrivateQaCodexRuntime(env) ? "direct" : config.codexDynamicToolsLoading ?? "searchable";
1390
+ }
1391
+ function normalizeCodexModelId(modelId) {
1392
+ const normalized = modelId?.trim().toLowerCase();
1393
+ if (!normalized) return "";
1394
+ return normalized.includes("/") ? normalized.split("/").at(-1) : normalized;
1395
+ }
1396
+ /** Returns true when model behavior requires direct dynamic-tool registration. */
1397
+ function shouldUseDirectCodexDynamicToolsForModel(modelId) {
1398
+ return shouldDisableCodexToolSearchForModel(modelId);
1399
+ }
1400
+ /** Returns true for models whose tool-search path is unsupported or inefficient. */
1401
+ function shouldDisableCodexToolSearchForModel(modelId) {
1402
+ return normalizeCodexModelId(modelId) === "gpt-5.4-nano";
1403
+ }
1404
+ /** Resolves dynamic-tool loading after applying model-specific restrictions. */
1405
+ function resolveCodexDynamicToolsLoadingForModel(config, modelId, env = process.env) {
1406
+ const loading = resolveCodexDynamicToolsLoading(config, env);
1407
+ return loading === "searchable" && shouldUseDirectCodexDynamicToolsForModel(modelId) ? "direct" : loading;
1408
+ }
1409
+ /** Resolves dynamic-tool loading for the app-server connection that will execute the turn. */
1410
+ function resolveCodexDynamicToolsLoadingForRuntime(config, modelId, options = {}, env = process.env) {
1411
+ const loading = resolveCodexDynamicToolsLoadingForModel(config, modelId, env);
1412
+ return loading === "searchable" && options.connectionClass === "remote" ? "direct" : loading;
1413
+ }
1414
+ /** Filters OpenClaw tools that Codex owns natively or config explicitly excludes. */
1415
+ function filterCodexDynamicTools(tools, config, env = process.env) {
1416
+ return filterCodexDynamicToolsWithOptions(tools, config, env, {
1417
+ preserveOpenClawReplacements: false,
1418
+ preserveOpenClawShell: false
1419
+ });
1420
+ }
1421
+ /** Keeps OpenClaw coding tools that replace a disabled Codex native surface. */
1422
+ function filterCodexDynamicToolsForDisabledNativeSurface(tools, config, options, env = process.env) {
1423
+ return filterCodexDynamicToolsWithOptions(tools, config, env, {
1424
+ preserveOpenClawReplacements: true,
1425
+ preserveOpenClawShell: options.preserveShell
1426
+ });
1427
+ }
1428
+ function filterCodexDynamicToolsWithOptions(tools, config, env, options) {
1429
+ const excludes = /* @__PURE__ */ new Set();
1430
+ if (!options.preserveOpenClawReplacements) for (const name of CODEX_NATIVE_GOAL_TOOL_EXCLUDES) excludes.add(name);
1431
+ if (isForcedPrivateQaCodexRuntime(env)) excludes.add("apply_patch");
1432
+ else for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) {
1433
+ if (options.preserveOpenClawReplacements && CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES.has(name)) continue;
1434
+ if (options.preserveOpenClawShell && CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES.has(name)) continue;
1435
+ excludes.add(name);
1436
+ }
1437
+ for (const name of config.codexDynamicToolsExclude ?? []) {
1438
+ const trimmed = normalizeCodexDynamicToolName(name);
1439
+ if (trimmed) excludes.add(trimmed);
1440
+ }
1441
+ return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(normalizeCodexDynamicToolName(tool.name)));
1442
+ }
1443
+ //#endregion
1444
+ //#region extensions/codex/src/app-server/thread-binding-policy.ts
1445
+ function shouldRotateCodexAppServerBindingForRuntime(params) {
1446
+ if (!params.current) return false;
1447
+ if (params.binding === params.current) return false;
1448
+ return params.connectionClass === "remote" || Boolean(params.binding);
1449
+ }
1450
+ function resolveCodexGpt56MultiAgentVersion(modelRef) {
1451
+ let modelId = modelRef?.trim().toLowerCase();
1452
+ if (!modelId) return;
1453
+ const slashIndex = modelId.indexOf("/");
1454
+ if (slashIndex > 0) {
1455
+ const provider = modelId.slice(0, slashIndex);
1456
+ if (provider !== "openai" && provider !== "codex") return;
1457
+ modelId = modelId.slice(slashIndex + 1);
1458
+ }
1459
+ if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra") return "v2";
1460
+ return modelId === "gpt-5.6-luna" ? "v1" : void 0;
1461
+ }
1462
+ function shouldRotateCodexGpt56MultiAgentBinding(params) {
1463
+ const bindingVersion = resolveCodexGpt56MultiAgentVersion(params.bindingModel);
1464
+ const requestedVersion = resolveCodexGpt56MultiAgentVersion(params.requestedModel);
1465
+ return Boolean(bindingVersion && requestedVersion && bindingVersion !== requestedVersion);
1466
+ }
1467
+ function isTransientWebSearchRestriction(params) {
1468
+ if (params.nativeProviderWebSearchSupport === "unknown") return true;
1469
+ if (params.params.config?.tools?.web?.search?.enabled === false) return false;
1470
+ if (params.params.disableTools === true) return true;
1471
+ const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
1472
+ if (params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction) return true;
1473
+ if (params.webSearchAllowed !== false) return false;
1474
+ if (params.persistentWebSearchAllowed !== void 0) return params.persistentWebSearchAllowed;
1475
+ if (params.params.toolsAllow === void 0) return false;
1476
+ return !params.params.toolsAllow.some((name) => {
1477
+ const normalized = normalizeCodexDynamicToolName(name);
1478
+ return normalized === "*" || normalized === "web_search";
1479
+ });
1480
+ }
1481
+ function shouldRecheckRecoverablePluginBinding(params) {
1482
+ if (!params.pluginThreadConfig?.enabled) return false;
1483
+ if (!params.binding.pluginAppsFingerprint || !params.binding.pluginAppsInputFingerprint || params.binding.pluginAppsInputFingerprint !== params.pluginThreadConfig.inputFingerprint) return false;
1484
+ const policyContext = params.binding.pluginAppPolicyContext;
1485
+ if (!policyContext) return false;
1486
+ const enabledPluginConfigKeys = params.pluginThreadConfig.enabledPluginConfigKeys ?? [];
1487
+ const recoverablePluginConfigKeys = params.pluginThreadConfig.recoverablePluginConfigKeys ?? enabledPluginConfigKeys;
1488
+ const recoverablePluginConfigKeySet = new Set(recoverablePluginConfigKeys);
1489
+ const bindingContainsSettledPlugin = enabledPluginConfigKeys.filter((configKey) => !recoverablePluginConfigKeySet.has(configKey)).some((configKey) => (policyContext.pluginAppIds[configKey]?.length ?? 0) > 0 || Object.values(policyContext.apps).some((app) => app.source !== "account" && app.configKey === configKey));
1490
+ const accountAppRecoveryEnabled = params.pluginThreadConfig.accountAppRecoveryEnabled ?? enabledPluginConfigKeys.length === 0;
1491
+ return bindingContainsSettledPlugin || accountAppRecoveryEnabled && Object.keys(policyContext.apps).length === 0 || recoverablePluginConfigKeys.length > 0;
1429
1492
  }
1430
1493
  //#endregion
1431
1494
  //#region extensions/codex/src/app-server/thread-fingerprints.ts
@@ -1469,21 +1532,15 @@ function fingerprintUserMcpServersAuthorizationHeader(value) {
1469
1532
  function fingerprintJsonObject(value) {
1470
1533
  return JSON.stringify(stabilizeJsonValue(value));
1471
1534
  }
1472
- /** Hash every resume-visible setting without retaining config, credentials, or instructions. */
1535
+ /** Hash thread-creation identity; settings already applied by turn/start must not restart Codex. */
1473
1536
  function fingerprintCodexThreadConfig(request, authProfileId, dynamicToolsFingerprint) {
1474
1537
  return hashCodexAppServerBindingFingerprint(fingerprintJsonObject({
1475
1538
  authProfileId: authProfileId ?? null,
1476
1539
  dynamicToolsFingerprint: dynamicToolsFingerprint ?? null,
1477
- model: request.model ?? null,
1478
- requestedModel: request.requestedModel === void 0 ? request.model ?? null : request.requestedModel,
1540
+ nativeMultiAgentVersion: resolveCodexGpt56MultiAgentVersion(typeof request.requestedModel === "string" ? request.requestedModel : typeof request.model === "string" ? request.model : void 0) ?? null,
1479
1541
  modelProvider: request.modelProvider ?? null,
1480
1542
  requestedModelProvider: request.requestedModelProvider === void 0 ? request.modelProvider ?? null : request.requestedModelProvider,
1481
- approvalPolicy: request.approvalPolicy ?? null,
1482
- approvalsReviewer: request.approvalsReviewer ?? null,
1483
- sandbox: request.sandbox ?? null,
1484
1543
  permissions: request.permissions ?? null,
1485
- personality: request.personality ?? null,
1486
- serviceTier: request.serviceTier === void 0 ? "<omitted>" : request.serviceTier,
1487
1544
  baseInstructions: request.baseInstructions ?? null,
1488
1545
  developerInstructions: request.developerInstructions ?? null,
1489
1546
  config: request.config ?? {}
@@ -1493,17 +1550,7 @@ function fingerprintEnvironmentSelection(environments) {
1493
1550
  return environments ? JSON.stringify(environments.map(stabilizeJsonValue)) : void 0;
1494
1551
  }
1495
1552
  function fingerprintDynamicToolSpec(tool) {
1496
- return stabilizeDynamicToolFingerprintValue(tool);
1497
- }
1498
- function stabilizeDynamicToolFingerprintValue(value) {
1499
- if (Array.isArray(value)) return value.map(stabilizeDynamicToolFingerprintValue);
1500
- if (!isJsonObject(value)) return value;
1501
- const stable = {};
1502
- for (const [key, child] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) {
1503
- if (key === "description") continue;
1504
- stable[key] = stabilizeDynamicToolFingerprintValue(child);
1505
- }
1506
- return stable;
1553
+ return stabilizeJsonValue(tool);
1507
1554
  }
1508
1555
  function stabilizeJsonValue(value) {
1509
1556
  if (Array.isArray(value)) return value.map(stabilizeJsonValue);
@@ -1545,10 +1592,6 @@ const nativeSkillIsolationByClient = /* @__PURE__ */ new WeakMap();
1545
1592
  function isMissingPathError(error) {
1546
1593
  return error.code === "ENOENT";
1547
1594
  }
1548
- function isPathWithin(root, candidate) {
1549
- const relative = path.relative(path.resolve(root), path.resolve(candidate));
1550
- return relative === "" || !path.isAbsolute(relative) && !relative.startsWith(`..${path.sep}`) && relative !== "..";
1551
- }
1552
1595
  async function canonicalizeExistingPath(candidate) {
1553
1596
  try {
1554
1597
  return await fs.realpath(candidate);
@@ -1574,12 +1617,12 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1574
1617
  const realDefaultCodexHome = await canonicalizeExistingPath(defaultCodexHome);
1575
1618
  roots.push({
1576
1619
  dir: path.join(defaultCodexHome, "skills"),
1577
- onlyEscapedStateTargets: isPathWithin(realStateDir, realDefaultCodexHome)
1620
+ onlyEscapedStateTargets: isPathInside(realStateDir, realDefaultCodexHome)
1578
1621
  });
1579
1622
  }
1580
1623
  const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
1581
1624
  if (configuredCodexHome) {
1582
- const stateOwned = isPathWithin(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1625
+ const stateOwned = isPathInside(realStateDir, await canonicalizeExistingPath(configuredCodexHome));
1583
1626
  roots.push({
1584
1627
  dir: path.join(configuredCodexHome, "skills"),
1585
1628
  onlyEscapedStateTargets: stateOwned
@@ -1597,7 +1640,7 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
1597
1640
  const recordSkillFile = async (filePath, onlyEscapedStateTargets) => {
1598
1641
  try {
1599
1642
  const skillRealPath = await fs.realpath(filePath);
1600
- if (!onlyEscapedStateTargets || !isPathWithin(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
1643
+ if (!onlyEscapedStateTargets || !isPathInside(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
1601
1644
  } catch (error) {
1602
1645
  if (!isMissingPathError(error)) complete = false;
1603
1646
  }
@@ -1714,171 +1757,33 @@ async function resolveUncachedCodexNativeSkillIsolation(params) {
1714
1757
  if (await usesDefaultStateDir()) return;
1715
1758
  const response = await params.client.request("skills/list", {
1716
1759
  cwds: [params.cwd],
1717
- forceReload: true
1718
- }, { signal: params.signal });
1719
- const homes = [params.home?.trim() || process.env.HOME?.trim() || process.env.USERPROFILE?.trim() || os.homedir()];
1720
- if (process.platform === "win32") homes.push(params.userProfile?.trim() || os.homedir());
1721
- const personalSkills = await collectPersonalSkillRealPaths([...new Set(homes.map((home) => path.resolve(home)))], params.codexHome);
1722
- return { disabledUserSkillPaths: [...personalSkills.complete ? personalSkills.skillPaths : /* @__PURE__ */ new Set([...personalSkills.skillPaths, ...response.data.flatMap((entry) => entry.skills.filter((skill) => skill.scope === "user").map((skill) => skill.path))])].toSorted((left, right) => left.localeCompare(right)) };
1723
- }
1724
- /** Applies path-exact session rules after caller config so isolated user skills stay disabled. */
1725
- function applyCodexNativeSkillIsolation(config, isolation) {
1726
- if (!isolation) return config;
1727
- const existingRules = config?.["skills.config"];
1728
- if (existingRules !== void 0 && !Array.isArray(existingRules)) throw new Error("Codex thread skills.config must be an array");
1729
- const disabledRules = isolation.disabledUserSkillPaths.map((skillPath) => ({
1730
- path: skillPath,
1731
- enabled: false
1732
- }));
1733
- return {
1734
- ...config,
1735
- "skills.include_instructions": false,
1736
- "skills.config": [...existingRules ?? [], ...disabledRules]
1737
- };
1738
- }
1739
- //#endregion
1740
- //#region extensions/codex/src/app-server/dynamic-tool-profile.ts
1741
- /** Tool names owned by Codex app-server and normally excluded from OpenClaw dynamic tools. */
1742
- const CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES = [
1743
- "read",
1744
- "write",
1745
- "edit",
1746
- "apply_patch",
1747
- "exec",
1748
- "process",
1749
- "update_plan",
1750
- "tool_call",
1751
- "tool_describe",
1752
- "tool_search",
1753
- "tool_search_code"
1754
- ];
1755
- const CODEX_NATIVE_GOAL_TOOL_EXCLUDES = [
1756
- "get_goal",
1757
- "create_goal",
1758
- "update_goal"
1759
- ];
1760
- const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
1761
- const DYNAMIC_TOOL_NAME_ALIASES = {
1762
- bash: "exec",
1763
- "apply-patch": "apply_patch"
1764
- };
1765
- /** Normalizes OpenClaw/Codex tool names before filtering and allowlist checks. */
1766
- function normalizeCodexDynamicToolName(name) {
1767
- const normalized = name.trim().toLowerCase();
1768
- return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
1769
- }
1770
- /** True only for the host-scoped OpenClaw run's exact tool contract. */
1771
- function isSystemAgentOnlyCodexDynamicToolAllowlist(toolsAllow) {
1772
- return toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "openclaw";
1773
- }
1774
- /** Returns true for private QA runs that force the Codex runtime profile. */
1775
- function isForcedPrivateQaCodexRuntime(env = process.env) {
1776
- return env.OPENCLAW_BUILD_PRIVATE_QA === "1" && env.OPENCLAW_QA_FORCE_RUNTIME?.trim().toLowerCase() === "codex";
1777
- }
1778
- /** Resolves whether dynamic tools load directly or through Codex tool search. */
1779
- function resolveCodexDynamicToolsLoading(config, env = process.env) {
1780
- return isForcedPrivateQaCodexRuntime(env) ? "direct" : config.codexDynamicToolsLoading ?? "searchable";
1781
- }
1782
- function normalizeCodexModelId(modelId) {
1783
- const normalized = modelId?.trim().toLowerCase();
1784
- if (!normalized) return "";
1785
- return normalized.includes("/") ? normalized.split("/").at(-1) : normalized;
1786
- }
1787
- /** Returns true when model behavior requires direct dynamic-tool registration. */
1788
- function shouldUseDirectCodexDynamicToolsForModel(modelId) {
1789
- return shouldDisableCodexToolSearchForModel(modelId);
1790
- }
1791
- /** Returns true for models whose tool-search path is unsupported or inefficient. */
1792
- function shouldDisableCodexToolSearchForModel(modelId) {
1793
- return normalizeCodexModelId(modelId) === "gpt-5.4-nano";
1794
- }
1795
- /** Resolves dynamic-tool loading after applying model-specific restrictions. */
1796
- function resolveCodexDynamicToolsLoadingForModel(config, modelId, env = process.env) {
1797
- const loading = resolveCodexDynamicToolsLoading(config, env);
1798
- return loading === "searchable" && shouldUseDirectCodexDynamicToolsForModel(modelId) ? "direct" : loading;
1799
- }
1800
- /** Resolves dynamic-tool loading for the app-server connection that will execute the turn. */
1801
- function resolveCodexDynamicToolsLoadingForRuntime(config, modelId, options = {}, env = process.env) {
1802
- const loading = resolveCodexDynamicToolsLoadingForModel(config, modelId, env);
1803
- return loading === "searchable" && options.connectionClass === "remote" ? "direct" : loading;
1804
- }
1805
- /** Filters OpenClaw tools that Codex owns natively or config explicitly excludes. */
1806
- function filterCodexDynamicTools(tools, config, env = process.env) {
1807
- return filterCodexDynamicToolsWithOptions(tools, config, env, { preserveOpenClawShell: false });
1808
- }
1809
- /** Keeps exec/process only when Codex cannot advertise an environment-backed native shell. */
1810
- function filterCodexDynamicToolsWithOpenClawShell(tools, config, env = process.env) {
1811
- return filterCodexDynamicToolsWithOptions(tools, config, env, { preserveOpenClawShell: true });
1812
- }
1813
- function filterCodexDynamicToolsWithOptions(tools, config, env, options) {
1814
- const excludes = /* @__PURE__ */ new Set();
1815
- for (const name of CODEX_NATIVE_GOAL_TOOL_EXCLUDES) excludes.add(name);
1816
- if (isForcedPrivateQaCodexRuntime(env)) excludes.add("apply_patch");
1817
- else for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) {
1818
- if (options.preserveOpenClawShell && CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES.has(name)) continue;
1819
- excludes.add(name);
1820
- }
1821
- for (const name of config.codexDynamicToolsExclude ?? []) {
1822
- const trimmed = normalizeCodexDynamicToolName(name);
1823
- if (trimmed) excludes.add(trimmed);
1824
- }
1825
- return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(normalizeCodexDynamicToolName(tool.name)));
1826
- }
1827
- //#endregion
1828
- //#region extensions/codex/src/app-server/thread-binding-policy.ts
1829
- function shouldRotateCodexAppServerBindingForRuntime(params) {
1830
- if (!params.current) return false;
1831
- if (params.binding === params.current) return false;
1832
- return params.connectionClass === "remote" || Boolean(params.binding);
1833
- }
1834
- function resolveCodexGpt56MultiAgentVersion(modelRef) {
1835
- let modelId = modelRef?.trim().toLowerCase();
1836
- if (!modelId) return;
1837
- const slashIndex = modelId.indexOf("/");
1838
- if (slashIndex > 0) {
1839
- const provider = modelId.slice(0, slashIndex);
1840
- if (provider !== "openai" && provider !== "codex") return;
1841
- modelId = modelId.slice(slashIndex + 1);
1842
- }
1843
- if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra") return "v2";
1844
- return modelId === "gpt-5.6-luna" ? "v1" : void 0;
1845
- }
1846
- function shouldRotateCodexGpt56MultiAgentBinding(params) {
1847
- const bindingVersion = resolveCodexGpt56MultiAgentVersion(params.bindingModel);
1848
- const requestedVersion = resolveCodexGpt56MultiAgentVersion(params.requestedModel);
1849
- return Boolean(bindingVersion && requestedVersion && bindingVersion !== requestedVersion);
1850
- }
1851
- function isTransientWebSearchRestriction(params) {
1852
- if (params.nativeProviderWebSearchSupport === "unknown") return true;
1853
- if (params.params.config?.tools?.web?.search?.enabled === false) return false;
1854
- if (params.params.disableTools === true) return true;
1855
- const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
1856
- if (params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction) return true;
1857
- if (params.webSearchAllowed !== false) return false;
1858
- if (params.persistentWebSearchAllowed !== void 0) return params.persistentWebSearchAllowed;
1859
- if (params.params.toolsAllow === void 0) return false;
1860
- return !params.params.toolsAllow.some((name) => {
1861
- const normalized = normalizeCodexDynamicToolName(name);
1862
- return normalized === "*" || normalized === "web_search";
1863
- });
1864
- }
1865
- function shouldRecheckRecoverablePluginBinding(params) {
1866
- if (!params.pluginThreadConfig?.enabled) return false;
1867
- if (!params.binding.pluginAppsFingerprint || !params.binding.pluginAppsInputFingerprint || params.binding.pluginAppsInputFingerprint !== params.pluginThreadConfig.inputFingerprint) return false;
1868
- const policyContext = params.binding.pluginAppPolicyContext;
1869
- if (!policyContext) return false;
1870
- const enabledPluginConfigKeys = params.pluginThreadConfig.enabledPluginConfigKeys ?? [];
1871
- const recoverablePluginConfigKeys = params.pluginThreadConfig.recoverablePluginConfigKeys ?? enabledPluginConfigKeys;
1872
- const recoverablePluginConfigKeySet = new Set(recoverablePluginConfigKeys);
1873
- const bindingContainsSettledPlugin = enabledPluginConfigKeys.filter((configKey) => !recoverablePluginConfigKeySet.has(configKey)).some((configKey) => (policyContext.pluginAppIds[configKey]?.length ?? 0) > 0 || Object.values(policyContext.apps).some((app) => app.source !== "account" && app.configKey === configKey));
1874
- const accountAppRecoveryEnabled = params.pluginThreadConfig.accountAppRecoveryEnabled ?? enabledPluginConfigKeys.length === 0;
1875
- return bindingContainsSettledPlugin || accountAppRecoveryEnabled && Object.keys(policyContext.apps).length === 0 || recoverablePluginConfigKeys.length > 0;
1760
+ forceReload: true
1761
+ }, { signal: params.signal });
1762
+ const homes = [params.home?.trim() || process.env.HOME?.trim() || process.env.USERPROFILE?.trim() || os.homedir()];
1763
+ if (process.platform === "win32") homes.push(params.userProfile?.trim() || os.homedir());
1764
+ const personalSkills = await collectPersonalSkillRealPaths([...new Set(homes.map((home) => path.resolve(home)))], params.codexHome);
1765
+ return { disabledUserSkillPaths: [...personalSkills.complete ? personalSkills.skillPaths : /* @__PURE__ */ new Set([...personalSkills.skillPaths, ...response.data.flatMap((entry) => entry.skills.filter((skill) => skill.scope === "user").map((skill) => skill.path))])].toSorted((left, right) => left.localeCompare(right)) };
1766
+ }
1767
+ /** Applies path-exact session rules after caller config so isolated user skills stay disabled. */
1768
+ function applyCodexNativeSkillIsolation(config, isolation) {
1769
+ if (!isolation) return config;
1770
+ const existingRules = config?.["skills.config"];
1771
+ if (existingRules !== void 0 && !Array.isArray(existingRules)) throw new Error("Codex thread skills.config must be an array");
1772
+ const disabledRules = isolation.disabledUserSkillPaths.map((skillPath) => ({
1773
+ path: skillPath,
1774
+ enabled: false
1775
+ }));
1776
+ return {
1777
+ ...config,
1778
+ "skills.include_instructions": false,
1779
+ "skills.config": [...existingRules ?? [], ...disabledRules]
1780
+ };
1876
1781
  }
1877
1782
  //#endregion
1878
1783
  //#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
1879
1784
  var CodexThreadStartRequestError = class extends Error {
1880
1785
  constructor(cause) {
1881
- super(formatErrorMessage(cause), { cause });
1786
+ super(`thread/start: ${formatErrorMessage(cause)}`, { cause });
1882
1787
  this.name = "CodexThreadStartRequestError";
1883
1788
  }
1884
1789
  };
@@ -1888,10 +1793,10 @@ var CodexThreadBindingConflictError = class extends Error {
1888
1793
  this.name = "CodexThreadBindingConflictError";
1889
1794
  }
1890
1795
  };
1891
- var CodexRingZeroAttestationError = class extends Error {
1796
+ var CodexRestrictedToolSurfaceAttestationError = class extends Error {
1892
1797
  constructor(cause) {
1893
- super("Codex ring-zero MCP attestation failed", { cause });
1894
- this.name = "CodexRingZeroAttestationError";
1798
+ super("Codex restricted-tool-surface MCP attestation failed", { cause });
1799
+ this.name = "CodexRestrictedToolSurfaceAttestationError";
1895
1800
  }
1896
1801
  };
1897
1802
  var CodexThreadBindingConflictAfterCleanupError = class extends CodexThreadBindingConflictError {};
@@ -1902,6 +1807,64 @@ var CodexAdoptedThreadActiveError = class extends Error {
1902
1807
  }
1903
1808
  };
1904
1809
  //#endregion
1810
+ //#region extensions/codex/src/app-server/managed-thread-store.ts
1811
+ const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
1812
+ const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
1813
+ const managedThreadSchema = z.object({
1814
+ version: z.literal(1),
1815
+ kind: z.literal("managed-thread"),
1816
+ sourceHomeId: z.string().min(1),
1817
+ threadId: z.string().min(1),
1818
+ rolloutPath: z.string().min(1).optional()
1819
+ });
1820
+ async function markStartedCodexManagedThread(store, params) {
1821
+ if (!store) return;
1822
+ try {
1823
+ await store.mark({
1824
+ sourceHomeId: params.sourceHomeId,
1825
+ threadId: params.threadId,
1826
+ ...params.rolloutPath ? { rolloutPath: params.rolloutPath } : {}
1827
+ });
1828
+ } catch (error) {
1829
+ embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
1830
+ }
1831
+ }
1832
+ function managedThreadStoreKey(sourceHomeId, threadId) {
1833
+ return `sha256:${createHash("sha256").update("openclaw:codex-managed-thread:v1\0").update(sourceHomeId).update("\0").update(threadId).digest("hex")}`;
1834
+ }
1835
+ /** Durable ownership index for Codex threads created by OpenClaw. */
1836
+ function createCodexManagedThreadStore(state) {
1837
+ return {
1838
+ async mark(params) {
1839
+ try {
1840
+ const value = managedThreadSchema.parse({
1841
+ version: 1,
1842
+ kind: "managed-thread",
1843
+ sourceHomeId: params.sourceHomeId.trim(),
1844
+ threadId: params.threadId.trim(),
1845
+ ...params.rolloutPath?.trim() ? { rolloutPath: params.rolloutPath.trim() } : {}
1846
+ });
1847
+ state.registerIfAbsent(managedThreadStoreKey(value.sourceHomeId, value.threadId), value);
1848
+ return true;
1849
+ } catch (error) {
1850
+ embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
1851
+ return false;
1852
+ }
1853
+ },
1854
+ async snapshot() {
1855
+ const byHome = /* @__PURE__ */ new Map();
1856
+ for (const entry of state.entries()) {
1857
+ const parsed = managedThreadSchema.safeParse(entry.value);
1858
+ if (!parsed.success) continue;
1859
+ const ids = byHome.get(parsed.data.sourceHomeId) ?? /* @__PURE__ */ new Set();
1860
+ ids.add(parsed.data.threadId);
1861
+ byHome.set(parsed.data.sourceHomeId, ids);
1862
+ }
1863
+ return byHome;
1864
+ }
1865
+ };
1866
+ }
1867
+ //#endregion
1905
1868
  //#region extensions/codex/src/app-server/plugin-thread-attestation.ts
1906
1869
  /**
1907
1870
  * Confirms admitted plugin and account apps against their actual Codex thread before
@@ -1957,6 +1920,50 @@ async function discardUnattestedCodexPluginThread(params) {
1957
1920
  }
1958
1921
  }
1959
1922
  //#endregion
1923
+ //#region extensions/codex/src/app-server/thread-lifecycle-result.ts
1924
+ /** Materializes the public lifecycle result after a fresh thread is durably committed. */
1925
+ function buildStartedCodexThreadBinding(input) {
1926
+ const { context, params, response, startParams } = input;
1927
+ return {
1928
+ threadId: response.thread.id,
1929
+ ...input.clientId ? { clientId: input.clientId } : {},
1930
+ cwd: params.cwd,
1931
+ ...input.rolloutPath ? { rolloutPath: input.rolloutPath } : {},
1932
+ authProfileId: params.params.authProfileId,
1933
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
1934
+ model: response.model ?? startParams.model ?? params.params.modelId,
1935
+ modelProvider: response.modelProvider ?? input.startModelProvider ?? input.modelProvider,
1936
+ dynamicToolsFingerprint: context.dynamicToolsFingerprint,
1937
+ dynamicToolsContainDeferred: context.dynamicToolsContainDeferred,
1938
+ nativeSkillIsolationFingerprint: context.nativeSkillIsolationFingerprint,
1939
+ userMcpServersFingerprint: context.userMcpServersFingerprint,
1940
+ mcpServersFingerprint: input.nextMcpServersFingerprint,
1941
+ configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
1942
+ ringZeroConfigFingerprint: context.ringZeroConfigFingerprint,
1943
+ ringZeroClientInstanceId: context.ringZeroClientInstanceId,
1944
+ networkProxyProfileName: params.appServer.networkProxy?.profileName,
1945
+ networkProxyConfigFingerprint: context.networkProxyConfigFingerprint,
1946
+ nativeHookRelayGeneration: input.finalConfigPatch.nativeHookRelayGeneration,
1947
+ appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
1948
+ pluginAppsFingerprint: input.pluginThreadConfig?.fingerprint,
1949
+ pluginAppsInputFingerprint: input.pluginThreadConfig?.inputFingerprint,
1950
+ pluginAppPolicyContext: input.pluginThreadConfig?.policyContext,
1951
+ contextEngine: context.contextEngineBinding,
1952
+ environmentSelectionFingerprint: context.environmentSelectionFingerprint,
1953
+ ...!context.preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
1954
+ ...startParams,
1955
+ model: response.model ?? startParams.model ?? null,
1956
+ requestedModel: startParams.model ?? null,
1957
+ modelProvider: input.bindingModelProvider ?? null,
1958
+ requestedModelProvider: startParams.modelProvider ?? input.bindingModelProvider ?? null
1959
+ }, params.params.authProfileId, context.dynamicToolsFingerprint) } : {},
1960
+ lifecycle: {
1961
+ action: "started",
1962
+ ...context.rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
1963
+ }
1964
+ };
1965
+ }
1966
+ //#endregion
1960
1967
  //#region extensions/codex/src/app-server/reasoning-effort.ts
1961
1968
  const CODEX_REASONING_EFFORTS = [
1962
1969
  "minimal",
@@ -1967,30 +1974,13 @@ const CODEX_REASONING_EFFORTS = [
1967
1974
  "max",
1968
1975
  "ultra"
1969
1976
  ];
1970
- const GPT_56_MAX_REASONING_EFFORTS = [
1971
- "low",
1972
- "medium",
1973
- "high",
1974
- "xhigh",
1975
- "max"
1976
- ];
1977
- const GPT_56_ULTRA_REASONING_EFFORTS = [...GPT_56_MAX_REASONING_EFFORTS, "ultra"];
1978
- const GPT_5_PRO_REASONING_EFFORTS = [
1977
+ const LEGACY_PRO_REASONING_EFFORTS = [
1979
1978
  "medium",
1980
1979
  "high",
1981
1980
  "xhigh"
1982
1981
  ];
1983
- const GPT_56_ULTRA_MODEL_IDS = /* @__PURE__ */ new Set(["gpt-5.6-sol", "gpt-5.6-terra"]);
1984
- const GPT_56_MAX_MODEL_IDS = /* @__PURE__ */ new Set([...GPT_56_ULTRA_MODEL_IDS, "gpt-5.6-luna"]);
1985
- const MODERN_CODEX_MODEL_IDS = /* @__PURE__ */ new Set([
1986
- ...GPT_56_MAX_MODEL_IDS,
1987
- "gpt-5.5",
1988
- "gpt-5.5-pro",
1989
- "gpt-5.4",
1990
- "gpt-5.4-pro",
1991
- "gpt-5.4-mini",
1992
- "gpt-5.3-codex-spark"
1993
- ]);
1982
+ const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
1983
+ const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
1994
1984
  function normalizeCodexReasoningEfforts(efforts) {
1995
1985
  if (!efforts) return [];
1996
1986
  const supported = new Set(efforts.map((effort) => effort.trim().toLowerCase()));
@@ -2010,24 +2000,21 @@ function resolveSupportedReasoningEffort(params) {
2010
2000
  const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
2011
2001
  return fallbackEfforts.find((effort) => CODEX_REASONING_EFFORTS.indexOf(effort) >= requestedRank) ?? fallbackEfforts.at(-1);
2012
2002
  }
2013
- function resolveFallbackReasoningEfforts(modelId) {
2014
- const normalized = modelId.trim().toLowerCase();
2015
- if (GPT_56_ULTRA_MODEL_IDS.has(normalized)) return GPT_56_ULTRA_REASONING_EFFORTS;
2016
- if (normalized === "gpt-5.6-luna") return GPT_56_MAX_REASONING_EFFORTS;
2017
- if (normalized === "gpt-5.5-pro" || normalized === "gpt-5.4-pro") return GPT_5_PRO_REASONING_EFFORTS;
2018
- }
2019
- /** Resolve a turn effort from app-server metadata, with exact-name offline fallbacks. */
2003
+ /** Resolve a turn effort from the selected model's provider-owned metadata. */
2020
2004
  function resolveCodexAppServerReasoningEffort(params) {
2021
2005
  if (params.thinkLevel === "off" || params.thinkLevel === "adaptive") return null;
2022
- const supportedReasoningEfforts = params.supportedReasoningEfforts ?? resolveFallbackReasoningEfforts(params.modelId);
2023
- if (supportedReasoningEfforts) return resolveSupportedReasoningEffort({
2006
+ if (params.supportedReasoningEfforts) return resolveSupportedReasoningEffort({
2024
2007
  requested: params.thinkLevel,
2025
- supportedReasoningEfforts
2008
+ supportedReasoningEfforts: params.supportedReasoningEfforts
2009
+ }) ?? null;
2010
+ const modelId = params.modelId.trim().toLowerCase();
2011
+ if (LEGACY_PRO_MODEL_ID_RE.test(modelId)) return resolveSupportedReasoningEffort({
2012
+ requested: params.thinkLevel,
2013
+ supportedReasoningEfforts: LEGACY_PRO_REASONING_EFFORTS
2026
2014
  }) ?? null;
2027
- const normalizedModelId = params.modelId.trim().toLowerCase();
2028
- if (params.thinkLevel === "minimal") return MODERN_CODEX_MODEL_IDS.has(normalizedModelId) ? "low" : "minimal";
2029
- if (params.thinkLevel === "low" || params.thinkLevel === "medium" || params.thinkLevel === "high" || params.thinkLevel === "xhigh") return params.thinkLevel;
2030
- return params.thinkLevel === "max" && GPT_56_MAX_MODEL_IDS.has(normalizedModelId) ? "max" : null;
2015
+ if (params.thinkLevel === "minimal" && MODERN_GPT_5_MODEL_ID_RE.test(modelId)) return "low";
2016
+ if (params.thinkLevel === "minimal" || params.thinkLevel === "low" || params.thinkLevel === "medium" || params.thinkLevel === "high" || params.thinkLevel === "xhigh") return params.thinkLevel;
2017
+ return null;
2031
2018
  }
2032
2019
  //#endregion
2033
2020
  //#region extensions/codex/src/app-server/thread-model-selection.ts
@@ -2105,11 +2092,21 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
2105
2092
  });
2106
2093
  }
2107
2094
  //#endregion
2095
+ //#region extensions/codex/src/app-server/project-doc-thread-config.ts
2096
+ const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 128 * 1024;
2097
+ function buildCodexProjectDocThreadConfig(config) {
2098
+ const defaults = { project_doc_max_bytes: CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
2099
+ return mergeCodexThreadConfigs(defaults, config) ?? defaults;
2100
+ }
2101
+ //#endregion
2108
2102
  //#region extensions/codex/src/app-server/thread-prompt.ts
2109
2103
  function buildDeveloperInstructions(params, options = {}) {
2110
2104
  const deferredToolNames = /* @__PURE__ */ new Set();
2111
2105
  let hasSkillWorkshop = false;
2106
+ let hasSessionsSpawn = false;
2112
2107
  let hasSessionsYield = false;
2108
+ let hasSubagentsList = false;
2109
+ let hasSessionsSend = false;
2113
2110
  let hasSeenDirectNamespace = false;
2114
2111
  let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
2115
2112
  for (const spec of options.dynamicTools ?? []) {
@@ -2119,7 +2116,10 @@ function buildDeveloperInstructions(params, options = {}) {
2119
2116
  const name = tool.name.trim();
2120
2117
  if (tool.deferLoading === true && name) deferredToolNames.add(name);
2121
2118
  hasSkillWorkshop ||= name === SKILL_WORKSHOP_TOOL_NAME;
2119
+ hasSessionsSpawn ||= name === "sessions_spawn";
2122
2120
  hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
2121
+ hasSubagentsList ||= name === "subagents";
2122
+ hasSessionsSend ||= name === "sessions_send";
2123
2123
  messageToolAvailable ||= name === "message";
2124
2124
  }
2125
2125
  }
@@ -2127,21 +2127,71 @@ function buildDeveloperInstructions(params, options = {}) {
2127
2127
  surface: "codex_app_server",
2128
2128
  includeLegacyGlobalGuidance: false
2129
2129
  }).join("\n");
2130
+ const delegationGuidanceAvailable = params.disableTools !== true && params.delegationCapability !== "report_only" && !isMessageOnlyCodexSourceReply(params);
2131
+ const nativeDelegationAvailable = delegationGuidanceAvailable && !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) && !shouldDisableCodexToolSearchForModel(params.modelId);
2132
+ const deferredToolDiscoveryGuidance = deferredToolNames.size > 0 || nativeDelegationAvailable ? "Deferred tools may be absent from the direct tool list. Use `tool_search` when directly callable. On code-mode-only models, use `exec` instead: filter `ALL_TOOLS` by name and description, then call the matching entry through `tools`." : void 0;
2130
2133
  return [
2131
2134
  "You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
2132
- deferredToolNames.size > 0 ? `Deferred searchable OpenClaw dynamic tools available: ${[...deferredToolNames].toSorted((left, right) => left.localeCompare(right)).join(", ")}. Use \`tool_search\` to load exact callable specs before use.` : void 0,
2135
+ deferredToolNames.size > 0 ? `Deferred searchable OpenClaw dynamic tools available: ${[...deferredToolNames].toSorted((left, right) => left.localeCompare(right)).join(", ")}.` : void 0,
2136
+ deferredToolDiscoveryGuidance,
2133
2137
  hasSkillWorkshop ? buildSkillWorkshopPromptSection().join("\n") : void 0,
2134
- "Use Codex native `spawn_agent` for Codex subagents. `spawn_agent` and the other native collaboration tools may be deferred: when `spawn_agent` is not directly listed, load it with `tool_search` before spawning. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent`.",
2135
- hasSessionsYield ? "When a native child's result belongs in a later turn, end the current turn with `openclaw_direct.sessions_yield`; the completion arrives as the next model-visible input. Use native `wait_agent` only for an intentional same-turn wait when the immediate next step is blocked on the child. Never loop-poll for native child completion." : void 0,
2136
- buildVisibleReplyInstruction(params, messageToolAvailable),
2138
+ nativeDelegationAvailable ? `Use Codex native \`spawn_agent\` for Codex subagents. \`spawn_agent\` and the other native collaboration tools may be deferred.${hasSessionsSpawn ? " Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent` on internal legwork." : ""}` : void 0,
2139
+ hasSessionsYield && nativeDelegationAvailable ? "When a native child's result belongs in a later turn, end the current turn with `openclaw_direct.sessions_yield`; the completion arrives as the next model-visible input. Use native `wait_agent` only for an intentional same-turn wait when the immediate next step is blocked on the child. Never loop-poll for native child completion." : void 0,
2140
+ delegationGuidanceAvailable ? buildDelegationGuidanceSection({
2141
+ mode: resolveMainSessionDelegationMode({
2142
+ config: params.config,
2143
+ agentId: params.agentId,
2144
+ sessionKey: params.sessionKey
2145
+ }),
2146
+ isMinimal: params.promptMode === "minimal" || params.promptMode === "none",
2147
+ hiddenDelegationTool: nativeDelegationAvailable ? "native `spawn_agent`" : hasSessionsSpawn ? "`sessions_spawn`" : "",
2148
+ hasVisibleSessionSpawn: hasSessionsSpawn,
2149
+ hasSessionsYield,
2150
+ hasSubagentsList,
2151
+ hasSessionsSend
2152
+ }).join("\n") : void 0,
2153
+ buildHarnessVisibleReplyGuidance({
2154
+ sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
2155
+ messageToolAvailable
2156
+ }),
2157
+ TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT,
2137
2158
  nativeCommandGuidance,
2138
2159
  params.extraSystemPrompt
2139
2160
  ].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
2140
2161
  }
2141
- function buildVisibleReplyInstruction(params, messageToolAvailable) {
2142
- if (params.sourceReplyDeliveryMode === "message_tool_only" && messageToolAvailable) return "Visible source replies are not automatically delivered for this run. Use `message(action=send)` for user-visible source-channel output. For progress, set `final=false`. When the message is the completed reply to the current source conversation, set `final=true`; OpenClaw stops after confirming delivery. If `final` is omitted, OpenClaw continues and resolves the latest omitted source reply only when the turn ends successfully. Do not repeat visible message content in your final answer.";
2143
- if (messageToolAvailable) return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation. Use `message` only for explicit out-of-band sends, media/file sends, or sends to a different target.";
2144
- return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation.";
2162
+ //#endregion
2163
+ //#region extensions/codex/src/app-server/thread-shell-environment.ts
2164
+ /** Applies host-selected values and any required login-shell restriction last. */
2165
+ function applyCodexManagedShellEnvironment(config, environment, disableLoginShell = false) {
2166
+ if (!environment || Object.keys(environment).length === 0) return disableLoginShell ? {
2167
+ ...config,
2168
+ allow_login_shell: false
2169
+ } : config;
2170
+ const current = isJsonObject(config.shell_environment_policy) ? config.shell_environment_policy : {};
2171
+ const currentSet = isJsonObject(current.set) ? current.set : {};
2172
+ const names = Object.keys(environment).toSorted();
2173
+ const includeOnly = Array.isArray(current.include_only) ? current.include_only.filter((entry) => typeof entry === "string") : [];
2174
+ const filters = isJsonObject(current.filters) ? current.filters : void 0;
2175
+ const hasIncludeFilter = filters && Object.values(filters).includes("include");
2176
+ const managedConfig = {
2177
+ ...config,
2178
+ shell_environment_policy: {
2179
+ ...current,
2180
+ experimental_use_profile: false,
2181
+ set: {
2182
+ ...currentSet,
2183
+ ...environment
2184
+ },
2185
+ ...filters ? hasIncludeFilter ? { filters: {
2186
+ ...filters,
2187
+ ...Object.fromEntries(names.map((name) => [name, "include"]))
2188
+ } } : {} : includeOnly.length > 0 ? { include_only: [.../* @__PURE__ */ new Set([...includeOnly, ...names])] } : {}
2189
+ }
2190
+ };
2191
+ return disableLoginShell ? {
2192
+ ...managedConfig,
2193
+ allow_login_shell: false
2194
+ } : managedConfig;
2145
2195
  }
2146
2196
  //#endregion
2147
2197
  //#region extensions/codex/src/app-server/web-search.ts
@@ -2149,16 +2199,13 @@ const CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG = {
2149
2199
  "features.standalone_web_search": false,
2150
2200
  web_search: "disabled"
2151
2201
  };
2152
- function normalizeOptionalString$1(value) {
2153
- return typeof value === "string" ? value.trim() || void 0 : void 0;
2154
- }
2155
2202
  function normalizeUniqueStrings(value) {
2156
2203
  if (!Array.isArray(value)) return;
2157
- const normalized = [...new Set(value.map(normalizeOptionalString$1).filter((entry) => Boolean(entry)))];
2204
+ const normalized = [...new Set(value.map(normalizeOptionalString).filter((entry) => Boolean(entry)))];
2158
2205
  return normalized.length > 0 ? normalized : void 0;
2159
2206
  }
2160
2207
  function hasManagedSearchProvider(config) {
2161
- return normalizeOptionalString$1(config?.tools?.web?.search?.provider) !== void 0;
2208
+ return normalizeOptionalString(config?.tools?.web?.search?.provider) !== void 0;
2162
2209
  }
2163
2210
  function hasNativeDomainRestrictions(config) {
2164
2211
  return normalizeUniqueStrings(config?.tools?.web?.search?.openaiCodex?.allowedDomains) !== void 0;
@@ -2173,10 +2220,10 @@ function buildCodexNativeWebSearchThreadConfig(config) {
2173
2220
  if (allowedDomains) threadConfig["tools.web_search.allowed_domains"] = allowedDomains;
2174
2221
  if (nativeConfig?.contextSize) threadConfig["tools.web_search.context_size"] = nativeConfig.contextSize;
2175
2222
  const location = nativeConfig?.userLocation;
2176
- const country = normalizeOptionalString$1(location?.country);
2177
- const region = normalizeOptionalString$1(location?.region);
2178
- const city = normalizeOptionalString$1(location?.city);
2179
- const timezone = normalizeOptionalString$1(location?.timezone);
2223
+ const country = normalizeOptionalString(location?.country);
2224
+ const region = normalizeOptionalString(location?.region);
2225
+ const city = normalizeOptionalString(location?.city);
2226
+ const timezone = normalizeOptionalString(location?.timezone);
2180
2227
  if (country) threadConfig["tools.web_search.location.country"] = country;
2181
2228
  if (region) threadConfig["tools.web_search.location.region"] = region;
2182
2229
  if (city) threadConfig["tools.web_search.location.city"] = city;
@@ -2207,7 +2254,8 @@ function resolveCodexWebSearchPlan(params) {
2207
2254
  return {
2208
2255
  kind: "native-hosted",
2209
2256
  suppressManagedWebSearch: true,
2210
- threadConfig: buildCodexNativeWebSearchThreadConfig(params.config)
2257
+ threadConfig: buildCodexNativeWebSearchThreadConfig(params.config),
2258
+ webFetchHostnameAllowlist: buildHostnameAllowlistPolicyFromSuffixAllowlist(nativeConfig?.allowedDomains)?.hostnameAllowlist
2211
2259
  };
2212
2260
  }
2213
2261
  const CODEX_CODE_MODE_THREAD_CONFIG = {
@@ -2216,32 +2264,56 @@ const CODEX_CODE_MODE_THREAD_CONFIG = {
2216
2264
  "features.apply_patch_streaming_events": true
2217
2265
  };
2218
2266
  const CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG = { "features.goals": false };
2267
+ const CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG = { "tools.update_plan.enabled": false };
2219
2268
  const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG = {
2220
2269
  "features.code_mode": false,
2221
2270
  "features.code_mode_only": false
2222
2271
  };
2223
- const CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG = { project_doc_max_bytes: 0 };
2272
+ const CODEX_NO_PROJECT_DOCS_CONFIG = { project_doc_max_bytes: 0 };
2224
2273
  const CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG = { "features.multi_agent": false };
2225
2274
  const CODEX_DELEGATION_DISABLED_THREAD_CONFIG = {
2275
+ "agents.enabled": false,
2226
2276
  "features.multi_agent": false,
2227
2277
  "features.multi_agent_v2": false
2228
2278
  };
2279
+ const CODEX_RING_ZERO_RESTRICTED_FEATURES = /* @__PURE__ */ new Set([
2280
+ "apps",
2281
+ "artifact",
2282
+ "browser_use",
2283
+ "browser_use_external",
2284
+ "browser_use_full_cdp_access",
2285
+ "chronicle",
2286
+ "code_mode",
2287
+ "code_mode_only",
2288
+ "computer_use",
2289
+ "current_time_reminder",
2290
+ "default_mode_request_user_input",
2291
+ "deferred_executor",
2292
+ "goals",
2293
+ "hooks",
2294
+ "image_generation",
2295
+ "memories",
2296
+ "multi_agent",
2297
+ "multi_agent_v2",
2298
+ "plugins",
2299
+ "request_permissions_tool",
2300
+ "skill_search",
2301
+ "shell_tool",
2302
+ "standalone_web_search",
2303
+ "token_budget",
2304
+ "unified_exec",
2305
+ "view_image",
2306
+ "web_search_cached",
2307
+ "web_search_request",
2308
+ "workspace_dependencies"
2309
+ ]);
2229
2310
  const CODEX_RING_ZERO_THREAD_CONFIG = {
2230
- "features.apps": false,
2231
- "features.current_time_reminder": false,
2232
- "features.deferred_executor": false,
2233
- "features.enable_fanout": false,
2234
- "features.goals": false,
2235
- "features.hooks": false,
2236
- "features.image_generation": false,
2237
- "features.memories": false,
2238
- "features.multi_agent": false,
2239
- "features.multi_agent_v2": false,
2240
- "features.plugins": false,
2241
- "features.standalone_web_search": false,
2242
- "features.token_budget": false,
2311
+ ...CODEX_DELEGATION_DISABLED_THREAD_CONFIG,
2312
+ ...Object.fromEntries([...CODEX_RING_ZERO_RESTRICTED_FEATURES].map((feature) => [`features.${feature}`, false])),
2243
2313
  "orchestrator.mcp.enabled": false,
2244
2314
  "orchestrator.skills.enabled": false,
2315
+ "skills.bundled.enabled": false,
2316
+ "skills.include_instructions": false,
2245
2317
  "tools.experimental_request_user_input.enabled": false,
2246
2318
  hooks: {
2247
2319
  PreToolUse: [],
@@ -2255,28 +2327,19 @@ const CODEX_RING_ZERO_THREAD_CONFIG = {
2255
2327
  SubagentStop: [],
2256
2328
  Stop: []
2257
2329
  },
2258
- project_doc_max_bytes: 0,
2259
2330
  notify: [],
2260
2331
  web_search: "disabled"
2261
2332
  };
2262
- const CODEX_RING_ZERO_RESTRICTED_FEATURES = /* @__PURE__ */ new Set([
2263
- "apps",
2264
- "code_mode",
2265
- "code_mode_only",
2266
- "current_time_reminder",
2267
- "deferred_executor",
2268
- "enable_fanout",
2269
- "goals",
2270
- "hooks",
2271
- "image_generation",
2272
- "memories",
2273
- "multi_agent",
2274
- "multi_agent_v2",
2275
- "plugins",
2276
- "standalone_web_search",
2277
- "token_budget"
2333
+ const CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES = /* @__PURE__ */ new Map([
2334
+ ["connectors", "apps"],
2335
+ ["imagegenext", "image_generation"],
2336
+ ["collab", "multi_agent"],
2337
+ ["memory_tool", "memories"],
2338
+ ["telepathy", "chronicle"],
2339
+ ["codex_hooks", "hooks"]
2278
2340
  ]);
2279
2341
  const CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
2342
+ "packagedDefaults",
2280
2343
  "mdm",
2281
2344
  "system",
2282
2345
  "enterpriseManaged",
@@ -2305,6 +2368,7 @@ function buildThreadStartParams(params, options) {
2305
2368
  model: modelSelection.model,
2306
2369
  ...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
2307
2370
  cwd: options.cwd,
2371
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
2308
2372
  approvalPolicy: options.appServer.approvalPolicy,
2309
2373
  approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
2310
2374
  ...codexThreadSandboxOrPermissions(options.appServer),
@@ -2320,7 +2384,9 @@ function buildThreadStartParams(params, options) {
2320
2384
  webSearchAllowed: options.webSearchAllowed,
2321
2385
  appServer: options.appServer,
2322
2386
  hostSystemAgentActive: options.hostSystemAgentActive,
2323
- ringZeroInheritedMcpServerNames: options.ringZeroInheritedMcpServerNames
2387
+ restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
2388
+ shellEnvironment: options.shellEnvironment,
2389
+ disableLoginShell: options.disableLoginShell
2324
2390
  }),
2325
2391
  ...resolveCodexThreadEnvironmentSelection(options),
2326
2392
  developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
@@ -2346,6 +2412,8 @@ function buildThreadResumeParams(params, options) {
2346
2412
  });
2347
2413
  return {
2348
2414
  threadId: options.threadId,
2415
+ ...options.cwd ? { cwd: options.cwd } : {},
2416
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
2349
2417
  excludeTurns: true,
2350
2418
  initialTurnsPage: {
2351
2419
  limit: 1,
@@ -2369,29 +2437,38 @@ function buildThreadResumeParams(params, options) {
2369
2437
  webSearchAllowed: options.webSearchAllowed,
2370
2438
  appServer: options.appServer,
2371
2439
  hostSystemAgentActive: options.hostSystemAgentActive,
2372
- ringZeroInheritedMcpServerNames: options.ringZeroInheritedMcpServerNames
2440
+ restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
2441
+ shellEnvironment: options.shellEnvironment,
2442
+ disableLoginShell: options.disableLoginShell
2373
2443
  }),
2374
2444
  developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
2375
2445
  };
2376
2446
  }
2377
2447
  function buildCodexRuntimeThreadConfig(config, options = {}) {
2448
+ const configured = buildCodexProjectDocThreadConfig(config);
2378
2449
  const codeModeConfig = {
2379
2450
  ...CODEX_CODE_MODE_THREAD_CONFIG,
2380
2451
  "features.code_mode_only": options.nativeCodeModeOnlyEnabled === true
2381
2452
  };
2382
2453
  if (options.nativeCodeModeEnabled === false) {
2383
- const disabledConfig = mergeCodexThreadConfigs(config, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG) ?? { ...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG };
2454
+ const disabledConfig = mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
2455
+ ...CODEX_CODE_MODE_DISABLED_THREAD_CONFIG,
2456
+ ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2457
+ ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
2458
+ };
2384
2459
  delete disabledConfig["features.apply_patch_streaming_events"];
2385
2460
  return disabledConfig;
2386
2461
  }
2387
- if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
2462
+ if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG, { "features.code_mode_only": true }) ?? {
2388
2463
  ...codeModeConfig,
2389
2464
  ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2465
+ ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG,
2390
2466
  "features.code_mode_only": true
2391
2467
  }, options.directOnlyToolNamespaces);
2392
- return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG) ?? {
2468
+ return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG) ?? {
2393
2469
  ...codeModeConfig,
2394
- ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG
2470
+ ...CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG,
2471
+ ...CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG
2395
2472
  }, options.directOnlyToolNamespaces);
2396
2473
  }
2397
2474
  function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
@@ -2408,9 +2485,16 @@ function resolveDirectOnlyToolNamespaces(dynamicTools) {
2408
2485
  }
2409
2486
  function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2410
2487
  const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
2488
+ const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
2489
+ const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
2490
+ const restrictedTurnDisablesProjectDocs = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted && params.disableTools;
2411
2491
  const configMcpServers = config?.mcp_servers;
2412
- if (ringZeroActive && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex ring-zero received invalid thread mcp_servers config");
2413
- const ringZeroMcpServerNames = [...options.ringZeroInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
2492
+ if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
2493
+ const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
2494
+ const restrictedRunConfig = restrictedToolSurface && isJsonObject(configMcpServers) ? {
2495
+ ...config,
2496
+ mcp_servers: {}
2497
+ } : config;
2414
2498
  const webSearchConfig = resolveCodexWebSearchPlan({
2415
2499
  config: params.config,
2416
2500
  disableTools: params.disableTools,
@@ -2418,16 +2502,20 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
2418
2502
  nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
2419
2503
  webSearchAllowed: options.webSearchAllowed
2420
2504
  }).threadConfig;
2421
- const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(config, webSearchConfig), options);
2422
- const runtimeConfig = mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0, params.delegationCapability === "report_only" ? CODEX_DELEGATION_DISABLED_THREAD_CONFIG : void 0, buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, ringZeroMcpServerNames)) ?? baseConfig;
2423
- if (params.bootstrapContextMode !== "lightweight") return runtimeConfig;
2424
- return mergeCodexThreadConfigs(runtimeConfig, CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG) ?? {
2425
- ...runtimeConfig,
2426
- ...CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG
2427
- };
2505
+ const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(restrictedRunConfig, webSearchConfig), options);
2506
+ return applyCodexManagedShellEnvironment({
2507
+ ...mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") ? { "features.image_generation": false } : void 0, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0, params.delegationCapability === "report_only" ? CODEX_DELEGATION_DISABLED_THREAD_CONFIG : void 0, messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true ? buildRestrictedToolConfigPatch(restrictedToolSurfaceMcpServerNames) : buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, restrictedToolSurfaceMcpServerNames), restrictedTurnDisablesProjectDocs ? CODEX_NO_PROJECT_DOCS_CONFIG : void 0, params.authoredContextTokenCap === void 0 ? void 0 : { model_context_window: params.authoredContextTokenCap }) ?? baseConfig,
2508
+ ...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
2509
+ }, options.shellEnvironment, options.disableLoginShell);
2428
2510
  }
2429
2511
  function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
2430
2512
  if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
2513
+ return {
2514
+ ...buildRestrictedToolConfigPatch(inheritedMcpServerNames),
2515
+ ...CODEX_NO_PROJECT_DOCS_CONFIG
2516
+ };
2517
+ }
2518
+ function buildRestrictedToolConfigPatch(inheritedMcpServerNames) {
2431
2519
  const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
2432
2520
  return {
2433
2521
  ...CODEX_RING_ZERO_THREAD_CONFIG,
@@ -2443,20 +2531,20 @@ async function readCodexInheritedMcpServerNames(client, cwd, signal) {
2443
2531
  if (!Array.isArray(response.layers)) throw new Error("Codex config/read omitted effective config layers");
2444
2532
  for (const layer of response.layers) {
2445
2533
  if (!isJsonObject(layer) || !isJsonObject(layer.name) || typeof layer.name.type !== "string") throw new Error("Codex config/read returned invalid effective config layers");
2446
- if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm") throw new Error(`Codex ring-zero cannot override config layer ${layer.name.type}`);
2447
- if (!CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES.has(layer.name.type)) throw new Error(`Codex ring-zero does not recognize config layer ${layer.name.type}`);
2534
+ if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm") throw new Error(`Codex restricted tool surface cannot override config layer ${layer.name.type}`);
2535
+ if (!CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES.has(layer.name.type)) throw new Error(`Codex restricted tool surface does not recognize config layer ${layer.name.type}`);
2448
2536
  }
2449
2537
  const configuredServers = response.config.mcp_servers;
2450
2538
  if (configuredServers === void 0) return [];
2451
2539
  if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
2452
2540
  return Object.keys(configuredServers).toSorted();
2453
2541
  }
2454
- async function assertCodexRingZeroHasNoManagedHooks(client, signal) {
2542
+ async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, options, signal) {
2455
2543
  const response = await client.request("configRequirements/read", void 0, { signal });
2456
2544
  if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
2457
2545
  if (response.requirements === null) return;
2458
2546
  if (!isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
2459
- for (const key of [
2547
+ if (options.restrictedToolSurface) for (const key of [
2460
2548
  "hooks",
2461
2549
  "managedHooks",
2462
2550
  "managed_hooks"
@@ -2464,30 +2552,45 @@ async function assertCodexRingZeroHasNoManagedHooks(client, signal) {
2464
2552
  const hooks = response.requirements[key];
2465
2553
  if (hooks === void 0 || hooks === null) continue;
2466
2554
  if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
2467
- if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex ring-zero cannot override managed hooks");
2555
+ if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex restricted tool surface cannot override managed hooks");
2468
2556
  }
2557
+ const additionalDeniedFeatures = new Set(options.additionalDeniedFeatures);
2469
2558
  for (const key of ["featureRequirements", "feature_requirements"]) {
2470
2559
  const requirements = response.requirements[key];
2471
2560
  if (requirements === void 0 || requirements === null) continue;
2472
2561
  if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid feature requirements");
2473
2562
  for (const [feature, enabled] of Object.entries(requirements)) {
2474
2563
  if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
2475
- if (enabled && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(feature)) throw new Error(`Codex ring-zero cannot override required feature ${feature}`);
2564
+ const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
2565
+ const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
2566
+ if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
2476
2567
  }
2477
2568
  }
2478
2569
  }
2479
- async function attestCodexRingZeroThreadHasNoMcpServers(client, threadId, signal) {
2570
+ async function attestCodexRestrictedToolSurfaceMcpServersDisabled(client, threadId, threadConfig, signal) {
2571
+ const configuredServers = threadConfig?.mcp_servers;
2572
+ if (configuredServers !== void 0 && !isJsonObject(configuredServers)) throw new Error("Codex restricted-tool-surface thread config has invalid mcp_servers");
2573
+ const expectedDisabledServerNames = /* @__PURE__ */ new Set();
2574
+ for (const [name, serverConfig] of Object.entries(configuredServers ?? {})) {
2575
+ if (!isJsonObject(serverConfig) || serverConfig.enabled !== false) throw new Error(`Codex restricted-tool-surface MCP server ${name} is not disabled`);
2576
+ expectedDisabledServerNames.add(name);
2577
+ }
2480
2578
  const response = await client.request("mcpServerStatus/list", {
2481
2579
  threadId,
2482
- limit: 1,
2483
2580
  detail: "toolsAndAuthOnly"
2484
2581
  }, { signal });
2485
- if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned an invalid ring-zero attestation");
2486
- if (response.data.length > 0) {
2487
- const first = response.data[0];
2488
- const serverName = isJsonObject(first) && typeof first.name === "string" ? first.name : "unknown";
2489
- throw new Error(`Codex ring-zero MCP attestation found server ${serverName}`);
2582
+ if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned an invalid restricted-tool-surface attestation");
2583
+ const observedDisabledServerNames = /* @__PURE__ */ new Set();
2584
+ for (const status of response.data) {
2585
+ if (!isJsonObject(status) || typeof status.name !== "string" || !isJsonObject(status.tools)) throw new Error("Codex mcpServerStatus/list returned an invalid restricted-tool-surface server");
2586
+ if (!expectedDisabledServerNames.has(status.name)) throw new Error(`Codex restricted-tool-surface MCP attestation found unexpected server ${status.name}`);
2587
+ if (observedDisabledServerNames.has(status.name)) throw new Error(`Codex restricted-tool-surface MCP attestation returned duplicate server ${status.name}`);
2588
+ observedDisabledServerNames.add(status.name);
2589
+ if (!Object.hasOwn(status, "serverInfo")) throw new Error(`Codex restricted-tool-surface MCP attestation returned malformed server ${status.name}`);
2590
+ if (status.serverInfo !== null) throw new Error(`Codex restricted-tool-surface MCP attestation found active server ${status.name}`);
2591
+ if (Object.keys(status.tools).length > 0) throw new Error(`Codex restricted-tool-surface MCP attestation found tools for server ${status.name}`);
2490
2592
  }
2593
+ for (const expectedName of expectedDisabledServerNames) if (!observedDisabledServerNames.has(expectedName)) throw new Error(`Codex restricted-tool-surface MCP attestation is missing server ${expectedName}`);
2491
2594
  if (response.nextCursor !== void 0 && response.nextCursor !== null) throw new Error("Codex mcpServerStatus/list returned an invalid empty-page cursor");
2492
2595
  }
2493
2596
  function hasNonEmptyJsonValue(value) {
@@ -2509,40 +2612,23 @@ function resolveCodexThreadEnvironmentSelection(options) {
2509
2612
  return {};
2510
2613
  }
2511
2614
  //#endregion
2512
- //#region extensions/codex/src/app-server/thread-resume.ts
2513
- /** Owns Codex thread/resume subscription safety. */
2514
- /** Resumes one thread and retires the physical client when acceptance is indeterminate. */
2515
- async function resumeCodexAppServerThread(params) {
2516
- const threadId = params.request.threadId;
2517
- let response;
2518
- try {
2519
- response = assertCodexThreadResumeResponse(await params.client.request("thread/resume", params.request, {
2520
- ...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
2521
- ...params.signal ? { signal: params.signal } : {}
2522
- }));
2523
- assertCodexThreadResumeSubscription(threadId, response.thread.id);
2524
- } catch (error) {
2525
- if (isCodexAppServerStartSelectionChangedError(error) || isCodexAppServerPrewriteRequestCancellationError(error)) throw error;
2526
- if (error instanceof CodexAppServerRpcError) throw error;
2527
- try {
2528
- await params.abandonClient();
2529
- } catch (abandonError) {
2530
- throw new CodexAppServerUnsafeSubscriptionError(`Codex thread/resume client could not be retired for ${threadId}`, { cause: abandonError });
2531
- }
2532
- if (error instanceof CodexAppServerUnsafeSubscriptionError) throw error;
2533
- throw new CodexAppServerUnsafeSubscriptionError(error instanceof Error ? error.message : `Codex thread/resume outcome is indeterminate for ${threadId}`, { cause: error });
2534
- }
2535
- return response;
2536
- }
2537
- //#endregion
2538
2615
  //#region extensions/codex/src/app-server/thread-lifecycle-io.ts
2616
+ function resolveManagedThreadSourceHomeId(params) {
2617
+ const agentId = resolveSessionAgentIds$1({
2618
+ config: params.params.config,
2619
+ sessionKey: params.params.sessionKey,
2620
+ agentId: params.agentId ?? params.params.agentId
2621
+ }).sessionAgentId;
2622
+ const agentDir = params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
2623
+ return codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir));
2624
+ }
2539
2625
  function resolveCodexThreadRolloutPath(thread) {
2540
2626
  const rolloutPath = thread.path?.trim();
2541
2627
  if (!rolloutPath || !path.isAbsolute(rolloutPath) || path.extname(rolloutPath) !== ".jsonl" || !path.basename(rolloutPath).includes(thread.id)) return;
2542
2628
  return rolloutPath;
2543
2629
  }
2544
2630
  async function resumeExistingCodexThread(params, context) {
2545
- const { binding: resumeBinding, bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive, ringZeroInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, clearCurrentBinding } = context;
2631
+ const { binding: resumeBinding, bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, clearCurrentBinding } = context;
2546
2632
  let resumeReservation;
2547
2633
  try {
2548
2634
  const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
@@ -2553,10 +2639,11 @@ async function resumeExistingCodexThread(params, context) {
2553
2639
  configPatch: params.finalConfigPatch,
2554
2640
  nativeHookRelayGeneration: params.nativeHookRelayGeneration
2555
2641
  };
2556
- const pluginAppsConfigPatch = params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0;
2642
+ const pluginAppsConfigPatch = context.prebuiltPluginThreadConfig?.configPatch ?? (params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0);
2557
2643
  const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
2558
2644
  const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
2559
2645
  threadId: resumeBinding.threadId,
2646
+ cwd: params.cwd,
2560
2647
  authProfileId,
2561
2648
  model: startModelSelection.model,
2562
2649
  modelProvider: startModelProvider,
@@ -2570,7 +2657,9 @@ async function resumeExistingCodexThread(params, context) {
2570
2657
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
2571
2658
  webSearchAllowed: params.webSearchAllowed,
2572
2659
  hostSystemAgentActive,
2573
- ringZeroInheritedMcpServerNames
2660
+ restrictedToolSurfaceInheritedMcpServerNames,
2661
+ shellEnvironment: params.shellEnvironment,
2662
+ disableLoginShell: params.disableLoginShell
2574
2663
  }));
2575
2664
  const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
2576
2665
  throwIfAborted();
@@ -2589,16 +2678,17 @@ async function resumeExistingCodexThread(params, context) {
2589
2678
  request: resumeParams,
2590
2679
  signal: params.signal
2591
2680
  }));
2592
- if (ringZeroActive) try {
2593
- await lifecycleTiming.measure("ring-zero-mcp-attestation", () => attestCodexRingZeroThreadHasNoMcpServers(params.client, response.thread.id, params.signal));
2681
+ if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
2682
+ await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, resumeParams.config, params.signal));
2594
2683
  } catch (error) {
2595
2684
  await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
2596
- throw new CodexRingZeroAttestationError(error);
2685
+ throw new CodexRestrictedToolSurfaceAttestationError(error);
2597
2686
  }
2598
2687
  throwIfAborted();
2599
2688
  const boundAuthProfileId = authProfileId;
2600
2689
  const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
2601
2690
  const resumePatch = {
2691
+ clientId: resolveCodexAppServerClientInstanceId(params.client),
2602
2692
  cwd: params.cwd,
2603
2693
  rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
2604
2694
  authProfileId: boundAuthProfileId,
@@ -2611,15 +2701,17 @@ async function resumeExistingCodexThread(params, context) {
2611
2701
  nativeSkillIsolationFingerprint,
2612
2702
  userMcpServersFingerprint,
2613
2703
  mcpServersFingerprint: nextMcpServersFingerprint,
2704
+ configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
2614
2705
  ringZeroConfigFingerprint,
2615
2706
  ringZeroClientInstanceId,
2707
+ nativeToolPolicyRestricted: restrictedToolSurface ? true : void 0,
2616
2708
  networkProxyProfileName: params.appServer.networkProxy?.profileName,
2617
2709
  networkProxyConfigFingerprint,
2618
2710
  nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
2619
2711
  appServerRuntimeFingerprint: resumeBinding.connectionScope === "supervision" ? buildCodexAppServerConnectionFingerprint(params.appServer, params.params.agentDir) : params.appServerRuntimeFingerprint,
2620
- pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
2621
- pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
2622
- pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
2712
+ pluginAppsFingerprint: context.prebuiltPluginThreadConfig?.fingerprint ?? resumeBinding.pluginAppsFingerprint,
2713
+ pluginAppsInputFingerprint: context.prebuiltPluginThreadConfig?.inputFingerprint ?? resumeBinding.pluginAppsInputFingerprint,
2714
+ pluginAppPolicyContext: context.prebuiltPluginThreadConfig?.policyContext ?? resumeBinding.pluginAppPolicyContext,
2623
2715
  contextEngine: contextEngineBinding,
2624
2716
  environmentSelectionFingerprint
2625
2717
  };
@@ -2665,8 +2757,8 @@ async function resumeExistingCodexThread(params, context) {
2665
2757
  } catch (error) {
2666
2758
  resumeReservation?.release();
2667
2759
  if (isCodexAppServerStartSelectionChangedError(error)) throw error;
2668
- if (error instanceof CodexRingZeroAttestationError) {
2669
- await clearCurrentBinding("retiring a failed ring-zero thread attestation");
2760
+ if (error instanceof CodexRestrictedToolSurfaceAttestationError) {
2761
+ await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
2670
2762
  throw error;
2671
2763
  }
2672
2764
  if (error instanceof CodexAdoptedThreadActiveError) throw error;
@@ -2683,7 +2775,7 @@ async function resumeExistingCodexThread(params, context) {
2683
2775
  }
2684
2776
  async function startFreshCodexThread(params, context) {
2685
2777
  const clientId = resolveCodexAppServerClientInstanceId(params.client);
2686
- const { bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive, ringZeroInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, prebuiltPluginThreadConfig, preserveExistingBinding, rotatedContextEngineBinding } = context;
2778
+ const { bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, prebuiltPluginThreadConfig, preserveExistingBinding, rotatedContextEngineBinding, replacementPredecessor } = context;
2687
2779
  const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
2688
2780
  const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
2689
2781
  configPatch: params.finalConfigPatch,
@@ -2704,7 +2796,9 @@ async function startFreshCodexThread(params, context) {
2704
2796
  model: startModelSelection.model,
2705
2797
  modelProvider: startModelProvider,
2706
2798
  hostSystemAgentActive,
2707
- ringZeroInheritedMcpServerNames
2799
+ restrictedToolSurfaceInheritedMcpServerNames,
2800
+ shellEnvironment: params.shellEnvironment,
2801
+ disableLoginShell: params.disableLoginShell
2708
2802
  }));
2709
2803
  const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
2710
2804
  const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
@@ -2735,13 +2829,27 @@ async function startFreshCodexThread(params, context) {
2735
2829
  throw error;
2736
2830
  }
2737
2831
  const rolloutPath = resolveCodexThreadRolloutPath(response.thread);
2738
- if (ringZeroActive) try {
2739
- await lifecycleTiming.measure("ring-zero-mcp-attestation", () => attestCodexRingZeroThreadHasNoMcpServers(params.client, response.thread.id, params.signal));
2832
+ if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
2833
+ await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, startParams.config, params.signal));
2740
2834
  } catch (error) {
2741
2835
  await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
2742
2836
  throw error;
2743
2837
  }
2744
- throwIfAborted();
2838
+ try {
2839
+ throwIfAborted();
2840
+ } catch (error) {
2841
+ if (replacementPredecessor) {
2842
+ if (!await discardUnattestedCodexPluginThread({
2843
+ client: params.client,
2844
+ threadId: response.thread.id,
2845
+ ephemeral: startParams.ephemeral === true
2846
+ })) {
2847
+ await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
2848
+ throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after an aborted binding replacement", { cause: error });
2849
+ }
2850
+ }
2851
+ throw error;
2852
+ }
2745
2853
  const modelProvider = resolveCodexAppServerModelProvider({
2746
2854
  provider: params.params.provider,
2747
2855
  authProfileId: params.params.authProfileId,
@@ -2752,36 +2860,70 @@ async function startFreshCodexThread(params, context) {
2752
2860
  const bindingModelProvider = normalizeBindingModelProvider(params.params.authProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider);
2753
2861
  const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
2754
2862
  if (!preserveExistingBinding) {
2755
- if (!await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
2756
- kind: "set",
2757
- if: { kind: "absent" },
2758
- binding: {
2863
+ const nextBinding = {
2864
+ threadId: response.thread.id,
2865
+ ...clientId ? { clientId } : {},
2866
+ cwd: params.cwd,
2867
+ ...rolloutPath ? { rolloutPath } : {},
2868
+ authProfileId: params.params.authProfileId,
2869
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
2870
+ model: response.model ?? startParams.model ?? params.params.modelId,
2871
+ modelProvider: bindingModelProvider,
2872
+ dynamicToolsFingerprint,
2873
+ dynamicToolsContainDeferred,
2874
+ webSearchThreadConfigFingerprint,
2875
+ nativeSkillIsolationFingerprint,
2876
+ userMcpServersFingerprint,
2877
+ mcpServersFingerprint: nextMcpServersFingerprint,
2878
+ configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
2879
+ ringZeroConfigFingerprint,
2880
+ ringZeroClientInstanceId,
2881
+ nativeToolPolicyRestricted: restrictedToolSurface ? true : void 0,
2882
+ networkProxyProfileName: params.appServer.networkProxy?.profileName,
2883
+ networkProxyConfigFingerprint,
2884
+ nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
2885
+ appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
2886
+ pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
2887
+ pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
2888
+ pluginAppPolicyContext: pluginThreadConfig?.policyContext,
2889
+ contextEngine: contextEngineBinding,
2890
+ environmentSelectionFingerprint
2891
+ };
2892
+ const cleanupUncommittedSuccessor = async (cause) => {
2893
+ if (!await discardUnattestedCodexPluginThread({
2894
+ client: params.client,
2759
2895
  threadId: response.thread.id,
2760
- ...clientId ? { clientId } : {},
2761
- cwd: params.cwd,
2762
- ...rolloutPath ? { rolloutPath } : {},
2763
- authProfileId: params.params.authProfileId,
2764
- model: response.model ?? startParams.model ?? params.params.modelId,
2765
- modelProvider: bindingModelProvider,
2766
- dynamicToolsFingerprint,
2767
- dynamicToolsContainDeferred,
2768
- webSearchThreadConfigFingerprint,
2769
- nativeSkillIsolationFingerprint,
2770
- userMcpServersFingerprint,
2771
- mcpServersFingerprint: nextMcpServersFingerprint,
2772
- ringZeroConfigFingerprint,
2773
- ringZeroClientInstanceId,
2774
- networkProxyProfileName: params.appServer.networkProxy?.profileName,
2775
- networkProxyConfigFingerprint,
2776
- nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
2777
- appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
2778
- pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
2779
- pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
2780
- pluginAppPolicyContext: pluginThreadConfig?.policyContext,
2781
- contextEngine: contextEngineBinding,
2782
- environmentSelectionFingerprint
2896
+ ephemeral: startParams.ephemeral === true
2897
+ })) {
2898
+ await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
2899
+ throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after a binding replacement conflict", cause === void 0 ? void 0 : { cause });
2783
2900
  }
2784
- }))) throw new CodexThreadBindingConflictError(response.thread.id, "committing a fresh thread");
2901
+ };
2902
+ const managedSourceHomeId = resolveManagedThreadSourceHomeId(params);
2903
+ await lifecycleTiming.measure("thread-start-mark-managed", () => markStartedCodexManagedThread(params.bindingStore.managedThreads, {
2904
+ sourceHomeId: managedSourceHomeId,
2905
+ threadId: response.thread.id,
2906
+ ...rolloutPath ? { rolloutPath } : {}
2907
+ }));
2908
+ let committed;
2909
+ try {
2910
+ committed = await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, replacementPredecessor ? {
2911
+ kind: "replace-thread",
2912
+ expectedThreadId: replacementPredecessor.threadId,
2913
+ binding: nextBinding
2914
+ } : {
2915
+ kind: "set",
2916
+ if: { kind: "absent" },
2917
+ binding: nextBinding
2918
+ }));
2919
+ } catch (error) {
2920
+ if (replacementPredecessor) await cleanupUncommittedSuccessor(error);
2921
+ throw error;
2922
+ }
2923
+ if (!committed) {
2924
+ if (replacementPredecessor) await cleanupUncommittedSuccessor();
2925
+ throw new CodexThreadBindingConflictError(replacementPredecessor?.threadId ?? response.thread.id, "committing a fresh thread");
2926
+ }
2785
2927
  if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
2786
2928
  sessionId: params.params.sessionId,
2787
2929
  sessionKey: params.params.sessionKey,
@@ -2800,42 +2942,20 @@ async function startFreshCodexThread(params, context) {
2800
2942
  threadId: response.thread.id,
2801
2943
  action: rotatedContextEngineBinding ? "rotated" : "started"
2802
2944
  });
2803
- return {
2804
- threadId: response.thread.id,
2805
- ...clientId ? { clientId } : {},
2806
- cwd: params.cwd,
2807
- ...rolloutPath ? { rolloutPath } : {},
2808
- authProfileId: params.params.authProfileId,
2809
- model: response.model ?? startParams.model ?? params.params.modelId,
2810
- modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
2811
- dynamicToolsFingerprint,
2812
- dynamicToolsContainDeferred,
2813
- nativeSkillIsolationFingerprint,
2814
- userMcpServersFingerprint,
2815
- mcpServersFingerprint: nextMcpServersFingerprint,
2816
- ringZeroConfigFingerprint,
2817
- ringZeroClientInstanceId,
2818
- networkProxyProfileName: params.appServer.networkProxy?.profileName,
2819
- networkProxyConfigFingerprint,
2820
- nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
2821
- appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
2822
- pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
2823
- pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
2824
- pluginAppPolicyContext: pluginThreadConfig?.policyContext,
2825
- contextEngine: contextEngineBinding,
2826
- environmentSelectionFingerprint,
2827
- ...!preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
2828
- ...startParams,
2829
- model: response.model ?? startParams.model ?? null,
2830
- requestedModel: startParams.model ?? null,
2831
- modelProvider: bindingModelProvider ?? null,
2832
- requestedModelProvider: startParams.modelProvider ?? bindingModelProvider ?? null
2833
- }, params.params.authProfileId, dynamicToolsFingerprint) } : {},
2834
- lifecycle: {
2835
- action: "started",
2836
- ...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
2837
- }
2838
- };
2945
+ return buildStartedCodexThreadBinding({
2946
+ bindingModelProvider,
2947
+ clientId,
2948
+ context,
2949
+ finalConfigPatch,
2950
+ nextMcpServersFingerprint,
2951
+ params,
2952
+ pluginThreadConfig,
2953
+ response,
2954
+ rolloutPath,
2955
+ startModelProvider: requestModelProvider ?? startModelProvider,
2956
+ startParams,
2957
+ modelProvider
2958
+ });
2839
2959
  }
2840
2960
  //#endregion
2841
2961
  //#region extensions/codex/src/app-server/profiler-flag.ts
@@ -2979,13 +3099,19 @@ async function prepareCodexThreadLifecyclePreflight(params) {
2979
3099
  const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
2980
3100
  const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
2981
3101
  const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
2982
- if (ringZeroActive && params.nativeCodeModeEnabled !== false) throw new Error("Codex ring-zero requires native code mode to be disabled");
2983
- const ringZeroInheritedMcpServerNames = ringZeroActive ? await lifecycleTiming.measure("ring-zero-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
2984
- if (ringZeroActive) await lifecycleTiming.measure("ring-zero-config-requirements-read", () => assertCodexRingZeroHasNoManagedHooks(params.client, params.signal));
3102
+ const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
3103
+ const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
3104
+ const imageGenerationDenied = params.params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") === true;
3105
+ if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
3106
+ const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
3107
+ if (restrictedToolSurface || imageGenerationDenied) await lifecycleTiming.measure("tool-policy-config-requirements-read", () => assertCodexManagedRequirementsDoNotOverrideToolPolicy(params.client, {
3108
+ restrictedToolSurface,
3109
+ additionalDeniedFeatures: imageGenerationDenied ? ["image_generation"] : void 0
3110
+ }, params.signal));
2985
3111
  const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
2986
3112
  version: 1,
2987
3113
  baseInstructions: "",
2988
- config: buildCodexRingZeroThreadConfigPatch(params.params, true, ringZeroInheritedMcpServerNames)
3114
+ config: buildCodexRingZeroThreadConfigPatch(params.params, true, restrictedToolSurfaceInheritedMcpServerNames)
2989
3115
  }) : void 0;
2990
3116
  return {
2991
3117
  contextEngineBinding,
@@ -3002,7 +3128,8 @@ async function prepareCodexThreadLifecyclePreflight(params) {
3002
3128
  ringZeroActive,
3003
3129
  ringZeroClientInstanceId: ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0,
3004
3130
  ringZeroConfigFingerprint,
3005
- ringZeroInheritedMcpServerNames,
3131
+ restrictedToolSurface,
3132
+ restrictedToolSurfaceInheritedMcpServerNames,
3006
3133
  userMcpServersConfigPatch,
3007
3134
  userMcpServersFingerprint,
3008
3135
  webSearchThreadConfigFingerprint
@@ -3034,8 +3161,8 @@ async function releaseCodexRetainedLiveThread(options) {
3034
3161
  }
3035
3162
  /** Reuses one safely owned, fully matching subscription on its original client. */
3036
3163
  async function tryReuseCodexLiveThread(options) {
3037
- const { params, binding, bindingIdentity, clientId, contextEngineBinding, dynamicToolsFingerprint, hostSystemAgentActive, lifecycleTiming, nativeSkillIsolation, releaseConsumedThread, ringZeroActive, ringZeroInheritedMcpServerNames, startModelProvider, startModelSelection, throwIfAborted, userMcpServersConfigPatch } = options;
3038
- if (!binding.clientId || binding.clientId !== clientId || binding.preserveNativeModel === true || binding.connectionScope === "supervision" || ringZeroActive || contextEngineBinding) return {};
3164
+ const { params, binding, bindingIdentity, clientId, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, lifecycleTiming, nativeSkillIsolation, releaseConsumedThread, ringZeroActive, restrictedToolSurfaceInheritedMcpServerNames, startModelProvider, startModelSelection, throwIfAborted, userMcpServersConfigPatch } = options;
3165
+ if (!binding.clientId || binding.clientId !== clientId || binding.preserveNativeModel === true || binding.connectionScope === "supervision" || ringZeroActive) return {};
3039
3166
  const prebuiltFinalConfigPatch = params.buildFinalConfigPatch?.({
3040
3167
  action: "resume",
3041
3168
  binding
@@ -3048,6 +3175,7 @@ async function tryReuseCodexLiveThread(options) {
3048
3175
  const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
3049
3176
  const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
3050
3177
  threadId: binding.threadId,
3178
+ cwd: params.cwd,
3051
3179
  authProfileId: resumeAuthProfileId,
3052
3180
  model: startModelSelection.model,
3053
3181
  modelProvider: startModelProvider,
@@ -3061,7 +3189,9 @@ async function tryReuseCodexLiveThread(options) {
3061
3189
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
3062
3190
  webSearchAllowed: params.webSearchAllowed,
3063
3191
  hostSystemAgentActive,
3064
- ringZeroInheritedMcpServerNames
3192
+ restrictedToolSurfaceInheritedMcpServerNames,
3193
+ shellEnvironment: params.shellEnvironment,
3194
+ disableLoginShell: params.disableLoginShell
3065
3195
  }));
3066
3196
  const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
3067
3197
  ...resumeParams,
@@ -3070,13 +3200,24 @@ async function tryReuseCodexLiveThread(options) {
3070
3200
  modelProvider: binding.modelProvider ?? resumeParams.modelProvider ?? null,
3071
3201
  requestedModelProvider: resumeParams.modelProvider ?? binding.modelProvider ?? null
3072
3202
  }, resumeAuthProfileId, dynamicToolsFingerprint);
3073
- if (!await consumeCodexAppServerLiveThread(params.client, binding.threadId, liveThreadConfigFingerprint)) return { prebuiltFinalConfigPatch };
3203
+ const retainedThread = await consumeCodexAppServerLiveThread(params.client, binding.threadId, liveThreadConfigFingerprint);
3204
+ if (!retainedThread) {
3205
+ const incompatibleOwnership = await consumeCodexAppServerLiveThread(params.client, binding.threadId);
3206
+ if (incompatibleOwnership) await incompatibleOwnership.release(binding.threadId);
3207
+ return { prebuiltFinalConfigPatch };
3208
+ }
3074
3209
  try {
3075
3210
  const nativeHookRelayGeneration = prebuiltFinalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration;
3211
+ const model = startModelSelection.model;
3076
3212
  if (!await lifecycleTiming.measure("warm-thread-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
3077
3213
  kind: "patch",
3078
3214
  threadId: binding.threadId,
3079
- patch: { nativeHookRelayGeneration }
3215
+ patch: {
3216
+ cwd: params.cwd,
3217
+ model,
3218
+ nativeHookRelayGeneration,
3219
+ environmentSelectionFingerprint
3220
+ }
3080
3221
  }))) throw new CodexThreadBindingConflictError(binding.threadId, "committing a reused thread");
3081
3222
  throwIfAborted();
3082
3223
  lifecycleTiming.mark("thread-ready");
@@ -3090,8 +3231,13 @@ async function tryReuseCodexLiveThread(options) {
3090
3231
  return {
3091
3232
  binding: {
3092
3233
  ...binding,
3234
+ cwd: params.cwd,
3235
+ model,
3093
3236
  nativeHookRelayGeneration,
3237
+ environmentSelectionFingerprint,
3094
3238
  liveThreadConfigFingerprint,
3239
+ liveThreadOwnership: retainedThread,
3240
+ ...retainedThread.serviceTier && resumeParams.serviceTier === void 0 ? { clearInheritedServiceTier: true } : {},
3095
3241
  lifecycle: { action: "resumed" }
3096
3242
  },
3097
3243
  prebuiltFinalConfigPatch
@@ -3143,6 +3289,7 @@ async function materializePendingSupervisionBranch(params) {
3143
3289
  pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId]);
3144
3290
  params.throwIfAborted();
3145
3291
  const probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
3292
+ if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
3146
3293
  const nativeModel = requireNonBlankSupervisionValue(probeResponse.model, "native model");
3147
3294
  const nativeModelProvider = requireNativeSupervisionModelProvider({
3148
3295
  responseModelProvider: probeResponse.modelProvider,
@@ -3163,7 +3310,11 @@ async function materializePendingSupervisionBranch(params) {
3163
3310
  webSearchAllowed: params.webSearchAllowed,
3164
3311
  environmentSelection: params.environmentSelection,
3165
3312
  model: nativeModel,
3166
- modelProvider: nativeModelProvider
3313
+ modelProvider: nativeModelProvider,
3314
+ hostSystemAgentActive: params.hostSystemAgentActive,
3315
+ restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
3316
+ shellEnvironment: params.shellEnvironment,
3317
+ disableLoginShell: params.disableLoginShell
3167
3318
  });
3168
3319
  assertExactSupervisionModelSelection(startParams, {
3169
3320
  model: nativeModel,
@@ -3191,6 +3342,7 @@ async function materializePendingSupervisionBranch(params) {
3191
3342
  modelProvider: nativeModelProvider,
3192
3343
  operation: "thread/start response"
3193
3344
  });
3345
+ if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, finalThreadId, startParams.config, params.signal));
3194
3346
  if (params.provisionalAppIds?.length) try {
3195
3347
  await params.lifecycleTiming.measure("plugin-app-attestation", () => attestCodexPluginThreadApps({
3196
3348
  client: params.client,
@@ -3316,7 +3468,11 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
3316
3468
  nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
3317
3469
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
3318
3470
  webSearchAllowed: params.webSearchAllowed,
3319
- appServer: params.appServer
3471
+ appServer: params.appServer,
3472
+ hostSystemAgentActive: params.hostSystemAgentActive,
3473
+ restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames,
3474
+ shellEnvironment: params.shellEnvironment,
3475
+ disableLoginShell: params.disableLoginShell
3320
3476
  });
3321
3477
  return {
3322
3478
  threadId: pending.sourceThreadId,
@@ -3453,9 +3609,11 @@ async function startOrResumeThread(params) {
3453
3609
  config: params.params.config
3454
3610
  });
3455
3611
  return await params.bindingStore.withLease(bindingIdentity, async () => {
3456
- const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, ringZeroInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = await prepareCodexThreadLifecyclePreflight(params);
3612
+ const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = await prepareCodexThreadLifecyclePreflight(params);
3457
3613
  let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
3614
+ let replacementPredecessor;
3458
3615
  const initialBoundThreadId = binding?.threadId;
3616
+ const initialBoundClientId = binding?.clientId;
3459
3617
  const normalizeBindingModelProvider = (authProfileId, modelProvider) => normalizeCodexAppServerBindingModelProvider({
3460
3618
  authProfileId,
3461
3619
  modelProvider,
@@ -3464,12 +3622,28 @@ async function startOrResumeThread(params) {
3464
3622
  config: params.params.config
3465
3623
  });
3466
3624
  const throwIfAborted = () => throwIfCodexThreadLifecycleAborted(params.signal);
3467
- const releaseRetainedThread = (threadId) => releaseCodexRetainedLiveThread({
3468
- client: params.client,
3469
- abandonClient: params.abandonClient,
3470
- lifecycleTiming,
3471
- threadId
3472
- });
3625
+ const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId) => {
3626
+ if (ownerClientId && ownerClientId !== clientId) {
3627
+ const previousClient = retainSharedCodexAppServerClientByInstanceId(ownerClientId);
3628
+ if (!previousClient) return;
3629
+ try {
3630
+ await releaseCodexRetainedLiveThread({
3631
+ client: previousClient.client,
3632
+ lifecycleTiming,
3633
+ threadId
3634
+ });
3635
+ } finally {
3636
+ previousClient.release();
3637
+ }
3638
+ return;
3639
+ }
3640
+ await releaseCodexRetainedLiveThread({
3641
+ client: params.client,
3642
+ abandonClient: params.abandonClient,
3643
+ lifecycleTiming,
3644
+ threadId
3645
+ });
3646
+ };
3473
3647
  if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
3474
3648
  if (!await lifecycleTiming.measure("reclaim-binding-generation", () => reclaimCurrentCodexSessionGeneration({
3475
3649
  bindingStore: params.bindingStore,
@@ -3502,6 +3676,11 @@ async function startOrResumeThread(params) {
3502
3676
  nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
3503
3677
  nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
3504
3678
  webSearchAllowed: params.webSearchAllowed,
3679
+ hostSystemAgentActive,
3680
+ restrictedToolSurface,
3681
+ restrictedToolSurfaceInheritedMcpServerNames,
3682
+ shellEnvironment: params.shellEnvironment,
3683
+ disableLoginShell: params.disableLoginShell,
3505
3684
  environmentSelection: params.environmentSelection,
3506
3685
  provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
3507
3686
  signal: params.signal,
@@ -3512,6 +3691,7 @@ async function startOrResumeThread(params) {
3512
3691
  cwd: params.cwd,
3513
3692
  ...clientId ? { clientId } : {},
3514
3693
  authProfileId: void 0,
3694
+ agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
3515
3695
  preserveNativeModel: true,
3516
3696
  dynamicToolsFingerprint,
3517
3697
  dynamicToolsContainDeferred,
@@ -3519,6 +3699,7 @@ async function startOrResumeThread(params) {
3519
3699
  nativeSkillIsolationFingerprint,
3520
3700
  userMcpServersFingerprint,
3521
3701
  mcpServersFingerprint: params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : pendingBinding.mcpServersFingerprint,
3702
+ configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
3522
3703
  networkProxyProfileName: params.appServer.networkProxy?.profileName,
3523
3704
  networkProxyConfigFingerprint,
3524
3705
  nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
@@ -3542,6 +3723,7 @@ async function startOrResumeThread(params) {
3542
3723
  })) throw new CodexThreadBindingConflictError(current.threadId, operation);
3543
3724
  binding = void 0;
3544
3725
  };
3726
+ if (binding?.threadId && !restrictedToolSurface && binding.nativeToolPolicyRestricted === true) await clearCurrentBinding("rotating a host-policy-restricted thread binding");
3545
3727
  if (binding?.threadId && binding.nativeSkillIsolationFingerprint !== nativeSkillIsolationFingerprint) {
3546
3728
  embeddedAgentLog.debug("codex app-server native skill isolation changed; starting a new thread", { threadId: binding.threadId });
3547
3729
  await clearCurrentBinding("rotating stale native skill isolation");
@@ -3576,7 +3758,7 @@ async function startOrResumeThread(params) {
3576
3758
  }
3577
3759
  const startModelSelection = resolveCodexAppServerThreadModelSelection({
3578
3760
  provider: params.params.provider,
3579
- model: params.params.modelId,
3761
+ model: params.runtimeModelId ?? params.params.modelId,
3580
3762
  binding,
3581
3763
  authProfileId: params.params.authProfileId,
3582
3764
  authProfileStore: params.params.authProfileStore,
@@ -3594,6 +3776,14 @@ async function startOrResumeThread(params) {
3594
3776
  const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
3595
3777
  const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
3596
3778
  const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
3779
+ if (binding?.threadId && (params.configuredMcpOwnershipVersion === 1 && (binding.configuredMcpOwnershipVersion !== 1 || binding.dynamicToolsFingerprint === void 0 || binding.mcpServersFingerprint !== void 0 || binding.userMcpServersFingerprint !== void 0) || params.configuredMcpOwnershipVersion !== 1 && binding.configuredMcpOwnershipVersion === 1) && binding?.threadId) {
3780
+ const predecessorBinding = binding;
3781
+ assertCodexBindingMayBeReplaced(predecessorBinding, "changing configured MCP ownership");
3782
+ embeddedAgentLog.debug("codex app-server configured MCP ownership changed; starting a new thread", { threadId: predecessorBinding.threadId });
3783
+ replacementPredecessor = predecessorBinding;
3784
+ binding = void 0;
3785
+ preserveExistingBinding = false;
3786
+ }
3597
3787
  if (binding?.threadId && params.mcpServersFingerprintEvaluated === true && binding.mcpServersFingerprint !== params.mcpServersFingerprint) {
3598
3788
  assertCodexBindingMayBeReplaced(binding, "changing MCP configuration");
3599
3789
  if (!ringZeroActive && (transientNativeToolRestriction || webSearchBindingChanged && (explicitTransientWebSearchRestriction || unknownProviderWebSearchSupport))) {
@@ -3609,7 +3799,7 @@ async function startOrResumeThread(params) {
3609
3799
  if (binding?.threadId && webSearchBindingChanged && !deferLegacyWebSearchRotationToTransientNativeSurface) {
3610
3800
  assertCodexBindingMayBeReplaced(binding, "changing web-search configuration");
3611
3801
  if (!ringZeroActive && transientWebSearchRestriction) {
3612
- embeddedAgentLog.debug("codex app-server web search restricted for turn; starting transient thread", { threadId: binding.threadId });
3802
+ embeddedAgentLog.debug("codex app-server tool surface restricted for turn; starting transient thread", { threadId: binding.threadId });
3613
3803
  preserveExistingBinding = true;
3614
3804
  } else {
3615
3805
  embeddedAgentLog.debug("codex app-server web search config changed; starting a new thread", { threadId: binding.threadId });
@@ -3655,11 +3845,6 @@ async function startOrResumeThread(params) {
3655
3845
  await clearCurrentBinding("rotating a stale thread binding");
3656
3846
  binding = void 0;
3657
3847
  }
3658
- if (binding?.threadId && binding.environmentSelectionFingerprint !== environmentSelectionFingerprint) {
3659
- embeddedAgentLog.debug("codex app-server environment selection changed; starting a new thread", { threadId: binding.threadId });
3660
- await clearCurrentBinding("rotating a stale thread binding");
3661
- binding = void 0;
3662
- }
3663
3848
  if (binding?.threadId && (binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint || binding.networkProxyProfileName !== params.appServer.networkProxy?.profileName)) {
3664
3849
  embeddedAgentLog.debug("codex app-server network proxy config changed; starting a new thread", { threadId: binding.threadId });
3665
3850
  await clearCurrentBinding("rotating a stale thread binding");
@@ -3673,13 +3858,15 @@ async function startOrResumeThread(params) {
3673
3858
  currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
3674
3859
  hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
3675
3860
  });
3676
- if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
3861
+ if (!pluginBindingStale && (params.pluginThreadConfig?.requiresCurrentPolicyCheck || shouldRecheckRecoverablePluginBinding({
3677
3862
  binding,
3678
3863
  pluginThreadConfig: params.pluginThreadConfig
3679
- })) try {
3680
- prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build());
3864
+ }))) try {
3865
+ const bindingThreadId = binding.threadId;
3866
+ prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build({ threadId: bindingThreadId }));
3681
3867
  pluginBindingStale = prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
3682
3868
  } catch (error) {
3869
+ if (params.pluginThreadConfig?.requiresCurrentPolicyCheck) throw error;
3683
3870
  embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
3684
3871
  error,
3685
3872
  threadId: binding.threadId
@@ -3737,8 +3924,8 @@ async function startOrResumeThread(params) {
3737
3924
  binding,
3738
3925
  bindingIdentity,
3739
3926
  clientId,
3740
- contextEngineBinding,
3741
3927
  dynamicToolsFingerprint,
3928
+ environmentSelectionFingerprint,
3742
3929
  hostSystemAgentActive,
3743
3930
  lifecycleTiming,
3744
3931
  nativeSkillIsolation,
@@ -3750,7 +3937,7 @@ async function startOrResumeThread(params) {
3750
3937
  cause
3751
3938
  }),
3752
3939
  ringZeroActive,
3753
- ringZeroInheritedMcpServerNames,
3940
+ restrictedToolSurfaceInheritedMcpServerNames,
3754
3941
  startModelProvider,
3755
3942
  startModelSelection,
3756
3943
  throwIfAborted,
@@ -3776,18 +3963,20 @@ async function startOrResumeThread(params) {
3776
3963
  environmentSelectionFingerprint,
3777
3964
  hostSystemAgentActive,
3778
3965
  ringZeroActive,
3779
- ringZeroInheritedMcpServerNames,
3966
+ restrictedToolSurface,
3967
+ restrictedToolSurfaceInheritedMcpServerNames,
3780
3968
  nativeSkillIsolation,
3781
3969
  lifecycleTiming,
3782
3970
  normalizeBindingModelProvider,
3783
3971
  throwIfAborted,
3784
3972
  clearCurrentBinding,
3785
- prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch
3973
+ prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
3974
+ prebuiltPluginThreadConfig
3786
3975
  });
3787
3976
  if (resumed) return resumed;
3788
3977
  }
3789
- if (initialBoundThreadId) await releaseRetainedThread(initialBoundThreadId);
3790
- return await startFreshCodexThread(params, {
3978
+ if (initialBoundThreadId && !preserveExistingBinding && !replacementPredecessor) await releaseRetainedThread(initialBoundThreadId);
3979
+ const started = await startFreshCodexThread(params, {
3791
3980
  bindingIdentity,
3792
3981
  startModelSelection,
3793
3982
  startModelProvider,
@@ -3804,85 +3993,27 @@ async function startOrResumeThread(params) {
3804
3993
  environmentSelectionFingerprint,
3805
3994
  hostSystemAgentActive,
3806
3995
  ringZeroActive,
3807
- ringZeroInheritedMcpServerNames,
3996
+ restrictedToolSurface,
3997
+ restrictedToolSurfaceInheritedMcpServerNames,
3808
3998
  nativeSkillIsolation,
3809
3999
  lifecycleTiming,
3810
4000
  normalizeBindingModelProvider,
3811
4001
  throwIfAborted,
3812
4002
  prebuiltPluginThreadConfig,
3813
4003
  preserveExistingBinding,
3814
- rotatedContextEngineBinding
4004
+ rotatedContextEngineBinding,
4005
+ replacementPredecessor
3815
4006
  });
4007
+ if (replacementPredecessor) await releaseRetainedThread(replacementPredecessor.threadId, replacementPredecessor.clientId);
4008
+ return started;
3816
4009
  });
3817
4010
  }
3818
4011
  //#endregion
3819
- //#region extensions/codex/src/app-server/image-payload-sanitizer.ts
3820
- /**
3821
- * Sanitizes inline image payloads mirrored through Codex history so invalid
3822
- * base64 data becomes readable text instead of poisoning replayed transcripts.
3823
- */
3824
- const IMAGE_OMITTED_TEXT = "omitted image payload: invalid inline image data";
3825
- /** Validates and normalizes an inline image data URL for Codex history payloads. */
3826
- function sanitizeInlineImageDataUrl$1(imageUrl) {
3827
- return sanitizeInlineImageDataUrl(imageUrl);
3828
- }
3829
- /** Builds the replacement text inserted when an inline image payload is invalid. */
3830
- function invalidInlineImageText(label) {
3831
- return `[${label}] ${IMAGE_OMITTED_TEXT}`;
3832
- }
3833
- function sanitizeImageContentRecord(record, label) {
3834
- if (record.type === "image" && typeof record.data === "string") {
3835
- const mimeType = typeof record.mimeType === "string" ? record.mimeType : "image/png";
3836
- const imageUrl = sanitizeInlineImageDataUrl$1(`data:${mimeType};base64,${record.data}`);
3837
- if (!imageUrl) return {
3838
- type: "text",
3839
- text: invalidInlineImageText(label)
3840
- };
3841
- const commaIndex = imageUrl.indexOf(",");
3842
- const mime = imageUrl.slice(INLINE_IMAGE_DATA_URL_PREFIX.length, commaIndex).split(";")[0] ?? mimeType;
3843
- return {
3844
- ...record,
3845
- mimeType: mime,
3846
- data: imageUrl.slice(commaIndex + 1)
3847
- };
3848
- }
3849
- if (record.type === "inputImage" && typeof record.imageUrl === "string") {
3850
- const imageUrl = sanitizeInlineImageDataUrl$1(record.imageUrl);
3851
- return imageUrl ? {
3852
- ...record,
3853
- imageUrl
3854
- } : {
3855
- type: "inputText",
3856
- text: invalidInlineImageText(label)
3857
- };
3858
- }
3859
- if (record.type === "input_image" && typeof record.image_url === "string") {
3860
- const imageUrl = sanitizeInlineImageDataUrl$1(record.image_url);
3861
- return imageUrl ? {
3862
- ...record,
3863
- image_url: imageUrl
3864
- } : {
3865
- type: "input_text",
3866
- text: invalidInlineImageText(label)
3867
- };
3868
- }
3869
- }
3870
- /** Recursively sanitizes all Codex history image shapes while preserving unknown structure. */
3871
- function sanitizeCodexHistoryImagePayloads(value, label) {
3872
- if (Array.isArray(value)) return value.map((entry) => sanitizeCodexHistoryImagePayloads(entry, label));
3873
- if (!isRecord(value)) return value;
3874
- const imageRecord = sanitizeImageContentRecord(value, label);
3875
- if (imageRecord) return imageRecord;
3876
- const next = {};
3877
- for (const [key, child] of Object.entries(value)) next[key] = sanitizeCodexHistoryImagePayloads(child, label);
3878
- return next;
3879
- }
3880
- //#endregion
3881
4012
  //#region extensions/codex/src/app-server/user-input.ts
3882
4013
  /** Builds ordered Codex user input for both new turns and same-turn steering. */
3883
4014
  function buildCodexUserInput(text, images) {
3884
4015
  const imageInputs = (images ?? []).map((image) => {
3885
- const imageUrl = sanitizeInlineImageDataUrl$1(`data:${image.mimeType};base64,${image.data}`);
4016
+ const imageUrl = sanitizeInlineImageDataUrl(`data:${image.mimeType};base64,${image.data}`);
3886
4017
  return imageUrl ? {
3887
4018
  type: "image",
3888
4019
  url: imageUrl
@@ -3900,6 +4031,27 @@ function buildCodexUserInput(text, images) {
3900
4031
  }
3901
4032
  //#endregion
3902
4033
  //#region extensions/codex/src/app-server/turn-params.ts
4034
+ const CODEX_CURRENT_SENDER_FIELD_MAX_CHARS = 256;
4035
+ function buildCodexCurrentSenderContextValue(params) {
4036
+ const metadata = asOptionalRecord(asOptionalRecord(params.userTurnTranscriptRecorder?.message)?.["__openclaw"]);
4037
+ const recorded = [
4038
+ normalizeOptionalString(metadata?.["senderId"]),
4039
+ normalizeOptionalString(metadata?.["senderName"]),
4040
+ normalizeOptionalString(metadata?.["senderUsername"])
4041
+ ];
4042
+ const [id, name, username] = recorded.some(Boolean) ? recorded : [
4043
+ normalizeOptionalString(params.senderId),
4044
+ normalizeOptionalString(params.senderName),
4045
+ normalizeOptionalString(params.senderUsername)
4046
+ ];
4047
+ if (!id && !name && !username) return;
4048
+ const bound = (value) => truncateUtf16Safe(value, CODEX_CURRENT_SENDER_FIELD_MAX_CHARS);
4049
+ return JSON.stringify({ sender: {
4050
+ ...id ? { id: bound(id) } : {},
4051
+ ...name ? { name: bound(name) } : {},
4052
+ ...username ? { username: bound(username) } : {}
4053
+ } });
4054
+ }
3903
4055
  function buildTurnStartParams(params, options) {
3904
4056
  const modelSelection = options.preserveNativeTurnSettings ? void 0 : resolveCodexAppServerRequestModelSelection({
3905
4057
  model: options.model ?? params.modelId,
@@ -3910,18 +4062,25 @@ function buildTurnStartParams(params, options) {
3910
4062
  config: params.config
3911
4063
  });
3912
4064
  const useThreadPermissionProfile = options.appServer.networkProxy && !options.sandboxPolicy;
4065
+ const currentSenderContext = params.trigger === "user" ? buildCodexCurrentSenderContextValue(params) : void 0;
4066
+ const additionalContext = currentSenderContext ? { openclaw_current_sender: {
4067
+ kind: "untrusted",
4068
+ value: currentSenderContext
4069
+ } } : void 0;
3913
4070
  return {
3914
4071
  threadId: options.threadId,
3915
- input: buildCodexUserInput(options.promptText ?? params.prompt, params.images),
4072
+ input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
4073
+ ...additionalContext ? { additionalContext } : {},
3916
4074
  cwd: options.cwd,
4075
+ ...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
3917
4076
  approvalPolicy: options.appServer.approvalPolicy,
3918
4077
  approvalsReviewer: options.appServer.approvalsReviewer,
3919
- ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd, options.appServer.start?.args) },
4078
+ ...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.appServer.sessionRoot ?? options.cwd, options.appServer.start?.args) },
3920
4079
  ...modelSelection ? {
3921
4080
  model: modelSelection.model,
3922
4081
  personality: CODEX_NATIVE_PERSONALITY_NONE
3923
4082
  } : {},
3924
- ...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
4083
+ ...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : options.clearInheritedServiceTier ? { serviceTier: null } : {},
3925
4084
  ...modelSelection ? { effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model, readCodexSupportedReasoningEfforts(params.model?.compat)) } : {},
3926
4085
  ...options.environmentSelection ? { environments: options.environmentSelection } : {},
3927
4086
  ...modelSelection ? { collaborationMode: buildTurnCollaborationMode(params, {
@@ -3946,7 +4105,7 @@ function buildTurnCollaborationMode(params, options = {}) {
3946
4105
  function buildTurnScopedCollaborationInstructions(params, options = {}) {
3947
4106
  const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
3948
4107
  if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
3949
- if (params.trigger === "heartbeat" && params.bootstrapContextRunKind !== "commitment-only") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
4108
+ if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
3950
4109
  if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
3951
4110
  return null;
3952
4111
  }
@@ -3984,4 +4143,4 @@ function joinPresentSections(...sections) {
3984
4143
  return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
3985
4144
  }
3986
4145
  //#endregion
3987
- export { readCodexErrorNotificationMessage as $, resolveCodexDynamicToolsLoading as A, resolveCodexContextEngineProjectionReserveTokens as B, resolveReasoningEffort as C, isForcedPrivateQaCodexRuntime as D, filterCodexDynamicToolsWithOpenClawShell as E, buildContextEngineBinding as F, mergeCodexThreadConfigs as G, buildCodexPluginThreadConfig as H, isContextEngineBindingCompatible as I, isOpenAiCuratedMarketplace as J, shouldBuildCodexPluginThreadConfig as K, fitCodexProjectedContextForTurnStart as L, areCodexDynamicToolFingerprintsCompatible as M, codexDynamicToolsFingerprint as N, isSystemAgentOnlyCodexDynamicToolAllowlist as O, codexLegacyDynamicToolsFingerprint as P, normalizeNonEmptyString as Q, projectContextEngineAssemblyForCodex as R, resolveCodexBindingModelProviderFallback as S, filterCodexDynamicTools as T, buildCodexPluginThreadConfigInputFingerprint as U, buildCodexPluginAppsConfigPatchFromPolicyContext as V, buildCodexPluginThreadConfigTimeoutFallback as W, resolveRecoverableCodexPluginConfigKeys as X, pluginReadParams as Y, extractRawAssistantText as Z, buildDeveloperInstructions as _, unsubscribeCodexThreadBestEffort as _t, sanitizeCodexHistoryImagePayloads as a, readNonNegativeInteger as at, resolveCodexAppServerRequestModelSelection as b, isCodexAppServerProfilerEnabled as c, readString as ct, attestCodexRingZeroThreadHasNoMcpServers as d, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS as dt, readHookOutputEntries as et, buildCodexRingZeroThreadConfigPatch as f, CodexAppServerUnsafeSubscriptionError as ft, resolveCodexWebSearchPlan as g, retireCodexAppServerClientAfterTimedOutTurn as gt, buildCodexNativeWebSearchThreadConfig as h, interruptCodexTurnBestEffort as ht, invalidInlineImageText as i, readNonEmptyStringArray as it, resolveCodexDynamicToolsLoadingForRuntime as j, normalizeCodexDynamicToolName as k, resumeCodexAppServerThread as l, splitPlanText as lt, readCodexInheritedMcpServerNames as m, interruptCodexTurnAndWaitBestEffort as mt, buildTurnStartParams as n, readItemString as nt, sanitizeInlineImageDataUrl$1 as o, readNullableString as ot, buildCodexRuntimeThreadConfig as p, closeCodexStartupClientBestEffort as pt, ensureCodexPluginActivation as q, buildCodexUserInput as r, readNonEmptyString as rt, startOrResumeThread as s, readNumber as st, buildTurnCollaborationMode as t, readItem as tt, assertCodexRingZeroHasNoManagedHooks as u, CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS as ut, CODEX_NATIVE_PERSONALITY_NONE as v, readCodexSupportedReasoningEfforts as w, resolveCodexAppServerThreadModelSelection as x, resolveCodexAppServerModelProvider as y, resolveCodexContextEngineProjectionMaxChars as z };
4146
+ export { ensureCodexPluginActivation as $, isForcedPrivateQaCodexRuntime as A, neutralizeCodexExplicitMentionSigils as B, CODEX_MANAGED_THREAD_NAMESPACE as C, codexLegacyDynamicToolsFingerprint as D, codexDynamicToolsFingerprint as E, resolveCodexDynamicToolsLoadingForRuntime as F, buildCodexPluginAppsConfigPatchFromPolicyContext as G, resolveCodexContextEngineProjectionMaxChars as H, buildContextEngineBinding as I, buildCodexPluginThreadConfigTimeoutFallback as J, buildCodexPluginThreadConfig as K, isContextEngineBindingCompatible as L, isSystemAgentOnlyCodexDynamicToolAllowlist as M, normalizeCodexDynamicToolName as N, filterCodexDynamicTools as O, resolveCodexDynamicToolsLoading as P, shouldBuildCodexPluginThreadConfig as Q, buildCodexContinuityCalibration as R, CODEX_MANAGED_THREAD_MAX_ENTRIES as S, areCodexDynamicToolFingerprintsCompatible as T, resolveCodexContextEngineProjectionReserveTokens as U, projectContextEngineAssemblyForCodex as V, resolveCodexContinuityProjectionMaxChars as W, buildPluginAppPolicyContext as X, buildDisabledAppsConfigPatch as Y, mergeCodexThreadConfigs as Z, resolveCodexAppServerRequestModelSelection as _, isCodexAppServerProfilerEnabled as a, resolveReasoningEffort as b, buildCodexRingZeroThreadConfigPatch as c, buildCodexNativeWebSearchThreadConfig as d, refreshCodexPluginRuntimeState as et, resolveCodexWebSearchPlan as f, resolveCodexAppServerModelProvider as g, CODEX_NATIVE_PERSONALITY_NONE as h, startOrResumeThread as i, resolveRecoverableCodexPluginConfigKeys as it, isMessageOnlyCodexSourceReply as j, filterCodexDynamicToolsForDisabledNativeSurface as k, buildCodexRuntimeThreadConfig as l, buildCodexProjectDocThreadConfig as m, buildTurnStartParams as n, isOpenAiCuratedMarketplaceName as nt, assertCodexManagedRequirementsDoNotOverrideToolPolicy as o, buildDeveloperInstructions as p, buildCodexPluginThreadConfigInputFingerprint as q, buildCodexUserInput as r, pluginReadParams as rt, attestCodexRestrictedToolSurfaceMcpServersDisabled as s, buildTurnCollaborationMode as t, isOpenAiCuratedMarketplace as tt, readCodexInheritedMcpServerNames as u, resolveCodexAppServerThreadModelSelection as v, createCodexManagedThreadStore as w, readCodexSupportedReasoningEfforts as x, resolveCodexBindingModelProviderFallback as y, fitCodexProjectedContextForTurnStart as z };