@openclaw/codex 2026.7.1-beta.6 → 2026.7.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -3
- package/dist/app-server-policy-DVcxdse_.js +49 -0
- package/dist/{attempt-notifications-BGsEIIDI.js → attempt-notifications-DEv9h7iC.js} +1 -1
- package/dist/auth-binding-C1czuHPA.js +62 -0
- package/dist/capabilities-42Dfn2TV.js +33 -0
- package/dist/cli-metadata.js +20 -0
- package/dist/{app-server-policy-C968Kgin.js → command-formatters-D6ZlGNmb.js} +8 -51
- package/dist/{command-handlers-Dv-pzAmg.js → command-handlers-Cpl9fUWv.js} +163 -60
- package/dist/{compact-4mVBriKT.js → compact-FnJmiPNA.js} +40 -10
- package/dist/computer-use-DVzr8OP1.js +685 -0
- package/dist/{config-CYEDnLJ2.js → config-c48K5HP9.js} +200 -29
- package/dist/conversation-control-DtQ-E7mr.js +288 -0
- package/dist/doctor-contract-api.js +120 -81
- package/dist/{provider-capabilities-CDnHbmUZ.js → dynamic-tools-BMLoaTeG.js} +5246 -5140
- package/dist/harness.js +55 -15
- package/dist/index.js +1435 -265
- package/dist/{media-understanding-provider-BHOu9-Gg.js → media-understanding-provider-D580L0P8.js} +28 -10
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-DDmO1zwd.js → models-DuKzZA6G.js} +1 -1
- package/dist/notification-correlation-DfaCm0mx.js +67 -0
- package/dist/plugin-app-cache-key-BKNjHMEs.js +315 -0
- package/dist/{protocol-2POPqAY4.js → protocol-BMifTfdW.js} +3 -1
- package/dist/provider-catalog.js +66 -27
- package/dist/provider.js +306 -1
- package/dist/{provider-zjPfx5Fs.js → rate-limits-Dhp04Rqo.js} +4 -261
- package/dist/request-DborTWgw.js +111 -0
- package/dist/{run-attempt-FUyOjGCV.js → run-attempt-VPVJoYDP.js} +7897 -7136
- package/dist/runtime-artifact-C2ITXdhM.js +488 -0
- package/dist/{sandbox-guard-DA2TQfZW.js → sandbox-guard-D1Wky__S.js} +10 -1
- package/dist/{session-binding-C1ZXdP-x.js → session-binding-BMfX1OX-.js} +247 -88
- package/dist/session-catalog-7H112Tr_.js +2396 -0
- package/dist/session-cli-B28RhCyp.js +175 -0
- package/dist/{shared-client-4ICy3U6d.js → shared-client-D4mFI9al.js} +1997 -1297
- package/dist/{side-question-N4OaDer2.js → side-question-CgJBz52s.js} +135 -67
- package/dist/{thread-lifecycle-qWE88Dn2.js → thread-lifecycle-BgLXzjvV.js} +747 -317
- package/dist/transcript-mirror-DhLwFIL4.js +485 -0
- package/dist/transport-stdio-DXgGZ7Ib.js +88 -0
- package/dist/{web-search-provider.runtime-BSlriav6.js → web-search-provider.runtime-Bs-eTn5U.js} +3 -3
- package/npm-shrinkwrap.json +56 -30
- package/openclaw.plugin.json +168 -10
- package/package.json +8 -6
- package/dist/command-rpc-eLM_mtC7.js +0 -784
- package/dist/computer-use-Bmaz333N.js +0 -377
- package/dist/notification-correlation-Bo7KB3ks.js +0 -35
- package/dist/plugin-app-cache-key-BrhVdeEf.js +0 -98
- package/dist/request-BcJyl8KL.js +0 -36
|
@@ -1,239 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { n as assertCodexThreadResumeResponse, r as assertCodexThreadStartResponse } from "./protocol-validators-dZQ-UTOa.js";
|
|
1
|
+
import { i as isJsonObject, r as flattenCodexDynamicToolFunctions } from "./protocol-BMifTfdW.js";
|
|
2
|
+
import { _ as resolveCodexPluginsPolicy, n as CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME, o as codexSandboxPolicyForTurn, t as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-c48K5HP9.js";
|
|
3
|
+
import { n as assertCodexThreadResumeResponse, r as assertCodexThreadStartResponse, t as assertCodexThreadForkResponse } from "./protocol-validators-dZQ-UTOa.js";
|
|
4
|
+
import { a as serializeCodexAppInventoryError, i as defaultCodexAppInventoryCache, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-BKNjHMEs.js";
|
|
5
|
+
import { O as CodexAppServerRpcError, P as isCodexAppServerPrewriteRequestCancellationError, d as retireSharedCodexAppServerClientIfCurrent, j as isCodexAppServerConnectionClosedError, k as getCodexAppServerClientInstanceId, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, o as isCodexAppServerStartSelectionChangedError, t as clearSharedCodexAppServerClientIfCurrent } from "./shared-client-D4mFI9al.js";
|
|
6
|
+
import { a as isCodexAppServerNativeAuthProfile, c as sessionBindingIdentity, i as hashCodexAppServerBindingFingerprint, n as assertCodexBindingMayBeReplaced, o as normalizeCodexAppServerBindingModelProvider, s as reclaimCurrentCodexSessionGeneration } from "./session-binding-BMfX1OX-.js";
|
|
4
7
|
import { CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY } from "./prompt-overlay.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import "node:
|
|
9
|
-
import "node:path";
|
|
10
|
-
import { isFutureDateTimestampMs, resolveDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
11
|
-
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
8
|
+
import { isMaxReasoningCodexModel, isModernCodexModel, readCodexSupportedReasoningEfforts, resolveCodexFallbackReasoningEfforts, resolveCodexSupportedReasoningEffort } from "./provider.js";
|
|
9
|
+
import { a as projectBoundedCodexThreadHistory } from "./transcript-mirror-DhLwFIL4.js";
|
|
10
|
+
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
11
|
+
import * as crypto$1 from "node:crypto";
|
|
12
12
|
import crypto from "node:crypto";
|
|
13
13
|
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
14
|
-
import { SKILL_WORKSHOP_TOOL_NAME, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
15
|
-
import {
|
|
14
|
+
import { SKILL_WORKSHOP_TOOL_NAME, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
15
|
+
import { buildCodexUserMcpServersThreadConfigPatchForRuntime } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
16
16
|
import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
|
|
17
17
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
18
18
|
import { INLINE_IMAGE_DATA_URL_PREFIX, sanitizeInlineImageDataUrl } from "openclaw/plugin-sdk/inline-image-data-url-runtime";
|
|
19
19
|
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
20
|
-
const CODEX_TARGETED_APP_INVENTORY_LIMIT = 1e3;
|
|
21
|
-
const MAX_SERIALIZED_ERROR_MESSAGE_LENGTH = 500;
|
|
22
|
-
/** In-memory app inventory cache with coalesced refreshes per key. */
|
|
23
|
-
var CodexAppInventoryCache = class {
|
|
24
|
-
constructor(options = {}) {
|
|
25
|
-
this.entries = /* @__PURE__ */ new Map();
|
|
26
|
-
this.inFlight = /* @__PURE__ */ new Map();
|
|
27
|
-
this.refreshTokens = /* @__PURE__ */ new Map();
|
|
28
|
-
this.diagnostics = /* @__PURE__ */ new Map();
|
|
29
|
-
this.revision = 0;
|
|
30
|
-
this.ttlMs = options.ttlMs ?? 36e5;
|
|
31
|
-
}
|
|
32
|
-
/** Reads a snapshot and schedules refresh when missing, stale, or forced. */
|
|
33
|
-
read(params) {
|
|
34
|
-
const nowMs = resolveDateTimestampMs(params.nowMs);
|
|
35
|
-
const entry = this.entries.get(params.key);
|
|
36
|
-
if (!entry) {
|
|
37
|
-
const refreshScheduled = params.suppressRefresh ? false : this.scheduleRefresh(params);
|
|
38
|
-
return {
|
|
39
|
-
state: "missing",
|
|
40
|
-
key: params.key,
|
|
41
|
-
revision: this.revision,
|
|
42
|
-
refreshScheduled,
|
|
43
|
-
...this.diagnostics.get(params.key) ? { diagnostic: this.diagnostics.get(params.key) } : {}
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
const state = entry.invalidated || !isFutureDateTimestampMs(entry.expiresAtMs, { nowMs }) ? "stale" : "fresh";
|
|
47
|
-
const refreshScheduled = state === "fresh" && !params.forceRefetch ? false : this.scheduleRefresh(params);
|
|
48
|
-
return {
|
|
49
|
-
state,
|
|
50
|
-
key: params.key,
|
|
51
|
-
revision: entry.revision,
|
|
52
|
-
snapshot: stripEntryState(entry),
|
|
53
|
-
refreshScheduled,
|
|
54
|
-
...entry.lastError ? { diagnostic: entry.lastError } : {}
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/** Forces or joins an immediate refresh for a cache key. */
|
|
58
|
-
refreshNow(params) {
|
|
59
|
-
return this.refresh(params);
|
|
60
|
-
}
|
|
61
|
-
/** Marks a key stale and records the reason as a diagnostic. */
|
|
62
|
-
invalidate(key, reason, nowMs = Date.now()) {
|
|
63
|
-
this.revision += 1;
|
|
64
|
-
const diagnostic = {
|
|
65
|
-
message: reason,
|
|
66
|
-
atMs: nowMs
|
|
67
|
-
};
|
|
68
|
-
const entry = this.entries.get(key);
|
|
69
|
-
if (entry) {
|
|
70
|
-
entry.invalidated = true;
|
|
71
|
-
entry.lastError = diagnostic;
|
|
72
|
-
entry.revision = this.revision;
|
|
73
|
-
} else this.diagnostics.set(key, diagnostic);
|
|
74
|
-
return this.revision;
|
|
75
|
-
}
|
|
76
|
-
/** Clears all cached snapshots, diagnostics, in-flight requests, and revision state. */
|
|
77
|
-
clear() {
|
|
78
|
-
this.entries.clear();
|
|
79
|
-
this.inFlight.clear();
|
|
80
|
-
this.refreshTokens.clear();
|
|
81
|
-
this.diagnostics.clear();
|
|
82
|
-
this.revision = 0;
|
|
83
|
-
}
|
|
84
|
-
/** Returns the monotonically increasing cache revision. */
|
|
85
|
-
getRevision() {
|
|
86
|
-
return this.revision;
|
|
87
|
-
}
|
|
88
|
-
scheduleRefresh(params) {
|
|
89
|
-
if (this.inFlight.has(params.key) && !params.forceRefetch) return true;
|
|
90
|
-
const promise = this.refresh(params);
|
|
91
|
-
this.inFlight.set(params.key, promise);
|
|
92
|
-
promise.catch(() => void 0);
|
|
93
|
-
return true;
|
|
94
|
-
}
|
|
95
|
-
async refresh(params) {
|
|
96
|
-
const existing = this.inFlight.get(params.key);
|
|
97
|
-
if (existing && !params.forceRefetch) return existing;
|
|
98
|
-
const refreshToken = (this.refreshTokens.get(params.key) ?? 0) + 1;
|
|
99
|
-
this.refreshTokens.set(params.key, refreshToken);
|
|
100
|
-
const promise = this.refreshUncoalesced(params, refreshToken);
|
|
101
|
-
this.inFlight.set(params.key, promise);
|
|
102
|
-
try {
|
|
103
|
-
return await promise;
|
|
104
|
-
} finally {
|
|
105
|
-
if (this.inFlight.get(params.key) === promise) this.inFlight.delete(params.key);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
async refreshUncoalesced(params, refreshToken) {
|
|
109
|
-
const nowMs = resolveDateTimestampMs(params.nowMs);
|
|
110
|
-
try {
|
|
111
|
-
const apps = await listAllApps(params.request, params.forceRefetch ?? false, params.targetAppIds);
|
|
112
|
-
this.revision += 1;
|
|
113
|
-
const expiresAtMs = resolveExpiresAtMsFromDurationMs(this.ttlMs, { nowMs }) ?? 0;
|
|
114
|
-
const snapshot = {
|
|
115
|
-
key: params.key,
|
|
116
|
-
apps,
|
|
117
|
-
fetchedAtMs: nowMs,
|
|
118
|
-
expiresAtMs,
|
|
119
|
-
revision: this.revision
|
|
120
|
-
};
|
|
121
|
-
if (this.refreshTokens.get(params.key) === refreshToken) {
|
|
122
|
-
this.entries.set(params.key, {
|
|
123
|
-
...snapshot,
|
|
124
|
-
invalidated: false
|
|
125
|
-
});
|
|
126
|
-
this.diagnostics.delete(params.key);
|
|
127
|
-
}
|
|
128
|
-
return snapshot;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
const diagnostic = {
|
|
131
|
-
message: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
|
132
|
-
atMs: nowMs
|
|
133
|
-
};
|
|
134
|
-
this.diagnostics.set(params.key, diagnostic);
|
|
135
|
-
const entry = this.entries.get(params.key);
|
|
136
|
-
if (entry) entry.lastError = diagnostic;
|
|
137
|
-
embeddedAgentLog.warn("codex app inventory refresh failed", {
|
|
138
|
-
forceRefetch: params.forceRefetch === true,
|
|
139
|
-
keyFingerprint: fingerprintInventoryCacheKey(params.key),
|
|
140
|
-
error: serializeCodexAppInventoryError(error)
|
|
141
|
-
});
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
/** Serializes a refresh failure without leaking large or sensitive error data. */
|
|
147
|
-
function serializeCodexAppInventoryError(error) {
|
|
148
|
-
const record = isRecord(error) ? error : void 0;
|
|
149
|
-
const data = record && "data" in record ? redactErrorData(record.data) : void 0;
|
|
150
|
-
return {
|
|
151
|
-
name: error instanceof Error ? error.name : typeof record?.name === "string" ? record.name : void 0,
|
|
152
|
-
message: sanitizeErrorMessage(error instanceof Error ? error.message : String(error)),
|
|
153
|
-
...typeof record?.code === "number" ? { code: record.code } : {},
|
|
154
|
-
...data !== void 0 ? { data } : {}
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
/** Shared app inventory cache used by Codex app-server runtime paths. */
|
|
158
|
-
const defaultCodexAppInventoryCache = new CodexAppInventoryCache();
|
|
159
|
-
/** Builds a stable cache key from build versions and runtime identity fields. */
|
|
160
|
-
function buildCodexAppInventoryCacheKey(input, openClawVersion, codexPluginVersion) {
|
|
161
|
-
return JSON.stringify({
|
|
162
|
-
openClawVersion,
|
|
163
|
-
codexPluginVersion,
|
|
164
|
-
codexHome: input.codexHome ?? null,
|
|
165
|
-
endpoint: input.endpoint ?? null,
|
|
166
|
-
runtimeIdentity: normalizeRuntimeIdentityForCacheKey(input.runtimeIdentity),
|
|
167
|
-
authProfileId: input.authProfileId ?? null,
|
|
168
|
-
accountId: input.accountId ?? null,
|
|
169
|
-
envApiKeyFingerprint: input.envApiKeyFingerprint ?? null,
|
|
170
|
-
appServerVersion: input.appServerVersion ?? null
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
function normalizeRuntimeIdentityForCacheKey(value) {
|
|
174
|
-
if (!value) return null;
|
|
175
|
-
const entries = Object.entries(value).flatMap(([key, rawValue]) => {
|
|
176
|
-
const normalized = rawValue?.trim();
|
|
177
|
-
return normalized ? [[key, normalized]] : [];
|
|
178
|
-
}).toSorted(([left], [right]) => left.localeCompare(right));
|
|
179
|
-
return entries.length > 0 ? Object.fromEntries(entries) : null;
|
|
180
|
-
}
|
|
181
|
-
async function listAllApps(request, forceRefetch, targetAppIds = []) {
|
|
182
|
-
const apps = [];
|
|
183
|
-
const targetIds = new Set(targetAppIds.filter(Boolean));
|
|
184
|
-
const remainingTargetIds = new Set(targetIds);
|
|
185
|
-
const seenCursors = /* @__PURE__ */ new Set();
|
|
186
|
-
let cursor;
|
|
187
|
-
do {
|
|
188
|
-
const response = await request("app/list", {
|
|
189
|
-
cursor,
|
|
190
|
-
limit: targetIds.size > 0 ? CODEX_TARGETED_APP_INVENTORY_LIMIT : 100,
|
|
191
|
-
forceRefetch
|
|
192
|
-
});
|
|
193
|
-
apps.push(...response.data);
|
|
194
|
-
for (const app of response.data) remainingTargetIds.delete(app.id);
|
|
195
|
-
if (targetIds.size > 0 && remainingTargetIds.size === 0) break;
|
|
196
|
-
cursor = response.nextCursor;
|
|
197
|
-
if (cursor && seenCursors.has(cursor)) throw new Error(`app/list returned repeated cursor ${cursor}`);
|
|
198
|
-
if (cursor) seenCursors.add(cursor);
|
|
199
|
-
} while (cursor);
|
|
200
|
-
return apps;
|
|
201
|
-
}
|
|
202
|
-
function stripEntryState(entry) {
|
|
203
|
-
const { invalidated: _invalidated, ...snapshot } = entry;
|
|
204
|
-
return snapshot;
|
|
205
|
-
}
|
|
206
|
-
function fingerprintInventoryCacheKey(key) {
|
|
207
|
-
let hash = 0;
|
|
208
|
-
for (let index = 0; index < key.length; index += 1) hash = hash * 31 + key.charCodeAt(index) >>> 0;
|
|
209
|
-
return hash.toString(16).padStart(8, "0");
|
|
210
|
-
}
|
|
211
|
-
function redactErrorData(value, depth = 0) {
|
|
212
|
-
if (value === void 0) return;
|
|
213
|
-
if (value === null || typeof value === "boolean" || typeof value === "number") return value;
|
|
214
|
-
if (depth > 6) return "[truncated]";
|
|
215
|
-
if (Array.isArray(value)) return value.map((entry) => redactErrorData(entry, depth + 1) ?? null);
|
|
216
|
-
if (isRecord(value)) {
|
|
217
|
-
const redacted = {};
|
|
218
|
-
for (const [key, entry] of Object.entries(value)) redacted[key] = isSensitiveErrorDataKey(key) ? "<redacted>" : redactErrorData(entry, depth + 1) ?? null;
|
|
219
|
-
return redacted;
|
|
220
|
-
}
|
|
221
|
-
if (typeof value === "string" && value.length > 500) return `${truncateUtf16Safe(value, 500)}...`;
|
|
222
|
-
if (typeof value === "string") return value;
|
|
223
|
-
if (typeof value === "bigint") return value.toString();
|
|
224
|
-
if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
|
|
225
|
-
if (typeof value === "function") return value.name ? `[function ${value.name}]` : "[function]";
|
|
226
|
-
return "[unserializable]";
|
|
227
|
-
}
|
|
228
|
-
function sanitizeErrorMessage(message) {
|
|
229
|
-
const htmlStart = message.search(/<html[\s>]/i);
|
|
230
|
-
const redacted = (htmlStart >= 0 ? `${message.slice(0, htmlStart).trimEnd()} [HTML response body omitted]` : message).replace(/([?&][^=\s"'<>]*(?:api[_-]?key|authorization|cookie|credential|password|secret|token|tk)[^=\s"'<>]*=)[^&\s"'<>]+/gi, "$1<redacted>");
|
|
231
|
-
return redacted.length > MAX_SERIALIZED_ERROR_MESSAGE_LENGTH ? `${redacted.slice(0, MAX_SERIALIZED_ERROR_MESSAGE_LENGTH)}...` : redacted;
|
|
232
|
-
}
|
|
233
|
-
function isSensitiveErrorDataKey(key) {
|
|
234
|
-
return /api[_-]?key|authorization|cookie|credential|password|secret|token/i.test(key);
|
|
235
|
-
}
|
|
236
|
-
//#endregion
|
|
237
20
|
//#region extensions/codex/src/app-server/plugin-inventory.ts
|
|
238
21
|
/**
|
|
239
22
|
* Reads Codex plugin marketplace state and app inventory to decide which
|
|
@@ -252,19 +35,21 @@ async function readCodexPluginInventory(params) {
|
|
|
252
35
|
}]
|
|
253
36
|
};
|
|
254
37
|
const appInventory = readCachedAppInventory(params);
|
|
255
|
-
const
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
|
|
38
|
+
const curatedListed = await params.request("plugin/list", { cwds: [] });
|
|
39
|
+
const shouldListWorkspacePlugins = policy.pluginPolicies.some((pluginPolicy) => pluginPolicy.enabled && pluginPolicy.marketplaceName === "workspace-directory");
|
|
40
|
+
let workspaceListResult;
|
|
41
|
+
if (shouldListWorkspacePlugins) try {
|
|
42
|
+
workspaceListResult = {
|
|
43
|
+
kind: "listed",
|
|
44
|
+
response: await params.request("plugin/list", {
|
|
45
|
+
cwds: [],
|
|
46
|
+
marketplaceKinds: [CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME]
|
|
47
|
+
})
|
|
48
|
+
};
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (!(error instanceof CodexAppServerRpcError)) throw error;
|
|
51
|
+
workspaceListResult = { kind: "rejected" };
|
|
52
|
+
}
|
|
268
53
|
const diagnostics = [];
|
|
269
54
|
const records = [];
|
|
270
55
|
if (appInventory?.state === "missing") diagnostics.push({
|
|
@@ -277,19 +62,27 @@ async function readCodexPluginInventory(params) {
|
|
|
277
62
|
});
|
|
278
63
|
for (const pluginPolicy of policy.pluginPolicies) {
|
|
279
64
|
if (!pluginPolicy.enabled) continue;
|
|
280
|
-
const
|
|
65
|
+
const listed = pluginPolicy.marketplaceName === "workspace-directory" ? workspaceListResult?.kind === "listed" ? workspaceListResult.response : void 0 : curatedListed;
|
|
66
|
+
const hasMarketplace = pluginPolicy.marketplaceName === "workspace-directory" ? listed?.marketplaces.some((entry) => entry.name === CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME) === true : listed?.marketplaces.some(isOpenAiCuratedMarketplace) === true;
|
|
67
|
+
if (!listed || !hasMarketplace) {
|
|
68
|
+
diagnostics.push({
|
|
69
|
+
code: "marketplace_missing",
|
|
70
|
+
plugin: pluginPolicy,
|
|
71
|
+
message: `Codex marketplace ${pluginPolicy.marketplaceName} was not found.`
|
|
72
|
+
});
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const resolvedPlugin = pluginPolicy.marketplaceName === "workspace-directory" ? findWorkspaceMarketplacePlugin(listed, pluginPolicy.pluginName) : findOpenAiCuratedMarketplacePlugin(listed, pluginPolicy.pluginName);
|
|
281
76
|
if (!resolvedPlugin) {
|
|
282
77
|
diagnostics.push({
|
|
283
78
|
code: "plugin_missing",
|
|
284
79
|
plugin: pluginPolicy,
|
|
285
|
-
message: `${pluginPolicy.pluginName} was not found in ${
|
|
80
|
+
message: `${pluginPolicy.pluginName} was not found in ${pluginPolicy.marketplaceName}.`
|
|
286
81
|
});
|
|
287
82
|
continue;
|
|
288
83
|
}
|
|
289
84
|
const { summary } = resolvedPlugin;
|
|
290
|
-
const
|
|
291
|
-
if (records.length === 0) marketplace = pluginMarketplace;
|
|
292
|
-
const detail = await readPluginDetail(params, pluginMarketplace, pluginPolicy, summary, diagnostics);
|
|
85
|
+
const detail = await readPluginDetail(params, marketplaceRef(resolvedPlugin.marketplace, pluginPolicy.marketplaceName), pluginPolicy, summary, diagnostics);
|
|
293
86
|
const ownedAppIds = detail?.apps.map((app) => app.id).filter(Boolean).toSorted() ?? [];
|
|
294
87
|
const appOwnership = resolveAppOwnership({
|
|
295
88
|
detail,
|
|
@@ -324,7 +117,6 @@ async function readCodexPluginInventory(params) {
|
|
|
324
117
|
}
|
|
325
118
|
return {
|
|
326
119
|
policy,
|
|
327
|
-
marketplace,
|
|
328
120
|
records,
|
|
329
121
|
diagnostics,
|
|
330
122
|
...appInventory ? { appInventory } : {}
|
|
@@ -334,7 +126,7 @@ async function readCodexPluginInventory(params) {
|
|
|
334
126
|
function findOpenAiCuratedPluginSummary(listed, pluginName) {
|
|
335
127
|
const resolved = findOpenAiCuratedMarketplacePlugin(listed, pluginName);
|
|
336
128
|
return resolved ? {
|
|
337
|
-
marketplace: marketplaceRef(resolved.marketplace),
|
|
129
|
+
marketplace: marketplaceRef(resolved.marketplace, CODEX_PLUGINS_MARKETPLACE_NAME),
|
|
338
130
|
summary: resolved.summary
|
|
339
131
|
} : void 0;
|
|
340
132
|
}
|
|
@@ -358,6 +150,14 @@ function readCachedAppInventory(params) {
|
|
|
358
150
|
}
|
|
359
151
|
async function readPluginDetail(params, marketplace, pluginPolicy, summary, diagnostics) {
|
|
360
152
|
if (params.readPluginDetails === false) return;
|
|
153
|
+
if (marketplace.name === "workspace-directory" && marketplace.remoteMarketplaceName && !summary.remotePluginId) {
|
|
154
|
+
diagnostics.push({
|
|
155
|
+
code: "plugin_detail_unavailable",
|
|
156
|
+
plugin: pluginPolicy,
|
|
157
|
+
message: `${pluginPolicy.pluginName} detail unavailable: Codex did not return a remote plugin id.`
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
361
161
|
try {
|
|
362
162
|
return (await params.request("plugin/read", pluginReadParams(marketplace, marketplace.remoteMarketplaceName && summary.remotePluginId ? summary.remotePluginId : pluginPolicy.pluginName))).plugin;
|
|
363
163
|
} catch (error) {
|
|
@@ -416,15 +216,23 @@ function findOpenAiCuratedMarketplacePlugin(listed, pluginName) {
|
|
|
416
216
|
};
|
|
417
217
|
}
|
|
418
218
|
}
|
|
219
|
+
function findWorkspaceMarketplacePlugin(listed, pluginName) {
|
|
220
|
+
const marketplace = listed.marketplaces.find((entry) => entry.name === CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME);
|
|
221
|
+
const summary = marketplace?.plugins.find((plugin) => plugin.id === pluginName);
|
|
222
|
+
return marketplace && summary ? {
|
|
223
|
+
marketplace,
|
|
224
|
+
summary
|
|
225
|
+
} : void 0;
|
|
226
|
+
}
|
|
419
227
|
function pluginNameFromPluginId(pluginId, marketplaceName) {
|
|
420
228
|
const trimmed = pluginId.trim();
|
|
421
229
|
if (!trimmed) return;
|
|
422
230
|
const marketplaceSuffix = `@${marketplaceName}`;
|
|
423
231
|
return (trimmed.endsWith(marketplaceSuffix) ? trimmed.slice(0, -marketplaceSuffix.length) : trimmed).split("/").at(-1)?.trim() || void 0;
|
|
424
232
|
}
|
|
425
|
-
function marketplaceRef(marketplace) {
|
|
233
|
+
function marketplaceRef(marketplace, name) {
|
|
426
234
|
return {
|
|
427
|
-
name
|
|
235
|
+
name,
|
|
428
236
|
...marketplace.path ? { path: marketplace.path } : {},
|
|
429
237
|
...!marketplace.path ? { remoteMarketplaceName: marketplace.name } : {}
|
|
430
238
|
};
|
|
@@ -434,9 +242,9 @@ function isOpenAiCuratedMarketplace(marketplace) {
|
|
|
434
242
|
}
|
|
435
243
|
//#endregion
|
|
436
244
|
//#region extensions/codex/src/app-server/plugin-activation.ts
|
|
437
|
-
/**
|
|
245
|
+
/** Activates a curated plugin or rejects a workspace plugin that is not already active. */
|
|
438
246
|
async function ensureCodexPluginActivation(params) {
|
|
439
|
-
if (params.identity.marketplaceName
|
|
247
|
+
if (params.identity.marketplaceName === "workspace-directory") return activationFailure(params.identity, "disabled", { message: "workspace-directory plugins must be installed and enabled outside OpenClaw before use." });
|
|
440
248
|
const listed = await params.request("plugin/list", { cwds: [] });
|
|
441
249
|
const resolved = findOpenAiCuratedPluginSummary(listed, params.identity.pluginName);
|
|
442
250
|
if (!resolved) {
|
|
@@ -1033,17 +841,19 @@ async function unsubscribeCodexThreadBestEffort(client, params) {
|
|
|
1033
841
|
* potentially wedged app-server connection.
|
|
1034
842
|
*/
|
|
1035
843
|
async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
|
|
1036
|
-
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
844
|
+
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client, { failActiveLeases: params.suspectPhysicalClient });
|
|
1037
845
|
const detachedSharedClient = Boolean(retiredSharedClient);
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
846
|
+
if (!(params.suspectPhysicalClient && (retiredSharedClient?.closed ?? false))) {
|
|
847
|
+
interruptCodexTurnBestEffort(client, {
|
|
848
|
+
threadId: params.threadId,
|
|
849
|
+
turnId: params.turnId,
|
|
850
|
+
timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
|
|
851
|
+
});
|
|
852
|
+
await unsubscribeCodexThreadBestEffort(client, {
|
|
853
|
+
threadId: params.threadId,
|
|
854
|
+
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
855
|
+
});
|
|
856
|
+
}
|
|
1047
857
|
let closedClient = retiredSharedClient?.closed ?? false;
|
|
1048
858
|
if (!detachedSharedClient) {
|
|
1049
859
|
const close = client.close;
|
|
@@ -1128,7 +938,7 @@ function resolveCodexContextEngineProjectionReserveTokens(params) {
|
|
|
1128
938
|
const compaction = asRecord(asRecord(asRecord(params.config)?.agents)?.defaults)?.compaction;
|
|
1129
939
|
const configuredReserveTokens = toNonNegativeInt(asRecord(compaction)?.reserveTokens);
|
|
1130
940
|
const configuredReserveTokensFloor = toNonNegativeInt(asRecord(compaction)?.reserveTokensFloor);
|
|
1131
|
-
if (configuredReserveTokens !== void 0) return Math.max(configuredReserveTokens, configuredReserveTokensFloor ??
|
|
941
|
+
if (configuredReserveTokens !== void 0) return Math.max(configuredReserveTokens, configuredReserveTokensFloor ?? DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS);
|
|
1132
942
|
if (configuredReserveTokensFloor !== void 0) return configuredReserveTokensFloor;
|
|
1133
943
|
}
|
|
1134
944
|
/** Fits projected context prompts under Codex app-server turn/start text limits. */
|
|
@@ -1311,7 +1121,9 @@ function resolveTextPartMaxChars(maxRenderedContextChars) {
|
|
|
1311
1121
|
return Math.min(MAX_TEXT_PART_CHARS, Math.max(DEFAULT_TEXT_PART_CHARS, Math.floor(maxRenderedContextChars / 4)));
|
|
1312
1122
|
}
|
|
1313
1123
|
function truncateText(text, maxChars) {
|
|
1314
|
-
|
|
1124
|
+
if (text.length <= maxChars) return text;
|
|
1125
|
+
const truncated = truncateUtf16Safe(text, maxChars);
|
|
1126
|
+
return `${truncated}\n[truncated ${text.length - truncated.length} chars]`;
|
|
1315
1127
|
}
|
|
1316
1128
|
function truncateOlderContext(text, maxChars) {
|
|
1317
1129
|
if (text.length <= maxChars) return text;
|
|
@@ -1322,15 +1134,7 @@ function truncateOlderContext(text, maxChars) {
|
|
|
1322
1134
|
marker = buildMarker(text.length - tailChars);
|
|
1323
1135
|
if (marker.length >= maxChars) return marker.slice(0, maxChars);
|
|
1324
1136
|
tailChars = maxChars - marker.length;
|
|
1325
|
-
return `${marker}${
|
|
1326
|
-
}
|
|
1327
|
-
function sliceTailFromCodePointBoundary(text, tailChars) {
|
|
1328
|
-
let start = text.length - tailChars;
|
|
1329
|
-
if (start > 0 && start < text.length) {
|
|
1330
|
-
const code = text.charCodeAt(start);
|
|
1331
|
-
if (code >= 56320 && code <= 57343) start += 1;
|
|
1332
|
-
}
|
|
1333
|
-
return text.slice(start);
|
|
1137
|
+
return `${marker}${sliceUtf16Safe(text, -tailChars).trimStart()}`;
|
|
1334
1138
|
}
|
|
1335
1139
|
//#endregion
|
|
1336
1140
|
//#region extensions/codex/src/app-server/dynamic-tool-profile.ts
|
|
@@ -1357,6 +1161,10 @@ function normalizeCodexDynamicToolName(name) {
|
|
|
1357
1161
|
const normalized = name.trim().toLowerCase();
|
|
1358
1162
|
return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
|
|
1359
1163
|
}
|
|
1164
|
+
/** True only for the host-scoped OpenClaw run's exact tool contract. */
|
|
1165
|
+
function isSystemAgentOnlyCodexDynamicToolAllowlist(toolsAllow) {
|
|
1166
|
+
return toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "openclaw";
|
|
1167
|
+
}
|
|
1360
1168
|
/** Returns true for private QA runs that force the Codex runtime profile. */
|
|
1361
1169
|
function isForcedPrivateQaCodexRuntime(env = process.env) {
|
|
1362
1170
|
return env.OPENCLAW_BUILD_PRIVATE_QA === "1" && env.OPENCLAW_QA_FORCE_RUNTIME?.trim().toLowerCase() === "codex";
|
|
@@ -1481,6 +1289,7 @@ async function resumeCodexAppServerThread(params) {
|
|
|
1481
1289
|
}));
|
|
1482
1290
|
assertCodexThreadResumeSubscription(threadId, response.thread.id);
|
|
1483
1291
|
} catch (error) {
|
|
1292
|
+
if (isCodexAppServerStartSelectionChangedError(error) || isCodexAppServerPrewriteRequestCancellationError(error)) throw error;
|
|
1484
1293
|
if (error instanceof CodexAppServerRpcError) throw error;
|
|
1485
1294
|
try {
|
|
1486
1295
|
await params.abandonClient();
|
|
@@ -1573,7 +1382,21 @@ var CodexThreadBindingConflictError = class extends Error {
|
|
|
1573
1382
|
this.name = "CodexThreadBindingConflictError";
|
|
1574
1383
|
}
|
|
1575
1384
|
};
|
|
1385
|
+
var CodexRingZeroAttestationError = class extends Error {
|
|
1386
|
+
constructor(cause) {
|
|
1387
|
+
super("Codex ring-zero MCP attestation failed", { cause });
|
|
1388
|
+
this.name = "CodexRingZeroAttestationError";
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
var CodexThreadBindingConflictAfterCleanupError = class extends CodexThreadBindingConflictError {};
|
|
1392
|
+
var CodexAdoptedThreadActiveError = class extends Error {
|
|
1393
|
+
constructor() {
|
|
1394
|
+
super("Codex session became active in another runner; wait for it to finish before continuing");
|
|
1395
|
+
this.name = "CodexAdoptedThreadActiveError";
|
|
1396
|
+
}
|
|
1397
|
+
};
|
|
1576
1398
|
const CODEX_NATIVE_PERSONALITY_NONE = "none";
|
|
1399
|
+
const CODEX_RING_ZERO_BASE_INSTRUCTIONS = "";
|
|
1577
1400
|
const CODEX_CODE_MODE_THREAD_CONFIG = {
|
|
1578
1401
|
"features.code_mode": true,
|
|
1579
1402
|
"features.code_mode_only": false,
|
|
@@ -1585,6 +1408,64 @@ const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG = {
|
|
|
1585
1408
|
};
|
|
1586
1409
|
const CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG = { project_doc_max_bytes: 0 };
|
|
1587
1410
|
const CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG = { "features.multi_agent": false };
|
|
1411
|
+
const CODEX_RING_ZERO_THREAD_CONFIG = {
|
|
1412
|
+
"features.apps": false,
|
|
1413
|
+
"features.current_time_reminder": false,
|
|
1414
|
+
"features.deferred_executor": false,
|
|
1415
|
+
"features.enable_fanout": false,
|
|
1416
|
+
"features.goals": false,
|
|
1417
|
+
"features.hooks": false,
|
|
1418
|
+
"features.image_generation": false,
|
|
1419
|
+
"features.memories": false,
|
|
1420
|
+
"features.multi_agent": false,
|
|
1421
|
+
"features.multi_agent_v2": false,
|
|
1422
|
+
"features.plugins": false,
|
|
1423
|
+
"features.standalone_web_search": false,
|
|
1424
|
+
"features.token_budget": false,
|
|
1425
|
+
"orchestrator.mcp.enabled": false,
|
|
1426
|
+
"orchestrator.skills.enabled": false,
|
|
1427
|
+
"tools.experimental_request_user_input.enabled": false,
|
|
1428
|
+
hooks: {
|
|
1429
|
+
PreToolUse: [],
|
|
1430
|
+
PermissionRequest: [],
|
|
1431
|
+
PostToolUse: [],
|
|
1432
|
+
PreCompact: [],
|
|
1433
|
+
PostCompact: [],
|
|
1434
|
+
SessionStart: [],
|
|
1435
|
+
UserPromptSubmit: [],
|
|
1436
|
+
SubagentStart: [],
|
|
1437
|
+
SubagentStop: [],
|
|
1438
|
+
Stop: []
|
|
1439
|
+
},
|
|
1440
|
+
project_doc_max_bytes: 0,
|
|
1441
|
+
notify: [],
|
|
1442
|
+
web_search: "disabled"
|
|
1443
|
+
};
|
|
1444
|
+
const CODEX_RING_ZERO_RESTRICTED_FEATURES = /* @__PURE__ */ new Set([
|
|
1445
|
+
"apps",
|
|
1446
|
+
"code_mode",
|
|
1447
|
+
"code_mode_only",
|
|
1448
|
+
"current_time_reminder",
|
|
1449
|
+
"deferred_executor",
|
|
1450
|
+
"enable_fanout",
|
|
1451
|
+
"goals",
|
|
1452
|
+
"hooks",
|
|
1453
|
+
"image_generation",
|
|
1454
|
+
"memories",
|
|
1455
|
+
"multi_agent",
|
|
1456
|
+
"multi_agent_v2",
|
|
1457
|
+
"plugins",
|
|
1458
|
+
"standalone_web_search",
|
|
1459
|
+
"token_budget"
|
|
1460
|
+
]);
|
|
1461
|
+
const CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
1462
|
+
"mdm",
|
|
1463
|
+
"system",
|
|
1464
|
+
"enterpriseManaged",
|
|
1465
|
+
"user",
|
|
1466
|
+
"project",
|
|
1467
|
+
"sessionFlags"
|
|
1468
|
+
]);
|
|
1588
1469
|
const CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS = 1e3;
|
|
1589
1470
|
const CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS = 500;
|
|
1590
1471
|
function shouldWarnCodexThreadLifecycleTimingSummary(summary, options = {}) {
|
|
@@ -1696,10 +1577,29 @@ async function startOrResumeThread(params) {
|
|
|
1696
1577
|
})).threadConfig);
|
|
1697
1578
|
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
1698
1579
|
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
1699
|
-
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 :
|
|
1580
|
+
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRuntime(params.params.config, {
|
|
1581
|
+
agentId: params.agentId ?? params.params.agentId,
|
|
1582
|
+
agentDir: params.params.agentDir,
|
|
1583
|
+
allowLiteralOAuthProjection: params.appServer.connectionClass !== "remote",
|
|
1584
|
+
onServerUnavailable: (serverName, error) => embeddedAgentLog.warn("skipping unavailable MCP OAuth server", {
|
|
1585
|
+
serverName,
|
|
1586
|
+
error: formatErrorMessage(error)
|
|
1587
|
+
})
|
|
1588
|
+
});
|
|
1700
1589
|
const legacyUserMcpServersFingerprint = legacyFingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1701
1590
|
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1702
1591
|
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
1592
|
+
const hostSystemAgentActive = params.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw");
|
|
1593
|
+
const ringZeroActive = hostSystemAgentActive && isSystemAgentOnlyCodexDynamicToolAllowlist(params.params.toolsAllow);
|
|
1594
|
+
if (ringZeroActive && params.nativeCodeModeEnabled !== false) throw new Error("Codex ring-zero requires native code mode to be disabled");
|
|
1595
|
+
const ringZeroInheritedMcpServerNames = ringZeroActive ? await lifecycleTiming.measure("ring-zero-mcp-config-read", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal)) : [];
|
|
1596
|
+
if (ringZeroActive) await lifecycleTiming.measure("ring-zero-config-requirements-read", () => assertCodexRingZeroHasNoManagedHooks(params.client, params.signal));
|
|
1597
|
+
const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
|
|
1598
|
+
version: 1,
|
|
1599
|
+
baseInstructions: CODEX_RING_ZERO_BASE_INSTRUCTIONS,
|
|
1600
|
+
config: buildCodexRingZeroThreadConfigPatch(params.params, true, ringZeroInheritedMcpServerNames)
|
|
1601
|
+
}) : void 0;
|
|
1602
|
+
const ringZeroClientInstanceId = ringZeroActive ? getCodexAppServerClientInstanceId(params.client) : void 0;
|
|
1703
1603
|
let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
|
|
1704
1604
|
const normalizeBindingModelProvider = (authProfileId, modelProvider) => normalizeCodexAppServerBindingModelProvider({
|
|
1705
1605
|
authProfileId,
|
|
@@ -1708,6 +1608,14 @@ async function startOrResumeThread(params) {
|
|
|
1708
1608
|
agentDir: params.params.agentDir,
|
|
1709
1609
|
config: params.params.config
|
|
1710
1610
|
});
|
|
1611
|
+
const throwIfAborted = () => {
|
|
1612
|
+
if (!params.signal?.aborted) return;
|
|
1613
|
+
const reason = params.signal.reason;
|
|
1614
|
+
if (reason instanceof Error) throw reason;
|
|
1615
|
+
const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
|
|
1616
|
+
error.name = "AbortError";
|
|
1617
|
+
throw error;
|
|
1618
|
+
};
|
|
1711
1619
|
if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
|
|
1712
1620
|
if (!await lifecycleTiming.measure("reclaim-binding-generation", () => reclaimCurrentCodexSessionGeneration({
|
|
1713
1621
|
bindingStore: params.bindingStore,
|
|
@@ -1715,18 +1623,74 @@ async function startOrResumeThread(params) {
|
|
|
1715
1623
|
config: params.params.config
|
|
1716
1624
|
}))) throw new Error(`Codex session generation is no longer current: ${bindingIdentity.sessionId}`);
|
|
1717
1625
|
}
|
|
1626
|
+
if (binding?.pendingSupervisionBranch) {
|
|
1627
|
+
const pendingBinding = binding;
|
|
1628
|
+
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
1629
|
+
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
1630
|
+
configPatch: params.finalConfigPatch,
|
|
1631
|
+
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
1632
|
+
};
|
|
1633
|
+
const config = lifecycleTiming.measureSync("merge-thread-config", () => mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch, finalConfigPatch.configPatch));
|
|
1634
|
+
return await materializePendingSupervisionBranch({
|
|
1635
|
+
client: params.client,
|
|
1636
|
+
abandonClient: params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)),
|
|
1637
|
+
bindingStore: params.bindingStore,
|
|
1638
|
+
bindingIdentity,
|
|
1639
|
+
binding: pendingBinding,
|
|
1640
|
+
attempt: params.params,
|
|
1641
|
+
cwd: params.cwd,
|
|
1642
|
+
dynamicTools: params.dynamicTools,
|
|
1643
|
+
appServer: params.appServer,
|
|
1644
|
+
developerInstructions: params.developerInstructions,
|
|
1645
|
+
config,
|
|
1646
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1647
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1648
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1649
|
+
webSearchAllowed: params.webSearchAllowed,
|
|
1650
|
+
environmentSelection: params.environmentSelection,
|
|
1651
|
+
signal: params.signal,
|
|
1652
|
+
throwIfAborted,
|
|
1653
|
+
lifecycleTiming,
|
|
1654
|
+
normalizeBindingModelProvider,
|
|
1655
|
+
bindingPatch: {
|
|
1656
|
+
cwd: params.cwd,
|
|
1657
|
+
authProfileId: void 0,
|
|
1658
|
+
preserveNativeModel: true,
|
|
1659
|
+
dynamicToolsFingerprint,
|
|
1660
|
+
dynamicToolsContainDeferred,
|
|
1661
|
+
webSearchThreadConfigFingerprint,
|
|
1662
|
+
userMcpServersFingerprint,
|
|
1663
|
+
mcpServersFingerprint: params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : pendingBinding.mcpServersFingerprint,
|
|
1664
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1665
|
+
networkProxyConfigFingerprint,
|
|
1666
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
1667
|
+
appServerRuntimeFingerprint: buildCodexAppServerConnectionFingerprint(params.appServer, params.params.agentDir),
|
|
1668
|
+
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
1669
|
+
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
1670
|
+
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
1671
|
+
contextEngine: contextEngineBinding,
|
|
1672
|
+
environmentSelectionFingerprint,
|
|
1673
|
+
conversationSourceTransferComplete: true
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
}
|
|
1718
1677
|
const clearCurrentBinding = async (operation) => {
|
|
1719
1678
|
const current = binding;
|
|
1720
1679
|
if (!current?.threadId) return;
|
|
1680
|
+
assertCodexBindingMayBeReplaced(current, operation);
|
|
1721
1681
|
if (!await params.bindingStore.mutate(bindingIdentity, {
|
|
1722
1682
|
kind: "clear",
|
|
1723
1683
|
threadId: current.threadId
|
|
1724
1684
|
})) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
1725
1685
|
binding = void 0;
|
|
1726
1686
|
};
|
|
1687
|
+
if (binding?.threadId && (binding.ringZeroConfigFingerprint !== ringZeroConfigFingerprint || binding.ringZeroClientInstanceId !== ringZeroClientInstanceId) && (ringZeroActive || binding.ringZeroConfigFingerprint !== void 0)) {
|
|
1688
|
+
embeddedAgentLog.debug("codex app-server ring-zero restriction changed; rotating thread", { threadId: binding.threadId });
|
|
1689
|
+
await clearCurrentBinding("rotating a ring-zero thread binding");
|
|
1690
|
+
}
|
|
1727
1691
|
if (binding?.threadId && shouldRotateCodexAppServerBindingForRuntime({
|
|
1728
1692
|
connectionClass: params.appServer.connectionClass,
|
|
1729
|
-
current: params.appServerRuntimeFingerprint,
|
|
1693
|
+
current: binding.connectionScope === "supervision" ? buildCodexAppServerConnectionFingerprint(params.appServer, params.params.agentDir) : params.appServerRuntimeFingerprint,
|
|
1730
1694
|
binding: binding.appServerRuntimeFingerprint
|
|
1731
1695
|
})) {
|
|
1732
1696
|
embeddedAgentLog.debug("codex app-server runtime identity changed; starting a new thread", {
|
|
@@ -1758,17 +1722,9 @@ async function startOrResumeThread(params) {
|
|
|
1758
1722
|
config: params.params.config
|
|
1759
1723
|
});
|
|
1760
1724
|
const startModelProvider = startModelSelection.modelProvider;
|
|
1761
|
-
let preserveExistingBinding = params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
|
|
1725
|
+
let preserveExistingBinding = !ringZeroActive && params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
|
|
1762
1726
|
let rotatedContextEngineBinding = false;
|
|
1763
1727
|
let prebuiltPluginThreadConfig;
|
|
1764
|
-
const throwIfAborted = () => {
|
|
1765
|
-
if (!params.signal?.aborted) return;
|
|
1766
|
-
const reason = params.signal.reason;
|
|
1767
|
-
if (reason instanceof Error) throw reason;
|
|
1768
|
-
const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
|
|
1769
|
-
error.name = "AbortError";
|
|
1770
|
-
throw error;
|
|
1771
|
-
};
|
|
1772
1728
|
const webSearchBindingChanged = binding?.threadId && binding.webSearchThreadConfigFingerprint !== webSearchThreadConfigFingerprint;
|
|
1773
1729
|
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
1774
1730
|
const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
|
|
@@ -1776,7 +1732,8 @@ async function startOrResumeThread(params) {
|
|
|
1776
1732
|
const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
|
|
1777
1733
|
const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
|
|
1778
1734
|
if (binding?.threadId && params.mcpServersFingerprintEvaluated === true && binding.mcpServersFingerprint !== params.mcpServersFingerprint) {
|
|
1779
|
-
|
|
1735
|
+
assertCodexBindingMayBeReplaced(binding, "changing MCP configuration");
|
|
1736
|
+
if (!ringZeroActive && (transientNativeToolRestriction || webSearchBindingChanged && (explicitTransientWebSearchRestriction || unknownProviderWebSearchSupport))) {
|
|
1780
1737
|
embeddedAgentLog.debug("codex app-server MCP config changed during transient restricted turn; starting transient thread", { threadId: binding.threadId });
|
|
1781
1738
|
preserveExistingBinding = true;
|
|
1782
1739
|
} else {
|
|
@@ -1787,7 +1744,8 @@ async function startOrResumeThread(params) {
|
|
|
1787
1744
|
}
|
|
1788
1745
|
const deferLegacyWebSearchRotationToTransientNativeSurface = params.nativeCodeModeEnabled === false && binding?.webSearchThreadConfigFingerprint === void 0 && !persistentWebSearchRestriction;
|
|
1789
1746
|
if (binding?.threadId && webSearchBindingChanged && !deferLegacyWebSearchRotationToTransientNativeSurface) {
|
|
1790
|
-
|
|
1747
|
+
assertCodexBindingMayBeReplaced(binding, "changing web-search configuration");
|
|
1748
|
+
if (!ringZeroActive && transientWebSearchRestriction) {
|
|
1791
1749
|
embeddedAgentLog.debug("codex app-server web search restricted for turn; starting transient thread", { threadId: binding.threadId });
|
|
1792
1750
|
preserveExistingBinding = true;
|
|
1793
1751
|
} else {
|
|
@@ -1796,7 +1754,8 @@ async function startOrResumeThread(params) {
|
|
|
1796
1754
|
}
|
|
1797
1755
|
binding = void 0;
|
|
1798
1756
|
}
|
|
1799
|
-
if (binding?.threadId && transientNativeToolRestriction) {
|
|
1757
|
+
if (binding?.threadId && transientNativeToolRestriction && !ringZeroActive) {
|
|
1758
|
+
assertCodexBindingMayBeReplaced(binding, "starting a native-tool-restricted turn");
|
|
1800
1759
|
embeddedAgentLog.debug("codex app-server native tool surface disabled for turn; starting transient thread", { threadId: binding.threadId });
|
|
1801
1760
|
preserveExistingBinding = true;
|
|
1802
1761
|
binding = void 0;
|
|
@@ -1872,6 +1831,7 @@ async function startOrResumeThread(params) {
|
|
|
1872
1831
|
}
|
|
1873
1832
|
}
|
|
1874
1833
|
if (binding?.threadId) if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint, legacyDynamicToolsFingerprint)) {
|
|
1834
|
+
assertCodexBindingMayBeReplaced(binding, "changing the dynamic tool catalog");
|
|
1875
1835
|
preserveExistingBinding = shouldStartTransientNoToolThread({
|
|
1876
1836
|
previous: binding.dynamicToolsFingerprint,
|
|
1877
1837
|
nextHasDynamicTools: params.dynamicTools.length > 0
|
|
@@ -1885,7 +1845,7 @@ async function startOrResumeThread(params) {
|
|
|
1885
1845
|
const resumeBinding = binding;
|
|
1886
1846
|
let resumeReservation;
|
|
1887
1847
|
try {
|
|
1888
|
-
const authProfileId = params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
1848
|
+
const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
1889
1849
|
const finalConfigPatch = params.buildFinalConfigPatch?.({
|
|
1890
1850
|
action: "resume",
|
|
1891
1851
|
binding: resumeBinding
|
|
@@ -1900,6 +1860,7 @@ async function startOrResumeThread(params) {
|
|
|
1900
1860
|
authProfileId,
|
|
1901
1861
|
model: startModelSelection.model,
|
|
1902
1862
|
modelProvider: startModelProvider,
|
|
1863
|
+
preserveNativeModel: resumeBinding.preserveNativeModel === true,
|
|
1903
1864
|
appServer: params.appServer,
|
|
1904
1865
|
dynamicTools: params.dynamicTools,
|
|
1905
1866
|
developerInstructions: params.developerInstructions,
|
|
@@ -1907,10 +1868,20 @@ async function startOrResumeThread(params) {
|
|
|
1907
1868
|
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1908
1869
|
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1909
1870
|
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1910
|
-
webSearchAllowed: params.webSearchAllowed
|
|
1871
|
+
webSearchAllowed: params.webSearchAllowed,
|
|
1872
|
+
hostSystemAgentActive,
|
|
1873
|
+
ringZeroInheritedMcpServerNames
|
|
1911
1874
|
}));
|
|
1912
1875
|
const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
|
|
1913
1876
|
throwIfAborted();
|
|
1877
|
+
if (resumeBinding.preserveNativeModel === true) {
|
|
1878
|
+
const current = await lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
|
|
1879
|
+
threadId: resumeBinding.threadId,
|
|
1880
|
+
includeTurns: false
|
|
1881
|
+
}, { signal: params.signal }));
|
|
1882
|
+
throwIfAborted();
|
|
1883
|
+
if (current.thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
|
|
1884
|
+
}
|
|
1914
1885
|
resumeReservation = params.reserveResumeThread?.(resumeBinding.threadId);
|
|
1915
1886
|
const response = await lifecycleTiming.measure("thread-resume-request", () => resumeCodexAppServerThread({
|
|
1916
1887
|
client: params.client,
|
|
@@ -1918,6 +1889,12 @@ async function startOrResumeThread(params) {
|
|
|
1918
1889
|
request: resumeParams,
|
|
1919
1890
|
signal: params.signal
|
|
1920
1891
|
}));
|
|
1892
|
+
if (ringZeroActive) try {
|
|
1893
|
+
await lifecycleTiming.measure("ring-zero-mcp-attestation", () => attestCodexRingZeroThreadHasNoMcpServers(params.client, response.thread.id, params.signal));
|
|
1894
|
+
} catch (error) {
|
|
1895
|
+
await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
|
|
1896
|
+
throw new CodexRingZeroAttestationError(error);
|
|
1897
|
+
}
|
|
1921
1898
|
throwIfAborted();
|
|
1922
1899
|
const boundAuthProfileId = authProfileId;
|
|
1923
1900
|
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
|
|
@@ -1925,16 +1902,19 @@ async function startOrResumeThread(params) {
|
|
|
1925
1902
|
cwd: params.cwd,
|
|
1926
1903
|
authProfileId: boundAuthProfileId,
|
|
1927
1904
|
model: response.model ?? resumeParams.model ?? params.params.modelId,
|
|
1905
|
+
preserveNativeModel: resumeBinding.preserveNativeModel === true ? true : void 0,
|
|
1928
1906
|
modelProvider: normalizeBindingModelProvider(boundAuthProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider),
|
|
1929
1907
|
dynamicToolsFingerprint,
|
|
1930
1908
|
dynamicToolsContainDeferred,
|
|
1931
1909
|
webSearchThreadConfigFingerprint,
|
|
1932
1910
|
userMcpServersFingerprint,
|
|
1933
1911
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1912
|
+
ringZeroConfigFingerprint,
|
|
1913
|
+
ringZeroClientInstanceId,
|
|
1934
1914
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1935
1915
|
networkProxyConfigFingerprint,
|
|
1936
1916
|
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
|
|
1937
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1917
|
+
appServerRuntimeFingerprint: resumeBinding.connectionScope === "supervision" ? buildCodexAppServerConnectionFingerprint(params.appServer, params.params.agentDir) : params.appServerRuntimeFingerprint,
|
|
1938
1918
|
pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
|
|
1939
1919
|
pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
|
|
1940
1920
|
pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
|
|
@@ -1975,6 +1955,12 @@ async function startOrResumeThread(params) {
|
|
|
1975
1955
|
};
|
|
1976
1956
|
} catch (error) {
|
|
1977
1957
|
resumeReservation?.release();
|
|
1958
|
+
if (isCodexAppServerStartSelectionChangedError(error)) throw error;
|
|
1959
|
+
if (error instanceof CodexRingZeroAttestationError) {
|
|
1960
|
+
await clearCurrentBinding("retiring a failed ring-zero thread attestation");
|
|
1961
|
+
throw error;
|
|
1962
|
+
}
|
|
1963
|
+
if (error instanceof CodexAdoptedThreadActiveError) throw error;
|
|
1978
1964
|
if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
|
|
1979
1965
|
const resumeRejected = error instanceof CodexAppServerRpcError;
|
|
1980
1966
|
if (!await unsubscribeCodexThreadBestEffort(params.client, {
|
|
@@ -2004,7 +1990,9 @@ async function startOrResumeThread(params) {
|
|
|
2004
1990
|
webSearchAllowed: params.webSearchAllowed,
|
|
2005
1991
|
environmentSelection: params.environmentSelection,
|
|
2006
1992
|
model: startModelSelection.model,
|
|
2007
|
-
modelProvider: startModelProvider
|
|
1993
|
+
modelProvider: startModelProvider,
|
|
1994
|
+
hostSystemAgentActive,
|
|
1995
|
+
ringZeroInheritedMcpServerNames
|
|
2008
1996
|
}));
|
|
2009
1997
|
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
2010
1998
|
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
@@ -2015,6 +2003,12 @@ async function startOrResumeThread(params) {
|
|
|
2015
2003
|
throw error;
|
|
2016
2004
|
}
|
|
2017
2005
|
}));
|
|
2006
|
+
if (ringZeroActive) try {
|
|
2007
|
+
await lifecycleTiming.measure("ring-zero-mcp-attestation", () => attestCodexRingZeroThreadHasNoMcpServers(params.client, response.thread.id, params.signal));
|
|
2008
|
+
} catch (error) {
|
|
2009
|
+
await (params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)))();
|
|
2010
|
+
throw error;
|
|
2011
|
+
}
|
|
2018
2012
|
throwIfAborted();
|
|
2019
2013
|
const modelProvider = resolveCodexAppServerModelProvider({
|
|
2020
2014
|
provider: params.params.provider,
|
|
@@ -2039,6 +2033,8 @@ async function startOrResumeThread(params) {
|
|
|
2039
2033
|
webSearchThreadConfigFingerprint,
|
|
2040
2034
|
userMcpServersFingerprint,
|
|
2041
2035
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2036
|
+
ringZeroConfigFingerprint,
|
|
2037
|
+
ringZeroClientInstanceId,
|
|
2042
2038
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2043
2039
|
networkProxyConfigFingerprint,
|
|
2044
2040
|
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
@@ -2078,6 +2074,8 @@ async function startOrResumeThread(params) {
|
|
|
2078
2074
|
dynamicToolsContainDeferred,
|
|
2079
2075
|
userMcpServersFingerprint,
|
|
2080
2076
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2077
|
+
ringZeroConfigFingerprint,
|
|
2078
|
+
ringZeroClientInstanceId,
|
|
2081
2079
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2082
2080
|
networkProxyConfigFingerprint,
|
|
2083
2081
|
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
@@ -2094,6 +2092,325 @@ async function startOrResumeThread(params) {
|
|
|
2094
2092
|
};
|
|
2095
2093
|
});
|
|
2096
2094
|
}
|
|
2095
|
+
async function materializePendingSupervisionBranch(params) {
|
|
2096
|
+
let pending = params.binding.pendingSupervisionBranch;
|
|
2097
|
+
const connectionFingerprint = buildCodexAppServerConnectionFingerprint(params.appServer, params.attempt.agentDir);
|
|
2098
|
+
if (!pending.connectionFingerprint || pending.connectionFingerprint !== connectionFingerprint) throw new Error("Codex supervision source connection changed before branch materialization");
|
|
2099
|
+
pending = await recoverPendingSupervisionArtifacts(params, pending);
|
|
2100
|
+
params.throwIfAborted();
|
|
2101
|
+
const sourceResponse = await params.lifecycleTiming.measure("supervision-source-read", () => params.client.request("thread/read", {
|
|
2102
|
+
threadId: pending.sourceThreadId,
|
|
2103
|
+
includeTurns: true
|
|
2104
|
+
}, { signal: params.signal }));
|
|
2105
|
+
params.throwIfAborted();
|
|
2106
|
+
const sourceThread = sourceResponse.thread;
|
|
2107
|
+
if (sourceThread.id !== pending.sourceThreadId) throw new Error(`Codex supervision source read returned ${sourceThread.id} for ${pending.sourceThreadId}`);
|
|
2108
|
+
assertPendingSupervisionSnapshotUnchanged(sourceThread, pending);
|
|
2109
|
+
const history = projectBoundedCodexThreadHistory({
|
|
2110
|
+
thread: sourceThread,
|
|
2111
|
+
throughTurnId: pending.lastTurnId ?? null,
|
|
2112
|
+
importedAt: Date.now(),
|
|
2113
|
+
modelProvider: sourceThread.modelProvider
|
|
2114
|
+
});
|
|
2115
|
+
let bindingCommitted = false;
|
|
2116
|
+
let provisionalCleanupSafe = true;
|
|
2117
|
+
try {
|
|
2118
|
+
const probeParams = buildPendingSupervisionProbeForkParams(params, pending);
|
|
2119
|
+
const rawProbeResponse = await params.lifecycleTiming.measure("supervision-model-probe-fork", async () => {
|
|
2120
|
+
try {
|
|
2121
|
+
return await params.client.request("thread/fork", probeParams, { signal: params.signal });
|
|
2122
|
+
} catch (error) {
|
|
2123
|
+
if (!(error instanceof CodexAppServerRpcError)) throw new CodexAppServerUnsafeSubscriptionError("Codex model probe fork may have materialized without a response", { cause: error });
|
|
2124
|
+
throw error;
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
2127
|
+
const probeThreadId = requireDistinctSupervisionThreadId({
|
|
2128
|
+
threadId: readSupervisionResponseThreadId(rawProbeResponse),
|
|
2129
|
+
sourceThreadId: pending.sourceThreadId,
|
|
2130
|
+
role: "model probe"
|
|
2131
|
+
});
|
|
2132
|
+
pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId]);
|
|
2133
|
+
params.throwIfAborted();
|
|
2134
|
+
const probeResponse = assertCodexThreadForkResponse(rawProbeResponse);
|
|
2135
|
+
const nativeModel = requireNonBlankSupervisionValue(probeResponse.model, "native model");
|
|
2136
|
+
const nativeModelProvider = requireNativeSupervisionModelProvider({
|
|
2137
|
+
responseModelProvider: probeResponse.modelProvider,
|
|
2138
|
+
responseThreadModelProvider: probeResponse.thread.modelProvider
|
|
2139
|
+
});
|
|
2140
|
+
const startParams = buildThreadStartParams({
|
|
2141
|
+
...params.attempt,
|
|
2142
|
+
modelId: nativeModel
|
|
2143
|
+
}, {
|
|
2144
|
+
cwd: params.cwd,
|
|
2145
|
+
dynamicTools: params.dynamicTools,
|
|
2146
|
+
appServer: params.appServer,
|
|
2147
|
+
developerInstructions: params.developerInstructions,
|
|
2148
|
+
config: params.config,
|
|
2149
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
2150
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
2151
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2152
|
+
webSearchAllowed: params.webSearchAllowed,
|
|
2153
|
+
environmentSelection: params.environmentSelection,
|
|
2154
|
+
model: nativeModel,
|
|
2155
|
+
modelProvider: nativeModelProvider
|
|
2156
|
+
});
|
|
2157
|
+
assertExactSupervisionModelSelection(startParams, {
|
|
2158
|
+
model: nativeModel,
|
|
2159
|
+
modelProvider: nativeModelProvider,
|
|
2160
|
+
operation: "thread/start request"
|
|
2161
|
+
});
|
|
2162
|
+
const rawStartResponse = await params.lifecycleTiming.measure("supervision-thread-start", async () => {
|
|
2163
|
+
try {
|
|
2164
|
+
return await params.client.request("thread/start", startParams, { signal: params.signal });
|
|
2165
|
+
} catch (error) {
|
|
2166
|
+
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
2167
|
+
throw new CodexAppServerUnsafeSubscriptionError("Canonical Codex branch may have started without a response", { cause: error });
|
|
2168
|
+
}
|
|
2169
|
+
});
|
|
2170
|
+
const finalThreadId = requireDistinctSupervisionThreadId({
|
|
2171
|
+
threadId: readSupervisionResponseThreadId(rawStartResponse),
|
|
2172
|
+
sourceThreadId: pending.sourceThreadId,
|
|
2173
|
+
otherThreadId: probeThreadId,
|
|
2174
|
+
role: "canonical branch"
|
|
2175
|
+
});
|
|
2176
|
+
pending = await trackPendingSupervisionArtifacts(params, pending, [probeThreadId, finalThreadId]);
|
|
2177
|
+
params.throwIfAborted();
|
|
2178
|
+
assertExactSupervisionModelSelection(assertCodexThreadStartResponse(rawStartResponse), {
|
|
2179
|
+
model: nativeModel,
|
|
2180
|
+
modelProvider: nativeModelProvider,
|
|
2181
|
+
operation: "thread/start response"
|
|
2182
|
+
});
|
|
2183
|
+
if (history.responseItems.length > 0) {
|
|
2184
|
+
await params.lifecycleTiming.measure("supervision-history-inject", () => params.client.request("thread/inject_items", {
|
|
2185
|
+
threadId: finalThreadId,
|
|
2186
|
+
items: history.responseItems
|
|
2187
|
+
}, { signal: params.signal }));
|
|
2188
|
+
params.throwIfAborted();
|
|
2189
|
+
}
|
|
2190
|
+
if (!await archiveSupervisionArtifact(params.client, probeThreadId)) throw new Error(`Failed to archive temporary Codex model probe: ${probeThreadId}`);
|
|
2191
|
+
pending = await trackPendingSupervisionArtifacts(params, pending, [finalThreadId]);
|
|
2192
|
+
const historyCoveredThrough = (/* @__PURE__ */ new Date()).toISOString();
|
|
2193
|
+
const bindingModelProvider = params.normalizeBindingModelProvider(params.attempt.authProfileId, nativeModelProvider);
|
|
2194
|
+
let committed = false;
|
|
2195
|
+
try {
|
|
2196
|
+
committed = await params.bindingStore.mutate(params.bindingIdentity, {
|
|
2197
|
+
kind: "commit-pending-supervision-branch",
|
|
2198
|
+
expected: pending,
|
|
2199
|
+
threadId: finalThreadId,
|
|
2200
|
+
patch: {
|
|
2201
|
+
...params.bindingPatch,
|
|
2202
|
+
model: nativeModel,
|
|
2203
|
+
modelProvider: bindingModelProvider,
|
|
2204
|
+
historyCoveredThrough
|
|
2205
|
+
}
|
|
2206
|
+
});
|
|
2207
|
+
} catch (error) {
|
|
2208
|
+
let current;
|
|
2209
|
+
try {
|
|
2210
|
+
current = await params.bindingStore.read(params.bindingIdentity);
|
|
2211
|
+
} catch (readError) {
|
|
2212
|
+
provisionalCleanupSafe = false;
|
|
2213
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Canonical Codex branch binding could not be verified: ${finalThreadId}`, { cause: new AggregateError([error, readError]) });
|
|
2214
|
+
}
|
|
2215
|
+
if (matchesMaterializedSupervisionBranch(current, {
|
|
2216
|
+
sourceThreadId: pending.sourceThreadId,
|
|
2217
|
+
connectionFingerprint,
|
|
2218
|
+
threadId: finalThreadId,
|
|
2219
|
+
model: nativeModel,
|
|
2220
|
+
modelProvider: bindingModelProvider,
|
|
2221
|
+
historyCoveredThrough
|
|
2222
|
+
})) committed = true;
|
|
2223
|
+
else {
|
|
2224
|
+
if (!matchesPendingSupervisionState(current, pending)) {
|
|
2225
|
+
provisionalCleanupSafe = false;
|
|
2226
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Canonical Codex branch binding changed while commit was uncertain: ${finalThreadId}`, { cause: error });
|
|
2227
|
+
}
|
|
2228
|
+
throw error;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
if (!committed) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "committing a supervised Codex branch");
|
|
2232
|
+
bindingCommitted = true;
|
|
2233
|
+
params.lifecycleTiming.mark("thread-ready");
|
|
2234
|
+
params.lifecycleTiming.logSummary({
|
|
2235
|
+
runId: params.attempt.runId,
|
|
2236
|
+
sessionId: params.attempt.sessionId,
|
|
2237
|
+
sessionKey: params.attempt.sessionKey,
|
|
2238
|
+
threadId: finalThreadId,
|
|
2239
|
+
action: "forked"
|
|
2240
|
+
});
|
|
2241
|
+
return {
|
|
2242
|
+
...params.binding,
|
|
2243
|
+
...params.bindingPatch,
|
|
2244
|
+
threadId: finalThreadId,
|
|
2245
|
+
pendingSupervisionBranch: void 0,
|
|
2246
|
+
model: nativeModel,
|
|
2247
|
+
modelProvider: bindingModelProvider,
|
|
2248
|
+
historyCoveredThrough,
|
|
2249
|
+
lifecycle: { action: "forked" }
|
|
2250
|
+
};
|
|
2251
|
+
} catch (error) {
|
|
2252
|
+
if (bindingCommitted) throw error;
|
|
2253
|
+
if (error instanceof CodexThreadBindingConflictAfterCleanupError) throw error;
|
|
2254
|
+
if (!provisionalCleanupSafe) {
|
|
2255
|
+
await params.abandonClient();
|
|
2256
|
+
throw error;
|
|
2257
|
+
}
|
|
2258
|
+
const cleanup = await cleanPendingSupervisionArtifacts(params.client, pending);
|
|
2259
|
+
let cleanupStateError;
|
|
2260
|
+
if (cleanup.remaining.length !== (pending.cleanupThreadIds?.length ?? 0)) {
|
|
2261
|
+
const nextPending = withPendingSupervisionCleanup(pending, cleanup.remaining);
|
|
2262
|
+
try {
|
|
2263
|
+
if (await params.bindingStore.mutate(params.bindingIdentity, {
|
|
2264
|
+
kind: "patch-pending-supervision-branch",
|
|
2265
|
+
expected: pending,
|
|
2266
|
+
pending: nextPending
|
|
2267
|
+
})) pending = nextPending;
|
|
2268
|
+
} catch (stateError) {
|
|
2269
|
+
cleanupStateError = stateError;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
const unsafeCleanup = cleanup.remaining.length > 0 || isCodexAppServerUnsafeSubscriptionError(error);
|
|
2273
|
+
if (unsafeCleanup) await params.abandonClient();
|
|
2274
|
+
if (cleanupStateError) {
|
|
2275
|
+
const cause = new AggregateError([error, cleanupStateError]);
|
|
2276
|
+
if (unsafeCleanup) throw new CodexAppServerUnsafeSubscriptionError("Codex supervised branch cleanup state could not be recorded", { cause });
|
|
2277
|
+
throw new AggregateError([error, cleanupStateError], "Codex supervised branch cleanup state could not be recorded", { cause: error });
|
|
2278
|
+
}
|
|
2279
|
+
if (cleanup.remaining.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch cleanup remains pending: ${cleanup.remaining.join(", ")}`, { cause: error });
|
|
2280
|
+
throw error;
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
function buildPendingSupervisionProbeForkParams(params, pending) {
|
|
2284
|
+
const runtimeConfig = buildCodexRuntimeThreadConfigForRun(params.attempt, params.config, {
|
|
2285
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
2286
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
2287
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2288
|
+
webSearchAllowed: params.webSearchAllowed,
|
|
2289
|
+
appServer: params.appServer
|
|
2290
|
+
});
|
|
2291
|
+
return {
|
|
2292
|
+
threadId: pending.sourceThreadId,
|
|
2293
|
+
...pending.lastTurnId ? { lastTurnId: pending.lastTurnId } : {},
|
|
2294
|
+
cwd: params.cwd,
|
|
2295
|
+
approvalPolicy: params.appServer.approvalPolicy,
|
|
2296
|
+
approvalsReviewer: resolveCodexThreadApprovalsReviewer(params.appServer, runtimeConfig),
|
|
2297
|
+
...codexThreadSandboxOrPermissions(params.appServer),
|
|
2298
|
+
...params.appServer.serviceTier !== void 0 ? { serviceTier: params.appServer.serviceTier } : {},
|
|
2299
|
+
config: runtimeConfig,
|
|
2300
|
+
developerInstructions: params.developerInstructions ?? buildDeveloperInstructions(params.attempt, { dynamicTools: params.dynamicTools }),
|
|
2301
|
+
ephemeral: false,
|
|
2302
|
+
threadSource: "appServer",
|
|
2303
|
+
excludeTurns: true
|
|
2304
|
+
};
|
|
2305
|
+
}
|
|
2306
|
+
function assertPendingSupervisionSnapshotUnchanged(thread, pending) {
|
|
2307
|
+
if (pending.lastTurnId) return;
|
|
2308
|
+
if (thread.status?.type === "active" || (thread.turns?.length ?? 0) > 0) throw new Error("Codex source changed after Continue; reopen the source session before sending a message");
|
|
2309
|
+
}
|
|
2310
|
+
function requireNonBlankSupervisionValue(value, label) {
|
|
2311
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`Codex supervision ${label} is missing`);
|
|
2312
|
+
return value.trim();
|
|
2313
|
+
}
|
|
2314
|
+
function requireNativeSupervisionModelProvider(params) {
|
|
2315
|
+
const responseProvider = requireNonBlankSupervisionValue(params.responseModelProvider, "native model provider");
|
|
2316
|
+
const threadProvider = params.responseThreadModelProvider?.trim();
|
|
2317
|
+
if (threadProvider && threadProvider !== responseProvider) throw new Error(`Codex supervision model provider mismatch: ${responseProvider} != ${threadProvider}`);
|
|
2318
|
+
return responseProvider;
|
|
2319
|
+
}
|
|
2320
|
+
function assertExactSupervisionModelSelection(value, expected) {
|
|
2321
|
+
if (value.model !== expected.model || value.modelProvider !== expected.modelProvider) throw new Error(`Codex supervision ${expected.operation} changed native model selection: ${value.modelProvider ?? "unknown"}/${value.model ?? "unknown"}`);
|
|
2322
|
+
}
|
|
2323
|
+
function matchesPendingSupervisionState(binding, expected) {
|
|
2324
|
+
const pending = binding?.pendingSupervisionBranch;
|
|
2325
|
+
const cleanupThreadIds = pending?.cleanupThreadIds ?? [];
|
|
2326
|
+
const expectedCleanupThreadIds = expected.cleanupThreadIds ?? [];
|
|
2327
|
+
return binding?.threadId === expected.sourceThreadId && binding.connectionScope === "supervision" && binding.supervisionSourceThreadId === expected.sourceThreadId && pending?.sourceThreadId === expected.sourceThreadId && pending.connectionFingerprint === expected.connectionFingerprint && pending.lastTurnId === expected.lastTurnId && cleanupThreadIds.length === expectedCleanupThreadIds.length && cleanupThreadIds.every((threadId, index) => threadId === expectedCleanupThreadIds[index]);
|
|
2328
|
+
}
|
|
2329
|
+
function matchesMaterializedSupervisionBranch(binding, expected) {
|
|
2330
|
+
return binding?.threadId === expected.threadId && binding.connectionScope === "supervision" && binding.supervisionSourceThreadId === expected.sourceThreadId && binding.appServerRuntimeFingerprint === expected.connectionFingerprint && binding.pendingSupervisionBranch === void 0 && binding.model === expected.model && binding.modelProvider === expected.modelProvider && binding.historyCoveredThrough === expected.historyCoveredThrough;
|
|
2331
|
+
}
|
|
2332
|
+
function requireDistinctSupervisionThreadId(params) {
|
|
2333
|
+
let threadId;
|
|
2334
|
+
try {
|
|
2335
|
+
threadId = requireNonBlankSupervisionValue(params.threadId, `${params.role} thread id`);
|
|
2336
|
+
} catch (error) {
|
|
2337
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Codex supervision ${params.role} may have materialized without a safe thread id`, { cause: error });
|
|
2338
|
+
}
|
|
2339
|
+
if (threadId === params.sourceThreadId || threadId === params.otherThreadId) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervision ${params.role} reused an existing thread: ${threadId}`);
|
|
2340
|
+
return threadId;
|
|
2341
|
+
}
|
|
2342
|
+
function readSupervisionResponseThreadId(value) {
|
|
2343
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return;
|
|
2344
|
+
const thread = value.thread;
|
|
2345
|
+
if (!thread || typeof thread !== "object" || Array.isArray(thread)) return;
|
|
2346
|
+
return thread.id;
|
|
2347
|
+
}
|
|
2348
|
+
async function recoverPendingSupervisionArtifacts(params, pending) {
|
|
2349
|
+
if (!pending.cleanupThreadIds?.length) return pending;
|
|
2350
|
+
const cleanup = await cleanPendingSupervisionArtifacts(params.client, pending);
|
|
2351
|
+
const next = withPendingSupervisionCleanup(pending, cleanup.remaining);
|
|
2352
|
+
if (cleanup.remaining.length > 0) {
|
|
2353
|
+
if (cleanup.remaining.length !== pending.cleanupThreadIds.length) {
|
|
2354
|
+
if (!await params.bindingStore.mutate(params.bindingIdentity, {
|
|
2355
|
+
kind: "patch-pending-supervision-branch",
|
|
2356
|
+
expected: pending,
|
|
2357
|
+
pending: next
|
|
2358
|
+
})) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "recording supervised Codex cleanup recovery");
|
|
2359
|
+
}
|
|
2360
|
+
throw new Error(`Codex supervised branch cleanup must finish before retry: ${cleanup.remaining.join(", ")}`);
|
|
2361
|
+
}
|
|
2362
|
+
if (!await params.bindingStore.mutate(params.bindingIdentity, {
|
|
2363
|
+
kind: "patch-pending-supervision-branch",
|
|
2364
|
+
expected: pending,
|
|
2365
|
+
pending: next
|
|
2366
|
+
})) throw new CodexThreadBindingConflictError(pending.sourceThreadId, "recovering a supervised Codex branch");
|
|
2367
|
+
return next;
|
|
2368
|
+
}
|
|
2369
|
+
async function trackPendingSupervisionArtifacts(params, pending, cleanupThreadIds) {
|
|
2370
|
+
const next = withPendingSupervisionCleanup(pending, cleanupThreadIds);
|
|
2371
|
+
if (!await params.bindingStore.mutate(params.bindingIdentity, {
|
|
2372
|
+
kind: "patch-pending-supervision-branch",
|
|
2373
|
+
expected: pending,
|
|
2374
|
+
pending: next
|
|
2375
|
+
})) {
|
|
2376
|
+
const cleanupFailed = [];
|
|
2377
|
+
for (const threadId of cleanupThreadIds) if (!await archiveSupervisionArtifact(params.client, threadId)) cleanupFailed.push(threadId);
|
|
2378
|
+
if (cleanupFailed.length > 0) throw new CodexAppServerUnsafeSubscriptionError(`Codex supervised branch CAS cleanup failed: ${cleanupFailed.join(", ")}`);
|
|
2379
|
+
throw new CodexThreadBindingConflictAfterCleanupError(pending.sourceThreadId, "tracking supervised Codex branch cleanup");
|
|
2380
|
+
}
|
|
2381
|
+
return next;
|
|
2382
|
+
}
|
|
2383
|
+
function withPendingSupervisionCleanup(pending, cleanupThreadIds) {
|
|
2384
|
+
return {
|
|
2385
|
+
sourceThreadId: pending.sourceThreadId,
|
|
2386
|
+
...pending.connectionFingerprint ? { connectionFingerprint: pending.connectionFingerprint } : {},
|
|
2387
|
+
...pending.lastTurnId ? { lastTurnId: pending.lastTurnId } : {},
|
|
2388
|
+
...cleanupThreadIds.length > 0 ? { cleanupThreadIds } : {}
|
|
2389
|
+
};
|
|
2390
|
+
}
|
|
2391
|
+
async function cleanPendingSupervisionArtifacts(client, pending) {
|
|
2392
|
+
const remaining = [];
|
|
2393
|
+
for (const threadId of pending.cleanupThreadIds ?? []) if (!await archiveSupervisionArtifact(client, threadId)) remaining.push(threadId);
|
|
2394
|
+
return { remaining };
|
|
2395
|
+
}
|
|
2396
|
+
async function archiveSupervisionArtifact(client, threadId) {
|
|
2397
|
+
try {
|
|
2398
|
+
await client.request("thread/archive", { threadId }, { timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS });
|
|
2399
|
+
return true;
|
|
2400
|
+
} catch (error) {
|
|
2401
|
+
const message = formatErrorMessage(error).toLowerCase();
|
|
2402
|
+
if (message.includes("no rollout found for thread id") || message.includes("thread not found") || message.includes("already archived")) return true;
|
|
2403
|
+
await unsubscribeCodexThreadBestEffort(client, {
|
|
2404
|
+
threadId,
|
|
2405
|
+
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
2406
|
+
});
|
|
2407
|
+
embeddedAgentLog.warn("failed to archive temporary Codex supervision thread", {
|
|
2408
|
+
threadId,
|
|
2409
|
+
error
|
|
2410
|
+
});
|
|
2411
|
+
return false;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2097
2414
|
function shouldRotateCodexAppServerBindingForRuntime(params) {
|
|
2098
2415
|
if (!params.current) return false;
|
|
2099
2416
|
if (params.binding === params.current) return false;
|
|
@@ -2191,6 +2508,7 @@ function shouldRecheckRecoverablePluginBinding(params) {
|
|
|
2191
2508
|
return Object.keys(policyContext.apps).length === 0 || expectedPluginConfigKeys.length > 0;
|
|
2192
2509
|
}
|
|
2193
2510
|
function buildThreadStartParams(params, options) {
|
|
2511
|
+
const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
|
|
2194
2512
|
const resolvedModelProvider = resolveCodexAppServerModelProvider({
|
|
2195
2513
|
provider: params.provider,
|
|
2196
2514
|
authProfileId: params.authProfileId,
|
|
@@ -2216,12 +2534,16 @@ function buildThreadStartParams(params, options) {
|
|
|
2216
2534
|
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
2217
2535
|
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
2218
2536
|
serviceName: "OpenClaw",
|
|
2537
|
+
...ringZeroActive ? { baseInstructions: CODEX_RING_ZERO_BASE_INSTRUCTIONS } : {},
|
|
2219
2538
|
config: buildCodexRuntimeThreadConfigForRun(params, options.config, {
|
|
2220
2539
|
nativeCodeModeEnabled: options.nativeCodeModeEnabled,
|
|
2221
2540
|
nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
|
|
2222
2541
|
nativeCodeModeOnlyEnabled: options.nativeCodeModeOnlyEnabled,
|
|
2542
|
+
directOnlyToolNamespaces: resolveDirectOnlyToolNamespaces(options.dynamicTools),
|
|
2223
2543
|
webSearchAllowed: options.webSearchAllowed,
|
|
2224
|
-
appServer: options.appServer
|
|
2544
|
+
appServer: options.appServer,
|
|
2545
|
+
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2546
|
+
ringZeroInheritedMcpServerNames: options.ringZeroInheritedMcpServerNames
|
|
2225
2547
|
}),
|
|
2226
2548
|
...resolveCodexThreadEnvironmentSelection(options),
|
|
2227
2549
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
|
|
@@ -2230,16 +2552,15 @@ function buildThreadStartParams(params, options) {
|
|
|
2230
2552
|
};
|
|
2231
2553
|
}
|
|
2232
2554
|
function buildThreadResumeParams(params, options) {
|
|
2233
|
-
const
|
|
2234
|
-
provider: params.provider,
|
|
2235
|
-
authProfileId: options.authProfileId ?? params.authProfileId,
|
|
2236
|
-
authProfileStore: params.authProfileStore,
|
|
2237
|
-
agentDir: params.agentDir,
|
|
2238
|
-
config: params.config
|
|
2239
|
-
});
|
|
2240
|
-
const modelSelection = resolveCodexAppServerRequestModelSelection({
|
|
2555
|
+
const modelSelection = options.preserveNativeModel ? void 0 : resolveCodexAppServerRequestModelSelection({
|
|
2241
2556
|
model: options.model ?? params.modelId,
|
|
2242
|
-
modelProvider: options.modelProvider ??
|
|
2557
|
+
modelProvider: options.modelProvider ?? resolveCodexAppServerModelProvider({
|
|
2558
|
+
provider: params.provider,
|
|
2559
|
+
authProfileId: options.authProfileId ?? params.authProfileId,
|
|
2560
|
+
authProfileStore: params.authProfileStore,
|
|
2561
|
+
agentDir: params.agentDir,
|
|
2562
|
+
config: params.config
|
|
2563
|
+
}),
|
|
2243
2564
|
authProfileId: options.authProfileId ?? params.authProfileId,
|
|
2244
2565
|
authProfileStore: params.authProfileStore,
|
|
2245
2566
|
agentDir: params.agentDir,
|
|
@@ -2247,8 +2568,10 @@ function buildThreadResumeParams(params, options) {
|
|
|
2247
2568
|
});
|
|
2248
2569
|
return {
|
|
2249
2570
|
threadId: options.threadId,
|
|
2250
|
-
|
|
2251
|
-
|
|
2571
|
+
...modelSelection ? {
|
|
2572
|
+
model: modelSelection.model,
|
|
2573
|
+
...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {}
|
|
2574
|
+
} : {},
|
|
2252
2575
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
2253
2576
|
approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
|
|
2254
2577
|
...codexThreadSandboxOrPermissions(options.appServer),
|
|
@@ -2258,8 +2581,11 @@ function buildThreadResumeParams(params, options) {
|
|
|
2258
2581
|
nativeCodeModeEnabled: options.nativeCodeModeEnabled,
|
|
2259
2582
|
nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
|
|
2260
2583
|
nativeCodeModeOnlyEnabled: options.nativeCodeModeOnlyEnabled,
|
|
2584
|
+
directOnlyToolNamespaces: resolveDirectOnlyToolNamespaces(options.dynamicTools),
|
|
2261
2585
|
webSearchAllowed: options.webSearchAllowed,
|
|
2262
|
-
appServer: options.appServer
|
|
2586
|
+
appServer: options.appServer,
|
|
2587
|
+
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2588
|
+
ringZeroInheritedMcpServerNames: options.ringZeroInheritedMcpServerNames
|
|
2263
2589
|
}),
|
|
2264
2590
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
2265
2591
|
};
|
|
@@ -2332,13 +2658,29 @@ function buildCodexRuntimeThreadConfig(config, options = {}) {
|
|
|
2332
2658
|
delete disabledConfig["features.apply_patch_streaming_events"];
|
|
2333
2659
|
return disabledConfig;
|
|
2334
2660
|
}
|
|
2335
|
-
if (options.nativeCodeModeOnlyEnabled === true) return mergeCodexThreadConfigs(codeModeConfig, config, { "features.code_mode_only": true }) ?? {
|
|
2661
|
+
if (options.nativeCodeModeOnlyEnabled === true) return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config, { "features.code_mode_only": true }) ?? {
|
|
2336
2662
|
...codeModeConfig,
|
|
2337
2663
|
"features.code_mode_only": true
|
|
2664
|
+
}, options.directOnlyToolNamespaces);
|
|
2665
|
+
return ensureDirectOnlyToolNamespaces(mergeCodexThreadConfigs(codeModeConfig, config) ?? { ...codeModeConfig }, options.directOnlyToolNamespaces);
|
|
2666
|
+
}
|
|
2667
|
+
function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
|
|
2668
|
+
if (!requiredNamespaces?.length) return config;
|
|
2669
|
+
const configured = config["code_mode.direct_only_tool_namespaces"];
|
|
2670
|
+
const namespaces = Array.isArray(configured) ? configured.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
2671
|
+
return {
|
|
2672
|
+
...config,
|
|
2673
|
+
"code_mode.direct_only_tool_namespaces": [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
|
|
2338
2674
|
};
|
|
2339
|
-
|
|
2675
|
+
}
|
|
2676
|
+
function resolveDirectOnlyToolNamespaces(dynamicTools) {
|
|
2677
|
+
return (dynamicTools ?? []).filter((tool) => tool.type === "namespace" && tool.name === "openclaw_direct").map((tool) => tool.name);
|
|
2340
2678
|
}
|
|
2341
2679
|
function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
2680
|
+
const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
|
|
2681
|
+
const configMcpServers = config?.mcp_servers;
|
|
2682
|
+
if (ringZeroActive && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex ring-zero received invalid thread mcp_servers config");
|
|
2683
|
+
const ringZeroMcpServerNames = [...options.ringZeroInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
|
|
2342
2684
|
const webSearchConfig = resolveCodexWebSearchPlan({
|
|
2343
2685
|
config: params.config,
|
|
2344
2686
|
disableTools: params.disableTools,
|
|
@@ -2347,15 +2689,85 @@ function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
|
2347
2689
|
webSearchAllowed: options.webSearchAllowed
|
|
2348
2690
|
}).threadConfig;
|
|
2349
2691
|
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(config, webSearchConfig), options);
|
|
2350
|
-
const runtimeConfig = mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0) ?? baseConfig;
|
|
2692
|
+
const runtimeConfig = mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0, buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, ringZeroMcpServerNames)) ?? baseConfig;
|
|
2351
2693
|
if (params.bootstrapContextMode !== "lightweight") return runtimeConfig;
|
|
2352
2694
|
return mergeCodexThreadConfigs(runtimeConfig, CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG) ?? {
|
|
2353
2695
|
...runtimeConfig,
|
|
2354
2696
|
...CODEX_LIGHTWEIGHT_CONTEXT_THREAD_CONFIG
|
|
2355
2697
|
};
|
|
2356
2698
|
}
|
|
2699
|
+
function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
|
|
2700
|
+
if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
|
|
2701
|
+
const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
|
|
2702
|
+
return {
|
|
2703
|
+
...CODEX_RING_ZERO_THREAD_CONFIG,
|
|
2704
|
+
...Object.keys(mcpServers).length > 0 ? { mcp_servers: mcpServers } : {}
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2707
|
+
async function readCodexInheritedMcpServerNames(client, cwd, signal) {
|
|
2708
|
+
const response = await client.request("config/read", {
|
|
2709
|
+
cwd,
|
|
2710
|
+
includeLayers: true
|
|
2711
|
+
}, { signal });
|
|
2712
|
+
if (!isJsonObject(response) || !isJsonObject(response.config)) throw new Error("Codex config/read returned an invalid effective config");
|
|
2713
|
+
if (!Array.isArray(response.layers)) throw new Error("Codex config/read omitted effective config layers");
|
|
2714
|
+
for (const layer of response.layers) {
|
|
2715
|
+
if (!isJsonObject(layer) || !isJsonObject(layer.name) || typeof layer.name.type !== "string") throw new Error("Codex config/read returned invalid effective config layers");
|
|
2716
|
+
if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm") throw new Error(`Codex ring-zero cannot override config layer ${layer.name.type}`);
|
|
2717
|
+
if (!CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES.has(layer.name.type)) throw new Error(`Codex ring-zero does not recognize config layer ${layer.name.type}`);
|
|
2718
|
+
}
|
|
2719
|
+
const configuredServers = response.config.mcp_servers;
|
|
2720
|
+
if (configuredServers === void 0) return [];
|
|
2721
|
+
if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
|
|
2722
|
+
return Object.keys(configuredServers).toSorted();
|
|
2723
|
+
}
|
|
2724
|
+
async function assertCodexRingZeroHasNoManagedHooks(client, signal) {
|
|
2725
|
+
const response = await client.request("configRequirements/read", void 0, { signal });
|
|
2726
|
+
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
|
|
2727
|
+
if (response.requirements === null) return;
|
|
2728
|
+
if (!isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
|
|
2729
|
+
for (const key of [
|
|
2730
|
+
"hooks",
|
|
2731
|
+
"managedHooks",
|
|
2732
|
+
"managed_hooks"
|
|
2733
|
+
]) {
|
|
2734
|
+
const hooks = response.requirements[key];
|
|
2735
|
+
if (hooks === void 0 || hooks === null) continue;
|
|
2736
|
+
if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
|
|
2737
|
+
if (hasNonEmptyJsonValue(hooks)) throw new Error("Codex ring-zero cannot override managed hooks");
|
|
2738
|
+
}
|
|
2739
|
+
for (const key of ["featureRequirements", "feature_requirements"]) {
|
|
2740
|
+
const requirements = response.requirements[key];
|
|
2741
|
+
if (requirements === void 0 || requirements === null) continue;
|
|
2742
|
+
if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2743
|
+
for (const [feature, enabled] of Object.entries(requirements)) {
|
|
2744
|
+
if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2745
|
+
if (enabled && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(feature)) throw new Error(`Codex ring-zero cannot override required feature ${feature}`);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
}
|
|
2749
|
+
async function attestCodexRingZeroThreadHasNoMcpServers(client, threadId, signal) {
|
|
2750
|
+
const response = await client.request("mcpServerStatus/list", {
|
|
2751
|
+
threadId,
|
|
2752
|
+
limit: 1,
|
|
2753
|
+
detail: "toolsAndAuthOnly"
|
|
2754
|
+
}, { signal });
|
|
2755
|
+
if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned an invalid ring-zero attestation");
|
|
2756
|
+
if (response.data.length > 0) {
|
|
2757
|
+
const first = response.data[0];
|
|
2758
|
+
const serverName = isJsonObject(first) && typeof first.name === "string" ? first.name : "unknown";
|
|
2759
|
+
throw new Error(`Codex ring-zero MCP attestation found server ${serverName}`);
|
|
2760
|
+
}
|
|
2761
|
+
if (response.nextCursor !== void 0 && response.nextCursor !== null) throw new Error("Codex mcpServerStatus/list returned an invalid empty-page cursor");
|
|
2762
|
+
}
|
|
2763
|
+
function hasNonEmptyJsonValue(value) {
|
|
2764
|
+
if (value === null || value === false || value === "") return false;
|
|
2765
|
+
if (Array.isArray(value)) return value.length > 0;
|
|
2766
|
+
if (typeof value === "object") return Object.values(value).some(hasNonEmptyJsonValue);
|
|
2767
|
+
return true;
|
|
2768
|
+
}
|
|
2357
2769
|
function buildTurnStartParams(params, options) {
|
|
2358
|
-
const modelSelection = resolveCodexAppServerRequestModelSelection({
|
|
2770
|
+
const modelSelection = options.preserveNativeTurnSettings ? void 0 : resolveCodexAppServerRequestModelSelection({
|
|
2359
2771
|
model: options.model ?? params.modelId,
|
|
2360
2772
|
modelProvider: options.modelProvider,
|
|
2361
2773
|
authProfileId: params.authProfileId,
|
|
@@ -2371,18 +2783,20 @@ function buildTurnStartParams(params, options) {
|
|
|
2371
2783
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
2372
2784
|
approvalsReviewer: options.appServer.approvalsReviewer,
|
|
2373
2785
|
...useThreadPermissionProfile ? {} : { sandboxPolicy: options.sandboxPolicy ?? codexSandboxPolicyForTurn(options.appServer.sandbox, options.cwd) },
|
|
2374
|
-
|
|
2375
|
-
|
|
2786
|
+
...modelSelection ? {
|
|
2787
|
+
model: modelSelection.model,
|
|
2788
|
+
personality: CODEX_NATIVE_PERSONALITY_NONE
|
|
2789
|
+
} : {},
|
|
2376
2790
|
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
2377
|
-
effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model, readCodexSupportedReasoningEfforts(params.model?.compat)),
|
|
2791
|
+
...modelSelection ? { effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model, readCodexSupportedReasoningEfforts(params.model?.compat)) } : {},
|
|
2378
2792
|
...options.environmentSelection ? { environments: options.environmentSelection } : {},
|
|
2379
|
-
collaborationMode: buildTurnCollaborationMode(params, {
|
|
2793
|
+
...modelSelection ? { collaborationMode: buildTurnCollaborationMode(params, {
|
|
2380
2794
|
model: modelSelection.model,
|
|
2381
2795
|
turnScopedDeveloperInstructions: options.turnScopedDeveloperInstructions,
|
|
2382
2796
|
skillsCollaborationInstructions: options.skillsCollaborationInstructions,
|
|
2383
2797
|
memoryCollaborationInstructions: options.memoryCollaborationInstructions,
|
|
2384
2798
|
heartbeatCollaborationInstructions: options.heartbeatCollaborationInstructions
|
|
2385
|
-
})
|
|
2799
|
+
}) } : {}
|
|
2386
2800
|
};
|
|
2387
2801
|
}
|
|
2388
2802
|
function resolveCodexThreadApprovalsReviewer(appServer, config) {
|
|
@@ -2467,7 +2881,23 @@ function legacyFingerprintUserMcpServersConfigPatch(configPatch) {
|
|
|
2467
2881
|
return configPatch ? JSON.stringify(stabilizeJsonValue(configPatch)) : void 0;
|
|
2468
2882
|
}
|
|
2469
2883
|
function fingerprintUserMcpServersConfigPatch(configPatch) {
|
|
2470
|
-
return configPatch ? hashCodexAppServerBindingFingerprint(JSON.stringify(stabilizeJsonValue(configPatch))) : void 0;
|
|
2884
|
+
return configPatch ? hashCodexAppServerBindingFingerprint(JSON.stringify(stabilizeJsonValue(redactUserMcpServersFingerprintSecrets(configPatch)))) : void 0;
|
|
2885
|
+
}
|
|
2886
|
+
function redactUserMcpServersFingerprintSecrets(value) {
|
|
2887
|
+
if (Array.isArray(value)) return value.map(redactUserMcpServersFingerprintSecrets);
|
|
2888
|
+
if (!value || typeof value !== "object") return value;
|
|
2889
|
+
const next = {};
|
|
2890
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
2891
|
+
if (key === "http_headers" && entry && typeof entry === "object" && !Array.isArray(entry)) {
|
|
2892
|
+
next[key] = Object.fromEntries(Object.entries(entry).map(([header, headerValue]) => [header, header.toLowerCase() === "authorization" ? fingerprintUserMcpServersAuthorizationHeader(headerValue) : headerValue]));
|
|
2893
|
+
continue;
|
|
2894
|
+
}
|
|
2895
|
+
next[key] = redactUserMcpServersFingerprintSecrets(entry);
|
|
2896
|
+
}
|
|
2897
|
+
return next;
|
|
2898
|
+
}
|
|
2899
|
+
function fingerprintUserMcpServersAuthorizationHeader(value) {
|
|
2900
|
+
return typeof value === "string" && value.length > 0 ? `<redacted:sha256:${crypto$1.createHash("sha256").update(value).digest("hex")}>` : "<redacted>";
|
|
2471
2901
|
}
|
|
2472
2902
|
function fingerprintJsonObject(value) {
|
|
2473
2903
|
return JSON.stringify(stabilizeJsonValue(value));
|
|
@@ -2541,7 +2971,7 @@ function buildSkillWorkshopInstruction(dynamicTools) {
|
|
|
2541
2971
|
}
|
|
2542
2972
|
function buildVisibleReplyInstruction(params, dynamicTools) {
|
|
2543
2973
|
const messageToolAvailable = dynamicTools ? flattenCodexDynamicToolFunctions(dynamicTools).some((tool) => tool.name.trim() === "message") : params.disableMessageTool !== true;
|
|
2544
|
-
if (params.sourceReplyDeliveryMode === "message_tool_only" && messageToolAvailable) return "Visible source replies are not automatically delivered for this run. Use `message(action=send)` for user-visible source-channel output. Do not repeat that visible content in your final answer.";
|
|
2974
|
+
if (params.sourceReplyDeliveryMode === "message_tool_only" && messageToolAvailable) return "Visible source replies are not automatically delivered for this run. Use `message(action=send)` for user-visible source-channel output. For progress, set `final=false`. When the message is the completed reply to the current source conversation, set `final=true`; OpenClaw stops after confirming delivery. Do not repeat that visible content in your final answer.";
|
|
2545
2975
|
if (messageToolAvailable) return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation. Use `message` only for explicit out-of-band sends, media/file sends, or sends to a different target.";
|
|
2546
2976
|
return "For the current source conversation, reply normally in your final assistant message; OpenClaw will deliver it through the active source conversation.";
|
|
2547
2977
|
}
|
|
@@ -2587,4 +3017,4 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
|
|
|
2587
3017
|
return null;
|
|
2588
3018
|
}
|
|
2589
3019
|
//#endregion
|
|
2590
|
-
export {
|
|
3020
|
+
export { fitCodexProjectedContextForTurnStart as A, unsubscribeCodexThreadBestEffort as B, sanitizeInlineImageDataUrl$1 as C, normalizeCodexDynamicToolName as D, isSystemAgentOnlyCodexDynamicToolAllowlist as E, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS as F, shouldBuildCodexPluginThreadConfig as G, buildCodexPluginThreadConfig as H, CodexAppServerUnsafeSubscriptionError as I, ensureCodexPluginActivation as K, closeCodexStartupClientBestEffort as L, resolveCodexContextEngineProjectionMaxChars as M, resolveCodexContextEngineProjectionReserveTokens as N, resolveCodexDynamicToolsLoading as O, CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS as P, interruptCodexTurnBestEffort as R, sanitizeCodexHistoryImagePayloads as S, isForcedPrivateQaCodexRuntime as T, buildCodexPluginThreadConfigInputFingerprint as U, buildCodexPluginAppsConfigPatchFromPolicyContext as V, mergeCodexThreadConfigs as W, buildCodexNativeWebSearchThreadConfig as _, buildDeveloperInstructions as a, isCodexAppServerProfilerEnabled as b, codexDynamicToolsFingerprint as c, resolveCodexAppServerModelProvider as d, resolveCodexAppServerRequestModelSelection as f, startOrResumeThread as g, resolveReasoningEffort as h, buildContextEngineBinding as i, projectContextEngineAssemblyForCodex as j, resolveCodexDynamicToolsLoadingForRuntime as k, codexLegacyDynamicToolsFingerprint as l, resolveCodexBindingModelProviderFallback as m, areCodexDynamicToolFingerprintsCompatible as n, buildTurnCollaborationMode as o, resolveCodexAppServerThreadModelSelection as p, pluginReadParams as q, buildCodexRuntimeThreadConfig as r, buildTurnStartParams as s, CODEX_NATIVE_PERSONALITY_NONE as t, isContextEngineBindingCompatible as u, resolveCodexWebSearchPlan as v, filterCodexDynamicTools as w, invalidInlineImageText as x, resumeCodexAppServerThread as y, retireCodexAppServerClientAfterTimedOutTurn as z };
|