@openclaw/codex 2026.6.11 → 2026.7.1-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-server-policy-BPTiVNsW.js → app-server-policy-C968Kgin.js} +5 -5
- package/dist/{attempt-notifications-BYnndyFl.js → attempt-notifications-BGsEIIDI.js} +29 -40
- package/dist/{command-handlers-DEPTJYk0.js → command-handlers-Dv-pzAmg.js} +278 -237
- package/dist/command-rpc-eLM_mtC7.js +784 -0
- package/dist/compact-4mVBriKT.js +603 -0
- package/dist/{computer-use-ClrJwoFp.js → computer-use-Bmaz333N.js} +3 -3
- package/dist/{config-CszD0vP3.js → config-CYEDnLJ2.js} +44 -18
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +590 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1134 -35
- package/dist/{media-understanding-provider-ZUQHN7gT.js → media-understanding-provider-BHOu9-Gg.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-CAmfBWRl.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-C9ILt7bl.js → plugin-app-cache-key-BrhVdeEf.js} +13 -5
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-B19q5BIX.js → protocol-validators-dZQ-UTOa.js} +205 -56
- package/dist/provider-capabilities-DzI8fgEu.js +6172 -0
- package/dist/provider-catalog.js +4 -2
- package/dist/{provider-B-OHpbD3.js → provider-zjPfx5Fs.js} +94 -23
- package/dist/provider.js +2 -2
- package/dist/{request-Dcd452Nk.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-CXxtFC9d.js → run-attempt-DNSNk_G8.js} +1250 -2992
- package/dist/{sandbox-guard-3tnjhjFb.js → sandbox-guard-DA2TQfZW.js} +5 -3
- package/dist/session-binding-C1ZXdP-x.js +627 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/shared-client-4ICy3U6d.js +1853 -0
- package/dist/{side-question-C-DhgJOd.js → side-question-CMOpEhvY.js} +134 -142
- package/dist/{thread-lifecycle-DtT3-ehU.js → thread-lifecycle-qWE88Dn2.js} +803 -481
- package/dist/{web-search-provider.runtime-CkwxrXQj.js → web-search-provider.runtime-BSlriav6.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +23 -3
- package/package.json +9 -6
- package/dist/client-CLrtWgrD.js +0 -729
- package/dist/client-factory-Bm6HsGob.js +0 -18
- package/dist/compact-aQJ0ZKhe.js +0 -278
- package/dist/conversation-binding-t0XMLz-j.js +0 -1557
- package/dist/provider-capabilities-D7hnTO76.js +0 -3201
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-HOuPt-E0.js +0 -338
- package/dist/shared-client-Dtx5i7Ez.js +0 -1013
|
@@ -1,79 +1,23 @@
|
|
|
1
|
-
import { n as
|
|
2
|
-
import {
|
|
3
|
-
import { n as assertCodexThreadResumeResponse, r as assertCodexThreadStartResponse } from "./protocol-validators-
|
|
1
|
+
import { n as flattenCodexDynamicToolFunctions, r as isJsonObject } from "./protocol-2POPqAY4.js";
|
|
2
|
+
import { h as resolveCodexPluginsPolicy, i as codexSandboxPolicyForTurn, t as CODEX_PLUGINS_MARKETPLACE_NAME } from "./config-CYEDnLJ2.js";
|
|
3
|
+
import { n as assertCodexThreadResumeResponse, r as assertCodexThreadStartResponse } from "./protocol-validators-dZQ-UTOa.js";
|
|
4
4
|
import { CODEX_GPT5_HEARTBEAT_PROMPT_OVERLAY } from "./prompt-overlay.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { i as isCodexAppServerNativeAuthProfile,
|
|
5
|
+
import { a as readCodexSupportedReasoningEfforts, i as isModernCodexModel, o as resolveCodexFallbackReasoningEfforts, r as isMaxReasoningCodexModel, s as resolveCodexSupportedReasoningEffort } from "./provider-zjPfx5Fs.js";
|
|
6
|
+
import { f as isCodexAppServerConnectionClosedError, n as clearSharedCodexAppServerClientIfCurrentAndUnclaimed, s as retireSharedCodexAppServerClientIfCurrent, t as clearSharedCodexAppServerClientIfCurrent, u as CodexAppServerRpcError } from "./shared-client-4ICy3U6d.js";
|
|
7
|
+
import { a as normalizeCodexAppServerBindingModelProvider, i as isCodexAppServerNativeAuthProfile, o as reclaimCurrentCodexSessionGeneration, r as hashCodexAppServerBindingFingerprint, s as sessionBindingIdentity } from "./session-binding-C1ZXdP-x.js";
|
|
8
8
|
import "node:fs/promises";
|
|
9
9
|
import "node:path";
|
|
10
10
|
import { isFutureDateTimestampMs, resolveDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
|
|
11
|
-
import {
|
|
11
|
+
import { truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
12
12
|
import crypto from "node:crypto";
|
|
13
|
+
import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
13
14
|
import { SKILL_WORKSHOP_TOOL_NAME, buildSkillWorkshopPromptSection, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
14
15
|
import { buildCodexUserMcpServersThreadConfigPatch } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
15
16
|
import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
|
|
16
17
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
17
18
|
import { INLINE_IMAGE_DATA_URL_PREFIX, sanitizeInlineImageDataUrl } from "openclaw/plugin-sdk/inline-image-data-url-runtime";
|
|
18
19
|
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Correlates Codex app-server notifications with the active thread/turn so
|
|
22
|
-
* projectors can ignore global or stale events without losing diagnostics.
|
|
23
|
-
*/
|
|
24
|
-
/** Returns true when a notification payload belongs to the exact active thread and turn. */
|
|
25
|
-
function isCodexNotificationForTurn(value, threadId, turnId) {
|
|
26
|
-
if (!isJsonObject(value)) return false;
|
|
27
|
-
return readCodexNotificationThreadId(value) === threadId && readCodexNotificationTurnId(value) === turnId;
|
|
28
|
-
}
|
|
29
|
-
/** Reads a thread id from either top-level notification params or nested turn payloads. */
|
|
30
|
-
function readCodexNotificationThreadId(record) {
|
|
31
|
-
return readNestedTurnThreadId(record) ?? readString(record, "threadId");
|
|
32
|
-
}
|
|
33
|
-
/** Reads a turn id from either top-level notification params or nested turn payloads. */
|
|
34
|
-
function readCodexNotificationTurnId(record) {
|
|
35
|
-
return readNestedTurnId(record) ?? readString(record, "turnId");
|
|
36
|
-
}
|
|
37
|
-
/** Builds structured correlation details for logs when notification routing is ambiguous. */
|
|
38
|
-
function describeCodexNotificationCorrelation(notification, active) {
|
|
39
|
-
const params = isJsonObject(notification.params) ? notification.params : void 0;
|
|
40
|
-
const turn = params && isJsonObject(params.turn) ? params.turn : void 0;
|
|
41
|
-
const threadId = params ? readString(params, "threadId") : void 0;
|
|
42
|
-
const turnId = params ? readString(params, "turnId") : void 0;
|
|
43
|
-
const nestedTurnThreadId = turn ? readString(turn, "threadId") : void 0;
|
|
44
|
-
const nestedTurnId = turn ? readString(turn, "id") : void 0;
|
|
45
|
-
const resolvedThreadId = params ? readCodexNotificationThreadId(params) : void 0;
|
|
46
|
-
const resolvedTurnId = params ? readCodexNotificationTurnId(params) : void 0;
|
|
47
|
-
const matchesActiveThread = resolvedThreadId === active.threadId;
|
|
48
|
-
const matchesActiveTurn = active.turnId ? matchesActiveThread && resolvedTurnId === active.turnId : void 0;
|
|
49
|
-
const items = turn?.items;
|
|
50
|
-
return {
|
|
51
|
-
method: notification.method,
|
|
52
|
-
...params ? { paramsKeys: Object.keys(params).toSorted() } : {},
|
|
53
|
-
activeThreadId: active.threadId,
|
|
54
|
-
...active.turnId ? { activeTurnId: active.turnId } : {},
|
|
55
|
-
...threadId ? { threadId } : {},
|
|
56
|
-
...turnId ? { turnId } : {},
|
|
57
|
-
...nestedTurnThreadId ? { nestedTurnThreadId } : {},
|
|
58
|
-
...nestedTurnId ? { nestedTurnId } : {},
|
|
59
|
-
...turn ? { turnStatus: readString(turn, "status") } : {},
|
|
60
|
-
...Array.isArray(items) ? { turnItemCount: items.length } : {},
|
|
61
|
-
matchesActiveThread,
|
|
62
|
-
...matchesActiveTurn === void 0 ? {} : { matchesActiveTurn }
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
function readNestedTurnId(record) {
|
|
66
|
-
const turn = record.turn;
|
|
67
|
-
return isJsonObject(turn) ? readString(turn, "id") : void 0;
|
|
68
|
-
}
|
|
69
|
-
function readNestedTurnThreadId(record) {
|
|
70
|
-
const turn = record.turn;
|
|
71
|
-
return isJsonObject(turn) ? readString(turn, "threadId") : void 0;
|
|
72
|
-
}
|
|
73
|
-
function readString(record, key) {
|
|
74
|
-
const value = record[key];
|
|
75
|
-
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
76
|
-
}
|
|
20
|
+
const CODEX_TARGETED_APP_INVENTORY_LIMIT = 1e3;
|
|
77
21
|
const MAX_SERIALIZED_ERROR_MESSAGE_LENGTH = 500;
|
|
78
22
|
/** In-memory app inventory cache with coalesced refreshes per key. */
|
|
79
23
|
var CodexAppInventoryCache = class {
|
|
@@ -212,9 +156,11 @@ function serializeCodexAppInventoryError(error) {
|
|
|
212
156
|
}
|
|
213
157
|
/** Shared app inventory cache used by Codex app-server runtime paths. */
|
|
214
158
|
const defaultCodexAppInventoryCache = new CodexAppInventoryCache();
|
|
215
|
-
/** Builds a stable cache key from runtime identity fields. */
|
|
216
|
-
function buildCodexAppInventoryCacheKey(input) {
|
|
159
|
+
/** Builds a stable cache key from build versions and runtime identity fields. */
|
|
160
|
+
function buildCodexAppInventoryCacheKey(input, openClawVersion, codexPluginVersion) {
|
|
217
161
|
return JSON.stringify({
|
|
162
|
+
openClawVersion,
|
|
163
|
+
codexPluginVersion,
|
|
218
164
|
codexHome: input.codexHome ?? null,
|
|
219
165
|
endpoint: input.endpoint ?? null,
|
|
220
166
|
runtimeIdentity: normalizeRuntimeIdentityForCacheKey(input.runtimeIdentity),
|
|
@@ -235,18 +181,21 @@ function normalizeRuntimeIdentityForCacheKey(value) {
|
|
|
235
181
|
async function listAllApps(request, forceRefetch, targetAppIds = []) {
|
|
236
182
|
const apps = [];
|
|
237
183
|
const targetIds = new Set(targetAppIds.filter(Boolean));
|
|
238
|
-
const
|
|
184
|
+
const remainingTargetIds = new Set(targetIds);
|
|
185
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
239
186
|
let cursor;
|
|
240
187
|
do {
|
|
241
188
|
const response = await request("app/list", {
|
|
242
189
|
cursor,
|
|
243
|
-
limit: 100,
|
|
190
|
+
limit: targetIds.size > 0 ? CODEX_TARGETED_APP_INVENTORY_LIMIT : 100,
|
|
244
191
|
forceRefetch
|
|
245
192
|
});
|
|
246
193
|
apps.push(...response.data);
|
|
247
|
-
for (const app of response.data)
|
|
194
|
+
for (const app of response.data) remainingTargetIds.delete(app.id);
|
|
195
|
+
if (targetIds.size > 0 && remainingTargetIds.size === 0) break;
|
|
248
196
|
cursor = response.nextCursor;
|
|
249
|
-
if (
|
|
197
|
+
if (cursor && seenCursors.has(cursor)) throw new Error(`app/list returned repeated cursor ${cursor}`);
|
|
198
|
+
if (cursor) seenCursors.add(cursor);
|
|
250
199
|
} while (cursor);
|
|
251
200
|
return apps;
|
|
252
201
|
}
|
|
@@ -269,7 +218,7 @@ function redactErrorData(value, depth = 0) {
|
|
|
269
218
|
for (const [key, entry] of Object.entries(value)) redacted[key] = isSensitiveErrorDataKey(key) ? "<redacted>" : redactErrorData(entry, depth + 1) ?? null;
|
|
270
219
|
return redacted;
|
|
271
220
|
}
|
|
272
|
-
if (typeof value === "string" && value.length > 500) return `${value
|
|
221
|
+
if (typeof value === "string" && value.length > 500) return `${truncateUtf16Safe(value, 500)}...`;
|
|
273
222
|
if (typeof value === "string") return value;
|
|
274
223
|
if (typeof value === "bigint") return value.toString();
|
|
275
224
|
if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
|
|
@@ -290,6 +239,7 @@ function isSensitiveErrorDataKey(key) {
|
|
|
290
239
|
* Reads Codex plugin marketplace state and app inventory to decide which
|
|
291
240
|
* plugin-owned apps can be exposed to a native Codex thread.
|
|
292
241
|
*/
|
|
242
|
+
const CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME = `${CODEX_PLUGINS_MARKETPLACE_NAME}-remote`;
|
|
293
243
|
/** Reads configured Codex plugin state and maps owned apps to readiness diagnostics. */
|
|
294
244
|
async function readCodexPluginInventory(params) {
|
|
295
245
|
const policy = params.policy ?? resolveCodexPluginsPolicy(params.pluginConfig);
|
|
@@ -302,7 +252,8 @@ async function readCodexPluginInventory(params) {
|
|
|
302
252
|
}]
|
|
303
253
|
};
|
|
304
254
|
const appInventory = readCachedAppInventory(params);
|
|
305
|
-
const
|
|
255
|
+
const listed = await params.request("plugin/list", { cwds: [] });
|
|
256
|
+
const marketplaceEntry = listed.marketplaces.find(isOpenAiCuratedMarketplace);
|
|
306
257
|
if (!marketplaceEntry) return {
|
|
307
258
|
policy,
|
|
308
259
|
records: [],
|
|
@@ -313,7 +264,7 @@ async function readCodexPluginInventory(params) {
|
|
|
313
264
|
})),
|
|
314
265
|
...appInventory ? { appInventory } : {}
|
|
315
266
|
};
|
|
316
|
-
|
|
267
|
+
let marketplace = marketplaceRef(marketplaceEntry);
|
|
317
268
|
const diagnostics = [];
|
|
318
269
|
const records = [];
|
|
319
270
|
if (appInventory?.state === "missing") diagnostics.push({
|
|
@@ -326,8 +277,8 @@ async function readCodexPluginInventory(params) {
|
|
|
326
277
|
});
|
|
327
278
|
for (const pluginPolicy of policy.pluginPolicies) {
|
|
328
279
|
if (!pluginPolicy.enabled) continue;
|
|
329
|
-
const
|
|
330
|
-
if (!
|
|
280
|
+
const resolvedPlugin = findOpenAiCuratedMarketplacePlugin(listed, pluginPolicy.pluginName);
|
|
281
|
+
if (!resolvedPlugin) {
|
|
331
282
|
diagnostics.push({
|
|
332
283
|
code: "plugin_missing",
|
|
333
284
|
plugin: pluginPolicy,
|
|
@@ -335,7 +286,10 @@ async function readCodexPluginInventory(params) {
|
|
|
335
286
|
});
|
|
336
287
|
continue;
|
|
337
288
|
}
|
|
338
|
-
const
|
|
289
|
+
const { summary } = resolvedPlugin;
|
|
290
|
+
const pluginMarketplace = marketplaceRef(resolvedPlugin.marketplace);
|
|
291
|
+
if (records.length === 0) marketplace = pluginMarketplace;
|
|
292
|
+
const detail = await readPluginDetail(params, pluginMarketplace, pluginPolicy, summary, diagnostics);
|
|
339
293
|
const ownedAppIds = detail?.apps.map((app) => app.id).filter(Boolean).toSorted() ?? [];
|
|
340
294
|
const appOwnership = resolveAppOwnership({
|
|
341
295
|
detail,
|
|
@@ -378,12 +332,10 @@ async function readCodexPluginInventory(params) {
|
|
|
378
332
|
}
|
|
379
333
|
/** Finds one plugin summary in the OpenAI curated marketplace response. */
|
|
380
334
|
function findOpenAiCuratedPluginSummary(listed, pluginName) {
|
|
381
|
-
const
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
marketplace: marketplaceRef(marketplaceEntry),
|
|
386
|
-
summary
|
|
335
|
+
const resolved = findOpenAiCuratedMarketplacePlugin(listed, pluginName);
|
|
336
|
+
return resolved ? {
|
|
337
|
+
marketplace: marketplaceRef(resolved.marketplace),
|
|
338
|
+
summary: resolved.summary
|
|
387
339
|
} : void 0;
|
|
388
340
|
}
|
|
389
341
|
/** Builds plugin/read or plugin/install params from a marketplace reference. */
|
|
@@ -404,10 +356,10 @@ function readCachedAppInventory(params) {
|
|
|
404
356
|
suppressRefresh: params.suppressAppInventoryRefresh
|
|
405
357
|
});
|
|
406
358
|
}
|
|
407
|
-
async function readPluginDetail(params, marketplace, pluginPolicy, diagnostics) {
|
|
359
|
+
async function readPluginDetail(params, marketplace, pluginPolicy, summary, diagnostics) {
|
|
408
360
|
if (params.readPluginDetails === false) return;
|
|
409
361
|
try {
|
|
410
|
-
return (await params.request("plugin/read", pluginReadParams(marketplace, pluginPolicy.pluginName))).plugin;
|
|
362
|
+
return (await params.request("plugin/read", pluginReadParams(marketplace, marketplace.remoteMarketplaceName && summary.remotePluginId ? summary.remotePluginId : pluginPolicy.pluginName))).plugin;
|
|
411
363
|
} catch (error) {
|
|
412
364
|
diagnostics.push({
|
|
413
365
|
code: "plugin_detail_unavailable",
|
|
@@ -454,6 +406,16 @@ function resolveOwnedApps(params) {
|
|
|
454
406
|
function findPluginSummary(marketplace, pluginName) {
|
|
455
407
|
return marketplace.plugins.find((plugin) => plugin.name === pluginName || plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}` || pluginNameFromPluginId(plugin.id, marketplace.name) === pluginName);
|
|
456
408
|
}
|
|
409
|
+
function findOpenAiCuratedMarketplacePlugin(listed, pluginName) {
|
|
410
|
+
for (const marketplace of listed.marketplaces) {
|
|
411
|
+
if (!isOpenAiCuratedMarketplace(marketplace)) continue;
|
|
412
|
+
const summary = findPluginSummary(marketplace, pluginName);
|
|
413
|
+
if (summary) return {
|
|
414
|
+
marketplace,
|
|
415
|
+
summary
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
457
419
|
function pluginNameFromPluginId(pluginId, marketplaceName) {
|
|
458
420
|
const trimmed = pluginId.trim();
|
|
459
421
|
if (!trimmed) return;
|
|
@@ -467,6 +429,9 @@ function marketplaceRef(marketplace) {
|
|
|
467
429
|
...!marketplace.path ? { remoteMarketplaceName: marketplace.name } : {}
|
|
468
430
|
};
|
|
469
431
|
}
|
|
432
|
+
function isOpenAiCuratedMarketplace(marketplace) {
|
|
433
|
+
return marketplace.name === "openai-curated" || marketplace.name === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME;
|
|
434
|
+
}
|
|
470
435
|
//#endregion
|
|
471
436
|
//#region extensions/codex/src/app-server/plugin-activation.ts
|
|
472
437
|
/** Installs/enables a configured Codex plugin and refreshes plugin/app state. */
|
|
@@ -486,7 +451,7 @@ async function ensureCodexPluginActivation(params) {
|
|
|
486
451
|
marketplace: resolved.marketplace,
|
|
487
452
|
diagnostics: []
|
|
488
453
|
};
|
|
489
|
-
const installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, params.identity.pluginName));
|
|
454
|
+
const installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, resolved.marketplace.remoteMarketplaceName && resolved.summary.remotePluginId ? resolved.summary.remotePluginId : params.identity.pluginName));
|
|
490
455
|
const refreshDiagnostics = [];
|
|
491
456
|
let refreshFailed = false;
|
|
492
457
|
try {
|
|
@@ -554,20 +519,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
|
|
|
554
519
|
//#endregion
|
|
555
520
|
//#region extensions/codex/src/app-server/plugin-thread-config.ts
|
|
556
521
|
/**
|
|
557
|
-
* Builds Codex thread config patches that expose only policy-approved
|
|
558
|
-
*
|
|
522
|
+
* Builds Codex thread config patches that expose only policy-approved apps
|
|
523
|
+
* for native Codex turns.
|
|
559
524
|
*/
|
|
560
|
-
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION =
|
|
561
|
-
const CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION =
|
|
525
|
+
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION = 3;
|
|
526
|
+
const CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION = 2;
|
|
562
527
|
/** Returns true when plugin config exists and thread config may need app patches. */
|
|
563
528
|
function shouldBuildCodexPluginThreadConfig(pluginConfig) {
|
|
564
529
|
return resolveCodexPluginsPolicy(pluginConfig).configured;
|
|
565
530
|
}
|
|
566
531
|
/** Fingerprints policy and app-cache identity before runtime inventory is read. */
|
|
567
532
|
function buildCodexPluginThreadConfigInputFingerprint(params) {
|
|
533
|
+
const policy = resolveCodexPluginsPolicy(params.pluginConfig);
|
|
568
534
|
return fingerprintJson({
|
|
569
535
|
version: CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION,
|
|
570
|
-
policy: policyFingerprint(
|
|
536
|
+
policy: policyFingerprint(policy),
|
|
571
537
|
appCacheKey: params.appCacheKey ?? null
|
|
572
538
|
});
|
|
573
539
|
}
|
|
@@ -584,7 +550,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
584
550
|
inputFingerprint,
|
|
585
551
|
configPatch: buildDisabledAppsConfigPatch()
|
|
586
552
|
});
|
|
587
|
-
let inventory = await readCodexPluginInventory({
|
|
553
|
+
let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
|
|
588
554
|
pluginConfig: params.pluginConfig,
|
|
589
555
|
policy,
|
|
590
556
|
request: params.request,
|
|
@@ -592,10 +558,11 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
592
558
|
appCacheKey: params.appCacheKey,
|
|
593
559
|
nowMs: params.nowMs,
|
|
594
560
|
suppressAppInventoryRefresh: true
|
|
595
|
-
});
|
|
561
|
+
}) : emptyCodexPluginInventory(policy);
|
|
562
|
+
const appInventoryRefreshDeferredForActivation = inventory.records.some((record) => record.activationRequired) && shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
596
563
|
if (shouldWaitForInitialAppInventory(params, policy, inventory)) {
|
|
597
564
|
await refreshAppInventoryNow(params, appCache, {
|
|
598
|
-
forceRefetch:
|
|
565
|
+
forceRefetch: false,
|
|
599
566
|
reason: "initial_missing",
|
|
600
567
|
targetAppIds: collectInventoryOwnedAppIds(inventory)
|
|
601
568
|
});
|
|
@@ -630,10 +597,12 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
630
597
|
message: activation.diagnostics.map((item) => item.message).join(" ") || activation.reason
|
|
631
598
|
});
|
|
632
599
|
}
|
|
633
|
-
|
|
600
|
+
const postInstallRefreshRequired = activationResults.some((activation) => activation.ok && activation.installAttempted);
|
|
601
|
+
const deferredMissingRefreshRequired = appInventoryRefreshDeferredForActivation && !postInstallRefreshRequired && shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
602
|
+
if (postInstallRefreshRequired || deferredMissingRefreshRequired) {
|
|
634
603
|
await refreshAppInventoryNow(params, appCache, {
|
|
635
604
|
forceRefetch: true,
|
|
636
|
-
reason: "post_install",
|
|
605
|
+
reason: postInstallRefreshRequired ? "post_install" : "deferred_missing",
|
|
637
606
|
targetAppIds: collectInventoryOwnedAppIds(inventory)
|
|
638
607
|
});
|
|
639
608
|
inventory = await readCodexPluginInventory({
|
|
@@ -668,7 +637,12 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
668
637
|
appCacheKey: params.appCacheKey
|
|
669
638
|
});
|
|
670
639
|
}
|
|
671
|
-
const
|
|
640
|
+
const accountAppsResult = policy.allowAllPlugins ? await readAccessibleAccountApps(params, appCache) : { apps: [] };
|
|
641
|
+
const diagnostics = [
|
|
642
|
+
...inventory.diagnostics,
|
|
643
|
+
...activationDiagnostics,
|
|
644
|
+
...accountAppsResult.diagnostic ? [accountAppsResult.diagnostic] : []
|
|
645
|
+
];
|
|
672
646
|
const apps = { _default: {
|
|
673
647
|
enabled: false,
|
|
674
648
|
destructive_enabled: false,
|
|
@@ -676,31 +650,32 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
676
650
|
} };
|
|
677
651
|
const policyApps = {};
|
|
678
652
|
const pluginAppIds = {};
|
|
653
|
+
const pluginOwnedAppIds = new Set(inventory.records.flatMap((record) => record.appOwnership === "proven" ? record.ownedAppIds : []));
|
|
679
654
|
for (const record of inventory.records) {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
}
|
|
655
|
+
const activation = activationResults.find((item) => item.identity.configKey === record.policy.configKey);
|
|
656
|
+
if (activation?.ok === false || record.activationRequired && !activation?.ok) continue;
|
|
683
657
|
if (record.appOwnership !== "proven") continue;
|
|
684
658
|
pluginAppIds[record.policy.configKey] = [...record.ownedAppIds].toSorted();
|
|
685
659
|
for (const app of resolveThreadConfigAppsForRecord({
|
|
686
660
|
record,
|
|
687
661
|
inventory
|
|
688
662
|
})) {
|
|
689
|
-
if (!app
|
|
663
|
+
if (!isPluginAppReadyForThreadStart(app)) {
|
|
690
664
|
diagnostics.push({
|
|
691
665
|
code: "app_not_ready",
|
|
692
666
|
plugin: record.policy,
|
|
693
|
-
message: `${app.id} is not accessible
|
|
667
|
+
message: `${app.id} is not accessible for ${record.policy.pluginName}.`
|
|
694
668
|
});
|
|
695
669
|
continue;
|
|
696
670
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
671
|
+
if (record.policy.destructiveApprovalMode === "ask" && !await clearPersistedAppToolApprovalOverrides({
|
|
672
|
+
request: params.request,
|
|
673
|
+
configCwd: params.configCwd,
|
|
674
|
+
plugin: record.policy,
|
|
675
|
+
app,
|
|
676
|
+
diagnostics
|
|
677
|
+
})) continue;
|
|
678
|
+
apps[app.id] = buildEnabledAppConfig(record.policy);
|
|
704
679
|
policyApps[app.id] = {
|
|
705
680
|
configKey: record.policy.configKey,
|
|
706
681
|
marketplaceName: record.policy.marketplaceName,
|
|
@@ -711,6 +686,24 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
711
686
|
};
|
|
712
687
|
}
|
|
713
688
|
}
|
|
689
|
+
for (const app of accountAppsResult.apps) {
|
|
690
|
+
if (pluginOwnedAppIds.has(app.id)) continue;
|
|
691
|
+
const accountApp = toOwnedAccountApp(app);
|
|
692
|
+
if (policy.destructiveApprovalMode === "ask" && !await clearPersistedAppToolApprovalOverrides({
|
|
693
|
+
request: params.request,
|
|
694
|
+
configCwd: params.configCwd,
|
|
695
|
+
app: accountApp,
|
|
696
|
+
diagnostics
|
|
697
|
+
})) continue;
|
|
698
|
+
apps[app.id] = buildEnabledAppConfig(policy);
|
|
699
|
+
policyApps[app.id] = {
|
|
700
|
+
source: "account",
|
|
701
|
+
appName: app.name,
|
|
702
|
+
allowDestructiveActions: policy.allowDestructiveActions,
|
|
703
|
+
destructiveApprovalMode: policy.destructiveApprovalMode,
|
|
704
|
+
mcpServerNames: []
|
|
705
|
+
};
|
|
706
|
+
}
|
|
714
707
|
const configPatch = { apps };
|
|
715
708
|
const policyContext = buildPluginAppPolicyContext(policyApps, pluginAppIds);
|
|
716
709
|
return {
|
|
@@ -766,10 +759,35 @@ function buildDisabledAppsConfigPatch() {
|
|
|
766
759
|
open_world_enabled: false
|
|
767
760
|
} } };
|
|
768
761
|
}
|
|
762
|
+
function buildEnabledAppConfig(policy) {
|
|
763
|
+
return {
|
|
764
|
+
enabled: true,
|
|
765
|
+
destructive_enabled: policy.allowDestructiveActions,
|
|
766
|
+
open_world_enabled: true,
|
|
767
|
+
default_tools_approval_mode: "auto",
|
|
768
|
+
...policy.destructiveApprovalMode === "ask" ? { approvals_reviewer: "user" } : {}
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/** Rebuilds the safe per-thread apps patch persisted with a Codex thread binding. */
|
|
772
|
+
function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
|
|
773
|
+
const apps = { _default: {
|
|
774
|
+
enabled: false,
|
|
775
|
+
destructive_enabled: false,
|
|
776
|
+
open_world_enabled: false
|
|
777
|
+
} };
|
|
778
|
+
for (const [appId, policy] of Object.entries(policyContext.apps).toSorted(([left], [right]) => left.localeCompare(right))) apps[appId] = {
|
|
779
|
+
enabled: true,
|
|
780
|
+
destructive_enabled: policy.allowDestructiveActions,
|
|
781
|
+
open_world_enabled: true,
|
|
782
|
+
default_tools_approval_mode: "auto",
|
|
783
|
+
...policy.destructiveApprovalMode === "ask" ? { approvals_reviewer: "user" } : {}
|
|
784
|
+
};
|
|
785
|
+
return { apps };
|
|
786
|
+
}
|
|
769
787
|
function buildPluginAppPolicyContext(apps, pluginAppIds) {
|
|
770
788
|
return {
|
|
771
789
|
fingerprint: fingerprintJson({
|
|
772
|
-
version:
|
|
790
|
+
version: 2,
|
|
773
791
|
apps,
|
|
774
792
|
pluginAppIds
|
|
775
793
|
}),
|
|
@@ -777,8 +795,53 @@ function buildPluginAppPolicyContext(apps, pluginAppIds) {
|
|
|
777
795
|
pluginAppIds
|
|
778
796
|
};
|
|
779
797
|
}
|
|
798
|
+
async function clearPersistedAppToolApprovalOverrides(params) {
|
|
799
|
+
try {
|
|
800
|
+
const overrideNames = await readPersistedAppToolApprovalOverrideNames(params);
|
|
801
|
+
for (const toolName of overrideNames) if (isOverriddenConfigWriteResponse(await params.request("config/value/write", {
|
|
802
|
+
keyPath: `apps.${quoteConfigKeyPathSegment(params.app.id)}.tools.${quoteConfigKeyPathSegment(toolName)}.approval_mode`,
|
|
803
|
+
value: null,
|
|
804
|
+
mergeStrategy: "replace"
|
|
805
|
+
}))) throw new Error(`approval override for ${toolName} is controlled by another config layer`);
|
|
806
|
+
const remainingOverrideNames = await readPersistedAppToolApprovalOverrideNames(params);
|
|
807
|
+
if (remainingOverrideNames.length > 0) throw new Error(`effective approval overrides remain for ${remainingOverrideNames.join(", ")}`);
|
|
808
|
+
return true;
|
|
809
|
+
} catch (error) {
|
|
810
|
+
params.diagnostics.push({
|
|
811
|
+
code: "approval_overrides_clear_failed",
|
|
812
|
+
...params.plugin ? { plugin: params.plugin } : {},
|
|
813
|
+
message: `Could not clear durable Codex app approval overrides for ${params.app.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
814
|
+
});
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
async function readPersistedAppToolApprovalOverrideNames(params) {
|
|
819
|
+
const response = await params.request("config/read", {
|
|
820
|
+
includeLayers: false,
|
|
821
|
+
...params.configCwd ? { cwd: params.configCwd } : {}
|
|
822
|
+
});
|
|
823
|
+
const config = isJsonObject(response) ? response.config : void 0;
|
|
824
|
+
const appsRoot = isJsonObject(config) ? config.apps : void 0;
|
|
825
|
+
const nestedApps = isJsonObject(appsRoot) ? appsRoot.apps : void 0;
|
|
826
|
+
const appConfig = isJsonObject(appsRoot) ? appsRoot[params.app.id] ?? (isJsonObject(nestedApps) ? nestedApps[params.app.id] : void 0) : void 0;
|
|
827
|
+
const tools = isJsonObject(appConfig) ? appConfig.tools : void 0;
|
|
828
|
+
if (!isJsonObject(tools)) return [];
|
|
829
|
+
return Object.entries(tools).filter(([, value]) => hasPersistedToolApprovalOverride(value)).map(([toolName]) => toolName).toSorted();
|
|
830
|
+
}
|
|
831
|
+
function hasPersistedToolApprovalOverride(value) {
|
|
832
|
+
return isJsonObject(value) && (value.approval_mode !== void 0 || value.approvalMode !== void 0);
|
|
833
|
+
}
|
|
834
|
+
function isOverriddenConfigWriteResponse(response) {
|
|
835
|
+
return isJsonObject(response) && response.status === "okOverridden";
|
|
836
|
+
}
|
|
837
|
+
function quoteConfigKeyPathSegment(segment) {
|
|
838
|
+
return `"${segment.replace(/["\\]/g, (char) => `\\${char}`)}"`;
|
|
839
|
+
}
|
|
780
840
|
function shouldWaitForInitialAppInventory(params, policy, inventory) {
|
|
781
841
|
if (inventory.records.some((record) => record.activationRequired)) return false;
|
|
842
|
+
return shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
843
|
+
}
|
|
844
|
+
function shouldRefreshMissingAppInventory(params, policy, inventory) {
|
|
782
845
|
return Boolean(params.appCacheKey && policy.pluginPolicies.some((plugin) => plugin.enabled) && inventory.appInventory?.state === "missing");
|
|
783
846
|
}
|
|
784
847
|
async function refreshAppInventoryNow(params, appCache, options = {}) {
|
|
@@ -805,18 +868,53 @@ async function refreshAppInventoryNow(params, appCache, options = {}) {
|
|
|
805
868
|
function collectInventoryOwnedAppIds(inventory) {
|
|
806
869
|
return Array.from(new Set(inventory.records.flatMap((record) => record.ownedAppIds).filter(Boolean))).toSorted();
|
|
807
870
|
}
|
|
871
|
+
function emptyCodexPluginInventory(policy) {
|
|
872
|
+
return {
|
|
873
|
+
policy,
|
|
874
|
+
records: [],
|
|
875
|
+
diagnostics: []
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
async function readAccessibleAccountApps(params, appCache) {
|
|
879
|
+
const snapshot = await refreshAppInventoryNow(params, appCache, {
|
|
880
|
+
forceRefetch: false,
|
|
881
|
+
reason: "account_apps_all",
|
|
882
|
+
targetAppIds: []
|
|
883
|
+
});
|
|
884
|
+
if (!snapshot) return {
|
|
885
|
+
apps: [],
|
|
886
|
+
diagnostic: {
|
|
887
|
+
code: "account_app_inventory_unavailable",
|
|
888
|
+
message: "Codex account app inventory was unavailable; account apps were not exposed."
|
|
889
|
+
}
|
|
890
|
+
};
|
|
891
|
+
return { apps: snapshot.apps.filter((app) => app.isAccessible).toSorted((left, right) => left.id.localeCompare(right.id)) };
|
|
892
|
+
}
|
|
893
|
+
function toOwnedAccountApp(app) {
|
|
894
|
+
return {
|
|
895
|
+
id: app.id,
|
|
896
|
+
name: app.name,
|
|
897
|
+
accessible: app.isAccessible,
|
|
898
|
+
enabled: app.isEnabled,
|
|
899
|
+
needsAuth: !app.isAccessible
|
|
900
|
+
};
|
|
901
|
+
}
|
|
808
902
|
function resolveThreadConfigAppsForRecord(params) {
|
|
809
903
|
if (params.inventory.appInventory?.state === "missing") return [];
|
|
810
904
|
return params.record.apps;
|
|
811
905
|
}
|
|
906
|
+
function isPluginAppReadyForThreadStart(app) {
|
|
907
|
+
return app.accessible;
|
|
908
|
+
}
|
|
812
909
|
function shouldForceRefreshForNotReadyPluginApps(params, policy, inventory) {
|
|
813
910
|
if (!params.appCacheKey || !policy.pluginPolicies.some((plugin) => plugin.enabled)) return false;
|
|
814
911
|
if (inventory.appInventory?.state === "missing") return false;
|
|
815
|
-
return inventory.records.some((record) => record.appOwnership === "proven" && record.ownedAppIds.length > 0 && (record.apps.length === 0 || record.apps.some((app) => !app.accessible
|
|
912
|
+
return inventory.records.some((record) => record.appOwnership === "proven" && record.ownedAppIds.length > 0 && (record.apps.length === 0 || record.apps.some((app) => !app.accessible)));
|
|
816
913
|
}
|
|
817
914
|
function policyFingerprint(policy) {
|
|
818
915
|
return {
|
|
819
916
|
enabled: policy.enabled,
|
|
917
|
+
allowAllPlugins: policy.allowAllPlugins,
|
|
820
918
|
allowDestructiveActions: policy.allowDestructiveActions,
|
|
821
919
|
destructiveApprovalMode: policy.destructiveApprovalMode,
|
|
822
920
|
plugins: policy.pluginPolicies.map((plugin) => ({
|
|
@@ -849,6 +947,127 @@ function stableStringify(value) {
|
|
|
849
947
|
return JSON.stringify(value);
|
|
850
948
|
}
|
|
851
949
|
//#endregion
|
|
950
|
+
//#region extensions/codex/src/app-server/attempt-client-cleanup.ts
|
|
951
|
+
/**
|
|
952
|
+
* Best-effort cleanup helpers for Codex app-server startup attempts and turns.
|
|
953
|
+
*/
|
|
954
|
+
/** Timeout for best-effort app-server turn interruption during cleanup. */
|
|
955
|
+
const CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS = 5e3;
|
|
956
|
+
/** Timeout for best-effort thread unsubscribe during cleanup. */
|
|
957
|
+
const CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS = 5e3;
|
|
958
|
+
/** Raised when a thread subscription may be live on a client OpenClaw no longer controls. */
|
|
959
|
+
var CodexAppServerUnsafeSubscriptionError = class extends Error {
|
|
960
|
+
constructor(message, options) {
|
|
961
|
+
super(message, options);
|
|
962
|
+
this.name = "CodexAppServerUnsafeSubscriptionError";
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
function isCodexAppServerUnsafeSubscriptionError(error) {
|
|
966
|
+
return error instanceof CodexAppServerUnsafeSubscriptionError;
|
|
967
|
+
}
|
|
968
|
+
/** Asserts Codex resumed the exact thread this attempt subscribed to. */
|
|
969
|
+
function assertCodexThreadResumeSubscription(requestedThreadId, returnedThreadId) {
|
|
970
|
+
if (returnedThreadId !== requestedThreadId) throw new CodexAppServerUnsafeSubscriptionError(`Codex thread/resume returned ${returnedThreadId} for ${requestedThreadId}`);
|
|
971
|
+
}
|
|
972
|
+
async function closeClientAndWaitIfAvailable(client) {
|
|
973
|
+
const closeable = client;
|
|
974
|
+
if (typeof closeable.closeAndWait === "function") {
|
|
975
|
+
await closeable.closeAndWait();
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
closeable.close?.();
|
|
979
|
+
}
|
|
980
|
+
async function closeCodexStartupClientBestEffort(client) {
|
|
981
|
+
if (!client) return;
|
|
982
|
+
const unclaimedSharedClient = clearSharedCodexAppServerClientIfCurrentAndUnclaimed(client);
|
|
983
|
+
if (unclaimedSharedClient.closed) {
|
|
984
|
+
await closeClientAndWaitIfAvailable(client);
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
if (unclaimedSharedClient.found) {
|
|
988
|
+
if (retireSharedCodexAppServerClientIfCurrent(client)?.closed) await closeClientAndWaitIfAvailable(client);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
992
|
+
if (retiredSharedClient) {
|
|
993
|
+
if (retiredSharedClient.closed) await closeClientAndWaitIfAvailable(client);
|
|
994
|
+
return;
|
|
995
|
+
}
|
|
996
|
+
if (clearSharedCodexAppServerClientIfCurrent(client)) {
|
|
997
|
+
await closeClientAndWaitIfAvailable(client);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
await closeClientAndWaitIfAvailable(client);
|
|
1001
|
+
}
|
|
1002
|
+
/** Sends a turn interrupt without blocking abort cleanup on app-server errors. */
|
|
1003
|
+
function interruptCodexTurnBestEffort(client, params) {
|
|
1004
|
+
const requestOptions = params.timeoutMs && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? { timeoutMs: params.timeoutMs } : void 0;
|
|
1005
|
+
const requestParams = {
|
|
1006
|
+
threadId: params.threadId,
|
|
1007
|
+
turnId: params.turnId
|
|
1008
|
+
};
|
|
1009
|
+
try {
|
|
1010
|
+
const interrupt = requestOptions ? client.request("turn/interrupt", requestParams, requestOptions) : client.request("turn/interrupt", requestParams);
|
|
1011
|
+
Promise.resolve(interrupt).catch((error) => {
|
|
1012
|
+
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
1013
|
+
});
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
embeddedAgentLog.debug("codex app-server turn interrupt failed during abort", { error });
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
/** Unsubscribes from a thread while swallowing cleanup-only failures. */
|
|
1019
|
+
async function unsubscribeCodexThreadBestEffort(client, params) {
|
|
1020
|
+
try {
|
|
1021
|
+
await client.request("thread/unsubscribe", { threadId: params.threadId }, { timeoutMs: params.timeoutMs });
|
|
1022
|
+
return true;
|
|
1023
|
+
} catch (error) {
|
|
1024
|
+
embeddedAgentLog.debug("codex app-server thread unsubscribe cleanup failed", {
|
|
1025
|
+
threadId: params.threadId,
|
|
1026
|
+
error
|
|
1027
|
+
});
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Retires the shared client after a timed-out turn so later runs do not reuse a
|
|
1033
|
+
* potentially wedged app-server connection.
|
|
1034
|
+
*/
|
|
1035
|
+
async function retireCodexAppServerClientAfterTimedOutTurn(client, params) {
|
|
1036
|
+
const retiredSharedClient = retireSharedCodexAppServerClientIfCurrent(client);
|
|
1037
|
+
const detachedSharedClient = Boolean(retiredSharedClient);
|
|
1038
|
+
interruptCodexTurnBestEffort(client, {
|
|
1039
|
+
threadId: params.threadId,
|
|
1040
|
+
turnId: params.turnId,
|
|
1041
|
+
timeoutMs: CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS
|
|
1042
|
+
});
|
|
1043
|
+
await unsubscribeCodexThreadBestEffort(client, {
|
|
1044
|
+
threadId: params.threadId,
|
|
1045
|
+
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
1046
|
+
});
|
|
1047
|
+
let closedClient = retiredSharedClient?.closed ?? false;
|
|
1048
|
+
if (!detachedSharedClient) {
|
|
1049
|
+
const close = client.close;
|
|
1050
|
+
if (typeof close === "function") try {
|
|
1051
|
+
close.call(client);
|
|
1052
|
+
closedClient = true;
|
|
1053
|
+
} catch (error) {
|
|
1054
|
+
embeddedAgentLog.debug("codex app-server client close failed during timeout cleanup", {
|
|
1055
|
+
threadId: params.threadId,
|
|
1056
|
+
turnId: params.turnId,
|
|
1057
|
+
error
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
embeddedAgentLog.warn("codex app-server client retired after timed-out turn", {
|
|
1062
|
+
threadId: params.threadId,
|
|
1063
|
+
turnId: params.turnId,
|
|
1064
|
+
reason: params.reason,
|
|
1065
|
+
detachedSharedClient,
|
|
1066
|
+
closedClient,
|
|
1067
|
+
activeSharedClientLeases: retiredSharedClient?.activeLeases ?? 0
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
//#endregion
|
|
852
1071
|
//#region extensions/codex/src/app-server/context-engine-projection.ts
|
|
853
1072
|
const CONTEXT_HEADER = "OpenClaw assembled context for this turn:";
|
|
854
1073
|
const CONTEXT_OPEN = "<conversation_context>";
|
|
@@ -1015,7 +1234,7 @@ function renderToolResultPayload(record) {
|
|
|
1015
1234
|
}
|
|
1016
1235
|
return payload;
|
|
1017
1236
|
}
|
|
1018
|
-
const TOOL_PAYLOAD_METADATA_KEYS = new Set([
|
|
1237
|
+
const TOOL_PAYLOAD_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
1019
1238
|
"type",
|
|
1020
1239
|
"name",
|
|
1021
1240
|
"id",
|
|
@@ -1249,6 +1468,31 @@ function isCodexAppServerProfilerEnabled(config, env = process.env) {
|
|
|
1249
1468
|
return PROFILER_FLAGS.some((flag) => isDiagnosticFlagEnabled(flag, config, env));
|
|
1250
1469
|
}
|
|
1251
1470
|
//#endregion
|
|
1471
|
+
//#region extensions/codex/src/app-server/thread-resume.ts
|
|
1472
|
+
/** Owns Codex thread/resume subscription safety. */
|
|
1473
|
+
/** Resumes one thread and retires the physical client when acceptance is indeterminate. */
|
|
1474
|
+
async function resumeCodexAppServerThread(params) {
|
|
1475
|
+
const threadId = params.request.threadId;
|
|
1476
|
+
let response;
|
|
1477
|
+
try {
|
|
1478
|
+
response = assertCodexThreadResumeResponse(await params.client.request("thread/resume", params.request, {
|
|
1479
|
+
...params.timeoutMs !== void 0 ? { timeoutMs: params.timeoutMs } : {},
|
|
1480
|
+
...params.signal ? { signal: params.signal } : {}
|
|
1481
|
+
}));
|
|
1482
|
+
assertCodexThreadResumeSubscription(threadId, response.thread.id);
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
if (error instanceof CodexAppServerRpcError) throw error;
|
|
1485
|
+
try {
|
|
1486
|
+
await params.abandonClient();
|
|
1487
|
+
} catch (abandonError) {
|
|
1488
|
+
throw new CodexAppServerUnsafeSubscriptionError(`Codex thread/resume client could not be retired for ${threadId}`, { cause: abandonError });
|
|
1489
|
+
}
|
|
1490
|
+
if (error instanceof CodexAppServerUnsafeSubscriptionError) throw error;
|
|
1491
|
+
throw new CodexAppServerUnsafeSubscriptionError(error instanceof Error ? error.message : `Codex thread/resume outcome is indeterminate for ${threadId}`, { cause: error });
|
|
1492
|
+
}
|
|
1493
|
+
return response;
|
|
1494
|
+
}
|
|
1495
|
+
//#endregion
|
|
1252
1496
|
//#region extensions/codex/src/app-server/web-search.ts
|
|
1253
1497
|
const CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG = {
|
|
1254
1498
|
"features.standalone_web_search": false,
|
|
@@ -1323,6 +1567,12 @@ var CodexThreadStartRequestError = class extends Error {
|
|
|
1323
1567
|
this.name = "CodexThreadStartRequestError";
|
|
1324
1568
|
}
|
|
1325
1569
|
};
|
|
1570
|
+
var CodexThreadBindingConflictError = class extends Error {
|
|
1571
|
+
constructor(threadId, operation) {
|
|
1572
|
+
super(`Codex thread binding changed while ${operation}: ${threadId}`);
|
|
1573
|
+
this.name = "CodexThreadBindingConflictError";
|
|
1574
|
+
}
|
|
1575
|
+
};
|
|
1326
1576
|
const CODEX_NATIVE_PERSONALITY_NONE = "none";
|
|
1327
1577
|
const CODEX_CODE_MODE_THREAD_CONFIG = {
|
|
1328
1578
|
"features.code_mode": true,
|
|
@@ -1423,227 +1673,383 @@ function createCodexThreadLifecycleTimingTracker(options = {}) {
|
|
|
1423
1673
|
};
|
|
1424
1674
|
}
|
|
1425
1675
|
async function startOrResumeThread(params) {
|
|
1426
|
-
const
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => fingerprintDynamicTools(params.dynamicTools));
|
|
1431
|
-
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
1432
|
-
const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
1433
|
-
config: params.params.config,
|
|
1434
|
-
disableTools: params.params.disableTools,
|
|
1435
|
-
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
1436
|
-
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1437
|
-
webSearchAllowed: params.webSearchAllowed
|
|
1438
|
-
})).threadConfig);
|
|
1439
|
-
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
1440
|
-
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
1441
|
-
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : buildCodexUserMcpServersThreadConfigPatch(params.params.config, { agentId: params.agentId ?? params.params.agentId });
|
|
1442
|
-
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1443
|
-
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
1444
|
-
let binding = await lifecycleTiming.measure("read-binding", () => readCodexAppServerBinding(params.params.sessionFile, {
|
|
1445
|
-
authProfileStore: params.params.authProfileStore,
|
|
1446
|
-
agentDir: params.params.agentDir,
|
|
1447
|
-
config: params.params.config
|
|
1448
|
-
}));
|
|
1449
|
-
if (binding?.threadId && shouldRotateCodexAppServerBindingForRuntime({
|
|
1450
|
-
connectionClass: params.appServer.connectionClass,
|
|
1451
|
-
current: params.appServerRuntimeFingerprint,
|
|
1452
|
-
binding: binding.appServerRuntimeFingerprint
|
|
1453
|
-
})) {
|
|
1454
|
-
embeddedAgentLog.debug("codex app-server runtime identity changed; starting a new thread", {
|
|
1455
|
-
threadId: binding.threadId,
|
|
1456
|
-
connectionClass: params.appServer.connectionClass
|
|
1457
|
-
});
|
|
1458
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1459
|
-
binding = void 0;
|
|
1460
|
-
}
|
|
1461
|
-
const startModelSelection = resolveCodexAppServerThreadModelSelection({
|
|
1462
|
-
provider: params.params.provider,
|
|
1463
|
-
model: params.params.modelId,
|
|
1464
|
-
binding,
|
|
1465
|
-
authProfileId: params.params.authProfileId,
|
|
1466
|
-
authProfileStore: params.params.authProfileStore,
|
|
1467
|
-
agentDir: params.params.agentDir,
|
|
1676
|
+
const bindingIdentity = sessionBindingIdentity({
|
|
1677
|
+
sessionId: params.params.sessionId,
|
|
1678
|
+
sessionKey: params.params.sessionKey,
|
|
1679
|
+
agentId: params.agentId ?? params.params.agentId,
|
|
1468
1680
|
config: params.params.config
|
|
1469
1681
|
});
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
const
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1682
|
+
return await params.bindingStore.withLease(bindingIdentity, async () => {
|
|
1683
|
+
const lifecycleTiming = createCodexThreadLifecycleTimingTracker({
|
|
1684
|
+
...params.timing,
|
|
1685
|
+
enabled: params.timing?.enabled ?? isCodexAppServerProfilerEnabled(params.params.config)
|
|
1686
|
+
});
|
|
1687
|
+
const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync("legacy-dynamic-tools-fingerprint", () => legacyFingerprintDynamicTools(params.dynamicTools));
|
|
1688
|
+
const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => hashCodexAppServerBindingFingerprint(legacyDynamicToolsFingerprint));
|
|
1689
|
+
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
1690
|
+
const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
1691
|
+
config: params.params.config,
|
|
1692
|
+
disableTools: params.params.disableTools,
|
|
1693
|
+
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
1694
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1695
|
+
webSearchAllowed: params.webSearchAllowed
|
|
1696
|
+
})).threadConfig);
|
|
1697
|
+
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
1698
|
+
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
1699
|
+
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : buildCodexUserMcpServersThreadConfigPatch(params.params.config, { agentId: params.agentId ?? params.params.agentId });
|
|
1700
|
+
const legacyUserMcpServersFingerprint = legacyFingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1701
|
+
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1702
|
+
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
1703
|
+
let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
|
|
1704
|
+
const normalizeBindingModelProvider = (authProfileId, modelProvider) => normalizeCodexAppServerBindingModelProvider({
|
|
1705
|
+
authProfileId,
|
|
1706
|
+
modelProvider,
|
|
1707
|
+
authProfileStore: params.params.authProfileStore,
|
|
1708
|
+
agentDir: params.params.agentDir,
|
|
1709
|
+
config: params.params.config
|
|
1710
|
+
});
|
|
1711
|
+
if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
|
|
1712
|
+
if (!await lifecycleTiming.measure("reclaim-binding-generation", () => reclaimCurrentCodexSessionGeneration({
|
|
1713
|
+
bindingStore: params.bindingStore,
|
|
1714
|
+
identity: bindingIdentity,
|
|
1715
|
+
config: params.params.config
|
|
1716
|
+
}))) throw new Error(`Codex session generation is no longer current: ${bindingIdentity.sessionId}`);
|
|
1495
1717
|
}
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1718
|
+
const clearCurrentBinding = async (operation) => {
|
|
1719
|
+
const current = binding;
|
|
1720
|
+
if (!current?.threadId) return;
|
|
1721
|
+
if (!await params.bindingStore.mutate(bindingIdentity, {
|
|
1722
|
+
kind: "clear",
|
|
1723
|
+
threadId: current.threadId
|
|
1724
|
+
})) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
1725
|
+
binding = void 0;
|
|
1726
|
+
};
|
|
1727
|
+
if (binding?.threadId && shouldRotateCodexAppServerBindingForRuntime({
|
|
1728
|
+
connectionClass: params.appServer.connectionClass,
|
|
1729
|
+
current: params.appServerRuntimeFingerprint,
|
|
1730
|
+
binding: binding.appServerRuntimeFingerprint
|
|
1731
|
+
})) {
|
|
1732
|
+
embeddedAgentLog.debug("codex app-server runtime identity changed; starting a new thread", {
|
|
1733
|
+
threadId: binding.threadId,
|
|
1734
|
+
connectionClass: params.appServer.connectionClass
|
|
1735
|
+
});
|
|
1736
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1737
|
+
binding = void 0;
|
|
1506
1738
|
}
|
|
1507
|
-
binding
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
binding = void 0;
|
|
1513
|
-
}
|
|
1514
|
-
if (binding?.threadId && (binding.contextEngine || contextEngineBinding)) {
|
|
1515
|
-
if (!contextEngineBinding || !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding)) {
|
|
1516
|
-
embeddedAgentLog.debug("codex app-server context-engine binding changed; starting a new thread", {
|
|
1739
|
+
if (binding?.threadId && shouldRotateCodexGpt56MultiAgentBinding({
|
|
1740
|
+
bindingModel: binding.model,
|
|
1741
|
+
requestedModel: params.params.modelId
|
|
1742
|
+
})) {
|
|
1743
|
+
embeddedAgentLog.debug("codex app-server GPT-5.6 multi-agent version changed; starting a new thread", {
|
|
1517
1744
|
threadId: binding.threadId,
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
epoch: contextEngineBinding?.projection?.epoch,
|
|
1521
|
-
previousEpoch: binding.contextEngine?.projection?.epoch,
|
|
1522
|
-
fingerprint: contextEngineBinding?.projection?.fingerprint,
|
|
1523
|
-
previousFingerprint: binding.contextEngine?.projection?.fingerprint,
|
|
1524
|
-
policyFingerprint: contextEngineBinding?.policyFingerprint,
|
|
1525
|
-
previousPolicyFingerprint: binding.contextEngine?.policyFingerprint
|
|
1745
|
+
bindingModel: binding.model,
|
|
1746
|
+
requestedModel: params.params.modelId
|
|
1526
1747
|
});
|
|
1527
|
-
await
|
|
1748
|
+
await clearCurrentBinding("rotating a GPT-5.6 multi-agent thread binding");
|
|
1528
1749
|
binding = void 0;
|
|
1529
|
-
rotatedContextEngineBinding = true;
|
|
1530
1750
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1535
|
-
binding = void 0;
|
|
1536
|
-
}
|
|
1537
|
-
if (binding?.threadId && binding.environmentSelectionFingerprint !== environmentSelectionFingerprint) {
|
|
1538
|
-
embeddedAgentLog.debug("codex app-server environment selection changed; starting a new thread", { threadId: binding.threadId });
|
|
1539
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1540
|
-
binding = void 0;
|
|
1541
|
-
}
|
|
1542
|
-
if (binding?.threadId && (binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint || binding.networkProxyProfileName !== params.appServer.networkProxy?.profileName)) {
|
|
1543
|
-
embeddedAgentLog.debug("codex app-server network proxy config changed; starting a new thread", { threadId: binding.threadId });
|
|
1544
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1545
|
-
binding = void 0;
|
|
1546
|
-
}
|
|
1547
|
-
if (binding?.threadId) {
|
|
1548
|
-
let pluginBindingStale = isCodexPluginThreadBindingStale({
|
|
1549
|
-
codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
|
|
1550
|
-
bindingFingerprint: binding.pluginAppsFingerprint,
|
|
1551
|
-
bindingInputFingerprint: binding.pluginAppsInputFingerprint,
|
|
1552
|
-
currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
|
|
1553
|
-
hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
|
|
1554
|
-
});
|
|
1555
|
-
if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
|
|
1751
|
+
const startModelSelection = resolveCodexAppServerThreadModelSelection({
|
|
1752
|
+
provider: params.params.provider,
|
|
1753
|
+
model: params.params.modelId,
|
|
1556
1754
|
binding,
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1755
|
+
authProfileId: params.params.authProfileId,
|
|
1756
|
+
authProfileStore: params.params.authProfileStore,
|
|
1757
|
+
agentDir: params.params.agentDir,
|
|
1758
|
+
config: params.params.config
|
|
1759
|
+
});
|
|
1760
|
+
const startModelProvider = startModelSelection.modelProvider;
|
|
1761
|
+
let preserveExistingBinding = params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
|
|
1762
|
+
let rotatedContextEngineBinding = false;
|
|
1763
|
+
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
|
+
const webSearchBindingChanged = binding?.threadId && binding.webSearchThreadConfigFingerprint !== webSearchThreadConfigFingerprint;
|
|
1773
|
+
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
1774
|
+
const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
|
|
1775
|
+
const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
|
|
1776
|
+
const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
|
|
1777
|
+
const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
|
|
1778
|
+
if (binding?.threadId && params.mcpServersFingerprintEvaluated === true && binding.mcpServersFingerprint !== params.mcpServersFingerprint) {
|
|
1779
|
+
if (transientNativeToolRestriction || webSearchBindingChanged && (explicitTransientWebSearchRestriction || unknownProviderWebSearchSupport)) {
|
|
1780
|
+
embeddedAgentLog.debug("codex app-server MCP config changed during transient restricted turn; starting transient thread", { threadId: binding.threadId });
|
|
1781
|
+
preserveExistingBinding = true;
|
|
1782
|
+
} else {
|
|
1783
|
+
embeddedAgentLog.debug("codex app-server MCP config changed; starting a new thread", { threadId: binding.threadId });
|
|
1784
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1785
|
+
}
|
|
1786
|
+
binding = void 0;
|
|
1566
1787
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1788
|
+
const deferLegacyWebSearchRotationToTransientNativeSurface = params.nativeCodeModeEnabled === false && binding?.webSearchThreadConfigFingerprint === void 0 && !persistentWebSearchRestriction;
|
|
1789
|
+
if (binding?.threadId && webSearchBindingChanged && !deferLegacyWebSearchRotationToTransientNativeSurface) {
|
|
1790
|
+
if (transientWebSearchRestriction) {
|
|
1791
|
+
embeddedAgentLog.debug("codex app-server web search restricted for turn; starting transient thread", { threadId: binding.threadId });
|
|
1792
|
+
preserveExistingBinding = true;
|
|
1793
|
+
} else {
|
|
1794
|
+
embeddedAgentLog.debug("codex app-server web search config changed; starting a new thread", { threadId: binding.threadId });
|
|
1795
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1796
|
+
}
|
|
1570
1797
|
binding = void 0;
|
|
1571
1798
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
embeddedAgentLog.debug("codex app-server dynamic tool loading changed; starting a new thread", { threadId: binding.threadId });
|
|
1576
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1799
|
+
if (binding?.threadId && transientNativeToolRestriction) {
|
|
1800
|
+
embeddedAgentLog.debug("codex app-server native tool surface disabled for turn; starting transient thread", { threadId: binding.threadId });
|
|
1801
|
+
preserveExistingBinding = true;
|
|
1577
1802
|
binding = void 0;
|
|
1578
1803
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1804
|
+
if (binding?.threadId && (binding.contextEngine || contextEngineBinding)) {
|
|
1805
|
+
if (!contextEngineBinding || !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding)) {
|
|
1806
|
+
embeddedAgentLog.debug("codex app-server context-engine binding changed; starting a new thread", {
|
|
1807
|
+
threadId: binding.threadId,
|
|
1808
|
+
engineId: contextEngineBinding?.engineId,
|
|
1809
|
+
previousEngineId: binding.contextEngine?.engineId,
|
|
1810
|
+
epoch: contextEngineBinding?.projection?.epoch,
|
|
1811
|
+
previousEpoch: binding.contextEngine?.projection?.epoch,
|
|
1812
|
+
fingerprint: contextEngineBinding?.projection?.fingerprint,
|
|
1813
|
+
previousFingerprint: binding.contextEngine?.projection?.fingerprint,
|
|
1814
|
+
policyFingerprint: contextEngineBinding?.policyFingerprint,
|
|
1815
|
+
previousPolicyFingerprint: binding.contextEngine?.policyFingerprint
|
|
1816
|
+
});
|
|
1817
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1818
|
+
binding = void 0;
|
|
1819
|
+
rotatedContextEngineBinding = true;
|
|
1820
|
+
}
|
|
1589
1821
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
};
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
})
|
|
1822
|
+
if (binding?.threadId && !areUserMcpServersFingerprintsCompatible({
|
|
1823
|
+
previous: binding.userMcpServersFingerprint,
|
|
1824
|
+
next: userMcpServersFingerprint,
|
|
1825
|
+
nextLegacy: legacyUserMcpServersFingerprint
|
|
1826
|
+
})) {
|
|
1827
|
+
embeddedAgentLog.debug("codex app-server user MCP config changed; starting a new thread", { threadId: binding.threadId });
|
|
1828
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1829
|
+
binding = void 0;
|
|
1830
|
+
}
|
|
1831
|
+
if (binding?.threadId && binding.environmentSelectionFingerprint !== environmentSelectionFingerprint) {
|
|
1832
|
+
embeddedAgentLog.debug("codex app-server environment selection changed; starting a new thread", { threadId: binding.threadId });
|
|
1833
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1834
|
+
binding = void 0;
|
|
1835
|
+
}
|
|
1836
|
+
if (binding?.threadId && (binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint || binding.networkProxyProfileName !== params.appServer.networkProxy?.profileName)) {
|
|
1837
|
+
embeddedAgentLog.debug("codex app-server network proxy config changed; starting a new thread", { threadId: binding.threadId });
|
|
1838
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1839
|
+
binding = void 0;
|
|
1840
|
+
}
|
|
1841
|
+
if (binding?.threadId) {
|
|
1842
|
+
let pluginBindingStale = isCodexPluginThreadBindingStale({
|
|
1843
|
+
codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
|
|
1844
|
+
bindingFingerprint: binding.pluginAppsFingerprint,
|
|
1845
|
+
bindingInputFingerprint: binding.pluginAppsInputFingerprint,
|
|
1846
|
+
currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
|
|
1847
|
+
hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
|
|
1848
|
+
});
|
|
1849
|
+
if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
|
|
1850
|
+
binding,
|
|
1851
|
+
pluginThreadConfig: params.pluginThreadConfig
|
|
1852
|
+
})) try {
|
|
1853
|
+
prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build());
|
|
1854
|
+
pluginBindingStale = prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
|
|
1857
|
+
error,
|
|
1858
|
+
threadId: binding.threadId
|
|
1859
|
+
});
|
|
1860
|
+
}
|
|
1861
|
+
if (pluginBindingStale) {
|
|
1862
|
+
embeddedAgentLog.debug("codex app-server plugin app config changed; starting a new thread", { threadId: binding.threadId });
|
|
1863
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1864
|
+
binding = void 0;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
if (binding?.threadId) {
|
|
1868
|
+
if (binding.dynamicToolsFingerprint && params.dynamicTools.length > 0 && binding.dynamicToolsContainDeferred !== dynamicToolsContainDeferred && (binding.dynamicToolsContainDeferred !== void 0 || !dynamicToolsContainDeferred)) {
|
|
1869
|
+
embeddedAgentLog.debug("codex app-server dynamic tool loading changed; starting a new thread", { threadId: binding.threadId });
|
|
1870
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1871
|
+
binding = void 0;
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
if (binding?.threadId) if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint, legacyDynamicToolsFingerprint)) {
|
|
1875
|
+
preserveExistingBinding = shouldStartTransientNoToolThread({
|
|
1876
|
+
previous: binding.dynamicToolsFingerprint,
|
|
1877
|
+
nextHasDynamicTools: params.dynamicTools.length > 0
|
|
1878
|
+
});
|
|
1879
|
+
if (preserveExistingBinding) embeddedAgentLog.debug("codex app-server dynamic tools unavailable for turn; starting transient thread", { threadId: binding.threadId });
|
|
1880
|
+
else {
|
|
1881
|
+
embeddedAgentLog.debug("codex app-server dynamic tool catalog changed; starting a new thread", { threadId: binding.threadId });
|
|
1882
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1883
|
+
}
|
|
1884
|
+
} else {
|
|
1885
|
+
const resumeBinding = binding;
|
|
1886
|
+
let resumeReservation;
|
|
1887
|
+
try {
|
|
1888
|
+
const authProfileId = params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
1889
|
+
const finalConfigPatch = params.buildFinalConfigPatch?.({
|
|
1890
|
+
action: "resume",
|
|
1891
|
+
binding: resumeBinding
|
|
1892
|
+
}) ?? {
|
|
1893
|
+
configPatch: params.finalConfigPatch,
|
|
1894
|
+
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
1895
|
+
};
|
|
1896
|
+
const pluginAppsConfigPatch = params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0;
|
|
1897
|
+
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch);
|
|
1898
|
+
const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
1899
|
+
threadId: resumeBinding.threadId,
|
|
1900
|
+
authProfileId,
|
|
1901
|
+
model: startModelSelection.model,
|
|
1902
|
+
modelProvider: startModelProvider,
|
|
1903
|
+
appServer: params.appServer,
|
|
1904
|
+
dynamicTools: params.dynamicTools,
|
|
1905
|
+
developerInstructions: params.developerInstructions,
|
|
1906
|
+
config: resumeConfig,
|
|
1907
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1908
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1909
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1910
|
+
webSearchAllowed: params.webSearchAllowed
|
|
1911
|
+
}));
|
|
1912
|
+
const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
|
|
1913
|
+
throwIfAborted();
|
|
1914
|
+
resumeReservation = params.reserveResumeThread?.(resumeBinding.threadId);
|
|
1915
|
+
const response = await lifecycleTiming.measure("thread-resume-request", () => resumeCodexAppServerThread({
|
|
1916
|
+
client: params.client,
|
|
1917
|
+
abandonClient: params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)),
|
|
1918
|
+
request: resumeParams,
|
|
1919
|
+
signal: params.signal
|
|
1920
|
+
}));
|
|
1921
|
+
throwIfAborted();
|
|
1922
|
+
const boundAuthProfileId = authProfileId;
|
|
1923
|
+
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
|
|
1924
|
+
const resumePatch = {
|
|
1925
|
+
cwd: params.cwd,
|
|
1926
|
+
authProfileId: boundAuthProfileId,
|
|
1927
|
+
model: response.model ?? resumeParams.model ?? params.params.modelId,
|
|
1928
|
+
modelProvider: normalizeBindingModelProvider(boundAuthProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider),
|
|
1929
|
+
dynamicToolsFingerprint,
|
|
1930
|
+
dynamicToolsContainDeferred,
|
|
1931
|
+
webSearchThreadConfigFingerprint,
|
|
1932
|
+
userMcpServersFingerprint,
|
|
1933
|
+
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1934
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1935
|
+
networkProxyConfigFingerprint,
|
|
1936
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
|
|
1937
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1938
|
+
pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
|
|
1939
|
+
pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
|
|
1940
|
+
pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
|
|
1941
|
+
contextEngine: contextEngineBinding,
|
|
1942
|
+
environmentSelectionFingerprint
|
|
1943
|
+
};
|
|
1944
|
+
if (!await lifecycleTiming.measure("thread-resume-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
|
|
1945
|
+
kind: "patch",
|
|
1946
|
+
threadId: resumeBinding.threadId,
|
|
1947
|
+
patch: resumePatch
|
|
1948
|
+
}))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
|
|
1949
|
+
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
1950
|
+
sessionId: params.params.sessionId,
|
|
1951
|
+
sessionKey: params.params.sessionKey,
|
|
1952
|
+
threadId: response.thread.id,
|
|
1953
|
+
engineId: contextEngineBinding.engineId,
|
|
1954
|
+
epoch: contextEngineBinding.projection?.epoch,
|
|
1955
|
+
fingerprint: contextEngineBinding.projection?.fingerprint,
|
|
1956
|
+
action: "resumed"
|
|
1957
|
+
});
|
|
1958
|
+
lifecycleTiming.mark("thread-ready");
|
|
1959
|
+
lifecycleTiming.logSummary({
|
|
1960
|
+
runId: params.params.runId,
|
|
1961
|
+
sessionId: params.params.sessionId,
|
|
1962
|
+
sessionKey: params.params.sessionKey,
|
|
1963
|
+
threadId: response.thread.id,
|
|
1964
|
+
action: "resumed"
|
|
1965
|
+
});
|
|
1966
|
+
const activeTurnIds = readActiveCodexTurnIds(response.thread);
|
|
1967
|
+
return {
|
|
1968
|
+
...resumeBinding,
|
|
1969
|
+
threadId: response.thread.id,
|
|
1970
|
+
...resumePatch,
|
|
1971
|
+
lifecycle: {
|
|
1972
|
+
action: "resumed",
|
|
1973
|
+
...activeTurnIds.length ? { activeTurnIds } : {}
|
|
1974
|
+
}
|
|
1975
|
+
};
|
|
1976
|
+
} catch (error) {
|
|
1977
|
+
resumeReservation?.release();
|
|
1978
|
+
if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
|
|
1979
|
+
const resumeRejected = error instanceof CodexAppServerRpcError;
|
|
1980
|
+
if (!await unsubscribeCodexThreadBestEffort(params.client, {
|
|
1981
|
+
threadId: resumeBinding.threadId,
|
|
1982
|
+
timeoutMs: 5e3
|
|
1983
|
+
}) && !resumeRejected && !isCodexAppServerConnectionClosedError(error) && !params.signal?.aborted) throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
|
|
1984
|
+
if (isCodexAppServerConnectionClosedError(error) || params.signal?.aborted) throw error;
|
|
1985
|
+
embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
|
|
1986
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
1990
|
+
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
1991
|
+
configPatch: params.finalConfigPatch,
|
|
1992
|
+
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
1993
|
+
};
|
|
1994
|
+
const config = lifecycleTiming.measureSync("merge-thread-config", () => mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch, finalConfigPatch.configPatch));
|
|
1995
|
+
const startParams = lifecycleTiming.measureSync("thread-start-params", () => buildThreadStartParams(params.params, {
|
|
1996
|
+
cwd: params.cwd,
|
|
1997
|
+
dynamicTools: params.dynamicTools,
|
|
1998
|
+
appServer: params.appServer,
|
|
1999
|
+
developerInstructions: params.developerInstructions,
|
|
2000
|
+
config,
|
|
2001
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
2002
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
2003
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
2004
|
+
webSearchAllowed: params.webSearchAllowed,
|
|
2005
|
+
environmentSelection: params.environmentSelection,
|
|
2006
|
+
model: startModelSelection.model,
|
|
2007
|
+
modelProvider: startModelProvider
|
|
2008
|
+
}));
|
|
2009
|
+
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
2010
|
+
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
2011
|
+
try {
|
|
2012
|
+
return await params.client.request("thread/start", startParams, { signal: params.signal });
|
|
2013
|
+
} catch (error) {
|
|
2014
|
+
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
2015
|
+
throw error;
|
|
2016
|
+
}
|
|
2017
|
+
}));
|
|
2018
|
+
throwIfAborted();
|
|
2019
|
+
const modelProvider = resolveCodexAppServerModelProvider({
|
|
2020
|
+
provider: params.params.provider,
|
|
2021
|
+
authProfileId: params.params.authProfileId,
|
|
2022
|
+
authProfileStore: params.params.authProfileStore,
|
|
2023
|
+
agentDir: params.params.agentDir,
|
|
2024
|
+
config: params.params.config
|
|
2025
|
+
});
|
|
2026
|
+
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
|
|
2027
|
+
if (!preserveExistingBinding) {
|
|
2028
|
+
if (!await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
|
|
2029
|
+
kind: "set",
|
|
2030
|
+
if: { kind: "absent" },
|
|
2031
|
+
binding: {
|
|
2032
|
+
threadId: response.thread.id,
|
|
2033
|
+
cwd: params.cwd,
|
|
2034
|
+
authProfileId: params.params.authProfileId,
|
|
2035
|
+
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
2036
|
+
modelProvider: normalizeBindingModelProvider(params.params.authProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider),
|
|
2037
|
+
dynamicToolsFingerprint,
|
|
2038
|
+
dynamicToolsContainDeferred,
|
|
2039
|
+
webSearchThreadConfigFingerprint,
|
|
2040
|
+
userMcpServersFingerprint,
|
|
2041
|
+
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2042
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2043
|
+
networkProxyConfigFingerprint,
|
|
2044
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
2045
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
2046
|
+
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
2047
|
+
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
2048
|
+
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
2049
|
+
contextEngine: contextEngineBinding,
|
|
2050
|
+
environmentSelectionFingerprint
|
|
2051
|
+
}
|
|
2052
|
+
}))) throw new CodexThreadBindingConflictError(response.thread.id, "committing a fresh thread");
|
|
1647
2053
|
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
1648
2054
|
sessionId: params.params.sessionId,
|
|
1649
2055
|
sessionKey: params.params.sessionKey,
|
|
@@ -1651,90 +2057,18 @@ async function startOrResumeThread(params) {
|
|
|
1651
2057
|
engineId: contextEngineBinding.engineId,
|
|
1652
2058
|
epoch: contextEngineBinding.projection?.epoch,
|
|
1653
2059
|
fingerprint: contextEngineBinding.projection?.fingerprint,
|
|
1654
|
-
action: "
|
|
2060
|
+
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
1655
2061
|
});
|
|
1656
|
-
lifecycleTiming.mark("thread-ready");
|
|
1657
|
-
lifecycleTiming.logSummary({
|
|
1658
|
-
runId: params.params.runId,
|
|
1659
|
-
sessionId: params.params.sessionId,
|
|
1660
|
-
sessionKey: params.params.sessionKey,
|
|
1661
|
-
threadId: response.thread.id,
|
|
1662
|
-
action: "resumed"
|
|
1663
|
-
});
|
|
1664
|
-
const activeTurnIds = readActiveCodexTurnIds(response.thread);
|
|
1665
|
-
return {
|
|
1666
|
-
...resumeBinding,
|
|
1667
|
-
threadId: response.thread.id,
|
|
1668
|
-
cwd: params.cwd,
|
|
1669
|
-
authProfileId: boundAuthProfileId,
|
|
1670
|
-
model: response.model ?? resumeParams.model ?? params.params.modelId,
|
|
1671
|
-
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider,
|
|
1672
|
-
dynamicToolsFingerprint,
|
|
1673
|
-
dynamicToolsContainDeferred,
|
|
1674
|
-
webSearchThreadConfigFingerprint,
|
|
1675
|
-
userMcpServersFingerprint,
|
|
1676
|
-
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1677
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1678
|
-
networkProxyConfigFingerprint,
|
|
1679
|
-
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
|
|
1680
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1681
|
-
pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
|
|
1682
|
-
pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
|
|
1683
|
-
pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
|
|
1684
|
-
contextEngine: contextEngineBinding,
|
|
1685
|
-
environmentSelectionFingerprint,
|
|
1686
|
-
lifecycle: {
|
|
1687
|
-
action: "resumed",
|
|
1688
|
-
...activeTurnIds.length ? { activeTurnIds } : {}
|
|
1689
|
-
}
|
|
1690
|
-
};
|
|
1691
|
-
} catch (error) {
|
|
1692
|
-
if (isCodexAppServerConnectionClosedError(error)) throw error;
|
|
1693
|
-
embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
|
|
1694
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
1698
|
-
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
1699
|
-
configPatch: params.finalConfigPatch,
|
|
1700
|
-
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
1701
|
-
};
|
|
1702
|
-
const config = lifecycleTiming.measureSync("merge-thread-config", () => mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch, finalConfigPatch.configPatch));
|
|
1703
|
-
const startParams = lifecycleTiming.measureSync("thread-start-params", () => buildThreadStartParams(params.params, {
|
|
1704
|
-
cwd: params.cwd,
|
|
1705
|
-
dynamicTools: params.dynamicTools,
|
|
1706
|
-
appServer: params.appServer,
|
|
1707
|
-
developerInstructions: params.developerInstructions,
|
|
1708
|
-
config,
|
|
1709
|
-
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1710
|
-
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1711
|
-
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1712
|
-
webSearchAllowed: params.webSearchAllowed,
|
|
1713
|
-
environmentSelection: params.environmentSelection,
|
|
1714
|
-
model: startModelSelection.model,
|
|
1715
|
-
modelProvider: startModelProvider
|
|
1716
|
-
}));
|
|
1717
|
-
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
1718
|
-
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
1719
|
-
try {
|
|
1720
|
-
return await params.client.request("thread/start", startParams, { signal: params.signal });
|
|
1721
|
-
} catch (error) {
|
|
1722
|
-
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
1723
|
-
throw error;
|
|
1724
2062
|
}
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1735
|
-
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
|
|
1736
|
-
if (!preserveExistingBinding) {
|
|
1737
|
-
await lifecycleTiming.measure("thread-start-write-binding", () => writeCodexAppServerBinding(params.params.sessionFile, {
|
|
2063
|
+
lifecycleTiming.mark("thread-ready");
|
|
2064
|
+
lifecycleTiming.logSummary({
|
|
2065
|
+
runId: params.params.runId,
|
|
2066
|
+
sessionId: params.params.sessionId,
|
|
2067
|
+
sessionKey: params.params.sessionKey,
|
|
2068
|
+
threadId: response.thread.id,
|
|
2069
|
+
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
2070
|
+
});
|
|
2071
|
+
return {
|
|
1738
2072
|
threadId: response.thread.id,
|
|
1739
2073
|
cwd: params.cwd,
|
|
1740
2074
|
authProfileId: params.params.authProfileId,
|
|
@@ -1742,7 +2076,6 @@ async function startOrResumeThread(params) {
|
|
|
1742
2076
|
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
|
|
1743
2077
|
dynamicToolsFingerprint,
|
|
1744
2078
|
dynamicToolsContainDeferred,
|
|
1745
|
-
webSearchThreadConfigFingerprint,
|
|
1746
2079
|
userMcpServersFingerprint,
|
|
1747
2080
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1748
2081
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
@@ -1754,64 +2087,35 @@ async function startOrResumeThread(params) {
|
|
|
1754
2087
|
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
1755
2088
|
contextEngine: contextEngineBinding,
|
|
1756
2089
|
environmentSelectionFingerprint,
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
}));
|
|
1763
|
-
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
1764
|
-
sessionId: params.params.sessionId,
|
|
1765
|
-
sessionKey: params.params.sessionKey,
|
|
1766
|
-
threadId: response.thread.id,
|
|
1767
|
-
engineId: contextEngineBinding.engineId,
|
|
1768
|
-
epoch: contextEngineBinding.projection?.epoch,
|
|
1769
|
-
fingerprint: contextEngineBinding.projection?.fingerprint,
|
|
1770
|
-
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
1771
|
-
});
|
|
1772
|
-
}
|
|
1773
|
-
lifecycleTiming.mark("thread-ready");
|
|
1774
|
-
lifecycleTiming.logSummary({
|
|
1775
|
-
runId: params.params.runId,
|
|
1776
|
-
sessionId: params.params.sessionId,
|
|
1777
|
-
sessionKey: params.params.sessionKey,
|
|
1778
|
-
threadId: response.thread.id,
|
|
1779
|
-
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
2090
|
+
lifecycle: {
|
|
2091
|
+
action: "started",
|
|
2092
|
+
...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
|
|
2093
|
+
}
|
|
2094
|
+
};
|
|
1780
2095
|
});
|
|
1781
|
-
return {
|
|
1782
|
-
schemaVersion: 2,
|
|
1783
|
-
threadId: response.thread.id,
|
|
1784
|
-
sessionFile: params.params.sessionFile,
|
|
1785
|
-
cwd: params.cwd,
|
|
1786
|
-
authProfileId: params.params.authProfileId,
|
|
1787
|
-
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
1788
|
-
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
|
|
1789
|
-
dynamicToolsFingerprint,
|
|
1790
|
-
dynamicToolsContainDeferred,
|
|
1791
|
-
userMcpServersFingerprint,
|
|
1792
|
-
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1793
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1794
|
-
networkProxyConfigFingerprint,
|
|
1795
|
-
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
1796
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1797
|
-
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
1798
|
-
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
1799
|
-
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
1800
|
-
contextEngine: contextEngineBinding,
|
|
1801
|
-
environmentSelectionFingerprint,
|
|
1802
|
-
createdAt,
|
|
1803
|
-
updatedAt: createdAt,
|
|
1804
|
-
lifecycle: {
|
|
1805
|
-
action: "started",
|
|
1806
|
-
...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
|
|
1807
|
-
}
|
|
1808
|
-
};
|
|
1809
2096
|
}
|
|
1810
2097
|
function shouldRotateCodexAppServerBindingForRuntime(params) {
|
|
1811
2098
|
if (!params.current) return false;
|
|
1812
2099
|
if (params.binding === params.current) return false;
|
|
1813
2100
|
return params.connectionClass === "remote" || Boolean(params.binding);
|
|
1814
2101
|
}
|
|
2102
|
+
function resolveCodexGpt56MultiAgentVersion(modelRef) {
|
|
2103
|
+
let modelId = modelRef?.trim().toLowerCase();
|
|
2104
|
+
if (!modelId) return;
|
|
2105
|
+
const slashIndex = modelId.indexOf("/");
|
|
2106
|
+
if (slashIndex > 0) {
|
|
2107
|
+
const provider = modelId.slice(0, slashIndex);
|
|
2108
|
+
if (provider !== "openai" && provider !== "codex") return;
|
|
2109
|
+
modelId = modelId.slice(slashIndex + 1);
|
|
2110
|
+
}
|
|
2111
|
+
if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra") return "v2";
|
|
2112
|
+
return modelId === "gpt-5.6-luna" ? "v1" : void 0;
|
|
2113
|
+
}
|
|
2114
|
+
function shouldRotateCodexGpt56MultiAgentBinding(params) {
|
|
2115
|
+
const bindingVersion = resolveCodexGpt56MultiAgentVersion(params.bindingModel);
|
|
2116
|
+
const requestedVersion = resolveCodexGpt56MultiAgentVersion(params.requestedModel);
|
|
2117
|
+
return Boolean(bindingVersion && requestedVersion && bindingVersion !== requestedVersion);
|
|
2118
|
+
}
|
|
1815
2119
|
function isTransientWebSearchRestriction(params) {
|
|
1816
2120
|
if (params.nativeProviderWebSearchSupport === "unknown") return true;
|
|
1817
2121
|
if (params.params.config?.tools?.web?.search?.enabled === false) return false;
|
|
@@ -1907,7 +2211,7 @@ function buildThreadStartParams(params, options) {
|
|
|
1907
2211
|
...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
|
|
1908
2212
|
cwd: options.cwd,
|
|
1909
2213
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
1910
|
-
approvalsReviewer: options.appServer.
|
|
2214
|
+
approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
|
|
1911
2215
|
...codexThreadSandboxOrPermissions(options.appServer),
|
|
1912
2216
|
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
1913
2217
|
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
@@ -1921,21 +2225,10 @@ function buildThreadStartParams(params, options) {
|
|
|
1921
2225
|
}),
|
|
1922
2226
|
...resolveCodexThreadEnvironmentSelection(options),
|
|
1923
2227
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
|
|
1924
|
-
dynamicTools:
|
|
1925
|
-
experimentalRawEvents: true
|
|
1926
|
-
persistExtendedHistory: true
|
|
2228
|
+
dynamicTools: [...options.dynamicTools],
|
|
2229
|
+
experimentalRawEvents: true
|
|
1927
2230
|
};
|
|
1928
2231
|
}
|
|
1929
|
-
function toCodexThreadStartDynamicTools(dynamicTools) {
|
|
1930
|
-
return dynamicTools.flatMap((tool) => tool.type === "namespace" ? tool.tools.map((child) => toCodexLegacyDynamicTool(child, tool.name)) : [toCodexLegacyDynamicTool(tool)]);
|
|
1931
|
-
}
|
|
1932
|
-
function toCodexLegacyDynamicTool(tool, namespace) {
|
|
1933
|
-
const { type: _type, ...legacyTool } = tool;
|
|
1934
|
-
return namespace ? {
|
|
1935
|
-
...legacyTool,
|
|
1936
|
-
namespace
|
|
1937
|
-
} : legacyTool;
|
|
1938
|
-
}
|
|
1939
2232
|
function buildThreadResumeParams(params, options) {
|
|
1940
2233
|
const resolvedModelProvider = resolveCodexAppServerModelProvider({
|
|
1941
2234
|
provider: params.provider,
|
|
@@ -1957,7 +2250,7 @@ function buildThreadResumeParams(params, options) {
|
|
|
1957
2250
|
model: modelSelection.model,
|
|
1958
2251
|
...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
|
|
1959
2252
|
approvalPolicy: options.appServer.approvalPolicy,
|
|
1960
|
-
approvalsReviewer: options.appServer.
|
|
2253
|
+
approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
|
|
1961
2254
|
...codexThreadSandboxOrPermissions(options.appServer),
|
|
1962
2255
|
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
1963
2256
|
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
@@ -1968,8 +2261,7 @@ function buildThreadResumeParams(params, options) {
|
|
|
1968
2261
|
webSearchAllowed: options.webSearchAllowed,
|
|
1969
2262
|
appServer: options.appServer
|
|
1970
2263
|
}),
|
|
1971
|
-
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
1972
|
-
persistExtendedHistory: true
|
|
2264
|
+
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
1973
2265
|
};
|
|
1974
2266
|
}
|
|
1975
2267
|
function resolveCodexBindingModelProviderFallback(params) {
|
|
@@ -2082,7 +2374,7 @@ function buildTurnStartParams(params, options) {
|
|
|
2082
2374
|
model: modelSelection.model,
|
|
2083
2375
|
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
2084
2376
|
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
2085
|
-
effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model),
|
|
2377
|
+
effort: resolveReasoningEffort(params.thinkLevel, modelSelection.model, readCodexSupportedReasoningEfforts(params.model?.compat)),
|
|
2086
2378
|
...options.environmentSelection ? { environments: options.environmentSelection } : {},
|
|
2087
2379
|
collaborationMode: buildTurnCollaborationMode(params, {
|
|
2088
2380
|
model: modelSelection.model,
|
|
@@ -2093,6 +2385,9 @@ function buildTurnStartParams(params, options) {
|
|
|
2093
2385
|
})
|
|
2094
2386
|
};
|
|
2095
2387
|
}
|
|
2388
|
+
function resolveCodexThreadApprovalsReviewer(appServer, config) {
|
|
2389
|
+
return config?.approvals_reviewer === "user" ? "user" : appServer.approvalsReviewer;
|
|
2390
|
+
}
|
|
2096
2391
|
function codexThreadSandboxOrPermissions(appServer) {
|
|
2097
2392
|
if (appServer.networkProxy) return {};
|
|
2098
2393
|
return { sandbox: appServer.sandbox };
|
|
@@ -2108,7 +2403,7 @@ function buildTurnCollaborationMode(params, options = {}) {
|
|
|
2108
2403
|
mode: "default",
|
|
2109
2404
|
settings: {
|
|
2110
2405
|
model,
|
|
2111
|
-
reasoning_effort: resolveReasoningEffort(params.thinkLevel, model),
|
|
2406
|
+
reasoning_effort: resolveReasoningEffort(params.thinkLevel, model, readCodexSupportedReasoningEfforts(params.model?.compat)),
|
|
2112
2407
|
developer_instructions: buildTurnScopedCollaborationInstructions(params, options)
|
|
2113
2408
|
}
|
|
2114
2409
|
};
|
|
@@ -2116,7 +2411,7 @@ function buildTurnCollaborationMode(params, options = {}) {
|
|
|
2116
2411
|
function buildTurnScopedCollaborationInstructions(params, options = {}) {
|
|
2117
2412
|
const contextInstructions = joinPresentSections(options.turnScopedDeveloperInstructions, options.memoryCollaborationInstructions, options.skillsCollaborationInstructions);
|
|
2118
2413
|
if (params.trigger === "cron") return joinPresentSections(buildCronCollaborationInstructions(), contextInstructions);
|
|
2119
|
-
if (params.trigger === "heartbeat") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions, options.heartbeatCollaborationInstructions);
|
|
2414
|
+
if (params.trigger === "heartbeat" && params.bootstrapContextRunKind !== "commitment-only") return joinPresentSections(buildHeartbeatCollaborationInstructions(), contextInstructions, options.heartbeatCollaborationInstructions);
|
|
2120
2415
|
if (contextInstructions?.trim()) return joinPresentSections(buildDefaultCollaborationInstructions(), contextInstructions);
|
|
2121
2416
|
return null;
|
|
2122
2417
|
}
|
|
@@ -2156,15 +2451,24 @@ function joinPresentSections(...sections) {
|
|
|
2156
2451
|
function codexDynamicToolsFingerprint(dynamicTools) {
|
|
2157
2452
|
return fingerprintDynamicTools(dynamicTools);
|
|
2158
2453
|
}
|
|
2454
|
+
function codexLegacyDynamicToolsFingerprint(dynamicTools) {
|
|
2455
|
+
return legacyFingerprintDynamicTools(dynamicTools);
|
|
2456
|
+
}
|
|
2159
2457
|
function areCodexDynamicToolFingerprintsCompatible(params) {
|
|
2160
|
-
return areDynamicToolFingerprintsCompatible(params.previous, params.next);
|
|
2458
|
+
return areDynamicToolFingerprintsCompatible(params.previous, params.next, params.nextLegacy);
|
|
2161
2459
|
}
|
|
2162
2460
|
function fingerprintDynamicTools(dynamicTools) {
|
|
2461
|
+
return hashCodexAppServerBindingFingerprint(legacyFingerprintDynamicTools(dynamicTools));
|
|
2462
|
+
}
|
|
2463
|
+
function legacyFingerprintDynamicTools(dynamicTools) {
|
|
2163
2464
|
return JSON.stringify(dynamicTools.map(fingerprintDynamicToolSpec).toSorted(compareJsonFingerprint));
|
|
2164
2465
|
}
|
|
2165
|
-
function
|
|
2466
|
+
function legacyFingerprintUserMcpServersConfigPatch(configPatch) {
|
|
2166
2467
|
return configPatch ? JSON.stringify(stabilizeJsonValue(configPatch)) : void 0;
|
|
2167
2468
|
}
|
|
2469
|
+
function fingerprintUserMcpServersConfigPatch(configPatch) {
|
|
2470
|
+
return configPatch ? hashCodexAppServerBindingFingerprint(JSON.stringify(stabilizeJsonValue(configPatch))) : void 0;
|
|
2471
|
+
}
|
|
2168
2472
|
function fingerprintJsonObject(value) {
|
|
2169
2473
|
return JSON.stringify(stabilizeJsonValue(value));
|
|
2170
2474
|
}
|
|
@@ -2194,12 +2498,19 @@ function stabilizeJsonValue(value) {
|
|
|
2194
2498
|
function readActiveCodexTurnIds(thread) {
|
|
2195
2499
|
return (thread.turns ?? []).filter((turn) => turn.status === "inProgress").map((turn) => typeof turn.id === "string" ? turn.id : "").filter((turnId) => turnId.trim().length > 0);
|
|
2196
2500
|
}
|
|
2197
|
-
const
|
|
2198
|
-
|
|
2199
|
-
|
|
2501
|
+
const LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT = legacyFingerprintDynamicTools([]);
|
|
2502
|
+
const EMPTY_DYNAMIC_TOOLS_FINGERPRINT = hashCodexAppServerBindingFingerprint(LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT);
|
|
2503
|
+
function areDynamicToolFingerprintsCompatible(previous, next, nextLegacy) {
|
|
2504
|
+
return !previous || previous === next || previous === nextLegacy;
|
|
2505
|
+
}
|
|
2506
|
+
function areUserMcpServersFingerprintsCompatible(params) {
|
|
2507
|
+
return params.previous === params.next || params.previous === params.nextLegacy || params.nextLegacy !== void 0 && params.previous === hashCodexAppServerBindingFingerprint(params.nextLegacy);
|
|
2200
2508
|
}
|
|
2201
2509
|
function shouldStartTransientNoToolThread(params) {
|
|
2202
|
-
return Boolean(params.previous && params.previous
|
|
2510
|
+
return Boolean(params.previous && !isEmptyDynamicToolsFingerprint(params.previous) && !params.nextHasDynamicTools);
|
|
2511
|
+
}
|
|
2512
|
+
function isEmptyDynamicToolsFingerprint(fingerprint) {
|
|
2513
|
+
return fingerprint === EMPTY_DYNAMIC_TOOLS_FINGERPRINT || fingerprint === LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT;
|
|
2203
2514
|
}
|
|
2204
2515
|
function compareJsonFingerprint(left, right) {
|
|
2205
2516
|
return JSON.stringify(left).localeCompare(JSON.stringify(right));
|
|
@@ -2213,7 +2524,7 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2213
2524
|
"You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
|
|
2214
2525
|
buildDeferredDynamicToolManifest(options.dynamicTools),
|
|
2215
2526
|
buildSkillWorkshopInstruction(options.dynamicTools),
|
|
2216
|
-
"Use Codex native `spawn_agent` for Codex subagents. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation
|
|
2527
|
+
"Use Codex native `spawn_agent` for Codex subagents. `spawn_agent` and the other native collaboration tools may be deferred: when `spawn_agent` is not directly listed, load it with `tool_search` before spawning. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent`.",
|
|
2217
2528
|
buildVisibleReplyInstruction(params, options.dynamicTools),
|
|
2218
2529
|
nativeCommandGuidance,
|
|
2219
2530
|
params.extraSystemPrompt
|
|
@@ -2259,10 +2570,21 @@ function resolveCodexAppServerModelProvider(params) {
|
|
|
2259
2570
|
if (isCodexAppServerNativeAuthProfile(params) && normalizedLower === "openai") return;
|
|
2260
2571
|
return normalizedLower === "openai" ? "openai" : normalized;
|
|
2261
2572
|
}
|
|
2262
|
-
function resolveReasoningEffort(thinkLevel, modelId) {
|
|
2573
|
+
function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts) {
|
|
2574
|
+
if (thinkLevel === "off" || thinkLevel === "adaptive") return null;
|
|
2575
|
+
if (supportedReasoningEfforts) return resolveCodexSupportedReasoningEffort({
|
|
2576
|
+
requested: thinkLevel,
|
|
2577
|
+
supportedReasoningEfforts
|
|
2578
|
+
}) ?? null;
|
|
2579
|
+
const fallbackReasoningEfforts = resolveCodexFallbackReasoningEfforts(modelId);
|
|
2580
|
+
if (fallbackReasoningEfforts) return resolveCodexSupportedReasoningEffort({
|
|
2581
|
+
requested: thinkLevel,
|
|
2582
|
+
supportedReasoningEfforts: fallbackReasoningEfforts
|
|
2583
|
+
}) ?? null;
|
|
2263
2584
|
if (thinkLevel === "minimal") return isModernCodexModel(modelId) ? "low" : "minimal";
|
|
2264
2585
|
if (thinkLevel === "low" || thinkLevel === "medium" || thinkLevel === "high" || thinkLevel === "xhigh") return thinkLevel;
|
|
2586
|
+
if (thinkLevel === "max" && isMaxReasoningCodexModel(modelId)) return "max";
|
|
2265
2587
|
return null;
|
|
2266
2588
|
}
|
|
2267
2589
|
//#endregion
|
|
2268
|
-
export {
|
|
2590
|
+
export { resolveCodexContextEngineProjectionMaxChars as A, buildCodexPluginThreadConfig as B, filterCodexDynamicTools as C, resolveCodexDynamicToolsLoadingForRuntime as D, resolveCodexDynamicToolsLoading as E, closeCodexStartupClientBestEffort as F, pluginReadParams as G, mergeCodexThreadConfigs as H, interruptCodexTurnBestEffort as I, buildCodexAppInventoryCacheKey as K, retireCodexAppServerClientAfterTimedOutTurn as L, CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS as M, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS as N, fitCodexProjectedContextForTurnStart as O, CodexAppServerUnsafeSubscriptionError as P, unsubscribeCodexThreadBestEffort as R, sanitizeInlineImageDataUrl$1 as S, normalizeCodexDynamicToolName as T, shouldBuildCodexPluginThreadConfig as U, buildCodexPluginThreadConfigInputFingerprint as V, ensureCodexPluginActivation as W, buildCodexNativeWebSearchThreadConfig as _, buildDeveloperInstructions as a, invalidInlineImageText as b, codexDynamicToolsFingerprint as c, resolveCodexAppServerModelProvider as d, resolveCodexAppServerRequestModelSelection as f, startOrResumeThread as g, resolveReasoningEffort as h, buildContextEngineBinding as i, resolveCodexContextEngineProjectionReserveTokens as j, projectContextEngineAssemblyForCodex as k, codexLegacyDynamicToolsFingerprint as l, resolveCodexBindingModelProviderFallback as m, areCodexDynamicToolFingerprintsCompatible as n, buildTurnCollaborationMode as o, resolveCodexAppServerThreadModelSelection as p, defaultCodexAppInventoryCache as q, buildCodexRuntimeThreadConfig as r, buildTurnStartParams as s, CODEX_NATIVE_PERSONALITY_NONE as t, isContextEngineBindingCompatible as u, resolveCodexWebSearchPlan as v, isForcedPrivateQaCodexRuntime as w, sanitizeCodexHistoryImagePayloads as x, isCodexAppServerProfilerEnabled as y, buildCodexPluginAppsConfigPatchFromPolicyContext as z };
|