@openclaw/codex 2026.7.2-beta.7 → 2026.8.1-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/{app-server-policy-BSaO2JIT.js → app-server-policy-zcBDc1JU.js} +1 -1
- package/dist/attempt-client-cleanup-DMLXUfa8.js +735 -0
- package/dist/{notification-correlation-CU6_AnOb.js → binding-connection-I5Bo-AsX.js} +3 -37
- package/dist/{bounded-turn-D2ZAgZVz.js → bounded-turn-qgZmzy_4.js} +50 -49
- package/dist/{capabilities-BFDTVYr4.js → capabilities-CybGI1oz.js} +8 -2
- package/dist/cli-metadata.js +1 -1
- package/dist/{command-formatters-C336eRVS.js → command-formatters-B1AEz8ZA.js} +18 -18
- package/dist/{command-handlers-DAHUSry9.js → command-handlers-Db8pu_ds.js} +484 -95
- package/dist/{compact-D_uvHurS.js → compact-LspQaGfl.js} +213 -199
- package/dist/{conversation-binding-data-BHRpYbX3.js → conversation-binding-data-CW2GuupV.js} +6 -10
- package/dist/doctor-contract-api.js +40 -43
- package/dist/{dynamic-tools-D2gYkSBO.js → dynamic-tools-BVdyWkUD.js} +2597 -1221
- package/dist/{effective-mcp-catalog-Bbd98Inw.js → effective-mcp-catalog-DIIhAtOZ.js} +20 -15
- package/dist/{event-projector-assistant-message-DTgnPPVF.js → event-projector-assistant-message-BwxV4d6N.js} +11 -23
- package/dist/{attempt-notifications-AP-a-Q5k.js → event-projector-usage-e5huIQWu.js} +113 -29
- package/dist/harness.js +80 -14
- package/dist/image-payload-sanitizer-CnfoUPCE.js +65 -0
- package/dist/index.js +591 -240
- package/dist/isolated-completion-C58NbL40.js +75 -0
- package/dist/local-runtime-attribution-B_2mnqhb.js +19 -0
- package/dist/media-understanding-provider.js +5 -5
- package/dist/{models-BBMKU4jA.js → models-BFlpn1Ex.js} +12 -17
- package/dist/native-subagent-monitor-DyTGmTUx.js +3111 -0
- package/dist/{plugin-app-cache-key-DztPafOA.js → plugin-app-cache-key-C3H2CVp6.js} +5 -5
- package/dist/plugin-marketplace-discovery-CzrGJdCR.js +385 -0
- package/dist/{computer-use-CYEc8U3w.js → plugin-metadata-cache-CNJPj4_b.js} +105 -5
- package/dist/protocol-validators-B4F7L8Rf.js +2346 -0
- package/dist/{rate-limits-CU-gVCCS.js → rate-limits-DZXmOL8-.js} +17 -35
- package/dist/{request-qKh331yu.js → request-BRl5_UfX.js} +3 -3
- package/dist/{run-attempt-CY9WlDLc.js → run-attempt-P8BdxC1I.js} +4837 -9047
- package/dist/{runtime-artifact-CmtG_t1L.js → runtime-artifact-BVSPdd6g.js} +8 -23
- package/dist/{sandbox-guard-NuhpXmPB.js → sandbox-guard-Cmwc9eIk.js} +6 -13
- package/dist/{session-binding-BXpo9ymA.js → session-binding-DPxIvMgE.js} +246 -265
- package/dist/{session-catalog-DyOgR4WH.js → session-catalog-xwNi_SDM.js} +159 -109
- package/dist/{session-cli-2PZaFLRh.js → session-cli-7hgXZDV7.js} +1 -1
- package/dist/session-retirement-DoMMJFKm.js +36 -0
- package/dist/{session-upstream-marker-B6U7tLw5.js → session-upstream-marker-CrIFX06J.js} +1 -1
- package/dist/{settled-turn-finalizer-CXY2Z9dT.js → settled-turn-finalizer-BRWBO4MO.js} +21 -13
- package/dist/{shared-client-DTbvH-XV.js → shared-client-kIBbGjk4.js} +587 -223
- package/dist/{side-question-_ulBk1AL.js → side-question-9HAL26l9.js} +101 -52
- package/dist/{thread-lifecycle-DF53ogiA.js → thread-lifecycle-Bu1BTDXr.js} +694 -753
- package/dist/thread-ownership-6kOQHlyC.js +92 -0
- package/dist/thread-resume-CPrJNQVM.js +30 -0
- package/dist/transcript-mirror-B9gGgnJS.js +570 -0
- package/dist/{transport-stdio-DXgGZ7Ib.js → transport-stdio-DIImwDVS.js} +11 -0
- package/dist/upstream-prompt-provenance-ClRBije0.js +41 -0
- package/dist/{upstream-session-fork-Dy4Xvuoj.js → upstream-session-fork-CE-_LdnP.js} +5 -5
- package/dist/{usage-CQ6GhD5-.js → usage-CHlV0e-o.js} +3 -3
- package/dist/{web-search-provider.runtime-CCFhg2z5.js → web-search-provider.runtime-DRw2Vl42.js} +8 -9
- package/openclaw.plugin.json +18 -3
- package/package.json +5 -5
- package/dist/conversation-control-xsIeXbo4.js +0 -287
- package/dist/event-projector-usage-Xp4AlR2k.js +0 -86
- package/dist/transcript-mirror-DzsdNCf5.js +0 -6924
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import { D as
|
|
2
|
-
import { M as
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
1
|
+
import { D as CODEX_PLUGINS_MARKETPLACE_NAME, E as resolveCodexPluginsPolicy, O as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME, a as hashCodexAppServerBindingFingerprint, c as reclaimCurrentCodexSessionGeneration, d as sessionBindingIdentity, i as createCodexSessionGenerationSupersededError, m as codexSandboxPolicyForTurn, n as assertCodexBindingMayBeReplaced, o as isCodexAppServerNativeAuthProfile, s as normalizeCodexAppServerBindingModelProvider } from "./session-binding-DPxIvMgE.js";
|
|
2
|
+
import { M as isCodexAppServerConnectionClosedError, R as resolveCodexAppServerClientInstanceId, V as consumeCodexAppServerLiveThread, ct as isJsonObject, d as retainSharedCodexAppServerClientByInstanceId, k as getCodexAppServerClientInstanceId, s as isCodexAppServerStartSelectionChangedError, st as flattenCodexDynamicToolFunctions, z as CodexAppServerRpcError } from "./shared-client-kIBbGjk4.js";
|
|
3
|
+
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-DMLXUfa8.js";
|
|
4
|
+
import { a as assertCodexThreadStartResponse, r as assertCodexThreadForkResponse } from "./protocol-validators-B4F7L8Rf.js";
|
|
5
|
+
import { a as serializeCodexAppInventoryError, i as defaultCodexAppInventoryCache, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-C3H2CVp6.js";
|
|
5
6
|
import { t as isIncognitoSessionKey } from "./incognito-session-ET_ufDH-.js";
|
|
7
|
+
import { t as resumeCodexAppServerThread } from "./thread-resume-CPrJNQVM.js";
|
|
8
|
+
import { u as projectBoundedCodexThreadHistory } from "./transcript-mirror-B9gGgnJS.js";
|
|
9
|
+
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-CnfoUPCE.js";
|
|
10
|
+
import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
6
11
|
import fs from "node:fs/promises";
|
|
7
12
|
import path from "node:path";
|
|
8
|
-
import { SKILL_WORKSHOP_TOOL_NAME, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
13
|
+
import { SKILL_WORKSHOP_TOOL_NAME, TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
9
14
|
import * as crypto$1 from "node:crypto";
|
|
10
15
|
import crypto from "node:crypto";
|
|
11
16
|
import os from "node:os";
|
|
12
|
-
import {
|
|
17
|
+
import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
13
18
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
14
19
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
15
20
|
import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
@@ -17,207 +22,6 @@ import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plu
|
|
|
17
22
|
import { buildCodexUserMcpServersThreadConfigPatchForRuntime } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
18
23
|
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
19
24
|
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
25
|
//#region extensions/codex/src/app-server/plugin-inventory.ts
|
|
222
26
|
/**
|
|
223
27
|
* Reads Codex plugin marketplace state and app inventory to decide which
|
|
@@ -241,7 +45,7 @@ async function readCodexPluginInventory(params) {
|
|
|
241
45
|
...params,
|
|
242
46
|
policy
|
|
243
47
|
});
|
|
244
|
-
|
|
48
|
+
const pluginCatalogs = /* @__PURE__ */ new Map();
|
|
245
49
|
const diagnostics = [];
|
|
246
50
|
const records = [];
|
|
247
51
|
if (appInventory?.state === "missing") diagnostics.push({
|
|
@@ -255,13 +59,19 @@ async function readCodexPluginInventory(params) {
|
|
|
255
59
|
for (const pluginPolicy of policy.pluginPolicies) {
|
|
256
60
|
if (!pluginPolicy.enabled && !policy.allowAllPlugins) continue;
|
|
257
61
|
let listed = installedPlugins;
|
|
258
|
-
let resolvedPlugin =
|
|
259
|
-
if (!resolvedPlugin && pluginPolicy.enabled && pluginPolicy.marketplaceName
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
62
|
+
let resolvedPlugin = findConfiguredMarketplacePlugin(listed, pluginPolicy);
|
|
63
|
+
if (!resolvedPlugin && pluginPolicy.enabled && pluginPolicy.marketplaceName !== "workspace-directory") {
|
|
64
|
+
const requestParams = buildPluginCatalogRequestParams(params, pluginPolicy.marketplaceName);
|
|
65
|
+
const catalogKey = JSON.stringify([requestParams, pluginMetadataCatalogScope(pluginPolicy.marketplaceName)]);
|
|
66
|
+
let catalog = pluginCatalogs.get(catalogKey);
|
|
67
|
+
if (!catalog) {
|
|
68
|
+
catalog = listCodexPluginMetadata(params, pluginPolicy.marketplaceName);
|
|
69
|
+
pluginCatalogs.set(catalogKey, catalog);
|
|
70
|
+
}
|
|
71
|
+
listed = await catalog;
|
|
72
|
+
resolvedPlugin = findConfiguredMarketplacePlugin(listed, pluginPolicy);
|
|
263
73
|
}
|
|
264
|
-
if (!
|
|
74
|
+
if (!listed.marketplaces.some((marketplace) => marketplaceMatchesConfiguredName(marketplace, pluginPolicy.marketplaceName))) {
|
|
265
75
|
diagnostics.push({
|
|
266
76
|
code: "marketplace_missing",
|
|
267
77
|
plugin: pluginPolicy,
|
|
@@ -278,6 +88,15 @@ async function readCodexPluginInventory(params) {
|
|
|
278
88
|
continue;
|
|
279
89
|
}
|
|
280
90
|
const { summary } = resolvedPlugin;
|
|
91
|
+
const unavailableByMarketplacePolicy = summary.availability === "DISABLED_BY_ADMIN" || summary.installPolicy === "NOT_AVAILABLE";
|
|
92
|
+
if (unavailableByMarketplacePolicy) {
|
|
93
|
+
diagnostics.push({
|
|
94
|
+
code: "plugin_disabled",
|
|
95
|
+
plugin: pluginPolicy,
|
|
96
|
+
message: `${pluginPolicy.pluginName} is unavailable in ${pluginPolicy.marketplaceName}.`
|
|
97
|
+
});
|
|
98
|
+
if (!summary.installed) continue;
|
|
99
|
+
}
|
|
281
100
|
const detail = await readPluginDetail(params, marketplaceRef(resolvedPlugin.marketplace, pluginPolicy.marketplaceName), pluginPolicy, summary, diagnostics);
|
|
282
101
|
const ownedAppIds = detail?.apps.map((app) => app.id).filter(Boolean).toSorted() ?? [];
|
|
283
102
|
const appOwnership = resolveAppOwnership({
|
|
@@ -304,7 +123,7 @@ async function readCodexPluginInventory(params) {
|
|
|
304
123
|
policy: pluginPolicy,
|
|
305
124
|
summary,
|
|
306
125
|
...detail ? { detail } : {},
|
|
307
|
-
activationRequired: pluginPolicy.enabled && (!summary.installed || !summary.enabled),
|
|
126
|
+
activationRequired: pluginPolicy.enabled && (unavailableByMarketplacePolicy || !summary.installed || !summary.enabled),
|
|
308
127
|
authRequired: apps.some((app) => app.needsAuth || !app.accessible),
|
|
309
128
|
appOwnership,
|
|
310
129
|
ownedAppIds,
|
|
@@ -318,11 +137,14 @@ async function readCodexPluginInventory(params) {
|
|
|
318
137
|
...appInventory ? { appInventory } : {}
|
|
319
138
|
};
|
|
320
139
|
}
|
|
321
|
-
/** Finds
|
|
322
|
-
function
|
|
323
|
-
const resolved =
|
|
140
|
+
/** Finds a configured plugin only in its authorized marketplace identity. */
|
|
141
|
+
function findCodexMarketplacePluginSummary(listed, marketplaceName, pluginName) {
|
|
142
|
+
const resolved = findConfiguredMarketplacePlugin(listed, {
|
|
143
|
+
marketplaceName,
|
|
144
|
+
pluginName
|
|
145
|
+
});
|
|
324
146
|
return resolved ? {
|
|
325
|
-
marketplace: marketplaceRef(resolved.marketplace,
|
|
147
|
+
marketplace: marketplaceRef(resolved.marketplace, marketplaceName),
|
|
326
148
|
summary: resolved.summary
|
|
327
149
|
} : void 0;
|
|
328
150
|
}
|
|
@@ -343,15 +165,16 @@ function resolveRecoverableCodexPluginConfigKeys(params) {
|
|
|
343
165
|
configCwd: params.configCwd
|
|
344
166
|
})).map((pluginPolicy) => pluginPolicy.configKey).toSorted();
|
|
345
167
|
}
|
|
346
|
-
async function listCodexPluginMetadata(params) {
|
|
347
|
-
const requestParams =
|
|
168
|
+
async function listCodexPluginMetadata(params, marketplaceName) {
|
|
169
|
+
const requestParams = buildPluginCatalogRequestParams(params, marketplaceName);
|
|
348
170
|
if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/list", requestParams);
|
|
349
171
|
return (await params.metadataCache.load({
|
|
350
172
|
appCacheKey: params.appCacheKey,
|
|
351
173
|
queryKind: "curated-global",
|
|
352
174
|
requestParams,
|
|
175
|
+
catalogScope: pluginMetadataCatalogScope(marketplaceName),
|
|
353
176
|
request: async (method, listedParams) => await params.request(method, listedParams),
|
|
354
|
-
cacheable: (response) => response.marketplaces.some((marketplace) =>
|
|
177
|
+
cacheable: (response) => response.marketplaces.some((marketplace) => marketplaceMatchesConfiguredName(marketplace, marketplaceName))
|
|
355
178
|
})).response;
|
|
356
179
|
}
|
|
357
180
|
async function readInstalledCodexPluginMetadata(params) {
|
|
@@ -364,17 +187,26 @@ async function readInstalledCodexPluginMetadata(params) {
|
|
|
364
187
|
request: async (method, installedParams) => await params.request(method, installedParams),
|
|
365
188
|
cacheable: (response) => params.policy.pluginPolicies.every((pluginPolicy) => {
|
|
366
189
|
if (!pluginPolicy.enabled && !params.policy.allowAllPlugins) return true;
|
|
367
|
-
return
|
|
190
|
+
return Boolean(findConfiguredMarketplacePlugin(response, pluginPolicy));
|
|
368
191
|
})
|
|
369
192
|
})).response;
|
|
370
193
|
}
|
|
371
194
|
function isSettledMissingPluginPolicy(params) {
|
|
372
195
|
const queryKind = params.pluginPolicy.marketplaceName === "workspace-directory" ? "installed" : "curated-global";
|
|
373
|
-
const requestParams = queryKind === "installed"
|
|
374
|
-
const listed = params.metadataCache.read(params.appCacheKey, queryKind, requestParams)?.response;
|
|
196
|
+
const requestParams = queryKind === "installed" ? params.configCwd ? { cwds: [params.configCwd] } : {} : buildPluginCatalogRequestParams(params, params.pluginPolicy.marketplaceName);
|
|
197
|
+
const listed = params.metadataCache.read(params.appCacheKey, queryKind, requestParams, queryKind === "curated-global" ? pluginMetadataCatalogScope(params.pluginPolicy.marketplaceName) : void 0)?.response;
|
|
375
198
|
if (!listed) return false;
|
|
376
|
-
|
|
377
|
-
|
|
199
|
+
return !findConfiguredMarketplacePlugin(listed, params.pluginPolicy);
|
|
200
|
+
}
|
|
201
|
+
function pluginMetadataCatalogScope(marketplaceName) {
|
|
202
|
+
return isOpenAiCuratedMarketplaceName(marketplaceName) ? void 0 : marketplaceName;
|
|
203
|
+
}
|
|
204
|
+
function buildPluginCatalogRequestParams(params, marketplaceName) {
|
|
205
|
+
const marketplaceKinds = marketplaceName === "created-by-me-remote" ? ["created-by-me-remote"] : marketplaceName.startsWith("workspace-shared-with-me") ? ["shared-with-me"] : void 0;
|
|
206
|
+
return {
|
|
207
|
+
...params.configCwd ? { cwds: [params.configCwd] } : {},
|
|
208
|
+
...marketplaceKinds ? { marketplaceKinds: [...marketplaceKinds] } : {}
|
|
209
|
+
};
|
|
378
210
|
}
|
|
379
211
|
function readCachedAppInventory(params) {
|
|
380
212
|
if (!params.appCache || !params.appCacheKey) return;
|
|
@@ -442,18 +274,25 @@ function resolveOwnedApps(params) {
|
|
|
442
274
|
}).toSorted((left, right) => left.id.localeCompare(right.id));
|
|
443
275
|
}
|
|
444
276
|
function findPluginSummary(marketplace, pluginName) {
|
|
445
|
-
|
|
277
|
+
const exact = marketplace.plugins.find((plugin) => plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`);
|
|
278
|
+
if (exact) return exact;
|
|
279
|
+
const matches = marketplace.plugins.filter((plugin) => plugin.name === pluginName || pluginNameFromPluginId(plugin.id, marketplace.name) === pluginName);
|
|
280
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
446
281
|
}
|
|
447
|
-
function
|
|
282
|
+
function findConfiguredMarketplacePlugin(listed, plugin) {
|
|
283
|
+
if (plugin.marketplaceName === "workspace-directory") return findWorkspaceMarketplacePlugin(listed, plugin.pluginName);
|
|
448
284
|
for (const marketplace of listed.marketplaces) {
|
|
449
|
-
if (!
|
|
450
|
-
const summary = findPluginSummary(marketplace, pluginName);
|
|
285
|
+
if (!marketplaceMatchesConfiguredName(marketplace, plugin.marketplaceName)) continue;
|
|
286
|
+
const summary = findPluginSummary(marketplace, plugin.pluginName);
|
|
451
287
|
if (summary) return {
|
|
452
288
|
marketplace,
|
|
453
289
|
summary
|
|
454
290
|
};
|
|
455
291
|
}
|
|
456
292
|
}
|
|
293
|
+
function marketplaceMatchesConfiguredName(marketplace, configuredMarketplaceName) {
|
|
294
|
+
return isOpenAiCuratedMarketplaceName(configuredMarketplaceName) ? isOpenAiCuratedMarketplace(marketplace) : marketplace.name === configuredMarketplaceName;
|
|
295
|
+
}
|
|
457
296
|
function findWorkspaceMarketplacePlugin(listed, pluginName) {
|
|
458
297
|
const marketplace = listed.marketplaces.find((entry) => entry.name === CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME);
|
|
459
298
|
const summary = marketplace?.plugins.find((plugin) => plugin.id === pluginName);
|
|
@@ -477,20 +316,33 @@ function marketplaceRef(marketplace, name) {
|
|
|
477
316
|
}
|
|
478
317
|
/** True for any supported OpenAI curated marketplace wire name, matching Codex's own curated predicate. */
|
|
479
318
|
function isOpenAiCuratedMarketplace(marketplace) {
|
|
480
|
-
return marketplace.name
|
|
319
|
+
return isOpenAiCuratedMarketplaceName(marketplace.name);
|
|
320
|
+
}
|
|
321
|
+
/** True for all Codex wire aliases of the same OpenAI-curated catalog. */
|
|
322
|
+
function isOpenAiCuratedMarketplaceName(marketplaceName) {
|
|
323
|
+
return marketplaceName === "openai-curated" || marketplaceName === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME || marketplaceName === CODEX_PLUGINS_API_MARKETPLACE_NAME;
|
|
481
324
|
}
|
|
482
325
|
//#endregion
|
|
483
326
|
//#region extensions/codex/src/app-server/plugin-activation.ts
|
|
484
|
-
/**
|
|
327
|
+
/**
|
|
328
|
+
* Activates legacy curated Codex plugins while requiring owner-managed
|
|
329
|
+
* installation for every other marketplace.
|
|
330
|
+
*/
|
|
331
|
+
/** Activates legacy curated plugins without granting install authority to other marketplaces. */
|
|
485
332
|
async function ensureCodexPluginActivation(params) {
|
|
486
333
|
if (params.identity.marketplaceName === "workspace-directory") return activationFailure(params.identity, "disabled", { message: "workspace-directory plugins must be installed and enabled outside OpenClaw before use." });
|
|
334
|
+
if (!isOpenAiCuratedMarketplaceName(params.identity.marketplaceName)) {
|
|
335
|
+
const target = params.identity.pluginName.endsWith(`@${params.identity.marketplaceName}`) ? params.identity.pluginName : `${params.identity.pluginName}@${params.identity.marketplaceName}`;
|
|
336
|
+
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}.` });
|
|
337
|
+
}
|
|
487
338
|
const listed = await listCuratedCodexPluginMetadata(params);
|
|
488
|
-
const resolved =
|
|
339
|
+
const resolved = findCodexMarketplacePluginSummary(listed, params.identity.marketplaceName, params.identity.pluginName);
|
|
489
340
|
if (!resolved) {
|
|
490
|
-
if (!listed.marketplaces.some(isOpenAiCuratedMarketplace)) return activationFailure(params.identity, "marketplace_missing", { message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.` });
|
|
341
|
+
if (!listed.marketplaces.some((marketplace) => isOpenAiCuratedMarketplace(marketplace))) return activationFailure(params.identity, "marketplace_missing", { message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.` });
|
|
491
342
|
return activationFailure(params.identity, "plugin_missing", { message: `${params.identity.pluginName} was not found in ${CODEX_PLUGINS_MARKETPLACE_NAME}.` });
|
|
492
343
|
}
|
|
493
344
|
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.` });
|
|
345
|
+
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
346
|
if (resolved.summary.installed && resolved.summary.enabled && !params.installEvenIfActive) return {
|
|
495
347
|
identity: params.identity,
|
|
496
348
|
ok: true,
|
|
@@ -499,7 +351,21 @@ async function ensureCodexPluginActivation(params) {
|
|
|
499
351
|
marketplace: resolved.marketplace,
|
|
500
352
|
diagnostics: []
|
|
501
353
|
};
|
|
502
|
-
const
|
|
354
|
+
const remotePluginId = resolved.marketplace.remoteMarketplaceName ? resolved.summary.remotePluginId : void 0;
|
|
355
|
+
let installResponse;
|
|
356
|
+
try {
|
|
357
|
+
installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, remotePluginId ?? params.identity.pluginName));
|
|
358
|
+
} catch (error) {
|
|
359
|
+
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;
|
|
360
|
+
return {
|
|
361
|
+
identity: params.identity,
|
|
362
|
+
ok: false,
|
|
363
|
+
reason: "install_failed",
|
|
364
|
+
installAttempted: true,
|
|
365
|
+
marketplace: resolved.marketplace,
|
|
366
|
+
diagnostics: [{ message: `Codex plugin install failed: ${coerceErrorMessage(error)}` }]
|
|
367
|
+
};
|
|
368
|
+
}
|
|
503
369
|
if (params.metadataCache && params.appCacheKey) params.metadataCache.invalidate(params.appCacheKey);
|
|
504
370
|
const refreshDiagnostics = [];
|
|
505
371
|
let refreshFailed = false;
|
|
@@ -508,6 +374,7 @@ async function ensureCodexPluginActivation(params) {
|
|
|
508
374
|
request: params.request,
|
|
509
375
|
appCache: params.appCache,
|
|
510
376
|
appCacheKey: params.appCacheKey,
|
|
377
|
+
configCwd: params.configCwd,
|
|
511
378
|
metadataCache: params.metadataCache,
|
|
512
379
|
deferAppInventoryRefresh: params.deferAppInventoryRefresh,
|
|
513
380
|
targetAppIds: params.targetAppIds
|
|
@@ -515,7 +382,7 @@ async function ensureCodexPluginActivation(params) {
|
|
|
515
382
|
refreshDiagnostics.push(...refreshResult.diagnostics);
|
|
516
383
|
} catch (error) {
|
|
517
384
|
refreshFailed = true;
|
|
518
|
-
refreshDiagnostics.push({ message: `Codex plugin runtime refresh failed after install: ${
|
|
385
|
+
refreshDiagnostics.push({ message: `Codex plugin runtime refresh failed after install: ${coerceErrorMessage(error)}` });
|
|
519
386
|
}
|
|
520
387
|
const authRequired = installResponse.appsNeedingAuth.length > 0;
|
|
521
388
|
return {
|
|
@@ -533,13 +400,13 @@ async function refreshCodexPluginRuntimeState(params) {
|
|
|
533
400
|
const diagnostics = [];
|
|
534
401
|
await listCuratedCodexPluginMetadata(params, { forceRefetch: true });
|
|
535
402
|
await params.request("skills/list", {
|
|
536
|
-
cwds: [],
|
|
403
|
+
cwds: params.configCwd ? [params.configCwd] : [],
|
|
537
404
|
forceReload: true
|
|
538
405
|
});
|
|
539
406
|
try {
|
|
540
|
-
await params.request("hooks/list", { cwds: [] });
|
|
407
|
+
await params.request("hooks/list", { cwds: params.configCwd ? [params.configCwd] : [] });
|
|
541
408
|
} catch (error) {
|
|
542
|
-
diagnostics.push({ message: `Codex hooks refresh skipped: ${
|
|
409
|
+
diagnostics.push({ message: `Codex hooks refresh skipped: ${coerceErrorMessage(error)}` });
|
|
543
410
|
}
|
|
544
411
|
await params.request("config/mcpServer/reload", void 0);
|
|
545
412
|
if (params.appCache && params.appCacheKey) {
|
|
@@ -554,13 +421,16 @@ async function refreshCodexPluginRuntimeState(params) {
|
|
|
554
421
|
targetAppIds: params.targetAppIds
|
|
555
422
|
});
|
|
556
423
|
} catch (error) {
|
|
557
|
-
diagnostics.push({ message: `Codex app inventory refresh skipped: ${
|
|
424
|
+
diagnostics.push({ message: `Codex app inventory refresh skipped: ${coerceErrorMessage(error)}` });
|
|
558
425
|
}
|
|
559
426
|
}
|
|
560
427
|
return { diagnostics };
|
|
561
428
|
}
|
|
562
429
|
async function listCuratedCodexPluginMetadata(params, options = {}) {
|
|
563
|
-
const requestParams =
|
|
430
|
+
const requestParams = {
|
|
431
|
+
...params.configCwd ? { cwds: [params.configCwd] } : {},
|
|
432
|
+
...options.forceRefetch ? { forceRefetch: true } : {}
|
|
433
|
+
};
|
|
564
434
|
if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/list", requestParams);
|
|
565
435
|
return (await params.metadataCache.load({
|
|
566
436
|
appCacheKey: params.appCacheKey,
|
|
@@ -792,6 +662,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
792
662
|
request: params.request,
|
|
793
663
|
appCache,
|
|
794
664
|
appCacheKey: params.appCacheKey,
|
|
665
|
+
configCwd: params.configCwd,
|
|
795
666
|
metadataCache: params.metadataCache,
|
|
796
667
|
deferAppInventoryRefresh: true,
|
|
797
668
|
targetAppIds: record.ownedAppIds
|
|
@@ -867,7 +738,12 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
867
738
|
inventory,
|
|
868
739
|
accountApps: accountAppsResult.apps
|
|
869
740
|
});
|
|
870
|
-
const unresolvedDisabledPluginOwnership = policy.allowAllPlugins ? policy.pluginPolicies.find((pluginPolicy) =>
|
|
741
|
+
const unresolvedDisabledPluginOwnership = policy.allowAllPlugins ? policy.pluginPolicies.find((pluginPolicy) => {
|
|
742
|
+
const record = inventory.records.find((candidate) => candidate.policy.configKey === pluginPolicy.configKey);
|
|
743
|
+
const disabledByMarketplacePolicy = record?.summary.availability === "DISABLED_BY_ADMIN" || record?.summary.installPolicy === "NOT_AVAILABLE";
|
|
744
|
+
const unresolvedPluginIdentity = !record && inventory.diagnostics.some((diagnostic) => diagnostic.plugin?.configKey === pluginPolicy.configKey && (diagnostic.code === "plugin_disabled" || diagnostic.code === "plugin_missing" || diagnostic.code === "marketplace_missing"));
|
|
745
|
+
return (!pluginPolicy.enabled || disabledByMarketplacePolicy || unresolvedPluginIdentity) && !record?.detail;
|
|
746
|
+
}) : void 0;
|
|
871
747
|
if (unresolvedDisabledPluginOwnership) diagnostics.push({
|
|
872
748
|
code: "account_app_ownership_unavailable",
|
|
873
749
|
plugin: unresolvedDisabledPluginOwnership,
|
|
@@ -907,6 +783,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
907
783
|
marketplaceName: record.policy.marketplaceName,
|
|
908
784
|
pluginName: record.policy.pluginName,
|
|
909
785
|
allowDestructiveActions: record.policy.allowDestructiveActions,
|
|
786
|
+
allowOpenWorld: true,
|
|
910
787
|
destructiveApprovalMode: record.policy.destructiveApprovalMode,
|
|
911
788
|
mcpServerNames: [...record.detail?.mcpServers ?? []].toSorted()
|
|
912
789
|
};
|
|
@@ -938,6 +815,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
938
815
|
source: "account",
|
|
939
816
|
appName: app.name,
|
|
940
817
|
allowDestructiveActions: policy.allowDestructiveActions,
|
|
818
|
+
allowOpenWorld: true,
|
|
941
819
|
destructiveApprovalMode: policy.destructiveApprovalMode,
|
|
942
820
|
mcpServerNames: []
|
|
943
821
|
};
|
|
@@ -1017,7 +895,7 @@ function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
|
|
|
1017
895
|
for (const [appId, policy] of Object.entries(policyContext.apps).toSorted(([left], [right]) => left.localeCompare(right))) apps[appId] = {
|
|
1018
896
|
enabled: true,
|
|
1019
897
|
destructive_enabled: policy.allowDestructiveActions,
|
|
1020
|
-
open_world_enabled:
|
|
898
|
+
open_world_enabled: policy.allowOpenWorld !== false,
|
|
1021
899
|
default_tools_approval_mode: "auto",
|
|
1022
900
|
...policy.destructiveApprovalMode === "ask" ? { approvals_reviewer: "user" } : {}
|
|
1023
901
|
};
|
|
@@ -1110,13 +988,10 @@ function mergeJsonObjects(left, right) {
|
|
|
1110
988
|
const merged = { ...left };
|
|
1111
989
|
for (const [key, value] of Object.entries(right)) {
|
|
1112
990
|
const existing = merged[key];
|
|
1113
|
-
merged[key] =
|
|
991
|
+
merged[key] = isJsonObject(existing) && isJsonObject(value) ? mergeJsonObjects(existing, value) : value;
|
|
1114
992
|
}
|
|
1115
993
|
return merged;
|
|
1116
994
|
}
|
|
1117
|
-
function isPlainJsonObject(value) {
|
|
1118
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1119
|
-
}
|
|
1120
995
|
function fingerprintJson(value) {
|
|
1121
996
|
return crypto.createHash("sha256").update(stableStringify(value)).digest("hex");
|
|
1122
997
|
}
|
|
@@ -1142,15 +1017,18 @@ const CODEX_TURN_START_TEXT_INPUT_MAX_CHARS = 1 << 20;
|
|
|
1142
1017
|
const DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS = 2e4;
|
|
1143
1018
|
const MIN_PROMPT_BUDGET_RATIO = .5;
|
|
1144
1019
|
const MIN_PROMPT_BUDGET_TOKENS = 8e3;
|
|
1020
|
+
function neutralizeCodexExplicitMentionSigils(text) {
|
|
1021
|
+
return text.replace(/\$(?=[A-Za-z0-9_:-])/gu, "$").replace(/\[@(?=[A-Za-z0-9_:-]+\]\()/gu, "[@");
|
|
1022
|
+
}
|
|
1145
1023
|
/** Projects assembled OpenClaw context-engine messages into Codex prompt inputs. */
|
|
1146
1024
|
function projectContextEngineAssemblyForCodex(params) {
|
|
1147
1025
|
const prompt = params.prompt.trim();
|
|
1148
1026
|
const contextMessages = dropDuplicateTrailingPrompt(params.assembledMessages, prompt);
|
|
1149
1027
|
const maxRenderedContextChars = normalizeRenderedContextMaxChars(params.maxRenderedContextChars);
|
|
1150
|
-
const renderedContext = renderMessagesForCodexContext(contextMessages, {
|
|
1028
|
+
const renderedContext = neutralizeCodexExplicitMentionSigils(renderMessagesForCodexContext(contextMessages, {
|
|
1151
1029
|
maxTextPartChars: resolveTextPartMaxChars(maxRenderedContextChars),
|
|
1152
1030
|
toolPayloadMode: params.toolPayloadMode ?? "elide"
|
|
1153
|
-
});
|
|
1031
|
+
}));
|
|
1154
1032
|
const boundedContext = renderedContext ? truncateOlderContext(renderedContext, maxRenderedContextChars) : void 0;
|
|
1155
1033
|
const promptPrefix = boundedContext ? [
|
|
1156
1034
|
CONTEXT_HEADER,
|
|
@@ -1414,18 +1292,172 @@ function areContextEngineProjectionBindingsCompatible(previous, next) {
|
|
|
1414
1292
|
return previous?.schemaVersion === next.schemaVersion && previous.mode === next.mode && previous.epoch === next.epoch && previous.fingerprint === next.fingerprint;
|
|
1415
1293
|
}
|
|
1416
1294
|
function resolveContextEngineCitationsMode(config) {
|
|
1417
|
-
const rootConfig =
|
|
1418
|
-
const citations = (
|
|
1295
|
+
const rootConfig = isRecord(config) ? config : void 0;
|
|
1296
|
+
const citations = (isRecord(rootConfig?.memory) ? rootConfig.memory : void 0)?.citations;
|
|
1419
1297
|
return isJsonConfigValue(citations) ? citations : void 0;
|
|
1420
1298
|
}
|
|
1421
|
-
function isUnknownRecord(value) {
|
|
1422
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1423
|
-
}
|
|
1424
1299
|
function isJsonConfigValue(value) {
|
|
1425
1300
|
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
1426
1301
|
if (typeof value === "number") return Number.isFinite(value);
|
|
1427
1302
|
if (Array.isArray(value)) return value.every(isJsonConfigValue);
|
|
1428
|
-
return
|
|
1303
|
+
return isRecord(value) && Object.values(value).every(isJsonConfigValue);
|
|
1304
|
+
}
|
|
1305
|
+
//#endregion
|
|
1306
|
+
//#region extensions/codex/src/app-server/dynamic-tool-profile.ts
|
|
1307
|
+
/** Tool names owned by Codex app-server and normally excluded from OpenClaw dynamic tools. */
|
|
1308
|
+
const CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES = [
|
|
1309
|
+
"read",
|
|
1310
|
+
"write",
|
|
1311
|
+
"edit",
|
|
1312
|
+
"apply_patch",
|
|
1313
|
+
"exec",
|
|
1314
|
+
"process",
|
|
1315
|
+
"update_plan",
|
|
1316
|
+
"tool_call",
|
|
1317
|
+
"tool_describe",
|
|
1318
|
+
"tool_search",
|
|
1319
|
+
"tool_search_code"
|
|
1320
|
+
];
|
|
1321
|
+
const CODEX_NATIVE_GOAL_TOOL_EXCLUDES = [
|
|
1322
|
+
"get_goal",
|
|
1323
|
+
"create_goal",
|
|
1324
|
+
"update_goal"
|
|
1325
|
+
];
|
|
1326
|
+
const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set([
|
|
1327
|
+
"read",
|
|
1328
|
+
"write",
|
|
1329
|
+
"edit",
|
|
1330
|
+
"apply_patch",
|
|
1331
|
+
"update_plan",
|
|
1332
|
+
...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
|
|
1333
|
+
]);
|
|
1334
|
+
const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
|
|
1335
|
+
const DYNAMIC_TOOL_NAME_ALIASES = {
|
|
1336
|
+
bash: "exec",
|
|
1337
|
+
"apply-patch": "apply_patch"
|
|
1338
|
+
};
|
|
1339
|
+
/** Normalizes OpenClaw/Codex tool names before filtering and allowlist checks. */
|
|
1340
|
+
function normalizeCodexDynamicToolName(name) {
|
|
1341
|
+
const normalized = name.trim().toLowerCase();
|
|
1342
|
+
return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
|
|
1343
|
+
}
|
|
1344
|
+
/** True only for the host-scoped OpenClaw run's exact tool contract. */
|
|
1345
|
+
function isSystemAgentOnlyCodexDynamicToolAllowlist(toolsAllow) {
|
|
1346
|
+
return toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "openclaw";
|
|
1347
|
+
}
|
|
1348
|
+
/** True when a private source reply may use the message delivery tool only. */
|
|
1349
|
+
function isMessageOnlyCodexSourceReply(params) {
|
|
1350
|
+
return params.sourceReplyDeliveryMode === "message_tool_only" && params.toolsAllow?.length === 1 && normalizeCodexDynamicToolName(params.toolsAllow[0] ?? "") === "message";
|
|
1351
|
+
}
|
|
1352
|
+
/** Returns true for private QA runs that force the Codex runtime profile. */
|
|
1353
|
+
function isForcedPrivateQaCodexRuntime(env = process.env) {
|
|
1354
|
+
return env.OPENCLAW_BUILD_PRIVATE_QA === "1" && env.OPENCLAW_QA_FORCE_RUNTIME?.trim().toLowerCase() === "codex";
|
|
1355
|
+
}
|
|
1356
|
+
/** Resolves whether dynamic tools load directly or through Codex tool search. */
|
|
1357
|
+
function resolveCodexDynamicToolsLoading(config, env = process.env) {
|
|
1358
|
+
return isForcedPrivateQaCodexRuntime(env) ? "direct" : config.codexDynamicToolsLoading ?? "searchable";
|
|
1359
|
+
}
|
|
1360
|
+
function normalizeCodexModelId(modelId) {
|
|
1361
|
+
const normalized = modelId?.trim().toLowerCase();
|
|
1362
|
+
if (!normalized) return "";
|
|
1363
|
+
return normalized.includes("/") ? normalized.split("/").at(-1) : normalized;
|
|
1364
|
+
}
|
|
1365
|
+
/** Returns true when model behavior requires direct dynamic-tool registration. */
|
|
1366
|
+
function shouldUseDirectCodexDynamicToolsForModel(modelId) {
|
|
1367
|
+
return shouldDisableCodexToolSearchForModel(modelId);
|
|
1368
|
+
}
|
|
1369
|
+
/** Returns true for models whose tool-search path is unsupported or inefficient. */
|
|
1370
|
+
function shouldDisableCodexToolSearchForModel(modelId) {
|
|
1371
|
+
return normalizeCodexModelId(modelId) === "gpt-5.4-nano";
|
|
1372
|
+
}
|
|
1373
|
+
/** Resolves dynamic-tool loading after applying model-specific restrictions. */
|
|
1374
|
+
function resolveCodexDynamicToolsLoadingForModel(config, modelId, env = process.env) {
|
|
1375
|
+
const loading = resolveCodexDynamicToolsLoading(config, env);
|
|
1376
|
+
return loading === "searchable" && shouldUseDirectCodexDynamicToolsForModel(modelId) ? "direct" : loading;
|
|
1377
|
+
}
|
|
1378
|
+
/** Resolves dynamic-tool loading for the app-server connection that will execute the turn. */
|
|
1379
|
+
function resolveCodexDynamicToolsLoadingForRuntime(config, modelId, options = {}, env = process.env) {
|
|
1380
|
+
const loading = resolveCodexDynamicToolsLoadingForModel(config, modelId, env);
|
|
1381
|
+
return loading === "searchable" && options.connectionClass === "remote" ? "direct" : loading;
|
|
1382
|
+
}
|
|
1383
|
+
/** Filters OpenClaw tools that Codex owns natively or config explicitly excludes. */
|
|
1384
|
+
function filterCodexDynamicTools(tools, config, env = process.env) {
|
|
1385
|
+
return filterCodexDynamicToolsWithOptions(tools, config, env, {
|
|
1386
|
+
preserveOpenClawReplacements: false,
|
|
1387
|
+
preserveOpenClawShell: false
|
|
1388
|
+
});
|
|
1389
|
+
}
|
|
1390
|
+
/** Keeps OpenClaw coding tools that replace a disabled Codex native surface. */
|
|
1391
|
+
function filterCodexDynamicToolsForDisabledNativeSurface(tools, config, options, env = process.env) {
|
|
1392
|
+
return filterCodexDynamicToolsWithOptions(tools, config, env, {
|
|
1393
|
+
preserveOpenClawReplacements: true,
|
|
1394
|
+
preserveOpenClawShell: options.preserveShell
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
function filterCodexDynamicToolsWithOptions(tools, config, env, options) {
|
|
1398
|
+
const excludes = /* @__PURE__ */ new Set();
|
|
1399
|
+
if (!options.preserveOpenClawReplacements) for (const name of CODEX_NATIVE_GOAL_TOOL_EXCLUDES) excludes.add(name);
|
|
1400
|
+
if (isForcedPrivateQaCodexRuntime(env)) excludes.add("apply_patch");
|
|
1401
|
+
else for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) {
|
|
1402
|
+
if (options.preserveOpenClawReplacements && CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES.has(name)) continue;
|
|
1403
|
+
if (options.preserveOpenClawShell && CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES.has(name)) continue;
|
|
1404
|
+
excludes.add(name);
|
|
1405
|
+
}
|
|
1406
|
+
for (const name of config.codexDynamicToolsExclude ?? []) {
|
|
1407
|
+
const trimmed = normalizeCodexDynamicToolName(name);
|
|
1408
|
+
if (trimmed) excludes.add(trimmed);
|
|
1409
|
+
}
|
|
1410
|
+
return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(normalizeCodexDynamicToolName(tool.name)));
|
|
1411
|
+
}
|
|
1412
|
+
//#endregion
|
|
1413
|
+
//#region extensions/codex/src/app-server/thread-binding-policy.ts
|
|
1414
|
+
function shouldRotateCodexAppServerBindingForRuntime(params) {
|
|
1415
|
+
if (!params.current) return false;
|
|
1416
|
+
if (params.binding === params.current) return false;
|
|
1417
|
+
return params.connectionClass === "remote" || Boolean(params.binding);
|
|
1418
|
+
}
|
|
1419
|
+
function resolveCodexGpt56MultiAgentVersion(modelRef) {
|
|
1420
|
+
let modelId = modelRef?.trim().toLowerCase();
|
|
1421
|
+
if (!modelId) return;
|
|
1422
|
+
const slashIndex = modelId.indexOf("/");
|
|
1423
|
+
if (slashIndex > 0) {
|
|
1424
|
+
const provider = modelId.slice(0, slashIndex);
|
|
1425
|
+
if (provider !== "openai" && provider !== "codex") return;
|
|
1426
|
+
modelId = modelId.slice(slashIndex + 1);
|
|
1427
|
+
}
|
|
1428
|
+
if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra") return "v2";
|
|
1429
|
+
return modelId === "gpt-5.6-luna" ? "v1" : void 0;
|
|
1430
|
+
}
|
|
1431
|
+
function shouldRotateCodexGpt56MultiAgentBinding(params) {
|
|
1432
|
+
const bindingVersion = resolveCodexGpt56MultiAgentVersion(params.bindingModel);
|
|
1433
|
+
const requestedVersion = resolveCodexGpt56MultiAgentVersion(params.requestedModel);
|
|
1434
|
+
return Boolean(bindingVersion && requestedVersion && bindingVersion !== requestedVersion);
|
|
1435
|
+
}
|
|
1436
|
+
function isTransientWebSearchRestriction(params) {
|
|
1437
|
+
if (params.nativeProviderWebSearchSupport === "unknown") return true;
|
|
1438
|
+
if (params.params.config?.tools?.web?.search?.enabled === false) return false;
|
|
1439
|
+
if (params.params.disableTools === true) return true;
|
|
1440
|
+
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
1441
|
+
if (params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction) return true;
|
|
1442
|
+
if (params.webSearchAllowed !== false) return false;
|
|
1443
|
+
if (params.persistentWebSearchAllowed !== void 0) return params.persistentWebSearchAllowed;
|
|
1444
|
+
if (params.params.toolsAllow === void 0) return false;
|
|
1445
|
+
return !params.params.toolsAllow.some((name) => {
|
|
1446
|
+
const normalized = normalizeCodexDynamicToolName(name);
|
|
1447
|
+
return normalized === "*" || normalized === "web_search";
|
|
1448
|
+
});
|
|
1449
|
+
}
|
|
1450
|
+
function shouldRecheckRecoverablePluginBinding(params) {
|
|
1451
|
+
if (!params.pluginThreadConfig?.enabled) return false;
|
|
1452
|
+
if (!params.binding.pluginAppsFingerprint || !params.binding.pluginAppsInputFingerprint || params.binding.pluginAppsInputFingerprint !== params.pluginThreadConfig.inputFingerprint) return false;
|
|
1453
|
+
const policyContext = params.binding.pluginAppPolicyContext;
|
|
1454
|
+
if (!policyContext) return false;
|
|
1455
|
+
const enabledPluginConfigKeys = params.pluginThreadConfig.enabledPluginConfigKeys ?? [];
|
|
1456
|
+
const recoverablePluginConfigKeys = params.pluginThreadConfig.recoverablePluginConfigKeys ?? enabledPluginConfigKeys;
|
|
1457
|
+
const recoverablePluginConfigKeySet = new Set(recoverablePluginConfigKeys);
|
|
1458
|
+
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));
|
|
1459
|
+
const accountAppRecoveryEnabled = params.pluginThreadConfig.accountAppRecoveryEnabled ?? enabledPluginConfigKeys.length === 0;
|
|
1460
|
+
return bindingContainsSettledPlugin || accountAppRecoveryEnabled && Object.keys(policyContext.apps).length === 0 || recoverablePluginConfigKeys.length > 0;
|
|
1429
1461
|
}
|
|
1430
1462
|
//#endregion
|
|
1431
1463
|
//#region extensions/codex/src/app-server/thread-fingerprints.ts
|
|
@@ -1469,21 +1501,15 @@ function fingerprintUserMcpServersAuthorizationHeader(value) {
|
|
|
1469
1501
|
function fingerprintJsonObject(value) {
|
|
1470
1502
|
return JSON.stringify(stabilizeJsonValue(value));
|
|
1471
1503
|
}
|
|
1472
|
-
/** Hash
|
|
1504
|
+
/** Hash thread-creation identity; settings already applied by turn/start must not restart Codex. */
|
|
1473
1505
|
function fingerprintCodexThreadConfig(request, authProfileId, dynamicToolsFingerprint) {
|
|
1474
1506
|
return hashCodexAppServerBindingFingerprint(fingerprintJsonObject({
|
|
1475
1507
|
authProfileId: authProfileId ?? null,
|
|
1476
1508
|
dynamicToolsFingerprint: dynamicToolsFingerprint ?? null,
|
|
1477
|
-
|
|
1478
|
-
requestedModel: request.requestedModel === void 0 ? request.model ?? null : request.requestedModel,
|
|
1509
|
+
nativeMultiAgentVersion: resolveCodexGpt56MultiAgentVersion(typeof request.requestedModel === "string" ? request.requestedModel : typeof request.model === "string" ? request.model : void 0) ?? null,
|
|
1479
1510
|
modelProvider: request.modelProvider ?? null,
|
|
1480
1511
|
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
1512
|
permissions: request.permissions ?? null,
|
|
1485
|
-
personality: request.personality ?? null,
|
|
1486
|
-
serviceTier: request.serviceTier === void 0 ? "<omitted>" : request.serviceTier,
|
|
1487
1513
|
baseInstructions: request.baseInstructions ?? null,
|
|
1488
1514
|
developerInstructions: request.developerInstructions ?? null,
|
|
1489
1515
|
config: request.config ?? {}
|
|
@@ -1493,17 +1519,7 @@ function fingerprintEnvironmentSelection(environments) {
|
|
|
1493
1519
|
return environments ? JSON.stringify(environments.map(stabilizeJsonValue)) : void 0;
|
|
1494
1520
|
}
|
|
1495
1521
|
function fingerprintDynamicToolSpec(tool) {
|
|
1496
|
-
return
|
|
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;
|
|
1522
|
+
return stabilizeJsonValue(tool);
|
|
1507
1523
|
}
|
|
1508
1524
|
function stabilizeJsonValue(value) {
|
|
1509
1525
|
if (Array.isArray(value)) return value.map(stabilizeJsonValue);
|
|
@@ -1663,216 +1679,78 @@ async function collectPersonalSkillRealPaths(homes, codexHome) {
|
|
|
1663
1679
|
depth: current.depth + 1,
|
|
1664
1680
|
onlyEscapedStateTargets: current.onlyEscapedStateTargets
|
|
1665
1681
|
});
|
|
1666
|
-
continue;
|
|
1667
|
-
}
|
|
1668
|
-
}
|
|
1669
|
-
} catch (error) {
|
|
1670
|
-
if (!isMissingPathError(error)) complete = false;
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
return {
|
|
1674
|
-
complete,
|
|
1675
|
-
skillPaths
|
|
1676
|
-
};
|
|
1677
|
-
}
|
|
1678
|
-
/** Resolves the native user-scope skills that an isolated OpenClaw thread must disable. */
|
|
1679
|
-
async function resolveCodexNativeSkillIsolation(params) {
|
|
1680
|
-
params.signal?.throwIfAborted();
|
|
1681
|
-
if (!process.env.OPENCLAW_STATE_DIR?.trim()) return;
|
|
1682
|
-
const key = JSON.stringify([
|
|
1683
|
-
path.resolve(resolveStateDir()),
|
|
1684
|
-
path.resolve(params.cwd),
|
|
1685
|
-
params.codexHome?.trim() || process.env.CODEX_HOME?.trim() || "",
|
|
1686
|
-
params.home?.trim() || process.env.HOME?.trim() || "",
|
|
1687
|
-
params.userProfile?.trim() || process.env.USERPROFILE?.trim() || ""
|
|
1688
|
-
]);
|
|
1689
|
-
const cached = nativeSkillIsolationByClient.get(params.client);
|
|
1690
|
-
if (cached?.key === key && (cached.settled || cached.signal === params.signal)) {
|
|
1691
|
-
const isolation = await cached.result;
|
|
1692
|
-
params.signal?.throwIfAborted();
|
|
1693
|
-
return isolation;
|
|
1694
|
-
}
|
|
1695
|
-
const result = resolveUncachedCodexNativeSkillIsolation(params);
|
|
1696
|
-
const entry = {
|
|
1697
|
-
key,
|
|
1698
|
-
result,
|
|
1699
|
-
settled: false,
|
|
1700
|
-
signal: params.signal
|
|
1701
|
-
};
|
|
1702
|
-
nativeSkillIsolationByClient.set(params.client, entry);
|
|
1703
|
-
try {
|
|
1704
|
-
const isolation = await result;
|
|
1705
|
-
entry.settled = true;
|
|
1706
|
-
params.signal?.throwIfAborted();
|
|
1707
|
-
return isolation;
|
|
1708
|
-
} catch (error) {
|
|
1709
|
-
if (nativeSkillIsolationByClient.get(params.client)?.result === result) nativeSkillIsolationByClient.delete(params.client);
|
|
1710
|
-
throw error;
|
|
1711
|
-
}
|
|
1712
|
-
}
|
|
1713
|
-
async function resolveUncachedCodexNativeSkillIsolation(params) {
|
|
1714
|
-
if (await usesDefaultStateDir()) return;
|
|
1715
|
-
const response = await params.client.request("skills/list", {
|
|
1716
|
-
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);
|
|
1682
|
+
continue;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
} catch (error) {
|
|
1686
|
+
if (!isMissingPathError(error)) complete = false;
|
|
1687
|
+
}
|
|
1842
1688
|
}
|
|
1843
|
-
|
|
1844
|
-
|
|
1689
|
+
return {
|
|
1690
|
+
complete,
|
|
1691
|
+
skillPaths
|
|
1692
|
+
};
|
|
1845
1693
|
}
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1694
|
+
/** Resolves the native user-scope skills that an isolated OpenClaw thread must disable. */
|
|
1695
|
+
async function resolveCodexNativeSkillIsolation(params) {
|
|
1696
|
+
params.signal?.throwIfAborted();
|
|
1697
|
+
if (!process.env.OPENCLAW_STATE_DIR?.trim()) return;
|
|
1698
|
+
const key = JSON.stringify([
|
|
1699
|
+
path.resolve(resolveStateDir()),
|
|
1700
|
+
path.resolve(params.cwd),
|
|
1701
|
+
params.codexHome?.trim() || process.env.CODEX_HOME?.trim() || "",
|
|
1702
|
+
params.home?.trim() || process.env.HOME?.trim() || "",
|
|
1703
|
+
params.userProfile?.trim() || process.env.USERPROFILE?.trim() || ""
|
|
1704
|
+
]);
|
|
1705
|
+
const cached = nativeSkillIsolationByClient.get(params.client);
|
|
1706
|
+
if (cached?.key === key && (cached.settled || cached.signal === params.signal)) {
|
|
1707
|
+
const isolation = await cached.result;
|
|
1708
|
+
params.signal?.throwIfAborted();
|
|
1709
|
+
return isolation;
|
|
1710
|
+
}
|
|
1711
|
+
const result = resolveUncachedCodexNativeSkillIsolation(params);
|
|
1712
|
+
const entry = {
|
|
1713
|
+
key,
|
|
1714
|
+
result,
|
|
1715
|
+
settled: false,
|
|
1716
|
+
signal: params.signal
|
|
1717
|
+
};
|
|
1718
|
+
nativeSkillIsolationByClient.set(params.client, entry);
|
|
1719
|
+
try {
|
|
1720
|
+
const isolation = await result;
|
|
1721
|
+
entry.settled = true;
|
|
1722
|
+
params.signal?.throwIfAborted();
|
|
1723
|
+
return isolation;
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
if (nativeSkillIsolationByClient.get(params.client)?.result === result) nativeSkillIsolationByClient.delete(params.client);
|
|
1726
|
+
throw error;
|
|
1727
|
+
}
|
|
1850
1728
|
}
|
|
1851
|
-
function
|
|
1852
|
-
if (
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
if (
|
|
1859
|
-
|
|
1860
|
-
return
|
|
1861
|
-
const normalized = normalizeCodexDynamicToolName(name);
|
|
1862
|
-
return normalized === "*" || normalized === "web_search";
|
|
1863
|
-
});
|
|
1729
|
+
async function resolveUncachedCodexNativeSkillIsolation(params) {
|
|
1730
|
+
if (await usesDefaultStateDir()) return;
|
|
1731
|
+
const response = await params.client.request("skills/list", {
|
|
1732
|
+
cwds: [params.cwd],
|
|
1733
|
+
forceReload: true
|
|
1734
|
+
}, { signal: params.signal });
|
|
1735
|
+
const homes = [params.home?.trim() || process.env.HOME?.trim() || process.env.USERPROFILE?.trim() || os.homedir()];
|
|
1736
|
+
if (process.platform === "win32") homes.push(params.userProfile?.trim() || os.homedir());
|
|
1737
|
+
const personalSkills = await collectPersonalSkillRealPaths([...new Set(homes.map((home) => path.resolve(home)))], params.codexHome);
|
|
1738
|
+
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)) };
|
|
1864
1739
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
if (!
|
|
1868
|
-
const
|
|
1869
|
-
if (!
|
|
1870
|
-
const
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1740
|
+
/** Applies path-exact session rules after caller config so isolated user skills stay disabled. */
|
|
1741
|
+
function applyCodexNativeSkillIsolation(config, isolation) {
|
|
1742
|
+
if (!isolation) return config;
|
|
1743
|
+
const existingRules = config?.["skills.config"];
|
|
1744
|
+
if (existingRules !== void 0 && !Array.isArray(existingRules)) throw new Error("Codex thread skills.config must be an array");
|
|
1745
|
+
const disabledRules = isolation.disabledUserSkillPaths.map((skillPath) => ({
|
|
1746
|
+
path: skillPath,
|
|
1747
|
+
enabled: false
|
|
1748
|
+
}));
|
|
1749
|
+
return {
|
|
1750
|
+
...config,
|
|
1751
|
+
"skills.include_instructions": false,
|
|
1752
|
+
"skills.config": [...existingRules ?? [], ...disabledRules]
|
|
1753
|
+
};
|
|
1876
1754
|
}
|
|
1877
1755
|
//#endregion
|
|
1878
1756
|
//#region extensions/codex/src/app-server/thread-lifecycle-errors.ts
|
|
@@ -1888,10 +1766,10 @@ var CodexThreadBindingConflictError = class extends Error {
|
|
|
1888
1766
|
this.name = "CodexThreadBindingConflictError";
|
|
1889
1767
|
}
|
|
1890
1768
|
};
|
|
1891
|
-
var
|
|
1769
|
+
var CodexRestrictedToolSurfaceAttestationError = class extends Error {
|
|
1892
1770
|
constructor(cause) {
|
|
1893
|
-
super("Codex
|
|
1894
|
-
this.name = "
|
|
1771
|
+
super("Codex restricted-tool-surface MCP attestation failed", { cause });
|
|
1772
|
+
this.name = "CodexRestrictedToolSurfaceAttestationError";
|
|
1895
1773
|
}
|
|
1896
1774
|
};
|
|
1897
1775
|
var CodexThreadBindingConflictAfterCleanupError = class extends CodexThreadBindingConflictError {};
|
|
@@ -1967,30 +1845,13 @@ const CODEX_REASONING_EFFORTS = [
|
|
|
1967
1845
|
"max",
|
|
1968
1846
|
"ultra"
|
|
1969
1847
|
];
|
|
1970
|
-
const
|
|
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 = [
|
|
1848
|
+
const LEGACY_PRO_REASONING_EFFORTS = [
|
|
1979
1849
|
"medium",
|
|
1980
1850
|
"high",
|
|
1981
1851
|
"xhigh"
|
|
1982
1852
|
];
|
|
1983
|
-
const
|
|
1984
|
-
const
|
|
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
|
-
]);
|
|
1853
|
+
const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
|
|
1854
|
+
const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
|
|
1994
1855
|
function normalizeCodexReasoningEfforts(efforts) {
|
|
1995
1856
|
if (!efforts) return [];
|
|
1996
1857
|
const supported = new Set(efforts.map((effort) => effort.trim().toLowerCase()));
|
|
@@ -2010,24 +1871,21 @@ function resolveSupportedReasoningEffort(params) {
|
|
|
2010
1871
|
const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
|
|
2011
1872
|
return fallbackEfforts.find((effort) => CODEX_REASONING_EFFORTS.indexOf(effort) >= requestedRank) ?? fallbackEfforts.at(-1);
|
|
2012
1873
|
}
|
|
2013
|
-
|
|
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. */
|
|
1874
|
+
/** Resolve a turn effort from the selected model's provider-owned metadata. */
|
|
2020
1875
|
function resolveCodexAppServerReasoningEffort(params) {
|
|
2021
1876
|
if (params.thinkLevel === "off" || params.thinkLevel === "adaptive") return null;
|
|
2022
|
-
|
|
2023
|
-
if (supportedReasoningEfforts) return resolveSupportedReasoningEffort({
|
|
1877
|
+
if (params.supportedReasoningEfforts) return resolveSupportedReasoningEffort({
|
|
2024
1878
|
requested: params.thinkLevel,
|
|
2025
|
-
supportedReasoningEfforts
|
|
1879
|
+
supportedReasoningEfforts: params.supportedReasoningEfforts
|
|
1880
|
+
}) ?? null;
|
|
1881
|
+
const modelId = params.modelId.trim().toLowerCase();
|
|
1882
|
+
if (LEGACY_PRO_MODEL_ID_RE.test(modelId)) return resolveSupportedReasoningEffort({
|
|
1883
|
+
requested: params.thinkLevel,
|
|
1884
|
+
supportedReasoningEfforts: LEGACY_PRO_REASONING_EFFORTS
|
|
2026
1885
|
}) ?? null;
|
|
2027
|
-
|
|
2028
|
-
if (params.thinkLevel === "minimal"
|
|
2029
|
-
|
|
2030
|
-
return params.thinkLevel === "max" && GPT_56_MAX_MODEL_IDS.has(normalizedModelId) ? "max" : null;
|
|
1886
|
+
if (params.thinkLevel === "minimal" && MODERN_GPT_5_MODEL_ID_RE.test(modelId)) return "low";
|
|
1887
|
+
if (params.thinkLevel === "minimal" || params.thinkLevel === "low" || params.thinkLevel === "medium" || params.thinkLevel === "high" || params.thinkLevel === "xhigh") return params.thinkLevel;
|
|
1888
|
+
return null;
|
|
2031
1889
|
}
|
|
2032
1890
|
//#endregion
|
|
2033
1891
|
//#region extensions/codex/src/app-server/thread-model-selection.ts
|
|
@@ -2109,6 +1967,7 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
|
|
|
2109
1967
|
function buildDeveloperInstructions(params, options = {}) {
|
|
2110
1968
|
const deferredToolNames = /* @__PURE__ */ new Set();
|
|
2111
1969
|
let hasSkillWorkshop = false;
|
|
1970
|
+
let hasSessionsSpawn = false;
|
|
2112
1971
|
let hasSessionsYield = false;
|
|
2113
1972
|
let hasSeenDirectNamespace = false;
|
|
2114
1973
|
let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
|
|
@@ -2119,6 +1978,7 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2119
1978
|
const name = tool.name.trim();
|
|
2120
1979
|
if (tool.deferLoading === true && name) deferredToolNames.add(name);
|
|
2121
1980
|
hasSkillWorkshop ||= name === SKILL_WORKSHOP_TOOL_NAME;
|
|
1981
|
+
hasSessionsSpawn ||= name === "sessions_spawn";
|
|
2122
1982
|
hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
|
|
2123
1983
|
messageToolAvailable ||= name === "message";
|
|
2124
1984
|
}
|
|
@@ -2127,20 +1987,24 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2127
1987
|
surface: "codex_app_server",
|
|
2128
1988
|
includeLegacyGlobalGuidance: false
|
|
2129
1989
|
}).join("\n");
|
|
1990
|
+
const nativeDelegationAvailable = params.disableTools !== true && params.delegationCapability !== "report_only" && !isMessageOnlyCodexSourceReply(params) && !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) && !shouldDisableCodexToolSearchForModel(params.modelId);
|
|
1991
|
+
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
1992
|
return [
|
|
2131
1993
|
"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(", ")}
|
|
1994
|
+
deferredToolNames.size > 0 ? `Deferred searchable OpenClaw dynamic tools available: ${[...deferredToolNames].toSorted((left, right) => left.localeCompare(right)).join(", ")}.` : void 0,
|
|
1995
|
+
deferredToolDiscoveryGuidance,
|
|
2133
1996
|
hasSkillWorkshop ? buildSkillWorkshopPromptSection().join("\n") : void 0,
|
|
2134
|
-
|
|
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,
|
|
1997
|
+
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`." : ""}` : void 0,
|
|
1998
|
+
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,
|
|
2136
1999
|
buildVisibleReplyInstruction(params, messageToolAvailable),
|
|
2000
|
+
TRANSCRIPT_CREDENTIAL_SAFETY_PROMPT,
|
|
2137
2001
|
nativeCommandGuidance,
|
|
2138
2002
|
params.extraSystemPrompt
|
|
2139
2003
|
].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
|
|
2140
2004
|
}
|
|
2141
2005
|
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`.
|
|
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`
|
|
2006
|
+
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`. Set `final=true`, or omit it, for the completed reply to the current source conversation; OpenClaw stops after confirming delivery. Do not repeat visible message content in your final answer.";
|
|
2007
|
+
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` for supported non-text actions in the current conversation, such as reacting to its current message. Reserve other `message` actions for explicit out-of-band sends or media/file delivery. Reactions are not delivered automatically.";
|
|
2144
2008
|
return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation.";
|
|
2145
2009
|
}
|
|
2146
2010
|
//#endregion
|
|
@@ -2149,16 +2013,13 @@ const CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG = {
|
|
|
2149
2013
|
"features.standalone_web_search": false,
|
|
2150
2014
|
web_search: "disabled"
|
|
2151
2015
|
};
|
|
2152
|
-
function normalizeOptionalString$1(value) {
|
|
2153
|
-
return typeof value === "string" ? value.trim() || void 0 : void 0;
|
|
2154
|
-
}
|
|
2155
2016
|
function normalizeUniqueStrings(value) {
|
|
2156
2017
|
if (!Array.isArray(value)) return;
|
|
2157
|
-
const normalized = [...new Set(value.map(normalizeOptionalString
|
|
2018
|
+
const normalized = [...new Set(value.map(normalizeOptionalString).filter((entry) => Boolean(entry)))];
|
|
2158
2019
|
return normalized.length > 0 ? normalized : void 0;
|
|
2159
2020
|
}
|
|
2160
2021
|
function hasManagedSearchProvider(config) {
|
|
2161
|
-
return normalizeOptionalString
|
|
2022
|
+
return normalizeOptionalString(config?.tools?.web?.search?.provider) !== void 0;
|
|
2162
2023
|
}
|
|
2163
2024
|
function hasNativeDomainRestrictions(config) {
|
|
2164
2025
|
return normalizeUniqueStrings(config?.tools?.web?.search?.openaiCodex?.allowedDomains) !== void 0;
|
|
@@ -2173,10 +2034,10 @@ function buildCodexNativeWebSearchThreadConfig(config) {
|
|
|
2173
2034
|
if (allowedDomains) threadConfig["tools.web_search.allowed_domains"] = allowedDomains;
|
|
2174
2035
|
if (nativeConfig?.contextSize) threadConfig["tools.web_search.context_size"] = nativeConfig.contextSize;
|
|
2175
2036
|
const location = nativeConfig?.userLocation;
|
|
2176
|
-
const country = normalizeOptionalString
|
|
2177
|
-
const region = normalizeOptionalString
|
|
2178
|
-
const city = normalizeOptionalString
|
|
2179
|
-
const timezone = normalizeOptionalString
|
|
2037
|
+
const country = normalizeOptionalString(location?.country);
|
|
2038
|
+
const region = normalizeOptionalString(location?.region);
|
|
2039
|
+
const city = normalizeOptionalString(location?.city);
|
|
2040
|
+
const timezone = normalizeOptionalString(location?.timezone);
|
|
2180
2041
|
if (country) threadConfig["tools.web_search.location.country"] = country;
|
|
2181
2042
|
if (region) threadConfig["tools.web_search.location.region"] = region;
|
|
2182
2043
|
if (city) threadConfig["tools.web_search.location.city"] = city;
|
|
@@ -2223,26 +2084,50 @@ const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG = {
|
|
|
2223
2084
|
const CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG = { project_doc_max_bytes: 0 };
|
|
2224
2085
|
const CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG = { "features.multi_agent": false };
|
|
2225
2086
|
const CODEX_DELEGATION_DISABLED_THREAD_CONFIG = {
|
|
2087
|
+
"agents.enabled": false,
|
|
2226
2088
|
"features.multi_agent": false,
|
|
2227
2089
|
"features.multi_agent_v2": false
|
|
2228
2090
|
};
|
|
2091
|
+
const CODEX_RING_ZERO_RESTRICTED_FEATURES = /* @__PURE__ */ new Set([
|
|
2092
|
+
"apps",
|
|
2093
|
+
"artifact",
|
|
2094
|
+
"browser_use",
|
|
2095
|
+
"browser_use_external",
|
|
2096
|
+
"browser_use_full_cdp_access",
|
|
2097
|
+
"chronicle",
|
|
2098
|
+
"code_mode",
|
|
2099
|
+
"code_mode_only",
|
|
2100
|
+
"computer_use",
|
|
2101
|
+
"current_time_reminder",
|
|
2102
|
+
"default_mode_request_user_input",
|
|
2103
|
+
"deferred_executor",
|
|
2104
|
+
"goals",
|
|
2105
|
+
"hooks",
|
|
2106
|
+
"image_generation",
|
|
2107
|
+
"memories",
|
|
2108
|
+
"multi_agent",
|
|
2109
|
+
"multi_agent_v2",
|
|
2110
|
+
"plugins",
|
|
2111
|
+
"request_permissions_tool",
|
|
2112
|
+
"skill_search",
|
|
2113
|
+
"shell_tool",
|
|
2114
|
+
"standalone_web_search",
|
|
2115
|
+
"token_budget",
|
|
2116
|
+
"unified_exec",
|
|
2117
|
+
"view_image",
|
|
2118
|
+
"web_search_cached",
|
|
2119
|
+
"web_search_request",
|
|
2120
|
+
"workspace_dependencies"
|
|
2121
|
+
]);
|
|
2229
2122
|
const CODEX_RING_ZERO_THREAD_CONFIG = {
|
|
2230
|
-
|
|
2231
|
-
|
|
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,
|
|
2123
|
+
...CODEX_DELEGATION_DISABLED_THREAD_CONFIG,
|
|
2124
|
+
...Object.fromEntries([...CODEX_RING_ZERO_RESTRICTED_FEATURES].map((feature) => [`features.${feature}`, false])),
|
|
2243
2125
|
"orchestrator.mcp.enabled": false,
|
|
2244
2126
|
"orchestrator.skills.enabled": false,
|
|
2127
|
+
"skills.bundled.enabled": false,
|
|
2128
|
+
"skills.include_instructions": false,
|
|
2245
2129
|
"tools.experimental_request_user_input.enabled": false,
|
|
2130
|
+
"tools.update_plan.enabled": false,
|
|
2246
2131
|
hooks: {
|
|
2247
2132
|
PreToolUse: [],
|
|
2248
2133
|
PermissionRequest: [],
|
|
@@ -2259,22 +2144,13 @@ const CODEX_RING_ZERO_THREAD_CONFIG = {
|
|
|
2259
2144
|
notify: [],
|
|
2260
2145
|
web_search: "disabled"
|
|
2261
2146
|
};
|
|
2262
|
-
const
|
|
2263
|
-
"apps",
|
|
2264
|
-
"
|
|
2265
|
-
"
|
|
2266
|
-
"
|
|
2267
|
-
"
|
|
2268
|
-
"
|
|
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"
|
|
2147
|
+
const CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES = /* @__PURE__ */ new Map([
|
|
2148
|
+
["connectors", "apps"],
|
|
2149
|
+
["imagegenext", "image_generation"],
|
|
2150
|
+
["collab", "multi_agent"],
|
|
2151
|
+
["memory_tool", "memories"],
|
|
2152
|
+
["telepathy", "chronicle"],
|
|
2153
|
+
["codex_hooks", "hooks"]
|
|
2278
2154
|
]);
|
|
2279
2155
|
const CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
2280
2156
|
"mdm",
|
|
@@ -2320,7 +2196,7 @@ function buildThreadStartParams(params, options) {
|
|
|
2320
2196
|
webSearchAllowed: options.webSearchAllowed,
|
|
2321
2197
|
appServer: options.appServer,
|
|
2322
2198
|
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2323
|
-
|
|
2199
|
+
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames
|
|
2324
2200
|
}),
|
|
2325
2201
|
...resolveCodexThreadEnvironmentSelection(options),
|
|
2326
2202
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
|
|
@@ -2369,7 +2245,7 @@ function buildThreadResumeParams(params, options) {
|
|
|
2369
2245
|
webSearchAllowed: options.webSearchAllowed,
|
|
2370
2246
|
appServer: options.appServer,
|
|
2371
2247
|
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2372
|
-
|
|
2248
|
+
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames
|
|
2373
2249
|
}),
|
|
2374
2250
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
2375
2251
|
};
|
|
@@ -2408,9 +2284,15 @@ function resolveDirectOnlyToolNamespaces(dynamicTools) {
|
|
|
2408
2284
|
}
|
|
2409
2285
|
function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
2410
2286
|
const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
|
|
2287
|
+
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
|
|
2288
|
+
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
|
|
2411
2289
|
const configMcpServers = config?.mcp_servers;
|
|
2412
|
-
if (
|
|
2413
|
-
const
|
|
2290
|
+
if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
|
|
2291
|
+
const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
|
|
2292
|
+
const restrictedRunConfig = restrictedToolSurface && isJsonObject(configMcpServers) ? {
|
|
2293
|
+
...config,
|
|
2294
|
+
mcp_servers: {}
|
|
2295
|
+
} : config;
|
|
2414
2296
|
const webSearchConfig = resolveCodexWebSearchPlan({
|
|
2415
2297
|
config: params.config,
|
|
2416
2298
|
disableTools: params.disableTools,
|
|
@@ -2418,8 +2300,8 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2418
2300
|
nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
|
|
2419
2301
|
webSearchAllowed: options.webSearchAllowed
|
|
2420
2302
|
}).threadConfig;
|
|
2421
|
-
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(
|
|
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,
|
|
2303
|
+
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(restrictedRunConfig, webSearchConfig), options);
|
|
2304
|
+
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, messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true ? buildCodexRestrictedToolThreadConfigPatch(restrictedToolSurfaceMcpServerNames) : buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, restrictedToolSurfaceMcpServerNames)) ?? baseConfig;
|
|
2423
2305
|
if (params.bootstrapContextMode !== "lightweight") return runtimeConfig;
|
|
2424
2306
|
return mergeCodexThreadConfigs(runtimeConfig, CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG) ?? {
|
|
2425
2307
|
...runtimeConfig,
|
|
@@ -2428,6 +2310,9 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2428
2310
|
}
|
|
2429
2311
|
function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
|
|
2430
2312
|
if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
|
|
2313
|
+
return buildCodexRestrictedToolThreadConfigPatch(inheritedMcpServerNames);
|
|
2314
|
+
}
|
|
2315
|
+
function buildCodexRestrictedToolThreadConfigPatch(inheritedMcpServerNames) {
|
|
2431
2316
|
const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
|
|
2432
2317
|
return {
|
|
2433
2318
|
...CODEX_RING_ZERO_THREAD_CONFIG,
|
|
@@ -2443,15 +2328,15 @@ async function readCodexInheritedMcpServerNames(client, cwd, signal) {
|
|
|
2443
2328
|
if (!Array.isArray(response.layers)) throw new Error("Codex config/read omitted effective config layers");
|
|
2444
2329
|
for (const layer of response.layers) {
|
|
2445
2330
|
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
|
|
2447
|
-
if (!CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES.has(layer.name.type)) throw new Error(`Codex
|
|
2331
|
+
if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm") throw new Error(`Codex restricted tool surface cannot override config layer ${layer.name.type}`);
|
|
2332
|
+
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
2333
|
}
|
|
2449
2334
|
const configuredServers = response.config.mcp_servers;
|
|
2450
2335
|
if (configuredServers === void 0) return [];
|
|
2451
2336
|
if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
|
|
2452
2337
|
return Object.keys(configuredServers).toSorted();
|
|
2453
2338
|
}
|
|
2454
|
-
async function
|
|
2339
|
+
async function assertCodexRestrictedToolSurfaceHasNoManagedHooks(client, signal) {
|
|
2455
2340
|
const response = await client.request("configRequirements/read", void 0, { signal });
|
|
2456
2341
|
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
|
|
2457
2342
|
if (response.requirements === null) return;
|
|
@@ -2464,7 +2349,7 @@ async function assertCodexRingZeroHasNoManagedHooks(client, signal) {
|
|
|
2464
2349
|
const hooks = response.requirements[key];
|
|
2465
2350
|
if (hooks === void 0 || hooks === null) continue;
|
|
2466
2351
|
if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
|
|
2467
|
-
if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex
|
|
2352
|
+
if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex restricted tool surface cannot override managed hooks");
|
|
2468
2353
|
}
|
|
2469
2354
|
for (const key of ["featureRequirements", "feature_requirements"]) {
|
|
2470
2355
|
const requirements = response.requirements[key];
|
|
@@ -2472,22 +2357,35 @@ async function assertCodexRingZeroHasNoManagedHooks(client, signal) {
|
|
|
2472
2357
|
if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2473
2358
|
for (const [feature, enabled] of Object.entries(requirements)) {
|
|
2474
2359
|
if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2475
|
-
|
|
2360
|
+
const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
|
|
2361
|
+
if (enabled && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature)) throw new Error(`Codex restricted tool surface cannot override required feature ${feature}`);
|
|
2476
2362
|
}
|
|
2477
2363
|
}
|
|
2478
2364
|
}
|
|
2479
|
-
async function
|
|
2365
|
+
async function attestCodexRestrictedToolSurfaceMcpServersDisabled(client, threadId, threadConfig, signal) {
|
|
2366
|
+
const configuredServers = threadConfig?.mcp_servers;
|
|
2367
|
+
if (configuredServers !== void 0 && !isJsonObject(configuredServers)) throw new Error("Codex restricted-tool-surface thread config has invalid mcp_servers");
|
|
2368
|
+
const expectedDisabledServerNames = /* @__PURE__ */ new Set();
|
|
2369
|
+
for (const [name, serverConfig] of Object.entries(configuredServers ?? {})) {
|
|
2370
|
+
if (!isJsonObject(serverConfig) || serverConfig.enabled !== false) throw new Error(`Codex restricted-tool-surface MCP server ${name} is not disabled`);
|
|
2371
|
+
expectedDisabledServerNames.add(name);
|
|
2372
|
+
}
|
|
2480
2373
|
const response = await client.request("mcpServerStatus/list", {
|
|
2481
2374
|
threadId,
|
|
2482
|
-
limit: 1,
|
|
2483
2375
|
detail: "toolsAndAuthOnly"
|
|
2484
2376
|
}, { signal });
|
|
2485
|
-
if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned an invalid
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
throw new Error(`Codex
|
|
2490
|
-
|
|
2377
|
+
if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned an invalid restricted-tool-surface attestation");
|
|
2378
|
+
const observedDisabledServerNames = /* @__PURE__ */ new Set();
|
|
2379
|
+
for (const status of response.data) {
|
|
2380
|
+
if (!isJsonObject(status) || typeof status.name !== "string" || !isJsonObject(status.tools)) throw new Error("Codex mcpServerStatus/list returned an invalid restricted-tool-surface server");
|
|
2381
|
+
if (!expectedDisabledServerNames.has(status.name)) throw new Error(`Codex restricted-tool-surface MCP attestation found unexpected server ${status.name}`);
|
|
2382
|
+
if (observedDisabledServerNames.has(status.name)) throw new Error(`Codex restricted-tool-surface MCP attestation returned duplicate server ${status.name}`);
|
|
2383
|
+
observedDisabledServerNames.add(status.name);
|
|
2384
|
+
if (!Object.hasOwn(status, "serverInfo")) throw new Error(`Codex restricted-tool-surface MCP attestation returned malformed server ${status.name}`);
|
|
2385
|
+
if (status.serverInfo !== null) throw new Error(`Codex restricted-tool-surface MCP attestation found active server ${status.name}`);
|
|
2386
|
+
if (Object.keys(status.tools).length > 0) throw new Error(`Codex restricted-tool-surface MCP attestation found tools for server ${status.name}`);
|
|
2387
|
+
}
|
|
2388
|
+
for (const expectedName of expectedDisabledServerNames) if (!observedDisabledServerNames.has(expectedName)) throw new Error(`Codex restricted-tool-surface MCP attestation is missing server ${expectedName}`);
|
|
2491
2389
|
if (response.nextCursor !== void 0 && response.nextCursor !== null) throw new Error("Codex mcpServerStatus/list returned an invalid empty-page cursor");
|
|
2492
2390
|
}
|
|
2493
2391
|
function hasNonEmptyJsonValue(value) {
|
|
@@ -2509,32 +2407,6 @@ function resolveCodexThreadEnvironmentSelection(options) {
|
|
|
2509
2407
|
return {};
|
|
2510
2408
|
}
|
|
2511
2409
|
//#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
2410
|
//#region extensions/codex/src/app-server/thread-lifecycle-io.ts
|
|
2539
2411
|
function resolveCodexThreadRolloutPath(thread) {
|
|
2540
2412
|
const rolloutPath = thread.path?.trim();
|
|
@@ -2542,7 +2414,7 @@ function resolveCodexThreadRolloutPath(thread) {
|
|
|
2542
2414
|
return rolloutPath;
|
|
2543
2415
|
}
|
|
2544
2416
|
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,
|
|
2417
|
+
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
2418
|
let resumeReservation;
|
|
2547
2419
|
try {
|
|
2548
2420
|
const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
@@ -2553,7 +2425,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2553
2425
|
configPatch: params.finalConfigPatch,
|
|
2554
2426
|
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
2555
2427
|
};
|
|
2556
|
-
const pluginAppsConfigPatch = params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0;
|
|
2428
|
+
const pluginAppsConfigPatch = context.prebuiltPluginThreadConfig?.configPatch ?? (params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0);
|
|
2557
2429
|
const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
|
|
2558
2430
|
const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
2559
2431
|
threadId: resumeBinding.threadId,
|
|
@@ -2570,7 +2442,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2570
2442
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2571
2443
|
webSearchAllowed: params.webSearchAllowed,
|
|
2572
2444
|
hostSystemAgentActive,
|
|
2573
|
-
|
|
2445
|
+
restrictedToolSurfaceInheritedMcpServerNames
|
|
2574
2446
|
}));
|
|
2575
2447
|
const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
|
|
2576
2448
|
throwIfAborted();
|
|
@@ -2589,16 +2461,17 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2589
2461
|
request: resumeParams,
|
|
2590
2462
|
signal: params.signal
|
|
2591
2463
|
}));
|
|
2592
|
-
if (ringZeroActive) try {
|
|
2593
|
-
await lifecycleTiming.measure("
|
|
2464
|
+
if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
|
|
2465
|
+
await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, resumeParams.config, params.signal));
|
|
2594
2466
|
} catch (error) {
|
|
2595
2467
|
await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
|
|
2596
|
-
throw new
|
|
2468
|
+
throw new CodexRestrictedToolSurfaceAttestationError(error);
|
|
2597
2469
|
}
|
|
2598
2470
|
throwIfAborted();
|
|
2599
2471
|
const boundAuthProfileId = authProfileId;
|
|
2600
2472
|
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
|
|
2601
2473
|
const resumePatch = {
|
|
2474
|
+
clientId: resolveCodexAppServerClientInstanceId(params.client),
|
|
2602
2475
|
cwd: params.cwd,
|
|
2603
2476
|
rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
|
|
2604
2477
|
authProfileId: boundAuthProfileId,
|
|
@@ -2611,15 +2484,17 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2611
2484
|
nativeSkillIsolationFingerprint,
|
|
2612
2485
|
userMcpServersFingerprint,
|
|
2613
2486
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2487
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
2614
2488
|
ringZeroConfigFingerprint,
|
|
2615
2489
|
ringZeroClientInstanceId,
|
|
2490
|
+
nativeToolPolicyRestricted: restrictedToolSurface ? true : void 0,
|
|
2616
2491
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2617
2492
|
networkProxyConfigFingerprint,
|
|
2618
2493
|
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
|
|
2619
2494
|
appServerRuntimeFingerprint: resumeBinding.connectionScope === "supervision" ? buildCodexAppServerConnectionFingerprint(params.appServer, params.params.agentDir) : params.appServerRuntimeFingerprint,
|
|
2620
|
-
pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
|
|
2621
|
-
pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
|
|
2622
|
-
pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
|
|
2495
|
+
pluginAppsFingerprint: context.prebuiltPluginThreadConfig?.fingerprint ?? resumeBinding.pluginAppsFingerprint,
|
|
2496
|
+
pluginAppsInputFingerprint: context.prebuiltPluginThreadConfig?.inputFingerprint ?? resumeBinding.pluginAppsInputFingerprint,
|
|
2497
|
+
pluginAppPolicyContext: context.prebuiltPluginThreadConfig?.policyContext ?? resumeBinding.pluginAppPolicyContext,
|
|
2623
2498
|
contextEngine: contextEngineBinding,
|
|
2624
2499
|
environmentSelectionFingerprint
|
|
2625
2500
|
};
|
|
@@ -2665,8 +2540,8 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2665
2540
|
} catch (error) {
|
|
2666
2541
|
resumeReservation?.release();
|
|
2667
2542
|
if (isCodexAppServerStartSelectionChangedError(error)) throw error;
|
|
2668
|
-
if (error instanceof
|
|
2669
|
-
await clearCurrentBinding("retiring a failed
|
|
2543
|
+
if (error instanceof CodexRestrictedToolSurfaceAttestationError) {
|
|
2544
|
+
await clearCurrentBinding("retiring a failed restricted-tool-surface attestation");
|
|
2670
2545
|
throw error;
|
|
2671
2546
|
}
|
|
2672
2547
|
if (error instanceof CodexAdoptedThreadActiveError) throw error;
|
|
@@ -2683,7 +2558,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
2683
2558
|
}
|
|
2684
2559
|
async function startFreshCodexThread(params, context) {
|
|
2685
2560
|
const clientId = resolveCodexAppServerClientInstanceId(params.client);
|
|
2686
|
-
const { bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, ringZeroActive,
|
|
2561
|
+
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
2562
|
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
2688
2563
|
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
2689
2564
|
configPatch: params.finalConfigPatch,
|
|
@@ -2704,7 +2579,7 @@ async function startFreshCodexThread(params, context) {
|
|
|
2704
2579
|
model: startModelSelection.model,
|
|
2705
2580
|
modelProvider: startModelProvider,
|
|
2706
2581
|
hostSystemAgentActive,
|
|
2707
|
-
|
|
2582
|
+
restrictedToolSurfaceInheritedMcpServerNames
|
|
2708
2583
|
}));
|
|
2709
2584
|
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
2710
2585
|
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
@@ -2735,13 +2610,27 @@ async function startFreshCodexThread(params, context) {
|
|
|
2735
2610
|
throw error;
|
|
2736
2611
|
}
|
|
2737
2612
|
const rolloutPath = resolveCodexThreadRolloutPath(response.thread);
|
|
2738
|
-
if (ringZeroActive) try {
|
|
2739
|
-
await lifecycleTiming.measure("
|
|
2613
|
+
if (ringZeroActive || isMessageOnlyCodexSourceReply(params.params) || params.params.pluginHarnessToolPolicyRestricted === true) try {
|
|
2614
|
+
await lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, response.thread.id, startParams.config, params.signal));
|
|
2740
2615
|
} catch (error) {
|
|
2741
2616
|
await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
|
|
2742
2617
|
throw error;
|
|
2743
2618
|
}
|
|
2744
|
-
|
|
2619
|
+
try {
|
|
2620
|
+
throwIfAborted();
|
|
2621
|
+
} catch (error) {
|
|
2622
|
+
if (replacementPredecessor) {
|
|
2623
|
+
if (!await discardUnattestedCodexPluginThread({
|
|
2624
|
+
client: params.client,
|
|
2625
|
+
threadId: response.thread.id,
|
|
2626
|
+
ephemeral: startParams.ephemeral === true
|
|
2627
|
+
})) {
|
|
2628
|
+
await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
|
|
2629
|
+
throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after an aborted binding replacement", { cause: error });
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
throw error;
|
|
2633
|
+
}
|
|
2745
2634
|
const modelProvider = resolveCodexAppServerModelProvider({
|
|
2746
2635
|
provider: params.params.provider,
|
|
2747
2636
|
authProfileId: params.params.authProfileId,
|
|
@@ -2752,36 +2641,63 @@ async function startFreshCodexThread(params, context) {
|
|
|
2752
2641
|
const bindingModelProvider = normalizeBindingModelProvider(params.params.authProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider);
|
|
2753
2642
|
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
|
|
2754
2643
|
if (!preserveExistingBinding) {
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2644
|
+
const nextBinding = {
|
|
2645
|
+
threadId: response.thread.id,
|
|
2646
|
+
...clientId ? { clientId } : {},
|
|
2647
|
+
cwd: params.cwd,
|
|
2648
|
+
...rolloutPath ? { rolloutPath } : {},
|
|
2649
|
+
authProfileId: params.params.authProfileId,
|
|
2650
|
+
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
2651
|
+
modelProvider: bindingModelProvider,
|
|
2652
|
+
dynamicToolsFingerprint,
|
|
2653
|
+
dynamicToolsContainDeferred,
|
|
2654
|
+
webSearchThreadConfigFingerprint,
|
|
2655
|
+
nativeSkillIsolationFingerprint,
|
|
2656
|
+
userMcpServersFingerprint,
|
|
2657
|
+
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2658
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
2659
|
+
ringZeroConfigFingerprint,
|
|
2660
|
+
ringZeroClientInstanceId,
|
|
2661
|
+
nativeToolPolicyRestricted: restrictedToolSurface ? true : void 0,
|
|
2662
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2663
|
+
networkProxyConfigFingerprint,
|
|
2664
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
2665
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
2666
|
+
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
2667
|
+
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
2668
|
+
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
2669
|
+
contextEngine: contextEngineBinding,
|
|
2670
|
+
environmentSelectionFingerprint
|
|
2671
|
+
};
|
|
2672
|
+
const cleanupUncommittedSuccessor = async (cause) => {
|
|
2673
|
+
if (!await discardUnattestedCodexPluginThread({
|
|
2674
|
+
client: params.client,
|
|
2759
2675
|
threadId: response.thread.id,
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
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
|
|
2676
|
+
ephemeral: startParams.ephemeral === true
|
|
2677
|
+
})) {
|
|
2678
|
+
await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
|
|
2679
|
+
throw new CodexAppServerUnsafeSubscriptionError("Codex successor cleanup failed after a binding replacement conflict", cause === void 0 ? void 0 : { cause });
|
|
2783
2680
|
}
|
|
2784
|
-
}
|
|
2681
|
+
};
|
|
2682
|
+
let committed;
|
|
2683
|
+
try {
|
|
2684
|
+
committed = await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, replacementPredecessor ? {
|
|
2685
|
+
kind: "replace-thread",
|
|
2686
|
+
expectedThreadId: replacementPredecessor.threadId,
|
|
2687
|
+
binding: nextBinding
|
|
2688
|
+
} : {
|
|
2689
|
+
kind: "set",
|
|
2690
|
+
if: { kind: "absent" },
|
|
2691
|
+
binding: nextBinding
|
|
2692
|
+
}));
|
|
2693
|
+
} catch (error) {
|
|
2694
|
+
if (replacementPredecessor) await cleanupUncommittedSuccessor(error);
|
|
2695
|
+
throw error;
|
|
2696
|
+
}
|
|
2697
|
+
if (!committed) {
|
|
2698
|
+
if (replacementPredecessor) await cleanupUncommittedSuccessor();
|
|
2699
|
+
throw new CodexThreadBindingConflictError(replacementPredecessor?.threadId ?? response.thread.id, "committing a fresh thread");
|
|
2700
|
+
}
|
|
2785
2701
|
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
2786
2702
|
sessionId: params.params.sessionId,
|
|
2787
2703
|
sessionKey: params.params.sessionKey,
|
|
@@ -2813,6 +2729,7 @@ async function startFreshCodexThread(params, context) {
|
|
|
2813
2729
|
nativeSkillIsolationFingerprint,
|
|
2814
2730
|
userMcpServersFingerprint,
|
|
2815
2731
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2732
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
2816
2733
|
ringZeroConfigFingerprint,
|
|
2817
2734
|
ringZeroClientInstanceId,
|
|
2818
2735
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
@@ -2979,13 +2896,15 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
2979
2896
|
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
2980
2897
|
const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
|
|
2981
2898
|
const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
|
|
2982
|
-
|
|
2983
|
-
const
|
|
2984
|
-
if (
|
|
2899
|
+
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params.params);
|
|
2900
|
+
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.params.pluginHarnessToolPolicyRestricted === true;
|
|
2901
|
+
if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
|
|
2902
|
+
const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
|
|
2903
|
+
if (restrictedToolSurface) await lifecycleTiming.measure("restricted-tool-surface-config-requirements-read", () => assertCodexRestrictedToolSurfaceHasNoManagedHooks(params.client, params.signal));
|
|
2985
2904
|
const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
|
|
2986
2905
|
version: 1,
|
|
2987
2906
|
baseInstructions: "",
|
|
2988
|
-
config: buildCodexRingZeroThreadConfigPatch(params.params, true,
|
|
2907
|
+
config: buildCodexRingZeroThreadConfigPatch(params.params, true, restrictedToolSurfaceInheritedMcpServerNames)
|
|
2989
2908
|
}) : void 0;
|
|
2990
2909
|
return {
|
|
2991
2910
|
contextEngineBinding,
|
|
@@ -3002,7 +2921,8 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
3002
2921
|
ringZeroActive,
|
|
3003
2922
|
ringZeroClientInstanceId: ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0,
|
|
3004
2923
|
ringZeroConfigFingerprint,
|
|
3005
|
-
|
|
2924
|
+
restrictedToolSurface,
|
|
2925
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3006
2926
|
userMcpServersConfigPatch,
|
|
3007
2927
|
userMcpServersFingerprint,
|
|
3008
2928
|
webSearchThreadConfigFingerprint
|
|
@@ -3034,8 +2954,8 @@ async function releaseCodexRetainedLiveThread(options) {
|
|
|
3034
2954
|
}
|
|
3035
2955
|
/** Reuses one safely owned, fully matching subscription on its original client. */
|
|
3036
2956
|
async function tryReuseCodexLiveThread(options) {
|
|
3037
|
-
const { params, binding, bindingIdentity, clientId,
|
|
3038
|
-
if (!binding.clientId || binding.clientId !== clientId || binding.preserveNativeModel === true || binding.connectionScope === "supervision" || ringZeroActive
|
|
2957
|
+
const { params, binding, bindingIdentity, clientId, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, lifecycleTiming, nativeSkillIsolation, releaseConsumedThread, ringZeroActive, restrictedToolSurfaceInheritedMcpServerNames, startModelProvider, startModelSelection, throwIfAborted, userMcpServersConfigPatch } = options;
|
|
2958
|
+
if (!binding.clientId || binding.clientId !== clientId || binding.preserveNativeModel === true || binding.connectionScope === "supervision" || ringZeroActive) return {};
|
|
3039
2959
|
const prebuiltFinalConfigPatch = params.buildFinalConfigPatch?.({
|
|
3040
2960
|
action: "resume",
|
|
3041
2961
|
binding
|
|
@@ -3061,7 +2981,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3061
2981
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
3062
2982
|
webSearchAllowed: params.webSearchAllowed,
|
|
3063
2983
|
hostSystemAgentActive,
|
|
3064
|
-
|
|
2984
|
+
restrictedToolSurfaceInheritedMcpServerNames
|
|
3065
2985
|
}));
|
|
3066
2986
|
const liveThreadConfigFingerprint = fingerprintCodexThreadConfig({
|
|
3067
2987
|
...resumeParams,
|
|
@@ -3070,13 +2990,24 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3070
2990
|
modelProvider: binding.modelProvider ?? resumeParams.modelProvider ?? null,
|
|
3071
2991
|
requestedModelProvider: resumeParams.modelProvider ?? binding.modelProvider ?? null
|
|
3072
2992
|
}, resumeAuthProfileId, dynamicToolsFingerprint);
|
|
3073
|
-
|
|
2993
|
+
const retainedThread = await consumeCodexAppServerLiveThread(params.client, binding.threadId, liveThreadConfigFingerprint);
|
|
2994
|
+
if (!retainedThread) {
|
|
2995
|
+
const incompatibleOwnership = await consumeCodexAppServerLiveThread(params.client, binding.threadId);
|
|
2996
|
+
if (incompatibleOwnership) await incompatibleOwnership.release(binding.threadId);
|
|
2997
|
+
return { prebuiltFinalConfigPatch };
|
|
2998
|
+
}
|
|
3074
2999
|
try {
|
|
3075
3000
|
const nativeHookRelayGeneration = prebuiltFinalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration;
|
|
3001
|
+
const model = startModelSelection.model;
|
|
3076
3002
|
if (!await lifecycleTiming.measure("warm-thread-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
|
|
3077
3003
|
kind: "patch",
|
|
3078
3004
|
threadId: binding.threadId,
|
|
3079
|
-
patch: {
|
|
3005
|
+
patch: {
|
|
3006
|
+
cwd: params.cwd,
|
|
3007
|
+
model,
|
|
3008
|
+
nativeHookRelayGeneration,
|
|
3009
|
+
environmentSelectionFingerprint
|
|
3010
|
+
}
|
|
3080
3011
|
}))) throw new CodexThreadBindingConflictError(binding.threadId, "committing a reused thread");
|
|
3081
3012
|
throwIfAborted();
|
|
3082
3013
|
lifecycleTiming.mark("thread-ready");
|
|
@@ -3090,8 +3021,13 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3090
3021
|
return {
|
|
3091
3022
|
binding: {
|
|
3092
3023
|
...binding,
|
|
3024
|
+
cwd: params.cwd,
|
|
3025
|
+
model,
|
|
3093
3026
|
nativeHookRelayGeneration,
|
|
3027
|
+
environmentSelectionFingerprint,
|
|
3094
3028
|
liveThreadConfigFingerprint,
|
|
3029
|
+
liveThreadOwnership: retainedThread,
|
|
3030
|
+
...retainedThread.serviceTier && resumeParams.serviceTier === void 0 ? { clearInheritedServiceTier: true } : {},
|
|
3095
3031
|
lifecycle: { action: "resumed" }
|
|
3096
3032
|
},
|
|
3097
3033
|
prebuiltFinalConfigPatch
|
|
@@ -3143,6 +3079,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3143
3079
|
pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId]);
|
|
3144
3080
|
params.throwIfAborted();
|
|
3145
3081
|
const probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
|
|
3082
|
+
if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, probeThreadId, probeParams.config ?? void 0, params.signal));
|
|
3146
3083
|
const nativeModel = requireNonBlankSupervisionValue(probeResponse.model, "native model");
|
|
3147
3084
|
const nativeModelProvider = requireNativeSupervisionModelProvider({
|
|
3148
3085
|
responseModelProvider: probeResponse.modelProvider,
|
|
@@ -3163,7 +3100,9 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3163
3100
|
webSearchAllowed: params.webSearchAllowed,
|
|
3164
3101
|
environmentSelection: params.environmentSelection,
|
|
3165
3102
|
model: nativeModel,
|
|
3166
|
-
modelProvider: nativeModelProvider
|
|
3103
|
+
modelProvider: nativeModelProvider,
|
|
3104
|
+
hostSystemAgentActive: params.hostSystemAgentActive,
|
|
3105
|
+
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
|
|
3167
3106
|
});
|
|
3168
3107
|
assertExactSupervisionModelSelection(startParams, {
|
|
3169
3108
|
model: nativeModel,
|
|
@@ -3191,6 +3130,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3191
3130
|
modelProvider: nativeModelProvider,
|
|
3192
3131
|
operation: "thread/start response"
|
|
3193
3132
|
});
|
|
3133
|
+
if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, finalThreadId, startParams.config, params.signal));
|
|
3194
3134
|
if (params.provisionalAppIds?.length) try {
|
|
3195
3135
|
await params.lifecycleTiming.measure("plugin-app-attestation", () => attestCodexPluginThreadApps({
|
|
3196
3136
|
client: params.client,
|
|
@@ -3316,7 +3256,9 @@ function buildPendingSupervisionProbeForkParams(params, pending) {
|
|
|
3316
3256
|
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
3317
3257
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
3318
3258
|
webSearchAllowed: params.webSearchAllowed,
|
|
3319
|
-
appServer: params.appServer
|
|
3259
|
+
appServer: params.appServer,
|
|
3260
|
+
hostSystemAgentActive: params.hostSystemAgentActive,
|
|
3261
|
+
restrictedToolSurfaceInheritedMcpServerNames: params.restrictedToolSurfaceInheritedMcpServerNames
|
|
3320
3262
|
});
|
|
3321
3263
|
return {
|
|
3322
3264
|
threadId: pending.sourceThreadId,
|
|
@@ -3453,9 +3395,11 @@ async function startOrResumeThread(params) {
|
|
|
3453
3395
|
config: params.params.config
|
|
3454
3396
|
});
|
|
3455
3397
|
return await params.bindingStore.withLease(bindingIdentity, async () => {
|
|
3456
|
-
const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint,
|
|
3398
|
+
const { contextEngineBinding, dynamicToolsContainDeferred, dynamicToolsFingerprint, environmentSelectionFingerprint, hostSystemAgentActive, legacyDynamicToolsFingerprint, legacyUserMcpServersFingerprint, lifecycleTiming, nativeSkillIsolation, nativeSkillIsolationFingerprint, networkProxyConfigFingerprint, ringZeroActive, ringZeroClientInstanceId, ringZeroConfigFingerprint, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, userMcpServersConfigPatch, userMcpServersFingerprint, webSearchThreadConfigFingerprint } = await prepareCodexThreadLifecyclePreflight(params);
|
|
3457
3399
|
let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
|
|
3400
|
+
let replacementPredecessor;
|
|
3458
3401
|
const initialBoundThreadId = binding?.threadId;
|
|
3402
|
+
const initialBoundClientId = binding?.clientId;
|
|
3459
3403
|
const normalizeBindingModelProvider = (authProfileId, modelProvider) => normalizeCodexAppServerBindingModelProvider({
|
|
3460
3404
|
authProfileId,
|
|
3461
3405
|
modelProvider,
|
|
@@ -3464,12 +3408,28 @@ async function startOrResumeThread(params) {
|
|
|
3464
3408
|
config: params.params.config
|
|
3465
3409
|
});
|
|
3466
3410
|
const throwIfAborted = () => throwIfCodexThreadLifecycleAborted(params.signal);
|
|
3467
|
-
const releaseRetainedThread = (threadId) =>
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3411
|
+
const releaseRetainedThread = async (threadId, ownerClientId = initialBoundClientId) => {
|
|
3412
|
+
if (ownerClientId && ownerClientId !== clientId) {
|
|
3413
|
+
const previousClient = retainSharedCodexAppServerClientByInstanceId(ownerClientId);
|
|
3414
|
+
if (!previousClient) return;
|
|
3415
|
+
try {
|
|
3416
|
+
await releaseCodexRetainedLiveThread({
|
|
3417
|
+
client: previousClient.client,
|
|
3418
|
+
lifecycleTiming,
|
|
3419
|
+
threadId
|
|
3420
|
+
});
|
|
3421
|
+
} finally {
|
|
3422
|
+
previousClient.release();
|
|
3423
|
+
}
|
|
3424
|
+
return;
|
|
3425
|
+
}
|
|
3426
|
+
await releaseCodexRetainedLiveThread({
|
|
3427
|
+
client: params.client,
|
|
3428
|
+
abandonClient: params.abandonClient,
|
|
3429
|
+
lifecycleTiming,
|
|
3430
|
+
threadId
|
|
3431
|
+
});
|
|
3432
|
+
};
|
|
3473
3433
|
if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
|
|
3474
3434
|
if (!await lifecycleTiming.measure("reclaim-binding-generation", () => reclaimCurrentCodexSessionGeneration({
|
|
3475
3435
|
bindingStore: params.bindingStore,
|
|
@@ -3502,6 +3462,9 @@ async function startOrResumeThread(params) {
|
|
|
3502
3462
|
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
3503
3463
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
3504
3464
|
webSearchAllowed: params.webSearchAllowed,
|
|
3465
|
+
hostSystemAgentActive,
|
|
3466
|
+
restrictedToolSurface,
|
|
3467
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3505
3468
|
environmentSelection: params.environmentSelection,
|
|
3506
3469
|
provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
|
|
3507
3470
|
signal: params.signal,
|
|
@@ -3519,6 +3482,7 @@ async function startOrResumeThread(params) {
|
|
|
3519
3482
|
nativeSkillIsolationFingerprint,
|
|
3520
3483
|
userMcpServersFingerprint,
|
|
3521
3484
|
mcpServersFingerprint: params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : pendingBinding.mcpServersFingerprint,
|
|
3485
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
3522
3486
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
3523
3487
|
networkProxyConfigFingerprint,
|
|
3524
3488
|
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
@@ -3542,6 +3506,7 @@ async function startOrResumeThread(params) {
|
|
|
3542
3506
|
})) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
3543
3507
|
binding = void 0;
|
|
3544
3508
|
};
|
|
3509
|
+
if (binding?.threadId && !restrictedToolSurface && binding.nativeToolPolicyRestricted === true) await clearCurrentBinding("rotating a host-policy-restricted thread binding");
|
|
3545
3510
|
if (binding?.threadId && binding.nativeSkillIsolationFingerprint !== nativeSkillIsolationFingerprint) {
|
|
3546
3511
|
embeddedAgentLog.debug("codex app-server native skill isolation changed; starting a new thread", { threadId: binding.threadId });
|
|
3547
3512
|
await clearCurrentBinding("rotating stale native skill isolation");
|
|
@@ -3594,6 +3559,14 @@ async function startOrResumeThread(params) {
|
|
|
3594
3559
|
const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
|
|
3595
3560
|
const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
|
|
3596
3561
|
const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
|
|
3562
|
+
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) {
|
|
3563
|
+
const predecessorBinding = binding;
|
|
3564
|
+
assertCodexBindingMayBeReplaced(predecessorBinding, "changing configured MCP ownership");
|
|
3565
|
+
embeddedAgentLog.debug("codex app-server configured MCP ownership changed; starting a new thread", { threadId: predecessorBinding.threadId });
|
|
3566
|
+
replacementPredecessor = predecessorBinding;
|
|
3567
|
+
binding = void 0;
|
|
3568
|
+
preserveExistingBinding = false;
|
|
3569
|
+
}
|
|
3597
3570
|
if (binding?.threadId && params.mcpServersFingerprintEvaluated === true && binding.mcpServersFingerprint !== params.mcpServersFingerprint) {
|
|
3598
3571
|
assertCodexBindingMayBeReplaced(binding, "changing MCP configuration");
|
|
3599
3572
|
if (!ringZeroActive && (transientNativeToolRestriction || webSearchBindingChanged && (explicitTransientWebSearchRestriction || unknownProviderWebSearchSupport))) {
|
|
@@ -3609,7 +3582,7 @@ async function startOrResumeThread(params) {
|
|
|
3609
3582
|
if (binding?.threadId && webSearchBindingChanged && !deferLegacyWebSearchRotationToTransientNativeSurface) {
|
|
3610
3583
|
assertCodexBindingMayBeReplaced(binding, "changing web-search configuration");
|
|
3611
3584
|
if (!ringZeroActive && transientWebSearchRestriction) {
|
|
3612
|
-
embeddedAgentLog.debug("codex app-server
|
|
3585
|
+
embeddedAgentLog.debug("codex app-server tool surface restricted for turn; starting transient thread", { threadId: binding.threadId });
|
|
3613
3586
|
preserveExistingBinding = true;
|
|
3614
3587
|
} else {
|
|
3615
3588
|
embeddedAgentLog.debug("codex app-server web search config changed; starting a new thread", { threadId: binding.threadId });
|
|
@@ -3655,11 +3628,6 @@ async function startOrResumeThread(params) {
|
|
|
3655
3628
|
await clearCurrentBinding("rotating a stale thread binding");
|
|
3656
3629
|
binding = void 0;
|
|
3657
3630
|
}
|
|
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
3631
|
if (binding?.threadId && (binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint || binding.networkProxyProfileName !== params.appServer.networkProxy?.profileName)) {
|
|
3664
3632
|
embeddedAgentLog.debug("codex app-server network proxy config changed; starting a new thread", { threadId: binding.threadId });
|
|
3665
3633
|
await clearCurrentBinding("rotating a stale thread binding");
|
|
@@ -3673,13 +3641,15 @@ async function startOrResumeThread(params) {
|
|
|
3673
3641
|
currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
|
|
3674
3642
|
hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
|
|
3675
3643
|
});
|
|
3676
|
-
if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
|
|
3644
|
+
if (!pluginBindingStale && (params.pluginThreadConfig?.requiresCurrentPolicyCheck || shouldRecheckRecoverablePluginBinding({
|
|
3677
3645
|
binding,
|
|
3678
3646
|
pluginThreadConfig: params.pluginThreadConfig
|
|
3679
|
-
})) try {
|
|
3680
|
-
|
|
3647
|
+
}))) try {
|
|
3648
|
+
const bindingThreadId = binding.threadId;
|
|
3649
|
+
prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build({ threadId: bindingThreadId }));
|
|
3681
3650
|
pluginBindingStale = prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
|
|
3682
3651
|
} catch (error) {
|
|
3652
|
+
if (params.pluginThreadConfig?.requiresCurrentPolicyCheck) throw error;
|
|
3683
3653
|
embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
|
|
3684
3654
|
error,
|
|
3685
3655
|
threadId: binding.threadId
|
|
@@ -3737,8 +3707,8 @@ async function startOrResumeThread(params) {
|
|
|
3737
3707
|
binding,
|
|
3738
3708
|
bindingIdentity,
|
|
3739
3709
|
clientId,
|
|
3740
|
-
contextEngineBinding,
|
|
3741
3710
|
dynamicToolsFingerprint,
|
|
3711
|
+
environmentSelectionFingerprint,
|
|
3742
3712
|
hostSystemAgentActive,
|
|
3743
3713
|
lifecycleTiming,
|
|
3744
3714
|
nativeSkillIsolation,
|
|
@@ -3750,7 +3720,7 @@ async function startOrResumeThread(params) {
|
|
|
3750
3720
|
cause
|
|
3751
3721
|
}),
|
|
3752
3722
|
ringZeroActive,
|
|
3753
|
-
|
|
3723
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3754
3724
|
startModelProvider,
|
|
3755
3725
|
startModelSelection,
|
|
3756
3726
|
throwIfAborted,
|
|
@@ -3776,18 +3746,20 @@ async function startOrResumeThread(params) {
|
|
|
3776
3746
|
environmentSelectionFingerprint,
|
|
3777
3747
|
hostSystemAgentActive,
|
|
3778
3748
|
ringZeroActive,
|
|
3779
|
-
|
|
3749
|
+
restrictedToolSurface,
|
|
3750
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3780
3751
|
nativeSkillIsolation,
|
|
3781
3752
|
lifecycleTiming,
|
|
3782
3753
|
normalizeBindingModelProvider,
|
|
3783
3754
|
throwIfAborted,
|
|
3784
3755
|
clearCurrentBinding,
|
|
3785
|
-
prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch
|
|
3756
|
+
prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
|
|
3757
|
+
prebuiltPluginThreadConfig
|
|
3786
3758
|
});
|
|
3787
3759
|
if (resumed) return resumed;
|
|
3788
3760
|
}
|
|
3789
|
-
if (initialBoundThreadId) await releaseRetainedThread(initialBoundThreadId);
|
|
3790
|
-
|
|
3761
|
+
if (initialBoundThreadId && !preserveExistingBinding && !replacementPredecessor) await releaseRetainedThread(initialBoundThreadId);
|
|
3762
|
+
const started = await startFreshCodexThread(params, {
|
|
3791
3763
|
bindingIdentity,
|
|
3792
3764
|
startModelSelection,
|
|
3793
3765
|
startModelProvider,
|
|
@@ -3804,85 +3776,27 @@ async function startOrResumeThread(params) {
|
|
|
3804
3776
|
environmentSelectionFingerprint,
|
|
3805
3777
|
hostSystemAgentActive,
|
|
3806
3778
|
ringZeroActive,
|
|
3807
|
-
|
|
3779
|
+
restrictedToolSurface,
|
|
3780
|
+
restrictedToolSurfaceInheritedMcpServerNames,
|
|
3808
3781
|
nativeSkillIsolation,
|
|
3809
3782
|
lifecycleTiming,
|
|
3810
3783
|
normalizeBindingModelProvider,
|
|
3811
3784
|
throwIfAborted,
|
|
3812
3785
|
prebuiltPluginThreadConfig,
|
|
3813
3786
|
preserveExistingBinding,
|
|
3814
|
-
rotatedContextEngineBinding
|
|
3787
|
+
rotatedContextEngineBinding,
|
|
3788
|
+
replacementPredecessor
|
|
3815
3789
|
});
|
|
3790
|
+
if (replacementPredecessor) await releaseRetainedThread(replacementPredecessor.threadId, replacementPredecessor.clientId);
|
|
3791
|
+
return started;
|
|
3816
3792
|
});
|
|
3817
3793
|
}
|
|
3818
3794
|
//#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
3795
|
//#region extensions/codex/src/app-server/user-input.ts
|
|
3882
3796
|
/** Builds ordered Codex user input for both new turns and same-turn steering. */
|
|
3883
3797
|
function buildCodexUserInput(text, images) {
|
|
3884
3798
|
const imageInputs = (images ?? []).map((image) => {
|
|
3885
|
-
const imageUrl = sanitizeInlineImageDataUrl
|
|
3799
|
+
const imageUrl = sanitizeInlineImageDataUrl(`data:${image.mimeType};base64,${image.data}`);
|
|
3886
3800
|
return imageUrl ? {
|
|
3887
3801
|
type: "image",
|
|
3888
3802
|
url: imageUrl
|
|
@@ -3900,6 +3814,27 @@ function buildCodexUserInput(text, images) {
|
|
|
3900
3814
|
}
|
|
3901
3815
|
//#endregion
|
|
3902
3816
|
//#region extensions/codex/src/app-server/turn-params.ts
|
|
3817
|
+
const CODEX_CURRENT_SENDER_FIELD_MAX_CHARS = 256;
|
|
3818
|
+
function buildCodexCurrentSenderContextValue(params) {
|
|
3819
|
+
const metadata = asOptionalRecord(asOptionalRecord(params.userTurnTranscriptRecorder?.message)?.["__openclaw"]);
|
|
3820
|
+
const recorded = [
|
|
3821
|
+
normalizeOptionalString(metadata?.["senderId"]),
|
|
3822
|
+
normalizeOptionalString(metadata?.["senderName"]),
|
|
3823
|
+
normalizeOptionalString(metadata?.["senderUsername"])
|
|
3824
|
+
];
|
|
3825
|
+
const [id, name, username] = recorded.some(Boolean) ? recorded : [
|
|
3826
|
+
normalizeOptionalString(params.senderId),
|
|
3827
|
+
normalizeOptionalString(params.senderName),
|
|
3828
|
+
normalizeOptionalString(params.senderUsername)
|
|
3829
|
+
];
|
|
3830
|
+
if (!id && !name && !username) return;
|
|
3831
|
+
const bound = (value) => truncateUtf16Safe(value, CODEX_CURRENT_SENDER_FIELD_MAX_CHARS);
|
|
3832
|
+
return JSON.stringify({ sender: {
|
|
3833
|
+
...id ? { id: bound(id) } : {},
|
|
3834
|
+
...name ? { name: bound(name) } : {},
|
|
3835
|
+
...username ? { username: bound(username) } : {}
|
|
3836
|
+
} });
|
|
3837
|
+
}
|
|
3903
3838
|
function buildTurnStartParams(params, options) {
|
|
3904
3839
|
const modelSelection = options.preserveNativeTurnSettings ? void 0 : resolveCodexAppServerRequestModelSelection({
|
|
3905
3840
|
model: options.model ?? params.modelId,
|
|
@@ -3910,9 +3845,15 @@ function buildTurnStartParams(params, options) {
|
|
|
3910
3845
|
config: params.config
|
|
3911
3846
|
});
|
|
3912
3847
|
const useThreadPermissionProfile = options.appServer.networkProxy && !options.sandboxPolicy;
|
|
3848
|
+
const currentSenderContext = params.trigger === "user" ? buildCodexCurrentSenderContextValue(params) : void 0;
|
|
3849
|
+
const additionalContext = currentSenderContext ? { openclaw_current_sender: {
|
|
3850
|
+
kind: "untrusted",
|
|
3851
|
+
value: currentSenderContext
|
|
3852
|
+
} } : void 0;
|
|
3913
3853
|
return {
|
|
3914
3854
|
threadId: options.threadId,
|
|
3915
|
-
input: buildCodexUserInput(options.promptText ?? params.prompt, params.images),
|
|
3855
|
+
input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images), ...options.explicitSkillInputs ?? []],
|
|
3856
|
+
...additionalContext ? { additionalContext } : {},
|
|
3916
3857
|
cwd: options.cwd,
|
|
3917
3858
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
3918
3859
|
approvalsReviewer: options.appServer.approvalsReviewer,
|
|
@@ -3921,7 +3862,7 @@ function buildTurnStartParams(params, options) {
|
|
|
3921
3862
|
model: modelSelection.model,
|
|
3922
3863
|
personality: CODEX_NATIVE_PERSONALITY_NONE
|
|
3923
3864
|
} : {},
|
|
3924
|
-
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
3865
|
+
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : options.clearInheritedServiceTier ? { serviceTier: null } : {},
|
|
3925
3866
|
...modelSelection ? { effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model, readCodexSupportedReasoningEfforts(params.model?.compat)) } : {},
|
|
3926
3867
|
...options.environmentSelection ? { environments: options.environmentSelection } : {},
|
|
3927
3868
|
...modelSelection ? { collaborationMode: buildTurnCollaborationMode(params, {
|
|
@@ -3946,7 +3887,7 @@ function buildTurnCollaborationMode(params, options = {}) {
|
|
|
3946
3887
|
function buildTurnScopedCollaborationInstructions(params, options = {}) {
|
|
3947
3888
|
const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
|
|
3948
3889
|
if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
|
|
3949
|
-
if (params.trigger === "heartbeat"
|
|
3890
|
+
if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions);
|
|
3950
3891
|
if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
|
|
3951
3892
|
return null;
|
|
3952
3893
|
}
|
|
@@ -3984,4 +3925,4 @@ function joinPresentSections(...sections) {
|
|
|
3984
3925
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
3985
3926
|
}
|
|
3986
3927
|
//#endregion
|
|
3987
|
-
export {
|
|
3928
|
+
export { resolveCodexDynamicToolsLoadingForRuntime as A, buildCodexPluginThreadConfigInputFingerprint as B, codexLegacyDynamicToolsFingerprint as C, isSystemAgentOnlyCodexDynamicToolAllowlist as D, isForcedPrivateQaCodexRuntime as E, projectContextEngineAssemblyForCodex as F, shouldBuildCodexPluginThreadConfig as G, buildDisabledAppsConfigPatch as H, resolveCodexContextEngineProjectionMaxChars as I, isOpenAiCuratedMarketplace as J, ensureCodexPluginActivation as K, resolveCodexContextEngineProjectionReserveTokens as L, isContextEngineBindingCompatible as M, fitCodexProjectedContextForTurnStart as N, normalizeCodexDynamicToolName as O, neutralizeCodexExplicitMentionSigils as P, buildCodexPluginAppsConfigPatchFromPolicyContext as R, codexDynamicToolsFingerprint as S, filterCodexDynamicToolsForDisabledNativeSurface as T, buildPluginAppPolicyContext as U, buildCodexPluginThreadConfigTimeoutFallback as V, mergeCodexThreadConfigs as W, pluginReadParams as X, isOpenAiCuratedMarketplaceName as Y, resolveRecoverableCodexPluginConfigKeys as Z, resolveCodexAppServerThreadModelSelection as _, isCodexAppServerProfilerEnabled as a, readCodexSupportedReasoningEfforts as b, buildCodexRingZeroThreadConfigPatch as c, buildCodexNativeWebSearchThreadConfig as d, resolveCodexWebSearchPlan as f, resolveCodexAppServerRequestModelSelection as g, resolveCodexAppServerModelProvider as h, startOrResumeThread as i, buildContextEngineBinding as j, resolveCodexDynamicToolsLoading as k, buildCodexRuntimeThreadConfig as l, CODEX_NATIVE_PERSONALITY_NONE as m, buildTurnStartParams as n, assertCodexRestrictedToolSurfaceHasNoManagedHooks as o, buildDeveloperInstructions as p, refreshCodexPluginRuntimeState as q, buildCodexUserInput as r, attestCodexRestrictedToolSurfaceMcpServersDisabled as s, buildTurnCollaborationMode as t, readCodexInheritedMcpServerNames as u, resolveCodexBindingModelProviderFallback as v, filterCodexDynamicTools as w, areCodexDynamicToolFingerprintsCompatible as x, resolveReasoningEffort as y, buildCodexPluginThreadConfig as z };
|