@openclaw/codex 2026.9.4 → 2026.9.6
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/assets/activity.svg +30 -0
- package/assets/icon.png +0 -0
- package/dist/.setup/account-usage-runtime-Eem_WGqY.mjs +75 -0
- package/dist/{apply-CvT8tW5i.js → .setup/apply-DDOi8uNx.mjs} +24 -30
- package/dist/.setup/async-questions-DdhOCwGi.mjs +27 -0
- package/dist/{attempt-client-cleanup-DKHzbwt5.js → .setup/attempt-client-cleanup-CEv1cKwF.mjs} +79 -49
- package/dist/.setup/attempt-terminal-gZrxQ35N.mjs +5 -0
- package/dist/.setup/attempt-workspace-context-DUVEn_CS.mjs +331 -0
- package/dist/{auth-binding-CWlKVccJ.js → .setup/auth-binding-DhLWa_7K.mjs} +1 -1
- package/dist/{auth-cache-key-7orQae_j.js → .setup/auth-cache-key-B2mpaI9D.mjs} +9 -81
- package/dist/{auth-profile-CwzO4_RG.js → .setup/auth-profile-WqZtZfXN.mjs} +1 -1
- package/dist/.setup/auth-profile-health-oHizqgye.mjs +53 -0
- package/dist/.setup/auth-profile-recovery-BREezV0L.mjs +14 -0
- package/dist/.setup/binding-connection-ThoatDcb.mjs +50 -0
- package/dist/.setup/binding-connection.runtime-yoHrABt-.mjs +48 -0
- package/dist/{bounded-turn-C_Hneydj.js → .setup/bounded-turn-B2HScEgT.mjs} +143 -32
- package/dist/{build-state-BCmg9-5p.js → .setup/build-state-C7EnDVgr.mjs} +1 -1
- package/dist/{capabilities-8vx68WLH.js → .setup/capabilities-CDXOOFdZ.mjs} +1 -1
- package/dist/{client-B57KWPQx.js → .setup/client-Cs08OXVQ.mjs} +705 -512
- package/dist/.setup/client-catalog-response-BMgt_4q4.mjs +285 -0
- package/dist/{command-dispatch-BpduNGVf.js → .setup/command-dispatch-B4JuOsOk.mjs} +8 -4
- package/dist/{command-formatters-lxlkNZgU.js → .setup/command-formatters-Bmqvr9tO.mjs} +8 -4
- package/dist/{command-handlers-tiL9iWj7.js → .setup/command-handlers-DW7Lx9oH.mjs} +137 -186
- package/dist/{command-rpc-CR5EOEZX.js → .setup/command-rpc-DixIj3HW.mjs} +47 -23
- package/dist/{compact-B_H3AOd7.js → .setup/compact-Du5UvtxA.mjs} +29 -51
- package/dist/{config-oIORaQ5T.js → .setup/config-BoTP_mrL.mjs} +5 -4
- package/dist/{config-options-CXMq1T-C.js → .setup/config-options-BvaRs51b.mjs} +50 -446
- package/dist/{protocol-5bh1G-H7.js → .setup/config-parsing-CcB9iPoq.mjs} +9 -51
- package/dist/{config-runtime-D0zGNIHu.js → .setup/config-runtime-C8Rw1m1m.mjs} +2 -2
- package/dist/.setup/config-security-BEReZ6go.mjs +380 -0
- package/dist/{conversation-binding-DsB4-O7U.js → .setup/conversation-binding-DP-gMrBP.mjs} +29 -38
- package/dist/{conversation-binding-data-BiodpIzq.js → .setup/conversation-binding-data-CrcyZk6O.mjs} +1 -1
- package/dist/{conversation-control-CtUu1b3m.js → .setup/conversation-control-uK9Jomlc.mjs} +32 -67
- package/dist/.setup/cyber-failover-DYwbmf1o.mjs +123 -0
- package/dist/.setup/cyber-failover-notice-e9G3J9-e.mjs +17 -0
- package/dist/{dynamic-tools-CmN5RAip.js → .setup/dynamic-tools-BDXl-8Cw.mjs} +936 -620
- package/dist/{effective-mcp-catalog-Bd73bqA8.js → .setup/effective-mcp-catalog-CvnvnGIe.mjs} +3 -3
- package/dist/{ephemeral-turn-D78_VmzO.js → .setup/ephemeral-turn-poidXgek.mjs} +9 -7
- package/dist/{event-projector-assistant-message-BmBM4qBV.js → .setup/event-projector-assistant-message-BsruvNrH.mjs} +8 -7
- package/dist/{event-projector-items-5VhsECCO.js → .setup/event-projector-items-C6L_5ZiT.mjs} +42 -8
- package/dist/{attempt-notifications-C9XufG6l.js → .setup/event-projector-usage-CnBS8Wa3.mjs} +102 -9
- package/dist/{harness-C7rRa7bm.js → .setup/harness-a_MsE4-_.mjs} +81 -21
- package/dist/{inference-context-Dr_mzvk4.js → .setup/inference-context-D96l9-ew.mjs} +1 -1
- package/dist/.setup/inference-proxy-b-aJMSxm.mjs +601 -0
- package/dist/{isolated-completion-DWVb5U2J.js → .setup/isolated-completion-BIZFhTNG.mjs} +18 -11
- package/dist/.setup/launch-args-DbFCehO7.mjs +196 -0
- package/dist/{managed-binary-D22rNEcR.js → .setup/managed-binary-BnshlFag.mjs} +14 -3
- package/dist/{managed-thread-store-D1t5jh3G.js → .setup/managed-thread-store-BMqgThVL.mjs} +48 -14
- package/dist/{model-catalog-Dq2n8f_p.js → .setup/model-catalog-CfXJf9Ay.mjs} +36 -12
- package/dist/{models-PCKOim-h.js → .setup/models-KnqBv0hE.mjs} +3 -3
- package/dist/.setup/native-auth-rWbGbMn3.mjs +57 -0
- package/dist/{native-subagent-history-DjtPloaU.js → .setup/native-subagent-history-0nYYgo0k.mjs} +43 -35
- package/dist/.setup/native-subagent-monitor-D3filLY2.mjs +4859 -0
- package/dist/{native-subagent-task-ids-BzTdT0Uw.js → .setup/native-subagent-task-ids-wb8CKZGN.mjs} +98 -44
- package/dist/{node-cli-sessions-DKaOmVUh.js → .setup/node-cli-sessions-r3avj02l.mjs} +86 -68
- package/dist/.setup/node-exec-server.runtime-BDwWXVpK.mjs +247 -0
- package/dist/{plan-7uG-Fp36.js → .setup/plan-CN3VwfNU.mjs} +133 -119
- package/dist/{plugin-activation-BYQculil.js → .setup/plugin-activation-DT3MNtqj.mjs} +7 -16
- package/dist/{plugin-app-cache-key-d0eKxy7b.js → .setup/plugin-app-cache-key-B2CsSdnV.mjs} +32 -44
- package/dist/{plugin-approval-roundtrip-Bz3FT8W5.js → .setup/plugin-approval-roundtrip-C0MZzCuP.mjs} +1 -1
- package/dist/{plugin-inventory-oL4Na1ZG.js → .setup/plugin-inventory-BoRei8Z4.mjs} +21 -11
- package/dist/{plugin-marketplace-discovery-P-Vndfqq.js → .setup/plugin-marketplace-discovery-CDOi8IFs.mjs} +4 -1
- package/dist/{plugin-metadata-cache-ClPJpRio.js → .setup/plugin-metadata-cache-Ctmo83k_.mjs} +226 -141
- package/dist/.setup/protocol-CANUwXJ3.mjs +17 -0
- package/dist/{provider-MQ8PVkuE.js → .setup/provider-BOsxQHOM.mjs} +9 -5
- package/dist/{provider-capabilities-BvCUL69A.js → .setup/provider-capabilities-BfE88FZ5.mjs} +31 -3
- package/dist/{rate-limits-CBMmWBr9.js → .setup/rate-limits-CMU3DUD0.mjs} +174 -151
- package/dist/{request-B7hEkBGq.js → .setup/request-D2L0zMrq.mjs} +130 -42
- package/dist/{run-attempt-CvsAAGa0.js → .setup/run-attempt-BHu11r7h.mjs} +1628 -966
- package/dist/.setup/run-attempt-lifecycle-B7JbY4-F.mjs +71 -0
- package/dist/{runtime-artifact-BJ0rXBZ_.js → .setup/runtime-artifact-CLZ8udh-.mjs} +29 -45
- package/dist/{sandbox-guard-BxgyoM8M.js → .setup/sandbox-guard-C2sRMOEY.mjs} +3 -3
- package/dist/{session-binding-CI8UuilT.js → .setup/session-binding-Cm0apEbd.mjs} +36 -83
- package/dist/{session-binding-record-Bcvslnhf.js → .setup/session-binding-record-BGoz8wOK.mjs} +202 -5
- package/dist/.setup/session-binding-sidecars-BlB5EvIS.mjs +546 -0
- package/dist/.setup/session-catalog-Pno1xGcl.mjs +2764 -0
- package/dist/{session-catalog-adoption-tcZi1IfX.js → .setup/session-catalog-adoption-BI6UTDZT.mjs} +58 -26
- package/dist/{session-catalog-archive-CwKg8Xz2.js → .setup/session-catalog-archive-lHBqzhaO.mjs} +6 -6
- package/dist/.setup/session-catalog-events-Bj6j94E_.mjs +347 -0
- package/dist/.setup/session-catalog-index-B9_VQIUz.mjs +1170 -0
- package/dist/.setup/session-catalog-index-order-hcdz07yN.mjs +48 -0
- package/dist/.setup/session-catalog-index-state-Dgh8ORJm.mjs +183 -0
- package/dist/{thread-history-page-D0JIp48V.js → .setup/session-catalog-native-projection-DowriLid.mjs} +223 -95
- package/dist/{session-catalog-node-adoption-B1tgDW-m.js → .setup/session-catalog-node-adoption-ITddWx_X.mjs} +25 -13
- package/dist/{session-catalog-node-continue-Ci-DXyJP.js → .setup/session-catalog-node-continue-6g6qi4A9.mjs} +24 -15
- package/dist/.setup/session-catalog-projection-zK_PUsiE.mjs +422 -0
- package/dist/{session-cli-DNN_jhGc.js → .setup/session-cli-irrD-uo7.mjs} +3 -3
- package/dist/{session-history-Cz-tOOYj.js → .setup/session-history-BrqhSCf9.mjs} +19 -19
- package/dist/{session-history-import-DwyijFI6.js → .setup/session-history-import-D-3wAgOP.mjs} +4 -4
- package/dist/{session-history-read-cxPUxyVV.js → .setup/session-history-read-B3ZfSUbF.mjs} +2 -2
- package/dist/{session-history.worker-BeK1YOyT.js → .setup/session-history.worker-CaCh0xF_.mjs} +10 -24
- package/dist/{session-initialization-Ct0_Hzmg.js → .setup/session-initialization-rcgbZiid.mjs} +2 -2
- package/dist/{session-permission-policy-BBmXA8Eg.js → .setup/session-permission-policy-BI1QK_98.mjs} +2 -2
- package/dist/{session-retirement-B2YfbAAT.js → .setup/session-retirement-BH-S6qkJ.mjs} +26 -7
- package/dist/{settled-turn-context-Du6gdUDd.js → .setup/settled-turn-context-fmwouT5I.mjs} +2 -2
- package/dist/{settled-turn-finalizer-Dn91stmc.js → .setup/settled-turn-finalizer-DiAi6Kx6.mjs} +12 -12
- package/dist/{shared-client-CscigXXL.js → .setup/shared-client-DA4VR4Eb.mjs} +332 -221
- package/dist/{side-question-BGMJQf4U.js → .setup/side-question-BJ1wSDcB.mjs} +183 -230
- package/dist/{source-CElXU0Oy.js → .setup/source-CUzJq5WN.mjs} +11 -10
- package/dist/.setup/spawn-error-CL3n6MAa.mjs +57 -0
- package/dist/{thread-archive-guard-DfpBr-Sz.js → .setup/thread-archive-guard-C9frICzX.mjs} +10 -3
- package/dist/.setup/thread-history-page-DoVpnHim.mjs +83 -0
- package/dist/{thread-lifecycle-DXl1-I6b.js → .setup/thread-lifecycle-B3mfz1TG.mjs} +347 -2318
- package/dist/{thread-ownership-BsDRTsyp.js → .setup/thread-ownership-DcTtAcXj.mjs} +6 -4
- package/dist/{thread-policy-BuEQfE0g.js → .setup/thread-policy-DGuQsuFC.mjs} +19 -3
- package/dist/.setup/thread-requests-BLvGkP2R.mjs +2205 -0
- package/dist/{thread-resume-DsDOZpFg.js → .setup/thread-resume-DIBXwOpE.mjs} +8 -5
- package/dist/.setup/timeout-C910MdAB.mjs +80 -0
- package/dist/{transcript-history-projection-v17wXgdK.js → .setup/transcript-history-projection-ZZpkFkJ_.mjs} +96 -61
- package/dist/{transcript-mirror-C5CKgNy6.js → .setup/transcript-mirror-BtHDBp1G.mjs} +224 -195
- package/dist/{transcript-mirror-attestation-DibE0krH.js → .setup/transcript-mirror-attestation-CzM-94NC.mjs} +4 -1
- package/dist/{transport-stdio-BGjHoYLr.js → .setup/transport-stdio-h2VHOskC.mjs} +274 -89
- package/dist/{upstream-session-fork-BZerMte_.js → .setup/upstream-session-fork-BUr4OUbC.mjs} +51 -42
- package/dist/{usage-tCk-mg_x.js → .setup/usage-BML0r-ua.mjs} +5 -4
- package/dist/.setup/usage-limit-error-DUMbefVY.mjs +166 -0
- package/dist/{web-search-provider.runtime-B7bGbGWq.js → .setup/web-search-provider.runtime-CDKlN-Rv.mjs} +4 -3
- package/dist/api.js +23 -12
- package/dist/catalog-page-worker-entrypoint.js +12 -0
- package/dist/catalog-page.worker.js +9 -0
- package/dist/cli-metadata.js +1 -1
- package/dist/doctor-contract-api.js +8 -528
- package/dist/harness.js +1 -1
- package/dist/index.js +306 -215
- package/dist/media-understanding-provider.js +3 -3
- package/dist/migration-provider-api.js +1 -1
- package/dist/native-tool-policy.js +12 -0
- package/dist/provider-discovery.js +18 -0
- package/dist/session-history-worker-runtime.js +36 -35
- package/dist/session-history.worker.js +2 -2
- package/dist/web-search-contract-api.js +1 -1
- package/openclaw.plugin.json +354 -69
- package/package.json +14 -8
- package/dist/attempt-context-D44yeDII.js +0 -636
- package/dist/binding-connection-D3udKbIE.js +0 -60
- package/dist/event-projector-usage-BfXmpOec.js +0 -102
- package/dist/inference-proxy-HGXKUIre.js +0 -311
- package/dist/launch-args-RXQwn8zV.js +0 -90
- package/dist/native-subagent-history-owner-Pcp7JuVF.js +0 -34
- package/dist/native-subagent-monitor-fQ7eIXEf.js +0 -2978
- package/dist/node-exec-server.runtime-Cl2vapEi.js +0 -226
- package/dist/rpc-error-ttN_QjEm.js +0 -30
- package/dist/session-catalog-ct-AH01I.js +0 -1477
- package/dist/session-catalog-home-id-CwhLdC03.js +0 -18
- package/dist/session-catalog-provenance-D-dIfRAH.js +0 -96
- package/dist/timeout-BVw8aaM1.js +0 -33
- package/dist/version-omm6IPI3.js +0 -12
- /package/dist/{auth-profile-selection-DFu9e167.js → .setup/auth-profile-selection-DFu9e167.mjs} +0 -0
- /package/dist/{config-utils-DujwEnhg.js → .setup/config-utils-DujwEnhg.mjs} +0 -0
- /package/dist/{context-compaction-activity-CMla5FPJ.js → .setup/context-compaction-activity-CMla5FPJ.mjs} +0 -0
- /package/dist/{elicitation-input-DPoK8F7w.js → .setup/elicitation-input-DPoK8F7w.mjs} +0 -0
- /package/dist/{history-rejection-B4nOXNXF.js → .setup/history-rejection-B4nOXNXF.mjs} +0 -0
- /package/dist/{image-payload-sanitizer-tupAr-2o.js → .setup/image-payload-sanitizer-tupAr-2o.mjs} +0 -0
- /package/dist/{incognito-session-uhrBF6wJ.js → .setup/incognito-session-uhrBF6wJ.mjs} +0 -0
- /package/dist/{local-runtime-attribution-B_2mnqhb.js → .setup/local-runtime-attribution-B_2mnqhb.mjs} +0 -0
- /package/dist/{native-execution-policy-umd0tgh4.js → .setup/native-execution-policy-umd0tgh4.mjs} +0 -0
- /package/dist/{rolldown-runtime-8H4AJuhK.js → .setup/rolldown-runtime-8H4AJuhK.mjs} +0 -0
- /package/dist/{session-binding-meta-B7aEMU7g.js → .setup/session-binding-meta-B7aEMU7g.mjs} +0 -0
- /package/dist/{session-upstream-marker-D15C9NHp.js → .setup/session-upstream-marker-D15C9NHp.mjs} +0 -0
- /package/dist/{upstream-prompt-provenance-LphB8slG.js → .setup/upstream-prompt-provenance-LphB8slG.mjs} +0 -0
- /package/dist/{web-search-provider.shared-BrZmlqyR.js → .setup/web-search-provider.shared-BrZmlqyR.mjs} +0 -0
|
@@ -1,619 +1,78 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { n as withCodexAppServerThreadMutation, t as isIncognitoSessionKey } from "./incognito-session-uhrBF6wJ.
|
|
8
|
-
import {
|
|
9
|
-
import { t as projectBoundedCodexThreadHistory } from "./transcript-history-projection-
|
|
10
|
-
import {
|
|
11
|
-
import { t as
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import "./
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import { n as
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import { t as
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import
|
|
24
|
-
import { r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread } from "./thread-ownership-BsDRTsyp.js";
|
|
25
|
-
import "./transcript-mirror-C5CKgNy6.js";
|
|
1
|
+
import { m as sessionBindingIdentity, n as assertCodexBindingMayBeReplaced } from "./session-binding-record-BGoz8wOK.mjs";
|
|
2
|
+
import { d as resolveCodexAppServerHomeDir, f as resolveCodexAppServerLocalHomeDir } from "./config-security-BEReZ6go.mjs";
|
|
3
|
+
import { i as isJsonObject, r as flattenCodexDynamicToolFunctions } from "./protocol-CANUwXJ3.mjs";
|
|
4
|
+
import { d as readCodexEffectiveConfig, n as codexSandboxPolicyForTurn, s as assertCodexModelBackedReviewerEffectiveConfig, u as CODEX_SESSION_OVERRIDABLE_LAYER_TYPES } from "./config-options-BvaRs51b.mjs";
|
|
5
|
+
import { i as markStartedCodexManagedThread } from "./managed-thread-store-BMqgThVL.mjs";
|
|
6
|
+
import { f as getCurrentSharedClientEntry, o as readCodexSessionMeta, s as codexCatalogHomeId } from "./session-catalog-events-Bj6j94E_.mjs";
|
|
7
|
+
import { n as withCodexAppServerThreadMutation, t as isIncognitoSessionKey } from "./incognito-session-uhrBF6wJ.mjs";
|
|
8
|
+
import { o as CodexAppServerRpcError, r as withAbortableTimeout, s as isCodexThreadReadMissingError } from "./timeout-C910MdAB.mjs";
|
|
9
|
+
import { t as projectBoundedCodexThreadHistory } from "./transcript-history-projection-ZZpkFkJ_.mjs";
|
|
10
|
+
import { t as buildCodexAppServerConnectionFingerprint } from "./plugin-app-cache-key-B2CsSdnV.mjs";
|
|
11
|
+
import { t as assertCodexSessionRuntimeOwnership } from "./binding-connection-ThoatDcb.mjs";
|
|
12
|
+
import "./config-BoTP_mrL.mjs";
|
|
13
|
+
import { r as sanitizeInlineImageDataUrl, t as invalidInlineImageText } from "./image-payload-sanitizer-tupAr-2o.mjs";
|
|
14
|
+
import { n as normalizeCodexAppServerBindingModelProvider } from "./auth-profile-WqZtZfXN.mjs";
|
|
15
|
+
import { C as CodexThreadBindingConflictError, G as unsubscribeCodexAppServerLiveThread, L as hasCodexAppServerSiblingThreadWork, N as consumeCodexAppServerLiveThread, O as assertCodexInferenceRouteConfig, R as isCodexAppServerClientRuntimeLive, S as CodexAdoptedThreadActiveError, T as CodexThreadStartRequestError, U as releaseCodexAppServerLiveThread, _ as retainSharedCodexAppServerClientByInstanceId, j as prepareCodexInferenceThreadConfig, k as bindCodexInferenceThread, n as captureCodexAppServerClientLifetime, w as CodexThreadClientReplacementError, z as isCodexAppServerLiveThreadClaimed } from "./shared-client-DA4VR4Eb.mjs";
|
|
16
|
+
import { d as isCodexAppServerRequestTimeoutError, l as isCodexAppServerOverloadError, p as resolveCodexAppServerClientInstanceId, r as getCodexAppServerClientInstanceId, v as assertCodexThreadAcceptsDirectInput, x as assertCodexThreadStartResponse, y as assertCodexThreadForkResponse } from "./client-Cs08OXVQ.mjs";
|
|
17
|
+
import { c as isCodexAppServerUnsafeSubscriptionError, g as hasCodexAppServerSiblingRouteWork, n as CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS, o as closeCodexStartupClientBestEffort, p as unsubscribeCodexThreadBestEffort, r as CodexAppServerUnsafeSubscriptionError, u as retireUnsafeCodexTurnClientBestEffort } from "./attempt-client-cleanup-CEv1cKwF.mjs";
|
|
18
|
+
import { $ as readActiveCodexTurnIdsFromResume, F as checkCodexThreadAppAvailability, G as areUserMcpServersFingerprintsCompatible, H as resolveCodexNativeSkillIsolation, I as discardUnattestedCodexPluginThread, J as fingerprintCodexThreadConfig, K as codexDynamicToolsFingerprint, L as attestCodexRestrictedToolSurfaceMcpServersDisabled, P as attestCodexThreadToolSurface, Q as legacyFingerprintUserMcpServersConfigPatch, R as hasCodexNativeToolCatalog, T as assertCodexNativeHookRelayAllowed, Tt as buildCodexAppApprovalOverrides, V as applyCodexNativeSkillIsolation, W as areDynamicToolFingerprintsCompatible, X as fingerprintJsonObject, Y as fingerprintEnvironmentSelection, Z as fingerprintUserMcpServersConfigPatch, _ as resolveCodexAppServerRequestModelSelection, bt as isCodexPluginThreadBindingStale, c as codexThreadSandboxOrPermissions, ct as isMessageOnlyCodexSourceReply, d as resolveCodexThreadApprovalsReviewer, et as shouldStartTransientNoToolThread, g as resolveCodexAppServerModelProvider, h as CODEX_NATIVE_PERSONALITY_NONE, i as buildCodexRuntimeThreadConfigForRun, it as shouldRotateCodexGpt56MultiAgentBinding, l as readCodexInheritedMcpServerNames, lt as isSystemAgentOnlyCodexDynamicToolAllowlist, m as buildDeveloperInstructions, n as buildCodexRingZeroThreadConfigPatch, nt as shouldRecheckRecoverablePluginBinding, o as buildThreadResumeParams, p as resolveCodexWebSearchPlan, pt as buildCodexPluginAppsConfigPatchFromPolicyContext, q as codexLegacyDynamicToolsFingerprint, rt as shouldRotateCodexAppServerBindingForRuntime, s as buildThreadStartParams, t as assertCodexManagedRequirementsDoNotOverrideToolPolicy, tt as isTransientWebSearchRestriction, u as readCodexManagedRequirementsFingerprint, v as resolveCodexAppServerThreadModelSelection, vt as buildPluginAppPolicyContext, wt as stringifyCodexPluginPolicy, x as mergeCodexNativeProjectDocThreadConfig, xt as mergeCodexThreadConfigs, yt as disableUnlistedCodexApps, z as loadCodexNativeToolCatalog } from "./thread-requests-BLvGkP2R.mjs";
|
|
19
|
+
import { o as resolveCodexSessionBinding, r as hashCodexAppServerBindingFingerprint } from "./session-binding-Cm0apEbd.mjs";
|
|
20
|
+
import { a as refreshCodexThreadPolicy, i as assertCodexSupervisionThreadLineage, n as CodexThreadPolicyHandoffError, r as assertAdoptedCodexThreadResumeAllowed, t as CodexIncognitoPolicyChangeError } from "./thread-policy-DGuQsuFC.mjs";
|
|
21
|
+
import { t as resumeCodexAppServerThread } from "./thread-resume-DIBXwOpE.mjs";
|
|
22
|
+
import { r as retainCodexAppServerBindingSubscription, s as withExclusiveCodexAppServerThread, t as isSameCodexAppServerThreadOwner } from "./thread-ownership-DcTtAcXj.mjs";
|
|
23
|
+
import "./transcript-mirror-BtHDBp1G.mjs";
|
|
26
24
|
import { asOptionalRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
27
|
-
import
|
|
28
|
-
import
|
|
25
|
+
import crypto from "node:crypto";
|
|
26
|
+
import { isDeepStrictEqual } from "node:util";
|
|
29
27
|
import { resolveSessionAgentIdsStrict } from "openclaw/plugin-sdk/agent-scope-runtime";
|
|
30
|
-
import { addTimerTimeoutGraceMs, finiteSecondsToTimerSafeMilliseconds } from "openclaw/plugin-sdk/number-runtime";
|
|
31
28
|
import path from "node:path";
|
|
32
|
-
import os from "node:os";
|
|
33
|
-
import fs from "node:fs/promises";
|
|
34
|
-
import { expectDefined } from "openclaw/plugin-sdk/expect-runtime";
|
|
35
29
|
import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
36
|
-
import { createDeferred } from "openclaw/plugin-sdk/extension-shared";
|
|
37
30
|
import { createStageTimingTracker, formatStageTimings } from "openclaw/plugin-sdk/time-runtime";
|
|
38
|
-
import { isPathInside } from "openclaw/plugin-sdk/file-access-runtime";
|
|
39
31
|
import { resolveAgentDir as resolveAgentDir$1 } from "openclaw/plugin-sdk/agent-runtime";
|
|
40
|
-
import { AgentHarnessPreflightError,
|
|
41
|
-
import { isDeepStrictEqual } from "node:util";
|
|
42
|
-
import { emitTrustedDiagnosticEvent } from "openclaw/plugin-sdk/diagnostic-runtime";
|
|
32
|
+
import { AgentHarnessPreflightError, buildHarnessVisibleReplyGuidance, buildTemporalContextText, embeddedAgentLog, formatErrorMessage, isActiveHarnessContextEngine, isHostScopedAgentToolActive, isOpenClawRuntimeContextCustomMessage } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
43
33
|
import { redactSensitiveFieldValue, redactToolPayloadText } from "openclaw/plugin-sdk/logging-core";
|
|
44
34
|
import { IMAGE_BLOCK_TOKENS } from "openclaw/plugin-sdk/agent-core";
|
|
45
|
-
import { resolveRequiredHomeDir, resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
46
35
|
import { buildCodexUserMcpServersThreadConfigPatchForRun } from "openclaw/plugin-sdk/codex-mcp-projection";
|
|
47
|
-
import { registerRetainedNativeHookRelayForBundledRuntime } from "openclaw/plugin-sdk/native-hook-relay-runtime";
|
|
48
36
|
import { isDiagnosticFlagEnabled } from "openclaw/plugin-sdk/diagnostic-flags";
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (Object.keys(fields).some((field) => value[field] !== void 0 && value[field] !== null)) projected.push([name, fields]);
|
|
69
|
-
}
|
|
70
|
-
if (projected.length > 0) overrides[section] = Object.fromEntries(projected);
|
|
71
|
-
}
|
|
72
|
-
return overrides;
|
|
73
|
-
}
|
|
74
|
-
//#endregion
|
|
75
|
-
//#region extensions/codex/src/app-server/plugin-thread-app-admission.ts
|
|
76
|
-
function resolveCodexPluginThreadAppCacheKey(params) {
|
|
77
|
-
return params.threadId ? `${params.appCacheKey}:thread:${encodeURIComponent(params.threadId)}` : params.appCacheKey;
|
|
78
|
-
}
|
|
79
|
-
function createCodexPluginThreadAppInventoryRequest(params) {
|
|
80
|
-
return async (method, requestParams) => await params.request(method, (method === "app/installed" || method === "app/read") && params.threadId ? {
|
|
81
|
-
...requestParams,
|
|
82
|
-
threadId: params.threadId
|
|
83
|
-
} : requestParams);
|
|
84
|
-
}
|
|
85
|
-
async function refreshCodexPluginAppInventory(params, appCache, options = {}) {
|
|
86
|
-
if (!params.appCacheKey) return;
|
|
87
|
-
const request = createCodexPluginThreadAppInventoryRequest(params);
|
|
88
|
-
try {
|
|
89
|
-
return await appCache.refreshNow({
|
|
90
|
-
key: resolveCodexPluginThreadAppCacheKey(params),
|
|
91
|
-
request,
|
|
92
|
-
nowMs: params.nowMs,
|
|
93
|
-
forceRefetch: options.forceRefetch,
|
|
94
|
-
targetAppIds: options.targetAppIds
|
|
95
|
-
});
|
|
96
|
-
} catch (error) {
|
|
97
|
-
embeddedAgentLog.warn("codex plugin thread config app inventory refresh failed", {
|
|
98
|
-
reason: options.reason,
|
|
99
|
-
forceRefetch: options.forceRefetch === true,
|
|
100
|
-
error: serializeCodexAppInventoryError(error)
|
|
101
|
-
});
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
function collectCodexPluginOwnedAppIds(inventory) {
|
|
106
|
-
return Array.from(new Set(inventory.records.flatMap((record) => record.ownedAppIds).filter(Boolean))).toSorted();
|
|
107
|
-
}
|
|
108
|
-
function collectCodexReservedPluginAppIds(params) {
|
|
109
|
-
const reserved = new Set(params.inventory.records.flatMap((record) => record.appOwnership === "proven" ? record.ownedAppIds : []));
|
|
110
|
-
const recordsByConfigKey = new Map(params.inventory.records.map((record) => [record.policy.configKey, record]));
|
|
111
|
-
const configuredOwnerNames = new Set(params.policy.pluginPolicies.flatMap((policy) => {
|
|
112
|
-
const record = recordsByConfigKey.get(policy.configKey);
|
|
113
|
-
return [
|
|
114
|
-
policy.configKey,
|
|
115
|
-
policy.pluginName,
|
|
116
|
-
record?.summary.name,
|
|
117
|
-
record?.summary.id
|
|
118
|
-
].filter((name) => Boolean(name)).map(normalizeCodexPluginOwnerName);
|
|
119
|
-
}));
|
|
120
|
-
for (const app of params.accountApps) if (app.pluginDisplayNames.some((name) => configuredOwnerNames.has(normalizeCodexPluginOwnerName(name)))) reserved.add(app.id);
|
|
121
|
-
return reserved;
|
|
122
|
-
}
|
|
123
|
-
function normalizeCodexPluginOwnerName(name) {
|
|
124
|
-
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
125
|
-
}
|
|
126
|
-
async function readCodexThreadAdmissibleAccountApps(params, appCache) {
|
|
127
|
-
const request = createCodexPluginThreadAppInventoryRequest(params);
|
|
128
|
-
const cachedInventory = appCache.read({
|
|
129
|
-
key: resolveCodexPluginThreadAppCacheKey(params),
|
|
130
|
-
request,
|
|
131
|
-
nowMs: params.nowMs,
|
|
132
|
-
suppressRefresh: true
|
|
133
|
-
});
|
|
134
|
-
const snapshot = cachedInventory.state === "fresh" && !cachedInventory.snapshot?.targetAppIds?.length ? cachedInventory.snapshot : await refreshCodexPluginAppInventory(params, appCache, {
|
|
135
|
-
forceRefetch: false,
|
|
136
|
-
reason: "account_apps_all",
|
|
137
|
-
targetAppIds: []
|
|
138
|
-
});
|
|
139
|
-
if (!snapshot) return {
|
|
140
|
-
apps: [],
|
|
141
|
-
diagnostic: {
|
|
142
|
-
code: "account_app_inventory_unavailable",
|
|
143
|
-
message: "Codex account app inventory was unavailable; account apps were not exposed."
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
const installedAppsById = new Map(snapshot.installedApps.map((app) => [app.id, app]));
|
|
147
|
-
return { apps: snapshot.apps.filter((app) => resolveCodexInstalledAppThreadAdmission(toCodexPluginOwnedAccountApp(app), installedAppsById.get(app.id)) !== "blocked").toSorted((left, right) => left.id.localeCompare(right.id)) };
|
|
148
|
-
}
|
|
149
|
-
function toCodexPluginOwnedAccountApp(app) {
|
|
150
|
-
return {
|
|
151
|
-
id: app.id,
|
|
152
|
-
name: app.name,
|
|
153
|
-
accessible: app.isAccessible,
|
|
154
|
-
enabled: app.isEnabled,
|
|
155
|
-
needsAuth: !app.isAccessible,
|
|
156
|
-
...resolveOwnedAppApprovalOverrideKeys(app)
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function resolveCodexThreadConfigAppsForRecord(params) {
|
|
160
|
-
return params.inventory.appInventory?.state === "missing" ? [] : params.record.apps;
|
|
161
|
-
}
|
|
162
|
-
function resolveCodexPluginAppThreadAdmission(app, inventory) {
|
|
163
|
-
const snapshot = inventory.appInventory?.snapshot;
|
|
164
|
-
if (!snapshot) return "blocked";
|
|
165
|
-
return resolveCodexInstalledAppThreadAdmission(app, snapshot.installedApps.find((candidate) => candidate.id === app.id));
|
|
166
|
-
}
|
|
167
|
-
function resolveCodexInstalledAppThreadAdmission(app, installed) {
|
|
168
|
-
if (!app.accessible || app.needsAuth || !installed) return "blocked";
|
|
169
|
-
if (installed.enabled && installed.callable) return "ready";
|
|
170
|
-
return !installed.enabled && !installed.callable ? "provisional" : "blocked";
|
|
171
|
-
}
|
|
172
|
-
async function readCodexConfigForAppAdmission(params) {
|
|
173
|
-
try {
|
|
174
|
-
const response = await params.request("config/read", {
|
|
175
|
-
includeLayers: true,
|
|
176
|
-
...params.configCwd ? { cwd: params.configCwd } : {}
|
|
177
|
-
});
|
|
178
|
-
if (!isJsonObject(response) || !isJsonObject(response.config) || !Array.isArray(response.layers)) throw new Error("Codex config/read omitted effective config or config layers");
|
|
179
|
-
return {
|
|
180
|
-
config: response.config,
|
|
181
|
-
layers: response.layers.flatMap((layer) => {
|
|
182
|
-
if (!isJsonObject(layer)) throw new Error("Codex config/read returned an invalid config layer");
|
|
183
|
-
if (layer.disabledReason !== void 0 && layer.disabledReason !== null) {
|
|
184
|
-
if (typeof layer.disabledReason !== "string") throw new Error("Codex config/read returned an invalid disabled layer");
|
|
185
|
-
return [];
|
|
186
|
-
}
|
|
187
|
-
if (!isJsonObject(layer.config)) throw new Error("Codex config/read returned an invalid layer config");
|
|
188
|
-
if (!isJsonObject(layer.name) || typeof layer.name.type !== "string") throw new Error("Codex config/read returned an invalid config layer source");
|
|
189
|
-
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`);
|
|
190
|
-
return [layer.config];
|
|
191
|
-
})
|
|
192
|
-
};
|
|
193
|
-
} catch (error) {
|
|
194
|
-
const details = serializeCodexAppInventoryError(error);
|
|
195
|
-
embeddedAgentLog.warn("codex plugin app admission config read failed", { error: details });
|
|
196
|
-
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 });
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function resolveCodexExplicitAppEnablement(layersHighestPrecedenceFirst, appId) {
|
|
200
|
-
for (const layer of layersHighestPrecedenceFirst) {
|
|
201
|
-
const apps = layer.apps;
|
|
202
|
-
const app = isJsonObject(apps) ? apps[appId] : void 0;
|
|
203
|
-
if (isJsonObject(app) && Object.hasOwn(app, "enabled")) return app.enabled === true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
function shouldForceRefreshCodexNotReadyPluginApps(params, policy, inventory) {
|
|
207
|
-
if (!params.appCacheKey || !policy.pluginPolicies.some((plugin) => plugin.enabled) || inventory.appInventory?.state === "missing") return false;
|
|
208
|
-
return inventory.records.some((record) => record.appOwnership === "proven" && record.ownedAppIds.length > 0 && (record.apps.length === 0 || record.apps.some((app) => !app.accessible)));
|
|
209
|
-
}
|
|
210
|
-
//#endregion
|
|
211
|
-
//#region extensions/codex/src/app-server/plugin-thread-config.ts
|
|
212
|
-
/**
|
|
213
|
-
* Builds Codex thread config patches that expose only policy-approved apps
|
|
214
|
-
* for native Codex turns.
|
|
215
|
-
*/
|
|
216
|
-
const CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION = 6;
|
|
217
|
-
const CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION = 2;
|
|
218
|
-
/** Returns true when plugin config exists and thread config may need app patches. */
|
|
219
|
-
function shouldBuildCodexPluginThreadConfig(pluginConfig) {
|
|
220
|
-
return resolveCodexPluginsPolicy(pluginConfig).configured;
|
|
221
|
-
}
|
|
222
|
-
/** Fingerprints policy and app-cache identity before runtime inventory is read. */
|
|
223
|
-
function buildCodexPluginThreadConfigInputFingerprint(params) {
|
|
224
|
-
const policy = resolveCodexPluginsPolicy(params.pluginConfig);
|
|
225
|
-
return fingerprintJson({
|
|
226
|
-
version: CODEX_PLUGIN_THREAD_CONFIG_INPUT_FINGERPRINT_VERSION,
|
|
227
|
-
policy: policyFingerprint(policy),
|
|
228
|
-
appCacheKey: params.appCacheKey ?? null
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
/** Builds the deny-all app patch used when plugin discovery exceeds its turn budget. */
|
|
232
|
-
function buildCodexPluginThreadConfigTimeoutFallback(params) {
|
|
233
|
-
return {
|
|
234
|
-
...emptyPluginThreadConfig({
|
|
235
|
-
enabled: true,
|
|
236
|
-
inputFingerprint: buildCodexPluginThreadConfigInputFingerprint(params),
|
|
237
|
-
configPatch: buildDisabledAppsConfigPatch()
|
|
238
|
-
}),
|
|
239
|
-
diagnostics: [{
|
|
240
|
-
code: "plugin_config_timeout",
|
|
241
|
-
message: params.message
|
|
242
|
-
}]
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
/** Builds the Codex apps config patch and policy context for a native thread. */
|
|
246
|
-
async function buildCodexPluginThreadConfig(params) {
|
|
247
|
-
const appCache = params.appCache ?? defaultCodexAppInventoryCache;
|
|
248
|
-
const threadAppCacheKey = resolveCodexPluginThreadAppCacheKey(params);
|
|
249
|
-
const threadRequest = (method, requestParams) => params.request(method, (method === "app/installed" || method === "app/read") && params.threadId && isJsonObject(requestParams) ? {
|
|
250
|
-
...requestParams,
|
|
251
|
-
threadId: params.threadId
|
|
252
|
-
} : requestParams);
|
|
253
|
-
let inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
|
|
254
|
-
pluginConfig: params.pluginConfig,
|
|
255
|
-
appCacheKey: params.appCacheKey
|
|
256
|
-
});
|
|
257
|
-
const policy = resolveCodexPluginsPolicy(params.pluginConfig);
|
|
258
|
-
if (!policy.enabled) return emptyPluginThreadConfig({
|
|
259
|
-
enabled: false,
|
|
260
|
-
inputFingerprint,
|
|
261
|
-
configPatch: buildDisabledAppsConfigPatch()
|
|
262
|
-
});
|
|
263
|
-
let inventory = policy.pluginPolicies.length > 0 ? await readCodexPluginInventory({
|
|
264
|
-
pluginConfig: params.pluginConfig,
|
|
265
|
-
policy,
|
|
266
|
-
request: threadRequest,
|
|
267
|
-
appCache,
|
|
268
|
-
appCacheKey: threadAppCacheKey,
|
|
269
|
-
configCwd: params.configCwd,
|
|
270
|
-
metadataCache: params.metadataCache,
|
|
271
|
-
nowMs: params.nowMs,
|
|
272
|
-
suppressAppInventoryRefresh: true
|
|
273
|
-
}) : emptyCodexPluginInventory(policy);
|
|
274
|
-
const appInventoryRefreshDeferredForActivation = inventory.records.some((record) => record.activationRequired) && shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
275
|
-
if (shouldWaitForInitialAppInventory(params, policy, inventory)) {
|
|
276
|
-
await refreshCodexPluginAppInventory(params, appCache, {
|
|
277
|
-
forceRefetch: false,
|
|
278
|
-
reason: "initial_missing",
|
|
279
|
-
targetAppIds: collectCodexPluginOwnedAppIds(inventory)
|
|
280
|
-
});
|
|
281
|
-
inventory = await readCodexPluginInventory({
|
|
282
|
-
pluginConfig: params.pluginConfig,
|
|
283
|
-
policy,
|
|
284
|
-
request: threadRequest,
|
|
285
|
-
appCache,
|
|
286
|
-
appCacheKey: threadAppCacheKey,
|
|
287
|
-
configCwd: params.configCwd,
|
|
288
|
-
metadataCache: params.metadataCache,
|
|
289
|
-
nowMs: params.nowMs
|
|
290
|
-
});
|
|
291
|
-
inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
|
|
292
|
-
pluginConfig: params.pluginConfig,
|
|
293
|
-
appCacheKey: params.appCacheKey
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
const activationDiagnostics = [];
|
|
297
|
-
const activationResults = [];
|
|
298
|
-
for (const record of inventory.records) {
|
|
299
|
-
if (!record.activationRequired) continue;
|
|
300
|
-
const activation = await ensureCodexPluginActivation({
|
|
301
|
-
identity: record.policy,
|
|
302
|
-
request: threadRequest,
|
|
303
|
-
appCache,
|
|
304
|
-
appCacheKey: threadAppCacheKey,
|
|
305
|
-
configCwd: params.configCwd,
|
|
306
|
-
metadataCache: params.metadataCache,
|
|
307
|
-
deferAppInventoryRefresh: true,
|
|
308
|
-
targetAppIds: record.ownedAppIds
|
|
309
|
-
});
|
|
310
|
-
activationResults.push(activation);
|
|
311
|
-
if (!activation.ok) activationDiagnostics.push({
|
|
312
|
-
code: "plugin_activation_failed",
|
|
313
|
-
plugin: record.policy,
|
|
314
|
-
message: activation.diagnostics.map((item) => item.message).join(" ") || activation.reason
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
const postInstallRefreshRequired = activationResults.some((activation) => activation.ok && activation.installAttempted);
|
|
318
|
-
const deferredMissingRefreshRequired = appInventoryRefreshDeferredForActivation && !postInstallRefreshRequired && shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
319
|
-
if (postInstallRefreshRequired || deferredMissingRefreshRequired) {
|
|
320
|
-
await refreshCodexPluginAppInventory(params, appCache, {
|
|
321
|
-
forceRefetch: true,
|
|
322
|
-
reason: postInstallRefreshRequired ? "post_install" : "deferred_missing",
|
|
323
|
-
targetAppIds: collectCodexPluginOwnedAppIds(inventory)
|
|
324
|
-
});
|
|
325
|
-
inventory = await readCodexPluginInventory({
|
|
326
|
-
pluginConfig: params.pluginConfig,
|
|
327
|
-
policy,
|
|
328
|
-
request: threadRequest,
|
|
329
|
-
appCache,
|
|
330
|
-
appCacheKey: threadAppCacheKey,
|
|
331
|
-
configCwd: params.configCwd,
|
|
332
|
-
metadataCache: params.metadataCache,
|
|
333
|
-
nowMs: params.nowMs
|
|
334
|
-
});
|
|
335
|
-
inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
|
|
336
|
-
pluginConfig: params.pluginConfig,
|
|
337
|
-
appCacheKey: params.appCacheKey
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
if (shouldForceRefreshCodexNotReadyPluginApps(params, policy, inventory)) {
|
|
341
|
-
await refreshCodexPluginAppInventory(params, appCache, {
|
|
342
|
-
forceRefetch: true,
|
|
343
|
-
reason: "not_ready_plugin_apps",
|
|
344
|
-
targetAppIds: collectCodexPluginOwnedAppIds(inventory)
|
|
345
|
-
});
|
|
346
|
-
inventory = await readCodexPluginInventory({
|
|
347
|
-
pluginConfig: params.pluginConfig,
|
|
348
|
-
policy,
|
|
349
|
-
request: threadRequest,
|
|
350
|
-
appCache,
|
|
351
|
-
appCacheKey: threadAppCacheKey,
|
|
352
|
-
configCwd: params.configCwd,
|
|
353
|
-
metadataCache: params.metadataCache,
|
|
354
|
-
nowMs: params.nowMs
|
|
355
|
-
});
|
|
356
|
-
inputFingerprint = buildCodexPluginThreadConfigInputFingerprint({
|
|
357
|
-
pluginConfig: params.pluginConfig,
|
|
358
|
-
appCacheKey: params.appCacheKey
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
const accountAppsResult = policy.allowAllPlugins ? await readCodexThreadAdmissibleAccountApps(params, appCache) : { apps: [] };
|
|
362
|
-
let appAdmissionConfig;
|
|
363
|
-
const getAdmissionConfig = () => appAdmissionConfig ??= readCodexConfigForAppAdmission(params);
|
|
364
|
-
const diagnostics = [
|
|
365
|
-
...inventory.diagnostics,
|
|
366
|
-
...activationDiagnostics,
|
|
367
|
-
...accountAppsResult.diagnostic ? [accountAppsResult.diagnostic] : []
|
|
368
|
-
];
|
|
369
|
-
const provisionalAppIds = /* @__PURE__ */ new Set();
|
|
370
|
-
const { apps } = buildDisabledAppsConfigPatch();
|
|
371
|
-
const policyApps = {};
|
|
372
|
-
const pluginAppIds = {};
|
|
373
|
-
const pluginOwnedAppIds = collectCodexReservedPluginAppIds({
|
|
374
|
-
policy,
|
|
375
|
-
inventory,
|
|
376
|
-
accountApps: accountAppsResult.apps
|
|
377
|
-
});
|
|
378
|
-
const unresolvedDisabledPluginOwnership = policy.allowAllPlugins ? policy.pluginPolicies.find((pluginPolicy) => {
|
|
379
|
-
const record = inventory.records.find((candidate) => candidate.policy.configKey === pluginPolicy.configKey);
|
|
380
|
-
const disabledByMarketplacePolicy = record?.summary.availability === "DISABLED_BY_ADMIN" || record?.summary.installPolicy === "NOT_AVAILABLE";
|
|
381
|
-
const unresolvedPluginIdentity = !record && inventory.diagnostics.some((diagnostic) => diagnostic.plugin?.configKey === pluginPolicy.configKey && (diagnostic.code === "plugin_disabled" || diagnostic.code === "plugin_missing" || diagnostic.code === "marketplace_missing"));
|
|
382
|
-
return (!pluginPolicy.enabled || disabledByMarketplacePolicy || unresolvedPluginIdentity) && !record?.detail;
|
|
383
|
-
}) : void 0;
|
|
384
|
-
if (unresolvedDisabledPluginOwnership) diagnostics.push({
|
|
385
|
-
code: "account_app_ownership_unavailable",
|
|
386
|
-
plugin: unresolvedDisabledPluginOwnership,
|
|
387
|
-
message: `Could not verify disabled Codex plugin app ownership for ${unresolvedDisabledPluginOwnership.pluginName}; account apps were not exposed.`
|
|
388
|
-
});
|
|
389
|
-
for (const record of inventory.records) {
|
|
390
|
-
if (!record.policy.enabled) continue;
|
|
391
|
-
const activation = activationResults.find((item) => item.identity.configKey === record.policy.configKey);
|
|
392
|
-
if (activation?.ok === false || record.activationRequired && !activation?.ok) continue;
|
|
393
|
-
if (record.appOwnership !== "proven") continue;
|
|
394
|
-
pluginAppIds[record.policy.configKey] = [...record.ownedAppIds].toSorted();
|
|
395
|
-
for (const app of resolveCodexThreadConfigAppsForRecord({
|
|
396
|
-
record,
|
|
397
|
-
inventory
|
|
398
|
-
})) {
|
|
399
|
-
const admissionConfig = resolveCodexPluginAppThreadAdmission(app, inventory) === "blocked" ? void 0 : await getAdmissionConfig();
|
|
400
|
-
if (!admissionConfig || resolveCodexExplicitAppEnablement(admissionConfig.layers, app.id) === false) {
|
|
401
|
-
diagnostics.push({
|
|
402
|
-
code: "app_not_ready",
|
|
403
|
-
plugin: record.policy,
|
|
404
|
-
message: `${app.id} is not accessible for ${record.policy.pluginName}.`
|
|
405
|
-
});
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
provisionalAppIds.add(app.id);
|
|
409
|
-
apps[app.id] = buildEnabledAppConfig(record.policy, record.policy.destructiveApprovalMode === "ask" ? buildCodexAppApprovalOverrides(admissionConfig.config, app) : void 0);
|
|
410
|
-
policyApps[app.id] = {
|
|
411
|
-
configKey: record.policy.configKey,
|
|
412
|
-
marketplaceName: record.policy.marketplaceName,
|
|
413
|
-
pluginName: record.policy.pluginName,
|
|
414
|
-
allowDestructiveActions: record.policy.allowDestructiveActions,
|
|
415
|
-
allowOpenWorld: true,
|
|
416
|
-
destructiveApprovalMode: record.policy.destructiveApprovalMode,
|
|
417
|
-
mcpServerNames: [...record.detail?.mcpServers ?? []].toSorted()
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
for (const app of unresolvedDisabledPluginOwnership ? [] : accountAppsResult.apps) {
|
|
422
|
-
if (pluginOwnedAppIds.has(app.id)) continue;
|
|
423
|
-
const admissionConfig = await getAdmissionConfig();
|
|
424
|
-
if (resolveCodexExplicitAppEnablement(admissionConfig.layers, app.id) === false) continue;
|
|
425
|
-
const accountApp = toCodexPluginOwnedAccountApp(app);
|
|
426
|
-
provisionalAppIds.add(app.id);
|
|
427
|
-
apps[app.id] = buildEnabledAppConfig(policy, policy.destructiveApprovalMode === "ask" ? buildCodexAppApprovalOverrides(admissionConfig.config, accountApp) : void 0);
|
|
428
|
-
policyApps[app.id] = {
|
|
429
|
-
source: "account",
|
|
430
|
-
appName: app.name,
|
|
431
|
-
allowDestructiveActions: policy.allowDestructiveActions,
|
|
432
|
-
allowOpenWorld: true,
|
|
433
|
-
destructiveApprovalMode: policy.destructiveApprovalMode,
|
|
434
|
-
mcpServerNames: []
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
const configPatch = Object.keys(policyApps).length === 0 ? buildDisabledAppsConfigPatch() : disableUnlistedCodexApps({ apps }, (await getAdmissionConfig()).config);
|
|
438
|
-
const policyContext = buildPluginAppPolicyContext(policyApps, pluginAppIds);
|
|
439
|
-
return {
|
|
440
|
-
enabled: true,
|
|
441
|
-
configPatch,
|
|
442
|
-
...provisionalAppIds.size > 0 ? { provisionalAppIds: Array.from(provisionalAppIds).toSorted() } : {},
|
|
443
|
-
fingerprint: fingerprintJson({
|
|
444
|
-
version: CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION,
|
|
445
|
-
inputFingerprint,
|
|
446
|
-
configPatch,
|
|
447
|
-
policyContext
|
|
448
|
-
}),
|
|
449
|
-
inputFingerprint,
|
|
450
|
-
policyContext,
|
|
451
|
-
inventory,
|
|
452
|
-
diagnostics
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
/** Deep-merges optional Codex thread config patches, returning undefined when empty. */
|
|
456
|
-
function mergeCodexThreadConfigs(...configs) {
|
|
457
|
-
let merged;
|
|
458
|
-
for (const config of configs) {
|
|
459
|
-
if (!config) continue;
|
|
460
|
-
merged = mergeJsonObjects(merged ?? {}, config);
|
|
461
|
-
}
|
|
462
|
-
return merged && Object.keys(merged).length > 0 ? merged : void 0;
|
|
463
|
-
}
|
|
464
|
-
/** Detects when a stored thread binding no longer matches current plugin policy inputs. */
|
|
465
|
-
function isCodexPluginThreadBindingStale(params) {
|
|
466
|
-
if (!params.codexPluginsEnabled) return Boolean(params.bindingFingerprint || params.bindingInputFingerprint || params.hasBindingPolicyContext);
|
|
467
|
-
if (!params.bindingFingerprint || !params.bindingInputFingerprint || !params.hasBindingPolicyContext) return true;
|
|
468
|
-
return params.bindingInputFingerprint !== params.currentInputFingerprint;
|
|
469
|
-
}
|
|
470
|
-
function emptyPluginThreadConfig(params) {
|
|
471
|
-
const policyContext = buildPluginAppPolicyContext({}, {});
|
|
472
|
-
return {
|
|
473
|
-
enabled: params.enabled,
|
|
474
|
-
fingerprint: fingerprintJson({
|
|
475
|
-
version: CODEX_PLUGIN_THREAD_CONFIG_FINGERPRINT_VERSION,
|
|
476
|
-
inputFingerprint: params.inputFingerprint,
|
|
477
|
-
configPatch: params.configPatch ?? null,
|
|
478
|
-
policyContext
|
|
479
|
-
}),
|
|
480
|
-
inputFingerprint: params.inputFingerprint,
|
|
481
|
-
...params.configPatch ? { configPatch: params.configPatch } : {},
|
|
482
|
-
policyContext,
|
|
483
|
-
diagnostics: []
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
function buildDisabledAppsConfigPatch() {
|
|
487
|
-
return {
|
|
488
|
-
"features.apps": false,
|
|
489
|
-
apps: { _default: {
|
|
490
|
-
enabled: false,
|
|
491
|
-
destructive_enabled: false,
|
|
492
|
-
open_world_enabled: false
|
|
493
|
-
} }
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
function disableUnlistedCodexApps(configPatch, nativeConfig) {
|
|
497
|
-
const apps = { ...configPatch.apps };
|
|
498
|
-
for (const id of Object.keys(isJsonObject(nativeConfig.apps) ? nativeConfig.apps : {})) if (id !== "_default" && !Object.hasOwn(apps, id)) apps[id] = { enabled: false };
|
|
499
|
-
return {
|
|
500
|
-
...configPatch,
|
|
501
|
-
apps
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
function buildEnabledAppConfig(policy, approvalOverrides = {}) {
|
|
505
|
-
return {
|
|
506
|
-
...approvalOverrides,
|
|
507
|
-
enabled: true,
|
|
508
|
-
destructive_enabled: policy.allowDestructiveActions,
|
|
509
|
-
open_world_enabled: policy.allowOpenWorld !== false,
|
|
510
|
-
default_tools_approval_mode: "auto",
|
|
511
|
-
...policy.destructiveApprovalMode === "ask" ? { approvals_reviewer: "user" } : {}
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
/** Rebuilds the safe per-thread apps patch persisted with a Codex thread binding. */
|
|
515
|
-
function buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext) {
|
|
516
|
-
const disabledConfigPatch = buildDisabledAppsConfigPatch();
|
|
517
|
-
const { apps } = disabledConfigPatch;
|
|
518
|
-
for (const [appId, policy] of Object.entries(policyContext.apps).toSorted(([left], [right]) => left.localeCompare(right))) apps[appId] = buildEnabledAppConfig(policy);
|
|
519
|
-
return Object.keys(policyContext.apps).length > 0 ? { apps } : disabledConfigPatch;
|
|
520
|
-
}
|
|
521
|
-
/** Projects current ask overrides before a side thread replays its bound app policy. */
|
|
522
|
-
async function refreshCodexPluginAppApprovalPolicy(params) {
|
|
523
|
-
if (Object.keys(params.policyContext.apps).length === 0) return {
|
|
524
|
-
policyContext: params.policyContext,
|
|
525
|
-
configPatch: buildDisabledAppsConfigPatch(),
|
|
526
|
-
diagnostics: []
|
|
527
|
-
};
|
|
528
|
-
const targetApps = Object.entries(params.policyContext.apps).filter(([, app]) => app.destructiveApprovalMode === "ask").toSorted(([left], [right]) => left.localeCompare(right));
|
|
529
|
-
const targetAppIds = targetApps.map(([id]) => id);
|
|
530
|
-
const diagnostics = [];
|
|
531
|
-
const readParams = {
|
|
532
|
-
...params,
|
|
533
|
-
appCacheKey: "approval-policy-replay"
|
|
534
|
-
};
|
|
535
|
-
const [inventory, admissionConfig] = await Promise.all([targetAppIds.length > 0 ? refreshCodexPluginAppInventory(readParams, new CodexAppInventoryCache(), { targetAppIds }) : void 0, readCodexConfigForAppAdmission(readParams)]);
|
|
536
|
-
const configPatch = disableUnlistedCodexApps(buildCodexPluginAppsConfigPatchFromPolicyContext(params.policyContext), admissionConfig.config);
|
|
537
|
-
const currentApps = new Map(inventory?.apps.map((app) => [app.id, toCodexPluginOwnedAccountApp(app)]));
|
|
538
|
-
const apps = { ...params.policyContext.apps };
|
|
539
|
-
for (const [id, policy] of targetApps) {
|
|
540
|
-
const app = currentApps.get(id);
|
|
541
|
-
if (!app) diagnostics.push({
|
|
542
|
-
code: "app_not_ready",
|
|
543
|
-
message: `Could not verify current Codex app approval policy for ${id}; the app was not exposed.`
|
|
544
|
-
});
|
|
545
|
-
else {
|
|
546
|
-
configPatch.apps[id] = buildEnabledAppConfig(policy, buildCodexAppApprovalOverrides(admissionConfig.config, app));
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
delete apps[id];
|
|
550
|
-
configPatch.apps[id] = { enabled: false };
|
|
551
|
-
}
|
|
552
|
-
return {
|
|
553
|
-
policyContext: buildPluginAppPolicyContext(apps, Object.fromEntries(Object.entries(params.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]))),
|
|
554
|
-
configPatch,
|
|
555
|
-
diagnostics
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
function buildPluginAppPolicyContext(apps, pluginAppIds) {
|
|
559
|
-
return {
|
|
560
|
-
fingerprint: fingerprintJson({
|
|
561
|
-
version: 2,
|
|
562
|
-
apps,
|
|
563
|
-
pluginAppIds
|
|
564
|
-
}),
|
|
565
|
-
apps,
|
|
566
|
-
pluginAppIds
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
function shouldWaitForInitialAppInventory(params, policy, inventory) {
|
|
570
|
-
if (inventory.records.some((record) => record.activationRequired)) return false;
|
|
571
|
-
return shouldRefreshMissingAppInventory(params, policy, inventory);
|
|
572
|
-
}
|
|
573
|
-
function shouldRefreshMissingAppInventory(params, policy, inventory) {
|
|
574
|
-
return Boolean(params.appCacheKey && policy.pluginPolicies.some((plugin) => plugin.enabled) && inventory.appInventory?.state === "missing");
|
|
575
|
-
}
|
|
576
|
-
function emptyCodexPluginInventory(policy) {
|
|
577
|
-
return {
|
|
578
|
-
policy,
|
|
579
|
-
records: [],
|
|
580
|
-
diagnostics: []
|
|
581
|
-
};
|
|
582
|
-
}
|
|
583
|
-
function policyFingerprint(policy) {
|
|
584
|
-
return {
|
|
585
|
-
enabled: policy.enabled,
|
|
586
|
-
allowAllPlugins: policy.allowAllPlugins,
|
|
587
|
-
allowDestructiveActions: policy.allowDestructiveActions,
|
|
588
|
-
destructiveApprovalMode: policy.destructiveApprovalMode,
|
|
589
|
-
plugins: policy.pluginPolicies.map((plugin) => ({
|
|
590
|
-
configKey: plugin.configKey,
|
|
591
|
-
marketplaceName: plugin.marketplaceName,
|
|
592
|
-
pluginName: plugin.pluginName,
|
|
593
|
-
enabled: plugin.enabled,
|
|
594
|
-
allowDestructiveActions: plugin.allowDestructiveActions,
|
|
595
|
-
destructiveApprovalMode: plugin.destructiveApprovalMode
|
|
596
|
-
}))
|
|
597
|
-
};
|
|
598
|
-
}
|
|
599
|
-
function mergeJsonObjects(left, right) {
|
|
600
|
-
const merged = {
|
|
601
|
-
...left,
|
|
602
|
-
...right
|
|
603
|
-
};
|
|
604
|
-
for (const [key, value] of Object.entries(right)) {
|
|
605
|
-
const existing = left[key];
|
|
606
|
-
if (Object.hasOwn(left, key) && isJsonObject(existing) && isJsonObject(value)) merged[key] = mergeJsonObjects(existing, value);
|
|
607
|
-
}
|
|
608
|
-
return merged;
|
|
37
|
+
//#region extensions/codex/src/app-server/reasoning-effort.ts
|
|
38
|
+
const CODEX_REASONING_EFFORTS = [
|
|
39
|
+
"minimal",
|
|
40
|
+
"low",
|
|
41
|
+
"medium",
|
|
42
|
+
"high",
|
|
43
|
+
"xhigh",
|
|
44
|
+
"max"
|
|
45
|
+
];
|
|
46
|
+
const LEGACY_PRO_REASONING_EFFORTS = [
|
|
47
|
+
"medium",
|
|
48
|
+
"high",
|
|
49
|
+
"xhigh"
|
|
50
|
+
];
|
|
51
|
+
const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
|
|
52
|
+
const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
|
|
53
|
+
/** Read reasoning metadata after the Codex app-server route has been selected. */
|
|
54
|
+
function readCodexSupportedReasoningEfforts(compat) {
|
|
55
|
+
return compat && "supportedReasoningEfforts" in compat ? compat.supportedReasoningEfforts : void 0;
|
|
609
56
|
}
|
|
610
|
-
function
|
|
611
|
-
|
|
57
|
+
function resolveSupportedReasoningEffort(params) {
|
|
58
|
+
const declared = new Set(params.supportedReasoningEfforts.map((effort) => effort.trim().toLowerCase()));
|
|
59
|
+
const supported = CODEX_REASONING_EFFORTS.filter((effort) => declared.has(effort));
|
|
60
|
+
if (supported.includes(params.requested)) return params.requested;
|
|
61
|
+
const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
|
|
62
|
+
return supported.find((effort) => CODEX_REASONING_EFFORTS.indexOf(effort) >= requestedRank) ?? supported.at(-1);
|
|
612
63
|
}
|
|
613
|
-
function
|
|
614
|
-
if (
|
|
615
|
-
if (
|
|
616
|
-
|
|
64
|
+
function resolveCodexAppServerReasoningEffort(params) {
|
|
65
|
+
if (params.thinkLevel === "ultra") return "ultra";
|
|
66
|
+
if (params.thinkLevel === "off") return params.supportedReasoningEfforts?.includes("none") ? "none" : null;
|
|
67
|
+
if (params.thinkLevel === "adaptive") return null;
|
|
68
|
+
const modelId = params.modelId.trim().toLowerCase();
|
|
69
|
+
const supportedReasoningEfforts = params.supportedReasoningEfforts ?? (LEGACY_PRO_MODEL_ID_RE.test(modelId) ? LEGACY_PRO_REASONING_EFFORTS : void 0);
|
|
70
|
+
if (supportedReasoningEfforts) return resolveSupportedReasoningEffort({
|
|
71
|
+
requested: params.thinkLevel,
|
|
72
|
+
supportedReasoningEfforts
|
|
73
|
+
}) ?? null;
|
|
74
|
+
if (params.thinkLevel === "minimal" && MODERN_GPT_5_MODEL_ID_RE.test(modelId)) return "low";
|
|
75
|
+
return params.thinkLevel === "max" ? null : params.thinkLevel;
|
|
617
76
|
}
|
|
618
77
|
//#endregion
|
|
619
78
|
//#region extensions/codex/src/app-server/context-engine-projection.ts
|
|
@@ -638,8 +97,34 @@ const CODEX_TURN_START_TEXT_INPUT_MAX_CHARS = 1 << 20;
|
|
|
638
97
|
const DEFAULT_CODEX_PROJECTION_RESERVE_TOKENS = 2e4;
|
|
639
98
|
const MIN_PROMPT_BUDGET_RATIO = .5;
|
|
640
99
|
const MIN_PROMPT_BUDGET_TOKENS = 8e3;
|
|
100
|
+
const CODEX_CONTEXT_SENDER_FIELD_MAX_CHARS = 256;
|
|
101
|
+
/**
|
|
102
|
+
* This projection has no access to agent-core's private compaction helper, but
|
|
103
|
+
* must keep the same attribution contract: a stable ID is identity; display
|
|
104
|
+
* labels are optional metadata, never provenance on their own.
|
|
105
|
+
*/
|
|
106
|
+
function formatCodexContextSenderSuffix(message) {
|
|
107
|
+
if (message.role !== "user") return "";
|
|
108
|
+
const metadata = Reflect.get(message, "__openclaw");
|
|
109
|
+
if (!metadata || typeof metadata !== "object") return "";
|
|
110
|
+
const normalize = (value) => {
|
|
111
|
+
if (typeof value !== "string") return;
|
|
112
|
+
const normalized = value.replaceAll("\0", "").trim();
|
|
113
|
+
return normalized ? truncateUtf16Safe(normalized, CODEX_CONTEXT_SENDER_FIELD_MAX_CHARS) : void 0;
|
|
114
|
+
};
|
|
115
|
+
const record = metadata;
|
|
116
|
+
const id = normalize(record.senderId);
|
|
117
|
+
if (!id) return "";
|
|
118
|
+
const name = normalize(record.senderName);
|
|
119
|
+
const username = normalize(record.senderUsername);
|
|
120
|
+
return ` sender=${JSON.stringify({
|
|
121
|
+
id,
|
|
122
|
+
...name ? { name } : {},
|
|
123
|
+
...username ? { username } : {}
|
|
124
|
+
})}`;
|
|
125
|
+
}
|
|
641
126
|
function neutralizeCodexExplicitMentionSigils(text) {
|
|
642
|
-
return text.replace(/\$(?=[A-Za-z0-9_:-])/gu, "$").replace(/\[@(?=[A-Za-z0-9_:-]+\]\()/gu, "[@");
|
|
127
|
+
return text.replace(/\$(?=[A-Za-z0-9_:-])/gu, "$").replace(/\[@(?=[A-Za-z0-9_:-]+\]\s*\()/gu, "[@");
|
|
643
128
|
}
|
|
644
129
|
/** Hidden durable notes are context; transient runtime carriers are current-turn only. */
|
|
645
130
|
function isCodexDurableCustomMessage(message) {
|
|
@@ -675,7 +160,11 @@ async function projectContextEngineAssemblyForCodex(params) {
|
|
|
675
160
|
...promptContextRange ? { promptContextRange } : {},
|
|
676
161
|
assembledMessages: params.assembledMessages,
|
|
677
162
|
prePromptMessageCount: params.originalHistoryMessages.length,
|
|
678
|
-
...context.
|
|
163
|
+
...context.imageGroups.length && promptPrefix ? { imageGroups: context.imageGroups.map((group) => ({
|
|
164
|
+
images: group.images,
|
|
165
|
+
start: group.start + promptPrefix.length,
|
|
166
|
+
end: group.end + promptPrefix.length
|
|
167
|
+
})) } : {}
|
|
679
168
|
};
|
|
680
169
|
}
|
|
681
170
|
/** Resolves rendered context size from a token budget and reserve. */
|
|
@@ -719,32 +208,61 @@ function resolveCodexContinuityProjectionMaxChars(params) {
|
|
|
719
208
|
}
|
|
720
209
|
/** Fits projected context prompts under Codex app-server turn/start text limits. */
|
|
721
210
|
function fitCodexProjectedContextForTurnStart(params) {
|
|
211
|
+
const slice = (start, end, budget = end - start) => {
|
|
212
|
+
const retained = truncateOlderContext(params.promptText.slice(start, end), budget);
|
|
213
|
+
return {
|
|
214
|
+
...retained,
|
|
215
|
+
sourceStart: start + retained.retainedStart,
|
|
216
|
+
sourceEnd: end
|
|
217
|
+
};
|
|
218
|
+
};
|
|
219
|
+
const finish = (...parts) => {
|
|
220
|
+
const imageGroups = [];
|
|
221
|
+
let offset = 0;
|
|
222
|
+
for (const part of parts) {
|
|
223
|
+
for (const group of params.imageGroups ?? []) if (group.start >= part.sourceStart && group.end <= part.sourceEnd) {
|
|
224
|
+
const shift = offset + part.prefixLength - part.sourceStart;
|
|
225
|
+
imageGroups.push({
|
|
226
|
+
...group,
|
|
227
|
+
start: group.start + shift,
|
|
228
|
+
end: group.end + shift
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
offset += part.text.length;
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
promptText: parts.map((part) => part.text).join(""),
|
|
235
|
+
...imageGroups.length ? { imageGroups } : {}
|
|
236
|
+
};
|
|
237
|
+
};
|
|
722
238
|
const maxChars = typeof params.maxChars === "number" && Number.isFinite(params.maxChars) ? Math.max(0, Math.floor(params.maxChars)) : CODEX_TURN_START_TEXT_INPUT_MAX_CHARS;
|
|
723
|
-
if (params.promptText.length <= maxChars) return params.promptText;
|
|
239
|
+
if (params.promptText.length <= maxChars) return finish(slice(0, params.promptText.length));
|
|
724
240
|
const range = normalizeProjectedContextRange(params.contextRange, params.promptText.length);
|
|
725
241
|
if (!range) {
|
|
726
242
|
const preservedRange = normalizeProjectedContextRange(params.preservedRange, params.promptText.length);
|
|
727
|
-
if (!preservedRange) return params.promptText;
|
|
243
|
+
if (!preservedRange) return finish(slice(0, params.promptText.length));
|
|
728
244
|
const preservedText = params.promptText.slice(preservedRange.start, preservedRange.end);
|
|
729
|
-
if (!preservedText) return
|
|
730
|
-
if (preservedText.length >= maxChars) return
|
|
731
|
-
return
|
|
245
|
+
if (!preservedText) return finish(slice(0, params.promptText.length, maxChars));
|
|
246
|
+
if (preservedText.length >= maxChars) return finish(slice(preservedRange.start, preservedRange.end, maxChars));
|
|
247
|
+
return finish(slice(0, preservedRange.start, maxChars - preservedText.length), slice(preservedRange.start, preservedRange.end));
|
|
732
248
|
}
|
|
733
249
|
const beforeContext = params.promptText.slice(0, range.start);
|
|
734
|
-
const context = params.promptText.slice(range.start, range.end);
|
|
735
250
|
const afterContext = params.promptText.slice(range.end);
|
|
736
251
|
const requestRange = normalizeProjectedContextRange(params.requestRange, params.promptText.length);
|
|
737
252
|
if (requestRange && requestRange.start >= range.end && requestRange.end < params.promptText.length) {
|
|
738
253
|
const request = params.promptText.slice(requestRange.start, requestRange.end);
|
|
739
|
-
if (request.length >= maxChars) return
|
|
740
|
-
const fittedAppendedContext =
|
|
741
|
-
const
|
|
742
|
-
|
|
254
|
+
if (request.length >= maxChars) return finish(slice(requestRange.start, requestRange.end, maxChars));
|
|
255
|
+
const fittedAppendedContext = slice(requestRange.end, params.promptText.length, maxChars - request.length);
|
|
256
|
+
const contextBudget = maxChars - request.length - fittedAppendedContext.text.length;
|
|
257
|
+
const fittedContext = slice(range.start, range.end, contextBudget);
|
|
258
|
+
const beforeContextBudget = maxChars - fittedContext.text.length - request.length - fittedAppendedContext.text.length;
|
|
259
|
+
return finish(slice(0, range.start, beforeContextBudget), fittedContext, slice(requestRange.start, requestRange.end), fittedAppendedContext);
|
|
743
260
|
}
|
|
744
261
|
const contextBudget = maxChars - beforeContext.length - afterContext.length;
|
|
745
|
-
if (contextBudget > 0) return
|
|
746
|
-
const afterContextText =
|
|
747
|
-
|
|
262
|
+
if (contextBudget > 0) return finish(slice(0, range.start), slice(range.start, range.end, contextBudget), slice(range.end, params.promptText.length));
|
|
263
|
+
const afterContextText = slice(range.end, params.promptText.length, maxChars);
|
|
264
|
+
const contextBudgetAfterRequest = maxChars - afterContextText.text.length;
|
|
265
|
+
return finish(slice(range.start, range.end, contextBudgetAfterRequest), afterContextText);
|
|
748
266
|
}
|
|
749
267
|
function normalizeProjectedContextRange(range, textLength) {
|
|
750
268
|
if (!range) return;
|
|
@@ -765,7 +283,6 @@ function resolveProjectionPromptBudgetTokens(params) {
|
|
|
765
283
|
}
|
|
766
284
|
async function renderMessagesForCodexContext(messages, options) {
|
|
767
285
|
const tail = [];
|
|
768
|
-
const images = [];
|
|
769
286
|
let retainedImageChars = 0;
|
|
770
287
|
let totalChars = 0;
|
|
771
288
|
let retainedChars = 0;
|
|
@@ -786,29 +303,50 @@ async function renderMessagesForCodexContext(messages, options) {
|
|
|
786
303
|
imageChars > 0 && !imagesFit ? "[Attachment images omitted: context budget exceeded]" : void 0
|
|
787
304
|
].filter(Boolean).join("\n\n");
|
|
788
305
|
if (!text && acceptedImageChars === 0) continue;
|
|
789
|
-
const
|
|
306
|
+
const separator = totalChars > 0 ? "\n\n" : "";
|
|
307
|
+
const chunk = `[${message.role}${formatCodexContextSenderSuffix(message)}]\n${text}${separator}`;
|
|
790
308
|
totalChars += chunk.length;
|
|
791
309
|
if (remaining > 0) {
|
|
792
310
|
const retained = neutralizeCodexExplicitMentionSigils(chunk).slice(-(remaining - acceptedImageChars));
|
|
793
|
-
tail.push(
|
|
311
|
+
tail.push({
|
|
312
|
+
text: retained,
|
|
313
|
+
separatorLength: separator.length,
|
|
314
|
+
...imagesFit && files?.images.length && retained.length === chunk.length ? { images: files.images } : {}
|
|
315
|
+
});
|
|
794
316
|
retainedChars += retained.length + acceptedImageChars;
|
|
795
317
|
retainedImageChars += acceptedImageChars;
|
|
796
|
-
if (imagesFit && files?.images.length) images.unshift(...files.images);
|
|
797
318
|
}
|
|
798
319
|
}
|
|
320
|
+
const ordered = tail.toReversed();
|
|
321
|
+
const fitted = truncateOlderContext(ordered.map((entry) => entry.text).join(""), options.maxRenderedContextChars - retainedImageChars, totalChars);
|
|
322
|
+
const imageGroups = [];
|
|
323
|
+
let offset = 0;
|
|
324
|
+
for (const entry of ordered) {
|
|
325
|
+
if (entry.images && offset >= fitted.retainedStart) {
|
|
326
|
+
const start = offset - fitted.retainedStart + fitted.prefixLength;
|
|
327
|
+
imageGroups.push({
|
|
328
|
+
start,
|
|
329
|
+
end: start + entry.text.length - entry.separatorLength,
|
|
330
|
+
images: entry.images
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
offset += entry.text.length;
|
|
334
|
+
}
|
|
799
335
|
return {
|
|
800
|
-
text:
|
|
801
|
-
|
|
336
|
+
text: fitted.text,
|
|
337
|
+
imageGroups
|
|
802
338
|
};
|
|
803
339
|
}
|
|
804
340
|
function renderMessageBody(message, options) {
|
|
805
341
|
if (message.role === "compactionSummary" || message.role === "branchSummary") return truncateText(message.summary.trim(), options.maxTextPartChars);
|
|
806
342
|
if (!hasMessageContent(message)) return "";
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
343
|
+
const toolResult = message.role === "toolResult";
|
|
344
|
+
const toolResultLabel = toolResult && message.toolCallId ? `tool result: ${message.toolCallId}` : "tool result";
|
|
345
|
+
if (toolResult && options.toolPayloadMode === "elide") return `${toolResultLabel} [content omitted]`;
|
|
346
|
+
const body = typeof message.content === "string" ? truncateText(message.content.trim(), options.maxTextPartChars) : Array.isArray(message.content) ? message.content.map((part) => renderMessagePart(part, options, toolResult)).filter((value) => value.length > 0).join("\n").trim() : "[non-text content omitted]";
|
|
347
|
+
return toolResult ? redactToolPayloadText(`${toolResultLabel}${message.toolName ? ` (${message.toolName})` : ""}\n${body}`) : body;
|
|
810
348
|
}
|
|
811
|
-
function renderMessagePart(part, options) {
|
|
349
|
+
function renderMessagePart(part, options, toolResultBody) {
|
|
812
350
|
if (!part || typeof part !== "object") return "";
|
|
813
351
|
const record = part;
|
|
814
352
|
const type = typeof record.type === "string" ? record.type : void 0;
|
|
@@ -821,7 +359,7 @@ function renderMessagePart(part, options) {
|
|
|
821
359
|
}
|
|
822
360
|
if (type === "toolResult" || type === "tool_result") {
|
|
823
361
|
const label = typeof record.toolUseId === "string" ? `tool result: ${record.toolUseId}` : "tool result";
|
|
824
|
-
if (options.toolPayloadMode === "preserve") return truncateText(`${label}\n${stableJson(renderToolResultPayload(record))}`, options.maxTextPartChars);
|
|
362
|
+
if (options.toolPayloadMode === "preserve") return truncateText(`${toolResultBody ? "" : `${label}\n`}${stableJson(renderToolResultPayload(record))}`, options.maxTextPartChars);
|
|
825
363
|
return `${label} [content omitted]`;
|
|
826
364
|
}
|
|
827
365
|
return `[${type ?? "non-text"} content omitted]`;
|
|
@@ -873,8 +411,12 @@ function summarizeToolInputShape(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
873
411
|
return `[${typeof value}]`;
|
|
874
412
|
}
|
|
875
413
|
function redactPreservedToolValue(key, value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
876
|
-
if (typeof value === "string"
|
|
877
|
-
|
|
414
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
415
|
+
const text = String(value);
|
|
416
|
+
const redacted = redactSensitiveFieldValue(key, redactToolPayloadText(text));
|
|
417
|
+
return redacted === text ? value : redacted;
|
|
418
|
+
}
|
|
419
|
+
if (value === null || value === void 0) return value;
|
|
878
420
|
if (Array.isArray(value)) {
|
|
879
421
|
if (seen.has(value)) return "[Circular]";
|
|
880
422
|
seen.add(value);
|
|
@@ -912,15 +454,32 @@ function truncateText(text, maxChars) {
|
|
|
912
454
|
return `${truncated}\n[truncated ${text.length - truncated.length} chars]`;
|
|
913
455
|
}
|
|
914
456
|
function truncateOlderContext(text, maxChars, totalChars = text.length) {
|
|
915
|
-
if (totalChars <= maxChars) return
|
|
916
|
-
|
|
457
|
+
if (totalChars <= maxChars) return {
|
|
458
|
+
text,
|
|
459
|
+
retainedStart: 0,
|
|
460
|
+
prefixLength: 0
|
|
461
|
+
};
|
|
462
|
+
if (maxChars <= 0) return {
|
|
463
|
+
text: "",
|
|
464
|
+
retainedStart: text.length,
|
|
465
|
+
prefixLength: 0
|
|
466
|
+
};
|
|
917
467
|
const buildMarker = (omittedChars) => `[truncated ${omittedChars} chars from older context]\n`;
|
|
918
468
|
let marker = buildMarker(totalChars - maxChars);
|
|
919
469
|
let tailChars = Math.max(0, maxChars - marker.length);
|
|
920
470
|
marker = buildMarker(totalChars - tailChars);
|
|
921
|
-
if (marker.length >= maxChars) return
|
|
471
|
+
if (marker.length >= maxChars) return {
|
|
472
|
+
text: marker.slice(0, maxChars),
|
|
473
|
+
retainedStart: text.length,
|
|
474
|
+
prefixLength: maxChars
|
|
475
|
+
};
|
|
922
476
|
tailChars = maxChars - marker.length;
|
|
923
|
-
|
|
477
|
+
const tail = sliceUtf16Safe(text, -tailChars).trimStart();
|
|
478
|
+
return {
|
|
479
|
+
text: `${marker}${tail}`,
|
|
480
|
+
retainedStart: text.length - tail.length,
|
|
481
|
+
prefixLength: marker.length
|
|
482
|
+
};
|
|
924
483
|
}
|
|
925
484
|
//#endregion
|
|
926
485
|
//#region extensions/codex/src/app-server/thread-context-engine.ts
|
|
@@ -950,1001 +509,28 @@ function buildContextEngineBinding(params, projection) {
|
|
|
950
509
|
function buildContextEngineProjectionBinding(projection) {
|
|
951
510
|
return {
|
|
952
511
|
schemaVersion: 1,
|
|
953
|
-
mode: "thread_bootstrap",
|
|
954
|
-
epoch: projection.epoch,
|
|
955
|
-
fingerprint: projection.fingerprint
|
|
956
|
-
};
|
|
957
|
-
}
|
|
958
|
-
function isContextEngineBindingCompatible(previous, next) {
|
|
959
|
-
return previous?.schemaVersion === next.schemaVersion && previous.engineId === next.engineId && previous.policyFingerprint === next.policyFingerprint && areContextEngineProjectionBindingsCompatible(previous.projection, next.projection);
|
|
960
|
-
}
|
|
961
|
-
function areContextEngineProjectionBindingsCompatible(previous, next) {
|
|
962
|
-
if (!next) return previous === void 0;
|
|
963
|
-
return previous?.schemaVersion === next.schemaVersion && previous.mode === next.mode && previous.epoch === next.epoch && previous.fingerprint === next.fingerprint;
|
|
964
|
-
}
|
|
965
|
-
function resolveContextEngineCitationsMode(config) {
|
|
966
|
-
const rootConfig = isRecord(config) ? config : void 0;
|
|
967
|
-
const citations = (isRecord(rootConfig?.memory) ? rootConfig.memory : void 0)?.citations;
|
|
968
|
-
return isJsonConfigValue(citations) ? citations : void 0;
|
|
969
|
-
}
|
|
970
|
-
function isJsonConfigValue(value) {
|
|
971
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
972
|
-
if (typeof value === "number") return Number.isFinite(value);
|
|
973
|
-
if (Array.isArray(value)) return value.every(isJsonConfigValue);
|
|
974
|
-
return isRecord(value) && Object.values(value).every(isJsonConfigValue);
|
|
975
|
-
}
|
|
976
|
-
//#endregion
|
|
977
|
-
//#region extensions/codex/src/app-server/dynamic-tool-profile.ts
|
|
978
|
-
/** Tool names owned by Codex app-server and normally excluded from OpenClaw dynamic tools. */
|
|
979
|
-
const CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES = [
|
|
980
|
-
"read",
|
|
981
|
-
"write",
|
|
982
|
-
"edit",
|
|
983
|
-
"apply_patch",
|
|
984
|
-
"exec",
|
|
985
|
-
"process",
|
|
986
|
-
"update_plan",
|
|
987
|
-
"tool_call",
|
|
988
|
-
"tool_describe",
|
|
989
|
-
"tool_search",
|
|
990
|
-
"tool_search_code"
|
|
991
|
-
];
|
|
992
|
-
const CODEX_NATIVE_GOAL_TOOL_EXCLUDES = [
|
|
993
|
-
"get_goal",
|
|
994
|
-
"create_goal",
|
|
995
|
-
"update_goal"
|
|
996
|
-
];
|
|
997
|
-
const CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES = /* @__PURE__ */ new Set([
|
|
998
|
-
"read",
|
|
999
|
-
"write",
|
|
1000
|
-
"edit",
|
|
1001
|
-
"apply_patch",
|
|
1002
|
-
...CODEX_NATIVE_GOAL_TOOL_EXCLUDES
|
|
1003
|
-
]);
|
|
1004
|
-
const CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES = /* @__PURE__ */ new Set(["exec", "process"]);
|
|
1005
|
-
const DYNAMIC_TOOL_NAME_ALIASES = {
|
|
1006
|
-
bash: "exec",
|
|
1007
|
-
"apply-patch": "apply_patch"
|
|
1008
|
-
};
|
|
1009
|
-
/** Normalizes OpenClaw/Codex tool names before filtering and allowlist checks. */
|
|
1010
|
-
function normalizeCodexDynamicToolName(name) {
|
|
1011
|
-
const normalized = name.trim().toLowerCase();
|
|
1012
|
-
return DYNAMIC_TOOL_NAME_ALIASES[normalized] ?? normalized;
|
|
1013
|
-
}
|
|
1014
|
-
/** True only for the host-scoped OpenClaw run's exact tool contract. */
|
|
1015
|
-
function isSystemAgentOnlyCodexDynamicToolAllowlist(toolsAllow) {
|
|
1016
|
-
return toolsAllow?.length === 1 && normalizeCodexDynamicToolName(toolsAllow[0] ?? "") === "openclaw";
|
|
1017
|
-
}
|
|
1018
|
-
/** True when a private source reply may use the message delivery tool only. */
|
|
1019
|
-
function isMessageOnlyCodexSourceReply(params) {
|
|
1020
|
-
return params.sourceReplyDeliveryMode === "message_tool_only" && params.toolsAllow?.length === 1 && normalizeCodexDynamicToolName(params.toolsAllow[0] ?? "") === "message";
|
|
1021
|
-
}
|
|
1022
|
-
/** Returns true for private QA runs that force the Codex runtime profile. */
|
|
1023
|
-
function isForcedPrivateQaCodexRuntime(env = process.env) {
|
|
1024
|
-
return env.OPENCLAW_BUILD_PRIVATE_QA === "1" && env.OPENCLAW_QA_FORCE_RUNTIME?.trim().toLowerCase() === "codex";
|
|
1025
|
-
}
|
|
1026
|
-
/** Resolves whether dynamic tools load directly or through Codex tool search. */
|
|
1027
|
-
function resolveCodexDynamicToolsLoading(config, env = process.env) {
|
|
1028
|
-
return isForcedPrivateQaCodexRuntime(env) ? "direct" : config.codexDynamicToolsLoading ?? "searchable";
|
|
1029
|
-
}
|
|
1030
|
-
function normalizeCodexModelId(modelId) {
|
|
1031
|
-
const normalized = modelId?.trim().toLowerCase();
|
|
1032
|
-
if (!normalized) return "";
|
|
1033
|
-
return normalized.includes("/") ? normalized.split("/").at(-1) : normalized;
|
|
1034
|
-
}
|
|
1035
|
-
/** Returns true when model behavior requires direct dynamic-tool registration. */
|
|
1036
|
-
function shouldUseDirectCodexDynamicToolsForModel(modelId) {
|
|
1037
|
-
return shouldDisableCodexToolSearchForModel(modelId);
|
|
1038
|
-
}
|
|
1039
|
-
/** Returns true for models whose tool-search path is unsupported or inefficient. */
|
|
1040
|
-
function shouldDisableCodexToolSearchForModel(modelId) {
|
|
1041
|
-
return normalizeCodexModelId(modelId) === "gpt-5.4-nano";
|
|
1042
|
-
}
|
|
1043
|
-
/** Resolves dynamic-tool loading after applying model-specific restrictions. */
|
|
1044
|
-
function resolveCodexDynamicToolsLoadingForModel(config, modelId, env = process.env) {
|
|
1045
|
-
const loading = resolveCodexDynamicToolsLoading(config, env);
|
|
1046
|
-
return loading === "searchable" && shouldUseDirectCodexDynamicToolsForModel(modelId) ? "direct" : loading;
|
|
1047
|
-
}
|
|
1048
|
-
/** Resolves dynamic-tool loading for the app-server connection that will execute the turn. */
|
|
1049
|
-
function resolveCodexDynamicToolsLoadingForRuntime(config, modelId, options = {}, env = process.env) {
|
|
1050
|
-
const loading = resolveCodexDynamicToolsLoadingForModel(config, modelId, env);
|
|
1051
|
-
return loading === "searchable" && options.connectionClass === "remote" ? "direct" : loading;
|
|
1052
|
-
}
|
|
1053
|
-
/** Filters OpenClaw tools that Codex owns natively or config explicitly excludes. */
|
|
1054
|
-
function filterCodexDynamicTools(tools, config, env = process.env) {
|
|
1055
|
-
return filterCodexDynamicToolsWithOptions(tools, config, env, {
|
|
1056
|
-
preserveOpenClawReplacements: false,
|
|
1057
|
-
preserveOpenClawShell: false
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
|
-
/** Keeps OpenClaw coding tools that replace a disabled Codex native surface. */
|
|
1061
|
-
function filterCodexDynamicToolsForDisabledNativeSurface(tools, config, options, env = process.env) {
|
|
1062
|
-
return filterCodexDynamicToolsWithOptions(tools, config, env, {
|
|
1063
|
-
preserveOpenClawReplacements: true,
|
|
1064
|
-
preserveOpenClawShell: options.preserveShell
|
|
1065
|
-
});
|
|
1066
|
-
}
|
|
1067
|
-
function filterCodexDynamicToolsWithOptions(tools, config, env, options) {
|
|
1068
|
-
const excludes = /* @__PURE__ */ new Set();
|
|
1069
|
-
if (!options.preserveOpenClawReplacements) for (const name of CODEX_NATIVE_GOAL_TOOL_EXCLUDES) excludes.add(name);
|
|
1070
|
-
if (isForcedPrivateQaCodexRuntime(env)) excludes.add("apply_patch");
|
|
1071
|
-
else for (const name of CODEX_APP_SERVER_OWNED_DYNAMIC_TOOL_EXCLUDES) {
|
|
1072
|
-
if (options.preserveOpenClawReplacements && CODEX_APP_SERVER_OWNED_REPLACEABLE_TOOL_EXCLUDES.has(name)) continue;
|
|
1073
|
-
if (options.preserveOpenClawShell && CODEX_APP_SERVER_OWNED_SHELL_TOOL_EXCLUDES.has(name)) continue;
|
|
1074
|
-
excludes.add(name);
|
|
1075
|
-
}
|
|
1076
|
-
for (const name of config.codexDynamicToolsExclude ?? []) {
|
|
1077
|
-
const trimmed = normalizeCodexDynamicToolName(name);
|
|
1078
|
-
if (trimmed) excludes.add(trimmed);
|
|
1079
|
-
}
|
|
1080
|
-
return excludes.size === 0 ? tools : tools.filter((tool) => !excludes.has(normalizeCodexDynamicToolName(tool.name)));
|
|
1081
|
-
}
|
|
1082
|
-
//#endregion
|
|
1083
|
-
//#region extensions/codex/src/app-server/thread-binding-policy.ts
|
|
1084
|
-
function shouldRotateCodexAppServerBindingForRuntime(params) {
|
|
1085
|
-
if (!params.current) return false;
|
|
1086
|
-
if (params.binding === params.current) return false;
|
|
1087
|
-
return params.connectionClass === "remote" || Boolean(params.binding);
|
|
1088
|
-
}
|
|
1089
|
-
function resolveCodexGpt56MultiAgentVersion(modelRef) {
|
|
1090
|
-
let modelId = modelRef?.trim().toLowerCase();
|
|
1091
|
-
if (!modelId) return;
|
|
1092
|
-
const slashIndex = modelId.indexOf("/");
|
|
1093
|
-
if (slashIndex > 0) {
|
|
1094
|
-
const provider = modelId.slice(0, slashIndex);
|
|
1095
|
-
if (provider !== "openai" && provider !== "codex") return;
|
|
1096
|
-
modelId = modelId.slice(slashIndex + 1);
|
|
1097
|
-
}
|
|
1098
|
-
if (modelId === "gpt-5.6-sol" || modelId === "gpt-5.6-terra") return "v2";
|
|
1099
|
-
return modelId === "gpt-5.6-luna" ? "v1" : void 0;
|
|
1100
|
-
}
|
|
1101
|
-
function shouldRotateCodexGpt56MultiAgentBinding(params) {
|
|
1102
|
-
const bindingVersion = resolveCodexGpt56MultiAgentVersion(params.bindingModel);
|
|
1103
|
-
const requestedVersion = resolveCodexGpt56MultiAgentVersion(params.requestedModel);
|
|
1104
|
-
return Boolean(bindingVersion && requestedVersion && bindingVersion !== requestedVersion);
|
|
1105
|
-
}
|
|
1106
|
-
function isTransientWebSearchRestriction(params) {
|
|
1107
|
-
if (params.nativeProviderWebSearchSupport === "unknown") return true;
|
|
1108
|
-
if (params.params.config?.tools?.web?.search?.enabled === false) return false;
|
|
1109
|
-
if (params.params.disableTools === true) return true;
|
|
1110
|
-
const persistentWebSearchRestriction = params.webSearchAllowed === false && params.persistentWebSearchAllowed === false;
|
|
1111
|
-
if (params.nativeCodeModeEnabled === false && !persistentWebSearchRestriction) return true;
|
|
1112
|
-
if (params.webSearchAllowed !== false) return false;
|
|
1113
|
-
if (params.persistentWebSearchAllowed !== void 0) return params.persistentWebSearchAllowed;
|
|
1114
|
-
if (params.params.toolsAllow === void 0) return false;
|
|
1115
|
-
return !params.params.toolsAllow.some((name) => {
|
|
1116
|
-
const normalized = normalizeCodexDynamicToolName(name);
|
|
1117
|
-
return normalized === "*" || normalized === "web_search";
|
|
1118
|
-
});
|
|
1119
|
-
}
|
|
1120
|
-
function shouldRecheckRecoverablePluginBinding(params) {
|
|
1121
|
-
if (!params.pluginThreadConfig?.enabled) return false;
|
|
1122
|
-
if (!params.binding.pluginAppsFingerprint || !params.binding.pluginAppsInputFingerprint || params.binding.pluginAppsInputFingerprint !== params.pluginThreadConfig.inputFingerprint) return false;
|
|
1123
|
-
const policyContext = params.binding.pluginAppPolicyContext;
|
|
1124
|
-
if (!policyContext) return false;
|
|
1125
|
-
const enabledPluginConfigKeys = params.pluginThreadConfig.enabledPluginConfigKeys ?? [];
|
|
1126
|
-
const recoverablePluginConfigKeys = params.pluginThreadConfig.recoverablePluginConfigKeys ?? enabledPluginConfigKeys;
|
|
1127
|
-
const recoverablePluginConfigKeySet = new Set(recoverablePluginConfigKeys);
|
|
1128
|
-
const bindingContainsSettledPlugin = enabledPluginConfigKeys.filter((configKey) => !recoverablePluginConfigKeySet.has(configKey)).some((configKey) => (policyContext.pluginAppIds[configKey]?.length ?? 0) > 0 || Object.values(policyContext.apps).some((app) => app.source !== "account" && app.configKey === configKey));
|
|
1129
|
-
const accountAppRecoveryEnabled = params.pluginThreadConfig.accountAppRecoveryEnabled ?? enabledPluginConfigKeys.length === 0;
|
|
1130
|
-
return bindingContainsSettledPlugin || accountAppRecoveryEnabled && Object.keys(policyContext.apps).length === 0 || recoverablePluginConfigKeys.length > 0;
|
|
1131
|
-
}
|
|
1132
|
-
//#endregion
|
|
1133
|
-
//#region extensions/codex/src/app-server/thread-fingerprints.ts
|
|
1134
|
-
function codexDynamicToolsFingerprint(dynamicTools) {
|
|
1135
|
-
return hashCodexAppServerBindingFingerprint(legacyFingerprintDynamicTools(dynamicTools));
|
|
1136
|
-
}
|
|
1137
|
-
function codexLegacyDynamicToolsFingerprint(dynamicTools) {
|
|
1138
|
-
return legacyFingerprintDynamicTools(dynamicTools);
|
|
1139
|
-
}
|
|
1140
|
-
function areCodexDynamicToolFingerprintsCompatible(params) {
|
|
1141
|
-
return areDynamicToolFingerprintsCompatible(params.previous, params.next, params.nextLegacy);
|
|
1142
|
-
}
|
|
1143
|
-
function legacyFingerprintDynamicTools(dynamicTools) {
|
|
1144
|
-
return JSON.stringify(dynamicTools.map(stabilizeJsonValue).toSorted(compareJsonFingerprint));
|
|
1145
|
-
}
|
|
1146
|
-
function legacyFingerprintUserMcpServersConfigPatch(configPatch) {
|
|
1147
|
-
return configPatch ? JSON.stringify(stabilizeJsonValue(configPatch)) : void 0;
|
|
1148
|
-
}
|
|
1149
|
-
function fingerprintUserMcpServersConfigPatch(configPatch) {
|
|
1150
|
-
return configPatch ? hashCodexAppServerBindingFingerprint(JSON.stringify(stabilizeJsonValue(redactUserMcpServersFingerprintSecrets(configPatch)))) : void 0;
|
|
1151
|
-
}
|
|
1152
|
-
function redactUserMcpServersFingerprintSecrets(value) {
|
|
1153
|
-
if (Array.isArray(value)) return value.map(redactUserMcpServersFingerprintSecrets);
|
|
1154
|
-
if (!value || typeof value !== "object") return value;
|
|
1155
|
-
return Object.fromEntries(Object.entries(value).map(([key, entry]) => {
|
|
1156
|
-
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]))];
|
|
1157
|
-
return [key, redactUserMcpServersFingerprintSecrets(entry)];
|
|
1158
|
-
}));
|
|
1159
|
-
}
|
|
1160
|
-
function fingerprintUserMcpServersAuthorizationHeader(value) {
|
|
1161
|
-
return typeof value === "string" && value.length > 0 ? `<redacted:sha256:${crypto$1.createHash("sha256").update(value).digest("hex")}>` : "<redacted>";
|
|
1162
|
-
}
|
|
1163
|
-
function fingerprintJsonObject(value) {
|
|
1164
|
-
return JSON.stringify(stabilizeJsonValue(value));
|
|
1165
|
-
}
|
|
1166
|
-
/** Hash thread-creation identity; settings already applied by turn/start must not restart Codex. */
|
|
1167
|
-
function fingerprintCodexThreadConfig(request, authProfileId, dynamicToolsFingerprint) {
|
|
1168
|
-
return hashCodexAppServerBindingFingerprint(fingerprintJsonObject({
|
|
1169
|
-
authProfileId: authProfileId ?? null,
|
|
1170
|
-
dynamicToolsFingerprint: dynamicToolsFingerprint ?? null,
|
|
1171
|
-
nativeMultiAgentVersion: resolveCodexGpt56MultiAgentVersion(typeof request.requestedModel === "string" ? request.requestedModel : typeof request.model === "string" ? request.model : void 0) ?? null,
|
|
1172
|
-
modelProvider: request.modelProvider ?? null,
|
|
1173
|
-
requestedModelProvider: request.requestedModelProvider === void 0 ? request.modelProvider ?? null : request.requestedModelProvider,
|
|
1174
|
-
permissions: request.permissions ?? null,
|
|
1175
|
-
baseInstructions: request.baseInstructions ?? null,
|
|
1176
|
-
developerInstructions: request.developerInstructions ?? null,
|
|
1177
|
-
config: request.config ?? {}
|
|
1178
|
-
}));
|
|
1179
|
-
}
|
|
1180
|
-
function fingerprintEnvironmentSelection(environments) {
|
|
1181
|
-
return environments ? JSON.stringify(environments.map(stabilizeJsonValue)) : void 0;
|
|
1182
|
-
}
|
|
1183
|
-
function stabilizeJsonValue(value) {
|
|
1184
|
-
if (Array.isArray(value)) return value.map(stabilizeJsonValue);
|
|
1185
|
-
if (!isJsonObject(value)) return value;
|
|
1186
|
-
return Object.fromEntries(Object.entries(value).toSorted(([left], [right]) => left.localeCompare(right)).map(([key, child]) => [key, stabilizeJsonValue(child)]));
|
|
1187
|
-
}
|
|
1188
|
-
function readActiveCodexTurnIds(thread) {
|
|
1189
|
-
return (thread.turns ?? []).filter((turn) => turn.status === "inProgress").map((turn) => typeof turn.id === "string" ? turn.id : "").filter((turnId) => turnId.trim().length > 0);
|
|
1190
|
-
}
|
|
1191
|
-
function readActiveCodexTurnIdsFromResume(response) {
|
|
1192
|
-
const pagedTurns = response.initialTurnsPage?.data;
|
|
1193
|
-
return readActiveCodexTurnIds(Array.isArray(pagedTurns) ? { turns: pagedTurns } : response.thread);
|
|
1194
|
-
}
|
|
1195
|
-
const LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT = legacyFingerprintDynamicTools([]);
|
|
1196
|
-
const EMPTY_DYNAMIC_TOOLS_FINGERPRINT = hashCodexAppServerBindingFingerprint(LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT);
|
|
1197
|
-
function areDynamicToolFingerprintsCompatible(previous, next, nextLegacy) {
|
|
1198
|
-
return !previous || previous === next || previous === nextLegacy;
|
|
1199
|
-
}
|
|
1200
|
-
function areUserMcpServersFingerprintsCompatible(params) {
|
|
1201
|
-
return params.previous === params.next || params.previous === params.nextLegacy || params.nextLegacy !== void 0 && params.previous === hashCodexAppServerBindingFingerprint(params.nextLegacy);
|
|
1202
|
-
}
|
|
1203
|
-
function shouldStartTransientNoToolThread(params) {
|
|
1204
|
-
return Boolean(params.previous && !isEmptyDynamicToolsFingerprint(params.previous) && !params.nextHasDynamicTools);
|
|
1205
|
-
}
|
|
1206
|
-
function isEmptyDynamicToolsFingerprint(fingerprint) {
|
|
1207
|
-
return fingerprint === EMPTY_DYNAMIC_TOOLS_FINGERPRINT || fingerprint === LEGACY_EMPTY_DYNAMIC_TOOLS_FINGERPRINT;
|
|
1208
|
-
}
|
|
1209
|
-
function compareJsonFingerprint(left, right) {
|
|
1210
|
-
return JSON.stringify(left).localeCompare(JSON.stringify(right));
|
|
1211
|
-
}
|
|
1212
|
-
//#endregion
|
|
1213
|
-
//#region extensions/codex/src/app-server/native-skill-isolation.ts
|
|
1214
|
-
const MAX_PERSONAL_SKILL_DIRECTORIES = 2e3;
|
|
1215
|
-
const MAX_PERSONAL_SKILL_DEPTH = 6;
|
|
1216
|
-
const MAX_PERSONAL_SKILL_ENTRIES = 1e4;
|
|
1217
|
-
const nativeSkillIsolationByClient = /* @__PURE__ */ new WeakMap();
|
|
1218
|
-
function isMissingPathError(error) {
|
|
1219
|
-
return error.code === "ENOENT";
|
|
1220
|
-
}
|
|
1221
|
-
async function canonicalizeExistingPath(candidate) {
|
|
1222
|
-
try {
|
|
1223
|
-
return await fs.realpath(candidate);
|
|
1224
|
-
} catch {
|
|
1225
|
-
return path.resolve(candidate);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
async function usesDefaultStateDir() {
|
|
1229
|
-
if (!process.env.OPENCLAW_STATE_DIR?.trim()) return true;
|
|
1230
|
-
const home = resolveRequiredHomeDir();
|
|
1231
|
-
const [stateDir, defaultStateDir] = await Promise.all([canonicalizeExistingPath(resolveStateDir()), canonicalizeExistingPath(path.join(home, ".openclaw"))]);
|
|
1232
|
-
return stateDir === defaultStateDir;
|
|
1233
|
-
}
|
|
1234
|
-
async function collectPersonalSkillRealPaths(homes, codexHome) {
|
|
1235
|
-
const realStateDir = await canonicalizeExistingPath(resolveStateDir());
|
|
1236
|
-
const roots = [];
|
|
1237
|
-
for (const home of homes) {
|
|
1238
|
-
for (const dir of [".agents", ".claude"]) roots.push({
|
|
1239
|
-
dir: path.join(home, dir, "skills"),
|
|
1240
|
-
onlyEscapedStateTargets: false
|
|
1241
|
-
});
|
|
1242
|
-
const defaultCodexHome = path.join(home, ".codex");
|
|
1243
|
-
const realDefaultCodexHome = await canonicalizeExistingPath(defaultCodexHome);
|
|
1244
|
-
roots.push({
|
|
1245
|
-
dir: path.join(defaultCodexHome, "skills"),
|
|
1246
|
-
onlyEscapedStateTargets: isPathInside(realStateDir, realDefaultCodexHome)
|
|
1247
|
-
});
|
|
1248
|
-
}
|
|
1249
|
-
const configuredCodexHome = codexHome?.trim() || process.env.CODEX_HOME?.trim();
|
|
1250
|
-
if (configuredCodexHome) {
|
|
1251
|
-
const realCodexHome = await canonicalizeExistingPath(configuredCodexHome);
|
|
1252
|
-
const stateOwned = isPathInside(realStateDir, realCodexHome);
|
|
1253
|
-
roots.push({
|
|
1254
|
-
dir: path.join(configuredCodexHome, "skills"),
|
|
1255
|
-
onlyEscapedStateTargets: stateOwned
|
|
1256
|
-
});
|
|
1257
|
-
}
|
|
1258
|
-
const skillPaths = /* @__PURE__ */ new Set();
|
|
1259
|
-
let complete = true;
|
|
1260
|
-
const seenDirectories = /* @__PURE__ */ new Set();
|
|
1261
|
-
const queue = roots.map((root) => ({
|
|
1262
|
-
dir: root.dir,
|
|
1263
|
-
onlyEscapedStateTargets: root.onlyEscapedStateTargets,
|
|
1264
|
-
depth: 0
|
|
1265
|
-
}));
|
|
1266
|
-
let entryCount = 0;
|
|
1267
|
-
const recordSkillFile = async (filePath, onlyEscapedStateTargets) => {
|
|
1268
|
-
try {
|
|
1269
|
-
const skillRealPath = await fs.realpath(filePath);
|
|
1270
|
-
if (!onlyEscapedStateTargets || !isPathInside(realStateDir, skillRealPath)) skillPaths.add(skillRealPath);
|
|
1271
|
-
} catch (error) {
|
|
1272
|
-
if (!isMissingPathError(error)) complete = false;
|
|
1273
|
-
}
|
|
1274
|
-
};
|
|
1275
|
-
for (const current of queue) {
|
|
1276
|
-
let realDir;
|
|
1277
|
-
try {
|
|
1278
|
-
realDir = await fs.realpath(current.dir);
|
|
1279
|
-
} catch (error) {
|
|
1280
|
-
if (isMissingPathError(error)) continue;
|
|
1281
|
-
complete = false;
|
|
1282
|
-
continue;
|
|
1283
|
-
}
|
|
1284
|
-
if (seenDirectories.has(realDir)) continue;
|
|
1285
|
-
seenDirectories.add(realDir);
|
|
1286
|
-
if (seenDirectories.size > MAX_PERSONAL_SKILL_DIRECTORIES) {
|
|
1287
|
-
complete = false;
|
|
1288
|
-
break;
|
|
1289
|
-
}
|
|
1290
|
-
let directory;
|
|
1291
|
-
try {
|
|
1292
|
-
directory = await fs.opendir(current.dir);
|
|
1293
|
-
} catch (error) {
|
|
1294
|
-
if (!isMissingPathError(error)) complete = false;
|
|
1295
|
-
continue;
|
|
1296
|
-
}
|
|
1297
|
-
try {
|
|
1298
|
-
for await (const entry of directory) {
|
|
1299
|
-
entryCount += 1;
|
|
1300
|
-
if (entryCount > MAX_PERSONAL_SKILL_ENTRIES) {
|
|
1301
|
-
complete = false;
|
|
1302
|
-
queue.length = 0;
|
|
1303
|
-
break;
|
|
1304
|
-
}
|
|
1305
|
-
if (entry.name.startsWith(".")) continue;
|
|
1306
|
-
const entryPath = path.join(current.dir, entry.name);
|
|
1307
|
-
if (entry.name === "SKILL.md" && entry.isFile()) {
|
|
1308
|
-
await recordSkillFile(entryPath, current.onlyEscapedStateTargets);
|
|
1309
|
-
continue;
|
|
1310
|
-
}
|
|
1311
|
-
if (entry.isSymbolicLink()) {
|
|
1312
|
-
try {
|
|
1313
|
-
const stat = await fs.stat(entryPath);
|
|
1314
|
-
if (entry.name === "SKILL.md" && stat.isFile()) await recordSkillFile(entryPath, current.onlyEscapedStateTargets);
|
|
1315
|
-
else if (stat.isDirectory()) {
|
|
1316
|
-
if (current.depth < MAX_PERSONAL_SKILL_DEPTH) queue.push({
|
|
1317
|
-
dir: entryPath,
|
|
1318
|
-
depth: current.depth + 1,
|
|
1319
|
-
onlyEscapedStateTargets: current.onlyEscapedStateTargets
|
|
1320
|
-
});
|
|
1321
|
-
else complete = false;
|
|
1322
|
-
}
|
|
1323
|
-
} catch (error) {
|
|
1324
|
-
if (!isMissingPathError(error)) complete = false;
|
|
1325
|
-
}
|
|
1326
|
-
continue;
|
|
1327
|
-
}
|
|
1328
|
-
if (current.depth >= MAX_PERSONAL_SKILL_DEPTH) {
|
|
1329
|
-
if (entry.isDirectory()) complete = false;
|
|
1330
|
-
continue;
|
|
1331
|
-
}
|
|
1332
|
-
if (entry.isDirectory()) {
|
|
1333
|
-
queue.push({
|
|
1334
|
-
dir: entryPath,
|
|
1335
|
-
depth: current.depth + 1,
|
|
1336
|
-
onlyEscapedStateTargets: current.onlyEscapedStateTargets
|
|
1337
|
-
});
|
|
1338
|
-
continue;
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
} catch (error) {
|
|
1342
|
-
if (!isMissingPathError(error)) complete = false;
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
return {
|
|
1346
|
-
complete,
|
|
1347
|
-
skillPaths
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
/** Resolves the native user-scope skills that an isolated OpenClaw thread must disable. */
|
|
1351
|
-
async function resolveCodexNativeSkillIsolation(params) {
|
|
1352
|
-
params.signal?.throwIfAborted();
|
|
1353
|
-
if (!process.env.OPENCLAW_STATE_DIR?.trim()) return;
|
|
1354
|
-
const key = JSON.stringify([
|
|
1355
|
-
path.resolve(resolveStateDir()),
|
|
1356
|
-
path.resolve(params.cwd),
|
|
1357
|
-
params.codexHome?.trim() || process.env.CODEX_HOME?.trim() || "",
|
|
1358
|
-
params.home?.trim() || process.env.HOME?.trim() || "",
|
|
1359
|
-
params.userProfile?.trim() || process.env.USERPROFILE?.trim() || ""
|
|
1360
|
-
]);
|
|
1361
|
-
const cached = nativeSkillIsolationByClient.get(params.client);
|
|
1362
|
-
if (cached?.key === key && (cached.settled || cached.signal === params.signal)) {
|
|
1363
|
-
const isolation = await cached.result;
|
|
1364
|
-
params.signal?.throwIfAborted();
|
|
1365
|
-
return isolation;
|
|
1366
|
-
}
|
|
1367
|
-
const result = resolveUncachedCodexNativeSkillIsolation(params);
|
|
1368
|
-
const entry = {
|
|
1369
|
-
key,
|
|
1370
|
-
result,
|
|
1371
|
-
settled: false,
|
|
1372
|
-
signal: params.signal
|
|
1373
|
-
};
|
|
1374
|
-
nativeSkillIsolationByClient.set(params.client, entry);
|
|
1375
|
-
try {
|
|
1376
|
-
const isolation = await result;
|
|
1377
|
-
entry.settled = true;
|
|
1378
|
-
params.signal?.throwIfAborted();
|
|
1379
|
-
return isolation;
|
|
1380
|
-
} catch (error) {
|
|
1381
|
-
if (nativeSkillIsolationByClient.get(params.client)?.result === result) nativeSkillIsolationByClient.delete(params.client);
|
|
1382
|
-
throw error;
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
async function resolveUncachedCodexNativeSkillIsolation(params) {
|
|
1386
|
-
if (await usesDefaultStateDir()) return;
|
|
1387
|
-
const response = await params.client.request("skills/list", {
|
|
1388
|
-
cwds: [params.cwd],
|
|
1389
|
-
forceReload: true
|
|
1390
|
-
}, { signal: params.signal });
|
|
1391
|
-
const homes = [params.home?.trim() || process.env.HOME?.trim() || process.env.USERPROFILE?.trim() || os.homedir()];
|
|
1392
|
-
if (process.platform === "win32") homes.push(params.userProfile?.trim() || os.homedir());
|
|
1393
|
-
const personalSkills = await collectPersonalSkillRealPaths([...new Set(homes.map((home) => path.resolve(home)))], params.codexHome);
|
|
1394
|
-
return { disabledUserSkillPaths: [...personalSkills.complete ? personalSkills.skillPaths : /* @__PURE__ */ new Set([...personalSkills.skillPaths, ...response.data.flatMap((entry) => entry.skills.filter((skill) => skill.scope === "user").map((skill) => skill.path))])].toSorted((left, right) => left.localeCompare(right)) };
|
|
1395
|
-
}
|
|
1396
|
-
/** Applies path-exact session rules after caller config so isolated user skills stay disabled. */
|
|
1397
|
-
function applyCodexNativeSkillIsolation(config, isolation) {
|
|
1398
|
-
if (!isolation) return config;
|
|
1399
|
-
const existingRules = config?.["skills.config"];
|
|
1400
|
-
if (existingRules !== void 0 && !Array.isArray(existingRules)) throw new Error("Codex thread skills.config must be an array");
|
|
1401
|
-
const disabledRules = isolation.disabledUserSkillPaths.map((skillPath) => ({
|
|
1402
|
-
path: skillPath,
|
|
1403
|
-
enabled: false
|
|
1404
|
-
}));
|
|
1405
|
-
return {
|
|
1406
|
-
...config,
|
|
1407
|
-
"skills.include_instructions": false,
|
|
1408
|
-
"skills.config": [...existingRules ?? [], ...disabledRules]
|
|
1409
|
-
};
|
|
1410
|
-
}
|
|
1411
|
-
//#endregion
|
|
1412
|
-
//#region extensions/codex/src/app-server/native-tool-catalog.ts
|
|
1413
|
-
function hasCodexNativeToolCatalog(binding) {
|
|
1414
|
-
return binding?.connectionScope === "supervision" && !binding.pendingSupervisionBranch;
|
|
1415
|
-
}
|
|
1416
|
-
/** Pinned serde omits empty catalogs and false deferLoading; declarations remain native-owned. */
|
|
1417
|
-
function parseCodexNativeToolCatalog(metadata, threadId, fingerprint) {
|
|
1418
|
-
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.");
|
|
1419
|
-
if (!isJsonObject(metadata) || metadata.id !== threadId) throw fail();
|
|
1420
|
-
const catalog = metadata.dynamic_tools ?? [];
|
|
1421
|
-
if (!Array.isArray(catalog) || Buffer.byteLength(JSON.stringify(catalog)) > 1048576) throw fail();
|
|
1422
|
-
const names = /* @__PURE__ */ new Set();
|
|
1423
|
-
const namespaces = /* @__PURE__ */ new Set();
|
|
1424
|
-
const validName = (name) => typeof name === "string" && /^[a-zA-Z0-9_-]{1,128}$/u.test(name);
|
|
1425
|
-
const readFunction = (value) => {
|
|
1426
|
-
if (!isJsonObject(value) || value.type !== "function" || !validName(value.name) || typeof value.description !== "string" || !isJsonObject(value.inputSchema) || value.deferLoading !== void 0 && typeof value.deferLoading !== "boolean" || Object.keys(value).some((key) => ![
|
|
1427
|
-
"type",
|
|
1428
|
-
"name",
|
|
1429
|
-
"description",
|
|
1430
|
-
"inputSchema",
|
|
1431
|
-
"deferLoading"
|
|
1432
|
-
].includes(key)) || names.has(value.name) || names.size >= 2e3) throw fail();
|
|
1433
|
-
names.add(value.name);
|
|
1434
|
-
return {
|
|
1435
|
-
type: "function",
|
|
1436
|
-
name: value.name,
|
|
1437
|
-
description: value.description,
|
|
1438
|
-
inputSchema: structuredClone(value.inputSchema),
|
|
1439
|
-
...value.deferLoading === true ? { deferLoading: true } : {}
|
|
1440
|
-
};
|
|
1441
|
-
};
|
|
1442
|
-
const tools = catalog.map((value) => {
|
|
1443
|
-
if (!isJsonObject(value) || value.type !== "namespace") return readFunction(value);
|
|
1444
|
-
if (!validName(value.name) || namespaces.has(value.name) || typeof value.description !== "string" || !Array.isArray(value.tools) || !value.tools.length || Object.keys(value).some((key) => ![
|
|
1445
|
-
"type",
|
|
1446
|
-
"name",
|
|
1447
|
-
"description",
|
|
1448
|
-
"tools"
|
|
1449
|
-
].includes(key))) throw fail();
|
|
1450
|
-
namespaces.add(value.name);
|
|
1451
|
-
return {
|
|
1452
|
-
type: "namespace",
|
|
1453
|
-
name: value.name,
|
|
1454
|
-
description: value.description,
|
|
1455
|
-
tools: value.tools.map(readFunction)
|
|
1456
|
-
};
|
|
1457
|
-
});
|
|
1458
|
-
if (fingerprint !== void 0 && codexDynamicToolsFingerprint(tools) !== fingerprint) throw fail();
|
|
1459
|
-
return tools;
|
|
1460
|
-
}
|
|
1461
|
-
/** Existing supervised bindings identify data, never authorize an executor or a new adoption. */
|
|
1462
|
-
async function loadCodexNativeToolCatalog(params) {
|
|
1463
|
-
const { binding, client, appServer, agentDir, assertCurrent } = params;
|
|
1464
|
-
assertCurrent();
|
|
1465
|
-
const home = resolveCodexAppServerLocalHomeDir(appServer.start, agentDir);
|
|
1466
|
-
const actualHome = client.getRuntimeIdentity()?.codexHome;
|
|
1467
|
-
if (!hasCodexNativeToolCatalog(binding) || !binding.dynamicToolsFingerprint || appServer.start.transport !== "stdio" || appServer.remoteWorkspaceRoot || !actualHome || path.resolve(actualHome) !== path.resolve(home) || binding.appServerRuntimeFingerprint !== buildCodexAppServerConnectionFingerprint(appServer, agentDir)) throw new Error("Canonical Codex declarations require the original verified local binding and selected native connection; the thread is preserved.");
|
|
1468
|
-
const metadata = await readCodexClientSessionMeta(client, path.join(home, "sessions"), binding.rolloutPath, binding.threadId);
|
|
1469
|
-
assertCurrent();
|
|
1470
|
-
return parseCodexNativeToolCatalog(metadata, binding.threadId, binding.dynamicToolsFingerprint);
|
|
1471
|
-
}
|
|
1472
|
-
//#endregion
|
|
1473
|
-
//#region extensions/codex/src/app-server/thread-mcp-attestation.ts
|
|
1474
|
-
async function attestCodexRestrictedToolSurfaceMcpServersDisabled(client, threadId, threadConfig, signal, expectedActiveServerNames = []) {
|
|
1475
|
-
const configuredServers = threadConfig?.mcp_servers;
|
|
1476
|
-
if (configuredServers !== void 0 && !isJsonObject(configuredServers)) throw new Error("Codex restricted-tool-surface thread config has invalid mcp_servers");
|
|
1477
|
-
const expectedServers = /* @__PURE__ */ new Map();
|
|
1478
|
-
for (const [name, serverConfig] of Object.entries(configuredServers ?? {})) {
|
|
1479
|
-
if (!isJsonObject(serverConfig) || serverConfig.enabled !== false) throw new Error(`Codex restricted-tool-surface MCP server ${name} is not disabled`);
|
|
1480
|
-
expectedServers.set(name, "disabled");
|
|
1481
|
-
}
|
|
1482
|
-
for (const name of expectedActiveServerNames) {
|
|
1483
|
-
if (expectedServers.get(name) === "disabled") throw new Error(`Codex restricted-tool-surface MCP server ${name} has conflicting policy`);
|
|
1484
|
-
expectedServers.set(name, "active");
|
|
1485
|
-
}
|
|
1486
|
-
const response = await client.request("mcpServerStatus/list", {
|
|
1487
|
-
threadId,
|
|
1488
|
-
detail: "toolsAndAuthOnly"
|
|
1489
|
-
}, { signal });
|
|
1490
|
-
if (!isJsonObject(response) || !Array.isArray(response.data)) throw new Error("Codex mcpServerStatus/list returned an invalid restricted-tool-surface attestation");
|
|
1491
|
-
const observedServerNames = /* @__PURE__ */ new Set();
|
|
1492
|
-
for (const status of response.data) {
|
|
1493
|
-
if (!isJsonObject(status) || typeof status.name !== "string" || !isJsonObject(status.tools)) throw new Error("Codex mcpServerStatus/list returned an invalid restricted-tool-surface server");
|
|
1494
|
-
if (!expectedServers.has(status.name)) throw new Error(`Codex restricted-tool-surface MCP attestation found unexpected server ${status.name}`);
|
|
1495
|
-
if (observedServerNames.has(status.name)) throw new Error(`Codex restricted-tool-surface MCP attestation returned duplicate server ${status.name}`);
|
|
1496
|
-
observedServerNames.add(status.name);
|
|
1497
|
-
if (!Object.hasOwn(status, "serverInfo")) throw new Error(`Codex restricted-tool-surface MCP attestation returned malformed server ${status.name}`);
|
|
1498
|
-
if (expectedServers.get(status.name) === "active") {
|
|
1499
|
-
if (status.serverInfo === null || Object.keys(status.tools).length === 0) throw new Error(`Codex restricted-tool-surface MCP attestation found inactive admitted server ${status.name}`);
|
|
1500
|
-
continue;
|
|
1501
|
-
}
|
|
1502
|
-
if (status.serverInfo !== null) throw new Error(`Codex restricted-tool-surface MCP attestation found active server ${status.name}`);
|
|
1503
|
-
if (Object.keys(status.tools).length > 0) throw new Error(`Codex restricted-tool-surface MCP attestation found tools for server ${status.name}`);
|
|
1504
|
-
}
|
|
1505
|
-
for (const [expectedName, state] of expectedServers) if (!observedServerNames.has(expectedName)) throw new Error(`Codex restricted-tool-surface MCP attestation is missing ${state === "active" ? "admitted " : ""}server ${expectedName}`);
|
|
1506
|
-
if (response.nextCursor !== void 0 && response.nextCursor !== null) throw new Error("Codex mcpServerStatus/list returned an invalid empty-page cursor");
|
|
1507
|
-
}
|
|
1508
|
-
//#endregion
|
|
1509
|
-
//#region extensions/codex/src/app-server/plugin-thread-attestation.ts
|
|
1510
|
-
/**
|
|
1511
|
-
* Checks app availability and enforces restricted MCP surfaces before a turn.
|
|
1512
|
-
*/
|
|
1513
|
-
/** Every admission path checks the same surface; its lifecycle owner keeps the claim fenced. */
|
|
1514
|
-
async function attestCodexThreadToolSurface(params) {
|
|
1515
|
-
params.assertCurrent();
|
|
1516
|
-
if (params.appIds.length > 0) {
|
|
1517
|
-
await params.lifecycleTiming.measure("plugin-app-attestation", () => checkCodexThreadAppAvailability(params));
|
|
1518
|
-
params.assertCurrent();
|
|
1519
|
-
}
|
|
1520
|
-
if (params.restrictedToolSurface) {
|
|
1521
|
-
await params.lifecycleTiming.measure("restricted-tool-surface-mcp-attestation", () => attestCodexRestrictedToolSurfaceMcpServersDisabled(params.client, params.threadId, params.threadConfig, params.signal, params.appIds.length > 0 ? ["codex_apps"] : []));
|
|
1522
|
-
params.assertCurrent();
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
var CodexPluginThreadAppAttestationError = class extends Error {
|
|
1526
|
-
constructor(message, options) {
|
|
1527
|
-
super(message, options);
|
|
1528
|
-
this.name = "CodexPluginThreadAppAttestationError";
|
|
1529
|
-
}
|
|
1530
|
-
};
|
|
1531
|
-
/** Reads the existing runtime snapshot with the started thread's effective app policy. */
|
|
1532
|
-
async function checkCodexThreadAppAvailability(params) {
|
|
1533
|
-
const appIds = Array.from(new Set(params.appIds.filter(Boolean))).toSorted();
|
|
1534
|
-
if (appIds.length === 0) return;
|
|
1535
|
-
let response;
|
|
1536
|
-
try {
|
|
1537
|
-
response = await params.client.request("app/installed", {
|
|
1538
|
-
threadId: params.threadId,
|
|
1539
|
-
forceRefresh: false
|
|
1540
|
-
}, { signal: params.signal });
|
|
1541
|
-
} catch (error) {
|
|
1542
|
-
params.signal?.throwIfAborted();
|
|
1543
|
-
throw new CodexPluginThreadAppAttestationError(`Codex could not confirm admitted apps for thread ${params.threadId}`, { cause: error });
|
|
1544
|
-
}
|
|
1545
|
-
params.signal?.throwIfAborted();
|
|
1546
|
-
const installedById = new Map(response.apps.map((app) => [app.id, app]));
|
|
1547
|
-
const failures = appIds.flatMap((appId) => {
|
|
1548
|
-
const app = installedById.get(appId);
|
|
1549
|
-
if (!app) return [`${appId}:missing`];
|
|
1550
|
-
if (!app.enabled) return [`${appId}:disabled`];
|
|
1551
|
-
return app.callable ? [] : [`${appId}:not-callable`];
|
|
1552
|
-
});
|
|
1553
|
-
if (failures.length > 0) embeddedAgentLog.warn("codex apps unavailable; continuing with remaining tools", {
|
|
1554
|
-
threadId: params.threadId,
|
|
1555
|
-
failures
|
|
1556
|
-
});
|
|
1557
|
-
}
|
|
1558
|
-
/** Deletes a persistent pre-turn thread; ephemeral threads can only be unsubscribed. */
|
|
1559
|
-
async function discardUnattestedCodexPluginThread(params) {
|
|
1560
|
-
if (params.ephemeral) return await unsubscribeCodexThreadBestEffort(params.client, {
|
|
1561
|
-
threadId: params.threadId,
|
|
1562
|
-
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
1563
|
-
});
|
|
1564
|
-
try {
|
|
1565
|
-
await params.client.request("thread/delete", { threadId: params.threadId }, { timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS });
|
|
1566
|
-
return true;
|
|
1567
|
-
} catch (error) {
|
|
1568
|
-
embeddedAgentLog.debug("codex plugin app attestation thread deletion failed", {
|
|
1569
|
-
threadId: params.threadId,
|
|
1570
|
-
error
|
|
1571
|
-
});
|
|
1572
|
-
await unsubscribeCodexThreadBestEffort(params.client, {
|
|
1573
|
-
threadId: params.threadId,
|
|
1574
|
-
timeoutMs: CODEX_APP_SERVER_UNSUBSCRIBE_TIMEOUT_MS
|
|
1575
|
-
});
|
|
1576
|
-
return false;
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
//#endregion
|
|
1580
|
-
//#region extensions/codex/src/app-server/native-hook-relay-state.ts
|
|
1581
|
-
const pending = /* @__PURE__ */ new Set();
|
|
1582
|
-
/** Owns delayed hook-relay cleanup across runtime scheduling and test teardown. */
|
|
1583
|
-
const nativeHookRelayUnregisterQueue = {
|
|
1584
|
-
add(entry) {
|
|
1585
|
-
pending.add(entry);
|
|
1586
|
-
},
|
|
1587
|
-
delete(entry) {
|
|
1588
|
-
return pending.delete(entry);
|
|
1589
|
-
},
|
|
1590
|
-
flush() {
|
|
1591
|
-
while (pending.size > 0) {
|
|
1592
|
-
const entry = pending.values().next().value;
|
|
1593
|
-
if (!entry) return;
|
|
1594
|
-
clearTimeout(entry.timeout);
|
|
1595
|
-
entry.unregister();
|
|
1596
|
-
}
|
|
1597
|
-
},
|
|
1598
|
-
clear() {
|
|
1599
|
-
for (const entry of pending) clearTimeout(entry.timeout);
|
|
1600
|
-
pending.clear();
|
|
1601
|
-
}
|
|
1602
|
-
};
|
|
1603
|
-
//#endregion
|
|
1604
|
-
//#region extensions/codex/src/app-server/native-hook-relay.ts
|
|
1605
|
-
/**
|
|
1606
|
-
* Bridges Codex native hook callbacks into OpenClaw's native hook relay so
|
|
1607
|
-
* app-server tool events can still run OpenClaw policy and diagnostics.
|
|
1608
|
-
*/
|
|
1609
|
-
/** Codex hook events that can be registered through OpenClaw's native relay. */
|
|
1610
|
-
const CODEX_NATIVE_HOOK_RELAY_EVENTS = [
|
|
1611
|
-
"pre_tool_use",
|
|
1612
|
-
"post_tool_use",
|
|
1613
|
-
"permission_request",
|
|
1614
|
-
"before_agent_finalize"
|
|
1615
|
-
];
|
|
1616
|
-
const CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS = CODEX_NATIVE_HOOK_RELAY_EVENTS.filter((event) => event !== "permission_request");
|
|
1617
|
-
const CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS = 18e5;
|
|
1618
|
-
/** Extra relay lifetime after the expected turn budget, preventing late hook drops. */
|
|
1619
|
-
const CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS = 3e5;
|
|
1620
|
-
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS = 250;
|
|
1621
|
-
const CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS = 1e3;
|
|
1622
|
-
const CODEX_NATIVE_HOOK_RELAY_DEFAULT_TIMEOUT_SEC = 10;
|
|
1623
|
-
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS = 1e4;
|
|
1624
|
-
const CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS = 5e3;
|
|
1625
|
-
const MAX_PENDING_DIRECT_CHILD_ADMISSIONS = 32;
|
|
1626
|
-
const nativeHookPolicyByClient = /* @__PURE__ */ new WeakMap();
|
|
1627
|
-
const CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID = {
|
|
1628
|
-
exec: [
|
|
1629
|
-
"Bash",
|
|
1630
|
-
"exec",
|
|
1631
|
-
"exec_command"
|
|
1632
|
-
],
|
|
1633
|
-
apply_patch: [
|
|
1634
|
-
"apply_patch",
|
|
1635
|
-
"Write",
|
|
1636
|
-
"Edit"
|
|
1637
|
-
],
|
|
1638
|
-
spawn_agent: ["spawn_agent", "Agent"]
|
|
1639
|
-
};
|
|
1640
|
-
/** Enterprise managed-only policy silently drops the session-layer hooks that enforce OpenClaw. */
|
|
1641
|
-
async function assertCodexNativeHookRelayAllowed(client, signal) {
|
|
1642
|
-
let attestation = nativeHookPolicyByClient.get(client);
|
|
1643
|
-
if (!attestation) {
|
|
1644
|
-
attestation = client.request("configRequirements/read", void 0, { signal }).then((response) => {
|
|
1645
|
-
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid hook policy response");
|
|
1646
|
-
const requirements = response.requirements;
|
|
1647
|
-
if (requirements === null) return;
|
|
1648
|
-
if (!isJsonObject(requirements)) throw new Error("Codex configRequirements/read returned invalid hook policy requirements");
|
|
1649
|
-
const managedOnly = requirements.allowManagedHooksOnly;
|
|
1650
|
-
if (managedOnly !== void 0 && managedOnly !== null && typeof managedOnly !== "boolean") throw new Error("Codex configRequirements/read returned invalid managed-only hook policy");
|
|
1651
|
-
if (managedOnly === true) throw new Error("Codex managed-only hooks disable the OpenClaw native hook relay; refusing unenforced execution");
|
|
1652
|
-
});
|
|
1653
|
-
nativeHookPolicyByClient.set(client, attestation);
|
|
1654
|
-
attestation.catch(() => {
|
|
1655
|
-
if (nativeHookPolicyByClient.get(client) === attestation) nativeHookPolicyByClient.delete(client);
|
|
1656
|
-
});
|
|
1657
|
-
}
|
|
1658
|
-
await attestation;
|
|
1659
|
-
}
|
|
1660
|
-
/** Defers relay unregister so late native hook subprocesses can still resolve. */
|
|
1661
|
-
function scheduleCodexNativeHookRelayUnregister(params) {
|
|
1662
|
-
let pending;
|
|
1663
|
-
const unregister = () => {
|
|
1664
|
-
if (!pending) return;
|
|
1665
|
-
const current = pending;
|
|
1666
|
-
pending = void 0;
|
|
1667
|
-
if (!nativeHookRelayUnregisterQueue.delete(current)) return;
|
|
1668
|
-
params.relay.unregister();
|
|
1669
|
-
};
|
|
1670
|
-
const timeout = setTimeout(unregister, resolveCodexNativeHookRelayUnregisterGraceMs(params.hookTimeoutSec));
|
|
1671
|
-
pending = {
|
|
1672
|
-
timeout,
|
|
1673
|
-
unregister
|
|
1674
|
-
};
|
|
1675
|
-
nativeHookRelayUnregisterQueue.add(pending);
|
|
1676
|
-
timeout.unref();
|
|
1677
|
-
}
|
|
1678
|
-
/** Computes the delayed unregister window from Codex's hook timeout. */
|
|
1679
|
-
function resolveCodexNativeHookRelayUnregisterGraceMs(hookTimeoutSec) {
|
|
1680
|
-
const hookTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 0;
|
|
1681
|
-
return Math.max(CODEX_NATIVE_HOOK_RELAY_UNREGISTER_GRACE_MS, addTimerTimeoutGraceMs(hookTimeoutMs, CODEX_NATIVE_HOOK_RELAY_UNREGISTER_EXTRA_GRACE_MS) ?? 0);
|
|
1682
|
-
}
|
|
1683
|
-
/** Records a native pre-tool failure that Codex does not project as a tool item. */
|
|
1684
|
-
function emitCodexNativePreToolUseFailureDiagnostic(params) {
|
|
1685
|
-
emitTrustedDiagnosticEvent({
|
|
1686
|
-
type: "tool.execution.error",
|
|
1687
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
1688
|
-
sessionId: params.sessionId,
|
|
1689
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
1690
|
-
runId: params.runId,
|
|
1691
|
-
toolName: params.failure.toolName,
|
|
1692
|
-
toolCallId: params.failure.toolCallId,
|
|
1693
|
-
durationMs: params.failure.durationMs,
|
|
1694
|
-
errorCategory: "before_tool_call",
|
|
1695
|
-
terminalReason: params.terminalReason ?? (params.signal?.aborted ? resolveCodexToolAbortTerminalReason(params.signal) : params.failure.disposition),
|
|
1696
|
-
...params.sourceTimestampMs !== void 0 ? { sourceTimestampMs: params.sourceTimestampMs } : {}
|
|
1697
|
-
});
|
|
1698
|
-
}
|
|
1699
|
-
/** Registers an OpenClaw native hook relay for a Codex app-server turn. */
|
|
1700
|
-
function createCodexNativeHookRelay(params) {
|
|
1701
|
-
if (params.options?.enabled === false) return;
|
|
1702
|
-
const directChildClaims = /* @__PURE__ */ new Map();
|
|
1703
|
-
const pendingDirectChildAdmissions = /* @__PURE__ */ new Map();
|
|
1704
|
-
let foregroundClosed = false;
|
|
1705
|
-
let successfulYieldRetentionAuthorized = false;
|
|
1706
|
-
const assertClaim = (threadId, claim) => () => directChildClaims.get(threadId) === claim;
|
|
1707
|
-
const rejectPendingAdmissions = (reason) => {
|
|
1708
|
-
for (const pending of pendingDirectChildAdmissions.values()) pending.reject(new Error(reason));
|
|
1709
|
-
pendingDirectChildAdmissions.clear();
|
|
1710
|
-
};
|
|
1711
|
-
const relay = registerRetainedNativeHookRelayForBundledRuntime({
|
|
1712
|
-
provider: "codex",
|
|
1713
|
-
relayId: buildCodexNativeHookRelayId({
|
|
1714
|
-
agentId: params.agentId,
|
|
1715
|
-
sessionId: params.sessionId,
|
|
1716
|
-
sessionKey: params.sessionKey
|
|
1717
|
-
}),
|
|
1718
|
-
...params.generation ? { generation: params.generation } : {},
|
|
1719
|
-
...params.generationMismatchGraceMs ? { generationMismatchGraceMs: params.generationMismatchGraceMs } : {},
|
|
1720
|
-
...params.agentId ? { agentId: params.agentId } : {},
|
|
1721
|
-
sessionId: params.sessionId,
|
|
1722
|
-
...params.sessionKey ? { sessionKey: params.sessionKey } : {},
|
|
1723
|
-
...params.config ? { config: params.config } : {},
|
|
1724
|
-
autoApproveMcpTools: params.autoApproveMcpTools,
|
|
1725
|
-
projectedMcpServers: params.projectedMcpServers,
|
|
1726
|
-
runId: params.runId,
|
|
1727
|
-
...params.channelId ? { channelId: params.channelId } : {},
|
|
1728
|
-
...params.requester ? { requester: params.requester } : {},
|
|
1729
|
-
...params.approvalContext ? { approvalContext: params.approvalContext } : {},
|
|
1730
|
-
allowedEvents: params.events,
|
|
1731
|
-
preToolUseLoopDetection: params.loopDetectionPreToolUseRelay,
|
|
1732
|
-
ttlMs: resolveCodexNativeHookRelayTtlMs({
|
|
1733
|
-
explicitTtlMs: params.options?.ttlMs,
|
|
1734
|
-
attemptTimeoutMs: params.attemptTimeoutMs,
|
|
1735
|
-
startupTimeoutMs: params.startupTimeoutMs,
|
|
1736
|
-
turnStartTimeoutMs: params.turnStartTimeoutMs
|
|
1737
|
-
}),
|
|
1738
|
-
signal: params.signal,
|
|
1739
|
-
runBeforeToolCall: params.hostCapabilities.runBeforeToolCall,
|
|
1740
|
-
assertActive: () => {
|
|
1741
|
-
params.hostCapabilities.assertActive();
|
|
1742
|
-
params.assertCurrent?.();
|
|
1743
|
-
},
|
|
1744
|
-
retention: {
|
|
1745
|
-
readClaim: readCodexNativeChildThreadId,
|
|
1746
|
-
shouldRetainAfterForegroundClose: () => successfulYieldRetentionAuthorized && directChildClaims.size > 0,
|
|
1747
|
-
allowPreToolUse: (childThreadId) => directChildClaims.has(childThreadId),
|
|
1748
|
-
awaitForegroundAdmission: (childThreadId) => {
|
|
1749
|
-
if (foregroundClosed) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission unavailable"));
|
|
1750
|
-
const existingClaim = directChildClaims.get(childThreadId);
|
|
1751
|
-
if (existingClaim) return Promise.resolve(assertClaim(childThreadId, existingClaim));
|
|
1752
|
-
const existingPending = pendingDirectChildAdmissions.get(childThreadId);
|
|
1753
|
-
if (existingPending) return existingPending.promise.then((claim) => assertClaim(childThreadId, claim));
|
|
1754
|
-
if (pendingDirectChildAdmissions.size >= MAX_PENDING_DIRECT_CHILD_ADMISSIONS) return Promise.reject(/* @__PURE__ */ new Error("native hook relay foreground admission capacity reached"));
|
|
1755
|
-
const { promise, resolve, reject } = createDeferred();
|
|
1756
|
-
pendingDirectChildAdmissions.set(childThreadId, {
|
|
1757
|
-
promise,
|
|
1758
|
-
resolve,
|
|
1759
|
-
reject
|
|
1760
|
-
});
|
|
1761
|
-
return promise.then((claim) => assertClaim(childThreadId, claim));
|
|
1762
|
-
},
|
|
1763
|
-
onDispose: () => {
|
|
1764
|
-
foregroundClosed = true;
|
|
1765
|
-
rejectPendingAdmissions("native hook relay registration closed");
|
|
1766
|
-
}
|
|
1767
|
-
},
|
|
1768
|
-
onPreToolUseFailure: params.onPreToolUseFailure,
|
|
1769
|
-
command: {
|
|
1770
|
-
nice: 10,
|
|
1771
|
-
timeoutMs: params.options?.gatewayTimeoutMs
|
|
1772
|
-
}
|
|
1773
|
-
});
|
|
1774
|
-
const unregister = () => {
|
|
1775
|
-
foregroundClosed = true;
|
|
1776
|
-
rejectPendingAdmissions("native hook relay foreground closed");
|
|
1777
|
-
relay.unregister();
|
|
1778
|
-
};
|
|
1779
|
-
return {
|
|
1780
|
-
...relay,
|
|
1781
|
-
unregister,
|
|
1782
|
-
authorizeRetentionAfterSuccessfulYield: () => {
|
|
1783
|
-
successfulYieldRetentionAuthorized = true;
|
|
1784
|
-
},
|
|
1785
|
-
hasClaimedDirectChild: () => directChildClaims.size > 0,
|
|
1786
|
-
rejectPendingDirectChild: (threadIdInput, reason) => {
|
|
1787
|
-
const threadId = threadIdInput.trim();
|
|
1788
|
-
const pending = threadId ? pendingDirectChildAdmissions.get(threadId) : void 0;
|
|
1789
|
-
if (!pending) return;
|
|
1790
|
-
pendingDirectChildAdmissions.delete(threadId);
|
|
1791
|
-
pending.reject(new Error(reason));
|
|
1792
|
-
},
|
|
1793
|
-
claimDirectChild: (threadIdInput) => {
|
|
1794
|
-
const threadId = threadIdInput.trim();
|
|
1795
|
-
if (!threadId) return () => void 0;
|
|
1796
|
-
if (directChildClaims.get(threadId)) return () => void 0;
|
|
1797
|
-
const claim = Symbol(threadId);
|
|
1798
|
-
directChildClaims.set(threadId, claim);
|
|
1799
|
-
const pending = pendingDirectChildAdmissions.get(threadId);
|
|
1800
|
-
pendingDirectChildAdmissions.delete(threadId);
|
|
1801
|
-
pending?.resolve(claim);
|
|
1802
|
-
let released = false;
|
|
1803
|
-
return () => {
|
|
1804
|
-
if (released) return;
|
|
1805
|
-
released = true;
|
|
1806
|
-
if (directChildClaims.get(threadId) !== claim) return;
|
|
1807
|
-
directChildClaims.delete(threadId);
|
|
1808
|
-
if (foregroundClosed && directChildClaims.size === 0) relay.unregister();
|
|
1809
|
-
};
|
|
1810
|
-
}
|
|
1811
|
-
};
|
|
1812
|
-
}
|
|
1813
|
-
function readCodexNativeChildThreadId(rawPayload) {
|
|
1814
|
-
if (!isJsonObject(rawPayload) || typeof rawPayload.agent_id !== "string") return;
|
|
1815
|
-
return rawPayload.agent_id.trim() || void 0;
|
|
1816
|
-
}
|
|
1817
|
-
/** Selects the native hook events Codex should install for the current approval mode. */
|
|
1818
|
-
function resolveCodexNativeHookRelayEvents(params) {
|
|
1819
|
-
if (params.configuredEvents?.length) return params.configuredEvents;
|
|
1820
|
-
return params.appServer.approvalPolicy === "never" ? CODEX_NATIVE_HOOK_RELAY_EVENTS : CODEX_NATIVE_HOOK_RELAY_EVENTS_WITH_APP_SERVER_APPROVALS;
|
|
1821
|
-
}
|
|
1822
|
-
/** Derives the native hook relay TTL from the turn budget unless explicitly configured. */
|
|
1823
|
-
function resolveCodexNativeHookRelayTtlMs(params) {
|
|
1824
|
-
if (params.explicitTtlMs !== void 0) return params.explicitTtlMs;
|
|
1825
|
-
const relayBudgetMs = params.attemptTimeoutMs + params.startupTimeoutMs + params.turnStartTimeoutMs + CODEX_NATIVE_HOOK_RELAY_TTL_GRACE_MS;
|
|
1826
|
-
return Math.max(CODEX_NATIVE_HOOK_RELAY_MIN_TTL_MS, Math.floor(relayBudgetMs));
|
|
1827
|
-
}
|
|
1828
|
-
/** Builds a stable relay id scoped to the agent and session identity. */
|
|
1829
|
-
function buildCodexNativeHookRelayId(params) {
|
|
1830
|
-
const hash = createHash("sha256");
|
|
1831
|
-
hash.update("openclaw:codex:native-hook-relay:v1");
|
|
1832
|
-
hash.update("\0");
|
|
1833
|
-
hash.update(params.agentId?.trim() || "");
|
|
1834
|
-
hash.update("\0");
|
|
1835
|
-
hash.update(params.sessionKey?.trim() || params.sessionId);
|
|
1836
|
-
return `codex-${hash.digest("hex").slice(0, 40)}`;
|
|
1837
|
-
}
|
|
1838
|
-
const CODEX_HOOK_EVENT_BY_NATIVE_EVENT = {
|
|
1839
|
-
pre_tool_use: "PreToolUse",
|
|
1840
|
-
post_tool_use: "PostToolUse",
|
|
1841
|
-
permission_request: "PermissionRequest",
|
|
1842
|
-
before_agent_finalize: "Stop"
|
|
1843
|
-
};
|
|
1844
|
-
const CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT = {
|
|
1845
|
-
pre_tool_use: "pre_tool_use",
|
|
1846
|
-
post_tool_use: "post_tool_use",
|
|
1847
|
-
permission_request: "permission_request",
|
|
1848
|
-
before_agent_finalize: "stop"
|
|
1849
|
-
};
|
|
1850
|
-
const CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS = ["/<session-flags>/config.toml", "<session-flags>/config.toml"];
|
|
1851
|
-
/** Builds the Codex config overlay that installs trusted command hooks for relay events. */
|
|
1852
|
-
function buildCodexNativeHookRelayConfig(params) {
|
|
1853
|
-
const events = params.events?.length ? params.events : CODEX_NATIVE_HOOK_RELAY_EVENTS;
|
|
1854
|
-
const selectedEvents = new Set(events);
|
|
1855
|
-
const config = { "features.hooks": true };
|
|
1856
|
-
const hookState = {};
|
|
1857
|
-
for (const event of CODEX_NATIVE_HOOK_RELAY_EVENTS) {
|
|
1858
|
-
const codexEvent = CODEX_HOOK_EVENT_BY_NATIVE_EVENT[event];
|
|
1859
|
-
const selected = selectedEvents.has(event);
|
|
1860
|
-
const shouldRelay = params.relay.shouldRelayEvent(event);
|
|
1861
|
-
if (!selected || !shouldRelay) {
|
|
1862
|
-
if (selected || params.clearOmittedEvents) config[`hooks.${codexEvent}`] = [];
|
|
1863
|
-
if (params.clearOmittedEvents) for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = { enabled: false };
|
|
1864
|
-
continue;
|
|
1865
|
-
}
|
|
1866
|
-
const timeout = normalizeHookTimeoutSec(params.hookTimeoutSec);
|
|
1867
|
-
const command = params.relay.commandForEvent(event, { timeoutMs: resolveCodexNativeHookRelayCommandTimeoutMs(timeout) });
|
|
1868
|
-
const matcher = buildCodexNativeToolMatcher(params.relay.toolMatcherForEvent(event));
|
|
1869
|
-
config[`hooks.${codexEvent}`] = [{
|
|
1870
|
-
...matcher ? { matcher } : {},
|
|
1871
|
-
hooks: [{
|
|
1872
|
-
type: "command",
|
|
1873
|
-
command,
|
|
1874
|
-
timeout,
|
|
1875
|
-
async: false,
|
|
1876
|
-
statusMessage: "OpenClaw native hook relay"
|
|
1877
|
-
}]
|
|
1878
|
-
}];
|
|
1879
|
-
const state = {
|
|
1880
|
-
enabled: true,
|
|
1881
|
-
trusted_hash: codexCommandHookTrustedHash({
|
|
1882
|
-
event,
|
|
1883
|
-
command,
|
|
1884
|
-
matcher,
|
|
1885
|
-
timeout,
|
|
1886
|
-
statusMessage: "OpenClaw native hook relay"
|
|
1887
|
-
})
|
|
1888
|
-
};
|
|
1889
|
-
for (const sourcePath of CODEX_SESSION_FLAGS_HOOK_SOURCE_PATHS) hookState[`${sourcePath}:${CODEX_HOOK_KEY_LABEL_BY_NATIVE_EVENT[event]}:0:0`] = state;
|
|
1890
|
-
}
|
|
1891
|
-
config["hooks.state"] = hookState;
|
|
1892
|
-
return config;
|
|
1893
|
-
}
|
|
1894
|
-
/** Builds a Codex config overlay that disables native hooks and clears hook arrays. */
|
|
1895
|
-
function buildCodexNativeHookRelayDisabledConfig() {
|
|
1896
|
-
return {
|
|
1897
|
-
"features.hooks": false,
|
|
1898
|
-
"hooks.PreToolUse": [],
|
|
1899
|
-
"hooks.PostToolUse": [],
|
|
1900
|
-
"hooks.PermissionRequest": [],
|
|
1901
|
-
"hooks.Stop": []
|
|
512
|
+
mode: "thread_bootstrap",
|
|
513
|
+
epoch: projection.epoch,
|
|
514
|
+
fingerprint: projection.fingerprint
|
|
1902
515
|
};
|
|
1903
516
|
}
|
|
1904
|
-
function
|
|
1905
|
-
return
|
|
1906
|
-
}
|
|
1907
|
-
function resolveCodexNativeHookRelayCommandTimeoutMs(hookTimeoutSec) {
|
|
1908
|
-
const parentTimeoutMs = finiteSecondsToTimerSafeMilliseconds(normalizeHookTimeoutSec(hookTimeoutSec)) ?? 5e3;
|
|
1909
|
-
const parentMarginMs = Math.min(CODEX_NATIVE_HOOK_RELAY_COMMAND_MAX_PARENT_MARGIN_MS, Math.max(CODEX_NATIVE_HOOK_RELAY_COMMAND_MIN_PARENT_MARGIN_MS, Math.floor(parentTimeoutMs / 5)));
|
|
1910
|
-
return Math.max(1, parentTimeoutMs - parentMarginMs);
|
|
517
|
+
function isContextEngineBindingCompatible(previous, next) {
|
|
518
|
+
return previous?.schemaVersion === next.schemaVersion && previous.engineId === next.engineId && previous.policyFingerprint === next.policyFingerprint && areContextEngineProjectionBindingsCompatible(previous.projection, next.projection);
|
|
1911
519
|
}
|
|
1912
|
-
function
|
|
1913
|
-
if (
|
|
1914
|
-
|
|
1915
|
-
const nativeNames = /* @__PURE__ */ new Set();
|
|
1916
|
-
let hasCustomToolName = false;
|
|
1917
|
-
for (const toolName of toolNames) {
|
|
1918
|
-
const canonicalToolName = toolName.trim();
|
|
1919
|
-
if (!canonicalToolName || canonicalToolName === "*") throw new TypeError("Codex native hook matcher requires canonical OpenClaw tool ids");
|
|
1920
|
-
const nativeAliases = CODEX_HOOK_MATCHER_NAMES_BY_TOOL_ID[canonicalToolName];
|
|
1921
|
-
if (!nativeAliases) hasCustomToolName = true;
|
|
1922
|
-
for (const nativeName of nativeAliases ?? [canonicalToolName]) nativeNames.add(nativeName);
|
|
1923
|
-
}
|
|
1924
|
-
const sortedNames = Array.from(nativeNames).toSorted();
|
|
1925
|
-
if (!hasCustomToolName && sortedNames.every((toolName) => /^[A-Za-z0-9_]+$/.test(toolName))) return sortedNames.join("|");
|
|
1926
|
-
return `(?i)^(?:${sortedNames.map((toolName) => toolName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|")})$`;
|
|
520
|
+
function areContextEngineProjectionBindingsCompatible(previous, next) {
|
|
521
|
+
if (!next) return previous === void 0;
|
|
522
|
+
return previous?.schemaVersion === next.schemaVersion && previous.mode === next.mode && previous.epoch === next.epoch && previous.fingerprint === next.fingerprint;
|
|
1927
523
|
}
|
|
1928
|
-
function
|
|
1929
|
-
const
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
hooks: [{
|
|
1933
|
-
async: false,
|
|
1934
|
-
command: params.command,
|
|
1935
|
-
statusMessage: params.statusMessage,
|
|
1936
|
-
timeout: params.timeout,
|
|
1937
|
-
type: "command"
|
|
1938
|
-
}]
|
|
1939
|
-
};
|
|
1940
|
-
return `sha256:${createHash("sha256").update(JSON.stringify(sortJsonValue(identity))).digest("hex")}`;
|
|
524
|
+
function resolveContextEngineCitationsMode(config) {
|
|
525
|
+
const rootConfig = isRecord(config) ? config : void 0;
|
|
526
|
+
const citations = (isRecord(rootConfig?.memory) ? rootConfig.memory : void 0)?.citations;
|
|
527
|
+
return isJsonConfigValue(citations) ? citations : void 0;
|
|
1941
528
|
}
|
|
1942
|
-
function
|
|
1943
|
-
if (
|
|
1944
|
-
if (
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
return sorted;
|
|
529
|
+
function isJsonConfigValue(value) {
|
|
530
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
531
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
532
|
+
if (Array.isArray(value)) return value.every(isJsonConfigValue);
|
|
533
|
+
return isRecord(value) && Object.values(value).every(isJsonConfigValue);
|
|
1948
534
|
}
|
|
1949
535
|
//#endregion
|
|
1950
536
|
//#region extensions/codex/src/app-server/profiler-flag.ts
|
|
@@ -1954,23 +540,6 @@ function isCodexAppServerProfilerEnabled(config, env = process.env) {
|
|
|
1954
540
|
return PROFILER_FLAGS.some((flag) => isDiagnosticFlagEnabled(flag, config, env));
|
|
1955
541
|
}
|
|
1956
542
|
//#endregion
|
|
1957
|
-
//#region extensions/codex/src/app-server/project-doc-thread-config.ts
|
|
1958
|
-
const CODEX_NATIVE_PROJECT_DOC_MAX_BYTES = 131072;
|
|
1959
|
-
function buildCodexProjectDocThreadConfig(config, effectiveNativeConfig) {
|
|
1960
|
-
const defaults = { project_doc_max_bytes: resolveCodexNativeProjectDocMaxBytes(effectiveNativeConfig) ?? CODEX_NATIVE_PROJECT_DOC_MAX_BYTES };
|
|
1961
|
-
return mergeCodexThreadConfigs(defaults, config) ?? defaults;
|
|
1962
|
-
}
|
|
1963
|
-
function resolveCodexNativeProjectDocMaxBytes(effectiveNativeConfig) {
|
|
1964
|
-
if (effectiveNativeConfig?.origins?.project_doc_max_bytes === void 0) return;
|
|
1965
|
-
const authoredMaxBytes = effectiveNativeConfig.config.project_doc_max_bytes;
|
|
1966
|
-
if (typeof authoredMaxBytes !== "number" || !Number.isSafeInteger(authoredMaxBytes) || authoredMaxBytes < 0) throw new Error("Codex config/read returned an invalid project_doc_max_bytes value");
|
|
1967
|
-
return authoredMaxBytes;
|
|
1968
|
-
}
|
|
1969
|
-
function mergeCodexNativeProjectDocThreadConfig(config, effectiveNativeConfig) {
|
|
1970
|
-
const authoredMaxBytes = resolveCodexNativeProjectDocMaxBytes(effectiveNativeConfig);
|
|
1971
|
-
return authoredMaxBytes === void 0 ? config : mergeCodexThreadConfigs({ project_doc_max_bytes: authoredMaxBytes }, config);
|
|
1972
|
-
}
|
|
1973
|
-
//#endregion
|
|
1974
543
|
//#region extensions/codex/src/app-server/mcp-tool-metadata.ts
|
|
1975
544
|
/** Hosted app ownership is authoritative only on metadata supplied by Codex. */
|
|
1976
545
|
function readCodexMcpToolConnectorId(tool) {
|
|
@@ -1985,567 +554,44 @@ function readCodexMcpToolUiVisibility(tool) {
|
|
|
1985
554
|
return [...new Set(visibility.filter((value) => value === "app" || value === "model"))].toSorted();
|
|
1986
555
|
}
|
|
1987
556
|
//#endregion
|
|
1988
|
-
//#region extensions/codex/src/app-server/
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
const provider = params.provider?.trim().toLowerCase();
|
|
1992
|
-
if (provider && provider !== "codex") return;
|
|
1993
|
-
const currentModel = params.currentModel?.trim();
|
|
1994
|
-
const bindingModel = params.bindingModel?.trim();
|
|
1995
|
-
if (currentModel && bindingModel && currentModel === bindingModel && params.bindingModelProvider) return params.bindingModelProvider;
|
|
1996
|
-
return hasProviderQualifiedModelRef(currentModel) ? void 0 : params.bindingModelProvider;
|
|
1997
|
-
}
|
|
1998
|
-
function resolveCodexAppServerThreadModelSelection(params) {
|
|
1999
|
-
const authProfileId = params.authProfileId ?? params.binding?.authProfileId;
|
|
2000
|
-
const explicitModelProvider = resolveCodexAppServerModelProvider({
|
|
2001
|
-
provider: params.provider,
|
|
2002
|
-
authProfileId,
|
|
2003
|
-
authProfileStore: params.authProfileStore,
|
|
2004
|
-
agentDir: params.agentDir,
|
|
2005
|
-
config: params.config
|
|
2006
|
-
});
|
|
2007
|
-
const bindingModelProvider = params.binding?.threadId ? resolveCodexBindingModelProviderFallback({
|
|
2008
|
-
provider: params.provider,
|
|
2009
|
-
currentModel: params.model,
|
|
2010
|
-
bindingModel: params.binding.model,
|
|
2011
|
-
bindingModelProvider: params.binding.modelProvider
|
|
2012
|
-
}) : void 0;
|
|
2013
|
-
return resolveCodexAppServerRequestModelSelection({
|
|
2014
|
-
model: params.model,
|
|
2015
|
-
modelProvider: explicitModelProvider ?? bindingModelProvider,
|
|
2016
|
-
authProfileId,
|
|
2017
|
-
authProfileStore: params.authProfileStore,
|
|
2018
|
-
agentDir: params.agentDir,
|
|
2019
|
-
config: params.config
|
|
2020
|
-
});
|
|
2021
|
-
}
|
|
2022
|
-
function resolveCodexAppServerRequestModelSelection(params) {
|
|
2023
|
-
const model = params.model.trim();
|
|
2024
|
-
const modelProvider = params.modelProvider?.trim();
|
|
2025
|
-
if (modelProvider) return {
|
|
2026
|
-
model,
|
|
2027
|
-
modelProvider
|
|
2028
|
-
};
|
|
2029
|
-
const slashIndex = model.indexOf("/");
|
|
2030
|
-
if (slashIndex <= 0 || slashIndex >= model.length - 1) return { model };
|
|
2031
|
-
const inferredModelProvider = resolveCodexAppServerModelProvider({
|
|
2032
|
-
provider: model.slice(0, slashIndex),
|
|
2033
|
-
authProfileId: params.authProfileId,
|
|
2034
|
-
authProfileStore: params.authProfileStore,
|
|
2035
|
-
agentDir: params.agentDir,
|
|
2036
|
-
config: params.config
|
|
2037
|
-
});
|
|
2038
|
-
return {
|
|
2039
|
-
model: model.slice(slashIndex + 1).trim(),
|
|
2040
|
-
...inferredModelProvider ? { modelProvider: inferredModelProvider } : {}
|
|
2041
|
-
};
|
|
2042
|
-
}
|
|
2043
|
-
function hasProviderQualifiedModelRef(model) {
|
|
2044
|
-
const trimmed = model?.trim();
|
|
2045
|
-
const slashIndex = trimmed?.indexOf("/") ?? -1;
|
|
2046
|
-
return slashIndex > 0 && slashIndex < (trimmed?.length ?? 0) - 1;
|
|
2047
|
-
}
|
|
2048
|
-
function resolveCodexAppServerModelProvider(params) {
|
|
2049
|
-
const normalized = params.provider.trim();
|
|
2050
|
-
const normalizedLower = normalized.toLowerCase();
|
|
2051
|
-
if (!normalized || normalizedLower === "codex") return;
|
|
2052
|
-
if (isCodexAppServerNativeAuthProfile(params) && normalizedLower === "openai") return;
|
|
2053
|
-
return normalizedLower === "openai" ? "openai" : normalized;
|
|
2054
|
-
}
|
|
2055
|
-
//#endregion
|
|
2056
|
-
//#region extensions/codex/src/app-server/thread-prompt.ts
|
|
2057
|
-
function buildDeveloperInstructions(params, options = {}) {
|
|
2058
|
-
const deferredToolNames = /* @__PURE__ */ new Set();
|
|
2059
|
-
let showWidgetToolName;
|
|
2060
|
-
let dashboardToolName;
|
|
2061
|
-
let portalToolName;
|
|
2062
|
-
let messageTool;
|
|
2063
|
-
let hasSkillWorkshop = false;
|
|
2064
|
-
let hasSessionsSpawn = false;
|
|
2065
|
-
let hasSessionsYield = false;
|
|
2066
|
-
let hasSubagentsList = false;
|
|
2067
|
-
let hasSessionsSend = false;
|
|
2068
|
-
let hasControlTools = false;
|
|
2069
|
-
let hasSeenDirectNamespace = false;
|
|
2070
|
-
for (const spec of options.dynamicTools ?? []) {
|
|
2071
|
-
const isDirectNamespace = spec.type === "namespace" && !hasSeenDirectNamespace && spec.name.trim() === "openclaw_direct";
|
|
2072
|
-
if (isDirectNamespace) hasSeenDirectNamespace = true;
|
|
2073
|
-
for (const tool of spec.type === "namespace" ? spec.tools : [spec]) {
|
|
2074
|
-
const name = tool.name.trim();
|
|
2075
|
-
const qualifiedName = spec.type === "namespace" ? `${spec.name}.${name}` : name;
|
|
2076
|
-
if (tool.deferLoading === true && name) deferredToolNames.add(name);
|
|
2077
|
-
if (name === "show_widget") showWidgetToolName ??= qualifiedName;
|
|
2078
|
-
if (name === "dashboard") dashboardToolName ??= qualifiedName;
|
|
2079
|
-
if (name === "portal") portalToolName ??= qualifiedName;
|
|
2080
|
-
if (name === "message") messageTool ??= {
|
|
2081
|
-
name: qualifiedName,
|
|
2082
|
-
parameters: tool.inputSchema
|
|
2083
|
-
};
|
|
2084
|
-
hasSkillWorkshop ||= name === SKILL_WORKSHOP_TOOL_NAME;
|
|
2085
|
-
hasSessionsSpawn ||= name === "sessions_spawn";
|
|
2086
|
-
hasSessionsYield ||= isDirectNamespace && name === "sessions_yield";
|
|
2087
|
-
hasSubagentsList ||= name === "subagents";
|
|
2088
|
-
hasSessionsSend ||= name === "sessions_send";
|
|
2089
|
-
hasControlTools ||= name === "openclaw" || name === "gateway";
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
const nativeCommandGuidance = listRegisteredPluginAgentPromptGuidance({
|
|
2093
|
-
surface: "codex_app_server",
|
|
2094
|
-
includeLegacyGlobalGuidance: false
|
|
2095
|
-
}).join("\n");
|
|
2096
|
-
const delegationGuidanceAvailable = params.disableTools !== true && params.delegationCapability !== "report_only" && !isMessageOnlyCodexSourceReply(params);
|
|
2097
|
-
const nativeDelegationAvailable = delegationGuidanceAvailable && !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) && !shouldDisableCodexToolSearchForModel(params.modelId);
|
|
2098
|
-
const deferredToolDiscoveryGuidance = deferredToolNames.size > 0 || nativeDelegationAvailable ? "Deferred tools may be absent from the direct tool list. Use `tool_search` when directly callable. On code-mode-only models, use `exec` instead: filter `ALL_TOOLS` by name and description, then call the matching entry through `tools`." : void 0;
|
|
2099
|
-
return [
|
|
2100
|
-
"You are a personal agent running inside OpenClaw. OpenClaw has dynamic tools for OpenClaw-owned messaging, cron, sessions, media, gateway, and nodes.",
|
|
2101
|
-
deferredToolNames.size > 0 ? `Deferred searchable OpenClaw dynamic tools available: ${[...deferredToolNames].toSorted((left, right) => left.localeCompare(right)).join(", ")}.` : void 0,
|
|
2102
|
-
deferredToolDiscoveryGuidance,
|
|
2103
|
-
hasSkillWorkshop ? buildSkillWorkshopPromptSection().join("\n") : void 0,
|
|
2104
|
-
nativeDelegationAvailable ? `Use Codex native \`spawn_agent\` for Codex subagents. \`spawn_agent\` and the other native collaboration tools may be deferred.${hasSessionsSpawn ? " Use OpenClaw `sessions_spawn` only for OpenClaw or ACP delegation, never as a substitute for `spawn_agent` on internal legwork." : ""}` : void 0,
|
|
2105
|
-
hasSessionsYield && nativeDelegationAvailable ? "When a native child's result belongs in a later turn, end the current turn with `openclaw_direct.sessions_yield`; the completion arrives as the next model-visible input. Use native `wait_agent` only for an intentional same-turn wait when the immediate next step is blocked on the child. Never loop-poll for native child completion." : void 0,
|
|
2106
|
-
delegationGuidanceAvailable ? buildDelegationGuidanceSection({
|
|
2107
|
-
mode: resolveMainSessionDelegationMode({
|
|
2108
|
-
config: params.config,
|
|
2109
|
-
agentId: params.agentId,
|
|
2110
|
-
sessionKey: params.sessionKey
|
|
2111
|
-
}),
|
|
2112
|
-
isMinimal: params.promptMode === "minimal" || params.promptMode === "none",
|
|
2113
|
-
hiddenDelegationTool: nativeDelegationAvailable ? "native `spawn_agent`" : hasSessionsSpawn ? "`sessions_spawn`" : "",
|
|
2114
|
-
hasVisibleSessionSpawn: hasSessionsSpawn,
|
|
2115
|
-
hasSessionsYield,
|
|
2116
|
-
hasSubagentsList,
|
|
2117
|
-
hasSessionsSend
|
|
2118
|
-
}).join("\n") : void 0,
|
|
2119
|
-
params.disableTools !== true && params.promptMode !== "minimal" && params.promptMode !== "none" ? buildUiPresentationPrompt({
|
|
2120
|
-
showWidgetToolName,
|
|
2121
|
-
dashboardToolName,
|
|
2122
|
-
portalToolName,
|
|
2123
|
-
messageTool
|
|
2124
|
-
}) : void 0,
|
|
2125
|
-
buildCredentialSafetyPrompt({ controlToolsAvailable: params.disableTools !== true && hasControlTools }),
|
|
2126
|
-
nativeCommandGuidance,
|
|
2127
|
-
params.gitCoauthorPrompt,
|
|
2128
|
-
params.extraSystemPrompt
|
|
2129
|
-
].filter((section) => typeof section === "string" && section.trim()).join("\n\n");
|
|
557
|
+
//#region extensions/codex/src/app-server/scheduled-app-tool-policy.ts
|
|
558
|
+
function normalizeAppToolApprovalMode(value) {
|
|
559
|
+
return value === "auto" || value === "prompt" || value === "writes" || value === "approve" ? value : void 0;
|
|
2130
560
|
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
const hasIncludeFilter = filters && Object.values(filters).includes("include");
|
|
2145
|
-
const managedConfig = {
|
|
2146
|
-
...config,
|
|
2147
|
-
shell_environment_policy: {
|
|
2148
|
-
...current,
|
|
2149
|
-
experimental_use_profile: false,
|
|
2150
|
-
set: {
|
|
2151
|
-
...currentSet,
|
|
2152
|
-
...environment
|
|
2153
|
-
},
|
|
2154
|
-
...filters ? hasIncludeFilter ? { filters: {
|
|
2155
|
-
...filters,
|
|
2156
|
-
...Object.fromEntries(names.map((name) => [name, "include"]))
|
|
2157
|
-
} } : {} : includeOnly.length > 0 ? { include_only: [.../* @__PURE__ */ new Set([...includeOnly, ...names])] } : {}
|
|
561
|
+
function readCurrentToolPolicy(config, appId, toolName, metadata, fallbackApprovalMode = "auto") {
|
|
562
|
+
const apps = asOptionalRecord(config.apps);
|
|
563
|
+
const app = asOptionalRecord(apps?.[appId]);
|
|
564
|
+
const defaults = asOptionalRecord(apps?.["_default"]);
|
|
565
|
+
const tools = asOptionalRecord(app?.tools);
|
|
566
|
+
const tool = asOptionalRecord(tools?.[toolName] ?? (metadata?.title !== void 0 ? tools?.[metadata.title] : void 0));
|
|
567
|
+
const toolApprovalMode = normalizeAppToolApprovalMode(tool?.approval_mode);
|
|
568
|
+
let approvalMode = toolApprovalMode ?? normalizeAppToolApprovalMode(app?.default_tools_approval_mode) ?? normalizeAppToolApprovalMode(defaults?.default_tools_approval_mode) ?? fallbackApprovalMode;
|
|
569
|
+
if (!toolApprovalMode) {
|
|
570
|
+
const links = asOptionalRecord(app?.links);
|
|
571
|
+
if (metadata?.requiresExplicitLinkId) for (const link of Object.values(links ?? {})) {
|
|
572
|
+
const linkMode = normalizeAppToolApprovalMode(asOptionalRecord(link)?.default_tools_approval_mode);
|
|
573
|
+
if (linkMode) approvalMode = intersectToolApprovalMode(approvalMode, linkMode);
|
|
2158
574
|
}
|
|
2159
|
-
|
|
2160
|
-
return disableLoginShell ? {
|
|
2161
|
-
...managedConfig,
|
|
2162
|
-
allow_login_shell: false
|
|
2163
|
-
} : managedConfig;
|
|
2164
|
-
}
|
|
2165
|
-
//#endregion
|
|
2166
|
-
//#region extensions/codex/src/app-server/web-search.ts
|
|
2167
|
-
const CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG = {
|
|
2168
|
-
"features.standalone_web_search": false,
|
|
2169
|
-
web_search: "disabled"
|
|
2170
|
-
};
|
|
2171
|
-
function normalizeUniqueStrings(value) {
|
|
2172
|
-
if (!Array.isArray(value)) return;
|
|
2173
|
-
const normalized = [...new Set(value.map(normalizeOptionalString).filter((entry) => Boolean(entry)))];
|
|
2174
|
-
return normalized.length > 0 ? normalized : void 0;
|
|
2175
|
-
}
|
|
2176
|
-
function hasManagedSearchProvider(config) {
|
|
2177
|
-
return normalizeOptionalString(config?.tools?.web?.search?.provider) !== void 0;
|
|
2178
|
-
}
|
|
2179
|
-
function hasNativeDomainRestrictions(config) {
|
|
2180
|
-
return normalizeUniqueStrings(config?.tools?.web?.search?.openaiCodex?.allowedDomains) !== void 0;
|
|
2181
|
-
}
|
|
2182
|
-
function buildCodexNativeWebSearchThreadConfig(config) {
|
|
2183
|
-
const nativeConfig = config?.tools?.web?.search?.openaiCodex;
|
|
2184
|
-
const threadConfig = {
|
|
2185
|
-
"features.standalone_web_search": false,
|
|
2186
|
-
web_search: nativeConfig?.mode === "live" ? "live" : "cached"
|
|
2187
|
-
};
|
|
2188
|
-
const allowedDomains = normalizeUniqueStrings(nativeConfig?.allowedDomains);
|
|
2189
|
-
if (allowedDomains) threadConfig["tools.web_search.allowed_domains"] = allowedDomains;
|
|
2190
|
-
if (nativeConfig?.contextSize) threadConfig["tools.web_search.context_size"] = nativeConfig.contextSize;
|
|
2191
|
-
const location = nativeConfig?.userLocation;
|
|
2192
|
-
const country = normalizeOptionalString(location?.country);
|
|
2193
|
-
const region = normalizeOptionalString(location?.region);
|
|
2194
|
-
const city = normalizeOptionalString(location?.city);
|
|
2195
|
-
const timezone = normalizeOptionalString(location?.timezone);
|
|
2196
|
-
if (country) threadConfig["tools.web_search.location.country"] = country;
|
|
2197
|
-
if (region) threadConfig["tools.web_search.location.region"] = region;
|
|
2198
|
-
if (city) threadConfig["tools.web_search.location.city"] = city;
|
|
2199
|
-
if (timezone) threadConfig["tools.web_search.location.timezone"] = timezone;
|
|
2200
|
-
return threadConfig;
|
|
2201
|
-
}
|
|
2202
|
-
function resolveCodexWebSearchPlan(params) {
|
|
2203
|
-
if (params.disableTools === true || params.webSearchAllowed === false || params.config?.tools?.web?.search?.enabled === false) return {
|
|
2204
|
-
kind: "disabled",
|
|
2205
|
-
suppressManagedWebSearch: true,
|
|
2206
|
-
threadConfig: CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG
|
|
2207
|
-
};
|
|
2208
|
-
const nativeConfig = params.config?.tools?.web?.search?.openaiCodex;
|
|
2209
|
-
const managedSearchExplicit = hasManagedSearchProvider(params.config) || nativeConfig?.enabled === false;
|
|
2210
|
-
const nativeProviderSupportsSearch = params.nativeProviderWebSearchSupport === void 0 || params.nativeProviderWebSearchSupport === "supported";
|
|
2211
|
-
if (!(params.nativeToolSurfaceEnabled !== false && nativeProviderSupportsSearch && nativeConfig?.enabled !== false && !hasManagedSearchProvider(params.config))) {
|
|
2212
|
-
if (!managedSearchExplicit && hasNativeDomainRestrictions(params.config)) return {
|
|
2213
|
-
kind: "disabled",
|
|
2214
|
-
suppressManagedWebSearch: true,
|
|
2215
|
-
threadConfig: CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG
|
|
2216
|
-
};
|
|
2217
|
-
return {
|
|
2218
|
-
kind: "managed",
|
|
2219
|
-
suppressManagedWebSearch: false,
|
|
2220
|
-
threadConfig: CODEX_NATIVE_WEB_SEARCH_DISABLED_CONFIG
|
|
2221
|
-
};
|
|
2222
|
-
}
|
|
2223
|
-
return {
|
|
2224
|
-
kind: "native-hosted",
|
|
2225
|
-
suppressManagedWebSearch: true,
|
|
2226
|
-
threadConfig: buildCodexNativeWebSearchThreadConfig(params.config),
|
|
2227
|
-
webFetchHostnameAllowlist: buildHostnameAllowlistPolicyFromSuffixAllowlist(nativeConfig?.allowedDomains)?.hostnameAllowlist
|
|
2228
|
-
};
|
|
2229
|
-
}
|
|
2230
|
-
const CODEX_CODE_MODE_THREAD_CONFIG = {
|
|
2231
|
-
"features.code_mode": true,
|
|
2232
|
-
"features.code_mode_only": false,
|
|
2233
|
-
"features.shell_tool": true,
|
|
2234
|
-
"features.apply_patch_streaming_events": true,
|
|
2235
|
-
suppress_unstable_features_warning: true
|
|
2236
|
-
};
|
|
2237
|
-
const CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG = { "features.goals": false };
|
|
2238
|
-
const CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG = { "tools.update_plan.enabled": false };
|
|
2239
|
-
const CODEX_CODE_MODE_DISABLED_THREAD_CONFIG = {
|
|
2240
|
-
"features.code_mode": false,
|
|
2241
|
-
"features.code_mode_only": false
|
|
2242
|
-
};
|
|
2243
|
-
const CODEX_NO_PROJECT_DOCS_CONFIG = { project_doc_max_bytes: 0 };
|
|
2244
|
-
const CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG = { "features.multi_agent": false };
|
|
2245
|
-
const CODEX_DELEGATION_DISABLED_THREAD_CONFIG = {
|
|
2246
|
-
"agents.enabled": false,
|
|
2247
|
-
"features.multi_agent": false,
|
|
2248
|
-
"features.multi_agent_v2": false
|
|
2249
|
-
};
|
|
2250
|
-
const CODEX_RING_ZERO_RESTRICTED_FEATURES = /* @__PURE__ */ new Set([
|
|
2251
|
-
"apps",
|
|
2252
|
-
"artifact",
|
|
2253
|
-
"browser_use",
|
|
2254
|
-
"browser_use_external",
|
|
2255
|
-
"browser_use_full_cdp_access",
|
|
2256
|
-
"chronicle",
|
|
2257
|
-
"code_mode",
|
|
2258
|
-
"code_mode_only",
|
|
2259
|
-
"computer_use",
|
|
2260
|
-
"context_management",
|
|
2261
|
-
"current_time_reminder",
|
|
2262
|
-
"default_mode_request_user_input",
|
|
2263
|
-
"deferred_executor",
|
|
2264
|
-
"goals",
|
|
2265
|
-
"hooks",
|
|
2266
|
-
"image_generation",
|
|
2267
|
-
"memories",
|
|
2268
|
-
"multi_agent",
|
|
2269
|
-
"multi_agent_v2",
|
|
2270
|
-
"plugins",
|
|
2271
|
-
"request_permissions_tool",
|
|
2272
|
-
"skill_search",
|
|
2273
|
-
"shell_tool",
|
|
2274
|
-
"standalone_web_search",
|
|
2275
|
-
"token_budget",
|
|
2276
|
-
"unified_exec",
|
|
2277
|
-
"view_image",
|
|
2278
|
-
"web_search_cached",
|
|
2279
|
-
"web_search_request",
|
|
2280
|
-
"workspace_dependencies"
|
|
2281
|
-
]);
|
|
2282
|
-
const CODEX_RING_ZERO_THREAD_CONFIG = {
|
|
2283
|
-
...CODEX_DELEGATION_DISABLED_THREAD_CONFIG,
|
|
2284
|
-
...Object.fromEntries([...CODEX_RING_ZERO_RESTRICTED_FEATURES].map((feature) => [`features.${feature}`, false])),
|
|
2285
|
-
"orchestrator.mcp.enabled": false,
|
|
2286
|
-
"orchestrator.skills.enabled": false,
|
|
2287
|
-
"skills.bundled.enabled": false,
|
|
2288
|
-
"skills.include_instructions": false,
|
|
2289
|
-
"tools.experimental_request_user_input.enabled": false,
|
|
2290
|
-
hooks: {
|
|
2291
|
-
PreToolUse: [],
|
|
2292
|
-
PermissionRequest: [],
|
|
2293
|
-
PostToolUse: [],
|
|
2294
|
-
PreCompact: [],
|
|
2295
|
-
PostCompact: [],
|
|
2296
|
-
SessionStart: [],
|
|
2297
|
-
UserPromptSubmit: [],
|
|
2298
|
-
SubagentStart: [],
|
|
2299
|
-
SubagentStop: [],
|
|
2300
|
-
Stop: []
|
|
2301
|
-
},
|
|
2302
|
-
notify: [],
|
|
2303
|
-
web_search: "disabled"
|
|
2304
|
-
};
|
|
2305
|
-
const CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES = /* @__PURE__ */ new Map([
|
|
2306
|
-
["connectors", "apps"],
|
|
2307
|
-
["imagegenext", "image_generation"],
|
|
2308
|
-
["collab", "multi_agent"],
|
|
2309
|
-
["memory_tool", "memories"],
|
|
2310
|
-
["telepathy", "chronicle"],
|
|
2311
|
-
["codex_hooks", "hooks"]
|
|
2312
|
-
]);
|
|
2313
|
-
/** Common deterministic start/resume/fork fields; no run resources or unsupported setters. */
|
|
2314
|
-
function buildCodexThreadConfiguration(params, options) {
|
|
2315
|
-
return {
|
|
2316
|
-
...options.cwd !== void 0 ? { cwd: options.cwd } : {},
|
|
2317
|
-
...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
|
|
2318
|
-
approvalPolicy: options.appServer.approvalPolicy,
|
|
2319
|
-
approvalsReviewer: resolveCodexThreadApprovalsReviewer(options.appServer, options.config),
|
|
2320
|
-
...codexThreadSandboxOrPermissions(options.appServer),
|
|
2321
|
-
...options.appServer.serviceTier !== void 0 ? { serviceTier: options.appServer.serviceTier } : {},
|
|
2322
|
-
config: buildCodexRuntimeThreadConfigForRun(params, options.config, {
|
|
2323
|
-
nativeCodeModeEnabled: options.nativeCodeModeEnabled,
|
|
2324
|
-
nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
|
|
2325
|
-
nativeCodeModeOnlyEnabled: options.nativeCodeModeOnlyEnabled,
|
|
2326
|
-
directOnlyToolNamespaces: resolveDirectOnlyToolNamespaces(options.dynamicTools),
|
|
2327
|
-
webSearchAllowed: options.webSearchAllowed,
|
|
2328
|
-
appServer: options.appServer,
|
|
2329
|
-
hostSystemAgentActive: options.hostSystemAgentActive,
|
|
2330
|
-
restrictedToolSurfaceInheritedMcpServerNames: options.restrictedToolSurfaceInheritedMcpServerNames,
|
|
2331
|
-
shellEnvironment: options.shellEnvironment,
|
|
2332
|
-
disableLoginShell: options.disableLoginShell
|
|
2333
|
-
}),
|
|
2334
|
-
developerInstructions: options.developerInstructions ?? buildDeveloperInstructions(params, { dynamicTools: options.dynamicTools })
|
|
2335
|
-
};
|
|
2336
|
-
}
|
|
2337
|
-
function buildThreadStartParams(params, options) {
|
|
2338
|
-
const resolvedModelProvider = resolveCodexAppServerModelProvider({
|
|
2339
|
-
provider: params.provider,
|
|
2340
|
-
authProfileId: params.authProfileId,
|
|
2341
|
-
authProfileStore: params.authProfileStore,
|
|
2342
|
-
agentDir: params.agentDir,
|
|
2343
|
-
config: params.config
|
|
2344
|
-
});
|
|
2345
|
-
const modelSelection = resolveCodexAppServerRequestModelSelection({
|
|
2346
|
-
model: options.model ?? params.modelId,
|
|
2347
|
-
modelProvider: options.modelProvider ?? resolvedModelProvider,
|
|
2348
|
-
authProfileId: params.authProfileId,
|
|
2349
|
-
authProfileStore: params.authProfileStore,
|
|
2350
|
-
agentDir: params.agentDir,
|
|
2351
|
-
config: params.config
|
|
2352
|
-
});
|
|
2353
|
-
return {
|
|
2354
|
-
model: modelSelection.model,
|
|
2355
|
-
...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {},
|
|
2356
|
-
...buildCodexThreadConfiguration(params, options),
|
|
2357
|
-
...(options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow) ? { baseInstructions: "" } : {},
|
|
2358
|
-
personality: CODEX_NATIVE_PERSONALITY_NONE,
|
|
2359
|
-
serviceName: "OpenClaw",
|
|
2360
|
-
...resolveCodexThreadEnvironmentSelection(options),
|
|
2361
|
-
dynamicTools: [...options.dynamicTools],
|
|
2362
|
-
experimentalRawEvents: true,
|
|
2363
|
-
...isIncognitoSessionKey(params.sessionKey) ? { ephemeral: true } : {}
|
|
2364
|
-
};
|
|
2365
|
-
}
|
|
2366
|
-
function buildThreadResumeParams(params, options) {
|
|
2367
|
-
const modelSelection = options.preserveNativeModel ? void 0 : resolveCodexAppServerRequestModelSelection({
|
|
2368
|
-
model: options.model ?? params.modelId,
|
|
2369
|
-
modelProvider: options.modelProvider ?? resolveCodexAppServerModelProvider({
|
|
2370
|
-
provider: params.provider,
|
|
2371
|
-
authProfileId: options.authProfileId ?? params.authProfileId,
|
|
2372
|
-
authProfileStore: params.authProfileStore,
|
|
2373
|
-
agentDir: params.agentDir,
|
|
2374
|
-
config: params.config
|
|
2375
|
-
}),
|
|
2376
|
-
authProfileId: options.authProfileId ?? params.authProfileId,
|
|
2377
|
-
authProfileStore: params.authProfileStore,
|
|
2378
|
-
agentDir: params.agentDir,
|
|
2379
|
-
config: params.config
|
|
2380
|
-
});
|
|
2381
|
-
return {
|
|
2382
|
-
threadId: options.threadId,
|
|
2383
|
-
excludeTurns: true,
|
|
2384
|
-
initialTurnsPage: {
|
|
2385
|
-
limit: 1,
|
|
2386
|
-
sortDirection: "desc",
|
|
2387
|
-
itemsView: "notLoaded"
|
|
2388
|
-
},
|
|
2389
|
-
...modelSelection ? {
|
|
2390
|
-
model: modelSelection.model,
|
|
2391
|
-
...modelSelection.modelProvider ? { modelProvider: modelSelection.modelProvider } : {}
|
|
2392
|
-
} : {},
|
|
2393
|
-
...buildCodexThreadConfiguration(params, options),
|
|
2394
|
-
personality: CODEX_NATIVE_PERSONALITY_NONE
|
|
2395
|
-
};
|
|
2396
|
-
}
|
|
2397
|
-
function buildCodexRuntimeThreadConfig(config, options = {}) {
|
|
2398
|
-
const configured = buildCodexProjectDocThreadConfig(config);
|
|
2399
|
-
const codeModeConfig = {
|
|
2400
|
-
...CODEX_CODE_MODE_THREAD_CONFIG,
|
|
2401
|
-
"features.code_mode_only": options.nativeCodeModeOnlyEnabled === true
|
|
2402
|
-
};
|
|
2403
|
-
if (options.nativeCodeModeEnabled === false) {
|
|
2404
|
-
const disabledConfig = expectDefined(mergeCodexThreadConfigs(configured, CODEX_CODE_MODE_DISABLED_THREAD_CONFIG, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG), "Codex disabled code mode config");
|
|
2405
|
-
delete disabledConfig["features.apply_patch_streaming_events"];
|
|
2406
|
-
return disabledConfig;
|
|
575
|
+
else if (metadata?.linkId) approvalMode = normalizeAppToolApprovalMode(asOptionalRecord(links?.[metadata.linkId])?.default_tools_approval_mode) ?? approvalMode;
|
|
2407
576
|
}
|
|
2408
|
-
|
|
2409
|
-
return ensureDirectOnlyToolNamespaces(expectDefined(mergeCodexThreadConfigs(codeModeConfig, configured, CODEX_GOAL_CONTINUATION_DISABLED_THREAD_CONFIG, CODEX_NATIVE_UPDATE_PLAN_DISABLED_THREAD_CONFIG), "Codex code mode config"), options.directOnlyToolNamespaces);
|
|
2410
|
-
}
|
|
2411
|
-
function ensureDirectOnlyToolNamespaces(config, requiredNamespaces) {
|
|
2412
|
-
if (!requiredNamespaces?.length) return config;
|
|
2413
|
-
const feature = expectDefined(config["features.code_mode"], "Codex code mode config");
|
|
2414
|
-
const configured = isJsonObject(feature) ? feature : { enabled: feature };
|
|
2415
|
-
const namespaces = Array.isArray(configured.direct_only_tool_namespaces) ? configured.direct_only_tool_namespaces.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
|
|
2416
|
-
return {
|
|
2417
|
-
...config,
|
|
2418
|
-
"features.code_mode": {
|
|
2419
|
-
...configured,
|
|
2420
|
-
direct_only_tool_namespaces: [.../* @__PURE__ */ new Set([...namespaces, ...requiredNamespaces])]
|
|
2421
|
-
}
|
|
2422
|
-
};
|
|
2423
|
-
}
|
|
2424
|
-
function resolveDirectOnlyToolNamespaces(dynamicTools) {
|
|
2425
|
-
return (dynamicTools ?? []).filter((tool) => tool.type === "namespace" && tool.name === "openclaw_direct").map((tool) => tool.name);
|
|
2426
|
-
}
|
|
2427
|
-
function buildCodexRuntimeThreadConfigForRun(params, config, options = {}) {
|
|
2428
|
-
const ringZeroActive = (options.hostSystemAgentActive ?? isHostScopedAgentToolActive("openclaw")) && isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow);
|
|
2429
|
-
const messageOnlySourceReply = isMessageOnlyCodexSourceReply(params);
|
|
2430
|
-
const restrictedToolSurface = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true;
|
|
2431
|
-
const restrictedTurnDisablesProjectDocs = ringZeroActive || messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted && params.disableTools;
|
|
2432
|
-
const configMcpServers = config?.mcp_servers;
|
|
2433
|
-
if (restrictedToolSurface && configMcpServers !== void 0 && !isJsonObject(configMcpServers)) throw new Error("Codex restricted tool surface received invalid thread mcp_servers config");
|
|
2434
|
-
const restrictedToolSurfaceMcpServerNames = [...options.restrictedToolSurfaceInheritedMcpServerNames ?? [], ...isJsonObject(configMcpServers) ? Object.keys(configMcpServers) : []];
|
|
2435
|
-
const webSearchConfig = resolveCodexWebSearchPlan({
|
|
2436
|
-
config: params.config,
|
|
2437
|
-
disableTools: params.disableTools,
|
|
2438
|
-
nativeToolSurfaceEnabled: options.nativeCodeModeEnabled,
|
|
2439
|
-
nativeProviderWebSearchSupport: options.nativeProviderWebSearchSupport,
|
|
2440
|
-
webSearchAllowed: options.webSearchAllowed
|
|
2441
|
-
}).threadConfig;
|
|
2442
|
-
const baseConfig = buildCodexRuntimeThreadConfig(mergeCodexThreadConfigs(config, webSearchConfig), options);
|
|
2443
|
-
return applyCodexManagedShellEnvironment({
|
|
2444
|
-
...mergeCodexThreadConfigs(baseConfig, options.appServer?.networkProxy?.configPatch, params.pluginHarnessToolPolicySafeDeniedTools?.includes("image_generate") ? { "features.image_generation": false } : void 0, shouldDisableCodexToolSearchForModel(params.modelId) ? CODEX_TOOL_SEARCH_UNSUPPORTED_THREAD_CONFIG : void 0, params.delegationCapability === "report_only" ? CODEX_DELEGATION_DISABLED_THREAD_CONFIG : void 0, messageOnlySourceReply || params.pluginHarnessToolPolicyRestricted === true ? buildRestrictedToolConfigPatch(restrictedToolSurfaceMcpServerNames, Boolean(params.scheduledRuntimeAuthority)) : buildCodexRingZeroThreadConfigPatch(params, options.hostSystemAgentActive, restrictedToolSurfaceMcpServerNames), restrictedTurnDisablesProjectDocs ? CODEX_NO_PROJECT_DOCS_CONFIG : void 0, params.authoredContextTokenCap === void 0 ? void 0 : { model_context_window: params.authoredContextTokenCap }) ?? baseConfig,
|
|
2445
|
-
...params.bootstrapContextMode === "lightweight" ? CODEX_NO_PROJECT_DOCS_CONFIG : {}
|
|
2446
|
-
}, options.shellEnvironment, options.disableLoginShell);
|
|
2447
|
-
}
|
|
2448
|
-
function buildCodexRingZeroThreadConfigPatch(params, hostSystemAgentActive = isHostScopedAgentToolActive("openclaw"), inheritedMcpServerNames = []) {
|
|
2449
|
-
if (!hostSystemAgentActive || !isSystemAgentOnlyCodexDynamicToolAllowlist(params.toolsAllow)) return;
|
|
2450
|
-
return {
|
|
2451
|
-
...buildRestrictedToolConfigPatch(inheritedMcpServerNames),
|
|
2452
|
-
...CODEX_NO_PROJECT_DOCS_CONFIG
|
|
2453
|
-
};
|
|
2454
|
-
}
|
|
2455
|
-
function buildRestrictedToolConfigPatch(inheritedMcpServerNames, scheduledAppAuthorityActive = false) {
|
|
2456
|
-
const mcpServers = Object.fromEntries([...new Set(inheritedMcpServerNames)].toSorted().map((name) => [name, { enabled: false }]));
|
|
577
|
+
const defaultToolsEnabled = app?.default_tools_enabled;
|
|
2457
578
|
return {
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
...Object.keys(mcpServers).length > 0 ? { mcp_servers: mcpServers } : {}
|
|
579
|
+
enabled: (app ? app.enabled !== false : defaults?.enabled !== false) && (typeof tool?.enabled === "boolean" ? tool.enabled : typeof defaultToolsEnabled === "boolean" ? defaultToolsEnabled : appToolHintsAllowed(metadata, {
|
|
580
|
+
allowDestructiveActions: (app?.destructive_enabled ?? defaults?.destructive_enabled) !== false,
|
|
581
|
+
allowOpenWorld: (app?.open_world_enabled ?? defaults?.open_world_enabled) !== false
|
|
582
|
+
})),
|
|
583
|
+
approvalMode
|
|
2464
584
|
};
|
|
2465
585
|
}
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
if (!Array.isArray(response.layers)) throw new Error("Codex config/read omitted effective config layers");
|
|
2469
|
-
for (const layer of response.layers) {
|
|
2470
|
-
if (!isJsonObject(layer) || !isJsonObject(layer.name) || typeof layer.name.type !== "string") throw new Error("Codex config/read returned invalid effective config layers");
|
|
2471
|
-
if (layer.name.type === "legacyManagedConfigTomlFromFile" || layer.name.type === "legacyManagedConfigTomlFromMdm") {
|
|
2472
|
-
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";
|
|
2473
|
-
throw new Error(`Codex restricted tool surface cannot override config layer ${layer.name.type}; ${migrationGuidance}.`);
|
|
2474
|
-
}
|
|
2475
|
-
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}`);
|
|
2476
|
-
}
|
|
2477
|
-
const configuredServers = response.config.mcp_servers;
|
|
2478
|
-
if (configuredServers === void 0) return [];
|
|
2479
|
-
if (!isJsonObject(configuredServers)) throw new Error("Codex config/read returned invalid mcp_servers");
|
|
2480
|
-
return Object.keys(configuredServers).toSorted();
|
|
2481
|
-
}
|
|
2482
|
-
async function assertCodexManagedRequirementsDoNotOverrideToolPolicy(client, options, signal) {
|
|
2483
|
-
const requirements = await readCodexManagedRequirements(client, signal);
|
|
2484
|
-
const managedRequirementsFingerprint = buildCodexManagedRequirementsFingerprint(requirements);
|
|
2485
|
-
const managedRequirementsMatch = options.allowedManagedRequirementsFingerprint !== void 0 && managedRequirementsFingerprint === options.allowedManagedRequirementsFingerprint;
|
|
2486
|
-
const managedHooksAllowed = managedRequirementsMatch || options.allowConfiguredManagedHooks === true;
|
|
2487
|
-
if (options.allowedManagedRequirementsFingerprint !== void 0 && !managedRequirementsMatch) throw new Error("Codex managed requirements changed since this automation was authorized; reauthorize the automation from a fresh owner turn");
|
|
2488
|
-
if (requirements === null) return;
|
|
2489
|
-
if (options.restrictedToolSurface) for (const key of [
|
|
2490
|
-
"hooks",
|
|
2491
|
-
"managedHooks",
|
|
2492
|
-
"managed_hooks"
|
|
2493
|
-
]) {
|
|
2494
|
-
const hooks = requirements[key];
|
|
2495
|
-
if (hooks === void 0 || hooks === null) continue;
|
|
2496
|
-
if (!isJsonObject(hooks)) throw new Error("Codex configRequirements/read returned invalid managed hooks");
|
|
2497
|
-
if (hasNonEmptyJsonValue(hooks) && !managedHooksAllowed) throw new Error("Codex restricted tool surface cannot override managed hooks");
|
|
2498
|
-
}
|
|
2499
|
-
const additionalDeniedFeatures = new Set(options.additionalDeniedFeatures);
|
|
2500
|
-
for (const key of ["featureRequirements", "feature_requirements"]) {
|
|
2501
|
-
const featureRequirements = requirements[key];
|
|
2502
|
-
if (featureRequirements === void 0 || featureRequirements === null) continue;
|
|
2503
|
-
if (!isJsonObject(featureRequirements)) throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2504
|
-
for (const [feature, enabled] of Object.entries(featureRequirements)) {
|
|
2505
|
-
if (typeof enabled !== "boolean") throw new Error("Codex configRequirements/read returned invalid feature requirements");
|
|
2506
|
-
const canonicalFeature = CODEX_RING_ZERO_RESTRICTED_FEATURE_ALIASES.get(feature) ?? feature;
|
|
2507
|
-
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.");
|
|
2508
|
-
const deniedByToolPolicy = options.restrictedToolSurface && CODEX_RING_ZERO_RESTRICTED_FEATURES.has(canonicalFeature) || additionalDeniedFeatures.has(canonicalFeature);
|
|
2509
|
-
if (canonicalFeature === "hooks" && managedHooksAllowed) continue;
|
|
2510
|
-
if (enabled && deniedByToolPolicy) throw new Error(`Codex tool policy cannot override required feature ${feature}`);
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
}
|
|
2514
|
-
/** Hashes the exact managed requirements without retaining their hook commands or policy details. */
|
|
2515
|
-
function buildCodexManagedRequirementsFingerprint(requirements) {
|
|
2516
|
-
const fingerprint = fingerprintJsonObject({
|
|
2517
|
-
version: 1,
|
|
2518
|
-
requirements
|
|
2519
|
-
});
|
|
2520
|
-
return crypto.createHash("sha256").update(fingerprint).digest("hex");
|
|
2521
|
-
}
|
|
2522
|
-
/** Reads and fingerprints the exact managed requirements active on this app-server. */
|
|
2523
|
-
async function readCodexManagedRequirementsFingerprint(client, signal) {
|
|
2524
|
-
return buildCodexManagedRequirementsFingerprint(await readCodexManagedRequirements(client, signal));
|
|
2525
|
-
}
|
|
2526
|
-
async function readCodexManagedRequirements(client, signal) {
|
|
2527
|
-
const response = await client.request("configRequirements/read", void 0, { signal });
|
|
2528
|
-
if (!isJsonObject(response) || !Object.hasOwn(response, "requirements")) throw new Error("Codex configRequirements/read returned an invalid response");
|
|
2529
|
-
if (response.requirements !== null && !isJsonObject(response.requirements)) throw new Error("Codex configRequirements/read returned invalid requirements");
|
|
2530
|
-
return response.requirements;
|
|
2531
|
-
}
|
|
2532
|
-
function hasNonEmptyJsonValue(value) {
|
|
2533
|
-
if (value === null || value === false || value === "") return false;
|
|
2534
|
-
if (Array.isArray(value)) return value.length > 0;
|
|
2535
|
-
if (typeof value === "object") return Object.values(value).some(hasNonEmptyJsonValue);
|
|
2536
|
-
return true;
|
|
2537
|
-
}
|
|
2538
|
-
function resolveCodexThreadApprovalsReviewer(appServer, config) {
|
|
2539
|
-
return config?.approvals_reviewer === "user" ? "user" : appServer.approvalsReviewer;
|
|
2540
|
-
}
|
|
2541
|
-
function codexThreadSandboxOrPermissions(appServer) {
|
|
2542
|
-
if (appServer.networkProxy) return {};
|
|
2543
|
-
return { sandbox: appServer.sandbox };
|
|
586
|
+
function appToolHintsAllowed(tool, policy) {
|
|
587
|
+
return (policy.allowDestructiveActions || tool?.destructiveHint === false) && (policy.allowOpenWorld !== false || tool?.openWorldHint === false);
|
|
2544
588
|
}
|
|
2545
|
-
function
|
|
2546
|
-
if (
|
|
2547
|
-
if (
|
|
2548
|
-
return
|
|
589
|
+
function intersectToolApprovalMode(captured, current) {
|
|
590
|
+
if (captured === current) return captured;
|
|
591
|
+
if (captured === "prompt" || current === "prompt") return "prompt";
|
|
592
|
+
if (captured === "approve") return current;
|
|
593
|
+
if (current === "approve") return captured;
|
|
594
|
+
return "prompt";
|
|
2549
595
|
}
|
|
2550
596
|
//#endregion
|
|
2551
597
|
//#region extensions/codex/src/app-server/scheduled-app-authority.ts
|
|
@@ -2585,9 +631,6 @@ function resolveScheduledCodexAppCreatorCaptureDecision(params) {
|
|
|
2585
631
|
function normalizeApprovalMode(value) {
|
|
2586
632
|
return value === "allow" || value === "deny" || value === "auto" || value === "ask" ? value : void 0;
|
|
2587
633
|
}
|
|
2588
|
-
function normalizeAppToolApprovalMode(value) {
|
|
2589
|
-
return value === "auto" || value === "prompt" || value === "writes" || value === "approve" ? value : void 0;
|
|
2590
|
-
}
|
|
2591
634
|
function defaultApprovalMode(entry) {
|
|
2592
635
|
return entry.destructiveApprovalMode ?? (entry.allowDestructiveActions ? "allow" : "deny");
|
|
2593
636
|
}
|
|
@@ -2658,9 +701,12 @@ async function readCodexScheduledAppToolsByApp(params) {
|
|
|
2658
701
|
if (connectorId) {
|
|
2659
702
|
const tools = toolsByApp.get(connectorId) ?? /* @__PURE__ */ new Map();
|
|
2660
703
|
const metadata = asOptionalRecord(tool);
|
|
704
|
+
const appMetadata = asOptionalRecord(metadata?._meta);
|
|
2661
705
|
const annotations = asOptionalRecord(metadata?.annotations);
|
|
2662
706
|
tools.set(toolName, {
|
|
2663
707
|
title: typeof metadata?.title === "string" ? metadata.title : void 0,
|
|
708
|
+
linkId: typeof appMetadata?.link_id === "string" && appMetadata.link_id.trim() ? appMetadata.link_id : void 0,
|
|
709
|
+
requiresExplicitLinkId: asOptionalRecord(appMetadata?.["_codex_apps"])?.requires_explicit_link_id === true,
|
|
2664
710
|
destructiveHint: annotations?.destructiveHint === false ? false : void 0,
|
|
2665
711
|
openWorldHint: annotations?.openWorldHint === false ? false : void 0
|
|
2666
712
|
});
|
|
@@ -2688,24 +734,6 @@ async function readCurrentCodexScheduledAppPolicy(params) {
|
|
|
2688
734
|
toolsByApp
|
|
2689
735
|
};
|
|
2690
736
|
}
|
|
2691
|
-
function readCurrentToolPolicy(config, appId, toolName, metadata, fallbackApprovalMode = "auto") {
|
|
2692
|
-
const apps = asOptionalRecord(config.apps);
|
|
2693
|
-
const app = asOptionalRecord(apps?.[appId]);
|
|
2694
|
-
const defaults = asOptionalRecord(apps?.["_default"]);
|
|
2695
|
-
const tools = asOptionalRecord(app?.tools);
|
|
2696
|
-
const tool = asOptionalRecord(tools?.[toolName] ?? (metadata?.title !== void 0 ? tools?.[metadata.title] : void 0));
|
|
2697
|
-
const defaultToolsEnabled = app?.default_tools_enabled;
|
|
2698
|
-
return {
|
|
2699
|
-
enabled: (app ? app.enabled !== false : defaults?.enabled !== false) && (typeof tool?.enabled === "boolean" ? tool.enabled : typeof defaultToolsEnabled === "boolean" ? defaultToolsEnabled : appToolHintsAllowed(metadata, {
|
|
2700
|
-
allowDestructiveActions: (app?.destructive_enabled ?? defaults?.destructive_enabled) !== false,
|
|
2701
|
-
allowOpenWorld: (app?.open_world_enabled ?? defaults?.open_world_enabled) !== false
|
|
2702
|
-
})),
|
|
2703
|
-
approvalMode: normalizeAppToolApprovalMode(tool?.approval_mode) ?? normalizeAppToolApprovalMode(app?.default_tools_approval_mode) ?? normalizeAppToolApprovalMode(defaults?.default_tools_approval_mode) ?? fallbackApprovalMode
|
|
2704
|
-
};
|
|
2705
|
-
}
|
|
2706
|
-
function appToolHintsAllowed(tool, policy) {
|
|
2707
|
-
return (policy.allowDestructiveActions || tool?.destructiveHint === false) && (policy.allowOpenWorld !== false || tool?.openWorldHint === false);
|
|
2708
|
-
}
|
|
2709
737
|
/** Captures only apps callable on the exact active Codex client/thread. */
|
|
2710
738
|
async function captureScheduledCodexAppAuthority(params) {
|
|
2711
739
|
const requestedTimeoutMs = params.timeoutMs ?? CODEX_APP_AUTHORITY_CAPTURE_TIMEOUT_MS;
|
|
@@ -2788,13 +816,6 @@ const APPROVAL_RANK = {
|
|
|
2788
816
|
function stricterApprovalMode(left, right) {
|
|
2789
817
|
return APPROVAL_RANK[left] <= APPROVAL_RANK[right] ? left : right;
|
|
2790
818
|
}
|
|
2791
|
-
function intersectToolApprovalMode(captured, current) {
|
|
2792
|
-
if (captured === current) return captured;
|
|
2793
|
-
if (captured === "prompt" || current === "prompt") return "prompt";
|
|
2794
|
-
if (captured === "approve") return current;
|
|
2795
|
-
if (current === "approve") return captured;
|
|
2796
|
-
return "prompt";
|
|
2797
|
-
}
|
|
2798
819
|
function appApprovalCeiling(mode) {
|
|
2799
820
|
if (mode === "allow") return "approve";
|
|
2800
821
|
return mode === "ask" ? "prompt" : "auto";
|
|
@@ -2827,7 +848,8 @@ function intersectCodexPluginThreadConfigWithScheduledAuthority(config, authorit
|
|
|
2827
848
|
destructiveApprovalMode: stricterApprovalMode(defaultApprovalMode(current), captured.destructiveApprovalMode)
|
|
2828
849
|
};
|
|
2829
850
|
}
|
|
2830
|
-
const
|
|
851
|
+
const pluginAppIds = Object.fromEntries(Object.entries(config.policyContext.pluginAppIds).map(([key, ids]) => [key, ids.filter((id) => Object.hasOwn(apps, id))]).filter(([, ids]) => ids.length > 0));
|
|
852
|
+
const policyContext = buildPluginAppPolicyContext(apps, pluginAppIds);
|
|
2831
853
|
const configPatch = disableUnlistedCodexApps(buildCodexPluginAppsConfigPatchFromPolicyContext(policyContext), currentPolicy.config);
|
|
2832
854
|
const appsPatch = asOptionalRecord(configPatch.apps);
|
|
2833
855
|
for (const [appId, captured] of capturedById) {
|
|
@@ -2981,13 +1003,14 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
2981
1003
|
const legacyDynamicToolsFingerprint = lifecycleTiming.measureSync("legacy-dynamic-tools-fingerprint", () => codexLegacyDynamicToolsFingerprint(params.dynamicTools));
|
|
2982
1004
|
const dynamicToolsFingerprint = lifecycleTiming.measureSync("dynamic-tools-fingerprint", () => hashCodexAppServerBindingFingerprint(legacyDynamicToolsFingerprint));
|
|
2983
1005
|
const dynamicToolsContainDeferred = flattenCodexDynamicToolFunctions(params.dynamicTools).some((tool) => tool.deferLoading === true);
|
|
2984
|
-
const
|
|
1006
|
+
const webSearchPlan = lifecycleTiming.measureSync("web-search-plan", () => resolveCodexWebSearchPlan({
|
|
2985
1007
|
config: params.params.config,
|
|
2986
1008
|
disableTools: params.params.disableTools,
|
|
2987
1009
|
nativeToolSurfaceEnabled: params.nativeCodeModeEnabled,
|
|
2988
1010
|
nativeProviderWebSearchSupport: params.nativeProviderWebSearchSupport,
|
|
2989
1011
|
webSearchAllowed: params.webSearchAllowed
|
|
2990
|
-
}))
|
|
1012
|
+
}));
|
|
1013
|
+
const webSearchThreadConfigFingerprint = fingerprintJsonObject(webSearchPlan.threadConfig);
|
|
2991
1014
|
const networkProxyConfigFingerprint = params.appServer.networkProxy?.configFingerprint;
|
|
2992
1015
|
const contextEngineBinding = lifecycleTiming.measureSync("context-engine-binding", () => buildContextEngineBinding(params.params, params.contextEngineProjection));
|
|
2993
1016
|
const userMcpServersConfigPatch = params.userMcpServersEnabled === false ? void 0 : await buildCodexUserMcpServersThreadConfigPatchForRun({
|
|
@@ -3003,7 +1026,7 @@ async function prepareCodexThreadLifecyclePreflight(params) {
|
|
|
3003
1026
|
});
|
|
3004
1027
|
const nativeSkillIsolation = await lifecycleTiming.measure("native-skill-isolation", () => resolveCodexNativeSkillIsolation({
|
|
3005
1028
|
client: params.client,
|
|
3006
|
-
codexHome: params.appServer.start.env?.CODEX_HOME,
|
|
1029
|
+
codexHome: params.appServer.start.codexHome ?? params.appServer.start.env?.CODEX_HOME,
|
|
3007
1030
|
cwd: params.cwd,
|
|
3008
1031
|
home: params.appServer.start.env?.HOME,
|
|
3009
1032
|
signal: params.signal,
|
|
@@ -3085,8 +1108,14 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3085
1108
|
disposeConfiguration = configuration.dispose;
|
|
3086
1109
|
await context.releaseRetainedThread(configuration.assertCurrent);
|
|
3087
1110
|
configuration.assertCurrent();
|
|
1111
|
+
const clientBoundThread = ringZeroClientInstanceId !== void 0 || resumeBinding.ringZeroClientInstanceId !== void 0 || resumeBinding.ringZeroConfigFingerprint !== void 0 || context.ringZeroActive;
|
|
1112
|
+
const sharedEntry = getCurrentSharedClientEntry(params.client);
|
|
1113
|
+
if (configuration.settledSystemError && !clientBoundThread && resumeBinding.connectionScope !== "supervision" && (!sharedEntry || sharedEntry.activeLeases <= 1 && sharedEntry.pendingAcquires === 0) && !hasCodexAppServerSiblingThreadWork(params.client, resumeBinding.threadId) && !hasCodexAppServerSiblingRouteWork(params.client, resumeBinding.threadId)) {
|
|
1114
|
+
await abandonClient();
|
|
1115
|
+
throw new CodexThreadClientReplacementError();
|
|
1116
|
+
}
|
|
3088
1117
|
const authProfileId = resumeBinding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? resumeBinding.authProfileId;
|
|
3089
|
-
const finalConfigPatch = context.prebuiltFinalConfigPatch ?? params.buildFinalConfigPatch?.({
|
|
1118
|
+
const finalConfigPatch = context.prebuiltFinalConfigPatch ?? await params.buildFinalConfigPatch?.({
|
|
3090
1119
|
action: "resume",
|
|
3091
1120
|
binding: resumeBinding
|
|
3092
1121
|
}) ?? {
|
|
@@ -3163,6 +1192,7 @@ async function resumeExistingCodexThread(params, context) {
|
|
|
3163
1192
|
const resumePatch = {
|
|
3164
1193
|
clientId: resolveCodexAppServerClientInstanceId(params.client),
|
|
3165
1194
|
pendingResumeConfiguration: void 0,
|
|
1195
|
+
...resumeBinding.agentWorkspaceDeveloperInstructions === void 0 && params.agentWorkspaceDeveloperInstructions !== void 0 ? { agentWorkspaceDeveloperInstructions: params.agentWorkspaceDeveloperInstructions } : {},
|
|
3166
1196
|
cwd: params.cwd,
|
|
3167
1197
|
rolloutPath: resolveCodexThreadRolloutPath(response.thread) ?? resumeBinding.rolloutPath,
|
|
3168
1198
|
authProfileId,
|
|
@@ -3261,7 +1291,7 @@ async function startFreshCodexThread(params, context) {
|
|
|
3261
1291
|
const clientId = resolveCodexAppServerClientInstanceId(params.client);
|
|
3262
1292
|
const { bindingIdentity, startModelSelection, startModelProvider, userMcpServersConfigPatch, dynamicToolsFingerprint, dynamicToolsContainDeferred, webSearchThreadConfigFingerprint, nativeSkillIsolationFingerprint, userMcpServersFingerprint, ringZeroConfigFingerprint, ringZeroClientInstanceId, networkProxyConfigFingerprint, contextEngineBinding, environmentSelectionFingerprint, hostSystemAgentActive, restrictedToolSurface, restrictedToolSurfaceInheritedMcpServerNames, nativeSkillIsolation, lifecycleTiming, normalizeBindingModelProvider, throwIfAborted, prebuiltPluginThreadConfig, preserveExistingBinding, rotatedContextEngineBinding, replacementPredecessor } = context;
|
|
3263
1293
|
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? prebuiltPluginThreadConfig ?? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
3264
|
-
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
1294
|
+
const finalConfigPatch = await params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
3265
1295
|
configPatch: params.finalConfigPatch,
|
|
3266
1296
|
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
3267
1297
|
};
|
|
@@ -3496,7 +1526,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3496
1526
|
const incognito = isIncognitoSessionKey(params.params.sessionKey);
|
|
3497
1527
|
if (incognito && (binding.preserveNativeModel || binding.connectionScope === "supervision")) {
|
|
3498
1528
|
if (binding.clientId === clientId && binding.clientId && ((await options.buildLoadedPluginThreadConfig(binding))?.fingerprint ?? binding.pluginAppsFingerprint) === binding.pluginAppsFingerprint) {
|
|
3499
|
-
params.buildFinalConfigPatch?.({
|
|
1529
|
+
await params.buildFinalConfigPatch?.({
|
|
3500
1530
|
action: "resume",
|
|
3501
1531
|
binding
|
|
3502
1532
|
});
|
|
@@ -3511,7 +1541,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3511
1541
|
}
|
|
3512
1542
|
return { kind: "rotate" };
|
|
3513
1543
|
}
|
|
3514
|
-
if (!binding.clientId || binding.clientId !== clientId ||
|
|
1544
|
+
if (!binding.clientId || binding.clientId !== clientId || ringZeroActive && !incognito) return { kind: "resume" };
|
|
3515
1545
|
const retainedThread = await consumeCodexAppServerLiveThread(params.client, binding.threadId);
|
|
3516
1546
|
if (!retainedThread) return { kind: "resume" };
|
|
3517
1547
|
const assertWarmOwner = () => {
|
|
@@ -3527,12 +1557,27 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3527
1557
|
};
|
|
3528
1558
|
let ownershipTransferred = false;
|
|
3529
1559
|
let preserveSubscription = false;
|
|
1560
|
+
let nativeThread;
|
|
3530
1561
|
try {
|
|
3531
1562
|
assertWarmOwner();
|
|
1563
|
+
if (binding.preserveNativeModel || binding.connectionScope === "supervision") {
|
|
1564
|
+
try {
|
|
1565
|
+
nativeThread = await assertAdoptedCodexThreadResumeAllowed(params, binding.threadId, options, assertWarmOwner);
|
|
1566
|
+
} catch (error) {
|
|
1567
|
+
assertWarmOwner();
|
|
1568
|
+
preserveSubscription = isSameCodexAppServerThreadOwner(params.bindingStore.read(bindingIdentity), binding);
|
|
1569
|
+
throw error;
|
|
1570
|
+
}
|
|
1571
|
+
assertWarmOwner();
|
|
1572
|
+
if (nativeThread.status?.type === "notLoaded") {
|
|
1573
|
+
preserveSubscription = true;
|
|
1574
|
+
return { kind: "resume" };
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
3532
1577
|
const pluginThreadConfig = await options.buildLoadedPluginThreadConfig(binding);
|
|
3533
1578
|
assertWarmOwner();
|
|
3534
1579
|
if (pluginThreadConfig && pluginThreadConfig.fingerprint !== binding.pluginAppsFingerprint) return { kind: "rotate" };
|
|
3535
|
-
const prebuiltFinalConfigPatch = params.buildFinalConfigPatch?.({
|
|
1580
|
+
const prebuiltFinalConfigPatch = await params.buildFinalConfigPatch?.({
|
|
3536
1581
|
action: "resume",
|
|
3537
1582
|
binding
|
|
3538
1583
|
}) ?? {
|
|
@@ -3540,7 +1585,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3540
1585
|
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
3541
1586
|
};
|
|
3542
1587
|
const pluginAppsConfigPatch = pluginThreadConfig?.configPatch ?? (params.pluginThreadConfig?.enabled && binding.pluginAppPolicyContext ? buildCodexPluginAppsConfigPatchFromPolicyContext(binding.pluginAppPolicyContext) : void 0);
|
|
3543
|
-
const resumeAuthProfileId = params.params.authProfileId ?? binding.authProfileId;
|
|
1588
|
+
const resumeAuthProfileId = binding.connectionScope === "supervision" ? void 0 : params.params.authProfileId ?? binding.authProfileId;
|
|
3544
1589
|
const resumeConfig = mergeCodexThreadConfigs(params.config, userMcpServersConfigPatch, pluginAppsConfigPatch, prebuiltFinalConfigPatch.configPatch);
|
|
3545
1590
|
const resumeParams = lifecycleTiming.measureSync("warm-thread-resume-params", () => buildThreadResumeParams(params.params, {
|
|
3546
1591
|
threadId: binding.threadId,
|
|
@@ -3548,7 +1593,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3548
1593
|
authProfileId: resumeAuthProfileId,
|
|
3549
1594
|
model: startModelSelection.model,
|
|
3550
1595
|
modelProvider: startModelProvider,
|
|
3551
|
-
preserveNativeModel:
|
|
1596
|
+
preserveNativeModel: binding.preserveNativeModel === true,
|
|
3552
1597
|
appServer: params.appServer,
|
|
3553
1598
|
dynamicTools: params.dynamicTools,
|
|
3554
1599
|
developerInstructions: params.developerInstructions,
|
|
@@ -3564,10 +1609,10 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3564
1609
|
}));
|
|
3565
1610
|
const liveThreadConfigFingerprint = incognito ? retainedThread.configFingerprint : fingerprintCodexThreadConfig({
|
|
3566
1611
|
...resumeParams,
|
|
3567
|
-
model: binding.model ?? resumeParams.model ?? null,
|
|
3568
|
-
requestedModel: resumeParams.model ?? null,
|
|
3569
|
-
modelProvider: binding.modelProvider ?? resumeParams.modelProvider ?? null,
|
|
3570
|
-
requestedModelProvider: resumeParams.modelProvider ?? binding.modelProvider ?? null
|
|
1612
|
+
model: binding.preserveNativeModel ? null : binding.model ?? resumeParams.model ?? null,
|
|
1613
|
+
requestedModel: binding.preserveNativeModel ? null : resumeParams.model ?? null,
|
|
1614
|
+
modelProvider: binding.preserveNativeModel ? null : binding.modelProvider ?? resumeParams.modelProvider ?? null,
|
|
1615
|
+
requestedModelProvider: binding.preserveNativeModel ? null : resumeParams.modelProvider ?? binding.modelProvider ?? null
|
|
3571
1616
|
}, resumeAuthProfileId, dynamicToolsFingerprint);
|
|
3572
1617
|
if (incognito && retainedThread.ephemeralPolicy !== resumeParams.developerInstructions) {
|
|
3573
1618
|
preserveSubscription = true;
|
|
@@ -3592,13 +1637,15 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3592
1637
|
});
|
|
3593
1638
|
assertWarmOwner();
|
|
3594
1639
|
const nativeHookRelayGeneration = prebuiltFinalConfigPatch.nativeHookRelayGeneration ?? binding.nativeHookRelayGeneration;
|
|
3595
|
-
const model = startModelSelection.model;
|
|
1640
|
+
const model = binding.preserveNativeModel ? nativeThread?.model?.trim() || binding.model : startModelSelection.model;
|
|
1641
|
+
const modelProvider = binding.preserveNativeModel ? nativeThread?.modelProvider?.trim() || binding.modelProvider : binding.modelProvider;
|
|
3596
1642
|
if (!(incognito || await lifecycleTiming.measure("warm-thread-write-binding", () => params.bindingStore.mutate(bindingIdentity, {
|
|
3597
1643
|
kind: "patch",
|
|
3598
1644
|
threadId: binding.threadId,
|
|
3599
1645
|
patch: {
|
|
3600
1646
|
cwd: params.cwd,
|
|
3601
1647
|
model,
|
|
1648
|
+
modelProvider,
|
|
3602
1649
|
nativeHookRelayGeneration,
|
|
3603
1650
|
environmentSelectionFingerprint
|
|
3604
1651
|
}
|
|
@@ -3620,6 +1667,7 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3620
1667
|
...!incognito ? {
|
|
3621
1668
|
cwd: params.cwd,
|
|
3622
1669
|
model,
|
|
1670
|
+
modelProvider,
|
|
3623
1671
|
nativeHookRelayGeneration,
|
|
3624
1672
|
environmentSelectionFingerprint
|
|
3625
1673
|
} : {},
|
|
@@ -3634,6 +1682,12 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3634
1682
|
if (!ownershipTransferred) {
|
|
3635
1683
|
let failure;
|
|
3636
1684
|
try {
|
|
1685
|
+
if (preserveSubscription) try {
|
|
1686
|
+
assertWarmOwner();
|
|
1687
|
+
preserveSubscription = isSameCodexAppServerThreadOwner(params.bindingStore.read(bindingIdentity), binding);
|
|
1688
|
+
} catch {
|
|
1689
|
+
preserveSubscription = false;
|
|
1690
|
+
}
|
|
3637
1691
|
if (preserveSubscription) {
|
|
3638
1692
|
if (!await retainCodexAppServerBindingSubscription(params.client, binding.threadId, retainedThread)) failure = { cause: /* @__PURE__ */ new Error("Codex live thread ownership could not be returned to its session") };
|
|
3639
1693
|
} else await retainedThread.release(binding.threadId);
|
|
@@ -3651,21 +1705,6 @@ async function tryReuseCodexLiveThread(options) {
|
|
|
3651
1705
|
}
|
|
3652
1706
|
//#endregion
|
|
3653
1707
|
//#region extensions/codex/src/app-server/thread-lifecycle-adoption.ts
|
|
3654
|
-
/** Passive refusal must precede releasing or acquiring any native subscription. */
|
|
3655
|
-
async function assertAdoptedCodexThreadResumeAllowed(params, threadId, context, assertCurrent) {
|
|
3656
|
-
const { thread } = await context.lifecycleTiming.measure("thread-read-adoption-status", () => params.client.request("thread/read", {
|
|
3657
|
-
threadId,
|
|
3658
|
-
includeTurns: false
|
|
3659
|
-
}, {
|
|
3660
|
-
signal: params.signal,
|
|
3661
|
-
assertCurrent
|
|
3662
|
-
}));
|
|
3663
|
-
context.throwIfAborted();
|
|
3664
|
-
assertCodexThreadAcceptsDirectInput(thread);
|
|
3665
|
-
if (thread.status?.type === "active") throw new CodexAdoptedThreadActiveError();
|
|
3666
|
-
if (thread.id !== threadId) throw new Error("Codex returned another thread during adoption status read");
|
|
3667
|
-
return thread;
|
|
3668
|
-
}
|
|
3669
1708
|
/** All bound preparation follows attach's native-queue-before-binding-lease order. */
|
|
3670
1709
|
async function withCodexThreadLifecycleBinding(params, run) {
|
|
3671
1710
|
const identity = sessionBindingIdentity({
|
|
@@ -3767,7 +1806,8 @@ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFing
|
|
|
3767
1806
|
return {
|
|
3768
1807
|
assertConfigured: observation.assertConfigured,
|
|
3769
1808
|
assertCurrent,
|
|
3770
|
-
dispose
|
|
1809
|
+
dispose,
|
|
1810
|
+
settledSystemError: observation.settledSystemError
|
|
3771
1811
|
};
|
|
3772
1812
|
} catch (error) {
|
|
3773
1813
|
dispose();
|
|
@@ -3776,7 +1816,7 @@ async function preparePendingCodexThreadResume(params, binding, dynamicToolsFing
|
|
|
3776
1816
|
}
|
|
3777
1817
|
/** Observe teardown before release; a successful resume alone can acknowledge ignored overrides. */
|
|
3778
1818
|
async function prepareCodexThreadResume(params, binding, context) {
|
|
3779
|
-
const assertClient = captureCodexAppServerClientLifetime(params.client, binding.connectionScope === "supervision" ? "connection" : "
|
|
1819
|
+
const assertClient = captureCodexAppServerClientLifetime(params.client, binding.connectionScope === "supervision" ? "connection" : "thread-configuration");
|
|
3780
1820
|
const assertCurrent = () => {
|
|
3781
1821
|
params.params.hostCapabilities.assertActive();
|
|
3782
1822
|
params.assertCurrent?.();
|
|
@@ -3802,11 +1842,13 @@ function isCodexThreadNonRunning(status) {
|
|
|
3802
1842
|
}
|
|
3803
1843
|
function observeCodexThreadConfiguration(params, thread, assertCurrent) {
|
|
3804
1844
|
if (!isCodexThreadNonRunning(thread.status)) throw new CodexAdoptedThreadActiveError();
|
|
1845
|
+
const settledSystemError = thread.status.type === "systemError";
|
|
3805
1846
|
let unloaded = thread.status.type === "notLoaded";
|
|
3806
1847
|
return {
|
|
3807
1848
|
dispose: params.client.addNotificationHandler((notification) => {
|
|
3808
1849
|
if (notification.method === "thread/status/changed" && isJsonObject(notification.params) && notification.params.threadId === thread.id && isJsonObject(notification.params.status) && notification.params.status.type === "notLoaded") unloaded = true;
|
|
3809
1850
|
}),
|
|
1851
|
+
settledSystemError,
|
|
3810
1852
|
assertConfigured: () => {
|
|
3811
1853
|
assertCurrent();
|
|
3812
1854
|
if (!unloaded) throw new Error("Codex did not confirm unloading its previous configuration. The thread is preserved; stop competing native work and reconnect before retrying.");
|
|
@@ -3898,10 +1940,11 @@ async function materializePendingSupervisionBranch(params) {
|
|
|
3898
1940
|
responseModelProvider: probeResponse.modelProvider,
|
|
3899
1941
|
responseThreadModelProvider: probeResponse.thread.modelProvider
|
|
3900
1942
|
});
|
|
3901
|
-
const
|
|
1943
|
+
const nativeAttempt = {
|
|
3902
1944
|
...params.attempt,
|
|
3903
1945
|
modelId: nativeModel
|
|
3904
|
-
}
|
|
1946
|
+
};
|
|
1947
|
+
const startParams = buildThreadStartParams(nativeAttempt, {
|
|
3905
1948
|
cwd: params.cwd,
|
|
3906
1949
|
dynamicTools: params.dynamicTools,
|
|
3907
1950
|
appServer: params.appServer,
|
|
@@ -4247,7 +2290,7 @@ async function startOrResumeThread(input) {
|
|
|
4247
2290
|
await releaseRetainedThread(binding.threadId);
|
|
4248
2291
|
const pendingBinding = binding;
|
|
4249
2292
|
const pluginThreadConfig = params.pluginThreadConfig?.enabled ? await lifecycleTiming.measure("plugin-config-build", () => params.pluginThreadConfig?.build()) : void 0;
|
|
4250
|
-
const finalConfigPatch = params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
2293
|
+
const finalConfigPatch = await params.buildFinalConfigPatch?.({ action: "start" }) ?? {
|
|
4251
2294
|
configPatch: params.finalConfigPatch,
|
|
4252
2295
|
nativeHookRelayGeneration: params.nativeHookRelayGeneration
|
|
4253
2296
|
};
|
|
@@ -4557,57 +2600,30 @@ async function startOrResumeThread(input) {
|
|
|
4557
2600
|
});
|
|
4558
2601
|
}
|
|
4559
2602
|
//#endregion
|
|
4560
|
-
//#region extensions/codex/src/app-server/reasoning-effort.ts
|
|
4561
|
-
const CODEX_REASONING_EFFORTS = [
|
|
4562
|
-
"minimal",
|
|
4563
|
-
"low",
|
|
4564
|
-
"medium",
|
|
4565
|
-
"high",
|
|
4566
|
-
"xhigh",
|
|
4567
|
-
"max"
|
|
4568
|
-
];
|
|
4569
|
-
const LEGACY_PRO_REASONING_EFFORTS = [
|
|
4570
|
-
"medium",
|
|
4571
|
-
"high",
|
|
4572
|
-
"xhigh"
|
|
4573
|
-
];
|
|
4574
|
-
const LEGACY_PRO_MODEL_ID_RE = /^gpt-5\.[45]-pro$/u;
|
|
4575
|
-
const MODERN_GPT_5_MODEL_ID_RE = /^gpt-5\.(?:[3-9]|[1-9]\d)(?:$|-)/u;
|
|
4576
|
-
/** Read reasoning metadata after the Codex app-server route has been selected. */
|
|
4577
|
-
function readCodexSupportedReasoningEfforts(compat) {
|
|
4578
|
-
if (!compat || typeof compat !== "object" || Array.isArray(compat)) return;
|
|
4579
|
-
const efforts = compat.supportedReasoningEfforts;
|
|
4580
|
-
if (!Array.isArray(efforts)) return;
|
|
4581
|
-
return efforts.filter((effort) => typeof effort === "string");
|
|
4582
|
-
}
|
|
4583
|
-
function resolveSupportedReasoningEffort(params) {
|
|
4584
|
-
const declared = new Set(params.supportedReasoningEfforts.map((effort) => effort.trim().toLowerCase()));
|
|
4585
|
-
const supported = CODEX_REASONING_EFFORTS.filter((effort) => declared.has(effort));
|
|
4586
|
-
if (supported.includes(params.requested)) return params.requested;
|
|
4587
|
-
const requestedRank = CODEX_REASONING_EFFORTS.indexOf(params.requested);
|
|
4588
|
-
return supported.find((effort) => CODEX_REASONING_EFFORTS.indexOf(effort) >= requestedRank) ?? supported.at(-1);
|
|
4589
|
-
}
|
|
4590
|
-
function resolveCodexAppServerReasoningEffort(params) {
|
|
4591
|
-
if (params.thinkLevel === "ultra") return "ultra";
|
|
4592
|
-
if (params.thinkLevel === "off") return params.supportedReasoningEfforts?.includes("none") ? "none" : null;
|
|
4593
|
-
if (params.thinkLevel === "adaptive") return null;
|
|
4594
|
-
if (params.supportedReasoningEfforts) return resolveSupportedReasoningEffort({
|
|
4595
|
-
requested: params.thinkLevel,
|
|
4596
|
-
supportedReasoningEfforts: params.supportedReasoningEfforts
|
|
4597
|
-
}) ?? null;
|
|
4598
|
-
const modelId = params.modelId.trim().toLowerCase();
|
|
4599
|
-
if (LEGACY_PRO_MODEL_ID_RE.test(modelId)) return resolveSupportedReasoningEffort({
|
|
4600
|
-
requested: params.thinkLevel,
|
|
4601
|
-
supportedReasoningEfforts: LEGACY_PRO_REASONING_EFFORTS
|
|
4602
|
-
}) ?? null;
|
|
4603
|
-
if (params.thinkLevel === "minimal" && MODERN_GPT_5_MODEL_ID_RE.test(modelId)) return "low";
|
|
4604
|
-
if (params.thinkLevel === "minimal" || params.thinkLevel === "low" || params.thinkLevel === "medium" || params.thinkLevel === "high" || params.thinkLevel === "xhigh") return params.thinkLevel;
|
|
4605
|
-
return null;
|
|
4606
|
-
}
|
|
4607
|
-
//#endregion
|
|
4608
2603
|
//#region extensions/codex/src/app-server/user-input.ts
|
|
2604
|
+
function prependHistoryProvenance(input, historyProvenancePrefix) {
|
|
2605
|
+
if (!historyProvenancePrefix) return input;
|
|
2606
|
+
const firstTextIndex = input.findIndex((item) => item.type === "text");
|
|
2607
|
+
if (firstTextIndex === -1) return [{
|
|
2608
|
+
type: "text",
|
|
2609
|
+
text: historyProvenancePrefix,
|
|
2610
|
+
text_elements: []
|
|
2611
|
+
}, ...input];
|
|
2612
|
+
return input.map((item, index) => index === firstTextIndex && item.type === "text" ? {
|
|
2613
|
+
...item,
|
|
2614
|
+
text: `${historyProvenancePrefix}${item.text}`
|
|
2615
|
+
} : item);
|
|
2616
|
+
}
|
|
4609
2617
|
/** Builds ordered Codex user input for both new turns and same-turn steering. */
|
|
4610
|
-
function buildCodexUserInput(text, images) {
|
|
2618
|
+
function buildCodexUserInput(text, images, contextImageGroups, historyProvenancePrefix) {
|
|
2619
|
+
if (text !== void 0 && contextImageGroups?.length) {
|
|
2620
|
+
let offset = 0;
|
|
2621
|
+
return prependHistoryProvenance([...contextImageGroups.flatMap((group) => {
|
|
2622
|
+
const parts = buildCodexUserInput(text.slice(offset, group.end), group.images);
|
|
2623
|
+
offset = group.end;
|
|
2624
|
+
return parts;
|
|
2625
|
+
}), ...buildCodexUserInput(text.slice(offset), images)], historyProvenancePrefix);
|
|
2626
|
+
}
|
|
4611
2627
|
const imageInputs = (images ?? []).map((image) => {
|
|
4612
2628
|
const imageUrl = sanitizeInlineImageDataUrl(`data:${image.mimeType};base64,${image.data}`);
|
|
4613
2629
|
return imageUrl ? {
|
|
@@ -4619,16 +2635,16 @@ function buildCodexUserInput(text, images) {
|
|
|
4619
2635
|
text_elements: []
|
|
4620
2636
|
};
|
|
4621
2637
|
});
|
|
4622
|
-
return [...text === void 0 ? [] : [{
|
|
2638
|
+
return prependHistoryProvenance([...text === void 0 ? [] : [{
|
|
4623
2639
|
type: "text",
|
|
4624
2640
|
text,
|
|
4625
2641
|
text_elements: []
|
|
4626
|
-
}], ...imageInputs];
|
|
2642
|
+
}], ...imageInputs], historyProvenancePrefix);
|
|
4627
2643
|
}
|
|
4628
2644
|
//#endregion
|
|
4629
2645
|
//#region extensions/codex/src/app-server/turn-params.ts
|
|
4630
2646
|
const CODEX_CURRENT_SENDER_FIELD_MAX_CHARS = 256;
|
|
4631
|
-
function
|
|
2647
|
+
function readCodexCurrentSender(params) {
|
|
4632
2648
|
const metadata = asOptionalRecord(asOptionalRecord(params.userTurnTranscriptRecorder?.message)?.["__openclaw"]);
|
|
4633
2649
|
const recorded = [
|
|
4634
2650
|
normalizeOptionalString(metadata?.["senderId"]),
|
|
@@ -4642,11 +2658,19 @@ function buildCodexCurrentSenderContextValue(params) {
|
|
|
4642
2658
|
];
|
|
4643
2659
|
if (!id && !name && !username) return;
|
|
4644
2660
|
const bound = (value) => truncateUtf16Safe(value, CODEX_CURRENT_SENDER_FIELD_MAX_CHARS);
|
|
4645
|
-
return
|
|
2661
|
+
return {
|
|
4646
2662
|
...id ? { id: bound(id) } : {},
|
|
4647
2663
|
...name ? { name: bound(name) } : {},
|
|
4648
2664
|
...username ? { username: bound(username) } : {}
|
|
4649
|
-
}
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
function buildCodexCurrentSenderContextValue(params) {
|
|
2668
|
+
const sender = readCodexCurrentSender(params);
|
|
2669
|
+
return sender ? JSON.stringify({ sender }) : void 0;
|
|
2670
|
+
}
|
|
2671
|
+
function buildCodexHistoryProvenancePrefix(params) {
|
|
2672
|
+
const sender = readCodexCurrentSender(params);
|
|
2673
|
+
return sender?.id ? neutralizeCodexExplicitMentionSigils(`[OpenClaw conversation info: sender=${JSON.stringify(sender)}]\n`) : void 0;
|
|
4650
2674
|
}
|
|
4651
2675
|
function buildTurnStartParams(params, options) {
|
|
4652
2676
|
const modelSelection = options.preserveNativeTurnSettings ? void 0 : resolveCodexAppServerRequestModelSelection({
|
|
@@ -4669,6 +2693,10 @@ function buildTurnStartParams(params, options) {
|
|
|
4669
2693
|
let additionalContext = buildCodexTemporalAdditionalContext(params, { sessionStatusAvailable: options.sessionStatusAvailable === true });
|
|
4670
2694
|
additionalContext = {
|
|
4671
2695
|
...additionalContext,
|
|
2696
|
+
openclaw_active_computer: {
|
|
2697
|
+
kind: "application",
|
|
2698
|
+
value: params.hostCapabilities.activeComputerContext?.() ?? "Current active computer: active_node=unknown (host presence unavailable)"
|
|
2699
|
+
},
|
|
4672
2700
|
openclaw_source_delivery: {
|
|
4673
2701
|
kind: "application",
|
|
4674
2702
|
value: ["Current source-delivery policy for this turn (replaces earlier source-delivery guidance):", buildHarnessVisibleReplyGuidance({
|
|
@@ -4694,7 +2722,8 @@ function buildTurnStartParams(params, options) {
|
|
|
4694
2722
|
};
|
|
4695
2723
|
return {
|
|
4696
2724
|
threadId: options.threadId,
|
|
4697
|
-
|
|
2725
|
+
...params.trigger ? { turnTrigger: params.trigger } : {},
|
|
2726
|
+
input: [...buildCodexUserInput(options.promptText ?? params.prompt, params.images, options.contextImageGroups, options.historyProvenancePrefix ?? (params.trigger === "user" ? buildCodexHistoryProvenancePrefix(params) : void 0)), ...options.explicitSkillInputs ?? []],
|
|
4698
2727
|
...additionalContext ? { additionalContext } : {},
|
|
4699
2728
|
cwd: options.cwd,
|
|
4700
2729
|
...options.appServer.sessionRoot ? { runtimeWorkspaceRoots: [options.appServer.sessionRoot] } : {},
|
|
@@ -4758,7 +2787,7 @@ function buildDefaultCollaborationInstructions() {
|
|
|
4758
2787
|
"",
|
|
4759
2788
|
"Use the `request_user_input` tool only when it is listed in the available tools for this turn.",
|
|
4760
2789
|
"",
|
|
4761
|
-
"In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions.
|
|
2790
|
+
"In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. When a missing preference, constraint, or clarification warrants a question, use `request_user_input_async` if it is available and continue independent work. Answers arrive as ordinary user messages. A suggested or preselected answer is not consent; wait for explicit approval before dependent actions that require it. If neither question tool is available, ask a concise plain-text question. Never write a multiple choice question as a textual assistant message."
|
|
4762
2791
|
].join("\n");
|
|
4763
2792
|
}
|
|
4764
2793
|
function buildCronCollaborationInstructions() {
|
|
@@ -4773,4 +2802,4 @@ function joinPresentSections(...sections) {
|
|
|
4773
2802
|
return sections.filter((section) => Boolean(section?.trim())).join("\n\n");
|
|
4774
2803
|
}
|
|
4775
2804
|
//#endregion
|
|
4776
|
-
export {
|
|
2805
|
+
export { resolveCodexContinuityProjectionMaxChars as A, buildCodexContinuityCalibration as C, projectContextEngineAssemblyForCodex as D, neutralizeCodexExplicitMentionSigils as E, resolveCodexAppServerReasoningEffort as M, resolveCodexContextEngineProjectionMaxChars as O, CodexContextAttachmentError as S, isCodexDurableCustomMessage as T, readCodexMcpToolUiVisibility as _, buildTurnStartParams as a, isContextEngineBindingCompatible as b, assertScheduledCodexAppAuthorityRuntime as c, buildScheduledCodexAppServerConnectionIdentity as d, captureScheduledCodexAppAuthority as f, readCodexMcpToolConnectorId as g, resolveScheduledCodexAppCreatorCaptureDecision as h, buildTurnCollaborationMode as i, readCodexSupportedReasoningEfforts as j, resolveCodexContextEngineProjectionReserveTokens as k, buildLegacyScheduledCodexAppRecoveryPrompt as l, readCurrentCodexScheduledAppPolicy as m, buildCodexParentLocalInstructions as n, buildCodexUserInput as o, intersectCodexPluginThreadConfigWithScheduledAuthority as p, buildCodexTemporalAdditionalContext as r, startOrResumeThread as s, buildCodexHistoryProvenancePrefix as t, buildScheduledCodexAppAuthorityInputFingerprint as u, isCodexAppServerProfilerEnabled as v, fitCodexProjectedContextForTurnStart as w, CODEX_TURN_START_TEXT_INPUT_MAX_CHARS as x, buildContextEngineBinding as y };
|