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