@openclaw/codex 2026.7.1-beta.1 → 2026.7.1-beta.4
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-xi0ZmBGO.js → app-server-policy-BUk0GLMy.js} +5 -5
- package/dist/{attempt-notifications-l9LElGOU.js → attempt-notifications-BGsEIIDI.js} +5 -40
- package/dist/{command-handlers-vZy8xX5X.js → command-handlers-UVniG5Hl.js} +267 -232
- package/dist/command-rpc-BjgEPh19.js +784 -0
- package/dist/compact-CZzbcygx.js +603 -0
- package/dist/{computer-use-FPGG-mtX.js → computer-use-DDeySrnb.js} +3 -3
- package/dist/{config-C_whBx3M.js → config-CYEDnLJ2.js} +36 -15
- package/dist/conversation-binding-data-BHRpYbX3.js +121 -0
- package/dist/doctor-contract-api.js +439 -22
- package/dist/harness.js +29 -10
- package/dist/index.js +1119 -28
- package/dist/{media-understanding-provider-C5yYzx_6.js → media-understanding-provider-DgqRvU2_.js} +13 -8
- package/dist/media-understanding-provider.js +1 -1
- package/dist/{models-Ccy1_7nN.js → models-DDmO1zwd.js} +2 -2
- package/dist/notification-correlation-Bo7KB3ks.js +35 -0
- package/dist/{plugin-app-cache-key-CDioyur-.js → plugin-app-cache-key-o-AHbdaf.js} +8 -4
- package/dist/{protocol-dh-ETiNd.js → protocol-2POPqAY4.js} +2 -1
- package/dist/{protocol-validators-CB4mtyrs.js → protocol-validators-dZQ-UTOa.js} +117 -62
- package/dist/{provider-capabilities-CE5k8cmO.js → provider-capabilities-gTCwjfmh.js} +2920 -114
- package/dist/{provider-DosqwRb6.js → provider-cc_62eQE.js} +19 -10
- package/dist/provider.js +2 -2
- package/dist/{request-BQuSQF29.js → request-BcJyl8KL.js} +2 -2
- package/dist/{run-attempt-O3Zs32KI.js → run-attempt-8SPLiDlT.js} +1016 -2877
- package/dist/{sandbox-guard-pTY2COoC.js → sandbox-guard-DA2TQfZW.js} +3 -3
- package/dist/session-binding-Dc03iwRF.js +595 -0
- package/dist/session-binding-meta-B7aEMU7g.js +6 -0
- package/dist/{shared-client-rR6TDJA3.js → shared-client-4ICy3U6d.js} +892 -103
- package/dist/{side-question-DDVgFmpv.js → side-question-DkjXvYv7.js} +132 -140
- package/dist/{thread-lifecycle-DeSMZSXr.js → thread-lifecycle-BskXnNP-.js} +609 -444
- package/dist/{web-search-provider.runtime-Q89rQhsL.js → web-search-provider.runtime-B5F42P7x.js} +3 -3
- package/npm-shrinkwrap.json +34 -34
- package/openclaw.plugin.json +20 -0
- package/package.json +9 -6
- package/dist/client-IcTBKEqA.js +0 -732
- package/dist/client-factory-CwjdQQ4j.js +0 -18
- package/dist/compact-BzB9AqN1.js +0 -278
- package/dist/conversation-binding-CEE2UyYb.js +0 -1570
- package/dist/rate-limit-cache-C7qmZ0Jh.js +0 -26
- package/dist/session-binding-Cm0Gg7Z1.js +0 -339
|
@@ -1,79 +1,22 @@
|
|
|
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 { a as readCodexSupportedReasoningEfforts, i as isModernCodexModel, o as
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { a as readCodexSupportedReasoningEfforts, i as isModernCodexModel, o as resolveCodexFallbackReasoningEfforts, r as isMaxReasoningCodexModel, s as resolveCodexSupportedReasoningEffort } from "./provider-cc_62eQE.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 reclaimCurrentCodexSessionGeneration, i as normalizeCodexAppServerBindingModelProvider, o as sessionBindingIdentity, r as isCodexAppServerNativeAuthProfile } from "./session-binding-Dc03iwRF.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
|
-
//#region extensions/codex/src/app-server/notification-correlation.ts
|
|
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
|
-
}
|
|
77
20
|
const CODEX_TARGETED_APP_INVENTORY_LIMIT = 1e3;
|
|
78
21
|
const MAX_SERIALIZED_ERROR_MESSAGE_LENGTH = 500;
|
|
79
22
|
/** In-memory app inventory cache with coalesced refreshes per key. */
|
|
@@ -275,7 +218,7 @@ function redactErrorData(value, depth = 0) {
|
|
|
275
218
|
for (const [key, entry] of Object.entries(value)) redacted[key] = isSensitiveErrorDataKey(key) ? "<redacted>" : redactErrorData(entry, depth + 1) ?? null;
|
|
276
219
|
return redacted;
|
|
277
220
|
}
|
|
278
|
-
if (typeof value === "string" && value.length > 500) return `${value
|
|
221
|
+
if (typeof value === "string" && value.length > 500) return `${truncateUtf16Safe(value, 500)}...`;
|
|
279
222
|
if (typeof value === "string") return value;
|
|
280
223
|
if (typeof value === "bigint") return value.toString();
|
|
281
224
|
if (typeof value === "symbol") return value.description ? `Symbol(${value.description})` : "Symbol()";
|
|
@@ -576,20 +519,21 @@ function activationFailure(identity, reason, diagnostic, extraDiagnostics = [])
|
|
|
576
519
|
//#endregion
|
|
577
520
|
//#region extensions/codex/src/app-server/plugin-thread-config.ts
|
|
578
521
|
/**
|
|
579
|
-
* Builds Codex thread config patches that expose only policy-approved
|
|
580
|
-
*
|
|
522
|
+
* Builds Codex thread config patches that expose only policy-approved apps
|
|
523
|
+
* for native Codex turns.
|
|
581
524
|
*/
|
|
582
|
-
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION =
|
|
583
|
-
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;
|
|
584
527
|
/** Returns true when plugin config exists and thread config may need app patches. */
|
|
585
528
|
function shouldBuildCodexPluginThreadConfig(pluginConfig) {
|
|
586
529
|
return resolveCodexPluginsPolicy(pluginConfig).configured;
|
|
587
530
|
}
|
|
588
531
|
/** Fingerprints policy and app-cache identity before runtime inventory is read. */
|
|
589
532
|
function buildCodexPluginThreadConfigInputFingerprint(params) {
|
|
533
|
+
const policy = resolveCodexPluginsPolicy(params.pluginConfig);
|
|
590
534
|
return fingerprintJson({
|
|
591
535
|
version: CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION,
|
|
592
|
-
policy: policyFingerprint(
|
|
536
|
+
policy: policyFingerprint(policy),
|
|
593
537
|
appCacheKey: params.appCacheKey ?? null
|
|
594
538
|
});
|
|
595
539
|
}
|
|
@@ -606,7 +550,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
606
550
|
inputFingerprint,
|
|
607
551
|
configPatch: buildDisabledAppsConfigPatch()
|
|
608
552
|
});
|
|
609
|
-
let inventory = await readCodexPluginInventory({
|
|
553
|
+
let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
|
|
610
554
|
pluginConfig: params.pluginConfig,
|
|
611
555
|
policy,
|
|
612
556
|
request: params.request,
|
|
@@ -614,7 +558,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
614
558
|
appCacheKey: params.appCacheKey,
|
|
615
559
|
nowMs: params.nowMs,
|
|
616
560
|
suppressAppInventoryRefresh: true
|
|
617
|
-
});
|
|
561
|
+
}) : emptyCodexPluginInventory(policy);
|
|
618
562
|
const appInventoryRefreshDeferredForActivation = inventory.records.some((record) => record.activationRequired) && shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
619
563
|
if (shouldWaitForInitialAppInventory(params, policy, inventory)) {
|
|
620
564
|
await refreshAppInventoryNow(params, appCache, {
|
|
@@ -693,7 +637,12 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
693
637
|
appCacheKey: params.appCacheKey
|
|
694
638
|
});
|
|
695
639
|
}
|
|
696
|
-
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
|
+
];
|
|
697
646
|
const apps = { _default: {
|
|
698
647
|
enabled: false,
|
|
699
648
|
destructive_enabled: false,
|
|
@@ -701,6 +650,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
701
650
|
} };
|
|
702
651
|
const policyApps = {};
|
|
703
652
|
const pluginAppIds = {};
|
|
653
|
+
const pluginOwnedAppIds = new Set(inventory.records.flatMap((record) => record.appOwnership === "proven" ? record.ownedAppIds : []));
|
|
704
654
|
for (const record of inventory.records) {
|
|
705
655
|
const activation = activationResults.find((item) => item.identity.configKey === record.policy.configKey);
|
|
706
656
|
if (activation?.ok === false || record.activationRequired && !activation?.ok) continue;
|
|
@@ -725,14 +675,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
725
675
|
app,
|
|
726
676
|
diagnostics
|
|
727
677
|
})) continue;
|
|
728
|
-
|
|
729
|
-
enabled: true,
|
|
730
|
-
destructive_enabled: record.policy.allowDestructiveActions,
|
|
731
|
-
open_world_enabled: true,
|
|
732
|
-
default_tools_approval_mode: "auto"
|
|
733
|
-
};
|
|
734
|
-
if (record.policy.destructiveApprovalMode === "ask") appConfig.approvals_reviewer = "user";
|
|
735
|
-
apps[app.id] = appConfig;
|
|
678
|
+
apps[app.id] = buildEnabledAppConfig(record.policy);
|
|
736
679
|
policyApps[app.id] = {
|
|
737
680
|
configKey: record.policy.configKey,
|
|
738
681
|
marketplaceName: record.policy.marketplaceName,
|
|
@@ -743,6 +686,24 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
743
686
|
};
|
|
744
687
|
}
|
|
745
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
|
+
}
|
|
746
707
|
const configPatch = { apps };
|
|
747
708
|
const policyContext = buildPluginAppPolicyContext(policyApps, pluginAppIds);
|
|
748
709
|
return {
|
|
@@ -798,6 +759,15 @@ function buildDisabledAppsConfigPatch() {
|
|
|
798
759
|
open_world_enabled: false
|
|
799
760
|
} } };
|
|
800
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
|
+
}
|
|
801
771
|
/** Rebuilds the safe per-thread apps patch persisted with a Codex thread binding. */
|
|
802
772
|
function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
|
|
803
773
|
const apps = { _default: {
|
|
@@ -817,7 +787,7 @@ function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
|
|
|
817
787
|
function buildPluginAppPolicyContext(apps, pluginAppIds) {
|
|
818
788
|
return {
|
|
819
789
|
fingerprint: fingerprintJson({
|
|
820
|
-
version:
|
|
790
|
+
version: 2,
|
|
821
791
|
apps,
|
|
822
792
|
pluginAppIds
|
|
823
793
|
}),
|
|
@@ -839,7 +809,7 @@ async function clearPersistedAppToolApprovalOverrides(params) {
|
|
|
839
809
|
} catch (error) {
|
|
840
810
|
params.diagnostics.push({
|
|
841
811
|
code: "approval_overrides_clear_failed",
|
|
842
|
-
plugin: params.plugin,
|
|
812
|
+
...params.plugin ? { plugin: params.plugin } : {},
|
|
843
813
|
message: `Could not clear durable Codex app approval overrides for ${params.app.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
844
814
|
});
|
|
845
815
|
return false;
|
|
@@ -898,6 +868,37 @@ async function refreshAppInventoryNow(params, appCache, options = {}) {
|
|
|
898
868
|
function collectInventoryOwnedAppIds(inventory) {
|
|
899
869
|
return Array.from(new Set(inventory.records.flatMap((record) => record.ownedAppIds).filter(Boolean))).toSorted();
|
|
900
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
|
+
}
|
|
901
902
|
function resolveThreadConfigAppsForRecord(params) {
|
|
902
903
|
if (params.inventory.appInventory?.state === "missing") return [];
|
|
903
904
|
return params.record.apps;
|
|
@@ -913,6 +914,7 @@ function shouldForceRefreshForNotReadyPluginApps(params, policy, inventory) {
|
|
|
913
914
|
function policyFingerprint(policy) {
|
|
914
915
|
return {
|
|
915
916
|
enabled: policy.enabled,
|
|
917
|
+
allowAllPlugins: policy.allowAllPlugins,
|
|
916
918
|
allowDestructiveActions: policy.allowDestructiveActions,
|
|
917
919
|
destructiveApprovalMode: policy.destructiveApprovalMode,
|
|
918
920
|
plugins: policy.pluginPolicies.map((plugin) => ({
|
|
@@ -945,6 +947,127 @@ function stableStringify(value) {
|
|
|
945
947
|
return JSON.stringify(value);
|
|
946
948
|
}
|
|
947
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
|
|
948
1071
|
//#region extensions/codex/src/app-server/context-engine-projection.ts
|
|
949
1072
|
const CONTEXT_HEADER = "OpenClaw assembled context for this turn:";
|
|
950
1073
|
const CONTEXT_OPEN = "<conversation_context>";
|
|
@@ -1111,7 +1234,7 @@ function renderToolResultPayload(record) {
|
|
|
1111
1234
|
}
|
|
1112
1235
|
return payload;
|
|
1113
1236
|
}
|
|
1114
|
-
const TOOL_PAYLOAD_METADATA_KEYS = new Set([
|
|
1237
|
+
const TOOL_PAYLOAD_METADATA_KEYS = /* @__PURE__ */ new Set([
|
|
1115
1238
|
"type",
|
|
1116
1239
|
"name",
|
|
1117
1240
|
"id",
|
|
@@ -1345,6 +1468,31 @@ function isCodexAppServerProfilerEnabled(config, env = process.env) {
|
|
|
1345
1468
|
return PROFILER_FLAGS.some((flag) => isDiagnosticFlagEnabled(flag, config, env));
|
|
1346
1469
|
}
|
|
1347
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
|
|
1348
1496
|
//#region extensions/codex/src/app-server/web-search.ts
|
|
1349
1497
|
const CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG = {
|
|
1350
1498
|
"features.standalone_web_search": false,
|
|
@@ -1419,6 +1567,12 @@ var CodexThreadStartRequestError = class extends Error {
|
|
|
1419
1567
|
this.name = "CodexThreadStartRequestError";
|
|
1420
1568
|
}
|
|
1421
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
|
+
};
|
|
1422
1576
|
const CODEX_NATIVE_PERSONALITY_NONE = "none";
|
|
1423
1577
|
const CODEX_CODE_MODE_THREAD_CONFIG = {
|
|
1424
1578
|
"features.code_mode": true,
|
|
@@ -1519,228 +1673,365 @@ function createCodexThreadLifecycleTimingTracker(options = {}) {
|
|
|
1519
1673
|
};
|
|
1520
1674
|
}
|
|
1521
1675
|
async function startOrResumeThread(params) {
|
|
1522
|
-
const
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => fingerprintDynamicTools(params.dynamicTools));
|
|
1527
|
-
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
1528
|
-
const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
1529
|
-
config: params.params.config,
|
|
1530
|
-
disableTools: params.params.disableTools,
|
|
1531
|
-
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
1532
|
-
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1533
|
-
webSearchAllowed: params.webSearchAllowed
|
|
1534
|
-
})).threadConfig);
|
|
1535
|
-
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
1536
|
-
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
1537
|
-
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : buildCodexUserMcpServersThreadConfigPatch(params.params.config, { agentId: params.agentId ?? params.params.agentId });
|
|
1538
|
-
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1539
|
-
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
1540
|
-
let binding = await lifecycleTiming.measure("read-binding", () => readCodexAppServerBinding(params.params.sessionFile, {
|
|
1541
|
-
authProfileStore: params.params.authProfileStore,
|
|
1542
|
-
agentDir: params.params.agentDir,
|
|
1543
|
-
config: params.params.config
|
|
1544
|
-
}));
|
|
1545
|
-
if (binding?.threadId && shouldRotateCodexAppServerBindingForRuntime({
|
|
1546
|
-
connectionClass: params.appServer.connectionClass,
|
|
1547
|
-
current: params.appServerRuntimeFingerprint,
|
|
1548
|
-
binding: binding.appServerRuntimeFingerprint
|
|
1549
|
-
})) {
|
|
1550
|
-
embeddedAgentLog.debug("codex app-server runtime identity changed; starting a new thread", {
|
|
1551
|
-
threadId: binding.threadId,
|
|
1552
|
-
connectionClass: params.appServer.connectionClass
|
|
1553
|
-
});
|
|
1554
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1555
|
-
binding = void 0;
|
|
1556
|
-
}
|
|
1557
|
-
const startModelSelection = resolveCodexAppServerThreadModelSelection({
|
|
1558
|
-
provider: params.params.provider,
|
|
1559
|
-
model: params.params.modelId,
|
|
1560
|
-
binding,
|
|
1561
|
-
authProfileId: params.params.authProfileId,
|
|
1562
|
-
authProfileStore: params.params.authProfileStore,
|
|
1563
|
-
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,
|
|
1564
1680
|
config: params.params.config
|
|
1565
1681
|
});
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
const
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
} else {
|
|
1600
|
-
embeddedAgentLog.debug("codex app-server web search config changed; starting a new thread", { threadId: binding.threadId });
|
|
1601
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
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 dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => fingerprintDynamicTools(params.dynamicTools));
|
|
1688
|
+
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
1689
|
+
const webSearchThreadConfigFingerprint = fingerprintJsonObject(lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
1690
|
+
config: params.params.config,
|
|
1691
|
+
disableTools: params.params.disableTools,
|
|
1692
|
+
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
1693
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1694
|
+
webSearchAllowed: params.webSearchAllowed
|
|
1695
|
+
})).threadConfig);
|
|
1696
|
+
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
1697
|
+
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
1698
|
+
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : buildCodexUserMcpServersThreadConfigPatch(params.params.config, { agentId: params.agentId ?? params.params.agentId });
|
|
1699
|
+
const userMcpServersFingerprint = fingerprintUserMcpServersConfigPatch(userMcpServersConfigPatch);
|
|
1700
|
+
const environmentSelectionFingerprint = fingerprintEnvironmentSelection(params.environmentSelection);
|
|
1701
|
+
let binding = await lifecycleTiming.measure("read-binding", () => params.bindingStore.read(bindingIdentity));
|
|
1702
|
+
const normalizeBindingModelProvider = (authProfileId, modelProvider) => normalizeCodexAppServerBindingModelProvider({
|
|
1703
|
+
authProfileId,
|
|
1704
|
+
modelProvider,
|
|
1705
|
+
authProfileStore: params.params.authProfileStore,
|
|
1706
|
+
agentDir: params.params.agentDir,
|
|
1707
|
+
config: params.params.config
|
|
1708
|
+
});
|
|
1709
|
+
if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
|
|
1710
|
+
if (!await lifecycleTiming.measure("reclaim-binding-generation", () => reclaimCurrentCodexSessionGeneration({
|
|
1711
|
+
bindingStore: params.bindingStore,
|
|
1712
|
+
identity: bindingIdentity,
|
|
1713
|
+
config: params.params.config
|
|
1714
|
+
}))) throw new Error(`Codex session generation is no longer current: ${bindingIdentity.sessionId}`);
|
|
1602
1715
|
}
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1716
|
+
const clearCurrentBinding = async (operation) => {
|
|
1717
|
+
const current = binding;
|
|
1718
|
+
if (!current?.threadId) return;
|
|
1719
|
+
if (!await params.bindingStore.mutate(bindingIdentity, {
|
|
1720
|
+
kind: "clear",
|
|
1721
|
+
threadId: current.threadId
|
|
1722
|
+
})) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
1723
|
+
binding = void 0;
|
|
1724
|
+
};
|
|
1725
|
+
if (binding?.threadId && shouldRotateCodexAppServerBindingForRuntime({
|
|
1726
|
+
connectionClass: params.appServer.connectionClass,
|
|
1727
|
+
current: params.appServerRuntimeFingerprint,
|
|
1728
|
+
binding: binding.appServerRuntimeFingerprint
|
|
1729
|
+
})) {
|
|
1730
|
+
embeddedAgentLog.debug("codex app-server runtime identity changed; starting a new thread", {
|
|
1613
1731
|
threadId: binding.threadId,
|
|
1614
|
-
|
|
1615
|
-
previousEngineId: binding.contextEngine?.engineId,
|
|
1616
|
-
epoch: contextEngineBinding?.projection?.epoch,
|
|
1617
|
-
previousEpoch: binding.contextEngine?.projection?.epoch,
|
|
1618
|
-
fingerprint: contextEngineBinding?.projection?.fingerprint,
|
|
1619
|
-
previousFingerprint: binding.contextEngine?.projection?.fingerprint,
|
|
1620
|
-
policyFingerprint: contextEngineBinding?.policyFingerprint,
|
|
1621
|
-
previousPolicyFingerprint: binding.contextEngine?.policyFingerprint
|
|
1732
|
+
connectionClass: params.appServer.connectionClass
|
|
1622
1733
|
});
|
|
1623
|
-
await
|
|
1734
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1624
1735
|
binding = void 0;
|
|
1625
|
-
rotatedContextEngineBinding = true;
|
|
1626
1736
|
}
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1631
|
-
binding = void 0;
|
|
1632
|
-
}
|
|
1633
|
-
if (binding?.threadId && binding.environmentSelectionFingerprint !== environmentSelectionFingerprint) {
|
|
1634
|
-
embeddedAgentLog.debug("codex app-server environment selection changed; starting a new thread", { threadId: binding.threadId });
|
|
1635
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1636
|
-
binding = void 0;
|
|
1637
|
-
}
|
|
1638
|
-
if (binding?.threadId && (binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint || binding.networkProxyProfileName !== params.appServer.networkProxy?.profileName)) {
|
|
1639
|
-
embeddedAgentLog.debug("codex app-server network proxy config changed; starting a new thread", { threadId: binding.threadId });
|
|
1640
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1641
|
-
binding = void 0;
|
|
1642
|
-
}
|
|
1643
|
-
if (binding?.threadId) {
|
|
1644
|
-
let pluginBindingStale = isCodexPluginThreadBindingStale({
|
|
1645
|
-
codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
|
|
1646
|
-
bindingFingerprint: binding.pluginAppsFingerprint,
|
|
1647
|
-
bindingInputFingerprint: binding.pluginAppsInputFingerprint,
|
|
1648
|
-
currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
|
|
1649
|
-
hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
|
|
1650
|
-
});
|
|
1651
|
-
if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
|
|
1737
|
+
const startModelSelection = resolveCodexAppServerThreadModelSelection({
|
|
1738
|
+
provider: params.params.provider,
|
|
1739
|
+
model: params.params.modelId,
|
|
1652
1740
|
binding,
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1741
|
+
authProfileId: params.params.authProfileId,
|
|
1742
|
+
authProfileStore: params.params.authProfileStore,
|
|
1743
|
+
agentDir: params.params.agentDir,
|
|
1744
|
+
config: params.params.config
|
|
1745
|
+
});
|
|
1746
|
+
const startModelProvider = startModelSelection.modelProvider;
|
|
1747
|
+
let preserveExistingBinding = params.nativeProviderWebSearchSupport === "unknown" && !binding?.threadId;
|
|
1748
|
+
let rotatedContextEngineBinding = false;
|
|
1749
|
+
let prebuiltPluginThreadConfig;
|
|
1750
|
+
const throwIfAborted = () => {
|
|
1751
|
+
if (!params.signal?.aborted) return;
|
|
1752
|
+
const reason = params.signal.reason;
|
|
1753
|
+
if (reason instanceof Error) throw reason;
|
|
1754
|
+
const error = new Error(typeof reason === "string" && reason.length > 0 ? reason : "codex app-server thread lifecycle aborted");
|
|
1755
|
+
error.name = "AbortError";
|
|
1756
|
+
throw error;
|
|
1757
|
+
};
|
|
1758
|
+
const webSearchBindingChanged = binding?.threadId && binding.webSearchThreadConfigFingerprint !== webSearchThreadConfigFingerprint;
|
|
1759
|
+
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
1760
|
+
const transientNativeToolRestriction = params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction;
|
|
1761
|
+
const transientWebSearchRestriction = isTransientWebSearchRestriction(params);
|
|
1762
|
+
const explicitTransientWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed !== false && transientWebSearchRestriction;
|
|
1763
|
+
const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
|
|
1764
|
+
if (binding?.threadId && params.mcpServersFingerprintEvaluated === true && binding.mcpServersFingerprint !== params.mcpServersFingerprint) {
|
|
1765
|
+
if (transientNativeToolRestriction || webSearchBindingChanged && (explicitTransientWebSearchRestriction || unknownProviderWebSearchSupport)) {
|
|
1766
|
+
embeddedAgentLog.debug("codex app-server MCP config changed during transient restricted turn; starting transient thread", { threadId: binding.threadId });
|
|
1767
|
+
preserveExistingBinding = true;
|
|
1768
|
+
} else {
|
|
1769
|
+
embeddedAgentLog.debug("codex app-server MCP config changed; starting a new thread", { threadId: binding.threadId });
|
|
1770
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1771
|
+
}
|
|
1772
|
+
binding = void 0;
|
|
1662
1773
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1774
|
+
const deferLegacyWebSearchRotationToTransientNativeSurface = params.nativeCodeModeEnabled === false && binding?.webSearchThreadConfigFingerprint === void 0 && !persistentWebSearchRestriction;
|
|
1775
|
+
if (binding?.threadId && webSearchBindingChanged && !deferLegacyWebSearchRotationToTransientNativeSurface) {
|
|
1776
|
+
if (transientWebSearchRestriction) {
|
|
1777
|
+
embeddedAgentLog.debug("codex app-server web search restricted for turn; starting transient thread", { threadId: binding.threadId });
|
|
1778
|
+
preserveExistingBinding = true;
|
|
1779
|
+
} else {
|
|
1780
|
+
embeddedAgentLog.debug("codex app-server web search config changed; starting a new thread", { threadId: binding.threadId });
|
|
1781
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1782
|
+
}
|
|
1666
1783
|
binding = void 0;
|
|
1667
1784
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
embeddedAgentLog.debug("codex app-server dynamic tool loading changed; starting a new thread", { threadId: binding.threadId });
|
|
1672
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1785
|
+
if (binding?.threadId && transientNativeToolRestriction) {
|
|
1786
|
+
embeddedAgentLog.debug("codex app-server native tool surface disabled for turn; starting transient thread", { threadId: binding.threadId });
|
|
1787
|
+
preserveExistingBinding = true;
|
|
1673
1788
|
binding = void 0;
|
|
1674
1789
|
}
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1790
|
+
if (binding?.threadId && (binding.contextEngine || contextEngineBinding)) {
|
|
1791
|
+
if (!contextEngineBinding || !isContextEngineBindingCompatible(binding.contextEngine, contextEngineBinding)) {
|
|
1792
|
+
embeddedAgentLog.debug("codex app-server context-engine binding changed; starting a new thread", {
|
|
1793
|
+
threadId: binding.threadId,
|
|
1794
|
+
engineId: contextEngineBinding?.engineId,
|
|
1795
|
+
previousEngineId: binding.contextEngine?.engineId,
|
|
1796
|
+
epoch: contextEngineBinding?.projection?.epoch,
|
|
1797
|
+
previousEpoch: binding.contextEngine?.projection?.epoch,
|
|
1798
|
+
fingerprint: contextEngineBinding?.projection?.fingerprint,
|
|
1799
|
+
previousFingerprint: binding.contextEngine?.projection?.fingerprint,
|
|
1800
|
+
policyFingerprint: contextEngineBinding?.policyFingerprint,
|
|
1801
|
+
previousPolicyFingerprint: binding.contextEngine?.policyFingerprint
|
|
1802
|
+
});
|
|
1803
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1804
|
+
binding = void 0;
|
|
1805
|
+
rotatedContextEngineBinding = true;
|
|
1806
|
+
}
|
|
1685
1807
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
}))
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
}
|
|
1808
|
+
if (binding?.threadId && binding.userMcpServersFingerprint !== userMcpServersFingerprint) {
|
|
1809
|
+
embeddedAgentLog.debug("codex app-server user MCP config changed; starting a new thread", { threadId: binding.threadId });
|
|
1810
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1811
|
+
binding = void 0;
|
|
1812
|
+
}
|
|
1813
|
+
if (binding?.threadId && binding.environmentSelectionFingerprint !== environmentSelectionFingerprint) {
|
|
1814
|
+
embeddedAgentLog.debug("codex app-server environment selection changed; starting a new thread", { threadId: binding.threadId });
|
|
1815
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1816
|
+
binding = void 0;
|
|
1817
|
+
}
|
|
1818
|
+
if (binding?.threadId && (binding.networkProxyConfigFingerprint !== networkProxyConfigFingerprint || binding.networkProxyProfileName !== params.appServer.networkProxy?.profileName)) {
|
|
1819
|
+
embeddedAgentLog.debug("codex app-server network proxy config changed; starting a new thread", { threadId: binding.threadId });
|
|
1820
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1821
|
+
binding = void 0;
|
|
1822
|
+
}
|
|
1823
|
+
if (binding?.threadId) {
|
|
1824
|
+
let pluginBindingStale = isCodexPluginThreadBindingStale({
|
|
1825
|
+
codexPluginsEnabled: params.pluginThreadConfig?.enabled ?? false,
|
|
1826
|
+
bindingFingerprint: binding.pluginAppsFingerprint,
|
|
1827
|
+
bindingInputFingerprint: binding.pluginAppsInputFingerprint,
|
|
1828
|
+
currentInputFingerprint: params.pluginThreadConfig?.inputFingerprint,
|
|
1829
|
+
hasBindingPolicyContext: Boolean(binding.pluginAppPolicyContext)
|
|
1830
|
+
});
|
|
1831
|
+
if (!pluginBindingStale && shouldRecheckRecoverablePluginBinding({
|
|
1832
|
+
binding,
|
|
1833
|
+
pluginThreadConfig: params.pluginThreadConfig
|
|
1834
|
+
})) try {
|
|
1835
|
+
prebuiltPluginThreadConfig = await lifecycleTiming.measure("plugin-config-recovery", () => params.pluginThreadConfig?.build());
|
|
1836
|
+
pluginBindingStale = prebuiltPluginThreadConfig?.fingerprint !== binding.pluginAppsFingerprint;
|
|
1837
|
+
} catch (error) {
|
|
1838
|
+
embeddedAgentLog.warn("codex app-server plugin app config recovery check failed", {
|
|
1839
|
+
error,
|
|
1840
|
+
threadId: binding.threadId
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
if (pluginBindingStale) {
|
|
1844
|
+
embeddedAgentLog.debug("codex app-server plugin app config changed; starting a new thread", { threadId: binding.threadId });
|
|
1845
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1846
|
+
binding = void 0;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
if (binding?.threadId) {
|
|
1850
|
+
if (binding.dynamicToolsFingerprint && params.dynamicTools.length > 0 && binding.dynamicToolsContainDeferred !== dynamicToolsContainDeferred && (binding.dynamicToolsContainDeferred !== void 0 || !dynamicToolsContainDeferred)) {
|
|
1851
|
+
embeddedAgentLog.debug("codex app-server dynamic tool loading changed; starting a new thread", { threadId: binding.threadId });
|
|
1852
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1853
|
+
binding = void 0;
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1856
|
+
if (binding?.threadId) if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint)) {
|
|
1857
|
+
preserveExistingBinding = shouldStartTransientNoToolThread({
|
|
1858
|
+
previous: binding.dynamicToolsFingerprint,
|
|
1859
|
+
next: dynamicToolsFingerprint
|
|
1860
|
+
});
|
|
1861
|
+
if (preserveExistingBinding) embeddedAgentLog.debug("codex app-server dynamic tools unavailable for turn; starting transient thread", { threadId: binding.threadId });
|
|
1862
|
+
else {
|
|
1863
|
+
embeddedAgentLog.debug("codex app-server dynamic tool catalog changed; starting a new thread", { threadId: binding.threadId });
|
|
1864
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1865
|
+
}
|
|
1866
|
+
} else {
|
|
1867
|
+
const resumeBinding = binding;
|
|
1868
|
+
let resumeReservation;
|
|
1869
|
+
try {
|
|
1870
|
+
const authProfileId = params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
1871
|
+
const finalConfigPatch = params.buildFinalConfigPatch?.({
|
|
1872
|
+
action: "resume",
|
|
1873
|
+
binding: resumeBinding
|
|
1874
|
+
}) ?? {
|
|
1875
|
+
configPatch: params.finalConfigPatch,
|
|
1876
|
+
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
1877
|
+
};
|
|
1878
|
+
const pluginAppsConfigPatch = params.pluginThreadConfig?.enabled && resumeBinding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(resumeBinding.pluginAppPolicyContext) : void 0;
|
|
1879
|
+
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch);
|
|
1880
|
+
const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
1881
|
+
threadId: resumeBinding.threadId,
|
|
1882
|
+
authProfileId,
|
|
1883
|
+
model: startModelSelection.model,
|
|
1884
|
+
modelProvider: startModelProvider,
|
|
1885
|
+
appServer: params.appServer,
|
|
1886
|
+
dynamicTools: params.dynamicTools,
|
|
1887
|
+
developerInstructions: params.developerInstructions,
|
|
1888
|
+
config: resumeConfig,
|
|
1889
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1890
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1891
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1892
|
+
webSearchAllowed: params.webSearchAllowed
|
|
1893
|
+
}));
|
|
1894
|
+
const requestModelProvider = typeof resumeParams.modelProvider === "string" && resumeParams.modelProvider.trim() ? resumeParams.modelProvider : void 0;
|
|
1895
|
+
throwIfAborted();
|
|
1896
|
+
resumeReservation = params.reserveResumeThread?.(resumeBinding.threadId);
|
|
1897
|
+
const response = await lifecycleTiming.measure("thread-resume-request", () => resumeCodexAppServerThread({
|
|
1898
|
+
client: params.client,
|
|
1899
|
+
abandonClient: params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client)),
|
|
1900
|
+
request: resumeParams,
|
|
1901
|
+
signal: params.signal
|
|
1902
|
+
}));
|
|
1903
|
+
throwIfAborted();
|
|
1904
|
+
const boundAuthProfileId = authProfileId;
|
|
1905
|
+
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : resumeBinding.mcpServersFingerprint;
|
|
1906
|
+
const resumePatch = {
|
|
1907
|
+
cwd: params.cwd,
|
|
1908
|
+
authProfileId: boundAuthProfileId,
|
|
1909
|
+
model: response.model ?? resumeParams.model ?? params.params.modelId,
|
|
1910
|
+
modelProvider: normalizeBindingModelProvider(boundAuthProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider),
|
|
1911
|
+
dynamicToolsFingerprint,
|
|
1912
|
+
dynamicToolsContainDeferred,
|
|
1913
|
+
webSearchThreadConfigFingerprint,
|
|
1914
|
+
userMcpServersFingerprint,
|
|
1915
|
+
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1916
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1917
|
+
networkProxyConfigFingerprint,
|
|
1918
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
|
|
1919
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1920
|
+
pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
|
|
1921
|
+
pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
|
|
1922
|
+
pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
|
|
1923
|
+
contextEngine: contextEngineBinding,
|
|
1924
|
+
environmentSelectionFingerprint
|
|
1925
|
+
};
|
|
1926
|
+
if (!await lifecycleTiming.measure("thread-resume-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
|
|
1927
|
+
kind: "patch",
|
|
1928
|
+
threadId: resumeBinding.threadId,
|
|
1929
|
+
patch: resumePatch
|
|
1930
|
+
}))) throw new CodexThreadBindingConflictError(resumeBinding.threadId, "committing a resumed thread");
|
|
1931
|
+
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
1932
|
+
sessionId: params.params.sessionId,
|
|
1933
|
+
sessionKey: params.params.sessionKey,
|
|
1934
|
+
threadId: response.thread.id,
|
|
1935
|
+
engineId: contextEngineBinding.engineId,
|
|
1936
|
+
epoch: contextEngineBinding.projection?.epoch,
|
|
1937
|
+
fingerprint: contextEngineBinding.projection?.fingerprint,
|
|
1938
|
+
action: "resumed"
|
|
1939
|
+
});
|
|
1940
|
+
lifecycleTiming.mark("thread-ready");
|
|
1941
|
+
lifecycleTiming.logSummary({
|
|
1942
|
+
runId: params.params.runId,
|
|
1943
|
+
sessionId: params.params.sessionId,
|
|
1944
|
+
sessionKey: params.params.sessionKey,
|
|
1945
|
+
threadId: response.thread.id,
|
|
1946
|
+
action: "resumed"
|
|
1947
|
+
});
|
|
1948
|
+
const activeTurnIds = readActiveCodexTurnIds(response.thread);
|
|
1949
|
+
return {
|
|
1950
|
+
...resumeBinding,
|
|
1951
|
+
threadId: response.thread.id,
|
|
1952
|
+
...resumePatch,
|
|
1953
|
+
lifecycle: {
|
|
1954
|
+
action: "resumed",
|
|
1955
|
+
...activeTurnIds.length ? { activeTurnIds } : {}
|
|
1956
|
+
}
|
|
1957
|
+
};
|
|
1958
|
+
} catch (error) {
|
|
1959
|
+
resumeReservation?.release();
|
|
1960
|
+
if (isCodexAppServerUnsafeSubscriptionError(error)) throw error;
|
|
1961
|
+
const resumeRejected = error instanceof CodexAppServerRpcError;
|
|
1962
|
+
if (!await unsubscribeCodexThreadBestEffort(params.client, {
|
|
1963
|
+
threadId: resumeBinding.threadId,
|
|
1964
|
+
timeoutMs: 5e3
|
|
1965
|
+
}) && !resumeRejected && !isCodexAppServerConnectionClosedError(error) && !params.signal?.aborted) throw new CodexAppServerUnsafeSubscriptionError("Codex thread/resume subscription cleanup failed", { cause: error });
|
|
1966
|
+
if (isCodexAppServerConnectionClosedError(error) || params.signal?.aborted) throw error;
|
|
1967
|
+
embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
|
|
1968
|
+
await clearCurrentBinding("rotating a stale thread binding");
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
1972
|
+
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
1973
|
+
configPatch: params.finalConfigPatch,
|
|
1974
|
+
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
1975
|
+
};
|
|
1976
|
+
const config = lifecycleTiming.measureSync("merge-thread-config", () => mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch, finalConfigPatch.configPatch));
|
|
1977
|
+
const startParams = lifecycleTiming.measureSync("thread-start-params", () => buildThreadStartParams(params.params, {
|
|
1978
|
+
cwd: params.cwd,
|
|
1979
|
+
dynamicTools: params.dynamicTools,
|
|
1980
|
+
appServer: params.appServer,
|
|
1981
|
+
developerInstructions: params.developerInstructions,
|
|
1982
|
+
config,
|
|
1983
|
+
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1984
|
+
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1985
|
+
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1986
|
+
webSearchAllowed: params.webSearchAllowed,
|
|
1987
|
+
environmentSelection: params.environmentSelection,
|
|
1988
|
+
model: startModelSelection.model,
|
|
1989
|
+
modelProvider: startModelProvider
|
|
1990
|
+
}));
|
|
1991
|
+
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
1992
|
+
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
1993
|
+
try {
|
|
1994
|
+
return await params.client.request("thread/start", startParams, { signal: params.signal });
|
|
1995
|
+
} catch (error) {
|
|
1996
|
+
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
1997
|
+
throw error;
|
|
1998
|
+
}
|
|
1999
|
+
}));
|
|
2000
|
+
throwIfAborted();
|
|
2001
|
+
const modelProvider = resolveCodexAppServerModelProvider({
|
|
2002
|
+
provider: params.params.provider,
|
|
2003
|
+
authProfileId: params.params.authProfileId,
|
|
2004
|
+
authProfileStore: params.params.authProfileStore,
|
|
2005
|
+
agentDir: params.params.agentDir,
|
|
2006
|
+
config: params.params.config
|
|
2007
|
+
});
|
|
2008
|
+
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
|
|
2009
|
+
if (!preserveExistingBinding) {
|
|
2010
|
+
if (!await lifecycleTiming.measure("thread-start-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
|
|
2011
|
+
kind: "set",
|
|
2012
|
+
if: { kind: "absent" },
|
|
2013
|
+
binding: {
|
|
2014
|
+
threadId: response.thread.id,
|
|
2015
|
+
cwd: params.cwd,
|
|
2016
|
+
authProfileId: params.params.authProfileId,
|
|
2017
|
+
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
2018
|
+
modelProvider: normalizeBindingModelProvider(params.params.authProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider),
|
|
2019
|
+
dynamicToolsFingerprint,
|
|
2020
|
+
dynamicToolsContainDeferred,
|
|
2021
|
+
webSearchThreadConfigFingerprint,
|
|
2022
|
+
userMcpServersFingerprint,
|
|
2023
|
+
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
2024
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
2025
|
+
networkProxyConfigFingerprint,
|
|
2026
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
2027
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
2028
|
+
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
2029
|
+
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
2030
|
+
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
2031
|
+
contextEngine: contextEngineBinding,
|
|
2032
|
+
environmentSelectionFingerprint
|
|
2033
|
+
}
|
|
2034
|
+
}))) throw new CodexThreadBindingConflictError(response.thread.id, "committing a fresh thread");
|
|
1744
2035
|
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
1745
2036
|
sessionId: params.params.sessionId,
|
|
1746
2037
|
sessionKey: params.params.sessionKey,
|
|
@@ -1748,90 +2039,18 @@ async function startOrResumeThread(params) {
|
|
|
1748
2039
|
engineId: contextEngineBinding.engineId,
|
|
1749
2040
|
epoch: contextEngineBinding.projection?.epoch,
|
|
1750
2041
|
fingerprint: contextEngineBinding.projection?.fingerprint,
|
|
1751
|
-
action: "
|
|
2042
|
+
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
1752
2043
|
});
|
|
1753
|
-
lifecycleTiming.mark("thread-ready");
|
|
1754
|
-
lifecycleTiming.logSummary({
|
|
1755
|
-
runId: params.params.runId,
|
|
1756
|
-
sessionId: params.params.sessionId,
|
|
1757
|
-
sessionKey: params.params.sessionKey,
|
|
1758
|
-
threadId: response.thread.id,
|
|
1759
|
-
action: "resumed"
|
|
1760
|
-
});
|
|
1761
|
-
const activeTurnIds = readActiveCodexTurnIds(response.thread);
|
|
1762
|
-
return {
|
|
1763
|
-
...resumeBinding,
|
|
1764
|
-
threadId: response.thread.id,
|
|
1765
|
-
cwd: params.cwd,
|
|
1766
|
-
authProfileId: boundAuthProfileId,
|
|
1767
|
-
model: response.model ?? resumeParams.model ?? params.params.modelId,
|
|
1768
|
-
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider,
|
|
1769
|
-
dynamicToolsFingerprint,
|
|
1770
|
-
dynamicToolsContainDeferred,
|
|
1771
|
-
webSearchThreadConfigFingerprint,
|
|
1772
|
-
userMcpServersFingerprint,
|
|
1773
|
-
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1774
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1775
|
-
networkProxyConfigFingerprint,
|
|
1776
|
-
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration ?? resumeBinding.nativeHookRelayGeneration,
|
|
1777
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1778
|
-
pluginAppsFingerprint: resumeBinding.pluginAppsFingerprint,
|
|
1779
|
-
pluginAppsInputFingerprint: resumeBinding.pluginAppsInputFingerprint,
|
|
1780
|
-
pluginAppPolicyContext: resumeBinding.pluginAppPolicyContext,
|
|
1781
|
-
contextEngine: contextEngineBinding,
|
|
1782
|
-
environmentSelectionFingerprint,
|
|
1783
|
-
lifecycle: {
|
|
1784
|
-
action: "resumed",
|
|
1785
|
-
...activeTurnIds.length ? { activeTurnIds } : {}
|
|
1786
|
-
}
|
|
1787
|
-
};
|
|
1788
|
-
} catch (error) {
|
|
1789
|
-
if (isCodexAppServerConnectionClosedError(error)) throw error;
|
|
1790
|
-
embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
|
|
1791
|
-
await clearCodexAppServerBinding(params.params.sessionFile);
|
|
1792
2044
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
dynamicTools: params.dynamicTools,
|
|
1803
|
-
appServer: params.appServer,
|
|
1804
|
-
developerInstructions: params.developerInstructions,
|
|
1805
|
-
config,
|
|
1806
|
-
nativeCodeModeEnabled: params.nativeCodeModeEnabled,
|
|
1807
|
-
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
1808
|
-
nativeCodeModeOnlyEnabled: params.nativeCodeModeOnlyEnabled,
|
|
1809
|
-
webSearchAllowed: params.webSearchAllowed,
|
|
1810
|
-
environmentSelection: params.environmentSelection,
|
|
1811
|
-
model: startModelSelection.model,
|
|
1812
|
-
modelProvider: startModelProvider
|
|
1813
|
-
}));
|
|
1814
|
-
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
1815
|
-
const response = assertCodexThreadStartResponse(await lifecycleTiming.measure("thread-start-request", async () => {
|
|
1816
|
-
try {
|
|
1817
|
-
return await params.client.request("thread/start", startParams, { signal: params.signal });
|
|
1818
|
-
} catch (error) {
|
|
1819
|
-
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
1820
|
-
throw error;
|
|
1821
|
-
}
|
|
1822
|
-
}));
|
|
1823
|
-
throwIfAborted();
|
|
1824
|
-
const modelProvider = resolveCodexAppServerModelProvider({
|
|
1825
|
-
provider: params.params.provider,
|
|
1826
|
-
authProfileId: params.params.authProfileId,
|
|
1827
|
-
authProfileStore: params.params.authProfileStore,
|
|
1828
|
-
agentDir: params.params.agentDir,
|
|
1829
|
-
config: params.params.config
|
|
1830
|
-
});
|
|
1831
|
-
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1832
|
-
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
|
|
1833
|
-
if (!preserveExistingBinding) {
|
|
1834
|
-
await lifecycleTiming.measure("thread-start-write-binding", () => writeCodexAppServerBinding(params.params.sessionFile, {
|
|
2045
|
+
lifecycleTiming.mark("thread-ready");
|
|
2046
|
+
lifecycleTiming.logSummary({
|
|
2047
|
+
runId: params.params.runId,
|
|
2048
|
+
sessionId: params.params.sessionId,
|
|
2049
|
+
sessionKey: params.params.sessionKey,
|
|
2050
|
+
threadId: response.thread.id,
|
|
2051
|
+
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
2052
|
+
});
|
|
2053
|
+
return {
|
|
1835
2054
|
threadId: response.thread.id,
|
|
1836
2055
|
cwd: params.cwd,
|
|
1837
2056
|
authProfileId: params.params.authProfileId,
|
|
@@ -1839,7 +2058,6 @@ async function startOrResumeThread(params) {
|
|
|
1839
2058
|
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
|
|
1840
2059
|
dynamicToolsFingerprint,
|
|
1841
2060
|
dynamicToolsContainDeferred,
|
|
1842
|
-
webSearchThreadConfigFingerprint,
|
|
1843
2061
|
userMcpServersFingerprint,
|
|
1844
2062
|
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1845
2063
|
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
@@ -1851,58 +2069,12 @@ async function startOrResumeThread(params) {
|
|
|
1851
2069
|
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
1852
2070
|
contextEngine: contextEngineBinding,
|
|
1853
2071
|
environmentSelectionFingerprint,
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
}));
|
|
1860
|
-
if (contextEngineBinding) embeddedAgentLog.info("codex app-server wrote context-engine thread binding", {
|
|
1861
|
-
sessionId: params.params.sessionId,
|
|
1862
|
-
sessionKey: params.params.sessionKey,
|
|
1863
|
-
threadId: response.thread.id,
|
|
1864
|
-
engineId: contextEngineBinding.engineId,
|
|
1865
|
-
epoch: contextEngineBinding.projection?.epoch,
|
|
1866
|
-
fingerprint: contextEngineBinding.projection?.fingerprint,
|
|
1867
|
-
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
1868
|
-
});
|
|
1869
|
-
}
|
|
1870
|
-
lifecycleTiming.mark("thread-ready");
|
|
1871
|
-
lifecycleTiming.logSummary({
|
|
1872
|
-
runId: params.params.runId,
|
|
1873
|
-
sessionId: params.params.sessionId,
|
|
1874
|
-
sessionKey: params.params.sessionKey,
|
|
1875
|
-
threadId: response.thread.id,
|
|
1876
|
-
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
2072
|
+
lifecycle: {
|
|
2073
|
+
action: "started",
|
|
2074
|
+
...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
1877
2077
|
});
|
|
1878
|
-
return {
|
|
1879
|
-
schemaVersion: 2,
|
|
1880
|
-
threadId: response.thread.id,
|
|
1881
|
-
sessionFile: params.params.sessionFile,
|
|
1882
|
-
cwd: params.cwd,
|
|
1883
|
-
authProfileId: params.params.authProfileId,
|
|
1884
|
-
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
1885
|
-
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
|
|
1886
|
-
dynamicToolsFingerprint,
|
|
1887
|
-
dynamicToolsContainDeferred,
|
|
1888
|
-
userMcpServersFingerprint,
|
|
1889
|
-
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
1890
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
1891
|
-
networkProxyConfigFingerprint,
|
|
1892
|
-
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
1893
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
1894
|
-
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
1895
|
-
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
1896
|
-
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
1897
|
-
contextEngine: contextEngineBinding,
|
|
1898
|
-
environmentSelectionFingerprint,
|
|
1899
|
-
createdAt,
|
|
1900
|
-
updatedAt: createdAt,
|
|
1901
|
-
lifecycle: {
|
|
1902
|
-
action: "started",
|
|
1903
|
-
...rotatedContextEngineBinding ? { rotatedContextEngineBinding } : {}
|
|
1904
|
-
}
|
|
1905
|
-
};
|
|
1906
2078
|
}
|
|
1907
2079
|
function shouldRotateCodexAppServerBindingForRuntime(params) {
|
|
1908
2080
|
if (!params.current) return false;
|
|
@@ -2018,21 +2190,10 @@ function buildThreadStartParams(params, options) {
|
|
|
2018
2190
|
}),
|
|
2019
2191
|
...resolveCodexThreadEnvironmentSelection(options),
|
|
2020
2192
|
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools }),
|
|
2021
|
-
dynamicTools:
|
|
2022
|
-
experimentalRawEvents: true
|
|
2023
|
-
persistExtendedHistory: true
|
|
2193
|
+
dynamicTools: [...options.dynamicTools],
|
|
2194
|
+
experimentalRawEvents: true
|
|
2024
2195
|
};
|
|
2025
2196
|
}
|
|
2026
|
-
function toCodexThreadStartDynamicTools(dynamicTools) {
|
|
2027
|
-
return dynamicTools.flatMap((tool) => tool.type === "namespace" ? tool.tools.map((child) => toCodexLegacyDynamicTool(child, tool.name)) : [toCodexLegacyDynamicTool(tool)]);
|
|
2028
|
-
}
|
|
2029
|
-
function toCodexLegacyDynamicTool(tool, namespace) {
|
|
2030
|
-
const { type: _type, ...legacyTool } = tool;
|
|
2031
|
-
return namespace ? {
|
|
2032
|
-
...legacyTool,
|
|
2033
|
-
namespace
|
|
2034
|
-
} : legacyTool;
|
|
2035
|
-
}
|
|
2036
2197
|
function buildThreadResumeParams(params, options) {
|
|
2037
2198
|
const resolvedModelProvider = resolveCodexAppServerModelProvider({
|
|
2038
2199
|
provider: params.provider,
|
|
@@ -2065,8 +2226,7 @@ function buildThreadResumeParams(params, options) {
|
|
|
2065
2226
|
webSearchAllowed: options.webSearchAllowed,
|
|
2066
2227
|
appServer: options.appServer
|
|
2067
2228
|
}),
|
|
2068
|
-
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
2069
|
-
persistExtendedHistory: true
|
|
2229
|
+
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
2070
2230
|
};
|
|
2071
2231
|
}
|
|
2072
2232
|
function resolveCodexBindingModelProviderFallback(params) {
|
|
@@ -2313,7 +2473,7 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2313
2473
|
"You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
|
|
2314
2474
|
buildDeferredDynamicToolManifest(options.dynamicTools),
|
|
2315
2475
|
buildSkillWorkshopInstruction(options.dynamicTools),
|
|
2316
|
-
"Use Codex native `spawn_agent` for Codex subagents. Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation
|
|
2476
|
+
"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`.",
|
|
2317
2477
|
buildVisibleReplyInstruction(params, options.dynamicTools),
|
|
2318
2478
|
nativeCommandGuidance,
|
|
2319
2479
|
params.extraSystemPrompt
|
|
@@ -2365,10 +2525,15 @@ function resolveReasoningEffort(thinkLevel, modelId, supportedReasoningEfforts)
|
|
|
2365
2525
|
requested: thinkLevel,
|
|
2366
2526
|
supportedReasoningEfforts
|
|
2367
2527
|
}) ?? null;
|
|
2528
|
+
const fallbackReasoningEfforts = resolveCodexFallbackReasoningEfforts(modelId);
|
|
2529
|
+
if (fallbackReasoningEfforts) return resolveCodexSupportedReasoningEffort({
|
|
2530
|
+
requested: thinkLevel,
|
|
2531
|
+
supportedReasoningEfforts: fallbackReasoningEfforts
|
|
2532
|
+
}) ?? null;
|
|
2368
2533
|
if (thinkLevel === "minimal") return isModernCodexModel(modelId) ? "low" : "minimal";
|
|
2369
2534
|
if (thinkLevel === "low" || thinkLevel === "medium" || thinkLevel === "high" || thinkLevel === "xhigh") return thinkLevel;
|
|
2370
2535
|
if (thinkLevel === "max" && isMaxReasoningCodexModel(modelId)) return "max";
|
|
2371
2536
|
return null;
|
|
2372
2537
|
}
|
|
2373
2538
|
//#endregion
|
|
2374
|
-
export { resolveCodexContextEngineProjectionReserveTokens as A,
|
|
2539
|
+
export { resolveCodexContextEngineProjectionReserveTokens as A, buildCodexPluginThreadConfigInputFingerprint as B, isForcedPrivateQaCodexRuntime as C, fitCodexProjectedContextForTurnStart as D, resolveCodexDynamicToolsLoadingForRuntime as E, interruptCodexTurnBestEffort as F, buildCodexAppInventoryCacheKey as G, shouldBuildCodexPluginThreadConfig as H, retireCodexAppServerClientAfterTimedOutTurn as I, defaultCodexAppInventoryCache as K, unsubscribeCodexThreadBestEffort as L, CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS as M, CodexAppServerUnsafeSubscriptionError as N, projectContextEngineAssemblyForCodex as O, closeCodexStartupClientBestEffort as P, buildCodexPluginAppsConfigPatchFromPolicyContext as R, filterCodexDynamicTools as S, resolveCodexDynamicToolsLoading as T, ensureCodexPluginActivation as U, mergeCodexThreadConfigs as V, pluginReadParams as W, resolveCodexWebSearchPlan as _, buildDeveloperInstructions as a, sanitizeCodexHistoryImagePayloads as b, codexDynamicToolsFingerprint as c, resolveCodexAppServerRequestModelSelection as d, resolveCodexAppServerThreadModelSelection as f, buildCodexNativeWebSearchThreadConfig as g, startOrResumeThread as h, buildContextEngineBinding as i, CODEX_APP_SERVER_INTERRUPT_TIMEOUT_MS as j, resolveCodexContextEngineProjectionMaxChars as k, isContextEngineBindingCompatible as l, resolveReasoningEffort as m, areCodexDynamicToolFingerprintsCompatible as n, buildTurnCollaborationMode as o, resolveCodexBindingModelProviderFallback as p, buildCodexRuntimeThreadConfig as r, buildTurnStartParams as s, CODEX_NATIVE_PERSONALITY_NONE as t, resolveCodexAppServerModelProvider as u, isCodexAppServerProfilerEnabled as v, normalizeCodexDynamicToolName as w, sanitizeInlineImageDataUrl$1 as x, invalidInlineImageText as y, buildCodexPluginThreadConfig as z };
|