chainlesschain 0.163.3 → 0.163.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/scripts/command-lifecycle-report.mjs +330 -38
- package/src/assets/web-panel/assets/{AIOps-DIY6KxV-.js → AIOps-DB5X7qqV.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-BXH0muPt.js → ActionButton-DxHRtKLk.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D39w8vmg.js → Analytics-DIH5QrxT.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-CUU1zvLs.js → AppLayout-uu0oxVz7.js} +4 -4
- package/src/assets/web-panel/assets/{Artifacts-D8azHO3T.js → Artifacts-BNSMiW9Y.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-D0tAwXTg.js → Audit-DcAzB4_x.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-C29mhJll.js → BackgroundAgents-ClSsvjIc.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-D6Aa1Gro.js → Backup-BMciY0js.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-B1uoHbPQ.js → BaseInput-Hn3b57Sh.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-Bs4DLOR5.js → Chat-CoSlC53V.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DUYIBSle.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CNcBPEa4.js → Checkbox-B4L4Eu4w.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CIY89763.js → Codegen-C77FgRxF.js} +1 -1
- package/src/assets/web-panel/assets/{Col-CsPYFmgo.js → Col-BrLtJLH3.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BBocEge4.js → Community-Be7pXXhA.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-TQ8Ux0Lf.js → Compact-DuGPecjo.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-DfBsZTft.js → Compliance-CzS66mjP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-Cg-I6FVH.js → Cowork-CEhjCoyK.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-DTQzfRTp.js → Cron-D7ir8Vja.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DGxEKGpR.js → Crosschain-BcXmuLxf.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DLZ1RDSr.js → DID-Cto1dtBy.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-XneICKNI.js → Dashboard-B7KjgwOX.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BRU00J1-.js → Dropdown-COMqxijg.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BzCbJuAy.js → EmailListRenderer-BZrya-nf.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D7BRyNBG.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-D9DeFIhW.js → Federation-kZU8c-27.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-6rAtA9Ko.js → FormItemContext-sOV22v2-.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-8Hqy80pU.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Cmvkj1rt.js → Git-K-ZxsLsH.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-BRiQUjjw.js → Governance-D3OEnawI.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CKN_EzVf.js → Inference-Bll0gRmP.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-BCDHwpwQ.js → KnowledgeGraph-DBXwjr53.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DK9ovhaB.js → Logs-CEzPwEfP.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BILPGakp.js → Marketplace-BYyRyOPz.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DTVCOr9-.js → McpTools-DVu8w_rM.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Hzc1iow4.js → Memory-tenuZ_hO.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-GXfgnGYV.js → MobileBridge-Bxgy4ozS.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-B-3TJWsS.js → MobileProjects-Dgx1D6sc.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-DwSJMWtK.js → Mtc-CRfJ8r8j.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-D2uxf-DA.js → MtcAudit-BBJq4Aiz.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-DG6kbKK0.js → Multisig-B3ii9lai.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-f5p49EDU.js → NLProgramming-FIi-Cubv.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-FUgHyzSD.js → Notes-DIq8MPf1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DCHGee7e.js → NotificationSettings-BFOD9eGd.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-Dm6LlIoa.js → OrderTableRenderer--41u7vre.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BawA7qAg.js → Organization-B_0AWztW.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-D6a5wZU1.js → Overflow-B6I7TuXv.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-K8WavwnA.js → P2P-Cdf6qens.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-4JAJYaqx.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BDWvi8PD.js → Permissions-6nFBjsQp.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-DF_K4E9i.js → PersonalDataHub-nQYV7xpn.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-BYeZlesI.js → Pipeline-CBVod5jm.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BIHlgnIc.js → Privacy-D52jW1Nt.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-Cvf-6B5P.js → ProjectInit-Bdx3BYhf.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CnwfJrRz.js → ProjectSettings-b7xr4DUa.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-b-jPXzF4.js → Projects-DhfifCKx.js} +1 -1
- package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-VLf3gAfL.js → QrScannerModal-FQKSmc3e.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-B5vGjRiF.js → QuickAsk-CSTrhzcF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend--UW4iYs-.js → Recommend-CxMABOUh.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DMSPcfKR.js → RemoteSession-Cv2T8yRa.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-kQXlEHmj.js → Reputation-CE1k4Vvu.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CxGu3pit.js → Row-BNgNcAV_.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Bi5lo8MZ.js → RssFeed-5ZiBZ3ph.js} +3 -3
- package/src/assets/web-panel/assets/{Search-Cq_H5RTh.js → Search-mDOIF1UV.js} +1 -1
- package/src/assets/web-panel/assets/{Security-YFbmkaQS.js → Security-CtuCTC-e.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DLOGdaLm.js → Services-DxFO2Zg7.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CteIxb3D.js → Skeleton-CKmKkH9y.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-CccopaIZ.js → Skills-Bmzl9qkk.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-AzfEQKX1.js → Sla-9RqaWHOC.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-ekGo5l77.js → SpeechSettings-CGdzFIYw.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BmZmAgYH.js → SyncSettings-CvNuCeBT.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-D7q3Z4uL.js → Tasks-B_sLdcnw.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-CX1Tgeka.js → Templates-D9_YijdK.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Zc59jF4i.js → Tenant-Cuc7BUSP.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CXOD7y83.js → Terminal-6VYRnJgd.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-B1aBHPBo.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-zALlOq_U.js → Tokens-BZ94Lt5W.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CQfJMXaq.js → Trigger-DEcZgDaX.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-BU0RcM-w.js → Trust-CC3z9THe.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-IFZ3HsbH.js → UkeySign-Cvtw8cV3.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BzslgW0v.js → VideoEditing-CL4e6Bn3.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BZAcQNS6.js → Wallet-ycEemP27.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CBK1DMi0.js → WebAuthn-DgZ4X_aw.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-BlMTPzO-.js → WorkflowEditor-D0qMhSv7.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B51-8QVn.js → chat-DsAg01Zn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-504RTByU.js → colors-B0oqAhrQ.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C-GP6IQM.js → compact-item-CmX0Rabz.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DOkIj7ID.js → createContext-Be4cn1Ub.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DN7V-_FW.js → hasIn-D5v12kVq.js} +1 -1
- package/src/assets/web-panel/assets/{index-kt_5rfcZ.js → index-0eWLkQKT.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUhQpiYv.js → index-301ogenv.js} +1 -1
- package/src/assets/web-panel/assets/{index-f4Bo4KXm.js → index-5LGY0JbG.js} +1 -1
- package/src/assets/web-panel/assets/{index--n-ZciV3.js → index-7VKDn-t1.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1phyca8.js → index-8WvYGnON.js} +1 -1
- package/src/assets/web-panel/assets/{index-7jIqHbzw.js → index-B1YGjOWC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DEMPUVa3.js → index-B3mUHuVc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkxhaeta.js → index-BA4oulaP.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYopNjcJ.js → index-BBREnn1V.js} +1 -1
- package/src/assets/web-panel/assets/{index-P2mfY27Z.js → index-BBvyHguy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DA2f1UV3.js → index-BKmsS2jj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CP2A226L.js → index-BRIqLy1m.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfiEM64n.js → index-BXGSbelg.js} +3 -3
- package/src/assets/web-panel/assets/{index-B2-x9bS8.js → index-BaE0Ui07.js} +1 -1
- package/src/assets/web-panel/assets/{index-CySTJKeq.js → index-BoAIAZ5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-kAqQXfP6.js → index-BqltWojQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DAOpQvl_.js → index-CGiIyTZw.js} +1 -1
- package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
- package/src/assets/web-panel/assets/{index-C4DV_5uA.js → index-CZ2wUEgr.js} +1 -1
- package/src/assets/web-panel/assets/{index-yYTQZypF.js → index-CZpfzcuy.js} +1 -1
- package/src/assets/web-panel/assets/{index-et5JIPHO.js → index-Ckrk3pIV.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaA__-q1.js → index-CqoCZ5vR.js} +1 -1
- package/src/assets/web-panel/assets/{index-By6u5IV1.js → index-CyWBir7N.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKTfZyfU.js → index-CyyGTMKd.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_vGU-Js.js → index-D0BSkxYQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cao5DZy7.js → index-D5YVopyr.js} +1 -1
- package/src/assets/web-panel/assets/{index-C3TSohzc.js → index-DKUBj36_.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFIFJ5Dr.js → index-DM724NgU.js} +1 -1
- package/src/assets/web-panel/assets/{index-ConjaJro.js → index-DQRsQen0.js} +1 -1
- package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
- package/src/assets/web-panel/assets/{index-C9_48OzU.js → index-DikJ0Qqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-C0k_Nqg9.js → index-Dn2dSdXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE-hmI2l.js → index-DyUaFWEk.js} +1 -1
- package/src/assets/web-panel/assets/{index-so8eIIxF.js → index-Hfe_dEGx.js} +1 -1
- package/src/assets/web-panel/assets/{index-FcV8J_Qz.js → index-_JjRLSD0.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9LyiN-L.js → index-frSojpHj.js} +1 -1
- package/src/assets/web-panel/assets/{index-CX78k-qf.js → index-jK3ax6Z6.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8UltbXK.js → index-qMuZ-NRR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqcLo5WV.js → index-q_fQ94SE.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CPYEIdpV.js → initDefaultProps-D4VdhJ9_.js} +1 -1
- package/src/assets/web-panel/assets/{motion-Cjim9pxs.js → motion-CZJA0MQW.js} +1 -1
- package/src/assets/web-panel/assets/{move-DuU-cgz3.js → move-BqEs7Sb1.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DJHNAkta.js → mtc-parser-BpHEtsu1.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DSQBgdBj.js → omit-BW94tY9W.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CTmT_j5m.js → pickAttrs-BnBjPWP9.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-Lfm6v-r4.js → placementArrow-uUkjQ_hu.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-CgrcPcDi.js → responsiveObserve-B-klUqXK.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CuE3110i.js → slide-CvY1cNAs.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-DiK5nbpf.js → statusUtils-Ix5sEDT2.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DPEJdQaZ.js → styleChecker-C8qcXOne.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CnnodGmp.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DWF84Yav.js → useFs-DGJr345n.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-a5b5Dw2f.js → usePersonalDataHub-BboN99rI.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DAluzXOq.js → vnode-Bgt8Vgte.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Dokg7uAZ.js → zoom-BhJtErgt.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/artifacts.js +98 -0
- package/src/commands/mcp.js +1 -1
- package/src/commands/review.js +221 -22
- package/src/commands/session-mcp-recovery.js +5 -5
- package/src/data/changelog.json +14 -1
- package/src/gateways/ws/ws-agent-handler.js +492 -14
- package/src/harness/jsonl-session-store.js +39 -1
- package/src/harness/mcp-client.js +172 -29
- package/src/harness/prompt-compressor.js +24 -0
- package/src/harness/provider-backed-compaction.js +205 -0
- package/src/lib/app-preview.js +3 -0
- package/src/lib/command-lifecycle-report.js +1340 -145
- package/src/lib/delivery-coordinator.js +30 -5
- package/src/lib/delivery-production-adapter.js +1777 -0
- package/src/lib/delivery-production-runner.js +240 -0
- package/src/lib/durable-security-store.js +29 -17
- package/src/lib/execution-trace/trace-context.cjs +179 -0
- package/src/lib/execution-trace/trace-context.js +8 -175
- package/src/lib/mcp-call-ledger-store.js +58 -6
- package/src/lib/mcp-host-recovery-runtime.js +2 -0
- package/src/lib/mcp-ledger-recovery-admission.js +39 -3
- package/src/lib/mcp-recovery-adjudication.js +256 -19
- package/src/lib/mcp-sandbox-policy.js +29 -0
- package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
- package/src/lib/mcp-stdio-executable-identity.js +33 -2
- package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
- package/src/lib/mcp-stdio-package-materialization.js +721 -333
- package/src/lib/observability/index.js +2 -0
- package/src/lib/observability/otlp-exporter.js +2 -2
- package/src/lib/packer/pack-update-applier.js +8 -1
- package/src/lib/packer/pkg-config-generator.js +34 -0
- package/src/lib/process-execution-broker/index.js +34 -5
- package/src/lib/process-execution-broker/platform-sandbox.js +82 -1
- package/src/lib/scheduler-kernel/contract.js +315 -0
- package/src/lib/scheduler-kernel/store.js +1345 -0
- package/src/lib/session-host-lease.js +403 -30
- package/src/lib/session-persistence-failure.js +4 -1
- package/src/lib/sub-agent-context.js +8 -0
- package/src/repl/agent-repl.js +285 -38
- package/src/runtime/agent-core.js +1044 -90
- package/src/runtime/headless-runner.js +260 -166
- package/src/runtime/headless-stream.js +254 -25
- package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +0 -1
- package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +0 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +0 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +0 -1
- package/src/assets/web-panel/assets/index-D_FWpDZM.js +0 -1
|
@@ -34,13 +34,16 @@ export const MCP_CALL_LEDGER_EVENT = "mcp_call_ledger";
|
|
|
34
34
|
export const MCP_CALL_LEDGER_EVENT_SCHEMA_VERSION = 1;
|
|
35
35
|
export const MCP_CALL_RECOVERY_ADJUDICATION_EVENT =
|
|
36
36
|
"mcp_call_recovery_adjudication";
|
|
37
|
-
export const MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION =
|
|
37
|
+
export const MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION = 2;
|
|
38
38
|
export const McpCallRecoveryDecision = Object.freeze({
|
|
39
39
|
CONFIRMED_APPLIED: "confirmed_applied",
|
|
40
40
|
CONFIRMED_NOT_APPLIED: "confirmed_not_applied",
|
|
41
41
|
});
|
|
42
42
|
export const MCP_CALL_RECOVERY_AUTHORITY = "local-cli-tty";
|
|
43
|
-
export const MCP_CALL_RECOVERY_CONFIRMATION =
|
|
43
|
+
export const MCP_CALL_RECOVERY_CONFIRMATION =
|
|
44
|
+
"typed-digest-host-authority-revoke";
|
|
45
|
+
export const MCP_CALL_RECOVERY_LEGACY_CONFIRMATION =
|
|
46
|
+
"typed-digest-host-stopped";
|
|
44
47
|
|
|
45
48
|
const TERMINAL = new Set([
|
|
46
49
|
McpCallStatus.COMPLETED,
|
|
@@ -86,7 +89,7 @@ const EFFECT_CONTRACT_FIELDS = new Set([
|
|
|
86
89
|
]);
|
|
87
90
|
const OUTPUT_SUMMARY_FIELDS = new Set(["sha256", "bytes", "kind"]);
|
|
88
91
|
const ERROR_SUMMARY_FIELDS = new Set(["name", "code", "messageDigest"]);
|
|
89
|
-
const
|
|
92
|
+
const LEGACY_ADJUDICATION_FIELDS = new Set([
|
|
90
93
|
"schemaVersion",
|
|
91
94
|
"requestId",
|
|
92
95
|
"sessionId",
|
|
@@ -98,6 +101,18 @@ const ADJUDICATION_FIELDS = new Set([
|
|
|
98
101
|
"confirmation",
|
|
99
102
|
"reasonDigest",
|
|
100
103
|
]);
|
|
104
|
+
const ADJUDICATION_FIELDS = new Set([
|
|
105
|
+
...LEGACY_ADJUDICATION_FIELDS,
|
|
106
|
+
"hostRevocation",
|
|
107
|
+
]);
|
|
108
|
+
const HOST_REVOCATION_FIELDS = new Set([
|
|
109
|
+
"requestId",
|
|
110
|
+
"revocationEpoch",
|
|
111
|
+
"targetLeaseId",
|
|
112
|
+
"targetFencingToken",
|
|
113
|
+
"targetOwnerPid",
|
|
114
|
+
]);
|
|
115
|
+
const HOST_LEASE_ID = /^lease-[0-9a-f-]{36}$/;
|
|
101
116
|
const RECOVERY_PROJECTION_FIELDS = new Set([
|
|
102
117
|
"sessionId",
|
|
103
118
|
"records",
|
|
@@ -414,9 +429,32 @@ function stableSorted(values) {
|
|
|
414
429
|
}
|
|
415
430
|
|
|
416
431
|
function canonicalAdjudication(value) {
|
|
432
|
+
const legacy = value?.schemaVersion === 1;
|
|
433
|
+
const validConfirmation =
|
|
434
|
+
(legacy && value?.confirmation === MCP_CALL_RECOVERY_LEGACY_CONFIRMATION) ||
|
|
435
|
+
(value?.schemaVersion === MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION &&
|
|
436
|
+
value?.confirmation === MCP_CALL_RECOVERY_CONFIRMATION);
|
|
437
|
+
const fields = legacy ? LEGACY_ADJUDICATION_FIELDS : ADJUDICATION_FIELDS;
|
|
438
|
+
const revocation = value?.hostRevocation;
|
|
439
|
+
const targetIsNull = revocation?.targetLeaseId === null;
|
|
440
|
+
const validRevocation =
|
|
441
|
+
legacy ||
|
|
442
|
+
(hasExactFields(revocation, HOST_REVOCATION_FIELDS) &&
|
|
443
|
+
revocation.requestId === value?.requestId &&
|
|
444
|
+
Number.isSafeInteger(revocation.revocationEpoch) &&
|
|
445
|
+
revocation.revocationEpoch >= 1 &&
|
|
446
|
+
(targetIsNull
|
|
447
|
+
? revocation.targetFencingToken === null &&
|
|
448
|
+
revocation.targetOwnerPid === null
|
|
449
|
+
: HOST_LEASE_ID.test(String(revocation.targetLeaseId || "")) &&
|
|
450
|
+
Number.isSafeInteger(revocation.targetFencingToken) &&
|
|
451
|
+
revocation.targetFencingToken >= 1 &&
|
|
452
|
+
Number.isSafeInteger(revocation.targetOwnerPid) &&
|
|
453
|
+
revocation.targetOwnerPid >= 1));
|
|
417
454
|
if (
|
|
418
|
-
!hasExactFields(value,
|
|
419
|
-
|
|
455
|
+
!hasExactFields(value, fields) ||
|
|
456
|
+
!validConfirmation ||
|
|
457
|
+
!validRevocation ||
|
|
420
458
|
!isCanonicalProtocolText(
|
|
421
459
|
value.requestId,
|
|
422
460
|
MCP_CALL_LEDGER_PROTOCOL_LIMITS.identifier,
|
|
@@ -433,7 +471,6 @@ function canonicalAdjudication(value) {
|
|
|
433
471
|
!RAW_AUTHORITY_HASH.test(String(value.expectedHeadHash || "")) ||
|
|
434
472
|
!PAYLOAD_DIGEST.test(String(value.expectedRecoveryDigest || "")) ||
|
|
435
473
|
value.authority !== MCP_CALL_RECOVERY_AUTHORITY ||
|
|
436
|
-
value.confirmation !== MCP_CALL_RECOVERY_CONFIRMATION ||
|
|
437
474
|
!PAYLOAD_DIGEST.test(String(value.reasonDigest || ""))
|
|
438
475
|
) {
|
|
439
476
|
return null;
|
|
@@ -449,6 +486,17 @@ function canonicalAdjudication(value) {
|
|
|
449
486
|
authority: value.authority,
|
|
450
487
|
confirmation: value.confirmation,
|
|
451
488
|
reasonDigest: value.reasonDigest,
|
|
489
|
+
...(legacy
|
|
490
|
+
? {}
|
|
491
|
+
: {
|
|
492
|
+
hostRevocation: Object.freeze({
|
|
493
|
+
requestId: revocation.requestId,
|
|
494
|
+
revocationEpoch: revocation.revocationEpoch,
|
|
495
|
+
targetLeaseId: revocation.targetLeaseId,
|
|
496
|
+
targetFencingToken: revocation.targetFencingToken,
|
|
497
|
+
targetOwnerPid: revocation.targetOwnerPid,
|
|
498
|
+
}),
|
|
499
|
+
}),
|
|
452
500
|
});
|
|
453
501
|
}
|
|
454
502
|
|
|
@@ -546,6 +594,10 @@ export function computeMcpRecoveryFenceDigest(recovery = {}) {
|
|
|
546
594
|
authority: entry?.authority || null,
|
|
547
595
|
confirmation: entry?.confirmation || null,
|
|
548
596
|
reasonDigest: entry?.reasonDigest || null,
|
|
597
|
+
...(entry?.schemaVersion ===
|
|
598
|
+
MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION
|
|
599
|
+
? { hostRevocation: entry?.hostRevocation || null }
|
|
600
|
+
: {}),
|
|
549
601
|
})),
|
|
550
602
|
),
|
|
551
603
|
replayDenied: stableSorted(
|
|
@@ -98,6 +98,7 @@ export function createMcpHostRecoveryRuntime({
|
|
|
98
98
|
recovery = null,
|
|
99
99
|
recoveryError = null,
|
|
100
100
|
controller = null,
|
|
101
|
+
dispatchAdmission = null,
|
|
101
102
|
} = {}) {
|
|
102
103
|
const admissionController = tightenRecoveryController(
|
|
103
104
|
controller,
|
|
@@ -120,6 +121,7 @@ export function createMcpHostRecoveryRuntime({
|
|
|
120
121
|
resolveEffect: (serverName, toolName) =>
|
|
121
122
|
resolveHostMcpEffect(bundle, serverName, toolName),
|
|
122
123
|
sessionId,
|
|
124
|
+
dispatchAdmission,
|
|
123
125
|
})
|
|
124
126
|
: rawClient;
|
|
125
127
|
return Object.freeze({
|
|
@@ -813,6 +813,7 @@ export function createRecoveryGuardedMcpClient({
|
|
|
813
813
|
controller,
|
|
814
814
|
resolveEffect = null,
|
|
815
815
|
sessionId = null,
|
|
816
|
+
dispatchAdmission = null,
|
|
816
817
|
} = {}) {
|
|
817
818
|
if (!client || typeof client.callTool !== "function") {
|
|
818
819
|
throw new TypeError("MCP client wrapper requires client.callTool()");
|
|
@@ -826,6 +827,9 @@ export function createRecoveryGuardedMcpClient({
|
|
|
826
827
|
if (resolveEffect != null && typeof resolveEffect !== "function") {
|
|
827
828
|
throw new TypeError("MCP client effect resolver must be a function");
|
|
828
829
|
}
|
|
830
|
+
if (dispatchAdmission != null && typeof dispatchAdmission !== "function") {
|
|
831
|
+
throw new TypeError("MCP client dispatch admission must be a function");
|
|
832
|
+
}
|
|
829
833
|
|
|
830
834
|
const existingGuard = GUARDED_MCP_CLIENTS.get(client);
|
|
831
835
|
if (existingGuard) {
|
|
@@ -833,7 +837,8 @@ export function createRecoveryGuardedMcpClient({
|
|
|
833
837
|
existingGuard.ledger === ledger &&
|
|
834
838
|
existingGuard.controller === controller &&
|
|
835
839
|
existingGuard.resolveEffect === resolveEffect &&
|
|
836
|
-
existingGuard.sessionId === sessionId
|
|
840
|
+
existingGuard.sessionId === sessionId &&
|
|
841
|
+
existingGuard.dispatchAdmission === dispatchAdmission
|
|
837
842
|
) {
|
|
838
843
|
return client;
|
|
839
844
|
}
|
|
@@ -850,6 +855,33 @@ export function createRecoveryGuardedMcpClient({
|
|
|
850
855
|
// Snapshot before the first await. Admission, durable identity and the raw
|
|
851
856
|
// transport all receive this exact deeply-frozen JSON value.
|
|
852
857
|
const inputSnapshot = snapshotMcpJsonRpcInput(input);
|
|
858
|
+
let callOptions = rest;
|
|
859
|
+
if (dispatchAdmission) {
|
|
860
|
+
const supplied = rest[0];
|
|
861
|
+
if (
|
|
862
|
+
supplied != null &&
|
|
863
|
+
(typeof supplied !== "object" ||
|
|
864
|
+
Array.isArray(supplied) ||
|
|
865
|
+
isProxy(supplied))
|
|
866
|
+
) {
|
|
867
|
+
throw new TypeError("MCP call options must be a plain object");
|
|
868
|
+
}
|
|
869
|
+
const merged = {};
|
|
870
|
+
if (supplied) {
|
|
871
|
+
for (const key of Reflect.ownKeys(supplied)) {
|
|
872
|
+
if (typeof key !== "string") {
|
|
873
|
+
throw new TypeError("MCP call options cannot contain symbols");
|
|
874
|
+
}
|
|
875
|
+
const descriptor = Object.getOwnPropertyDescriptor(supplied, key);
|
|
876
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
877
|
+
throw new TypeError("MCP call options cannot contain accessors");
|
|
878
|
+
}
|
|
879
|
+
merged[key] = descriptor.value;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
merged.dispatchAdmission = dispatchAdmission;
|
|
883
|
+
callOptions = [merged, ...rest.slice(1)];
|
|
884
|
+
}
|
|
853
885
|
const effect = await resolveHostEffectContract(
|
|
854
886
|
resolveEffect,
|
|
855
887
|
client,
|
|
@@ -871,10 +903,13 @@ export function createRecoveryGuardedMcpClient({
|
|
|
871
903
|
serverName,
|
|
872
904
|
toolName,
|
|
873
905
|
inputSnapshot,
|
|
874
|
-
...
|
|
906
|
+
...callOptions,
|
|
875
907
|
]);
|
|
876
908
|
} catch (callError) {
|
|
877
|
-
if (
|
|
909
|
+
if (
|
|
910
|
+
effect.effectContract.effect !== McpEffect.READ &&
|
|
911
|
+
safeProperty(callError, "dispatched") !== false
|
|
912
|
+
) {
|
|
878
913
|
throw outcomeUnknown(
|
|
879
914
|
controller,
|
|
880
915
|
ticket,
|
|
@@ -1060,6 +1095,7 @@ export function createRecoveryGuardedMcpClient({
|
|
|
1060
1095
|
controller,
|
|
1061
1096
|
resolveEffect,
|
|
1062
1097
|
sessionId,
|
|
1098
|
+
dispatchAdmission,
|
|
1063
1099
|
});
|
|
1064
1100
|
return wrappedClient;
|
|
1065
1101
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { randomUUID as nodeRandomUUID } from "node:crypto";
|
|
2
1
|
import { appendAuthorityEventIfHead as storeAppendAuthorityEventIfHead } from "../harness/jsonl-session-store.js";
|
|
3
2
|
import {
|
|
4
3
|
MCP_CALL_RECOVERY_ADJUDICATION_EVENT,
|
|
5
4
|
MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION,
|
|
6
5
|
MCP_CALL_RECOVERY_AUTHORITY,
|
|
7
6
|
MCP_CALL_RECOVERY_CONFIRMATION,
|
|
7
|
+
MCP_CALL_RECOVERY_LEGACY_CONFIRMATION,
|
|
8
8
|
McpCallRecoveryDecision,
|
|
9
9
|
deriveMcpExactReplayDenies,
|
|
10
10
|
loadMcpLedgerRecovery,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
sha256PayloadDigest,
|
|
16
16
|
snapshotMcpJsonRpcInput,
|
|
17
17
|
} from "./mcp-call-ledger.js";
|
|
18
|
+
import { revokeSessionHostAuthority as storeRevokeSessionHostAuthority } from "./session-host-lease.js";
|
|
18
19
|
|
|
19
20
|
const PAYLOAD_DIGEST = /^sha256:[0-9a-f]{64}$/;
|
|
20
21
|
const RAW_AUTHORITY_HASH = /^[0-9a-f]{64}$/;
|
|
@@ -32,6 +33,30 @@ const PUBLIC_REPLAY_DENY_FIELDS = new Set([
|
|
|
32
33
|
"inputBytes",
|
|
33
34
|
"replayDigest",
|
|
34
35
|
]);
|
|
36
|
+
const PUBLIC_ADJUDICATION_FIELDS_V1 = new Set([
|
|
37
|
+
"schemaVersion",
|
|
38
|
+
"requestId",
|
|
39
|
+
"sessionId",
|
|
40
|
+
"ledgerId",
|
|
41
|
+
"decision",
|
|
42
|
+
"expectedHeadHash",
|
|
43
|
+
"expectedRecoveryDigest",
|
|
44
|
+
"authority",
|
|
45
|
+
"confirmation",
|
|
46
|
+
"reasonDigest",
|
|
47
|
+
]);
|
|
48
|
+
const PUBLIC_ADJUDICATION_FIELDS_V2 = new Set([
|
|
49
|
+
...PUBLIC_ADJUDICATION_FIELDS_V1,
|
|
50
|
+
"hostRevocation",
|
|
51
|
+
]);
|
|
52
|
+
const PUBLIC_HOST_REVOCATION_FIELDS = new Set([
|
|
53
|
+
"requestId",
|
|
54
|
+
"revocationEpoch",
|
|
55
|
+
"targetLeaseId",
|
|
56
|
+
"targetFencingToken",
|
|
57
|
+
"targetOwnerPid",
|
|
58
|
+
]);
|
|
59
|
+
const HOST_LEASE_ID = /^lease-[0-9a-f-]{36}$/;
|
|
35
60
|
|
|
36
61
|
export class McpRecoveryAdjudicationError extends Error {
|
|
37
62
|
constructor(code, message, options = {}) {
|
|
@@ -40,6 +65,9 @@ export class McpRecoveryAdjudicationError extends Error {
|
|
|
40
65
|
this.code = code;
|
|
41
66
|
this.sessionId = options.sessionId || null;
|
|
42
67
|
this.ledgerId = options.ledgerId || null;
|
|
68
|
+
this.hostRevocation = options.hostRevocation || null;
|
|
69
|
+
this.commitState = options.commitState || null;
|
|
70
|
+
this.outcomeUnknown = options.outcomeUnknown === true;
|
|
43
71
|
}
|
|
44
72
|
}
|
|
45
73
|
|
|
@@ -226,25 +254,70 @@ export function publicMcpRecoveryAuthority(sessionId, recovery) {
|
|
|
226
254
|
});
|
|
227
255
|
});
|
|
228
256
|
const adjudications = snapshot.adjudications.map((entry) => {
|
|
257
|
+
const legacy = entry?.schemaVersion === 1;
|
|
258
|
+
const fields = Object.keys(entry || {});
|
|
259
|
+
const allowedFields = legacy
|
|
260
|
+
? PUBLIC_ADJUDICATION_FIELDS_V1
|
|
261
|
+
: PUBLIC_ADJUDICATION_FIELDS_V2;
|
|
229
262
|
const requestId = canonicalIdentifier(entry?.requestId);
|
|
230
263
|
const ledgerId = canonicalLedgerId(entry?.ledgerId);
|
|
264
|
+
const confirmationIsValid =
|
|
265
|
+
(entry?.schemaVersion === 1 &&
|
|
266
|
+
entry?.confirmation === MCP_CALL_RECOVERY_LEGACY_CONFIRMATION) ||
|
|
267
|
+
(entry?.schemaVersion === MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION &&
|
|
268
|
+
entry?.confirmation === MCP_CALL_RECOVERY_CONFIRMATION);
|
|
269
|
+
const revocation = entry?.hostRevocation;
|
|
270
|
+
const revocationFields = Object.keys(revocation || {});
|
|
271
|
+
const targetIsNull = revocation?.targetLeaseId === null;
|
|
272
|
+
const revocationIsValid =
|
|
273
|
+
legacy ||
|
|
274
|
+
(revocationFields.length === PUBLIC_HOST_REVOCATION_FIELDS.size &&
|
|
275
|
+
revocationFields.every((field) =>
|
|
276
|
+
PUBLIC_HOST_REVOCATION_FIELDS.has(field),
|
|
277
|
+
) &&
|
|
278
|
+
revocation?.requestId === requestId &&
|
|
279
|
+
Number.isSafeInteger(revocation?.revocationEpoch) &&
|
|
280
|
+
revocation.revocationEpoch >= 1 &&
|
|
281
|
+
(targetIsNull
|
|
282
|
+
? revocation.targetFencingToken === null &&
|
|
283
|
+
revocation.targetOwnerPid === null
|
|
284
|
+
: HOST_LEASE_ID.test(String(revocation?.targetLeaseId || "")) &&
|
|
285
|
+
Number.isSafeInteger(revocation?.targetFencingToken) &&
|
|
286
|
+
revocation.targetFencingToken >= 1 &&
|
|
287
|
+
Number.isSafeInteger(revocation?.targetOwnerPid) &&
|
|
288
|
+
revocation.targetOwnerPid >= 1));
|
|
231
289
|
if (
|
|
290
|
+
fields.length !== allowedFields.size ||
|
|
291
|
+
fields.some((field) => !allowedFields.has(field)) ||
|
|
232
292
|
!requestId ||
|
|
233
293
|
!ledgerId ||
|
|
234
294
|
!Object.values(McpCallRecoveryDecision).includes(entry?.decision) ||
|
|
235
295
|
entry?.authority !== MCP_CALL_RECOVERY_AUTHORITY ||
|
|
236
|
-
|
|
296
|
+
!confirmationIsValid ||
|
|
297
|
+
!revocationIsValid ||
|
|
237
298
|
!PAYLOAD_DIGEST.test(String(entry?.reasonDigest || ""))
|
|
238
299
|
) {
|
|
239
300
|
return invalidProjection();
|
|
240
301
|
}
|
|
241
302
|
return Object.freeze({
|
|
303
|
+
schemaVersion: entry.schemaVersion,
|
|
242
304
|
requestId,
|
|
243
305
|
ledgerId,
|
|
244
306
|
decision: entry.decision,
|
|
245
307
|
authority: entry.authority,
|
|
246
308
|
confirmation: entry.confirmation,
|
|
247
309
|
reasonDigest: entry.reasonDigest,
|
|
310
|
+
...(legacy
|
|
311
|
+
? {}
|
|
312
|
+
: {
|
|
313
|
+
hostRevocation: Object.freeze({
|
|
314
|
+
requestId: revocation.requestId,
|
|
315
|
+
revocationEpoch: revocation.revocationEpoch,
|
|
316
|
+
targetLeaseId: revocation.targetLeaseId,
|
|
317
|
+
targetFencingToken: revocation.targetFencingToken,
|
|
318
|
+
targetOwnerPid: revocation.targetOwnerPid,
|
|
319
|
+
}),
|
|
320
|
+
}),
|
|
248
321
|
});
|
|
249
322
|
});
|
|
250
323
|
return Object.freeze({
|
|
@@ -286,14 +359,106 @@ export function buildMcpRecoveryAdjudicationChallenge({
|
|
|
286
359
|
);
|
|
287
360
|
}
|
|
288
361
|
return (
|
|
289
|
-
`HOST STOPPED; ADJUDICATE ${canonicalSessionId} ${canonicalId} ` +
|
|
362
|
+
`HOST STOPPED AND MCP DISPATCH DRAINED; REVOKE HOST AUTHORITY; ADJUDICATE ${canonicalSessionId} ${canonicalId} ` +
|
|
290
363
|
`${canonicalDecision} ${canonicalDigest}`
|
|
291
364
|
);
|
|
292
365
|
}
|
|
293
366
|
|
|
367
|
+
function deterministicAdjudicationRequestId({
|
|
368
|
+
sessionId,
|
|
369
|
+
ledgerId,
|
|
370
|
+
decision,
|
|
371
|
+
expectedHeadHash,
|
|
372
|
+
expectedRecoveryDigest,
|
|
373
|
+
reasonDigest,
|
|
374
|
+
}) {
|
|
375
|
+
const digest = sha256PayloadDigest({
|
|
376
|
+
schemaVersion: MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION,
|
|
377
|
+
sessionId,
|
|
378
|
+
ledgerId,
|
|
379
|
+
decision,
|
|
380
|
+
expectedHeadHash,
|
|
381
|
+
expectedRecoveryDigest,
|
|
382
|
+
reasonDigest,
|
|
383
|
+
});
|
|
384
|
+
return `mcp-recovery-${digest.slice("sha256:".length)}`;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function canonicalHostRevocation(value, requestId, context) {
|
|
388
|
+
let snapshot;
|
|
389
|
+
try {
|
|
390
|
+
snapshot = snapshotMcpJsonRpcInput(value);
|
|
391
|
+
} catch (cause) {
|
|
392
|
+
throw new McpRecoveryAdjudicationError(
|
|
393
|
+
"CC_MCP_RECOVERY_HOST_REVOCATION_FAILED",
|
|
394
|
+
"MCP recovery host revocation returned malformed authority",
|
|
395
|
+
{ ...context, cause },
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
const targetIsNull = snapshot?.targetLeaseId === null;
|
|
399
|
+
if (
|
|
400
|
+
!snapshot ||
|
|
401
|
+
snapshot.requestId !== requestId ||
|
|
402
|
+
snapshot.reasonCode !== "mcp-recovery-adjudication" ||
|
|
403
|
+
!Number.isSafeInteger(snapshot.revocationEpoch) ||
|
|
404
|
+
snapshot.revocationEpoch < 1 ||
|
|
405
|
+
typeof snapshot.replayed !== "boolean" ||
|
|
406
|
+
(targetIsNull
|
|
407
|
+
? snapshot.targetFencingToken !== null || snapshot.targetOwnerPid !== null
|
|
408
|
+
: !HOST_LEASE_ID.test(String(snapshot.targetLeaseId || "")) ||
|
|
409
|
+
!Number.isSafeInteger(snapshot.targetFencingToken) ||
|
|
410
|
+
snapshot.targetFencingToken < 1 ||
|
|
411
|
+
!Number.isSafeInteger(snapshot.targetOwnerPid) ||
|
|
412
|
+
snapshot.targetOwnerPid < 1)
|
|
413
|
+
) {
|
|
414
|
+
throw new McpRecoveryAdjudicationError(
|
|
415
|
+
"CC_MCP_RECOVERY_HOST_REVOCATION_FAILED",
|
|
416
|
+
"MCP recovery host revocation returned malformed authority",
|
|
417
|
+
context,
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
return Object.freeze({
|
|
421
|
+
requestId: snapshot.requestId,
|
|
422
|
+
revocationEpoch: snapshot.revocationEpoch,
|
|
423
|
+
replayed: snapshot.replayed,
|
|
424
|
+
targetLeaseId: snapshot.targetLeaseId,
|
|
425
|
+
targetFencingToken: snapshot.targetFencingToken,
|
|
426
|
+
targetOwnerPid: snapshot.targetOwnerPid,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function persistedHostRevocation(hostRevocation) {
|
|
431
|
+
return Object.freeze({
|
|
432
|
+
requestId: hostRevocation.requestId,
|
|
433
|
+
revocationEpoch: hostRevocation.revocationEpoch,
|
|
434
|
+
targetLeaseId: hostRevocation.targetLeaseId,
|
|
435
|
+
targetFencingToken: hostRevocation.targetFencingToken,
|
|
436
|
+
targetOwnerPid: hostRevocation.targetOwnerPid,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function exactAdjudicationReadback(sessionId, data, dependencies) {
|
|
441
|
+
try {
|
|
442
|
+
const recovery = readMcpRecoveryAuthority(sessionId, dependencies);
|
|
443
|
+
const expected = sha256PayloadDigest(data);
|
|
444
|
+
const match = recovery.adjudications.find(
|
|
445
|
+
(entry) =>
|
|
446
|
+
entry?.requestId === data.requestId &&
|
|
447
|
+
sha256PayloadDigest(entry) === expected,
|
|
448
|
+
);
|
|
449
|
+
if (!match || !RAW_AUTHORITY_HASH.test(String(recovery.headHash || ""))) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
return Object.freeze({ hash: recovery.headHash, recovery });
|
|
453
|
+
} catch {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
294
458
|
/**
|
|
295
|
-
*
|
|
296
|
-
* CAS. A stale comparison is
|
|
459
|
+
* Durably revoke current host authority, then append exactly one human
|
|
460
|
+
* adjudication event under a verified-head CAS. A stale comparison is never
|
|
461
|
+
* retried; the capability-reducing revocation remains durable on append loss.
|
|
297
462
|
*/
|
|
298
463
|
export async function adjudicateMcpRecovery(request, dependencies = {}) {
|
|
299
464
|
let safeRequest;
|
|
@@ -364,9 +529,16 @@ export async function adjudicateMcpRecovery(request, dependencies = {}) {
|
|
|
364
529
|
}
|
|
365
530
|
|
|
366
531
|
const reasonDigest = sha256PayloadDigest(reason);
|
|
367
|
-
const randomUUID = dependencies.randomUUID || nodeRandomUUID;
|
|
368
532
|
const requestId = canonicalIdentifier(
|
|
369
|
-
safeRequest.requestId ||
|
|
533
|
+
safeRequest.requestId ||
|
|
534
|
+
deterministicAdjudicationRequestId({
|
|
535
|
+
sessionId,
|
|
536
|
+
ledgerId,
|
|
537
|
+
decision,
|
|
538
|
+
expectedHeadHash: recovery.headHash,
|
|
539
|
+
expectedRecoveryDigest: recovery.recoveryDigest,
|
|
540
|
+
reasonDigest,
|
|
541
|
+
}),
|
|
370
542
|
);
|
|
371
543
|
if (!requestId) {
|
|
372
544
|
throw new McpRecoveryAdjudicationError(
|
|
@@ -375,6 +547,39 @@ export async function adjudicateMcpRecovery(request, dependencies = {}) {
|
|
|
375
547
|
context,
|
|
376
548
|
);
|
|
377
549
|
}
|
|
550
|
+
const revokeSessionHostAuthority =
|
|
551
|
+
dependencies.revokeSessionHostAuthority || storeRevokeSessionHostAuthority;
|
|
552
|
+
let hostRevocation;
|
|
553
|
+
try {
|
|
554
|
+
hostRevocation = canonicalHostRevocation(
|
|
555
|
+
await revokeSessionHostAuthority(sessionId, {
|
|
556
|
+
requestId,
|
|
557
|
+
reasonCode: "mcp-recovery-adjudication",
|
|
558
|
+
}),
|
|
559
|
+
requestId,
|
|
560
|
+
context,
|
|
561
|
+
);
|
|
562
|
+
} catch (cause) {
|
|
563
|
+
if (cause instanceof McpRecoveryAdjudicationError) throw cause;
|
|
564
|
+
const commitState = cause?.commitState || "unknown";
|
|
565
|
+
if (commitState !== "not-committed") {
|
|
566
|
+
throw new McpRecoveryAdjudicationError(
|
|
567
|
+
"CC_MCP_RECOVERY_HOST_REVOCATION_OUTCOME_UNKNOWN",
|
|
568
|
+
"MCP recovery host revocation outcome is unknown; do not retry or adjudicate until exact authority readback succeeds",
|
|
569
|
+
{
|
|
570
|
+
...context,
|
|
571
|
+
cause,
|
|
572
|
+
commitState,
|
|
573
|
+
outcomeUnknown: true,
|
|
574
|
+
},
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
throw new McpRecoveryAdjudicationError(
|
|
578
|
+
"CC_MCP_RECOVERY_HOST_REVOCATION_FAILED",
|
|
579
|
+
"MCP recovery host authority could not be durably revoked; no adjudication was appended",
|
|
580
|
+
{ ...context, cause, commitState },
|
|
581
|
+
);
|
|
582
|
+
}
|
|
378
583
|
const data = Object.freeze({
|
|
379
584
|
schemaVersion: MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION,
|
|
380
585
|
requestId,
|
|
@@ -386,6 +591,7 @@ export async function adjudicateMcpRecovery(request, dependencies = {}) {
|
|
|
386
591
|
authority: MCP_CALL_RECOVERY_AUTHORITY,
|
|
387
592
|
confirmation: MCP_CALL_RECOVERY_CONFIRMATION,
|
|
388
593
|
reasonDigest,
|
|
594
|
+
hostRevocation: persistedHostRevocation(hostRevocation),
|
|
389
595
|
});
|
|
390
596
|
const appendAuthorityEventIfHead =
|
|
391
597
|
dependencies.appendAuthorityEventIfHead || storeAppendAuthorityEventIfHead;
|
|
@@ -403,22 +609,52 @@ export async function adjudicateMcpRecovery(request, dependencies = {}) {
|
|
|
403
609
|
if (cause?.code === "SESSION_REVISION_STALE") {
|
|
404
610
|
throw new McpRecoveryAdjudicationError(
|
|
405
611
|
"CC_MCP_RECOVERY_ADJUDICATION_STALE",
|
|
406
|
-
"MCP recovery authority changed; run show again before adjudicating",
|
|
407
|
-
{ ...context, cause },
|
|
612
|
+
"MCP recovery authority changed after host revocation; run show again before adjudicating",
|
|
613
|
+
{ ...context, cause, hostRevocation },
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
const commitState = cause?.commitState || "unknown";
|
|
617
|
+
if (commitState !== "not-committed") {
|
|
618
|
+
const readback = exactAdjudicationReadback(sessionId, data, dependencies);
|
|
619
|
+
if (readback) {
|
|
620
|
+
appended = readback;
|
|
621
|
+
} else {
|
|
622
|
+
throw new McpRecoveryAdjudicationError(
|
|
623
|
+
"CC_MCP_RECOVERY_ADJUDICATION_OUTCOME_UNKNOWN",
|
|
624
|
+
"MCP recovery adjudication persistence outcome is unknown; host authority remains revoked and exact transcript reconciliation is required before retry",
|
|
625
|
+
{
|
|
626
|
+
...context,
|
|
627
|
+
cause,
|
|
628
|
+
hostRevocation,
|
|
629
|
+
commitState,
|
|
630
|
+
outcomeUnknown: true,
|
|
631
|
+
},
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
} else {
|
|
635
|
+
throw new McpRecoveryAdjudicationError(
|
|
636
|
+
"CC_MCP_RECOVERY_ADJUDICATION_PERSIST_FAILED",
|
|
637
|
+
"MCP recovery adjudication was not persisted; host authority remains revoked",
|
|
638
|
+
{ ...context, cause, hostRevocation, commitState },
|
|
408
639
|
);
|
|
409
640
|
}
|
|
410
|
-
throw new McpRecoveryAdjudicationError(
|
|
411
|
-
"CC_MCP_RECOVERY_ADJUDICATION_PERSIST_FAILED",
|
|
412
|
-
"MCP recovery adjudication was not persisted",
|
|
413
|
-
{ ...context, cause },
|
|
414
|
-
);
|
|
415
641
|
}
|
|
416
642
|
if (!appended || !RAW_AUTHORITY_HASH.test(String(appended.hash || ""))) {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
643
|
+
const readback = exactAdjudicationReadback(sessionId, data, dependencies);
|
|
644
|
+
if (readback) {
|
|
645
|
+
appended = readback;
|
|
646
|
+
} else {
|
|
647
|
+
throw new McpRecoveryAdjudicationError(
|
|
648
|
+
"CC_MCP_RECOVERY_ADJUDICATION_OUTCOME_UNKNOWN",
|
|
649
|
+
"MCP recovery adjudication returned no durable authority hash; host authority remains revoked and exact transcript reconciliation is required",
|
|
650
|
+
{
|
|
651
|
+
...context,
|
|
652
|
+
hostRevocation,
|
|
653
|
+
commitState: appended?.commitState || "unknown",
|
|
654
|
+
outcomeUnknown: true,
|
|
655
|
+
},
|
|
656
|
+
);
|
|
657
|
+
}
|
|
422
658
|
}
|
|
423
659
|
|
|
424
660
|
return Object.freeze({
|
|
@@ -437,6 +673,7 @@ export async function adjudicateMcpRecovery(request, dependencies = {}) {
|
|
|
437
673
|
: [],
|
|
438
674
|
),
|
|
439
675
|
replayDenied: decision === McpCallRecoveryDecision.CONFIRMED_APPLIED,
|
|
676
|
+
hostRevocation,
|
|
440
677
|
runtimeReloadRequired: true,
|
|
441
678
|
remediation: "restart_or_resume_before_mcp_calls",
|
|
442
679
|
});
|
|
@@ -153,3 +153,32 @@ export function normalizeMcpSandboxPolicy(value, options = {}) {
|
|
|
153
153
|
|
|
154
154
|
return Object.freeze({ requiredBoundaries });
|
|
155
155
|
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Add the host-owned isolation floor for an exact-package stdio capsule.
|
|
159
|
+
*
|
|
160
|
+
* Source configuration is normalized as untrusted data first and can only add
|
|
161
|
+
* requirements to this floor. The returned policy is a new deeply-frozen
|
|
162
|
+
* runtime value, so neither the source object nor later launch preparation can
|
|
163
|
+
* remove a host requirement. Callers must only attach this policy after the
|
|
164
|
+
* trusted executable-identity preparer has issued a capsule execution
|
|
165
|
+
* contract; legacy stdio sources intentionally retain their existing policy.
|
|
166
|
+
* There is deliberately no source-configured escape hatch: future workspace
|
|
167
|
+
* or egress access must be a separate host-issued, authority-bound capability.
|
|
168
|
+
*
|
|
169
|
+
* @param {unknown} value
|
|
170
|
+
* @param {{label?: string}} [options]
|
|
171
|
+
* @returns {{requiredBoundaries: readonly string[]}}
|
|
172
|
+
*/
|
|
173
|
+
export function enforceMcpStdioCapsuleHostSandboxPolicy(value, options = {}) {
|
|
174
|
+
const sourcePolicy = normalizeMcpSandboxPolicy(value, options);
|
|
175
|
+
const requiredBoundaries = Object.freeze(
|
|
176
|
+
[
|
|
177
|
+
...new Set([
|
|
178
|
+
...MCP_SANDBOX_BOUNDARIES,
|
|
179
|
+
...(sourcePolicy?.requiredBoundaries || []),
|
|
180
|
+
]),
|
|
181
|
+
].sort(),
|
|
182
|
+
);
|
|
183
|
+
return Object.freeze({ requiredBoundaries });
|
|
184
|
+
}
|