@openclaw/codex 2026.9.1 → 2026.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/api.js +18 -23
- package/dist/apply-B6el6hdO.js +357 -0
- package/dist/{attempt-client-cleanup-DYHcRQAa.js → attempt-client-cleanup-D6cR54TV.js} +113 -49
- package/dist/{attempt-context-DmQUNWwT.js → attempt-context-AO4DSjE0.js} +11 -9
- package/dist/{event-projector-usage-C5mcrGjp.js → attempt-notifications--H8zXR5-.js} +3 -89
- package/dist/auth-cache-key-Kq6oerr6.js +498 -0
- package/dist/auth-profile-CwzO4_RG.js +43 -0
- package/dist/auth-profile-selection-DFu9e167.js +45 -0
- package/dist/{binding-connection-E6uDsPlx.js → binding-connection-QqgcPCgk.js} +18 -3
- package/dist/{bounded-turn-wzJrwKKe.js → bounded-turn-lRpmZHFv.js} +54 -150
- package/dist/build-state-6RL6qohz.js +19 -0
- package/dist/{capabilities-dIjNVCYH.js → capabilities-BeqLPbtM.js} +2 -1
- package/dist/cli-metadata.js +1 -1
- package/dist/client-BEKu4yDx.js +3983 -0
- package/dist/command-dispatch-CisT9Jqq.js +21 -0
- package/dist/{command-formatters-CLHhEB1Q.js → command-formatters-DeTfrapr.js} +2 -2
- package/dist/{command-handlers-CTGb2csu.js → command-handlers-BfIsJEuA.js} +158 -76
- package/dist/command-rpc-BMqRQOFX.js +226 -0
- package/dist/{compact-_t0HQrUS.js → compact-CGJZ2rlb.js} +89 -49
- package/dist/config-JDfTI4VI.js +106 -0
- package/dist/{config-BhmfC1s6.js → config-options-jnIFvwxD.js} +208 -654
- package/dist/config-runtime-BWf6JLWG.js +11 -0
- package/dist/config-utils-C57JiJrU.js +69 -0
- package/dist/conversation-binding-CxOlHiix.js +889 -0
- package/dist/{plugin-marketplace-discovery-CfFlKqDl.js → conversation-control-CUCrSIQI.js} +36 -155
- package/dist/doctor-contract-api.js +8 -9
- package/dist/{dynamic-tools-BU9tmlYm.js → dynamic-tools-D-JBoaLt.js} +734 -698
- package/dist/{effective-mcp-catalog-DzYBW4h1.js → effective-mcp-catalog-J_5Ep5Um.js} +4 -3
- package/dist/{elicitation-input-DMkeki8d.js → elicitation-input-DPoK8F7w.js} +8 -22
- package/dist/ephemeral-turn-DSwzP8YY.js +101 -0
- package/dist/{event-projector-assistant-message-DHRFXKbS.js → event-projector-assistant-message-BmBM4qBV.js} +23 -27
- package/dist/event-projector-usage-CIyrYYid.js +102 -0
- package/dist/{harness-DXVZ5II5.js → harness-FR6eQgyO.js} +47 -17
- package/dist/harness.js +1 -1
- package/dist/history-rejection-B4nOXNXF.js +13 -0
- package/dist/{image-payload-sanitizer-CnfoUPCE.js → image-payload-sanitizer-tupAr-2o.js} +29 -15
- package/dist/incognito-session-uhrBF6wJ.js +15 -0
- package/dist/index.js +413 -2893
- package/dist/{isolated-completion-kg9kv4iH.js → isolated-completion-DHBefrzg.js} +12 -21
- package/dist/{managed-binary-4-Vjz5GH.js → managed-binary-DrjjRRUD.js} +17 -84
- package/dist/managed-thread-store-D1t5jh3G.js +66 -0
- package/dist/media-understanding-provider.js +4 -1
- package/dist/{model-catalog-CUM68Hkl.js → model-catalog-B_6M04Wo.js} +6 -5
- package/dist/{models-sYnEc0gd.js → models-P9k2qAof.js} +4 -4
- package/dist/native-execution-policy-umd0tgh4.js +128 -0
- package/dist/{native-subagent-monitor-CnH147jH.js → native-subagent-monitor-BbCUKRwC.js} +76 -48
- package/dist/node-cli-sessions-rj0ysJwM.js +527 -0
- package/dist/{node-exec-server.runtime-CstNHCmC.js → node-exec-server.runtime-BY_ctYnT.js} +54 -23
- package/dist/plan-BMJdMOEF.js +711 -0
- package/dist/plugin-activation-BQK-f5sV.js +133 -0
- package/dist/{thread-resume-C7pyVKeW.js → plugin-app-cache-key-CdSQzWyX.js} +5 -53
- package/dist/{plugin-approval-roundtrip-CNe-Xrjx.js → plugin-approval-roundtrip-DeW95wB5.js} +1 -1
- package/dist/plugin-inventory-CoRCzWW4.js +325 -0
- package/dist/plugin-marketplace-discovery-3akBizzF.js +134 -0
- package/dist/{plugin-metadata-cache-BFrPhb2c.js → plugin-metadata-cache-DgNfRAGA.js} +13 -9
- package/dist/protocol-CAAcVIKF.js +275 -0
- package/dist/{provider-capabilities-DW5p4k9w.js → provider-capabilities-W4tfK5Y5.js} +2 -2
- package/dist/{rate-limits-CawQJfUS.js → rate-limits-Bqnx1N4z.js} +1 -1
- package/dist/request-CGvGOpeR.js +198 -0
- package/dist/{run-attempt-D1hJO4fX.js → run-attempt-DwKds3DN.js} +1498 -1028
- package/dist/{runtime-artifact-C3jVimFn.js → runtime-artifact-T-NwqeSH.js} +28 -28
- package/dist/sandbox-guard-BcT66fWy.js +165 -0
- package/dist/{session-binding-BHuTCRZQ.js → session-binding-CKSepwIC.js} +118 -324
- package/dist/session-binding-record-CaZjYwNS.js +256 -0
- package/dist/session-catalog-HaDcp7Sv.js +1533 -0
- package/dist/session-catalog-adoption-BKLtSrwi.js +251 -0
- package/dist/session-catalog-archive-D0i2pGj2.js +56 -0
- package/dist/session-catalog-home-id-D7-aLiNt.js +190 -0
- package/dist/session-catalog-node-adoption-BUuDYY8R.js +182 -0
- package/dist/session-catalog-node-continue-CBtc4wkI.js +238 -0
- package/dist/session-catalog-parsing-CdD3JhdM.js +280 -0
- package/dist/session-catalog-provenance-DkJHo735.js +124 -0
- package/dist/{session-cli-CmFEXMAM.js → session-cli-CbgaHh0f.js} +3 -2
- package/dist/session-history-Cz-tOOYj.js +96 -0
- package/dist/{session-upstream-marker-C351-5Qw.js → session-history-import-DFN7sWRU.js} +5 -49
- package/dist/session-history-read-cxPUxyVV.js +50 -0
- package/dist/session-history-worker-runtime.js +60 -0
- package/dist/{settled-turn-context-D3SNWtUn.js → session-history.worker-CNEvGaU5.js} +75 -69
- package/dist/session-history.worker.js +2 -0
- package/dist/{session-initialization-BeWzjGiR.js → session-initialization-Jo86C60I.js} +2 -1
- package/dist/{session-permission-policy-DeWVTCMj.js → session-permission-policy-B22InTp_.js} +4 -7
- package/dist/{session-retirement-BujDjXVF.js → session-retirement-BSNG_mhf.js} +8 -7
- package/dist/session-upstream-marker-D15C9NHp.js +46 -0
- package/dist/settled-turn-context-Du6gdUDd.js +45 -0
- package/dist/{settled-turn-finalizer-BVJx4yoc.js → settled-turn-finalizer-BuLUrZl2.js} +54 -42
- package/dist/shared-client-CVhez2Uo.js +2911 -0
- package/dist/{side-question-BTZAII4l.js → side-question-DLbr2vUU.js} +135 -191
- package/dist/source-Da-h6f7e.js +518 -0
- package/dist/thread-archive-guard-D1bP04Tf.js +53 -0
- package/dist/{thread-lifecycle-DAcEJ_Om.js → thread-lifecycle-D1pzRwHB.js} +467 -977
- package/dist/{thread-ownership-zGokd8i1.js → thread-ownership-D6oASVRu.js} +15 -17
- package/dist/{thread-policy-g4KbJPzp.js → thread-policy-D8kI7yAO.js} +2 -2
- package/dist/thread-resume-T5cveyxA.js +52 -0
- package/dist/timeout-BVw8aaM1.js +33 -0
- package/dist/transcript-mirror-attestation-DibE0krH.js +89 -0
- package/dist/{transcript-mirror-DTUxFWa_.js → transcript-mirror-t5TOccct.js} +111 -379
- package/dist/{transport-stdio-CyPldczm.js → transport-stdio-BGjHoYLr.js} +80 -48
- package/dist/{upstream-session-fork-CYlBIGVt.js → upstream-session-fork-De-sLJ2r.js} +94 -166
- package/dist/{usage-Bj-rrzkR.js → usage-CPPTmBvZ.js} +4 -3
- package/dist/version-omm6IPI3.js +12 -0
- package/dist/{web-search-provider.runtime-D2FWSK4w.js → web-search-provider.runtime-CSVgQL0w.js} +3 -3
- package/openclaw.plugin.json +22 -1
- package/package.json +6 -6
- package/dist/request-DYAI5FQE.js +0 -475
- package/dist/session-catalog-yyvTWFOw.js +0 -3120
- package/dist/session-history-C5J1AKzG.js +0 -87
- package/dist/shared-client-DxcfF9KX.js +0 -7494
- package/dist/{auth-binding-Ds_QVm-X.js → auth-binding-Co2Vfcf_.js} +1 -1
|
@@ -1,482 +1,73 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
1
|
+
import { l as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced } from "./session-binding-record-CaZjYwNS.js";
|
|
2
|
+
import { f as assertCodexModelBackedReviewerEffectiveConfig, g as readCodexEffectiveConfig, h as CODEX_SESSION_OVERRIDABLE_LAYER_TYPES, n as codexSandboxPolicyForTurn } from "./config-options-jnIFvwxD.js";
|
|
3
|
+
import { S as resolveCodexPluginsPolicy, a as flattenCodexDynamicToolFunctions, c as resolveCodexAppServerLocalHomeDir, o as isJsonObject, s as resolveCodexAppServerHomeDir } from "./protocol-CAAcVIKF.js";
|
|
4
|
+
import { L as resolveCodexToolAbortTerminalReason, _ as assertCodexThreadAcceptsDirectInput, b as assertCodexThreadStartResponse, d as isCodexAppServerRequestTimeoutError, l as isCodexAppServerOverloadError, p as resolveCodexAppServerClientInstanceId, r as getCodexAppServerClientInstanceId, v as assertCodexThreadForkResponse } from "./client-BEKu4yDx.js";
|
|
5
|
+
import { i as markStartedCodexManagedThread } from "./managed-thread-store-D1t5jh3G.js";
|
|
6
|
+
import { n as withCodexAppServerThreadMutation, t as isIncognitoSessionKey } from "./incognito-session-uhrBF6wJ.js";
|
|
7
|
+
import { i as isCodexThreadReadMissingError, n as readCodexSessionMeta, r as CodexAppServerRpcError } from "./session-catalog-provenance-DkJHo735.js";
|
|
8
|
+
import { n as codexCatalogHomeId, r as projectBoundedCodexThreadHistory } from "./session-catalog-home-id-D7-aLiNt.js";
|
|
9
|
+
import { c as serializeCodexAppInventoryError, o as CodexAppInventoryCache, s as defaultCodexAppInventoryCache, t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-CdSQzWyX.js";
|
|
10
|
+
import { t as withAbortableTimeout } from "./timeout-BVw8aaM1.js";
|
|
11
|
+
import "./config-JDfTI4VI.js";
|
|
12
|
+
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-tupAr-2o.js";
|
|
13
|
+
import { t as assertCodexSessionRuntimeOwnership } from "./binding-connection-QqgcPCgk.js";
|
|
14
|
+
import { n as normalizeCodexAppServerBindingModelProvider, t as isCodexAppServerNativeAuthProfile } from "./auth-profile-CwzO4_RG.js";
|
|
15
|
+
import { C as CodexThreadBindingConflictError, G as unsubscribeCodexAppServerLiveThread, H as readCodexClientSessionMeta, P as consumeCodexAppServerLiveThread, R as isCodexAppServerClientRuntimeLive, S as CodexAdoptedThreadActiveError, U as releaseCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientByInstanceId, n as captureCodexAppServerClientLifetime, w as CodexThreadStartRequestError, z as isCodexAppServerLiveThreadClaimed } from "./shared-client-CVhez2Uo.js";
|
|
16
|
+
import { a as closeCodexStartupClientBestEffort, d as unsubscribeCodexThreadBestEffort, l as retireUnsafeCodexTurnClientBestEffort, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, r as CodexAppServerUnsafeSubscriptionError, s as isCodexAppServerUnsafeSubscriptionError } from "./attempt-client-cleanup-D6cR54TV.js";
|
|
17
|
+
import { a as readCodexPluginInventory, o as resolveOwnedAppApprovalOverrideKeys } from "./plugin-inventory-CoRCzWW4.js";
|
|
18
|
+
import { t as ensureCodexPluginActivation } from "./plugin-activation-BQK-f5sV.js";
|
|
19
|
+
import { o as resolveCodexSessionBinding, r as hashCodexAppServerBindingFingerprint } from "./session-binding-CKSepwIC.js";
|
|
20
|
+
import { i as refreshCodexThreadPolicy, n as CodexThreadPolicyHandoffError, r as assertCodexSupervisionThreadLineage, t as CodexIncognitoPolicyChangeError } from "./thread-policy-D8kI7yAO.js";
|
|
21
|
+
import { t as resumeCodexAppServerThread } from "./thread-resume-T5cveyxA.js";
|
|
22
|
+
import { r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread } from "./thread-ownership-D6oASVRu.js";
|
|
23
|
+
import "./transcript-mirror-t5TOccct.js";
|
|
10
24
|
import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
11
|
-
import fs from "node:fs/promises";
|
|
12
|
-
import path from "node:path";
|
|
13
|
-
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
14
|
-
import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
15
|
-
import { AgentHarnessPreflightError, SKILL_WORKSHOP_TOOL_NAME, buildCredentialSafetyPrompt, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, buildTemporalContextText, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, resolveMainSessionDelegationMode } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
16
|
-
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
17
25
|
import * as crypto$1 from "node:crypto";
|
|
18
26
|
import crypto, { createHash } from "node:crypto";
|
|
27
|
+
import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
28
|
+
import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
29
|
+
import path from "node:path";
|
|
19
30
|
import os from "node:os";
|
|
31
|
+
import fs from "node:fs/promises";
|
|
32
|
+
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
33
|
+
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
34
|
+
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
35
|
+
import { createStageTimingTracker, formatStageTimings } from "openclaw/plugin-sdk/time-runtime";
|
|
36
|
+
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
20
37
|
import { isDeepStrictEqual } from "node:util";
|
|
21
38
|
import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
22
|
-
import {
|
|
23
|
-
import { z } from "zod";
|
|
24
|
-
import { coerceErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
|
25
|
-
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
39
|
+
import { AgentHarnessPreflightError, SKILL_WORKSHOP_TOOL_NAME, buildCredentialSafetyPrompt, buildDelegationGuidanceSection, buildHarnessVisibleReplyGuidance, buildSkillWorkshopPromptSection, buildTemporalContextText, buildUiPresentationPrompt, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, isOpenClawRuntimeContextCustomMessage, resolveMainSessionDelegationMode } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
26
40
|
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
27
|
-
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
28
41
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
42
|
+
import { IMAGE_BLOCK_TOKENS } from "openclaw/plugin-sdk/agent-core";
|
|
29
43
|
import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
30
44
|
import { buildCodexUserMcpServersThreadConfigPatchForRun } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
31
45
|
import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
|
|
32
46
|
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-flags";
|
|
33
47
|
import { listRegisteredPluginAgentPromptGuidance } from "openclaw/plugin-sdk/plugin-runtime";
|
|
34
48
|
import { buildHostnameAllowlistPolicyFromSuffixAllowlist } from "openclaw/plugin-sdk/ssrf-policy";
|
|
35
|
-
//#region extensions/codex/src/app-server/plugin-
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const appInventory = readCachedAppInventory(params);
|
|
54
|
-
const installedPlugins = await readInstalledCodexPluginMetadata({
|
|
55
|
-
...params,
|
|
56
|
-
policy
|
|
57
|
-
});
|
|
58
|
-
const pluginCatalogs = /* @__PURE__ */ new Map();
|
|
59
|
-
const diagnostics = [];
|
|
60
|
-
const records = [];
|
|
61
|
-
if (appInventory?.state === "missing") diagnostics.push({
|
|
62
|
-
code: "app_inventory_missing",
|
|
63
|
-
message: "Cached Codex app inventory is missing; plugin apps are excluded for this setup."
|
|
64
|
-
});
|
|
65
|
-
else if (appInventory?.state === "stale") diagnostics.push({
|
|
66
|
-
code: "app_inventory_stale",
|
|
67
|
-
message: "Cached Codex app inventory is stale; using stale app readiness and refreshing."
|
|
68
|
-
});
|
|
69
|
-
for (const pluginPolicy of policy.pluginPolicies) {
|
|
70
|
-
if (!pluginPolicy.enabled && !policy.allowAllPlugins) continue;
|
|
71
|
-
let listed = installedPlugins;
|
|
72
|
-
let resolvedPlugin = findConfiguredMarketplacePlugin(listed, pluginPolicy);
|
|
73
|
-
if (!resolvedPlugin && pluginPolicy.enabled && pluginPolicy.marketplaceName !== "workspace-directory") {
|
|
74
|
-
const requestParams = buildPluginCatalogRequestParams(params, pluginPolicy.marketplaceName);
|
|
75
|
-
const catalogKey = JSON.stringify([requestParams, pluginMetadataCatalogScope(pluginPolicy.marketplaceName)]);
|
|
76
|
-
let catalog = pluginCatalogs.get(catalogKey);
|
|
77
|
-
if (!catalog) {
|
|
78
|
-
catalog = listCodexPluginMetadata(params, pluginPolicy.marketplaceName);
|
|
79
|
-
pluginCatalogs.set(catalogKey, catalog);
|
|
80
|
-
}
|
|
81
|
-
listed = await catalog;
|
|
82
|
-
resolvedPlugin = findConfiguredMarketplacePlugin(listed, pluginPolicy);
|
|
83
|
-
}
|
|
84
|
-
if (!listed.marketplaces.some((marketplace) => marketplaceMatchesConfiguredName(marketplace, pluginPolicy.marketplaceName))) {
|
|
85
|
-
diagnostics.push({
|
|
86
|
-
code: "marketplace_missing",
|
|
87
|
-
plugin: pluginPolicy,
|
|
88
|
-
message: `Codex marketplace ${pluginPolicy.marketplaceName} was not found.`
|
|
89
|
-
});
|
|
90
|
-
continue;
|
|
91
|
-
}
|
|
92
|
-
if (!resolvedPlugin) {
|
|
93
|
-
diagnostics.push({
|
|
94
|
-
code: "plugin_missing",
|
|
95
|
-
plugin: pluginPolicy,
|
|
96
|
-
message: `${pluginPolicy.pluginName} was not found in ${pluginPolicy.marketplaceName}.`
|
|
97
|
-
});
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const { summary } = resolvedPlugin;
|
|
101
|
-
const unavailableByMarketplacePolicy = summary.availability === "DISABLED_BY_ADMIN" || summary.installPolicy === "NOT_AVAILABLE";
|
|
102
|
-
if (unavailableByMarketplacePolicy) {
|
|
103
|
-
diagnostics.push({
|
|
104
|
-
code: "plugin_disabled",
|
|
105
|
-
plugin: pluginPolicy,
|
|
106
|
-
message: `${pluginPolicy.pluginName} is unavailable in ${pluginPolicy.marketplaceName}.`
|
|
107
|
-
});
|
|
108
|
-
if (!summary.installed) continue;
|
|
109
|
-
}
|
|
110
|
-
const detail = await readPluginDetail(params, marketplaceRef(resolvedPlugin.marketplace, pluginPolicy.marketplaceName), pluginPolicy, summary, diagnostics);
|
|
111
|
-
const ownedAppIds = detail?.apps.map((app) => app.id).filter(Boolean).toSorted() ?? [];
|
|
112
|
-
const appOwnership = resolveAppOwnership({
|
|
113
|
-
detail,
|
|
114
|
-
appInventory,
|
|
115
|
-
summary
|
|
116
|
-
});
|
|
117
|
-
if (appOwnership === "ambiguous") diagnostics.push({
|
|
118
|
-
code: "app_ownership_ambiguous",
|
|
119
|
-
plugin: pluginPolicy,
|
|
120
|
-
message: `${pluginPolicy.pluginName} has only display-name app matches; apps are not exposed until ownership is stable.`
|
|
121
|
-
});
|
|
122
|
-
if (summary.installed && !summary.enabled) diagnostics.push({
|
|
123
|
-
code: "plugin_disabled",
|
|
124
|
-
plugin: pluginPolicy,
|
|
125
|
-
message: `${pluginPolicy.pluginName} is installed in Codex but disabled.`
|
|
126
|
-
});
|
|
127
|
-
const apps = resolveOwnedApps({
|
|
128
|
-
pluginPolicy,
|
|
129
|
-
detail,
|
|
130
|
-
appInventory
|
|
131
|
-
});
|
|
132
|
-
records.push({
|
|
133
|
-
policy: pluginPolicy,
|
|
134
|
-
summary,
|
|
135
|
-
...detail ? { detail } : {},
|
|
136
|
-
activationRequired: pluginPolicy.enabled && (unavailableByMarketplacePolicy || !summary.installed || !summary.enabled),
|
|
137
|
-
authRequired: apps.some((app) => app.needsAuth || !app.accessible),
|
|
138
|
-
appOwnership,
|
|
139
|
-
ownedAppIds,
|
|
140
|
-
apps
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
return {
|
|
144
|
-
policy,
|
|
145
|
-
records,
|
|
146
|
-
diagnostics,
|
|
147
|
-
...appInventory ? { appInventory } : {}
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
/** Finds a configured plugin only in its authorized marketplace identity. */
|
|
151
|
-
function findCodexMarketplacePluginSummary(listed, marketplaceName, pluginName) {
|
|
152
|
-
const resolved = findConfiguredMarketplacePlugin(listed, {
|
|
153
|
-
marketplaceName,
|
|
154
|
-
pluginName
|
|
155
|
-
});
|
|
156
|
-
return resolved ? {
|
|
157
|
-
marketplace: marketplaceRef(resolved.marketplace, marketplaceName),
|
|
158
|
-
summary: resolved.summary
|
|
159
|
-
} : void 0;
|
|
160
|
-
}
|
|
161
|
-
/** Builds plugin/read or plugin/install params from a marketplace reference. */
|
|
162
|
-
function pluginReadParams(marketplace, pluginName) {
|
|
163
|
-
return {
|
|
164
|
-
...marketplace.path ? { marketplacePath: marketplace.path } : {},
|
|
165
|
-
...marketplace.remoteMarketplaceName ? { remoteMarketplaceName: marketplace.remoteMarketplaceName } : {},
|
|
166
|
-
pluginName
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
/** Returns configured plugin keys whose current metadata may still recover. */
|
|
170
|
-
function resolveRecoverableCodexPluginConfigKeys(params) {
|
|
171
|
-
return params.policy.pluginPolicies.filter((pluginPolicy) => pluginPolicy.enabled && !isSettledMissingPluginPolicy({
|
|
172
|
-
pluginPolicy,
|
|
173
|
-
metadataCache: params.metadataCache,
|
|
174
|
-
appCacheKey: params.appCacheKey,
|
|
175
|
-
configCwd: params.configCwd
|
|
176
|
-
})).map((pluginPolicy) => pluginPolicy.configKey).toSorted();
|
|
177
|
-
}
|
|
178
|
-
async function listCodexPluginMetadata(params, marketplaceName) {
|
|
179
|
-
const requestParams = buildPluginCatalogRequestParams(params, marketplaceName);
|
|
180
|
-
if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/list", requestParams);
|
|
181
|
-
return (await params.metadataCache.load({
|
|
182
|
-
appCacheKey: params.appCacheKey,
|
|
183
|
-
queryKind: "curated-global",
|
|
184
|
-
requestParams,
|
|
185
|
-
catalogScope: pluginMetadataCatalogScope(marketplaceName),
|
|
186
|
-
request: async (method, listedParams) => await params.request(method, listedParams),
|
|
187
|
-
cacheable: (response) => response.marketplaces.some((marketplace) => marketplaceMatchesConfiguredName(marketplace, marketplaceName))
|
|
188
|
-
})).response;
|
|
189
|
-
}
|
|
190
|
-
async function readInstalledCodexPluginMetadata(params) {
|
|
191
|
-
const requestParams = params.configCwd ? { cwds: [params.configCwd] } : {};
|
|
192
|
-
if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/installed", requestParams);
|
|
193
|
-
return (await params.metadataCache.load({
|
|
194
|
-
appCacheKey: params.appCacheKey,
|
|
195
|
-
queryKind: "installed",
|
|
196
|
-
requestParams,
|
|
197
|
-
request: async (method, installedParams) => await params.request(method, installedParams),
|
|
198
|
-
cacheable: (response) => params.policy.pluginPolicies.every((pluginPolicy) => {
|
|
199
|
-
if (!pluginPolicy.enabled && !params.policy.allowAllPlugins) return true;
|
|
200
|
-
return Boolean(findConfiguredMarketplacePlugin(response, pluginPolicy));
|
|
201
|
-
})
|
|
202
|
-
})).response;
|
|
203
|
-
}
|
|
204
|
-
function isSettledMissingPluginPolicy(params) {
|
|
205
|
-
const queryKind = params.pluginPolicy.marketplaceName === "workspace-directory" ? "installed" : "curated-global";
|
|
206
|
-
const requestParams = queryKind === "installed" ? params.configCwd ? { cwds: [params.configCwd] } : {} : buildPluginCatalogRequestParams(params, params.pluginPolicy.marketplaceName);
|
|
207
|
-
const listed = params.metadataCache.read(params.appCacheKey, queryKind, requestParams, queryKind === "curated-global" ? pluginMetadataCatalogScope(params.pluginPolicy.marketplaceName) : void 0)?.response;
|
|
208
|
-
if (!listed) return false;
|
|
209
|
-
return !findConfiguredMarketplacePlugin(listed, params.pluginPolicy);
|
|
210
|
-
}
|
|
211
|
-
function pluginMetadataCatalogScope(marketplaceName) {
|
|
212
|
-
return isOpenAiCuratedMarketplaceName(marketplaceName) ? void 0 : marketplaceName;
|
|
213
|
-
}
|
|
214
|
-
function buildPluginCatalogRequestParams(params, marketplaceName) {
|
|
215
|
-
const marketplaceKinds = marketplaceName === "created-by-me-remote" ? ["created-by-me-remote"] : marketplaceName.startsWith("workspace-shared-with-me") ? ["shared-with-me"] : void 0;
|
|
216
|
-
return {
|
|
217
|
-
...params.configCwd ? { cwds: [params.configCwd] } : {},
|
|
218
|
-
...marketplaceKinds ? { marketplaceKinds: [...marketplaceKinds] } : {}
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
function readCachedAppInventory(params) {
|
|
222
|
-
if (!params.appCache || !params.appCacheKey) return;
|
|
223
|
-
const request = async (method, requestParams) => await params.request(method, requestParams);
|
|
224
|
-
return params.appCache.read({
|
|
225
|
-
key: params.appCacheKey,
|
|
226
|
-
request,
|
|
227
|
-
nowMs: params.nowMs,
|
|
228
|
-
suppressRefresh: params.suppressAppInventoryRefresh
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
async function readPluginDetail(params, marketplace, pluginPolicy, summary, diagnostics) {
|
|
232
|
-
if (params.readPluginDetails === false) return;
|
|
233
|
-
if (marketplace.remoteMarketplaceName && !summary.remotePluginId) {
|
|
234
|
-
diagnostics.push({
|
|
235
|
-
code: "plugin_detail_unavailable",
|
|
236
|
-
plugin: pluginPolicy,
|
|
237
|
-
message: `${pluginPolicy.pluginName} detail unavailable: Codex did not return a remote plugin id.`
|
|
238
|
-
});
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
try {
|
|
242
|
-
return (await params.request("plugin/read", pluginReadParams(marketplace, marketplace.remoteMarketplaceName && summary.remotePluginId ? summary.remotePluginId : pluginPolicy.pluginName))).plugin;
|
|
243
|
-
} catch (error) {
|
|
244
|
-
diagnostics.push({
|
|
245
|
-
code: "plugin_detail_unavailable",
|
|
246
|
-
plugin: pluginPolicy,
|
|
247
|
-
message: `${pluginPolicy.pluginName} detail unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
248
|
-
});
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
function resolveAppOwnership(params) {
|
|
253
|
-
if (params.detail && params.detail.apps.length > 0) return "proven";
|
|
254
|
-
return (params.appInventory?.snapshot?.apps ?? []).filter((app) => app.pluginDisplayNames.some((displayName) => displayName === params.summary.name)).length > 0 ? "ambiguous" : "none";
|
|
255
|
-
}
|
|
256
|
-
function resolveOwnedApps(params) {
|
|
257
|
-
const detailApps = params.detail?.apps ?? [];
|
|
258
|
-
if (detailApps.length === 0) return [];
|
|
259
|
-
if (params.appInventory?.state === "missing") {
|
|
260
|
-
embeddedAgentLog.warn("codex plugin inventory missing app inventory for detail apps", {
|
|
261
|
-
configKey: params.pluginPolicy.configKey,
|
|
262
|
-
pluginName: params.pluginPolicy.pluginName,
|
|
263
|
-
appIds: detailApps.map((app) => app.id).toSorted()
|
|
264
|
-
});
|
|
265
|
-
return [];
|
|
266
|
-
}
|
|
267
|
-
const appInfoById = new Map((params.appInventory?.snapshot?.apps ?? []).map((app) => [app.id, app]));
|
|
268
|
-
return detailApps.map((app) => {
|
|
269
|
-
const info = appInfoById.get(app.id);
|
|
270
|
-
if (!info) return {
|
|
271
|
-
id: app.id,
|
|
272
|
-
name: app.name,
|
|
273
|
-
accessible: false,
|
|
274
|
-
enabled: false,
|
|
275
|
-
needsAuth: true
|
|
276
|
-
};
|
|
277
|
-
return Object.assign({
|
|
278
|
-
id: app.id,
|
|
279
|
-
name: app.name,
|
|
280
|
-
accessible: info.isAccessible,
|
|
281
|
-
enabled: info.isEnabled,
|
|
282
|
-
needsAuth: !info.isAccessible
|
|
283
|
-
}, resolveOwnedAppApprovalOverrideKeys(info));
|
|
284
|
-
}).toSorted((left, right) => left.id.localeCompare(right.id));
|
|
285
|
-
}
|
|
286
|
-
/** Returns current tool keys whose overrides could bypass the requested reviewer. */
|
|
287
|
-
function resolveOwnedAppApprovalOverrideKeys(app) {
|
|
288
|
-
if (!app.toolSummaries) return {};
|
|
289
|
-
const appName = app.name.trim();
|
|
290
|
-
const appNameLower = appName.toLowerCase();
|
|
291
|
-
const keys = app.toolSummaries.filter((tool) => !tool.isReadOnly).flatMap((tool) => resolveAppToolConfigKeys({
|
|
292
|
-
appName,
|
|
293
|
-
appNameLower,
|
|
294
|
-
tool
|
|
295
|
-
}));
|
|
296
|
-
return { approvalOverrideToolConfigKeys: Array.from(new Set(keys)).toSorted() };
|
|
297
|
-
}
|
|
298
|
-
function resolveAppToolConfigKeys(params) {
|
|
299
|
-
const keys = [params.tool.name];
|
|
300
|
-
if (params.tool.title) keys.push(params.tool.title);
|
|
301
|
-
if (params.appName) keys.push(`${params.appName}_${params.tool.name}`);
|
|
302
|
-
if (params.appNameLower && params.appNameLower !== params.appName) keys.push(`${params.appNameLower}_${params.tool.name}`);
|
|
303
|
-
return keys;
|
|
304
|
-
}
|
|
305
|
-
function findPluginSummary(marketplace, pluginName) {
|
|
306
|
-
const exact = marketplace.plugins.find((plugin) => plugin.id === pluginName || plugin.id === `${pluginName}@${marketplace.name}`);
|
|
307
|
-
if (exact) return exact;
|
|
308
|
-
const matches = marketplace.plugins.filter((plugin) => plugin.name === pluginName || pluginNameFromPluginId(plugin.id, marketplace.name) === pluginName);
|
|
309
|
-
return matches.length === 1 ? matches[0] : void 0;
|
|
310
|
-
}
|
|
311
|
-
function findConfiguredMarketplacePlugin(listed, plugin) {
|
|
312
|
-
if (plugin.marketplaceName === "workspace-directory") return findWorkspaceMarketplacePlugin(listed, plugin.pluginName);
|
|
313
|
-
for (const marketplace of listed.marketplaces) {
|
|
314
|
-
if (!marketplaceMatchesConfiguredName(marketplace, plugin.marketplaceName)) continue;
|
|
315
|
-
const summary = findPluginSummary(marketplace, plugin.pluginName);
|
|
316
|
-
if (summary) return {
|
|
317
|
-
marketplace,
|
|
318
|
-
summary
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
function marketplaceMatchesConfiguredName(marketplace, configuredMarketplaceName) {
|
|
323
|
-
return isOpenAiCuratedMarketplaceName(configuredMarketplaceName) ? isOpenAiCuratedMarketplace(marketplace) : marketplace.name === configuredMarketplaceName;
|
|
324
|
-
}
|
|
325
|
-
function findWorkspaceMarketplacePlugin(listed, pluginName) {
|
|
326
|
-
const marketplace = listed.marketplaces.find((entry) => entry.name === CODEX_PLUGINS_WORKSPACE_MARKETPLACE_NAME);
|
|
327
|
-
const summary = marketplace?.plugins.find((plugin) => plugin.id === pluginName);
|
|
328
|
-
return marketplace && summary ? {
|
|
329
|
-
marketplace,
|
|
330
|
-
summary
|
|
331
|
-
} : void 0;
|
|
332
|
-
}
|
|
333
|
-
function pluginNameFromPluginId(pluginId, marketplaceName) {
|
|
334
|
-
const trimmed = pluginId.trim();
|
|
335
|
-
if (!trimmed) return;
|
|
336
|
-
const marketplaceSuffix = `@${marketplaceName}`;
|
|
337
|
-
return (trimmed.endsWith(marketplaceSuffix) ? trimmed.slice(0, -marketplaceSuffix.length) : trimmed).split("/").at(-1)?.trim() || void 0;
|
|
338
|
-
}
|
|
339
|
-
function marketplaceRef(marketplace, name) {
|
|
340
|
-
return {
|
|
341
|
-
name,
|
|
342
|
-
...marketplace.path ? { path: marketplace.path } : {},
|
|
343
|
-
...!marketplace.path ? { remoteMarketplaceName: marketplace.name } : {}
|
|
344
|
-
};
|
|
345
|
-
}
|
|
346
|
-
/** True for any supported OpenAI curated marketplace wire name, matching Codex's own curated predicate. */
|
|
347
|
-
function isOpenAiCuratedMarketplace(marketplace) {
|
|
348
|
-
return isOpenAiCuratedMarketplaceName(marketplace.name);
|
|
349
|
-
}
|
|
350
|
-
/** True for all Codex wire aliases of the same OpenAI-curated catalog. */
|
|
351
|
-
function isOpenAiCuratedMarketplaceName(marketplaceName) {
|
|
352
|
-
return marketplaceName === "openai-curated" || marketplaceName === CODEX_PLUGINS_REMOTE_MARKETPLACE_NAME || marketplaceName === CODEX_PLUGINS_API_MARKETPLACE_NAME;
|
|
353
|
-
}
|
|
354
|
-
//#endregion
|
|
355
|
-
//#region extensions/codex/src/app-server/plugin-activation.ts
|
|
356
|
-
/**
|
|
357
|
-
* Activates legacy curated Codex plugins while requiring owner-managed
|
|
358
|
-
* installation for every other marketplace.
|
|
359
|
-
*/
|
|
360
|
-
/** Activates legacy curated plugins without granting install authority to other marketplaces. */
|
|
361
|
-
async function ensureCodexPluginActivation(params) {
|
|
362
|
-
if (params.identity.marketplaceName === "workspace-directory") return activationFailure(params.identity, "disabled", { message: "workspace-directory plugins must be installed and enabled outside OpenClaw before use." });
|
|
363
|
-
if (!isOpenAiCuratedMarketplaceName(params.identity.marketplaceName)) {
|
|
364
|
-
const target = params.identity.pluginName.endsWith(`@${params.identity.marketplaceName}`) ? params.identity.pluginName : `${params.identity.pluginName}@${params.identity.marketplaceName}`;
|
|
365
|
-
return activationFailure(params.identity, "disabled", { message: `${params.identity.marketplaceName} plugins must be installed and enabled by an owner before use. Run /codex plugins install ${target}.` });
|
|
366
|
-
}
|
|
367
|
-
const listed = await listCuratedCodexPluginMetadata(params);
|
|
368
|
-
const resolved = findCodexMarketplacePluginSummary(listed, params.identity.marketplaceName, params.identity.pluginName);
|
|
369
|
-
if (!resolved) {
|
|
370
|
-
if (!listed.marketplaces.some((marketplace) => isOpenAiCuratedMarketplace(marketplace))) return activationFailure(params.identity, "marketplace_missing", { message: `Codex marketplace ${CODEX_PLUGINS_MARKETPLACE_NAME} was not found.` });
|
|
371
|
-
return activationFailure(params.identity, "plugin_missing", { message: `${params.identity.pluginName} was not found in ${CODEX_PLUGINS_MARKETPLACE_NAME}.` });
|
|
372
|
-
}
|
|
373
|
-
if (resolved.marketplace.remoteMarketplaceName && !resolved.summary.remotePluginId) return activationFailure(params.identity, "plugin_missing", { message: `${params.identity.pluginName} detail unavailable: Codex did not return a remote plugin id.` });
|
|
374
|
-
if (resolved.summary.availability === "DISABLED_BY_ADMIN" || resolved.summary.installPolicy === "NOT_AVAILABLE") return activationFailure(params.identity, "disabled", { message: `${params.identity.pluginName} was disabled or made unavailable by its marketplace administrator.` });
|
|
375
|
-
if (resolved.summary.installed && resolved.summary.enabled && !params.installEvenIfActive) return {
|
|
376
|
-
identity: params.identity,
|
|
377
|
-
ok: true,
|
|
378
|
-
reason: "already_active",
|
|
379
|
-
installAttempted: false,
|
|
380
|
-
marketplace: resolved.marketplace,
|
|
381
|
-
diagnostics: []
|
|
382
|
-
};
|
|
383
|
-
const remotePluginId = resolved.marketplace.remoteMarketplaceName ? resolved.summary.remotePluginId : void 0;
|
|
384
|
-
let installResponse;
|
|
385
|
-
try {
|
|
386
|
-
installResponse = await params.request("plugin/install", pluginReadParams(resolved.marketplace, remotePluginId ?? params.identity.pluginName));
|
|
387
|
-
} catch (error) {
|
|
388
|
-
if (!(error instanceof CodexAppServerRpcError) || error.code !== -32600 || !remotePluginId || error.message !== `remote plugin ${remotePluginId} is disabled by admin` && error.message !== `remote plugin ${remotePluginId} is not available for install`) throw error;
|
|
389
|
-
return {
|
|
390
|
-
identity: params.identity,
|
|
391
|
-
ok: false,
|
|
392
|
-
reason: "install_failed",
|
|
393
|
-
installAttempted: true,
|
|
394
|
-
marketplace: resolved.marketplace,
|
|
395
|
-
diagnostics: [{ message: `Codex plugin install failed: ${coerceErrorMessage(error)}` }]
|
|
396
|
-
};
|
|
397
|
-
}
|
|
398
|
-
if (params.metadataCache && params.appCacheKey) params.metadataCache.invalidate(params.appCacheKey);
|
|
399
|
-
const refreshDiagnostics = [];
|
|
400
|
-
let refreshFailed = false;
|
|
401
|
-
try {
|
|
402
|
-
const refreshResult = await refreshCodexPluginRuntimeState({
|
|
403
|
-
request: params.request,
|
|
404
|
-
appCache: params.appCache,
|
|
405
|
-
appCacheKey: params.appCacheKey,
|
|
406
|
-
configCwd: params.configCwd,
|
|
407
|
-
metadataCache: params.metadataCache,
|
|
408
|
-
deferAppInventoryRefresh: params.deferAppInventoryRefresh,
|
|
409
|
-
targetAppIds: params.targetAppIds
|
|
410
|
-
});
|
|
411
|
-
refreshDiagnostics.push(...refreshResult.diagnostics);
|
|
412
|
-
} catch (error) {
|
|
413
|
-
refreshFailed = true;
|
|
414
|
-
refreshDiagnostics.push({ message: `Codex plugin runtime refresh failed after install: ${coerceErrorMessage(error)}` });
|
|
415
|
-
}
|
|
416
|
-
const authRequired = installResponse.appsNeedingAuth.length > 0;
|
|
417
|
-
return {
|
|
418
|
-
identity: params.identity,
|
|
419
|
-
ok: !authRequired && !refreshFailed,
|
|
420
|
-
reason: refreshFailed ? "refresh_failed" : authRequired ? "auth_required" : resolved.summary.installed && resolved.summary.enabled ? "already_active" : "installed",
|
|
421
|
-
installAttempted: true,
|
|
422
|
-
marketplace: resolved.marketplace,
|
|
423
|
-
installResponse,
|
|
424
|
-
diagnostics: [...refreshDiagnostics, ...installResponse.appsNeedingAuth.map((app) => ({ message: `${app.name} requires app authentication before plugin tools are exposed.` }))]
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
/** Forces Codex plugin, skill, hook, MCP, and app inventory refreshes after activation. */
|
|
428
|
-
async function refreshCodexPluginRuntimeState(params) {
|
|
429
|
-
const diagnostics = [];
|
|
430
|
-
await listCuratedCodexPluginMetadata(params, { forceRefetch: true });
|
|
431
|
-
await params.request("skills/list", {
|
|
432
|
-
cwds: params.configCwd ? [params.configCwd] : [],
|
|
433
|
-
forceReload: true
|
|
434
|
-
});
|
|
435
|
-
try {
|
|
436
|
-
await params.request("hooks/list", { cwds: params.configCwd ? [params.configCwd] : [] });
|
|
437
|
-
} catch (error) {
|
|
438
|
-
diagnostics.push({ message: `Codex hooks refresh skipped: ${coerceErrorMessage(error)}` });
|
|
439
|
-
}
|
|
440
|
-
await params.request("config/mcpServer/reload", void 0);
|
|
441
|
-
if (params.appCache && params.appCacheKey) {
|
|
442
|
-
params.appCache.invalidate(params.appCacheKey, "Codex plugin activation changed app inventory", void 0, params.targetAppIds);
|
|
443
|
-
if (params.deferAppInventoryRefresh) return { diagnostics };
|
|
444
|
-
const request = async (method, requestParams) => await params.request(method, requestParams);
|
|
445
|
-
try {
|
|
446
|
-
await params.appCache.refreshNow({
|
|
447
|
-
key: params.appCacheKey,
|
|
448
|
-
request,
|
|
449
|
-
forceRefetch: true,
|
|
450
|
-
targetAppIds: params.targetAppIds
|
|
451
|
-
});
|
|
452
|
-
} catch (error) {
|
|
453
|
-
diagnostics.push({ message: `Codex app inventory refresh skipped: ${coerceErrorMessage(error)}` });
|
|
49
|
+
//#region extensions/codex/src/app-server/plugin-app-approval-overrides.ts
|
|
50
|
+
/** Projects ask approvals into the native session layer without changing saved settings. */
|
|
51
|
+
function buildCodexAppApprovalOverrides(config, app) {
|
|
52
|
+
const appsRoot = config.apps;
|
|
53
|
+
const appConfig = isJsonObject(appsRoot) ? appsRoot[app.id] : void 0;
|
|
54
|
+
if (!isJsonObject(appConfig)) return {};
|
|
55
|
+
const overrides = {};
|
|
56
|
+
const keys = app.approvalOverrideToolConfigKeys;
|
|
57
|
+
for (const [section, fields] of [["tools", { approval_mode: "auto" }], ["links", {
|
|
58
|
+
approvals_reviewer: "user",
|
|
59
|
+
default_tools_approval_mode: "auto"
|
|
60
|
+
}]]) {
|
|
61
|
+
const entries = appConfig[section];
|
|
62
|
+
if (!isJsonObject(entries)) continue;
|
|
63
|
+
const projected = [];
|
|
64
|
+
for (const [name, value] of Object.entries(entries).toSorted(([left], [right]) => left.localeCompare(right))) {
|
|
65
|
+
if (!isJsonObject(value) || section === "tools" && keys && !keys.includes(name)) continue;
|
|
66
|
+
if (Object.keys(fields).some((field) => value[field] !== void 0 && value[field] !== null)) projected.push([name, fields]);
|
|
454
67
|
}
|
|
68
|
+
if (projected.length > 0) overrides[section] = Object.fromEntries(projected);
|
|
455
69
|
}
|
|
456
|
-
return
|
|
457
|
-
}
|
|
458
|
-
async function listCuratedCodexPluginMetadata(params, options = {}) {
|
|
459
|
-
const requestParams = {
|
|
460
|
-
...params.configCwd ? { cwds: [params.configCwd] } : {},
|
|
461
|
-
...options.forceRefetch ? { forceRefetch: true } : {}
|
|
462
|
-
};
|
|
463
|
-
if (!params.metadataCache || !params.appCacheKey) return await params.request("plugin/list", requestParams);
|
|
464
|
-
return (await params.metadataCache.load({
|
|
465
|
-
appCacheKey: params.appCacheKey,
|
|
466
|
-
queryKind: "curated-global",
|
|
467
|
-
requestParams,
|
|
468
|
-
request: async (method, listedParams) => await params.request(method, listedParams),
|
|
469
|
-
cacheable: (response) => response.marketplaces.some((marketplace) => isOpenAiCuratedMarketplace(marketplace))
|
|
470
|
-
})).response;
|
|
471
|
-
}
|
|
472
|
-
function activationFailure(identity, reason, diagnostic, extraDiagnostics = []) {
|
|
473
|
-
return {
|
|
474
|
-
identity,
|
|
475
|
-
ok: false,
|
|
476
|
-
reason,
|
|
477
|
-
installAttempted: false,
|
|
478
|
-
diagnostics: [diagnostic, ...extraDiagnostics]
|
|
479
|
-
};
|
|
70
|
+
return overrides;
|
|
480
71
|
}
|
|
481
72
|
//#endregion
|
|
482
73
|
//#region extensions/codex/src/app-server/plugin-thread-app-admission.ts
|
|
@@ -592,12 +183,15 @@ async function readCodexConfigForAppAdmission(params) {
|
|
|
592
183
|
return [];
|
|
593
184
|
}
|
|
594
185
|
if (!isJsonObject(layer.config)) throw new Error("Codex config/read returned an invalid layer config");
|
|
186
|
+
if (!isJsonObject(layer.name) || typeof layer.name.type !== "string") throw new Error("Codex config/read returned an invalid config layer source");
|
|
187
|
+
if (layer.config.apps !== void 0 && !CODEX_SESSION_OVERRIDABLE_LAYER_TYPES.has(layer.name.type)) throw new Error(`Codex app policy cannot override ${layer.name.type}; move app settings to a supported user or project config layer before exposing native apps`);
|
|
595
188
|
return [layer.config];
|
|
596
189
|
})
|
|
597
190
|
};
|
|
598
191
|
} catch (error) {
|
|
599
|
-
|
|
600
|
-
|
|
192
|
+
const details = serializeCodexAppInventoryError(error);
|
|
193
|
+
embeddedAgentLog.warn("codex plugin app admission config read failed", { error: details });
|
|
194
|
+
throw new Error(`Could not verify the Codex app allowlist: ${String(details.message)}. No native thread was started; resolve the native configuration error and retry.`, { cause: error });
|
|
601
195
|
}
|
|
602
196
|
}
|
|
603
197
|
function resolveCodexExplicitAppEnablement(layersHighestPrecedenceFirst, appId) {
|
|
@@ -617,7 +211,7 @@ function shouldForceRefreshCodexNotReadyPluginApps(params, policy, inventory) {
|
|
|
617
211
|
* Builds Codex thread config patches that expose only policy-approved apps
|
|
618
212
|
* for native Codex turns.
|
|
619
213
|
*/
|
|
620
|
-
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION =
|
|
214
|
+
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION = 6;
|
|
621
215
|
const CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION = 2;
|
|
622
216
|
/** Returns true when plugin config exists and thread config may need app patches. */
|
|
623
217
|
function shouldBuildCodexPluginThreadConfig(pluginConfig) {
|
|
@@ -763,20 +357,17 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
763
357
|
});
|
|
764
358
|
}
|
|
765
359
|
const accountAppsResult = policy.allowAllPlugins ? await readCodexThreadAdmissibleAccountApps(params, appCache) : { apps: [] };
|
|
360
|
+
let appAdmissionConfig;
|
|
361
|
+
const getAdmissionConfig = () => appAdmissionConfig ??= readCodexConfigForAppAdmission(params);
|
|
766
362
|
const diagnostics = [
|
|
767
363
|
...inventory.diagnostics,
|
|
768
364
|
...activationDiagnostics,
|
|
769
365
|
...accountAppsResult.diagnostic ? [accountAppsResult.diagnostic] : []
|
|
770
366
|
];
|
|
771
367
|
const provisionalAppIds = /* @__PURE__ */ new Set();
|
|
772
|
-
const apps =
|
|
773
|
-
enabled: false,
|
|
774
|
-
destructive_enabled: false,
|
|
775
|
-
open_world_enabled: false
|
|
776
|
-
} };
|
|
368
|
+
const { apps } = buildDisabledAppsConfigPatch();
|
|
777
369
|
const policyApps = {};
|
|
778
370
|
const pluginAppIds = {};
|
|
779
|
-
let configForAppAdmission;
|
|
780
371
|
const pluginOwnedAppIds = collectCodexReservedPluginAppIds({
|
|
781
372
|
policy,
|
|
782
373
|
inventory,
|
|
@@ -803,7 +394,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
803
394
|
record,
|
|
804
395
|
inventory
|
|
805
396
|
})) {
|
|
806
|
-
const admissionConfig = resolveCodexPluginAppThreadAdmission(app, inventory) === "blocked" ? void 0 : await (
|
|
397
|
+
const admissionConfig = resolveCodexPluginAppThreadAdmission(app, inventory) === "blocked" ? void 0 : await getAdmissionConfig();
|
|
807
398
|
if (!admissionConfig || resolveCodexExplicitAppEnablement(admissionConfig.layers, app.id) === false) {
|
|
808
399
|
diagnostics.push({
|
|
809
400
|
code: "app_not_ready",
|
|
@@ -812,16 +403,8 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
812
403
|
});
|
|
813
404
|
continue;
|
|
814
405
|
}
|
|
815
|
-
if (record.policy.destructiveApprovalMode === "ask" && !await clearPersistedAppToolApprovalOverrides({
|
|
816
|
-
request: params.request,
|
|
817
|
-
configCwd: params.configCwd,
|
|
818
|
-
config: admissionConfig.config,
|
|
819
|
-
plugin: record.policy,
|
|
820
|
-
app,
|
|
821
|
-
diagnostics
|
|
822
|
-
})) continue;
|
|
823
406
|
provisionalAppIds.add(app.id);
|
|
824
|
-
apps[app.id] = buildEnabledAppConfig(record.policy);
|
|
407
|
+
apps[app.id] = buildEnabledAppConfig(record.policy, record.policy.destructiveApprovalMode === "ask" ? buildCodexAppApprovalOverrides(admissionConfig.config, app) : void 0);
|
|
825
408
|
policyApps[app.id] = {
|
|
826
409
|
configKey: record.policy.configKey,
|
|
827
410
|
marketplaceName: record.policy.marketplaceName,
|
|
@@ -835,26 +418,11 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
835
418
|
}
|
|
836
419
|
for (const app of unresolvedDisabledPluginOwnership ? [] : accountAppsResult.apps) {
|
|
837
420
|
if (pluginOwnedAppIds.has(app.id)) continue;
|
|
838
|
-
|
|
839
|
-
const admissionConfig = await configForAppAdmission;
|
|
840
|
-
if (!admissionConfig) {
|
|
841
|
-
diagnostics.push({
|
|
842
|
-
code: "account_app_config_unavailable",
|
|
843
|
-
message: "Codex account app configuration was unavailable; account apps were not exposed."
|
|
844
|
-
});
|
|
845
|
-
break;
|
|
846
|
-
}
|
|
421
|
+
const admissionConfig = await getAdmissionConfig();
|
|
847
422
|
if (resolveCodexExplicitAppEnablement(admissionConfig.layers, app.id) === false) continue;
|
|
848
423
|
const accountApp = toCodexPluginOwnedAccountApp(app);
|
|
849
|
-
if (policy.destructiveApprovalMode === "ask" && !await clearPersistedAppToolApprovalOverrides({
|
|
850
|
-
request: params.request,
|
|
851
|
-
configCwd: params.configCwd,
|
|
852
|
-
config: admissionConfig.config,
|
|
853
|
-
app: accountApp,
|
|
854
|
-
diagnostics
|
|
855
|
-
})) continue;
|
|
856
424
|
provisionalAppIds.add(app.id);
|
|
857
|
-
apps[app.id] = buildEnabledAppConfig(policy);
|
|
425
|
+
apps[app.id] = buildEnabledAppConfig(policy, policy.destructiveApprovalMode === "ask" ? buildCodexAppApprovalOverrides(admissionConfig.config, accountApp) : void 0);
|
|
858
426
|
policyApps[app.id] = {
|
|
859
427
|
source: "account",
|
|
860
428
|
appName: app.name,
|
|
@@ -864,7 +432,7 @@ async function buildCodexPluginThreadConfig(params) {
|
|
|
864
432
|
mcpServerNames: []
|
|
865
433
|
};
|
|
866
434
|
}
|
|
867
|
-
const configPatch = { apps };
|
|
435
|
+
const configPatch = Object.keys(policyApps).length === 0 ? buildDisabledAppsConfigPatch() : disableUnlistedCodexApps({ apps }, (await getAdmissionConfig()).config);
|
|
868
436
|
const policyContext = buildPluginAppPolicyContext(policyApps, pluginAppIds);
|
|
869
437
|
return {
|
|
870
438
|
enabled: true,
|
|
@@ -914,36 +482,76 @@ function emptyPluginThreadConfig(params) {
|
|
|
914
482
|
};
|
|
915
483
|
}
|
|
916
484
|
function buildDisabledAppsConfigPatch() {
|
|
917
|
-
return {
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
485
|
+
return {
|
|
486
|
+
"features.apps": false,
|
|
487
|
+
apps: { _default: {
|
|
488
|
+
enabled: false,
|
|
489
|
+
destructive_enabled: false,
|
|
490
|
+
open_world_enabled: false
|
|
491
|
+
} }
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function disableUnlistedCodexApps(configPatch, nativeConfig) {
|
|
495
|
+
const apps = { ...configPatch.apps };
|
|
496
|
+
for (const id of Object.keys(isJsonObject(nativeConfig.apps) ? nativeConfig.apps : {})) if (id !== "_default" && !Object.hasOwn(apps, id)) apps[id] = { enabled: false };
|
|
497
|
+
return {
|
|
498
|
+
...configPatch,
|
|
499
|
+
apps
|
|
500
|
+
};
|
|
922
501
|
}
|
|
923
|
-
function buildEnabledAppConfig(policy) {
|
|
502
|
+
function buildEnabledAppConfig(policy, approvalOverrides = {}) {
|
|
924
503
|
return {
|
|
504
|
+
...approvalOverrides,
|
|
925
505
|
enabled: true,
|
|
926
506
|
destructive_enabled: policy.allowDestructiveActions,
|
|
927
|
-
open_world_enabled:
|
|
507
|
+
open_world_enabled: policy.allowOpenWorld !== false,
|
|
928
508
|
default_tools_approval_mode: "auto",
|
|
929
509
|
...policy.destructiveApprovalMode === "ask" ? { approvals_reviewer: "user" } : {}
|
|
930
510
|
};
|
|
931
511
|
}
|
|
932
512
|
/** Rebuilds the safe per-thread apps patch persisted with a Codex thread binding. */
|
|
933
513
|
function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
514
|
+
const disabledConfigPatch = buildDisabledAppsConfigPatch();
|
|
515
|
+
const { apps } = disabledConfigPatch;
|
|
516
|
+
for (const [appId, policy] of Object.entries(policyContext.apps).toSorted(([left], [right]) => left.localeCompare(right))) apps[appId] = buildEnabledAppConfig(policy);
|
|
517
|
+
return Object.keys(policyContext.apps).length > 0 ? { apps } : disabledConfigPatch;
|
|
518
|
+
}
|
|
519
|
+
/** Projects current ask overrides before a side thread replays its bound app policy. */
|
|
520
|
+
async function refreshCodexPluginAppApprovalPolicy(params) {
|
|
521
|
+
if (Object.keys(params.policyContext.apps).length === 0) return {
|
|
522
|
+
policyContext: params.policyContext,
|
|
523
|
+
configPatch: buildDisabledAppsConfigPatch(),
|
|
524
|
+
diagnostics: []
|
|
525
|
+
};
|
|
526
|
+
const targetApps = Object.entries(params.policyContext.apps).filter(([, app]) => app.destructiveApprovalMode === "ask").toSorted(([left], [right]) => left.localeCompare(right));
|
|
527
|
+
const targetAppIds = targetApps.map(([id]) => id);
|
|
528
|
+
const diagnostics = [];
|
|
529
|
+
const readParams = {
|
|
530
|
+
...params,
|
|
531
|
+
appCacheKey: "approval-policy-replay"
|
|
532
|
+
};
|
|
533
|
+
const [inventory, admissionConfig] = await Promise.all([targetAppIds.length > 0 ? refreshCodexPluginAppInventory(readParams, new CodexAppInventoryCache(), { targetAppIds }) : void 0, readCodexConfigForAppAdmission(readParams)]);
|
|
534
|
+
const configPatch = disableUnlistedCodexApps(buildCodexPluginAppsConfigPatchFromPolicyContext(params.policyContext), admissionConfig.config);
|
|
535
|
+
const currentApps = new Map(inventory?.apps.map((app) => [app.id, toCodexPluginOwnedAccountApp(app)]));
|
|
536
|
+
const apps = { ...params.policyContext.apps };
|
|
537
|
+
for (const [id, policy] of targetApps) {
|
|
538
|
+
const app = currentApps.get(id);
|
|
539
|
+
if (!app) diagnostics.push({
|
|
540
|
+
code: "app_not_ready",
|
|
541
|
+
message: `Could not verify current Codex app approval policy for ${id}; the app was not exposed.`
|
|
542
|
+
});
|
|
543
|
+
else {
|
|
544
|
+
configPatch.apps[id] = buildEnabledAppConfig(policy, buildCodexAppApprovalOverrides(admissionConfig.config, app));
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
delete apps[id];
|
|
548
|
+
configPatch.apps[id] = { enabled: false };
|
|
549
|
+
}
|
|
550
|
+
return {
|
|
551
|
+
policyContext: buildPluginAppPolicyContext(apps, Object.fromEntries(Object.entries(params.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]))),
|
|
552
|
+
configPatch,
|
|
553
|
+
diagnostics
|
|
945
554
|
};
|
|
946
|
-
return { apps };
|
|
947
555
|
}
|
|
948
556
|
function buildPluginAppPolicyContext(apps, pluginAppIds) {
|
|
949
557
|
return {
|
|
@@ -956,49 +564,6 @@ function buildPluginAppPolicyContext(apps, pluginAppIds) {
|
|
|
956
564
|
pluginAppIds
|
|
957
565
|
};
|
|
958
566
|
}
|
|
959
|
-
async function clearPersistedAppToolApprovalOverrides(params) {
|
|
960
|
-
try {
|
|
961
|
-
const overrideNames = readPersistedAppToolApprovalOverrideNames(params.config, params.app);
|
|
962
|
-
if (overrideNames.length === 0) return true;
|
|
963
|
-
const edits = overrideNames.map((toolName) => ({
|
|
964
|
-
keyPath: `apps.${quoteConfigKeyPathSegment(params.app.id)}.tools.${quoteConfigKeyPathSegment(toolName)}.approval_mode`,
|
|
965
|
-
value: null,
|
|
966
|
-
mergeStrategy: "replace"
|
|
967
|
-
}));
|
|
968
|
-
const response = await params.request("config/batchWrite", { edits });
|
|
969
|
-
if (!isJsonObject(response) || response.status !== "ok" && response.status !== "okOverridden") throw new Error("Codex did not confirm the approval override batch");
|
|
970
|
-
if (response.status === "okOverridden") throw new Error(`approval override for ${overrideNames.join(", ")} is controlled by another config layer`);
|
|
971
|
-
const confirmed = await params.request("config/read", {
|
|
972
|
-
includeLayers: false,
|
|
973
|
-
...params.configCwd ? { cwd: params.configCwd } : {}
|
|
974
|
-
});
|
|
975
|
-
if (!isJsonObject(confirmed) || !isJsonObject(confirmed.config)) throw new Error("Codex did not confirm effective app approval configuration");
|
|
976
|
-
const remainingOverrideNames = readPersistedAppToolApprovalOverrideNames(confirmed.config, params.app);
|
|
977
|
-
if (remainingOverrideNames.length > 0) throw new Error(`effective approval overrides remain for ${remainingOverrideNames.join(", ")}`);
|
|
978
|
-
return true;
|
|
979
|
-
} catch (error) {
|
|
980
|
-
params.diagnostics.push({
|
|
981
|
-
code: "approval_overrides_clear_failed",
|
|
982
|
-
...params.plugin ? { plugin: params.plugin } : {},
|
|
983
|
-
message: `Could not clear durable Codex app approval overrides for ${params.app.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
984
|
-
});
|
|
985
|
-
return false;
|
|
986
|
-
}
|
|
987
|
-
}
|
|
988
|
-
function readPersistedAppToolApprovalOverrideNames(config, app) {
|
|
989
|
-
const appsRoot = config.apps;
|
|
990
|
-
const appConfig = isJsonObject(appsRoot) ? appsRoot[app.id] : void 0;
|
|
991
|
-
const tools = isJsonObject(appConfig) ? appConfig.tools : void 0;
|
|
992
|
-
if (!isJsonObject(tools)) return [];
|
|
993
|
-
const keys = app.approvalOverrideToolConfigKeys;
|
|
994
|
-
return Object.entries(tools).flatMap(([name, value]) => (!keys || keys.includes(name)) && hasPersistedToolApprovalOverride(value) ? [name] : []).toSorted();
|
|
995
|
-
}
|
|
996
|
-
function hasPersistedToolApprovalOverride(value) {
|
|
997
|
-
return isJsonObject(value) && value.approval_mode !== void 0 && value.approval_mode !== null;
|
|
998
|
-
}
|
|
999
|
-
function quoteConfigKeyPathSegment(segment) {
|
|
1000
|
-
return `"${segment.replace(/["\\]/g, (char) => `\\${char}`)}"`;
|
|
1001
|
-
}
|
|
1002
567
|
function shouldWaitForInitialAppInventory(params, policy, inventory) {
|
|
1003
568
|
if (inventory.records.some((record) => record.activationRequired)) return false;
|
|
1004
569
|
return shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
@@ -1030,23 +595,32 @@ function policyFingerprint(policy) {
|
|
|
1030
595
|
};
|
|
1031
596
|
}
|
|
1032
597
|
function mergeJsonObjects(left, right) {
|
|
1033
|
-
const merged = {
|
|
598
|
+
const merged = {
|
|
599
|
+
...left,
|
|
600
|
+
...right
|
|
601
|
+
};
|
|
1034
602
|
for (const [key, value] of Object.entries(right)) {
|
|
1035
|
-
const existing =
|
|
1036
|
-
|
|
603
|
+
const existing = left[key];
|
|
604
|
+
if (Object.hasOwn(left, key) && isJsonObject(existing) && isJsonObject(value)) merged[key] = mergeJsonObjects(existing, value);
|
|
1037
605
|
}
|
|
1038
606
|
return merged;
|
|
1039
607
|
}
|
|
1040
608
|
function fingerprintJson(value) {
|
|
1041
|
-
return crypto.createHash("sha256").update(
|
|
609
|
+
return crypto.createHash("sha256").update(stringifyCodexPluginPolicy(value)).digest("hex");
|
|
1042
610
|
}
|
|
1043
|
-
function
|
|
1044
|
-
if (Array.isArray(value)) return `[${value.map((item) =>
|
|
1045
|
-
if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${
|
|
611
|
+
function stringifyCodexPluginPolicy(value) {
|
|
612
|
+
if (Array.isArray(value)) return `[${value.map((item) => stringifyCodexPluginPolicy(item)).join(",")}]`;
|
|
613
|
+
if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stringifyCodexPluginPolicy(item)}`).join(",")}}`;
|
|
1046
614
|
return JSON.stringify(value);
|
|
1047
615
|
}
|
|
1048
616
|
//#endregion
|
|
1049
617
|
//#region extensions/codex/src/app-server/context-engine-projection.ts
|
|
618
|
+
/**
|
|
619
|
+
* Projects OpenClaw context-engine assemblies into Codex prompt text while
|
|
620
|
+
* preserving safety boundaries and redacting tool payloads.
|
|
621
|
+
*/
|
|
622
|
+
/** Attachment preparation must not degrade to a prompt that silently loses the saved input. */
|
|
623
|
+
var CodexContextAttachmentError = class extends Error {};
|
|
1050
624
|
const CONTEXT_HEADER = "OpenClaw assembled context for this turn:";
|
|
1051
625
|
const CONTEXT_OPEN = "<conversation_context>";
|
|
1052
626
|
const CONTEXT_CLOSE = "</conversation_context>";
|
|
@@ -1065,16 +639,22 @@ const MIN_PROMPT_BUDGET_TOKENS = 8e3;
|
|
|
1065
639
|
function neutralizeCodexExplicitMentionSigils(text) {
|
|
1066
640
|
return text.replace(/\$(?=[A-Za-z0-9_:-])/gu, "$").replace(/\[@(?=[A-Za-z0-9_:-]+\]\()/gu, "[@");
|
|
1067
641
|
}
|
|
642
|
+
/** Hidden durable notes are context; transient runtime carriers are current-turn only. */
|
|
643
|
+
function isCodexDurableCustomMessage(message) {
|
|
644
|
+
return message.role === "custom" && message.excludeFromContext !== true && !isOpenClawRuntimeContextCustomMessage(message);
|
|
645
|
+
}
|
|
1068
646
|
/** Projects assembled OpenClaw context-engine messages into Codex prompt inputs. */
|
|
1069
|
-
function projectContextEngineAssemblyForCodex(params) {
|
|
647
|
+
async function projectContextEngineAssemblyForCodex(params) {
|
|
1070
648
|
const prompt = params.prompt.trim();
|
|
1071
|
-
const contextMessages = dropDuplicateTrailingPrompt(params.assembledMessages, prompt);
|
|
1072
649
|
const maxRenderedContextChars = normalizeRenderedContextMaxChars(params.maxRenderedContextChars);
|
|
1073
|
-
const
|
|
650
|
+
const context = await renderMessagesForCodexContext(params.assembledMessages.filter((message) => message.role !== "custom" || isCodexDurableCustomMessage(message)), {
|
|
1074
651
|
maxTextPartChars: resolveTextPartMaxChars(maxRenderedContextChars),
|
|
1075
|
-
toolPayloadMode: params.toolPayloadMode ?? "elide"
|
|
1076
|
-
|
|
1077
|
-
|
|
652
|
+
toolPayloadMode: params.toolPayloadMode ?? "elide",
|
|
653
|
+
maxRenderedContextChars,
|
|
654
|
+
prepareFileContext: params.prepareFileContext,
|
|
655
|
+
currentUserTurnIdempotencyKey: params.currentUserTurnIdempotencyKey
|
|
656
|
+
});
|
|
657
|
+
const boundedContext = context.text;
|
|
1078
658
|
const promptPrefix = boundedContext ? [
|
|
1079
659
|
CONTEXT_HEADER,
|
|
1080
660
|
CONTEXT_SAFETY_NOTE,
|
|
@@ -1092,7 +672,8 @@ function projectContextEngineAssemblyForCodex(params) {
|
|
|
1092
672
|
promptText,
|
|
1093
673
|
...promptContextRange ? { promptContextRange } : {},
|
|
1094
674
|
assembledMessages: params.assembledMessages,
|
|
1095
|
-
prePromptMessageCount: params.originalHistoryMessages.length
|
|
675
|
+
prePromptMessageCount: params.originalHistoryMessages.length,
|
|
676
|
+
...context.images.length ? { images: context.images } : {}
|
|
1096
677
|
};
|
|
1097
678
|
}
|
|
1098
679
|
/** Resolves rendered context size from a token budget and reserve. */
|
|
@@ -1180,17 +761,43 @@ function resolveProjectionPromptBudgetTokens(params) {
|
|
|
1180
761
|
const effectiveReserveTokens = Math.min(requestedReserveTokens, Math.max(0, params.contextTokenBudget - minPromptBudget));
|
|
1181
762
|
return Math.max(1, params.contextTokenBudget - effectiveReserveTokens);
|
|
1182
763
|
}
|
|
1183
|
-
function
|
|
1184
|
-
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
764
|
+
async function renderMessagesForCodexContext(messages, options) {
|
|
765
|
+
const tail = [];
|
|
766
|
+
const images = [];
|
|
767
|
+
let retainedImageChars = 0;
|
|
768
|
+
let totalChars = 0;
|
|
769
|
+
let retainedChars = 0;
|
|
770
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
771
|
+
const message = messages[index];
|
|
772
|
+
if (message.role === "user" && options.currentUserTurnIdempotencyKey && Reflect.get(message, "idempotencyKey") === options.currentUserTurnIdempotencyKey) continue;
|
|
773
|
+
const remaining = options.maxRenderedContextChars - retainedChars;
|
|
774
|
+
const files = remaining > 0 && message.role === "user" ? await options.prepareFileContext?.(message, Math.min(remaining, options.maxTextPartChars)) : void 0;
|
|
775
|
+
const imageChars = (files?.images.length ?? 0) * IMAGE_BLOCK_TOKENS * APPROX_RENDERED_CHARS_PER_TOKEN;
|
|
776
|
+
const imagesFit = imageChars < remaining;
|
|
777
|
+
const acceptedImageChars = imagesFit ? imageChars : 0;
|
|
778
|
+
const text = [
|
|
779
|
+
renderMessageBody(message, {
|
|
780
|
+
...options,
|
|
781
|
+
mediaPrepared: files !== void 0
|
|
782
|
+
}),
|
|
783
|
+
files?.text ? truncateText(files.text, options.maxTextPartChars) : void 0,
|
|
784
|
+
imageChars > 0 && !imagesFit ? "[Attachment images omitted: context budget exceeded]" : void 0
|
|
785
|
+
].filter(Boolean).join("\n\n");
|
|
786
|
+
if (!text && acceptedImageChars === 0) continue;
|
|
787
|
+
const chunk = `[${message.role}]\n${text}${totalChars > 0 ? "\n\n" : ""}`;
|
|
788
|
+
totalChars += chunk.length;
|
|
789
|
+
if (remaining > 0) {
|
|
790
|
+
const retained = neutralizeCodexExplicitMentionSigils(chunk).slice(-(remaining - acceptedImageChars));
|
|
791
|
+
tail.push(retained);
|
|
792
|
+
retainedChars += retained.length + acceptedImageChars;
|
|
793
|
+
retainedImageChars += acceptedImageChars;
|
|
794
|
+
if (imagesFit && files?.images.length) images.unshift(...files.images);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return {
|
|
798
|
+
text: truncateOlderContext(tail.toReversed().join(""), options.maxRenderedContextChars - retainedImageChars, totalChars),
|
|
799
|
+
images
|
|
800
|
+
};
|
|
1194
801
|
}
|
|
1195
802
|
function renderMessageBody(message, options) {
|
|
1196
803
|
if (message.role === "compactionSummary" || message.role === "branchSummary") return truncateText(message.summary.trim(), options.maxTextPartChars);
|
|
@@ -1204,7 +811,7 @@ function renderMessagePart(part, options) {
|
|
|
1204
811
|
const record = part;
|
|
1205
812
|
const type = typeof record.type === "string" ? record.type : void 0;
|
|
1206
813
|
if (type === "text") return typeof record.text === "string" ? truncateText(record.text.trim(), options.maxTextPartChars) : "";
|
|
1207
|
-
if (type === "image") return "[image omitted]";
|
|
814
|
+
if (type === "image") return options.mediaPrepared ? "" : "[image omitted]";
|
|
1208
815
|
if (type === "toolCall" || type === "tool_use") {
|
|
1209
816
|
const label = `tool call${typeof record.name === "string" ? `: ${record.name}` : ""}`;
|
|
1210
817
|
if (options.toolPayloadMode === "preserve") return truncateText(`${label}\n${stableJson(renderToolCallPayload(record))}`, options.maxTextPartChars);
|
|
@@ -1287,16 +894,6 @@ function stableJson(value) {
|
|
|
1287
894
|
return "[unserializable payload omitted]";
|
|
1288
895
|
}
|
|
1289
896
|
}
|
|
1290
|
-
function extractMessageText(message) {
|
|
1291
|
-
if (!hasMessageContent(message)) return "";
|
|
1292
|
-
if (typeof message.content === "string") return message.content;
|
|
1293
|
-
if (!Array.isArray(message.content)) return "";
|
|
1294
|
-
return message.content.flatMap((part) => {
|
|
1295
|
-
if (!part || typeof part !== "object" || !("type" in part)) return [];
|
|
1296
|
-
const record = part;
|
|
1297
|
-
return record.type === "text" ? [typeof record.text === "string" ? record.text : ""] : [];
|
|
1298
|
-
}).join("\n");
|
|
1299
|
-
}
|
|
1300
897
|
function hasMessageContent(message) {
|
|
1301
898
|
return "content" in message;
|
|
1302
899
|
}
|
|
@@ -1312,13 +909,13 @@ function truncateText(text, maxChars) {
|
|
|
1312
909
|
const truncated = truncateUtf16Safe(text, maxChars);
|
|
1313
910
|
return `${truncated}\n[truncated ${text.length - truncated.length} chars]`;
|
|
1314
911
|
}
|
|
1315
|
-
function truncateOlderContext(text, maxChars) {
|
|
1316
|
-
if (
|
|
912
|
+
function truncateOlderContext(text, maxChars, totalChars = text.length) {
|
|
913
|
+
if (totalChars <= maxChars) return text;
|
|
1317
914
|
if (maxChars <= 0) return "";
|
|
1318
915
|
const buildMarker = (omittedChars) => `[truncated ${omittedChars} chars from older context]\n`;
|
|
1319
|
-
let marker = buildMarker(
|
|
916
|
+
let marker = buildMarker(totalChars - maxChars);
|
|
1320
917
|
let tailChars = Math.max(0, maxChars - marker.length);
|
|
1321
|
-
marker = buildMarker(
|
|
918
|
+
marker = buildMarker(totalChars - tailChars);
|
|
1322
919
|
if (marker.length >= maxChars) return marker.slice(0, maxChars);
|
|
1323
920
|
tailChars = maxChars - marker.length;
|
|
1324
921
|
return `${marker}${sliceUtf16Safe(text, -tailChars).trimStart()}`;
|
|
@@ -1533,7 +1130,7 @@ function shouldRecheckRecoverablePluginBinding(params) {
|
|
|
1533
1130
|
//#endregion
|
|
1534
1131
|
//#region extensions/codex/src/app-server/thread-fingerprints.ts
|
|
1535
1132
|
function codexDynamicToolsFingerprint(dynamicTools) {
|
|
1536
|
-
return
|
|
1133
|
+
return hashCodexAppServerBindingFingerprint(legacyFingerprintDynamicTools(dynamicTools));
|
|
1537
1134
|
}
|
|
1538
1135
|
function codexLegacyDynamicToolsFingerprint(dynamicTools) {
|
|
1539
1136
|
return legacyFingerprintDynamicTools(dynamicTools);
|
|
@@ -1541,11 +1138,8 @@ function codexLegacyDynamicToolsFingerprint(dynamicTools) {
|
|
|
1541
1138
|
function areCodexDynamicToolFingerprintsCompatible(params) {
|
|
1542
1139
|
return areDynamicToolFingerprintsCompatible(params.previous, params.next, params.nextLegacy);
|
|
1543
1140
|
}
|
|
1544
|
-
function fingerprintDynamicTools(dynamicTools) {
|
|
1545
|
-
return hashCodexAppServerBindingFingerprint(legacyFingerprintDynamicTools(dynamicTools));
|
|
1546
|
-
}
|
|
1547
1141
|
function legacyFingerprintDynamicTools(dynamicTools) {
|
|
1548
|
-
return JSON.stringify(dynamicTools.map(
|
|
1142
|
+
return JSON.stringify(dynamicTools.map(stabilizeJsonValue).toSorted(compareJsonFingerprint));
|
|
1549
1143
|
}
|
|
1550
1144
|
function legacyFingerprintUserMcpServersConfigPatch(configPatch) {
|
|
1551
1145
|
return configPatch ? JSON.stringify(stabilizeJsonValue(configPatch)) : void 0;
|
|
@@ -1556,15 +1150,10 @@ function fingerprintUserMcpServersConfigPatch(configPatch) {
|
|
|
1556
1150
|
function redactUserMcpServersFingerprintSecrets(value) {
|
|
1557
1151
|
if (Array.isArray(value)) return value.map(redactUserMcpServersFingerprintSecrets);
|
|
1558
1152
|
if (!value || typeof value !== "object") return value;
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
continue;
|
|
1564
|
-
}
|
|
1565
|
-
next[key] = redactUserMcpServersFingerprintSecrets(entry);
|
|
1566
|
-
}
|
|
1567
|
-
return next;
|
|
1153
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
1154
|
+
if (key === "http_headers" && entry && typeof entry === "object" && !Array.isArray(entry)) return [key, Object.fromEntries(Object.entries(entry).map(([header, headerValue]) => [header, header.toLowerCase() === "authorization" ? fingerprintUserMcpServersAuthorizationHeader(headerValue) : headerValue]))];
|
|
1155
|
+
return [key, redactUserMcpServersFingerprintSecrets(entry)];
|
|
1156
|
+
}));
|
|
1568
1157
|
}
|
|
1569
1158
|
function fingerprintUserMcpServersAuthorizationHeader(value) {
|
|
1570
1159
|
return typeof value === "string" && value.length > 0 ? `<redacted:sha256:${crypto$1.createHash("sha256").update(value).digest("hex")}>` : "<redacted>";
|
|
@@ -1589,15 +1178,10 @@ function fingerprintCodexThreadConfig(request, authProfileId, dynamicToolsFinger
|
|
|
1589
1178
|
function fingerprintEnvironmentSelection(environments) {
|
|
1590
1179
|
return environments ? JSON.stringify(environments.map(stabilizeJsonValue)) : void 0;
|
|
1591
1180
|
}
|
|
1592
|
-
function fingerprintDynamicToolSpec(tool) {
|
|
1593
|
-
return stabilizeJsonValue(tool);
|
|
1594
|
-
}
|
|
1595
1181
|
function stabilizeJsonValue(value) {
|
|
1596
1182
|
if (Array.isArray(value)) return value.map(stabilizeJsonValue);
|
|
1597
1183
|
if (!isJsonObject(value)) return value;
|
|
1598
|
-
|
|
1599
|
-
for (const [key, child] of Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right))) stable[key] = stabilizeJsonValue(child);
|
|
1600
|
-
return stable;
|
|
1184
|
+
return Object.fromEntries(Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, stabilizeJsonValue(child)]));
|
|
1601
1185
|
}
|
|
1602
1186
|
function readActiveCodexTurnIds(thread) {
|
|
1603
1187
|
return (thread.turns ?? []).filter((turn) => turn.status === "inProgress").map((turn) => typeof turn.id === "string" ? turn.id : "").filter((turnId) => turnId.trim().length > 0);
|
|
@@ -1827,10 +1411,12 @@ function applyCodexNativeSkillIsolation(config, isolation) {
|
|
|
1827
1411
|
function hasCodexNativeToolCatalog(binding) {
|
|
1828
1412
|
return binding?.connectionScope === "supervision" && !binding.pendingSupervisionBranch;
|
|
1829
1413
|
}
|
|
1830
|
-
/** Pinned serde omits false deferLoading;
|
|
1414
|
+
/** Pinned serde omits empty catalogs and false deferLoading; declarations remain native-owned. */
|
|
1831
1415
|
function parseCodexNativeToolCatalog(metadata, threadId, fingerprint) {
|
|
1832
1416
|
const fail = () => /* @__PURE__ */ new Error("The canonical Codex native tool catalog is missing, corrupt, or changed; the thread is preserved. Reconnect and inspect its native metadata before retrying.");
|
|
1833
|
-
if (!isJsonObject(metadata) || metadata.id !== threadId
|
|
1417
|
+
if (!isJsonObject(metadata) || metadata.id !== threadId) throw fail();
|
|
1418
|
+
const catalog = metadata.dynamic_tools ?? [];
|
|
1419
|
+
if (!Array.isArray(catalog) || Buffer.byteLength(JSON.stringify(catalog)) > 1048576) throw fail();
|
|
1834
1420
|
const names = /* @__PURE__ */ new Set();
|
|
1835
1421
|
const namespaces = /* @__PURE__ */ new Set();
|
|
1836
1422
|
const validName = (name) => typeof name === "string" && /^[a-zA-Z0-9_-]{1,128}$/u.test(name);
|
|
@@ -1851,7 +1437,7 @@ function parseCodexNativeToolCatalog(metadata, threadId, fingerprint) {
|
|
|
1851
1437
|
...value.deferLoading === true ? { deferLoading: true } : {}
|
|
1852
1438
|
};
|
|
1853
1439
|
};
|
|
1854
|
-
const tools =
|
|
1440
|
+
const tools = catalog.map((value) => {
|
|
1855
1441
|
if (!isJsonObject(value) || value.type !== "namespace") return readFunction(value);
|
|
1856
1442
|
if (!validName(value.name) || namespaces.has(value.name) || typeof value.description !== "string" || !Array.isArray(value.tools) || !value.tools.length || Object.keys(value).some((key) => ![
|
|
1857
1443
|
"type",
|
|
@@ -1882,68 +1468,6 @@ async function loadCodexNativeToolCatalog(params) {
|
|
|
1882
1468
|
return parseCodexNativeToolCatalog(metadata, binding.threadId, binding.dynamicToolsFingerprint);
|
|
1883
1469
|
}
|
|
1884
1470
|
//#endregion
|
|
1885
|
-
//#region extensions/codex/src/app-server/managed-thread-store.ts
|
|
1886
|
-
const CODEX_MANAGED_THREAD_NAMESPACE = "app-server-managed-threads";
|
|
1887
|
-
const CODEX_MANAGED_THREAD_MAX_ENTRIES = 2e4;
|
|
1888
|
-
const managedThreadSchema = z.object({
|
|
1889
|
-
version: z.literal(1),
|
|
1890
|
-
kind: z.literal("managed-thread"),
|
|
1891
|
-
sourceHomeId: z.string().min(1),
|
|
1892
|
-
threadId: z.string().min(1),
|
|
1893
|
-
rolloutPath: z.string().min(1).optional()
|
|
1894
|
-
});
|
|
1895
|
-
async function markStartedCodexManagedThread(store, params) {
|
|
1896
|
-
if (!store) return;
|
|
1897
|
-
try {
|
|
1898
|
-
await store.mark({
|
|
1899
|
-
sourceHomeId: params.sourceHomeId,
|
|
1900
|
-
threadId: params.threadId,
|
|
1901
|
-
...params.rolloutPath ? { rolloutPath: params.rolloutPath } : {}
|
|
1902
|
-
});
|
|
1903
|
-
} catch (error) {
|
|
1904
|
-
embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
|
|
1905
|
-
}
|
|
1906
|
-
}
|
|
1907
|
-
function managedThreadStoreKey(sourceHomeId, threadId) {
|
|
1908
|
-
return `sha256:${createHash("sha256").update("openclaw:codex-managed-thread:v1\0").update(sourceHomeId).update("\0").update(threadId).digest("hex")}`;
|
|
1909
|
-
}
|
|
1910
|
-
/** Durable ownership index for Codex threads created by OpenClaw. */
|
|
1911
|
-
function createCodexManagedThreadStore(state) {
|
|
1912
|
-
return {
|
|
1913
|
-
async has(sourceHomeId, threadId) {
|
|
1914
|
-
const parsed = managedThreadSchema.safeParse(state.lookup(managedThreadStoreKey(sourceHomeId, threadId)));
|
|
1915
|
-
return parsed.success && parsed.data.sourceHomeId === sourceHomeId && parsed.data.threadId === threadId;
|
|
1916
|
-
},
|
|
1917
|
-
async mark(params) {
|
|
1918
|
-
try {
|
|
1919
|
-
const value = managedThreadSchema.parse({
|
|
1920
|
-
version: 1,
|
|
1921
|
-
kind: "managed-thread",
|
|
1922
|
-
sourceHomeId: params.sourceHomeId.trim(),
|
|
1923
|
-
threadId: params.threadId.trim(),
|
|
1924
|
-
...params.rolloutPath?.trim() ? { rolloutPath: params.rolloutPath.trim() } : {}
|
|
1925
|
-
});
|
|
1926
|
-
state.registerIfAbsent(managedThreadStoreKey(value.sourceHomeId, value.threadId), value);
|
|
1927
|
-
return true;
|
|
1928
|
-
} catch (error) {
|
|
1929
|
-
embeddedAgentLog.warn("failed to record Codex managed thread ownership", { error });
|
|
1930
|
-
return false;
|
|
1931
|
-
}
|
|
1932
|
-
},
|
|
1933
|
-
async snapshot() {
|
|
1934
|
-
const byHome = /* @__PURE__ */ new Map();
|
|
1935
|
-
for (const entry of state.entries()) {
|
|
1936
|
-
const parsed = managedThreadSchema.safeParse(entry.value);
|
|
1937
|
-
if (!parsed.success) continue;
|
|
1938
|
-
const ids = byHome.get(parsed.data.sourceHomeId) ?? /* @__PURE__ */ new Set();
|
|
1939
|
-
ids.add(parsed.data.threadId);
|
|
1940
|
-
byHome.set(parsed.data.sourceHomeId, ids);
|
|
1941
|
-
}
|
|
1942
|
-
return byHome;
|
|
1943
|
-
}
|
|
1944
|
-
};
|
|
1945
|
-
}
|
|
1946
|
-
//#endregion
|
|
1947
1471
|
//#region extensions/codex/src/app-server/thread-mcp-attestation.ts
|
|
1948
1472
|
async function attestCodexRestrictedToolSurfaceMcpServersDisabled(client, threadId, threadConfig, signal, expectedActiveServerNames = []) {
|
|
1949
1473
|
const configuredServers = threadConfig?.mcp_servers;
|
|
@@ -1982,14 +1506,13 @@ async function attestCodexRestrictedToolSurfaceMcpServersDisabled(client, thread
|
|
|
1982
1506
|
//#endregion
|
|
1983
1507
|
//#region extensions/codex/src/app-server/plugin-thread-attestation.ts
|
|
1984
1508
|
/**
|
|
1985
|
-
*
|
|
1986
|
-
* OpenClaw commits a binding or starts a turn.
|
|
1509
|
+
* Checks app availability and enforces restricted MCP surfaces before a turn.
|
|
1987
1510
|
*/
|
|
1988
1511
|
/** Every admission path checks the same surface; its lifecycle owner keeps the claim fenced. */
|
|
1989
1512
|
async function attestCodexThreadToolSurface(params) {
|
|
1990
1513
|
params.assertCurrent();
|
|
1991
1514
|
if (params.appIds.length > 0) {
|
|
1992
|
-
await params.lifecycleTiming.measure("plugin-app-attestation", () =>
|
|
1515
|
+
await params.lifecycleTiming.measure("plugin-app-attestation", () => checkCodexThreadAppAvailability(params));
|
|
1993
1516
|
params.assertCurrent();
|
|
1994
1517
|
}
|
|
1995
1518
|
if (params.restrictedToolSurface) {
|
|
@@ -2004,7 +1527,7 @@ var CodexPluginThreadAppAttestationError = class extends Error {
|
|
|
2004
1527
|
}
|
|
2005
1528
|
};
|
|
2006
1529
|
/** Reads the existing runtime snapshot with the started thread's effective app policy. */
|
|
2007
|
-
async function
|
|
1530
|
+
async function checkCodexThreadAppAvailability(params) {
|
|
2008
1531
|
const appIds = Array.from(new Set(params.appIds.filter(Boolean))).toSorted();
|
|
2009
1532
|
if (appIds.length === 0) return;
|
|
2010
1533
|
let response;
|
|
@@ -2014,8 +1537,10 @@ async function attestCodexPluginThreadApps(params) {
|
|
|
2014
1537
|
forceRefresh: false
|
|
2015
1538
|
}, { signal: params.signal });
|
|
2016
1539
|
} catch (error) {
|
|
1540
|
+
params.signal?.throwIfAborted();
|
|
2017
1541
|
throw new CodexPluginThreadAppAttestationError(`Codex could not confirm admitted apps for thread ${params.threadId}`, { cause: error });
|
|
2018
1542
|
}
|
|
1543
|
+
params.signal?.throwIfAborted();
|
|
2019
1544
|
const installedById = new Map(response.apps.map((app) => [app.id, app]));
|
|
2020
1545
|
const failures = appIds.flatMap((appId) => {
|
|
2021
1546
|
const app = installedById.get(appId);
|
|
@@ -2023,7 +1548,10 @@ async function attestCodexPluginThreadApps(params) {
|
|
|
2023
1548
|
if (!app.enabled) return [`${appId}:disabled`];
|
|
2024
1549
|
return app.callable ? [] : [`${appId}:not-callable`];
|
|
2025
1550
|
});
|
|
2026
|
-
if (failures.length > 0)
|
|
1551
|
+
if (failures.length > 0) embeddedAgentLog.warn("codex apps unavailable; continuing with remaining tools", {
|
|
1552
|
+
threadId: params.threadId,
|
|
1553
|
+
failures
|
|
1554
|
+
});
|
|
2027
1555
|
}
|
|
2028
1556
|
/** Deletes a persistent pre-turn thread; ephemeral threads can only be unsubscribed. */
|
|
2029
1557
|
async function discardUnattestedCodexPluginThread(params) {
|
|
@@ -2207,7 +1735,10 @@ function createCodexNativeHookRelay(params) {
|
|
|
2207
1735
|
}),
|
|
2208
1736
|
signal: params.signal,
|
|
2209
1737
|
runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
|
|
2210
|
-
assertActive:
|
|
1738
|
+
assertActive: () => {
|
|
1739
|
+
params.hostCapabilities.assertActive();
|
|
1740
|
+
params.assertCurrent?.();
|
|
1741
|
+
},
|
|
2211
1742
|
retention: {
|
|
2212
1743
|
readClaim: readCodexNativeChildThreadId,
|
|
2213
1744
|
shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
|
|
@@ -2523,7 +2054,6 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2523
2054
|
let hasSubagentsList = false;
|
|
2524
2055
|
let hasSessionsSend = false;
|
|
2525
2056
|
let hasSeenDirectNamespace = false;
|
|
2526
|
-
let messageToolAvailable = options.dynamicTools ? false : params.disableMessageTool !== true;
|
|
2527
2057
|
for (const spec of options.dynamicTools ?? []) {
|
|
2528
2058
|
const isDirectNamespace = spec.type === "namespace" && !hasSeenDirectNamespace && spec.name.trim() === "openclaw_direct";
|
|
2529
2059
|
if (isDirectNamespace) hasSeenDirectNamespace = true;
|
|
@@ -2540,7 +2070,6 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2540
2070
|
hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
|
|
2541
2071
|
hasSubagentsList ||= name === "subagents";
|
|
2542
2072
|
hasSessionsSend ||= name === "sessions_send";
|
|
2543
|
-
messageToolAvailable ||= name === "message";
|
|
2544
2073
|
}
|
|
2545
2074
|
}
|
|
2546
2075
|
const nativeCommandGuidance = listRegisteredPluginAgentPromptGuidance({
|
|
@@ -2570,15 +2099,11 @@ function buildDeveloperInstructions(params, options = {}) {
|
|
|
2570
2099
|
hasSubagentsList,
|
|
2571
2100
|
hasSessionsSend
|
|
2572
2101
|
}).join("\n") : void 0,
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
dashboardToolName,
|
|
2579
|
-
portalToolName
|
|
2580
|
-
} : void 0
|
|
2581
|
-
}),
|
|
2102
|
+
params.disableTools !== true && params.promptMode !== "minimal" && params.promptMode !== "none" ? buildUiPresentationPrompt({
|
|
2103
|
+
showWidgetToolName,
|
|
2104
|
+
dashboardToolName,
|
|
2105
|
+
portalToolName
|
|
2106
|
+
}) : void 0,
|
|
2582
2107
|
buildCredentialSafetyPrompt(secretsToolName),
|
|
2583
2108
|
nativeCommandGuidance,
|
|
2584
2109
|
params.extraSystemPrompt
|
|
@@ -2686,6 +2211,7 @@ function resolveCodexWebSearchPlan(params) {
|
|
|
2686
2211
|
const CODEX_CODE_MODE_THREAD_CONFIG = {
|
|
2687
2212
|
"features.code_mode": true,
|
|
2688
2213
|
"features.code_mode_only": false,
|
|
2214
|
+
"features.shell_tool": true,
|
|
2689
2215
|
"features.apply_patch_streaming_events": true,
|
|
2690
2216
|
suppress_unstable_features_warning: true
|
|
2691
2217
|
};
|
|
@@ -2712,6 +2238,7 @@ const CODEX_RING_ZERO_RESTRICTED_FEATURES = /* @__PURE__ */ new Set([
|
|
|
2712
2238
|
"code_mode",
|
|
2713
2239
|
"code_mode_only",
|
|
2714
2240
|
"computer_use",
|
|
2241
|
+
"context_management",
|
|
2715
2242
|
"current_time_reminder",
|
|
2716
2243
|
"default_mode_request_user_input",
|
|
2717
2244
|
"deferred_executor",
|
|
@@ -2764,15 +2291,6 @@ const CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES = /* @__PURE__ */ new Map([
|
|
|
2764
2291
|
["telepathy", "chronicle"],
|
|
2765
2292
|
["codex_hooks", "hooks"]
|
|
2766
2293
|
]);
|
|
2767
|
-
const CODEX_RING_ZERO_OVERRIDABLE_LAYER_TYPES = /* @__PURE__ */ new Set([
|
|
2768
|
-
"packagedDefaults",
|
|
2769
|
-
"mdm",
|
|
2770
|
-
"system",
|
|
2771
|
-
"enterpriseManaged",
|
|
2772
|
-
"user",
|
|
2773
|
-
"project",
|
|
2774
|
-
"sessionFlags"
|
|
2775
|
-
]);
|
|
2776
2294
|
/** Common deterministic start/resume/fork fields; no run resources or unsupported setters. */
|
|
2777
2295
|
function buildCodexThreadConfiguration(params, options) {
|
|
2778
2296
|
return {
|
|
@@ -2926,17 +2444,16 @@ function buildRestrictedToolConfigPatch(inheritedMcpServerNames, scheduledAppAut
|
|
|
2926
2444
|
...Object.keys(mcpServers).length > 0 ? { mcp_servers: mcpServers } : {}
|
|
2927
2445
|
};
|
|
2928
2446
|
}
|
|
2929
|
-
async function readCodexInheritedMcpServerNames(client, cwd, signal) {
|
|
2930
|
-
const response = await client
|
|
2931
|
-
cwd,
|
|
2932
|
-
includeLayers: true
|
|
2933
|
-
}, { signal });
|
|
2934
|
-
if (!isJsonObject(response) || !isJsonObject(response.config)) throw new Error("Codex config/read returned an invalid effective config");
|
|
2447
|
+
async function readCodexInheritedMcpServerNames(client, cwd, signal, effectiveConfig) {
|
|
2448
|
+
const response = effectiveConfig ?? await readCodexEffectiveConfig(client, cwd, signal);
|
|
2935
2449
|
if (!Array.isArray(response.layers)) throw new Error("Codex config/read omitted effective config layers");
|
|
2936
2450
|
for (const layer of response.layers) {
|
|
2937
2451
|
if (!isJsonObject(layer) || !isJsonObject(layer.name) || typeof layer.name.type !== "string") throw new Error("Codex config/read returned invalid effective config layers");
|
|
2938
|
-
if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm")
|
|
2939
|
-
|
|
2452
|
+
if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm") {
|
|
2453
|
+
const migrationGuidance = layer.name.type === "legacyManagedConfigTomlFromFile" ? "migrate /etc/codex/managed_config.toml to /etc/codex/requirements.toml before running restricted or isolated turns. For ChatGPT-only authentication, use allowed_login_methods = [\"chatgpt\"] in /etc/codex/requirements.toml" : "replace the legacy MDM payload with base64-encoded TOML requirements in the com.openai.codex managed preference requirements_toml_base64 before running restricted or isolated turns. For ChatGPT-only authentication, include allowed_login_methods = [\"chatgpt\"] in that TOML payload";
|
|
2454
|
+
throw new Error(`Codex restricted tool surface cannot override config layer ${layer.name.type}; ${migrationGuidance}.`);
|
|
2455
|
+
}
|
|
2456
|
+
if (!CODEX_SESSION_OVERRIDABLE_LAYER_TYPES.has(layer.name.type)) throw new Error(`Codex restricted tool surface does not recognize config layer ${layer.name.type}`);
|
|
2940
2457
|
}
|
|
2941
2458
|
const configuredServers = response.config.mcp_servers;
|
|
2942
2459
|
if (configuredServers === void 0) return [];
|
|
@@ -2968,6 +2485,7 @@ async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, opt
|
|
|
2968
2485
|
for (const [feature, enabled] of Object.entries(featureRequirements)) {
|
|
2969
2486
|
if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2970
2487
|
const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
|
|
2488
|
+
if (options.requiredNativeShell && canonicalFeature === "shell_tool" && !enabled) throw new Error("Codex native code mode requires shell_tool, but managed requirements disable it. Ask your administrator to allow the shell, or select a tool policy that disables native code mode; no automation authority was captured.");
|
|
2971
2489
|
const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
|
|
2972
2490
|
if (canonicalFeature === "hooks" && managedHooksAllowed) continue;
|
|
2973
2491
|
if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
|
|
@@ -3262,11 +2780,6 @@ function appApprovalCeiling(mode) {
|
|
|
3262
2780
|
if (mode === "allow") return "approve";
|
|
3263
2781
|
return mode === "ask" ? "prompt" : "auto";
|
|
3264
2782
|
}
|
|
3265
|
-
function stableStringify(value) {
|
|
3266
|
-
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
3267
|
-
if (value && typeof value === "object") return `{${Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, item]) => `${JSON.stringify(key)}:${stableStringify(item)}`).join(",")}}`;
|
|
3268
|
-
return JSON.stringify(value);
|
|
3269
|
-
}
|
|
3270
2783
|
/** Intersects a stored app-ID cap with current policy without admitting new apps. */
|
|
3271
2784
|
function intersectCodexPluginThreadConfigWithScheduledAuthority(config, authority, currentPolicy = {
|
|
3272
2785
|
config: {},
|
|
@@ -3296,13 +2809,17 @@ function intersectCodexPluginThreadConfigWithScheduledAuthority(config, authorit
|
|
|
3296
2809
|
};
|
|
3297
2810
|
}
|
|
3298
2811
|
const policyContext = buildPluginAppPolicyContext(apps, Object.fromEntries(Object.entries(config.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]).filter(([, ids]) => ids.length > 0)));
|
|
3299
|
-
const configPatch = buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext);
|
|
2812
|
+
const configPatch = disableUnlistedCodexApps(buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext), currentPolicy.config);
|
|
3300
2813
|
const appsPatch = asOptionalRecord(configPatch.apps);
|
|
3301
2814
|
for (const [appId, captured] of capturedById) {
|
|
3302
2815
|
const appPatch = asOptionalRecord(appsPatch?.[appId]);
|
|
3303
2816
|
if (!appPatch || !Object.hasOwn(apps, appId)) continue;
|
|
3304
2817
|
const currentApp = apps[appId];
|
|
3305
2818
|
if (!currentApp) continue;
|
|
2819
|
+
if (currentApp.destructiveApprovalMode === "ask") Object.assign(appPatch, buildCodexAppApprovalOverrides(currentPolicy.config, {
|
|
2820
|
+
id: appId,
|
|
2821
|
+
approvalOverrideToolConfigKeys: []
|
|
2822
|
+
}));
|
|
3306
2823
|
const storedAppCeiling = appApprovalCeiling(captured.destructiveApprovalMode);
|
|
3307
2824
|
const currentAppCeiling = appApprovalCeiling(defaultApprovalMode(currentApp));
|
|
3308
2825
|
const tools = currentPolicy.toolsByApp.get(appId) ?? /* @__PURE__ */ new Map();
|
|
@@ -3315,7 +2832,7 @@ function intersectCodexPluginThreadConfigWithScheduledAuthority(config, authorit
|
|
|
3315
2832
|
}];
|
|
3316
2833
|
}));
|
|
3317
2834
|
}
|
|
3318
|
-
const fingerprint = crypto.createHash("sha256").update(
|
|
2835
|
+
const fingerprint = crypto.createHash("sha256").update(stringifyCodexPluginPolicy({
|
|
3319
2836
|
version: 1,
|
|
3320
2837
|
namespace: CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE,
|
|
3321
2838
|
authority: scheduled,
|
|
@@ -3355,7 +2872,7 @@ function buildLegacyScheduledCodexAppRecoveryPrompt(params) {
|
|
|
3355
2872
|
function buildScheduledCodexAppAuthorityInputFingerprint(baseFingerprint, authority) {
|
|
3356
2873
|
const scheduled = parseScheduledCodexAppAuthority(authority);
|
|
3357
2874
|
if (!scheduled) return baseFingerprint;
|
|
3358
|
-
return crypto.createHash("sha256").update(
|
|
2875
|
+
return crypto.createHash("sha256").update(stringifyCodexPluginPolicy({
|
|
3359
2876
|
version: 1,
|
|
3360
2877
|
namespace: CODEX_SCHEDULED_APP_AUTHORITY_NAMESPACE,
|
|
3361
2878
|
baseFingerprint,
|
|
@@ -3367,67 +2884,36 @@ function buildScheduledCodexAppAuthorityInputFingerprint(baseFingerprint, author
|
|
|
3367
2884
|
const CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS = 1e3;
|
|
3368
2885
|
const CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS = 500;
|
|
3369
2886
|
function shouldWarnCodexThreadLifecycleTimingSummary(summary, options = {}) {
|
|
3370
|
-
const
|
|
3371
|
-
const
|
|
2887
|
+
const detailed = options.enabled || options.log?.isEnabled?.("trace");
|
|
2888
|
+
const totalThresholdMs = options.totalThresholdMs ?? (detailed ? CODEX_THREAD_LIFECYCLE_TIMING_WARN_TOTAL_MS : 1e4);
|
|
2889
|
+
const stageThresholdMs = options.stageThresholdMs ?? (detailed ? CODEX_THREAD_LIFECYCLE_TIMING_WARN_STAGE_MS : 5e3);
|
|
3372
2890
|
return summary.totalMs >= totalThresholdMs || summary.spans.some((span) => span.durationMs >= stageThresholdMs);
|
|
3373
2891
|
}
|
|
3374
2892
|
function formatCodexThreadLifecycleTimingSummary(params) {
|
|
3375
|
-
const spans = params.summary.spans
|
|
2893
|
+
const spans = formatStageTimings(params.summary.spans);
|
|
3376
2894
|
return `[trace:codex-app-server] thread lifecycle: runId=${params.runId} sessionId=${params.sessionId} sessionKey=${params.sessionKey ?? "unknown"} action=${params.action} totalMs=${params.summary.totalMs} stages=${spans}`;
|
|
3377
2895
|
}
|
|
3378
2896
|
function createCodexThreadLifecycleTimingTracker(options = {}) {
|
|
3379
2897
|
const log = options.log ?? embeddedAgentLog;
|
|
3380
|
-
|
|
3381
|
-
async measure(_name, run) {
|
|
3382
|
-
return await run();
|
|
3383
|
-
},
|
|
3384
|
-
measureSync(_name, run) {
|
|
3385
|
-
return run();
|
|
3386
|
-
},
|
|
3387
|
-
mark() {},
|
|
3388
|
-
logSummary() {}
|
|
3389
|
-
};
|
|
3390
|
-
const now = options.now ?? Date.now;
|
|
3391
|
-
const startedAt = now();
|
|
2898
|
+
const timing = createStageTimingTracker(options.now ?? Date.now);
|
|
3392
2899
|
let didLog = false;
|
|
3393
|
-
const spans = [];
|
|
3394
|
-
const toMs = (value) => Math.max(0, Math.round(value));
|
|
3395
|
-
const record = (name, spanStartedAt) => {
|
|
3396
|
-
const currentAt = now();
|
|
3397
|
-
spans.push({
|
|
3398
|
-
name,
|
|
3399
|
-
durationMs: toMs(currentAt - spanStartedAt),
|
|
3400
|
-
elapsedMs: toMs(currentAt - startedAt)
|
|
3401
|
-
});
|
|
3402
|
-
};
|
|
3403
|
-
const snapshot = () => ({
|
|
3404
|
-
totalMs: toMs(now() - startedAt),
|
|
3405
|
-
spans: spans.slice()
|
|
3406
|
-
});
|
|
3407
2900
|
return {
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
try {
|
|
3411
|
-
return await run();
|
|
3412
|
-
} finally {
|
|
3413
|
-
record(name, spanStartedAt);
|
|
3414
|
-
}
|
|
3415
|
-
},
|
|
3416
|
-
measureSync(name, run) {
|
|
3417
|
-
const spanStartedAt = now();
|
|
3418
|
-
try {
|
|
3419
|
-
return run();
|
|
3420
|
-
} finally {
|
|
3421
|
-
record(name, spanStartedAt);
|
|
3422
|
-
}
|
|
3423
|
-
},
|
|
2901
|
+
measure: timing.measure,
|
|
2902
|
+
measureSync: timing.measureSync,
|
|
3424
2903
|
mark(name) {
|
|
3425
|
-
|
|
2904
|
+
timing.measureSync(name, () => void 0);
|
|
3426
2905
|
},
|
|
3427
2906
|
logSummary(params) {
|
|
3428
2907
|
if (didLog) return;
|
|
3429
|
-
const
|
|
3430
|
-
const
|
|
2908
|
+
const { totalMs, stages: spans } = timing.snapshot();
|
|
2909
|
+
const summary = {
|
|
2910
|
+
totalMs,
|
|
2911
|
+
spans
|
|
2912
|
+
};
|
|
2913
|
+
const shouldWarn = shouldWarnCodexThreadLifecycleTimingSummary(summary, {
|
|
2914
|
+
...options,
|
|
2915
|
+
log
|
|
2916
|
+
});
|
|
3431
2917
|
if (!shouldWarn && !log.isEnabled?.("trace")) return;
|
|
3432
2918
|
didLog = true;
|
|
3433
2919
|
const message = formatCodexThreadLifecycleTimingSummary({
|
|
@@ -3462,7 +2948,7 @@ function resolveCodexThreadAgentDir(params) {
|
|
|
3462
2948
|
return params.agentDir ?? params.params.agentDir ?? resolveAgentDir$1(params.params.config ?? {}, agentId);
|
|
3463
2949
|
}
|
|
3464
2950
|
async function prepareCodexThreadLifecyclePreflight(params) {
|
|
3465
|
-
await assertCodexModelBackedReviewerEffectiveConfig({
|
|
2951
|
+
let effectiveConfig = await assertCodexModelBackedReviewerEffectiveConfig({
|
|
3466
2952
|
client: params.client,
|
|
3467
2953
|
approvalsReviewer: params.appServer.approvalsReviewer,
|
|
3468
2954
|
cwd: params.cwd,
|
|
@@ -3518,13 +3004,17 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
3518
3004
|
const allowConfiguredManagedHooks = params.params.pluginHarnessToolPolicyRestricted === true && !ringZeroActive && !messageOnlySourceReply && params.params.scheduledRuntimeAuthority === void 0;
|
|
3519
3005
|
const imageGenerationDenied = params.params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") === true;
|
|
3520
3006
|
if (restrictedToolSurface && params.nativeCodeModeEnabled !== false) throw new Error("Codex restricted tool surfaces require native code mode to be disabled");
|
|
3521
|
-
|
|
3522
|
-
|
|
3007
|
+
if ((restrictedToolSurface || params.nativeCodeModeEnabled !== false) && !effectiveConfig) effectiveConfig = await lifecycleTiming.measure("tool-policy-config-read", () => readCodexEffectiveConfig(params.client, params.cwd, params.signal));
|
|
3008
|
+
const restrictedToolSurfaceInheritedMcpServerNames = restrictedToolSurface ? await lifecycleTiming.measure("restricted-tool-surface-mcp-policy", () => readCodexInheritedMcpServerNames(params.client, params.cwd, params.signal, effectiveConfig)) : [];
|
|
3009
|
+
if (restrictedToolSurface || imageGenerationDenied || params.nativeCodeModeEnabled !== false) await lifecycleTiming.measure("tool-policy-config-requirements-read", () => assertCodexManagedRequirementsDoNotOverrideToolPolicy(params.client, {
|
|
3523
3010
|
restrictedToolSurface,
|
|
3011
|
+
requiredNativeShell: params.nativeCodeModeEnabled !== false,
|
|
3524
3012
|
additionalDeniedFeatures: imageGenerationDenied ? ["image_generation"] : void 0,
|
|
3525
3013
|
allowedManagedRequirementsFingerprint: readScheduledCodexAppManagedRequirementsFingerprint(params.params.scheduledRuntimeAuthority),
|
|
3526
3014
|
allowConfiguredManagedHooks
|
|
3527
3015
|
}, params.signal));
|
|
3016
|
+
const features = effectiveConfig?.config.features;
|
|
3017
|
+
if (params.nativeCodeModeEnabled !== false && isJsonObject(features) && features.shell_tool === false && !CODEX_SESSION_OVERRIDABLE_LAYER_TYPES.has(effectiveConfig?.origins?.["features.shell_tool"]?.name.type ?? "")) throw new Error("Codex native code mode requires shell_tool, but the effective shell setting cannot be overridden. Ask your administrator to allow the shell, or select a tool policy that disables native code mode; no automation authority was captured.");
|
|
3528
3018
|
const ringZeroConfigFingerprint = ringZeroActive ? fingerprintJsonObject({
|
|
3529
3019
|
version: 1,
|
|
3530
3020
|
baseInstructions: "",
|
|
@@ -3553,71 +3043,26 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
3553
3043
|
};
|
|
3554
3044
|
}
|
|
3555
3045
|
//#endregion
|
|
3556
|
-
//#region extensions/codex/src/app-server/thread-lifecycle-
|
|
3046
|
+
//#region extensions/codex/src/app-server/thread-lifecycle-io.ts
|
|
3557
3047
|
function resolveCodexThreadRolloutPath(thread) {
|
|
3558
3048
|
const rolloutPath = thread.path?.trim();
|
|
3559
3049
|
if (!rolloutPath || !path.isAbsolute(rolloutPath) || path.extname(rolloutPath) !== ".jsonl" || !path.basename(rolloutPath).includes(thread.id)) return;
|
|
3560
3050
|
return rolloutPath;
|
|
3561
3051
|
}
|
|
3562
|
-
/** Materializes the public lifecycle result after a fresh thread is durably committed. */
|
|
3563
|
-
function buildStartedCodexThreadBinding(input) {
|
|
3564
|
-
const { context, params, response, startParams } = input;
|
|
3565
|
-
return {
|
|
3566
|
-
threadId: response.thread.id,
|
|
3567
|
-
...input.clientId ? { clientId: input.clientId } : {},
|
|
3568
|
-
cwd: params.cwd,
|
|
3569
|
-
...input.rolloutPath ? { rolloutPath: input.rolloutPath } : {},
|
|
3570
|
-
authProfileId: params.params.authProfileId,
|
|
3571
|
-
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
3572
|
-
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
3573
|
-
modelProvider: response.modelProvider ?? input.startModelProvider ?? input.modelProvider,
|
|
3574
|
-
dynamicToolsFingerprint: context.dynamicToolsFingerprint,
|
|
3575
|
-
dynamicToolsContainDeferred: context.dynamicToolsContainDeferred,
|
|
3576
|
-
nativeSkillIsolationFingerprint: context.nativeSkillIsolationFingerprint,
|
|
3577
|
-
userMcpServersFingerprint: context.userMcpServersFingerprint,
|
|
3578
|
-
mcpServersFingerprint: input.nextMcpServersFingerprint,
|
|
3579
|
-
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
3580
|
-
ringZeroConfigFingerprint: context.ringZeroConfigFingerprint,
|
|
3581
|
-
ringZeroClientInstanceId: context.ringZeroClientInstanceId,
|
|
3582
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
3583
|
-
networkProxyConfigFingerprint: context.networkProxyConfigFingerprint,
|
|
3584
|
-
nativeHookRelayGeneration: input.finalConfigPatch.nativeHookRelayGeneration,
|
|
3585
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
3586
|
-
pluginAppsFingerprint: input.pluginThreadConfig?.fingerprint,
|
|
3587
|
-
pluginAppsInputFingerprint: input.pluginThreadConfig?.inputFingerprint,
|
|
3588
|
-
pluginAppPolicyContext: input.pluginThreadConfig?.policyContext,
|
|
3589
|
-
contextEngine: context.contextEngineBinding,
|
|
3590
|
-
environmentSelectionFingerprint: context.environmentSelectionFingerprint,
|
|
3591
|
-
...startParams.ephemeral ? { liveThreadEphemeralPolicy: startParams.developerInstructions } : {},
|
|
3592
|
-
...!context.preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
|
|
3593
|
-
...startParams,
|
|
3594
|
-
model: response.model ?? startParams.model ?? null,
|
|
3595
|
-
requestedModel: startParams.model ?? null,
|
|
3596
|
-
modelProvider: input.bindingModelProvider ?? null,
|
|
3597
|
-
requestedModelProvider: startParams.modelProvider ?? input.bindingModelProvider ?? null
|
|
3598
|
-
}, params.params.authProfileId, context.dynamicToolsFingerprint) } : {},
|
|
3599
|
-
lifecycle: {
|
|
3600
|
-
action: "started",
|
|
3601
|
-
...context.rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
|
|
3602
|
-
}
|
|
3603
|
-
};
|
|
3604
|
-
}
|
|
3605
|
-
//#endregion
|
|
3606
|
-
//#region extensions/codex/src/app-server/thread-lifecycle-io.ts
|
|
3607
3052
|
async function resumeExistingCodexThread(params, context) {
|
|
3608
3053
|
const { binding: resumeBinding, bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, clearCurrentBinding } = context;
|
|
3054
|
+
let acceptedConfiguration;
|
|
3055
|
+
let disposeConfiguration;
|
|
3609
3056
|
let resumeReservation;
|
|
3610
|
-
let resumeResponseAccepted = false;
|
|
3611
3057
|
let ordinaryAppConfigChanged = false;
|
|
3612
3058
|
let policyOutcome = "not-written";
|
|
3613
|
-
const assertHandoffCurrent = () => {
|
|
3614
|
-
params.params.hostCapabilities.assertActive();
|
|
3615
|
-
throwIfAborted();
|
|
3616
|
-
context.assertResumeOwnership();
|
|
3617
|
-
context.assertResumeConfiguration();
|
|
3618
|
-
};
|
|
3619
3059
|
const abandonClient = params.abandonClient ?? (() => closeCodexStartupClientBestEffort(params.client));
|
|
3620
3060
|
try {
|
|
3061
|
+
const configuration = await context.prepareResume();
|
|
3062
|
+
const assertHandoffCurrent = configuration.assertConfigured;
|
|
3063
|
+
disposeConfiguration = configuration.dispose;
|
|
3064
|
+
await context.releaseRetainedThread(configuration.assertCurrent);
|
|
3065
|
+
configuration.assertCurrent();
|
|
3621
3066
|
const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
3622
3067
|
const finalConfigPatch = context.prebuiltFinalConfigPatch ?? params.buildFinalConfigPatch?.({
|
|
3623
3068
|
action: "resume",
|
|
@@ -3626,9 +3071,8 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3626
3071
|
configPatch: params.finalConfigPatch,
|
|
3627
3072
|
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
3628
3073
|
};
|
|
3629
|
-
const pluginThreadConfig = context.prebuiltPluginThreadConfig ?? (params.pluginThreadConfig?.
|
|
3630
|
-
const
|
|
3631
|
-
const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
|
|
3074
|
+
const pluginThreadConfig = context.prebuiltPluginThreadConfig ?? (params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0);
|
|
3075
|
+
const resumeConfig = applyCodexNativeSkillIsolation(mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginThreadConfig?.configPatch, finalConfigPatch.configPatch), nativeSkillIsolation);
|
|
3632
3076
|
const resumeParams = lifecycleTiming.measureSync("thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
3633
3077
|
threadId: resumeBinding.threadId,
|
|
3634
3078
|
cwd: params.cwd,
|
|
@@ -3657,11 +3101,12 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3657
3101
|
abandonClient,
|
|
3658
3102
|
request: resumeParams,
|
|
3659
3103
|
signal: params.signal,
|
|
3660
|
-
assertCurrent:
|
|
3104
|
+
assertCurrent: configuration.assertCurrent
|
|
3661
3105
|
}));
|
|
3662
|
-
|
|
3106
|
+
acceptedConfiguration = configuration;
|
|
3663
3107
|
assertCodexThreadAcceptsDirectInput(response.thread);
|
|
3664
|
-
|
|
3108
|
+
configuration.assertConfigured();
|
|
3109
|
+
if (requestModelProvider && response.modelProvider !== requestModelProvider) throw new Error("Codex resumed a different model provider than the one selected for this turn");
|
|
3665
3110
|
const loadedPluginThreadConfig = await context.buildLoadedPluginThreadConfig?.(resumeBinding);
|
|
3666
3111
|
if (loadedPluginThreadConfig && loadedPluginThreadConfig.fingerprint !== (pluginThreadConfig?.fingerprint ?? resumeBinding.pluginAppsFingerprint)) {
|
|
3667
3112
|
ordinaryAppConfigChanged = resumeBinding.connectionScope !== "supervision" && !resumeBinding.pendingResumeConfiguration;
|
|
@@ -3695,7 +3140,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3695
3140
|
cwd: params.cwd,
|
|
3696
3141
|
rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
|
|
3697
3142
|
authProfileId,
|
|
3698
|
-
model:
|
|
3143
|
+
model: resumeParams.model ?? response.model ?? params.params.modelId,
|
|
3699
3144
|
preserveNativeModel: resumeBinding.preserveNativeModel === true ? true : void 0,
|
|
3700
3145
|
modelProvider: normalizeBindingModelProvider(authProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider),
|
|
3701
3146
|
dynamicToolsFingerprint,
|
|
@@ -3760,13 +3205,13 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3760
3205
|
};
|
|
3761
3206
|
} catch (error) {
|
|
3762
3207
|
resumeReservation?.release();
|
|
3763
|
-
if (!
|
|
3764
|
-
if (
|
|
3208
|
+
if (!acceptedConfiguration && (!(error instanceof CodexAppServerRpcError) || error.method === "thread/read" && !isCodexThreadReadMissingError(error, resumeBinding.threadId) || isCodexAppServerOverloadError(error))) throw error;
|
|
3209
|
+
if (acceptedConfiguration) {
|
|
3765
3210
|
const handoffError = error instanceof CodexThreadPolicyHandoffError || error instanceof CodexAppServerUnsafeSubscriptionError ? error : new CodexThreadPolicyHandoffError(policyOutcome, error);
|
|
3766
3211
|
const subscriptionReleased = await unsubscribeCodexThreadBestEffort(params.client, {
|
|
3767
3212
|
threadId: resumeBinding.threadId,
|
|
3768
3213
|
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS,
|
|
3769
|
-
assertCurrent:
|
|
3214
|
+
assertCurrent: acceptedConfiguration.assertCurrent
|
|
3770
3215
|
}).catch(() => false);
|
|
3771
3216
|
if (!subscriptionReleased || handoffError instanceof CodexThreadPolicyHandoffError && handoffError.outcome === "unknown") {
|
|
3772
3217
|
if (resumeBinding.connectionScope === "supervision") await retireUnsafeCodexTurnClientBestEffort(params.client, "session policy handoff");
|
|
@@ -3777,11 +3222,13 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3777
3222
|
}
|
|
3778
3223
|
}
|
|
3779
3224
|
if (!ordinaryAppConfigChanged || !subscriptionReleased) throw handoffError;
|
|
3780
|
-
|
|
3225
|
+
acceptedConfiguration.assertConfigured();
|
|
3781
3226
|
}
|
|
3782
|
-
if (resumeBinding.pendingResumeConfiguration || resumeBinding.connectionScope === "supervision" || params.signal?.aborted) throw error;
|
|
3227
|
+
if (resumeBinding.pendingResumeConfiguration || resumeBinding.preserveNativeModel || resumeBinding.connectionScope === "supervision" || params.signal?.aborted) throw error;
|
|
3783
3228
|
embeddedAgentLog.warn("codex app-server thread resume failed; starting a new thread", { error });
|
|
3784
3229
|
await clearCurrentBinding("rotating a stale thread binding");
|
|
3230
|
+
} finally {
|
|
3231
|
+
disposeConfiguration?.();
|
|
3785
3232
|
}
|
|
3786
3233
|
}
|
|
3787
3234
|
async function startFreshCodexThread(params, context) {
|
|
@@ -3812,9 +3259,18 @@ async function startFreshCodexThread(params, context) {
|
|
|
3812
3259
|
disableLoginShell: params.disableLoginShell
|
|
3813
3260
|
}));
|
|
3814
3261
|
const requestModelProvider = typeof startParams.modelProvider === "string" && startParams.modelProvider.trim() ? startParams.modelProvider : void 0;
|
|
3262
|
+
const assertCurrent = () => {
|
|
3263
|
+
throwIfAborted();
|
|
3264
|
+
params.params.hostCapabilities.assertActive();
|
|
3265
|
+
params.assertCurrent?.();
|
|
3266
|
+
};
|
|
3815
3267
|
const threadStartResponse = await lifecycleTiming.measure("thread-start-request", async () => {
|
|
3816
3268
|
try {
|
|
3817
|
-
|
|
3269
|
+
assertCurrent();
|
|
3270
|
+
return await params.client.request("thread/start", startParams, {
|
|
3271
|
+
signal: params.signal,
|
|
3272
|
+
assertCurrent
|
|
3273
|
+
});
|
|
3818
3274
|
} catch (error) {
|
|
3819
3275
|
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
3820
3276
|
throw error;
|
|
@@ -3822,10 +3278,6 @@ async function startFreshCodexThread(params, context) {
|
|
|
3822
3278
|
});
|
|
3823
3279
|
const response = assertCodexThreadStartResponse(threadStartResponse);
|
|
3824
3280
|
const provisionalAppIds = pluginThreadConfig?.provisionalAppIds;
|
|
3825
|
-
const assertCurrent = () => {
|
|
3826
|
-
throwIfAborted();
|
|
3827
|
-
params.params.hostCapabilities.assertActive();
|
|
3828
|
-
};
|
|
3829
3281
|
const rejectUncommittedThread = async (cause) => {
|
|
3830
3282
|
if (!await discardUnattestedCodexPluginThread({
|
|
3831
3283
|
client: params.client,
|
|
@@ -3862,35 +3314,38 @@ async function startFreshCodexThread(params, context) {
|
|
|
3862
3314
|
});
|
|
3863
3315
|
const bindingModelProvider = normalizeBindingModelProvider(params.params.authProfileId, response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider);
|
|
3864
3316
|
const nextMcpServersFingerprint = params.mcpServersFingerprintEvaluated === true ? params.mcpServersFingerprint : void 0;
|
|
3317
|
+
const startedBinding = {
|
|
3318
|
+
threadId: response.thread.id,
|
|
3319
|
+
...clientId ? { clientId } : {},
|
|
3320
|
+
cwd: params.cwd,
|
|
3321
|
+
...rolloutPath ? { rolloutPath } : {},
|
|
3322
|
+
authProfileId: params.params.authProfileId,
|
|
3323
|
+
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
3324
|
+
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
3325
|
+
modelProvider: bindingModelProvider,
|
|
3326
|
+
dynamicToolsFingerprint,
|
|
3327
|
+
dynamicToolsContainDeferred,
|
|
3328
|
+
nativeSkillIsolationFingerprint,
|
|
3329
|
+
userMcpServersFingerprint,
|
|
3330
|
+
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
3331
|
+
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
3332
|
+
ringZeroConfigFingerprint,
|
|
3333
|
+
ringZeroClientInstanceId,
|
|
3334
|
+
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
3335
|
+
networkProxyConfigFingerprint,
|
|
3336
|
+
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
3337
|
+
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
3338
|
+
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
3339
|
+
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
3340
|
+
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
3341
|
+
contextEngine: contextEngineBinding,
|
|
3342
|
+
environmentSelectionFingerprint
|
|
3343
|
+
};
|
|
3865
3344
|
if (!preserveExistingBinding) {
|
|
3866
3345
|
const nextBinding = {
|
|
3867
|
-
|
|
3868
|
-
...clientId ? { clientId } : {},
|
|
3869
|
-
cwd: params.cwd,
|
|
3870
|
-
...rolloutPath ? { rolloutPath } : {},
|
|
3871
|
-
authProfileId: params.params.authProfileId,
|
|
3872
|
-
agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions,
|
|
3873
|
-
model: response.model ?? startParams.model ?? params.params.modelId,
|
|
3874
|
-
modelProvider: bindingModelProvider,
|
|
3875
|
-
dynamicToolsFingerprint,
|
|
3876
|
-
dynamicToolsContainDeferred,
|
|
3346
|
+
...startedBinding,
|
|
3877
3347
|
webSearchThreadConfigFingerprint,
|
|
3878
|
-
|
|
3879
|
-
userMcpServersFingerprint,
|
|
3880
|
-
mcpServersFingerprint: nextMcpServersFingerprint,
|
|
3881
|
-
configuredMcpOwnershipVersion: params.configuredMcpOwnershipVersion,
|
|
3882
|
-
ringZeroConfigFingerprint,
|
|
3883
|
-
ringZeroClientInstanceId,
|
|
3884
|
-
nativeToolPolicyRestricted: restrictedToolSurface ? true : void 0,
|
|
3885
|
-
networkProxyProfileName: params.appServer.networkProxy?.profileName,
|
|
3886
|
-
networkProxyConfigFingerprint,
|
|
3887
|
-
nativeHookRelayGeneration: finalConfigPatch.nativeHookRelayGeneration,
|
|
3888
|
-
appServerRuntimeFingerprint: params.appServerRuntimeFingerprint,
|
|
3889
|
-
pluginAppsFingerprint: pluginThreadConfig?.fingerprint,
|
|
3890
|
-
pluginAppsInputFingerprint: pluginThreadConfig?.inputFingerprint,
|
|
3891
|
-
pluginAppPolicyContext: pluginThreadConfig?.policyContext,
|
|
3892
|
-
contextEngine: contextEngineBinding,
|
|
3893
|
-
environmentSelectionFingerprint
|
|
3348
|
+
nativeToolPolicyRestricted: restrictedToolSurface ? true : void 0
|
|
3894
3349
|
};
|
|
3895
3350
|
const managedSourceHomeId = codexCatalogHomeId(resolveCodexAppServerLocalHomeDir(params.appServer.start, resolveCodexThreadAgentDir(params)));
|
|
3896
3351
|
let committed;
|
|
@@ -3931,20 +3386,22 @@ async function startFreshCodexThread(params, context) {
|
|
|
3931
3386
|
threadId: response.thread.id,
|
|
3932
3387
|
action: rotatedContextEngineBinding ? "rotated" : "started"
|
|
3933
3388
|
});
|
|
3934
|
-
return
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3389
|
+
return {
|
|
3390
|
+
...startedBinding,
|
|
3391
|
+
modelProvider: response.modelProvider ?? requestModelProvider ?? startModelProvider ?? modelProvider,
|
|
3392
|
+
...startParams.ephemeral ? { liveThreadEphemeralPolicy: startParams.developerInstructions } : {},
|
|
3393
|
+
...!preserveExistingBinding ? { liveThreadConfigFingerprint: fingerprintCodexThreadConfig({
|
|
3394
|
+
...startParams,
|
|
3395
|
+
model: response.model ?? startParams.model ?? null,
|
|
3396
|
+
requestedModel: startParams.model ?? null,
|
|
3397
|
+
modelProvider: bindingModelProvider ?? null,
|
|
3398
|
+
requestedModelProvider: startParams.modelProvider ?? bindingModelProvider ?? null
|
|
3399
|
+
}, params.params.authProfileId, dynamicToolsFingerprint) } : {},
|
|
3400
|
+
lifecycle: {
|
|
3401
|
+
action: "started",
|
|
3402
|
+
...rotatedContextEngineBinding ? { rotatedContextEngineBinding: true } : {}
|
|
3403
|
+
}
|
|
3404
|
+
};
|
|
3948
3405
|
}
|
|
3949
3406
|
//#endregion
|
|
3950
3407
|
//#region extensions/codex/src/app-server/thread-lifecycle-warm.ts
|
|
@@ -4032,6 +3489,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
4032
3489
|
try {
|
|
4033
3490
|
retainedThread.assertCurrent();
|
|
4034
3491
|
params.params.hostCapabilities.assertActive();
|
|
3492
|
+
params.assertCurrent?.();
|
|
4035
3493
|
} catch (cause) {
|
|
4036
3494
|
throw new AgentHarnessPreflightError("Codex warm thread ownership changed before this turn could run. No turn was sent; reconnect before continuing, or start a new conversation if the original thread was closed.", { cause });
|
|
4037
3495
|
}
|
|
@@ -4163,11 +3621,14 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
4163
3621
|
//#endregion
|
|
4164
3622
|
//#region extensions/codex/src/app-server/thread-lifecycle-adoption.ts
|
|
4165
3623
|
/** Passive refusal must precede releasing or acquiring any native subscription. */
|
|
4166
|
-
async function assertAdoptedCodexThreadResumeAllowed(params, threadId, context) {
|
|
3624
|
+
async function assertAdoptedCodexThreadResumeAllowed(params, threadId, context, assertCurrent) {
|
|
4167
3625
|
const { thread } = await context.lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
|
|
4168
3626
|
threadId,
|
|
4169
3627
|
includeTurns: false
|
|
4170
|
-
}, {
|
|
3628
|
+
}, {
|
|
3629
|
+
signal: params.signal,
|
|
3630
|
+
assertCurrent
|
|
3631
|
+
}));
|
|
4171
3632
|
context.throwIfAborted();
|
|
4172
3633
|
assertCodexThreadAcceptsDirectInput(thread);
|
|
4173
3634
|
if (thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
|
|
@@ -4182,13 +3643,25 @@ async function withCodexThreadLifecycleBinding(params, run) {
|
|
|
4182
3643
|
agentId: params.agentId ?? params.params.agentId,
|
|
4183
3644
|
config: params.params.config
|
|
4184
3645
|
});
|
|
4185
|
-
const snapshot = await
|
|
3646
|
+
const { binding: snapshot, assertCurrent } = await resolveCodexSessionBinding({
|
|
3647
|
+
reclaimStale: true,
|
|
3648
|
+
bindingStore: params.bindingStore,
|
|
3649
|
+
identity,
|
|
3650
|
+
config: params.params.config,
|
|
3651
|
+
storePath: params.params.sessionTarget?.storePath,
|
|
3652
|
+
assertCurrent: () => {
|
|
3653
|
+
params.params.hostCapabilities.assertActive();
|
|
3654
|
+
params.assertCurrent?.();
|
|
3655
|
+
},
|
|
3656
|
+
signal: params.signal,
|
|
3657
|
+
assertBinding: params.params.expectedSessionRuntimeOwnership ? (binding) => assertCodexSessionRuntimeOwnership(binding, params.params.expectedSessionRuntimeOwnership) : void 0
|
|
3658
|
+
});
|
|
4186
3659
|
const runWithLease = () => params.bindingStore.withLease(identity, async () => {
|
|
4187
|
-
const binding =
|
|
3660
|
+
const binding = params.bindingStore.read(identity);
|
|
3661
|
+
assertCodexSessionRuntimeOwnership(binding, params.params.expectedSessionRuntimeOwnership);
|
|
4188
3662
|
if (binding?.threadId !== snapshot?.threadId || binding?.clientId !== snapshot?.clientId) throw new CodexThreadBindingConflictError(binding?.threadId ?? snapshot?.threadId ?? params.params.sessionId, "acquiring thread lifecycle ownership");
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
return await run(identity, binding);
|
|
3663
|
+
assertCurrent();
|
|
3664
|
+
return await run(identity, binding, assertCurrent);
|
|
4192
3665
|
});
|
|
4193
3666
|
return snapshot?.pendingResumeConfiguration ? await withExclusiveCodexAppServerThread({
|
|
4194
3667
|
bindingStore: params.bindingStore,
|
|
@@ -4206,32 +3679,27 @@ async function resumePendingCodexThread(params, context) {
|
|
|
4206
3679
|
})) throw new Error(`Cannot configure resumed Codex thread ${binding.threadId} under a transient or incompatible session policy. The thread is preserved; retry from its normal session or use /new for the current policy.`);
|
|
4207
3680
|
const prebuiltPluginThreadConfig = params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
4208
3681
|
const clientId = resolveCodexAppServerClientInstanceId(params.client);
|
|
4209
|
-
const
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
3682
|
+
const resumed = await resumeExistingCodexThread(params, {
|
|
3683
|
+
...context,
|
|
3684
|
+
prebuiltPluginThreadConfig,
|
|
3685
|
+
prepareResume: () => preparePendingCodexThreadResume(params, binding, context.dynamicToolsFingerprint),
|
|
3686
|
+
releaseRetainedThread: async (assertCurrent) => {
|
|
3687
|
+
const released = await context.releaseRetainedThread(binding.threadId, assertCurrent);
|
|
3688
|
+
assertCurrent();
|
|
3689
|
+
if (!released || binding.clientId && binding.clientId !== clientId) await releaseCodexConsumedLiveThread({
|
|
3690
|
+
client: params.client,
|
|
3691
|
+
abandonClient: params.abandonClient,
|
|
3692
|
+
lifecycleTiming,
|
|
3693
|
+
threadId: binding.threadId,
|
|
3694
|
+
assertCurrent
|
|
3695
|
+
});
|
|
3696
|
+
}
|
|
4219
3697
|
});
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
...context,
|
|
4223
|
-
prebuiltPluginThreadConfig,
|
|
4224
|
-
assertResumeConfiguration: configuration.assertConfigured,
|
|
4225
|
-
assertResumeOwnership: configuration.assertCurrent
|
|
4226
|
-
});
|
|
4227
|
-
if (!resumed) throw new Error(`Codex did not configure resumed thread ${binding.threadId}.`);
|
|
4228
|
-
return resumed;
|
|
4229
|
-
} finally {
|
|
4230
|
-
configuration.dispose();
|
|
4231
|
-
}
|
|
3698
|
+
if (!resumed) throw new Error(`Codex did not configure resumed thread ${binding.threadId}.`);
|
|
3699
|
+
return resumed;
|
|
4232
3700
|
}
|
|
4233
3701
|
/** Manual attachment is intent, never evidence that loaded native overrides took effect. */
|
|
4234
|
-
async function preparePendingCodexThreadResume(params, binding, dynamicToolsFingerprint
|
|
3702
|
+
async function preparePendingCodexThreadResume(params, binding, dynamicToolsFingerprint) {
|
|
4235
3703
|
const fail = (reason) => /* @__PURE__ */ new Error(`Cannot configure resumed Codex thread ${binding.threadId}: ${reason}. The thread is preserved; continue it in native Codex or use /new for the current OpenClaw tools.`);
|
|
4236
3704
|
const agentDir = resolveCodexThreadAgentDir(params);
|
|
4237
3705
|
const localHome = resolveCodexAppServerLocalHomeDir(params.appServer.start, agentDir);
|
|
@@ -4240,6 +3708,7 @@ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFing
|
|
|
4240
3708
|
const assertClient = captureCodexAppServerClientLifetime(params.client, "native-process");
|
|
4241
3709
|
const assertCurrent = () => {
|
|
4242
3710
|
params.params.hostCapabilities.assertActive();
|
|
3711
|
+
params.assertCurrent?.();
|
|
4243
3712
|
params.signal?.throwIfAborted();
|
|
4244
3713
|
assertClient();
|
|
4245
3714
|
if (isCodexAppServerLiveThreadClaimed(params.client, binding.threadId)) throw new CodexAdoptedThreadActiveError();
|
|
@@ -4248,10 +3717,12 @@ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFing
|
|
|
4248
3717
|
const { thread } = await params.client.request("thread/read", {
|
|
4249
3718
|
threadId: binding.threadId,
|
|
4250
3719
|
includeTurns: false
|
|
4251
|
-
}, {
|
|
3720
|
+
}, {
|
|
3721
|
+
signal: params.signal,
|
|
3722
|
+
assertCurrent
|
|
3723
|
+
});
|
|
4252
3724
|
assertCurrent();
|
|
4253
|
-
|
|
4254
|
-
if (thread.id !== binding.threadId || statusType !== "idle" && statusType !== "notLoaded") throw fail("the native thread is not idle; wait for its current run to finish");
|
|
3725
|
+
if (thread.id !== binding.threadId || !isCodexThreadNonRunning(thread.status)) throw fail("the native thread is not idle; wait for its current run to finish");
|
|
4255
3726
|
assertCodexThreadAcceptsDirectInput(thread);
|
|
4256
3727
|
const observation = observeCodexThreadConfiguration(params, thread, assertCurrent);
|
|
4257
3728
|
const dispose = observation.dispose;
|
|
@@ -4262,8 +3733,6 @@ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFing
|
|
|
4262
3733
|
const recordedTools = metadata.dynamic_tools ?? [];
|
|
4263
3734
|
if (!Array.isArray(recordedTools) || codexDynamicToolsFingerprint(recordedTools) !== dynamicToolsFingerprint) throw fail("its immutable native tool catalog does not match the current OpenClaw tools");
|
|
4264
3735
|
assertCurrent();
|
|
4265
|
-
await releaseSubscription(assertCurrent);
|
|
4266
|
-
assertCurrent();
|
|
4267
3736
|
return {
|
|
4268
3737
|
assertConfigured: observation.assertConfigured,
|
|
4269
3738
|
assertCurrent,
|
|
@@ -4279,21 +3748,29 @@ async function prepareCodexThreadResume(params, binding, context) {
|
|
|
4279
3748
|
const assertClient = captureCodexAppServerClientLifetime(params.client, binding.connectionScope === "supervision" ? "connection" : "native-process");
|
|
4280
3749
|
const assertCurrent = () => {
|
|
4281
3750
|
params.params.hostCapabilities.assertActive();
|
|
3751
|
+
params.assertCurrent?.();
|
|
4282
3752
|
params.signal?.throwIfAborted();
|
|
4283
3753
|
assertClient();
|
|
4284
3754
|
if (isCodexAppServerLiveThreadClaimed(params.client, binding.threadId)) throw new CodexAdoptedThreadActiveError();
|
|
4285
3755
|
};
|
|
4286
3756
|
assertCurrent();
|
|
4287
|
-
|
|
4288
|
-
|
|
3757
|
+
let thread;
|
|
3758
|
+
try {
|
|
3759
|
+
thread = await assertAdoptedCodexThreadResumeAllowed(params, binding.threadId, context, assertCurrent);
|
|
3760
|
+
} finally {
|
|
3761
|
+
assertCurrent();
|
|
3762
|
+
}
|
|
4289
3763
|
assertCodexSupervisionThreadLineage(binding, thread);
|
|
4290
3764
|
return {
|
|
4291
3765
|
...observeCodexThreadConfiguration(params, thread, assertCurrent),
|
|
4292
3766
|
assertCurrent
|
|
4293
3767
|
};
|
|
4294
3768
|
}
|
|
3769
|
+
function isCodexThreadNonRunning(status) {
|
|
3770
|
+
return status?.type === "idle" || status?.type === "notLoaded" || status?.type === "systemError";
|
|
3771
|
+
}
|
|
4295
3772
|
function observeCodexThreadConfiguration(params, thread, assertCurrent) {
|
|
4296
|
-
if (thread.status
|
|
3773
|
+
if (!isCodexThreadNonRunning(thread.status)) throw new CodexAdoptedThreadActiveError();
|
|
4297
3774
|
let unloaded = thread.status.type === "notLoaded";
|
|
4298
3775
|
return {
|
|
4299
3776
|
dispose: params.client.addNotificationHandler((notification) => {
|
|
@@ -4309,6 +3786,10 @@ function observeCodexThreadConfiguration(params, thread, assertCurrent) {
|
|
|
4309
3786
|
//#region extensions/codex/src/app-server/thread-supervision.ts
|
|
4310
3787
|
async function materializePendingSupervisionBranch(params) {
|
|
4311
3788
|
let pending = params.binding.pendingSupervisionBranch;
|
|
3789
|
+
const requestOptions = {
|
|
3790
|
+
signal: params.signal,
|
|
3791
|
+
assertCurrent: params.throwIfAborted
|
|
3792
|
+
};
|
|
4312
3793
|
const connectionFingerprint = buildCodexAppServerConnectionFingerprint(params.appServer, params.attempt.agentDir);
|
|
4313
3794
|
if (!pending.connectionFingerprint || pending.connectionFingerprint !== connectionFingerprint) throw new Error("Codex supervision source connection changed before branch materialization");
|
|
4314
3795
|
pending = await recoverPendingSupervisionArtifacts(params, pending);
|
|
@@ -4316,7 +3797,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4316
3797
|
const sourceResponse = await params.lifecycleTiming.measure("supervision-source-read", () => params.client.request("thread/read", {
|
|
4317
3798
|
threadId: pending.sourceThreadId,
|
|
4318
3799
|
includeTurns: true
|
|
4319
|
-
},
|
|
3800
|
+
}, requestOptions));
|
|
4320
3801
|
params.throwIfAborted();
|
|
4321
3802
|
const sourceThread = sourceResponse.thread;
|
|
4322
3803
|
if (sourceThread.id !== pending.sourceThreadId) throw new Error(`Codex supervision source read returned ${sourceThread.id} for ${pending.sourceThreadId}`);
|
|
@@ -4342,7 +3823,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4342
3823
|
});
|
|
4343
3824
|
} catch (error) {
|
|
4344
3825
|
try {
|
|
4345
|
-
const current =
|
|
3826
|
+
const current = params.bindingStore.read(params.bindingIdentity);
|
|
4346
3827
|
if (matchesPendingSupervisionState(current, pending)) cleanupExpected = pending;
|
|
4347
3828
|
else if (matchesPendingSupervisionState(current, expected)) cleanupExpected = expected;
|
|
4348
3829
|
else throw new CodexThreadBindingConflictError(pending.sourceThreadId, "verifying supervised Codex cleanup tracking");
|
|
@@ -4359,7 +3840,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4359
3840
|
const probeParams = buildPendingSupervisionProbeForkParams(params, pending);
|
|
4360
3841
|
const rawProbeResponse = await params.lifecycleTiming.measure("supervision-model-probe-fork", async () => {
|
|
4361
3842
|
try {
|
|
4362
|
-
return await params.client.request("thread/fork", probeParams,
|
|
3843
|
+
return await params.client.request("thread/fork", probeParams, requestOptions);
|
|
4363
3844
|
} catch (error) {
|
|
4364
3845
|
if (!(error instanceof CodexAppServerRpcError)) throw new CodexAppServerUnsafeSubscriptionError("Codex model probe fork may have materialized without a response", { cause: error });
|
|
4365
3846
|
throw error;
|
|
@@ -4414,7 +3895,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4414
3895
|
});
|
|
4415
3896
|
const rawStartResponse = await params.lifecycleTiming.measure("supervision-thread-start", async () => {
|
|
4416
3897
|
try {
|
|
4417
|
-
return await params.client.request("thread/start", startParams,
|
|
3898
|
+
return await params.client.request("thread/start", startParams, requestOptions);
|
|
4418
3899
|
} catch (error) {
|
|
4419
3900
|
if (error instanceof CodexAppServerRpcError) throw new CodexThreadStartRequestError(error);
|
|
4420
3901
|
throw new CodexAppServerUnsafeSubscriptionError("Canonical Codex branch may have started without a response", { cause: error });
|
|
@@ -4435,7 +3916,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4435
3916
|
});
|
|
4436
3917
|
if (params.restrictedToolSurface) await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, finalThreadId, startParams.config, params.signal));
|
|
4437
3918
|
if (params.provisionalAppIds?.length) try {
|
|
4438
|
-
await params.lifecycleTiming.measure("plugin-app-attestation", () =>
|
|
3919
|
+
await params.lifecycleTiming.measure("plugin-app-attestation", () => checkCodexThreadAppAvailability({
|
|
4439
3920
|
client: params.client,
|
|
4440
3921
|
threadId: finalThreadId,
|
|
4441
3922
|
appIds: params.provisionalAppIds ?? [],
|
|
@@ -4457,7 +3938,7 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4457
3938
|
await params.lifecycleTiming.measure("supervision-history-inject", () => params.client.request("thread/inject_items", {
|
|
4458
3939
|
threadId: finalThreadId,
|
|
4459
3940
|
items: history.responseItems
|
|
4460
|
-
},
|
|
3941
|
+
}, requestOptions));
|
|
4461
3942
|
params.throwIfAborted();
|
|
4462
3943
|
}
|
|
4463
3944
|
const historyCoveredThrough = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4474,11 +3955,11 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
4474
3955
|
modelProvider: bindingModelProvider,
|
|
4475
3956
|
historyCoveredThrough
|
|
4476
3957
|
}
|
|
4477
|
-
});
|
|
3958
|
+
}, params.throwIfAborted);
|
|
4478
3959
|
} catch (error) {
|
|
4479
3960
|
let current;
|
|
4480
3961
|
try {
|
|
4481
|
-
current =
|
|
3962
|
+
current = params.bindingStore.read(params.bindingIdentity);
|
|
4482
3963
|
} catch (readError) {
|
|
4483
3964
|
provisionalCleanupSafe = false;
|
|
4484
3965
|
throw new CodexAppServerUnsafeSubscriptionError(`Canonical Codex branch binding could not be verified: ${finalThreadId}`, { cause: new AggregateError([error, readError]) });
|
|
@@ -4669,11 +4150,16 @@ async function archiveSupervisionArtifact(client, threadId) {
|
|
|
4669
4150
|
}
|
|
4670
4151
|
//#endregion
|
|
4671
4152
|
//#region extensions/codex/src/app-server/thread-lifecycle-run.ts
|
|
4672
|
-
async function startOrResumeThread(
|
|
4673
|
-
const incognito = isIncognitoSessionKey(
|
|
4674
|
-
const clientId = resolveCodexAppServerClientInstanceId(
|
|
4675
|
-
return await withCodexThreadLifecycleBinding(
|
|
4676
|
-
|
|
4153
|
+
async function startOrResumeThread(input) {
|
|
4154
|
+
const incognito = isIncognitoSessionKey(input.params.sessionKey);
|
|
4155
|
+
const clientId = resolveCodexAppServerClientInstanceId(input.client);
|
|
4156
|
+
return await withCodexThreadLifecycleBinding(input, async (bindingIdentity, saved, assert) => {
|
|
4157
|
+
const params = {
|
|
4158
|
+
...input,
|
|
4159
|
+
assertCurrent: assert
|
|
4160
|
+
};
|
|
4161
|
+
const expectedOwnership = params.params.expectedSessionRuntimeOwnership;
|
|
4162
|
+
let binding = saved;
|
|
4677
4163
|
if (hasCodexNativeToolCatalog(binding)) {
|
|
4678
4164
|
const nativeCatalog = await loadCodexNativeToolCatalog({
|
|
4679
4165
|
client: params.client,
|
|
@@ -4682,7 +4168,7 @@ async function startOrResumeThread(params) {
|
|
|
4682
4168
|
agentDir: resolveCodexThreadAgentDir(params),
|
|
4683
4169
|
assertCurrent: () => {
|
|
4684
4170
|
params.signal?.throwIfAborted();
|
|
4685
|
-
|
|
4171
|
+
assert();
|
|
4686
4172
|
}
|
|
4687
4173
|
});
|
|
4688
4174
|
if (!isDeepStrictEqual(params.dynamicTools, nativeCatalog)) throw new Error("Canonical Codex declarations changed after tool preparation; retry the turn on its preserved native thread.");
|
|
@@ -4709,13 +4195,6 @@ async function startOrResumeThread(params) {
|
|
|
4709
4195
|
threadId,
|
|
4710
4196
|
assertCurrent
|
|
4711
4197
|
});
|
|
4712
|
-
if (!binding && bindingIdentity.kind === "session" && bindingIdentity.sessionKey) {
|
|
4713
|
-
if (!await lifecycleTiming.measure("reclaim-binding-generation", () => reclaimCurrentCodexSessionGeneration({
|
|
4714
|
-
bindingStore: params.bindingStore,
|
|
4715
|
-
identity: bindingIdentity,
|
|
4716
|
-
config: params.params.config
|
|
4717
|
-
}))) throw createCodexSessionGenerationSupersededError(bindingIdentity.sessionId);
|
|
4718
|
-
}
|
|
4719
4198
|
if (binding?.pendingSupervisionBranch) {
|
|
4720
4199
|
await releaseRetainedThread(binding.threadId);
|
|
4721
4200
|
const pendingBinding = binding;
|
|
@@ -4749,7 +4228,10 @@ async function startOrResumeThread(params) {
|
|
|
4749
4228
|
environmentSelection: params.environmentSelection,
|
|
4750
4229
|
provisionalAppIds: pluginThreadConfig?.provisionalAppIds,
|
|
4751
4230
|
signal: params.signal,
|
|
4752
|
-
throwIfAborted
|
|
4231
|
+
throwIfAborted: () => {
|
|
4232
|
+
throwIfAborted();
|
|
4233
|
+
assert();
|
|
4234
|
+
},
|
|
4753
4235
|
lifecycleTiming,
|
|
4754
4236
|
normalizeBindingModelProvider,
|
|
4755
4237
|
bindingPatch: {
|
|
@@ -4781,11 +4263,11 @@ async function startOrResumeThread(params) {
|
|
|
4781
4263
|
const clearCurrentBinding = async (operation) => {
|
|
4782
4264
|
const current = binding;
|
|
4783
4265
|
if (!current?.threadId) return;
|
|
4784
|
-
assertCodexBindingMayBeReplaced(current, operation);
|
|
4266
|
+
assertCodexBindingMayBeReplaced(current, operation, expectedOwnership);
|
|
4785
4267
|
if (!await params.bindingStore.mutate(bindingIdentity, {
|
|
4786
4268
|
kind: "clear",
|
|
4787
4269
|
threadId: current.threadId
|
|
4788
|
-
})) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
4270
|
+
}, assert)) throw new CodexThreadBindingConflictError(current.threadId, operation);
|
|
4789
4271
|
binding = void 0;
|
|
4790
4272
|
};
|
|
4791
4273
|
const resolveRequestContext = () => {
|
|
@@ -4879,14 +4361,14 @@ async function startOrResumeThread(params) {
|
|
|
4879
4361
|
const unknownProviderWebSearchSupport = params.nativeProviderWebSearchSupport === "unknown";
|
|
4880
4362
|
if (binding?.threadId && (params.configuredMcpOwnershipVersion === 1 && (binding.configuredMcpOwnershipVersion !== 1 || binding.dynamicToolsFingerprint === void 0 || binding.mcpServersFingerprint !== void 0 || binding.userMcpServersFingerprint !== void 0) || params.configuredMcpOwnershipVersion !== 1 && binding.configuredMcpOwnershipVersion === 1) && binding?.threadId) {
|
|
4881
4363
|
const predecessorBinding = binding;
|
|
4882
|
-
assertCodexBindingMayBeReplaced(predecessorBinding, "changing configured MCP ownership");
|
|
4364
|
+
assertCodexBindingMayBeReplaced(predecessorBinding, "changing configured MCP ownership", expectedOwnership);
|
|
4883
4365
|
embeddedAgentLog.debug("codex app-server configured MCP ownership changed; starting a new thread", { threadId: predecessorBinding.threadId });
|
|
4884
4366
|
replacementPredecessor = predecessorBinding;
|
|
4885
4367
|
binding = void 0;
|
|
4886
4368
|
preserveExistingBinding = false;
|
|
4887
4369
|
}
|
|
4888
4370
|
if (binding?.threadId && params.mcpServersFingerprintEvaluated === true && binding.mcpServersFingerprint !== params.mcpServersFingerprint) {
|
|
4889
|
-
assertCodexBindingMayBeReplaced(binding, "changing MCP configuration");
|
|
4371
|
+
assertCodexBindingMayBeReplaced(binding, "changing MCP configuration", expectedOwnership);
|
|
4890
4372
|
if (!ringZeroActive && (transientNativeToolRestriction || webSearchBindingChanged && (explicitTransientWebSearchRestriction || unknownProviderWebSearchSupport))) {
|
|
4891
4373
|
embeddedAgentLog.debug("codex app-server MCP config changed during transient restricted turn; starting transient thread", { threadId: binding.threadId });
|
|
4892
4374
|
preserveExistingBinding = true;
|
|
@@ -4898,7 +4380,7 @@ async function startOrResumeThread(params) {
|
|
|
4898
4380
|
}
|
|
4899
4381
|
const deferLegacyWebSearchRotationToTransientNativeSurface = params.nativeCodeModeEnabled === false && binding?.webSearchThreadConfigFingerprint === void 0 && !persistentWebSearchRestriction;
|
|
4900
4382
|
if (binding?.threadId && webSearchBindingChanged && !deferLegacyWebSearchRotationToTransientNativeSurface) {
|
|
4901
|
-
assertCodexBindingMayBeReplaced(binding, "changing web-search configuration");
|
|
4383
|
+
assertCodexBindingMayBeReplaced(binding, "changing web-search configuration", expectedOwnership);
|
|
4902
4384
|
if (!ringZeroActive && transientWebSearchRestriction) {
|
|
4903
4385
|
embeddedAgentLog.debug("codex app-server tool surface restricted for turn; starting transient thread", { threadId: binding.threadId });
|
|
4904
4386
|
preserveExistingBinding = true;
|
|
@@ -4909,13 +4391,13 @@ async function startOrResumeThread(params) {
|
|
|
4909
4391
|
binding = void 0;
|
|
4910
4392
|
}
|
|
4911
4393
|
if (binding?.threadId && transientNativeToolRestriction && !ringZeroActive) {
|
|
4912
|
-
assertCodexBindingMayBeReplaced(binding, "starting a native-tool-restricted turn");
|
|
4394
|
+
assertCodexBindingMayBeReplaced(binding, "starting a native-tool-restricted turn", expectedOwnership);
|
|
4913
4395
|
embeddedAgentLog.debug("codex app-server native tool surface disabled for turn; starting transient thread", { threadId: binding.threadId });
|
|
4914
4396
|
preserveExistingBinding = true;
|
|
4915
4397
|
binding = void 0;
|
|
4916
4398
|
}
|
|
4917
4399
|
if (binding?.threadId && transientDelegationRestriction) {
|
|
4918
|
-
assertCodexBindingMayBeReplaced(binding, "starting a delegation-restricted turn");
|
|
4400
|
+
assertCodexBindingMayBeReplaced(binding, "starting a delegation-restricted turn", expectedOwnership);
|
|
4919
4401
|
embeddedAgentLog.debug("codex app-server delegation restricted for turn; starting transient thread", { threadId: binding.threadId });
|
|
4920
4402
|
binding = void 0;
|
|
4921
4403
|
}
|
|
@@ -4973,7 +4455,7 @@ async function startOrResumeThread(params) {
|
|
|
4973
4455
|
}
|
|
4974
4456
|
if (binding?.threadId) {
|
|
4975
4457
|
if (binding.dynamicToolsFingerprint && !areDynamicToolFingerprintsCompatible(binding.dynamicToolsFingerprint, dynamicToolsFingerprint, legacyDynamicToolsFingerprint)) {
|
|
4976
|
-
assertCodexBindingMayBeReplaced(binding, "changing the dynamic tool catalog");
|
|
4458
|
+
assertCodexBindingMayBeReplaced(binding, "changing the dynamic tool catalog", expectedOwnership);
|
|
4977
4459
|
preserveExistingBinding = shouldStartTransientNoToolThread({
|
|
4978
4460
|
previous: binding.dynamicToolsFingerprint,
|
|
4979
4461
|
nextHasDynamicTools: params.dynamicTools.length > 0
|
|
@@ -4996,27 +4478,24 @@ async function startOrResumeThread(params) {
|
|
|
4996
4478
|
throwIfAborted();
|
|
4997
4479
|
await clearCurrentBinding(incognito ? "rotating an unavailable ephemeral thread binding" : "rotating a stale plugin app binding");
|
|
4998
4480
|
} else {
|
|
4999
|
-
const
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
if (resumed) return resumed;
|
|
5014
|
-
} finally {
|
|
5015
|
-
configuration.dispose();
|
|
5016
|
-
}
|
|
4481
|
+
const resumeBinding = binding;
|
|
4482
|
+
const resumed = await resumeExistingCodexThread(params, {
|
|
4483
|
+
...requestContext,
|
|
4484
|
+
binding: resumeBinding,
|
|
4485
|
+
clearCurrentBinding,
|
|
4486
|
+
prebuiltFinalConfigPatch: warmReuse.prebuiltFinalConfigPatch,
|
|
4487
|
+
prebuiltPluginThreadConfig,
|
|
4488
|
+
buildLoadedPluginThreadConfig,
|
|
4489
|
+
prepareResume: () => prepareCodexThreadResume(params, resumeBinding, requestContext),
|
|
4490
|
+
releaseRetainedThread: async (assertCurrent) => {
|
|
4491
|
+
await releaseRetainedThread(resumeBinding.threadId, resumeBinding.clientId, assertCurrent);
|
|
4492
|
+
}
|
|
4493
|
+
});
|
|
4494
|
+
if (resumed) return resumed;
|
|
5017
4495
|
}
|
|
5018
4496
|
}
|
|
5019
4497
|
}
|
|
4498
|
+
assertCodexBindingMayBeReplaced(binding, "starting a fresh native thread", expectedOwnership);
|
|
5020
4499
|
if (initialBoundThreadId && !preserveExistingBinding && !replacementPredecessor) await releaseRetainedThread(initialBoundThreadId);
|
|
5021
4500
|
const started = await startFreshCodexThread(params, {
|
|
5022
4501
|
...requestContext,
|
|
@@ -5139,6 +4618,17 @@ function buildTurnStartParams(params, options) {
|
|
|
5139
4618
|
const useThreadPermissionProfile = options.appServer.networkProxy && !options.sandboxPolicy;
|
|
5140
4619
|
const currentSenderContext = params.trigger === "user" ? buildCodexCurrentSenderContextValue(params) : void 0;
|
|
5141
4620
|
let additionalContext = buildCodexTemporalAdditionalContext(params, { sessionStatusAvailable: options.sessionStatusAvailable === true });
|
|
4621
|
+
additionalContext = {
|
|
4622
|
+
...additionalContext,
|
|
4623
|
+
openclaw_source_delivery: {
|
|
4624
|
+
kind: "application",
|
|
4625
|
+
value: ["Current source-delivery policy for this turn (replaces earlier source-delivery guidance):", buildHarnessVisibleReplyGuidance({
|
|
4626
|
+
sourceReplyDeliveryMode: params.sourceReplyDeliveryMode,
|
|
4627
|
+
messageToolAvailable: options.messageToolAvailable === true,
|
|
4628
|
+
requireExplicitMessageTarget: options.requireExplicitMessageTarget
|
|
4629
|
+
})].join("\n")
|
|
4630
|
+
}
|
|
4631
|
+
};
|
|
5142
4632
|
if (currentSenderContext) additionalContext = {
|
|
5143
4633
|
...additionalContext,
|
|
5144
4634
|
openclaw_current_sender: {
|
|
@@ -5231,4 +4721,4 @@ function joinPresentSections(...sections) {
|
|
|
5231
4721
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
5232
4722
|
}
|
|
5233
4723
|
//#endregion
|
|
5234
|
-
export {
|
|
4724
|
+
export { codexLegacyDynamicToolsFingerprint as $, readCodexMcpToolConnectorId as A, emitCodexNativePreToolUseFailureDiagnostic as B, buildDeveloperInstructions as C, buildCodexPluginThreadConfigTimeoutFallback as Ct, resolveCodexAppServerThreadModelSelection as D, shouldBuildCodexPluginThreadConfig as Dt, resolveCodexAppServerRequestModelSelection as E, refreshCodexPluginAppApprovalPolicy as Et, assertCodexNativeHookRelayAllowed as F, attestCodexRestrictedToolSurfaceMcpServersDisabled as G, resolveCodexNativeHookRelayTtlMs as H, buildCodexNativeHookRelayConfig as I, parseCodexNativeToolCatalog as J, hasCodexNativeToolCatalog as K, buildCodexNativeHookRelayDisabledConfig as L, isCodexAppServerProfilerEnabled as M, CODEX_NATIVE_HOOK_RELAY_EVENTS as N, resolveCodexBindingModelProviderFallback as O, CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS as P, codexDynamicToolsFingerprint as Q, buildCodexNativeHookRelayId as R, resolveCodexWebSearchPlan as S, buildCodexPluginThreadConfigInputFingerprint as St, resolveCodexAppServerModelProvider as T, mergeCodexThreadConfigs as Tt, scheduleCodexNativeHookRelayUnregister as U, resolveCodexNativeHookRelayEvents as V, checkCodexThreadAppAvailability as W, resolveCodexNativeSkillIsolation as X, applyCodexNativeSkillIsolation as Y, areCodexDynamicToolFingerprintsCompatible as Z, buildCodexRingZeroThreadConfigPatch as _, projectContextEngineAssemblyForCodex as _t, readCodexSupportedReasoningEfforts as a, isMessageOnlyCodexSourceReply as at, readCodexInheritedMcpServerNames as b, resolveCodexContinuityProjectionMaxChars as bt, assertScheduledCodexAppAuthorityRuntime as c, resolveCodexDynamicToolsLoading as ct, buildScheduledCodexAppServerConnectionIdentity as d, isContextEngineBindingCompatible as dt, fingerprintJsonObject as et, captureScheduledCodexAppAuthority as f, CodexContextAttachmentError as ft, assertCodexManagedRequirementsDoNotOverrideToolPolicy as g, neutralizeCodexExplicitMentionSigils as gt, resolveScheduledCodexAppCreatorCaptureDecision as h, isCodexDurableCustomMessage as ht, buildCodexUserInput as i, isForcedPrivateQaCodexRuntime as it, readCodexMcpToolUiVisibility as j, buildCodexProjectDocThreadConfig as k, buildLegacyScheduledCodexAppRecoveryPrompt as l, resolveCodexDynamicToolsLoadingForRuntime as lt, readCurrentCodexScheduledAppPolicy as m, fitCodexProjectedContextForTurnStart as mt, buildTurnCollaborationMode as n, filterCodexDynamicTools as nt, resolveCodexAppServerReasoningEffort as o, isSystemAgentOnlyCodexDynamicToolAllowlist as ot, intersectCodexPluginThreadConfigWithScheduledAuthority as p, buildCodexContinuityCalibration as pt, loadCodexNativeToolCatalog as q, buildTurnStartParams as r, filterCodexDynamicToolsForDisabledNativeSurface as rt, startOrResumeThread as s, normalizeCodexDynamicToolName as st, buildCodexTemporalAdditionalContext as t, fingerprintUserMcpServersConfigPatch as tt, buildScheduledCodexAppAuthorityInputFingerprint as u, buildContextEngineBinding as ut, buildCodexRuntimeThreadConfig as v, resolveCodexContextEngineProjectionMaxChars as vt, CODEX_NATIVE_PERSONALITY_NONE as w, buildDisabledAppsConfigPatch as wt, buildCodexNativeWebSearchThreadConfig as x, buildCodexPluginThreadConfig as xt, buildCodexThreadConfiguration as y, resolveCodexContextEngineProjectionReserveTokens as yt, createCodexNativeHookRelay as z };
|