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.
Files changed (198) hide show
  1. package/package.json +3 -2
  2. package/scripts/command-lifecycle-report.mjs +330 -38
  3. package/src/assets/web-panel/assets/{AIOps-DIY6KxV-.js → AIOps-DB5X7qqV.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-BXH0muPt.js → ActionButton-DxHRtKLk.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-D39w8vmg.js → Analytics-DIH5QrxT.js} +2 -2
  6. package/src/assets/web-panel/assets/{AppLayout-CUU1zvLs.js → AppLayout-uu0oxVz7.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-D8azHO3T.js → Artifacts-BNSMiW9Y.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-D0tAwXTg.js → Audit-DcAzB4_x.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-C29mhJll.js → BackgroundAgents-ClSsvjIc.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-D6Aa1Gro.js → Backup-BMciY0js.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-B1uoHbPQ.js → BaseInput-Hn3b57Sh.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-Bs4DLOR5.js → Chat-CoSlC53V.js} +5 -5
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DUYIBSle.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-CNcBPEa4.js → Checkbox-B4L4Eu4w.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-CIY89763.js → Codegen-C77FgRxF.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CsPYFmgo.js → Col-BrLtJLH3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-BBocEge4.js → Community-Be7pXXhA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-TQ8Ux0Lf.js → Compact-DuGPecjo.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-DfBsZTft.js → Compliance-CzS66mjP.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-Cg-I6FVH.js → Cowork-CEhjCoyK.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-DTQzfRTp.js → Cron-D7ir8Vja.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-DGxEKGpR.js → Crosschain-BcXmuLxf.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-DLZ1RDSr.js → DID-Cto1dtBy.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-XneICKNI.js → Dashboard-B7KjgwOX.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BRU00J1-.js → Dropdown-COMqxijg.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BzCbJuAy.js → EmailListRenderer-BZrya-nf.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-D7BRyNBG.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-D9DeFIhW.js → Federation-kZU8c-27.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-6rAtA9Ko.js → FormItemContext-sOV22v2-.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-8Hqy80pU.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Cmvkj1rt.js → Git-K-ZxsLsH.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-BRiQUjjw.js → Governance-D3OEnawI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-CKN_EzVf.js → Inference-Bll0gRmP.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-BCDHwpwQ.js → KnowledgeGraph-DBXwjr53.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-DK9ovhaB.js → Logs-CEzPwEfP.js} +2 -2
  36. package/src/assets/web-panel/assets/MarkdownRenderer-CLmCbHx6.js +1 -0
  37. package/src/assets/web-panel/assets/{Marketplace-BILPGakp.js → Marketplace-BYyRyOPz.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DTVCOr9-.js → McpTools-DVu8w_rM.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-Hzc1iow4.js → Memory-tenuZ_hO.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-GXfgnGYV.js → MobileBridge-Bxgy4ozS.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-B-3TJWsS.js → MobileProjects-Dgx1D6sc.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-DwSJMWtK.js → Mtc-CRfJ8r8j.js} +4 -4
  43. package/src/assets/web-panel/assets/{MtcAudit-D2uxf-DA.js → MtcAudit-BBJq4Aiz.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-DG6kbKK0.js → Multisig-B3ii9lai.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-f5p49EDU.js → NLProgramming-FIi-Cubv.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-FUgHyzSD.js → Notes-DIq8MPf1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DCHGee7e.js → NotificationSettings-BFOD9eGd.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-Dm6LlIoa.js → OrderTableRenderer--41u7vre.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-BawA7qAg.js → Organization-B_0AWztW.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-D6a5wZU1.js → Overflow-B6I7TuXv.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-K8WavwnA.js → P2P-Cdf6qens.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-4JAJYaqx.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-BDWvi8PD.js → Permissions-6nFBjsQp.js} +3 -3
  54. package/src/assets/web-panel/assets/{PersonalDataHub-DF_K4E9i.js → PersonalDataHub-nQYV7xpn.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-BYeZlesI.js → Pipeline-CBVod5jm.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BIHlgnIc.js → Privacy-D52jW1Nt.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-Cvf-6B5P.js → ProjectInit-Bdx3BYhf.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-CnwfJrRz.js → ProjectSettings-b7xr4DUa.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-b-jPXzF4.js → Projects-DhfifCKx.js} +1 -1
  60. package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-VLf3gAfL.js → QrScannerModal-FQKSmc3e.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-B5vGjRiF.js → QuickAsk-CSTrhzcF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend--UW4iYs-.js → Recommend-CxMABOUh.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DMSPcfKR.js → RemoteSession-Cv2T8yRa.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-kQXlEHmj.js → Reputation-CE1k4Vvu.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-CxGu3pit.js → Row-BNgNcAV_.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-Bi5lo8MZ.js → RssFeed-5ZiBZ3ph.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-Cq_H5RTh.js → Search-mDOIF1UV.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-YFbmkaQS.js → Security-CtuCTC-e.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-DLOGdaLm.js → Services-DxFO2Zg7.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CteIxb3D.js → Skeleton-CKmKkH9y.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-CccopaIZ.js → Skills-Bmzl9qkk.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-AzfEQKX1.js → Sla-9RqaWHOC.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-ekGo5l77.js → SpeechSettings-CGdzFIYw.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-BmZmAgYH.js → SyncSettings-CvNuCeBT.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-D7q3Z4uL.js → Tasks-B_sLdcnw.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-CX1Tgeka.js → Templates-D9_YijdK.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-Zc59jF4i.js → Tenant-Cuc7BUSP.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-CXOD7y83.js → Terminal-6VYRnJgd.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-B1aBHPBo.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-zALlOq_U.js → Tokens-BZ94Lt5W.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-CQfJMXaq.js → Trigger-DEcZgDaX.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-BU0RcM-w.js → Trust-CC3z9THe.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-IFZ3HsbH.js → UkeySign-Cvtw8cV3.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-BzslgW0v.js → VideoEditing-CL4e6Bn3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-BZAcQNS6.js → Wallet-ycEemP27.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CBK1DMi0.js → WebAuthn-DgZ4X_aw.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-BlMTPzO-.js → WorkflowEditor-D0qMhSv7.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-B51-8QVn.js → chat-DsAg01Zn.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-504RTByU.js → colors-B0oqAhrQ.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-C-GP6IQM.js → compact-item-CmX0Rabz.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-DOkIj7ID.js → createContext-Be4cn1Ub.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DN7V-_FW.js → hasIn-D5v12kVq.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-kt_5rfcZ.js → index-0eWLkQKT.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CUhQpiYv.js → index-301ogenv.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-f4Bo4KXm.js → index-5LGY0JbG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index--n-ZciV3.js → index-7VKDn-t1.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D1phyca8.js → index-8WvYGnON.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-7jIqHbzw.js → index-B1YGjOWC.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DEMPUVa3.js → index-B3mUHuVc.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Bkxhaeta.js → index-BA4oulaP.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CYopNjcJ.js → index-BBREnn1V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-P2mfY27Z.js → index-BBvyHguy.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-DA2f1UV3.js → index-BKmsS2jj.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CP2A226L.js → index-BRIqLy1m.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-CfiEM64n.js → index-BXGSbelg.js} +3 -3
  108. package/src/assets/web-panel/assets/{index-B2-x9bS8.js → index-BaE0Ui07.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CySTJKeq.js → index-BoAIAZ5C.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-kAqQXfP6.js → index-BqltWojQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DAOpQvl_.js → index-CGiIyTZw.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
  113. package/src/assets/web-panel/assets/{index-C4DV_5uA.js → index-CZ2wUEgr.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-yYTQZypF.js → index-CZpfzcuy.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-et5JIPHO.js → index-Ckrk3pIV.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DaA__-q1.js → index-CqoCZ5vR.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-By6u5IV1.js → index-CyWBir7N.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CKTfZyfU.js → index-CyyGTMKd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-B_vGU-Js.js → index-D0BSkxYQ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Cao5DZy7.js → index-D5YVopyr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-C3TSohzc.js → index-DKUBj36_.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BFIFJ5Dr.js → index-DM724NgU.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-ConjaJro.js → index-DQRsQen0.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
  125. package/src/assets/web-panel/assets/{index-C9_48OzU.js → index-DikJ0Qqe.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C0k_Nqg9.js → index-Dn2dSdXx.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DE-hmI2l.js → index-DyUaFWEk.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-so8eIIxF.js → index-Hfe_dEGx.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-FcV8J_Qz.js → index-_JjRLSD0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D9LyiN-L.js → index-frSojpHj.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CX78k-qf.js → index-jK3ax6Z6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-C8UltbXK.js → index-qMuZ-NRR.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-CqcLo5WV.js → index-q_fQ94SE.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-CPYEIdpV.js → initDefaultProps-D4VdhJ9_.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-Cjim9pxs.js → motion-CZJA0MQW.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-DuU-cgz3.js → move-BqEs7Sb1.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DJHNAkta.js → mtc-parser-BpHEtsu1.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-DSQBgdBj.js → omit-BW94tY9W.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-CTmT_j5m.js → pickAttrs-BnBjPWP9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-Lfm6v-r4.js → placementArrow-uUkjQ_hu.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-CgrcPcDi.js → responsiveObserve-B-klUqXK.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-CuE3110i.js → slide-CvY1cNAs.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-DiK5nbpf.js → statusUtils-Ix5sEDT2.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-DPEJdQaZ.js → styleChecker-C8qcXOne.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-CnnodGmp.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-DWF84Yav.js → useFs-DGJr345n.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-a5b5Dw2f.js → usePersonalDataHub-BboN99rI.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-DAluzXOq.js → vnode-Bgt8Vgte.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-Dokg7uAZ.js → zoom-BhJtErgt.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +1 -1
  152. package/src/commands/artifacts.js +98 -0
  153. package/src/commands/mcp.js +1 -1
  154. package/src/commands/review.js +221 -22
  155. package/src/commands/session-mcp-recovery.js +5 -5
  156. package/src/data/changelog.json +14 -1
  157. package/src/gateways/ws/ws-agent-handler.js +492 -14
  158. package/src/harness/jsonl-session-store.js +39 -1
  159. package/src/harness/mcp-client.js +172 -29
  160. package/src/harness/prompt-compressor.js +24 -0
  161. package/src/harness/provider-backed-compaction.js +205 -0
  162. package/src/lib/app-preview.js +3 -0
  163. package/src/lib/command-lifecycle-report.js +1340 -145
  164. package/src/lib/delivery-coordinator.js +30 -5
  165. package/src/lib/delivery-production-adapter.js +1777 -0
  166. package/src/lib/delivery-production-runner.js +240 -0
  167. package/src/lib/durable-security-store.js +29 -17
  168. package/src/lib/execution-trace/trace-context.cjs +179 -0
  169. package/src/lib/execution-trace/trace-context.js +8 -175
  170. package/src/lib/mcp-call-ledger-store.js +58 -6
  171. package/src/lib/mcp-host-recovery-runtime.js +2 -0
  172. package/src/lib/mcp-ledger-recovery-admission.js +39 -3
  173. package/src/lib/mcp-recovery-adjudication.js +256 -19
  174. package/src/lib/mcp-sandbox-policy.js +29 -0
  175. package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
  176. package/src/lib/mcp-stdio-executable-identity.js +33 -2
  177. package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
  178. package/src/lib/mcp-stdio-package-materialization.js +721 -333
  179. package/src/lib/observability/index.js +2 -0
  180. package/src/lib/observability/otlp-exporter.js +2 -2
  181. package/src/lib/packer/pack-update-applier.js +8 -1
  182. package/src/lib/packer/pkg-config-generator.js +34 -0
  183. package/src/lib/process-execution-broker/index.js +34 -5
  184. package/src/lib/process-execution-broker/platform-sandbox.js +82 -1
  185. package/src/lib/scheduler-kernel/contract.js +315 -0
  186. package/src/lib/scheduler-kernel/store.js +1345 -0
  187. package/src/lib/session-host-lease.js +403 -30
  188. package/src/lib/session-persistence-failure.js +4 -1
  189. package/src/lib/sub-agent-context.js +8 -0
  190. package/src/repl/agent-repl.js +285 -38
  191. package/src/runtime/agent-core.js +1044 -90
  192. package/src/runtime/headless-runner.js +260 -166
  193. package/src/runtime/headless-stream.js +254 -25
  194. package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +0 -1
  195. package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +0 -1
  196. package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +0 -1
  197. package/src/assets/web-panel/assets/index-Bya64sZE.js +0 -1
  198. 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 = 1;
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 = "typed-digest-host-stopped";
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 ADJUDICATION_FIELDS = new Set([
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, ADJUDICATION_FIELDS) ||
419
- value.schemaVersion !== MCP_CALL_RECOVERY_ADJUDICATION_SCHEMA_VERSION ||
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
- ...rest,
906
+ ...callOptions,
875
907
  ]);
876
908
  } catch (callError) {
877
- if (effect.effectContract.effect !== McpEffect.READ) {
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
- entry?.confirmation !== MCP_CALL_RECOVERY_CONFIRMATION ||
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
- * Append exactly one human adjudication authority event under a verified-head
296
- * CAS. A stale comparison is returned to the caller and is never retried.
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 || `mcp-recovery-${randomUUID()}`,
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
- throw new McpRecoveryAdjudicationError(
418
- "CC_MCP_RECOVERY_ADJUDICATION_PERSIST_FAILED",
419
- "MCP recovery adjudication did not return a durable authority hash",
420
- context,
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
+ }