chainlesschain 0.163.1 → 0.163.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-Cn7quRgL.js → AIOps-DIY6KxV-.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-YqMX3ueH.js → ActionButton-BXH0muPt.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-Bxmy1b7Y.js → Analytics-D39w8vmg.js} +2 -2
  5. package/src/assets/web-panel/assets/{AppLayout-DBOw69jE.js → AppLayout-CUU1zvLs.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-mq1nyzN1.js → Artifacts-D8azHO3T.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-mMiKtZ0D.js → Audit-D0tAwXTg.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-WBUjpYVh.js → BackgroundAgents-C29mhJll.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-c25OoJgs.js → Backup-D6Aa1Gro.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-BbfWhZ4-.js → BaseInput-B1uoHbPQ.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-CVVzsTwU.js → Chat-Bs4DLOR5.js} +5 -5
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CVCjZJ04.js → ChatBubbleRenderer-DUYIBSle.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-C9o2HK-1.js → Checkbox-CNcBPEa4.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-B7X5LfKc.js → Codegen-CIY89763.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-jpPj2Pur.js → Col-CsPYFmgo.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-Dti6bHxN.js → Community-BBocEge4.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-S4ZsLt3W.js → Compact-TQ8Ux0Lf.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-Bge0sQ3O.js → Compliance-DfBsZTft.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-C4XyzVI7.js → Cowork-Cg-I6FVH.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-CPR0v2xy.js → Cron-DTQzfRTp.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-DAX8lDaj.js → Crosschain-DGxEKGpR.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-fBHPa0QH.js → DID-DLZ1RDSr.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-vvaUTafU.js → Dashboard-XneICKNI.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DyaQM63v.js → Dropdown-BRU00J1-.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-CAF0wiU2.js → EmailListRenderer-BzCbJuAy.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-ia-WzDRs.js → FamilyGuardDashboard-D7BRyNBG.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-Cz3gHoT5.js → Federation-D9DeFIhW.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-CJQDJr4Q.js → FormItemContext-6rAtA9Ko.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-B05BZlQp.js → GenericCardRenderer-8Hqy80pU.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-DT0YcQnI.js → Git-Cmvkj1rt.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-Bqbl6m5Q.js → Governance-BRiQUjjw.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-BdebLR0N.js → Inference-CKN_EzVf.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-M36b8YYV.js → KnowledgeGraph-BCDHwpwQ.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-5LN_XL43.js → Logs-DK9ovhaB.js} +2 -2
  35. package/src/assets/web-panel/assets/{MarkdownRenderer-BCb1Dm8l.js → MarkdownRenderer-D53pHiHy.js} +1 -1
  36. package/src/assets/web-panel/assets/{Marketplace-BkIin-QY.js → Marketplace-BILPGakp.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-Bkppx8Uj.js → McpTools-DTVCOr9-.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-Qk9wg_Ib.js → Memory-Hzc1iow4.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BsTDU3Aj.js → MobileBridge-GXfgnGYV.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-CrZW5I28.js → MobileProjects-B-3TJWsS.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BMkmgtsA.js → Mtc-DwSJMWtK.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-6m4CC0Yn.js → MtcAudit-D2uxf-DA.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-BZ8yB7QV.js → Multisig-DG6kbKK0.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CxitAMIO.js → NLProgramming-f5p49EDU.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Bv0msnHu.js → Notes-FUgHyzSD.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-DeVMqM3P.js → NotificationSettings-DCHGee7e.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-CXBSWa3R.js → OrderTableRenderer-Dm6LlIoa.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-b_G5Vs1Q.js → Organization-BawA7qAg.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DW7riOmA.js → Overflow-D6a5wZU1.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-CtpQNYjL.js → P2P-K8WavwnA.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-Ba63yT4l.js → PdhVaultBrowser-4JAJYaqx.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-DnzyeB67.js → Permissions-BDWvi8PD.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-DbPZ7HWw.js → PersonalDataHub-DF_K4E9i.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-gs_HOThJ.js → Pipeline-BYeZlesI.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-CZlQ1EjE.js → Privacy-BIHlgnIc.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-DQp__Ccb.js → ProjectInit-Cvf-6B5P.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-C7h2sOWZ.js → ProjectSettings-CnwfJrRz.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-b-jPXzF4.js +1 -0
  59. package/src/assets/web-panel/assets/Providers-CxC7JuYv.js +1 -0
  60. package/src/assets/web-panel/assets/{QrScannerModal-B_8M1-FC.js → QrScannerModal-VLf3gAfL.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-Bpycr8WD.js → QuickAsk-B5vGjRiF.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-Z4TrcNzs.js → Recommend--UW4iYs-.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-BD6TsJlS.js → RemoteSession-DMSPcfKR.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-IKxDXHEu.js → Reputation-kQXlEHmj.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DbmR20tJ.js → Row-CxGu3pit.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-CLjA5PPf.js → RssFeed-Bi5lo8MZ.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-Bm3owbIU.js → Search-Cq_H5RTh.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-CY4RyOuq.js → Security-YFbmkaQS.js} +2 -2
  69. package/src/assets/web-panel/assets/{Services-CYO9qrd_.js → Services-DLOGdaLm.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-ClF51Go6.js → Skeleton-CteIxb3D.js} +1 -1
  71. package/src/assets/web-panel/assets/Skills-CccopaIZ.js +1 -0
  72. package/src/assets/web-panel/assets/{Sla-zqpDUxzP.js → Sla-AzfEQKX1.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-DPSi8Qco.js → SpeechSettings-ekGo5l77.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CqABopsQ.js → SyncSettings-BmZmAgYH.js} +2 -2
  75. package/src/assets/web-panel/assets/Tasks-D7q3Z4uL.js +1 -0
  76. package/src/assets/web-panel/assets/{Templates-Dzl6f0M2.js → Templates-CX1Tgeka.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-XeZkw40a.js → Tenant-Zc59jF4i.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-CYw2YzPm.js → Terminal-CXOD7y83.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-Bc2pko4i.js → TimelineRenderer-B1aBHPBo.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CfCenOHx.js → Tokens-zALlOq_U.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DTz-_YqV.js → Trigger-CQfJMXaq.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-CB3tt3Z6.js → Trust-BU0RcM-w.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-y1ykmMvU.js → UkeySign-IFZ3HsbH.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-UlZ_lX0e.js → VideoEditing-BzslgW0v.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-DMBaHTha.js → Wallet-BZAcQNS6.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-Cksm-hBI.js → WebAuthn-CBK1DMi0.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-BhOa40sX.js → WorkflowEditor-BlMTPzO-.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DNbeSTDO.js → chat-B51-8QVn.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-9zz8gBmW.js → colors-504RTByU.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-D7aoVv-5.js → compact-item-C-GP6IQM.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-DYg7B8nd.js → createContext-DOkIj7ID.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-BNbhkcoI.js → hasIn-DN7V-_FW.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DeD1wWA8.js → index--n-ZciV3.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-Da0SysnX.js → index-7jIqHbzw.js} +1 -1
  96. package/src/assets/web-panel/assets/{index--TvoQ6UC.js → index-B2-x9bS8.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-m2pXS3W2.js → index-BFIFJ5Dr.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CzDDeZBo.js → index-B_vGU-Js.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-gNKj00Fx.js → index-Bkxhaeta.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-C8LC0nvw.js → index-By6u5IV1.js} +1 -1
  101. package/src/assets/web-panel/assets/index-Bya64sZE.js +1 -0
  102. package/src/assets/web-panel/assets/{index-B8irlIUK.js → index-C0k_Nqg9.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-B9zUawcE.js → index-C3TSohzc.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BPojutcZ.js → index-C4DV_5uA.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-ZPDbEh-L.js → index-C8UltbXK.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-jS6NASar.js → index-C9_48OzU.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-67GdrUx2.js → index-CKTfZyfU.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CYu-F-gp.js → index-CP2A226L.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Cq3l0L7C.js → index-CUhQpiYv.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DtQzs0hH.js → index-CX78k-qf.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Bdxe0nrD.js → index-CYopNjcJ.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-uc8Rz96X.js → index-Cao5DZy7.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-DnTM8FWK.js → index-CfiEM64n.js} +3 -3
  114. package/src/assets/web-panel/assets/{index-DHskwmwI.js → index-ConjaJro.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CA72qB1n.js → index-CqcLo5WV.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Dn-JFqEi.js → index-CySTJKeq.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-DUWmZ059.js → index-D1phyca8.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-_-Vt1Wqk.js → index-D9LyiN-L.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DIljkSt9.js → index-DA2f1UV3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B9TzVW7i.js → index-DAOpQvl_.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CRKukX1W.js → index-DE-hmI2l.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DIMVe3HJ.js → index-DEMPUVa3.js} +1 -1
  123. package/src/assets/web-panel/assets/index-D_FWpDZM.js +1 -0
  124. package/src/assets/web-panel/assets/{index-FlBuITVG.js → index-DaA__-q1.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-Cqy-hqD9.js → index-FcV8J_Qz.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CpEIwp4A.js → index-P2mfY27Z.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-nnmo3ipi.js → index-et5JIPHO.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-SmEIFaRm.js → index-f4Bo4KXm.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DMrqEsOS.js → index-kAqQXfP6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CV06YdPO.js → index-kt_5rfcZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CN6-OEgS.js → index-so8eIIxF.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-BRd1zkLB.js → index-yYTQZypF.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-D0mTMUGz.js → initDefaultProps-CPYEIdpV.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-DK16p4-o.js → motion-Cjim9pxs.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-DalC20HC.js → move-DuU-cgz3.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-B5RusszF.js → mtc-parser-DJHNAkta.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-C9Mhitht.js → omit-DSQBgdBj.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-51xQLgwM.js → pickAttrs-CTmT_j5m.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-CJzIqp8u.js → placementArrow-Lfm6v-r4.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-HQvC19u4.js → responsiveObserve-CgrcPcDi.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-C6hhabOp.js → slide-CuE3110i.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-Q1r48-oc.js → statusUtils-DiK5nbpf.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-UakVWrHs.js → styleChecker-DPEJdQaZ.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Dmv94cxx.js → useFlexGapSupport-CnnodGmp.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-DrxOnt_V.js → useFs-DWF84Yav.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BVf2M-Hg.js → usePersonalDataHub-a5b5Dw2f.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-BzzW9qKH.js → vnode-DAluzXOq.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-C1ZvMgAZ.js → zoom-Dokg7uAZ.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +1 -1
  151. package/src/commands/agenda.js +6 -5
  152. package/src/commands/agent.js +237 -75
  153. package/src/commands/agents.js +6 -0
  154. package/src/commands/background-session.js +11 -1
  155. package/src/commands/mcp.js +245 -30
  156. package/src/data/changelog.json +28 -2
  157. package/src/harness/mcp-client.js +256 -43
  158. package/src/harness/worktree-isolator.js +26 -3
  159. package/src/lib/agent-worktree.js +41 -2
  160. package/src/lib/background-agent-supervisor.js +790 -186
  161. package/src/lib/background-command-argv.js +318 -0
  162. package/src/lib/background-launch-profile.js +1 -0
  163. package/src/lib/background-phase-reporter.js +22 -2
  164. package/src/lib/background-worktree-policy.js +109 -0
  165. package/src/lib/cowork-mcp-tools.js +52 -6
  166. package/src/lib/cowork-task-runner.js +1 -0
  167. package/src/lib/cowork-task-templates.js +1 -0
  168. package/src/lib/mcp-headers-helper.js +76 -8
  169. package/src/lib/mcp-sandbox-policy.js +155 -0
  170. package/src/lib/mcp-stdio-executable-identity.js +75 -11
  171. package/src/lib/mcp-stdio-execution-authority.js +168 -0
  172. package/src/lib/mcp-stdio-workspace-authority.js +252 -0
  173. package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
  174. package/src/lib/process-execution-broker/index.js +495 -126
  175. package/src/lib/process-execution-broker/platform-sandbox.js +1424 -189
  176. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  177. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  178. package/src/lib/process-execution-broker/windows-sandbox.cs +5 -4
  179. package/src/lib/process-tree-termination.js +17 -1
  180. package/src/lib/skill-mcp.js +74 -3
  181. package/src/lib/web-fetch.js +4 -4
  182. package/src/runtime/agent-runtime.js +61 -8
  183. package/src/runtime/mcp-config.js +238 -28
  184. package/src/workers/background-agent-worker.js +492 -71
  185. package/src/assets/web-panel/assets/Projects-DdCpNRNt.js +0 -1
  186. package/src/assets/web-panel/assets/Providers-CyvtUkUf.js +0 -1
  187. package/src/assets/web-panel/assets/Skills-CCCEmSf5.js +0 -1
  188. package/src/assets/web-panel/assets/Tasks-4dbAJng4.js +0 -1
  189. package/src/assets/web-panel/assets/devWarning-uadaGF_y.js +0 -1
  190. package/src/assets/web-panel/assets/index-C7zhie9M.js +0 -1
  191. package/src/assets/web-panel/assets/index-Cmbg0Wbr.js +0 -1
@@ -36,7 +36,9 @@ const require = createRequire(import.meta.url);
36
36
  // platform-sandbox.js exports: applySandbox, postSpawnSandbox
37
37
  import {
38
38
  applySandbox as _applySandbox,
39
+ consumeWindowsMcpCodeSnapshotPlanBinding,
39
40
  postSpawnSandbox as _postSpawnSandbox,
41
+ MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256,
40
42
  SANDBOX_BOUNDARIES,
41
43
  } from "./platform-sandbox.js";
42
44
  import {
@@ -60,6 +62,9 @@ const SUPPORTED_SANDBOX_PROFILES = new Set([
60
62
  "strict",
61
63
  "network-only",
62
64
  ]);
65
+ // Broker-private admission state. Public runtime-probe fields are audit
66
+ // evidence, not authority to invoke a privileged post-spawn closure.
67
+ const admittedWindowsMcpCodeSnapshotPlans = new WeakSet();
63
68
 
64
69
  // 延迟导入避免循环依赖
65
70
  let _traceCtx = null;
@@ -1065,7 +1070,7 @@ class ProcessExecutionBroker extends EventEmitter {
1065
1070
  return error;
1066
1071
  }
1067
1072
 
1068
- _validateSandboxPlan(plan) {
1073
+ _validateSandboxPlan(plan, launchContext = {}) {
1069
1074
  if (!plan || typeof plan !== "object") {
1070
1075
  throw this._sandboxError(
1071
1076
  "invalid_sandbox_plan",
@@ -1159,6 +1164,7 @@ class ProcessExecutionBroker extends EventEmitter {
1159
1164
  let sanitizedNetworkPolicy = null;
1160
1165
  let sanitizedProcessTreePolicy = null;
1161
1166
  let sanitizedPtyPolicy = null;
1167
+ let windowsPlanBindingConsumed = false;
1162
1168
  if (plan.runtimeProbe !== null && plan.runtimeProbe !== undefined) {
1163
1169
  if (
1164
1170
  typeof plan.runtimeProbe !== "object" ||
@@ -1180,6 +1186,9 @@ class ProcessExecutionBroker extends EventEmitter {
1180
1186
  "contentSnapshotScope",
1181
1187
  "contentSnapshotMechanism",
1182
1188
  "runtimeLaunchMechanism",
1189
+ "planBindingMechanism",
1190
+ "runtimeLaunchPath",
1191
+ "entrySnapshotPath",
1183
1192
  "pluginTreeContentSnapshotScope",
1184
1193
  "pluginTreeContentSnapshotMechanism",
1185
1194
  "pluginTreeSnapshotConsistency",
@@ -1208,6 +1217,7 @@ class ProcessExecutionBroker extends EventEmitter {
1208
1217
  "sharedLibraryClosure",
1209
1218
  "entrySnapshotAtomic",
1210
1219
  "runtimeLaunchAtomic",
1220
+ "mcpCapsuleCodeSnapshot",
1211
1221
  ]) {
1212
1222
  if (
1213
1223
  plan.runtimeProbe[field] !== undefined &&
@@ -1243,6 +1253,7 @@ class ProcessExecutionBroker extends EventEmitter {
1243
1253
  "runtimeSnapshotSha256",
1244
1254
  "runtimeAttestedSha256",
1245
1255
  "entrySnapshotSha256",
1256
+ "planBindingDigest",
1246
1257
  ]) {
1247
1258
  if (
1248
1259
  plan.runtimeProbe[field] !== undefined &&
@@ -1255,6 +1266,22 @@ class ProcessExecutionBroker extends EventEmitter {
1255
1266
  );
1256
1267
  }
1257
1268
  }
1269
+ const planBindingDeclared =
1270
+ plan.runtimeProbe.planBindingMechanism !== undefined ||
1271
+ plan.runtimeProbe.planBindingDigest !== undefined;
1272
+ if (
1273
+ planBindingDeclared &&
1274
+ (plan.platform !== "win32" ||
1275
+ !guarantees.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT) ||
1276
+ plan.runtimeProbe.planBindingMechanism !==
1277
+ "windows-mcp-code-snapshot-plan-binding-v1" ||
1278
+ !/^[a-f0-9]{64}$/.test(plan.runtimeProbe.planBindingDigest || ""))
1279
+ ) {
1280
+ throw this._sandboxError(
1281
+ "invalid_sandbox_plan",
1282
+ "Sandbox runtime probe plan binding must use the typed Windows MCP code-snapshot contract",
1283
+ );
1284
+ }
1258
1285
  if (
1259
1286
  plan.runtimeProbe.pluginTreeContentSnapshotDigest !== undefined &&
1260
1287
  (typeof plan.runtimeProbe.pluginTreeContentSnapshotDigest !==
@@ -1293,6 +1320,20 @@ class ProcessExecutionBroker extends EventEmitter {
1293
1320
  "pluginTreeSnapshotContractBound",
1294
1321
  "pluginTreeSnapshotAtomic",
1295
1322
  ];
1323
+ const nodePluginTreeSnapshotEvidence =
1324
+ plan.runtimeProbe.kind === "linux-bwrap-plugin-node-policy-v1" &&
1325
+ plan.runtimeProbe.targetRuntime === "node" &&
1326
+ plan.runtimeProbe.contentSnapshotScope === "plugin-entry-source";
1327
+ const nativePluginRuntimeEvidence =
1328
+ (plan.runtimeProbe.kind ===
1329
+ "linux-bwrap-plugin-native-static-elf-policy-v1" &&
1330
+ plan.runtimeProbe.targetRuntime === "native-static-elf") ||
1331
+ (plan.runtimeProbe.kind ===
1332
+ "linux-bwrap-plugin-native-dynamic-elf-policy-v1" &&
1333
+ plan.runtimeProbe.targetRuntime === "native-dynamic-elf");
1334
+ const nativePluginTreeSnapshotEvidence =
1335
+ nativePluginRuntimeEvidence &&
1336
+ plan.runtimeProbe.contentSnapshotScope === "plugin-entry-executable";
1296
1337
  if (
1297
1338
  pluginTreeSnapshot &&
1298
1339
  (plan.applied !== true ||
@@ -1307,11 +1348,10 @@ class ProcessExecutionBroker extends EventEmitter {
1307
1348
  plan.runtimeProbe.attempted !== true ||
1308
1349
  plan.runtimeProbe.runnable !== true ||
1309
1350
  plan.runtimeProbe.reason !== null ||
1310
- plan.runtimeProbe.kind !== "linux-bwrap-plugin-node-policy-v1" ||
1351
+ (!nodePluginTreeSnapshotEvidence &&
1352
+ !nativePluginTreeSnapshotEvidence) ||
1311
1353
  plan.runtimeProbe.probeRuntime !== "node" ||
1312
- plan.runtimeProbe.targetRuntime !== "node" ||
1313
1354
  plan.runtimeProbe.contentSnapshot !== true ||
1314
- plan.runtimeProbe.contentSnapshotScope !== "plugin-entry-source" ||
1315
1355
  plan.runtimeProbe.contentSnapshotMechanism !==
1316
1356
  "verified-o_tmpfile-copy-bwrap-ro-bind-data-v1" ||
1317
1357
  plan.runtimeProbe.handleAtomic !== false ||
@@ -1355,6 +1395,18 @@ class ProcessExecutionBroker extends EventEmitter {
1355
1395
  "Sandbox runtime probe plugin tree snapshot evidence requires pluginTreeContentSnapshot",
1356
1396
  );
1357
1397
  }
1398
+ const successfulNativeProbe =
1399
+ plan.applied === true &&
1400
+ plan.runtimeProbe.attempted === true &&
1401
+ plan.runtimeProbe.runnable === true &&
1402
+ plan.runtimeProbe.reason === null &&
1403
+ nativePluginRuntimeEvidence;
1404
+ if (successfulNativeProbe && !pluginTreeSnapshot) {
1405
+ throw this._sandboxError(
1406
+ "invalid_sandbox_plan",
1407
+ "Successful Linux native plugin evidence requires a complete plugin tree snapshot",
1408
+ );
1409
+ }
1358
1410
  const supervisorStringFields = [
1359
1411
  "supervisorBindingScope",
1360
1412
  "supervisorDescriptorBindingMechanism",
@@ -1560,15 +1612,97 @@ class ProcessExecutionBroker extends EventEmitter {
1560
1612
  SANDBOX_BOUNDARIES.CODE_SNAPSHOT,
1561
1613
  );
1562
1614
  if (codeSnapshotGuaranteed) {
1615
+ const executionContract = launchContext.executionContract;
1616
+ const launchArgs = Array.isArray(launchContext.args)
1617
+ ? launchContext.args
1618
+ : [];
1619
+ const windowsPlanBindingDeclared =
1620
+ plan.platform === "win32" &&
1621
+ (plan.runtimeProbe.planBindingMechanism !== undefined ||
1622
+ plan.runtimeProbe.planBindingDigest !== undefined);
1623
+ // Consume before evaluating any other Windows evidence. A genuine
1624
+ // one-launch plan is therefore burned by its first admission attempt,
1625
+ // including an attempt with a mismatched command or contract.
1626
+ windowsPlanBindingConsumed = windowsPlanBindingDeclared
1627
+ ? consumeWindowsMcpCodeSnapshotPlanBinding(plan, {
1628
+ command: launchContext.command,
1629
+ args: launchArgs,
1630
+ cwd: launchContext.cwd,
1631
+ shell: launchContext.shell,
1632
+ detached: launchContext.detached,
1633
+ executionContract,
1634
+ profile: launchContext.profile,
1635
+ requiredBoundaries: launchContext.requiredBoundaries,
1636
+ sync: launchContext.sync,
1637
+ })
1638
+ : false;
1639
+ const passthroughArgsMatch = (candidateArgs) =>
1640
+ candidateArgs.length === Math.max(0, launchArgs.length - 1) &&
1641
+ candidateArgs.every(
1642
+ (candidateArg, index) => candidateArg === launchArgs[index + 1],
1643
+ );
1644
+ const contractBoundSnapshotEvidence =
1645
+ executionContract?.contractVersion === 1 &&
1646
+ executionContract.kind === MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND &&
1647
+ typeof launchContext.command === "string" &&
1648
+ launchContext.command === executionContract.runtimePath &&
1649
+ launchContext.command ===
1650
+ executionContract.runtimeIdentity?.realPath &&
1651
+ launchArgs.length >= 1 &&
1652
+ launchArgs[0] === executionContract.entryIdentity?.realPath &&
1653
+ /^[a-f0-9]{64}$/.test(
1654
+ executionContract.runtimeIdentity?.sha256 || "",
1655
+ ) &&
1656
+ Number.isSafeInteger(executionContract.runtimeIdentity?.bytes) &&
1657
+ executionContract.runtimeIdentity.bytes > 0 &&
1658
+ /^[a-f0-9]{64}$/.test(
1659
+ executionContract.entryIdentity?.sha256 || "",
1660
+ ) &&
1661
+ Number.isSafeInteger(executionContract.entryIdentity?.bytes) &&
1662
+ executionContract.entryIdentity.bytes >= 0 &&
1663
+ plan.runtimeProbe.entrySnapshotSha256 ===
1664
+ executionContract.entryIdentity.sha256 &&
1665
+ plan.runtimeProbe.entrySnapshotBytes ===
1666
+ executionContract.entryIdentity.bytes;
1667
+ const linuxRuntimeSnapshotContractBound =
1668
+ plan.runtimeProbe.runtimeSnapshotSha256 ===
1669
+ executionContract?.runtimeIdentity?.sha256 &&
1670
+ plan.runtimeProbe.runtimeSnapshotBytes ===
1671
+ executionContract?.runtimeIdentity?.bytes &&
1672
+ plan.runtimeProbe.runtimeAttestedSha256 === undefined &&
1673
+ plan.runtimeProbe.runtimeAttestedBytes === undefined;
1674
+ const windowsRuntimeAttestationContractBound =
1675
+ plan.runtimeProbe.runtimeAttestedSha256 ===
1676
+ executionContract?.runtimeIdentity?.sha256 &&
1677
+ plan.runtimeProbe.runtimeAttestedBytes ===
1678
+ executionContract?.runtimeIdentity?.bytes &&
1679
+ plan.runtimeProbe.runtimeSnapshotSha256 === undefined &&
1680
+ plan.runtimeProbe.runtimeSnapshotBytes === undefined;
1681
+ const contractEntryRelative = executionContract
1682
+ ? path.posix.relative(
1683
+ executionContract.pluginRoot,
1684
+ executionContract.entryIdentity.realPath,
1685
+ )
1686
+ : null;
1687
+ const expectedLinuxBubblewrapEntry =
1688
+ contractEntryRelative &&
1689
+ contractEntryRelative !== ".." &&
1690
+ !contractEntryRelative.startsWith("../") &&
1691
+ !path.posix.isAbsolute(contractEntryRelative)
1692
+ ? path.posix.join("/opt/chainless/plugin", contractEntryRelative)
1693
+ : null;
1563
1694
  const exactSnapshotEvidence =
1564
1695
  plan.applied === true &&
1565
1696
  candidateBackend === null &&
1697
+ contractBoundSnapshotEvidence &&
1566
1698
  plan.runtimeProbe.attempted === true &&
1567
1699
  plan.runtimeProbe.runnable === true &&
1568
1700
  plan.runtimeProbe.reason === null &&
1569
1701
  plan.runtimeProbe.probeRuntime === "node" &&
1570
1702
  plan.runtimeProbe.targetRuntime === "node" &&
1571
1703
  plan.runtimeProbe.contentSnapshot === true &&
1704
+ plan.runtimeProbe.entrySnapshotAtomic === true &&
1705
+ plan.runtimeProbe.runtimeLaunchAtomic === true &&
1572
1706
  plan.runtimeProbe.sharedLibraryClosure === false &&
1573
1707
  /^[a-f0-9]{64}$/.test(plan.runtimeProbe.entrySnapshotSha256 || "") &&
1574
1708
  Number.isSafeInteger(plan.runtimeProbe.entrySnapshotBytes) &&
@@ -1584,8 +1718,54 @@ class ProcessExecutionBroker extends EventEmitter {
1584
1718
  plan.runtimeProbe.contentSnapshotScope ===
1585
1719
  "mcp-capsule-entry-and-node-runtime" &&
1586
1720
  plan.runtimeProbe.contentSnapshotMechanism ===
1587
- "verified-o_tmpfile-copy-inherited-fd-exec-v1" &&
1721
+ "verified-o_tmpfile-copy-inherited-fd-module-compile-v1" &&
1588
1722
  plan.runtimeProbe.handleAtomic === true &&
1723
+ plan.runtimeProbe.entrySnapshotAtomic === true &&
1724
+ plan.runtimeProbe.runtimeLaunchAtomic === true &&
1725
+ plan.runtimeProbe.runtimeLaunchMechanism ===
1726
+ "inherited-executable-fd-v1" &&
1727
+ plan.runtimeProbe.entrySnapshotBootstrapSha256 ===
1728
+ MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256 &&
1729
+ linuxRuntimeSnapshotContractBound &&
1730
+ /^[a-f0-9]{64}$/.test(
1731
+ plan.runtimeProbe.runtimeSnapshotSha256 || "",
1732
+ ) &&
1733
+ Number.isSafeInteger(plan.runtimeProbe.runtimeSnapshotBytes) &&
1734
+ plan.runtimeProbe.runtimeSnapshotBytes > 0 &&
1735
+ plan.runtimeProbe.runtimeSnapshotBytes <= 256 * 1024 * 1024 &&
1736
+ /^\/proc\/self\/fd\/\d+$/.test(plan.command) &&
1737
+ plan.args[0] === "-e" &&
1738
+ crypto
1739
+ .createHash("sha256")
1740
+ .update(plan.args[1] || "")
1741
+ .digest("hex") === MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256 &&
1742
+ plan.args[2] === "--" &&
1743
+ passthroughArgsMatch(plan.args.slice(3));
1744
+ const linuxBubblewrapTargetSeparator = plan.args.indexOf("--");
1745
+ const linuxBubblewrapSnapshotEvidence =
1746
+ plan.platform === "linux" &&
1747
+ backend === "linux-bwrap" &&
1748
+ plan.enforcement === "linux-bwrap" &&
1749
+ policyAttested === true &&
1750
+ policyDigest !== null &&
1751
+ plan.runtimeProbe.kind === "linux-bwrap-plugin-node-policy-v1" &&
1752
+ plan.runtimeProbe.mcpCapsuleCodeSnapshot === true &&
1753
+ plan.runtimeProbe.contentSnapshotScope === "plugin-entry-source" &&
1754
+ plan.runtimeProbe.contentSnapshotMechanism ===
1755
+ "verified-o_tmpfile-copy-bwrap-ro-bind-data-v1" &&
1756
+ plan.runtimeProbe.handleAtomic === false &&
1757
+ plan.runtimeProbe.entrySnapshotAtomic === true &&
1758
+ plan.runtimeProbe.runtimeLaunchAtomic === true &&
1759
+ plan.runtimeProbe.runtimeLaunchMechanism ===
1760
+ "bwrap-descriptor-mount-node-runtime-exec-v1" &&
1761
+ plan.runtimeProbe.supervisorDescriptorBound === true &&
1762
+ plan.runtimeProbe.pluginTreeContentSnapshot === true &&
1763
+ plan.runtimeProbe.runtimeLaunchPath ===
1764
+ "/opt/chainless/runtime/node" &&
1765
+ expectedLinuxBubblewrapEntry !== null &&
1766
+ plan.runtimeProbe.entrySnapshotPath ===
1767
+ expectedLinuxBubblewrapEntry &&
1768
+ linuxRuntimeSnapshotContractBound &&
1589
1769
  /^[a-f0-9]{64}$/.test(
1590
1770
  plan.runtimeProbe.runtimeSnapshotSha256 || "",
1591
1771
  ) &&
@@ -1593,7 +1773,14 @@ class ProcessExecutionBroker extends EventEmitter {
1593
1773
  plan.runtimeProbe.runtimeSnapshotBytes > 0 &&
1594
1774
  plan.runtimeProbe.runtimeSnapshotBytes <= 256 * 1024 * 1024 &&
1595
1775
  /^\/proc\/self\/fd\/\d+$/.test(plan.command) &&
1596
- /^\/proc\/self\/fd\/\d+$/.test(plan.args[0] || "");
1776
+ linuxBubblewrapTargetSeparator >= 0 &&
1777
+ plan.args[linuxBubblewrapTargetSeparator + 1] ===
1778
+ plan.runtimeProbe.runtimeLaunchPath &&
1779
+ plan.args[linuxBubblewrapTargetSeparator + 2] ===
1780
+ plan.runtimeProbe.entrySnapshotPath &&
1781
+ passthroughArgsMatch(
1782
+ plan.args.slice(linuxBubblewrapTargetSeparator + 3),
1783
+ );
1597
1784
  const windowsSnapshotEvidence =
1598
1785
  plan.platform === "win32" &&
1599
1786
  [
@@ -1601,10 +1788,14 @@ class ProcessExecutionBroker extends EventEmitter {
1601
1788
  "windows-appcontainer-job-restricted-token",
1602
1789
  ].includes(backend) &&
1603
1790
  plan.enforcement === backend &&
1604
- [
1605
- "windows-plugin-node-entry-snapshot-v1",
1606
- "windows-appcontainer-launch-attestation-v1",
1607
- ].includes(plan.runtimeProbe.kind) &&
1791
+ ((backend === "windows-job-restricted-token" &&
1792
+ plan.runtimeProbe.kind ===
1793
+ "windows-plugin-node-entry-snapshot-v1") ||
1794
+ (backend === "windows-appcontainer-job-restricted-token" &&
1795
+ plan.runtimeProbe.kind ===
1796
+ "windows-appcontainer-launch-attestation-v1" &&
1797
+ policyAttested === true &&
1798
+ policyDigest !== null)) &&
1608
1799
  plan.runtimeProbe.contentSnapshotScope ===
1609
1800
  "mcp-capsule-entry-source" &&
1610
1801
  plan.runtimeProbe.contentSnapshotMechanism ===
@@ -1614,15 +1805,23 @@ class ProcessExecutionBroker extends EventEmitter {
1614
1805
  plan.runtimeProbe.runtimeLaunchAtomic === true &&
1615
1806
  plan.runtimeProbe.runtimeLaunchMechanism ===
1616
1807
  "filter-oplock-locked-createprocess-suspended-image-v1" &&
1808
+ plan.runtimeProbe.planBindingMechanism ===
1809
+ "windows-mcp-code-snapshot-plan-binding-v1" &&
1810
+ /^[a-f0-9]{64}$/.test(plan.runtimeProbe.planBindingDigest || "") &&
1811
+ windowsRuntimeAttestationContractBound &&
1617
1812
  /^[a-f0-9]{64}$/.test(
1618
1813
  plan.runtimeProbe.runtimeAttestedSha256 || "",
1619
1814
  ) &&
1620
1815
  Number.isSafeInteger(plan.runtimeProbe.runtimeAttestedBytes) &&
1621
1816
  plan.runtimeProbe.runtimeAttestedBytes > 0 &&
1622
- plan.runtimeProbe.runtimeAttestedBytes <= 256 * 1024 * 1024;
1817
+ plan.runtimeProbe.runtimeAttestedBytes <= 256 * 1024 * 1024 &&
1818
+ launchContext.builtInSandboxAdapter === true &&
1819
+ windowsPlanBindingConsumed;
1623
1820
  if (
1624
1821
  !exactSnapshotEvidence ||
1625
- (!linuxSnapshotEvidence && !windowsSnapshotEvidence)
1822
+ (!linuxSnapshotEvidence &&
1823
+ !linuxBubblewrapSnapshotEvidence &&
1824
+ !windowsSnapshotEvidence)
1626
1825
  ) {
1627
1826
  throw this._sandboxError(
1628
1827
  "invalid_sandbox_plan",
@@ -1910,6 +2109,22 @@ class ProcessExecutionBroker extends EventEmitter {
1910
2109
  ...(plan.runtimeProbe.handleAtomic !== undefined
1911
2110
  ? { handleAtomic: plan.runtimeProbe.handleAtomic }
1912
2111
  : {}),
2112
+ ...(plan.runtimeProbe.entrySnapshotAtomic !== undefined
2113
+ ? { entrySnapshotAtomic: plan.runtimeProbe.entrySnapshotAtomic }
2114
+ : {}),
2115
+ ...(plan.runtimeProbe.runtimeLaunchAtomic !== undefined
2116
+ ? { runtimeLaunchAtomic: plan.runtimeProbe.runtimeLaunchAtomic }
2117
+ : {}),
2118
+ ...(plan.runtimeProbe.runtimeLaunchMechanism !== undefined
2119
+ ? {
2120
+ runtimeLaunchMechanism: plan.runtimeProbe.runtimeLaunchMechanism,
2121
+ }
2122
+ : {}),
2123
+ ...(plan.runtimeProbe.sharedLibraryClosure !== undefined
2124
+ ? {
2125
+ sharedLibraryClosure: plan.runtimeProbe.sharedLibraryClosure,
2126
+ }
2127
+ : {}),
1913
2128
  ...(plan.runtimeProbe.contentSnapshotScope !== undefined
1914
2129
  ? {
1915
2130
  contentSnapshotScope: plan.runtimeProbe.contentSnapshotScope,
@@ -1921,12 +2136,35 @@ class ProcessExecutionBroker extends EventEmitter {
1921
2136
  plan.runtimeProbe.contentSnapshotMechanism,
1922
2137
  }
1923
2138
  : {}),
2139
+ ...(plan.runtimeProbe.planBindingMechanism !== undefined
2140
+ ? {
2141
+ planBindingMechanism: plan.runtimeProbe.planBindingMechanism,
2142
+ planBindingDigest: plan.runtimeProbe.planBindingDigest,
2143
+ }
2144
+ : {}),
1924
2145
  ...(plan.runtimeProbe.runtimeSnapshotSha256 !== undefined
1925
2146
  ? {
1926
2147
  runtimeSnapshotSha256: plan.runtimeProbe.runtimeSnapshotSha256,
1927
2148
  runtimeSnapshotBytes: plan.runtimeProbe.runtimeSnapshotBytes,
1928
2149
  entrySnapshotSha256: plan.runtimeProbe.entrySnapshotSha256,
1929
2150
  entrySnapshotBytes: plan.runtimeProbe.entrySnapshotBytes,
2151
+ entrySnapshotAtomic: plan.runtimeProbe.entrySnapshotAtomic,
2152
+ runtimeLaunchAtomic: plan.runtimeProbe.runtimeLaunchAtomic,
2153
+ runtimeLaunchMechanism: plan.runtimeProbe.runtimeLaunchMechanism,
2154
+ ...(plan.runtimeProbe.runtimeLaunchPath !== undefined
2155
+ ? { runtimeLaunchPath: plan.runtimeProbe.runtimeLaunchPath }
2156
+ : {}),
2157
+ ...(plan.runtimeProbe.entrySnapshotPath !== undefined
2158
+ ? { entrySnapshotPath: plan.runtimeProbe.entrySnapshotPath }
2159
+ : {}),
2160
+ ...(plan.runtimeProbe.mcpCapsuleCodeSnapshot !== undefined
2161
+ ? {
2162
+ mcpCapsuleCodeSnapshot:
2163
+ plan.runtimeProbe.mcpCapsuleCodeSnapshot,
2164
+ }
2165
+ : {}),
2166
+ entrySnapshotBootstrapSha256:
2167
+ plan.runtimeProbe.entrySnapshotBootstrapSha256,
1930
2168
  sharedLibraryClosure: plan.runtimeProbe.sharedLibraryClosure,
1931
2169
  }
1932
2170
  : {}),
@@ -1988,6 +2226,15 @@ class ProcessExecutionBroker extends EventEmitter {
1988
2226
  };
1989
2227
  }
1990
2228
  }
2229
+ if (
2230
+ guarantees.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT) &&
2231
+ runtimeProbe === null
2232
+ ) {
2233
+ throw this._sandboxError(
2234
+ "invalid_sandbox_plan",
2235
+ "Code snapshot guarantee requires typed atomic MCP capsule evidence",
2236
+ );
2237
+ }
1991
2238
  if (backend === "linux-bwrap-workspace" && !genericWorkspaceProbe) {
1992
2239
  throw this._sandboxError(
1993
2240
  "invalid_sandbox_plan",
@@ -2005,7 +2252,7 @@ class ProcessExecutionBroker extends EventEmitter {
2005
2252
  "Required post-spawn enforcement must declare sync or async mode",
2006
2253
  );
2007
2254
  }
2008
- return {
2255
+ const validatedPlan = {
2009
2256
  ...plan,
2010
2257
  args: [...plan.args],
2011
2258
  options: { ...plan.options },
@@ -2025,6 +2272,15 @@ class ProcessExecutionBroker extends EventEmitter {
2025
2272
  : {}),
2026
2273
  postSpawn: { ...postSpawn },
2027
2274
  };
2275
+ if (windowsPlanBindingConsumed) {
2276
+ const upstreamCleanup = validatedPlan.cleanup;
2277
+ validatedPlan.cleanup = (...args) => {
2278
+ admittedWindowsMcpCodeSnapshotPlans.delete(validatedPlan);
2279
+ return upstreamCleanup?.(...args);
2280
+ };
2281
+ admittedWindowsMcpCodeSnapshotPlans.add(validatedPlan);
2282
+ }
2283
+ return validatedPlan;
2028
2284
  }
2029
2285
 
2030
2286
  _assertRequiredSandboxBoundaries(plan, requiredBoundaries) {
@@ -2059,10 +2315,20 @@ class ProcessExecutionBroker extends EventEmitter {
2059
2315
  },
2060
2316
  );
2061
2317
  }
2062
- return {
2318
+ const assertedPlan = {
2063
2319
  ...plan,
2064
2320
  requiredBoundaries: [...requiredBoundaries],
2065
2321
  };
2322
+ if (admittedWindowsMcpCodeSnapshotPlans.has(plan)) {
2323
+ admittedWindowsMcpCodeSnapshotPlans.delete(plan);
2324
+ const upstreamCleanup = assertedPlan.cleanup;
2325
+ assertedPlan.cleanup = (...args) => {
2326
+ admittedWindowsMcpCodeSnapshotPlans.delete(assertedPlan);
2327
+ return upstreamCleanup?.(...args);
2328
+ };
2329
+ admittedWindowsMcpCodeSnapshotPlans.add(assertedPlan);
2330
+ }
2331
+ return assertedPlan;
2066
2332
  }
2067
2333
 
2068
2334
  _prepareSandboxPlan(
@@ -2116,17 +2382,44 @@ class ProcessExecutionBroker extends EventEmitter {
2116
2382
  // Keep the legacy string profile in argument four. The built-in adapter
2117
2383
  // reserves argument five for runtime injection, so the typed request is
2118
2384
  // additive in argument six and legacy injected adapters can ignore it.
2119
- const rawPlan = this._sandboxAdapter.applySandbox(
2120
- command,
2121
- args || [],
2122
- options,
2123
- profile,
2124
- undefined,
2125
- adapterRequest,
2126
- );
2385
+ const sandboxAdapter = this._sandboxAdapter;
2386
+ const applySandboxAdapter = sandboxAdapter.applySandbox;
2387
+ const builtInSandboxAdapter = applySandboxAdapter === _applySandbox;
2388
+ // Never dispatch the built-in through a mutable Function property or a
2389
+ // global reflective helper after consulting an injected adapter getter.
2390
+ // The lexical import is the identity we checked and the implementation we
2391
+ // invoke. Legacy adapters retain their historical `this` receiver.
2392
+ const rawPlan = builtInSandboxAdapter
2393
+ ? _applySandbox(
2394
+ command,
2395
+ args || [],
2396
+ options,
2397
+ profile,
2398
+ undefined,
2399
+ adapterRequest,
2400
+ )
2401
+ : Reflect.apply(applySandboxAdapter, sandboxAdapter, [
2402
+ command,
2403
+ args || [],
2404
+ options,
2405
+ profile,
2406
+ undefined,
2407
+ adapterRequest,
2408
+ ]);
2127
2409
  let plan;
2128
2410
  try {
2129
- plan = this._validateSandboxPlan(rawPlan);
2411
+ plan = this._validateSandboxPlan(rawPlan, {
2412
+ command,
2413
+ args: Object.freeze([...(args || [])]),
2414
+ cwd: options.cwd,
2415
+ shell: options.shell,
2416
+ detached: options.detached,
2417
+ executionContract: sandboxPolicy.executionContract || null,
2418
+ profile,
2419
+ requiredBoundaries: Object.freeze([...requiredBoundaries]),
2420
+ sync,
2421
+ builtInSandboxAdapter,
2422
+ });
2130
2423
  plan = this._assertRequiredSandboxBoundaries(plan, requiredBoundaries);
2131
2424
 
2132
2425
  if (!plan.applied && strict) {
@@ -2139,7 +2432,12 @@ class ProcessExecutionBroker extends EventEmitter {
2139
2432
  }
2140
2433
 
2141
2434
  if (plan.applied && plan.postSpawn.required) {
2142
- if (typeof this._sandboxAdapter.postSpawnSandbox !== "function") {
2435
+ const builtInWindowsMcpPostSpawn =
2436
+ admittedWindowsMcpCodeSnapshotPlans.has(plan);
2437
+ if (
2438
+ !builtInWindowsMcpPostSpawn &&
2439
+ typeof this._sandboxAdapter.postSpawnSandbox !== "function"
2440
+ ) {
2143
2441
  throw this._sandboxError(
2144
2442
  "post_spawn_adapter_unavailable",
2145
2443
  "Required post-spawn sandbox adapter is unavailable",
@@ -2312,12 +2610,6 @@ class ProcessExecutionBroker extends EventEmitter {
2312
2610
  }
2313
2611
 
2314
2612
  _createWorkspaceProcessCloseFence(auditEntry, proc) {
2315
- if (
2316
- !Array.isArray(auditEntry.workspaceTransactionIds) ||
2317
- auditEntry.workspaceTransactionIds.length === 0
2318
- ) {
2319
- return null;
2320
- }
2321
2613
  return new Promise((resolve) => {
2322
2614
  if (typeof proc?.once === "function") {
2323
2615
  proc.once("close", (exitCode, signal) => {
@@ -2339,6 +2631,54 @@ class ProcessExecutionBroker extends EventEmitter {
2339
2631
  });
2340
2632
  }
2341
2633
 
2634
+ _postSpawnOwnershipError(
2635
+ error,
2636
+ { proc, auditEntry, workspaceProcessClosed, cleanup },
2637
+ ) {
2638
+ let failure =
2639
+ error instanceof Error
2640
+ ? error
2641
+ : new Error(String(error || "spawn failed"));
2642
+ if (
2643
+ failure.processTerminationRequested !== true &&
2644
+ failure.workspaceTerminationRequested !== true
2645
+ ) {
2646
+ try {
2647
+ if (typeof proc?.once === "function") proc.once("error", () => {});
2648
+ } catch {
2649
+ // Listener installation failure is itself post-spawn evidence.
2650
+ }
2651
+ try {
2652
+ proc?.kill?.("SIGKILL");
2653
+ } catch {
2654
+ // The close fence remains the only settlement authority.
2655
+ }
2656
+ }
2657
+ try {
2658
+ cleanup?.();
2659
+ } catch {
2660
+ // Ownership evidence must survive cleanup reporting failures.
2661
+ }
2662
+ const attach = (target) => {
2663
+ target.auditEntry = auditEntry;
2664
+ target.spawnedProcess = proc;
2665
+ target.workspaceProcessClosed = workspaceProcessClosed;
2666
+ target.workspaceTerminationRequested = true;
2667
+ return target;
2668
+ };
2669
+ try {
2670
+ return attach(failure);
2671
+ } catch {
2672
+ const wrapped = new Error(failure.message || "post-spawn failure", {
2673
+ cause: failure,
2674
+ });
2675
+ wrapped.name = failure.name || wrapped.name;
2676
+ if (failure.code !== undefined) wrapped.code = failure.code;
2677
+ failure = wrapped;
2678
+ return attach(failure);
2679
+ }
2680
+ }
2681
+
2342
2682
  _runPostSpawnSandbox(proc, plan, auditEntry) {
2343
2683
  if (!plan.applied || !plan.postSpawn.required) {
2344
2684
  this._applySandboxAudit(auditEntry, plan, plan.applied);
@@ -2354,7 +2694,28 @@ class ProcessExecutionBroker extends EventEmitter {
2354
2694
 
2355
2695
  let postSpawnResult;
2356
2696
  try {
2357
- postSpawnResult = this._sandboxAdapter.postSpawnSandbox(proc, plan);
2697
+ const windowsMcpPlanBindingDeclared =
2698
+ plan.platform === "win32" &&
2699
+ plan.runtimeProbe?.planBindingMechanism ===
2700
+ "windows-mcp-code-snapshot-plan-binding-v1";
2701
+ const builtInWindowsMcpPlan =
2702
+ admittedWindowsMcpCodeSnapshotPlans.has(plan);
2703
+ if (windowsMcpPlanBindingDeclared && !builtInWindowsMcpPlan) {
2704
+ throw this._sandboxError(
2705
+ "windows_mcp_plan_binding_consumed",
2706
+ "Windows MCP sandbox plan binding is unavailable or already consumed",
2707
+ );
2708
+ }
2709
+ if (builtInWindowsMcpPlan) {
2710
+ // Burn the one-launch post-spawn authority before invoking untrusted
2711
+ // process-facing code. Success, failure, cleanup, and reentry all
2712
+ // observe the capability as consumed.
2713
+ admittedWindowsMcpCodeSnapshotPlans.delete(plan);
2714
+ }
2715
+ const postSpawnAdapter = builtInWindowsMcpPlan
2716
+ ? (child, boundPlan) => boundPlan.postSpawnWindows(child)
2717
+ : this._sandboxAdapter.postSpawnSandbox;
2718
+ postSpawnResult = postSpawnAdapter(proc, plan);
2358
2719
  } catch (error) {
2359
2720
  this._applySandboxAudit(auditEntry, plan, false);
2360
2721
  auditEntry.sandboxState = "failed";
@@ -3034,6 +3395,7 @@ class ProcessExecutionBroker extends EventEmitter {
3034
3395
  // Use native spawn from _native (set by patch-child-process.js)
3035
3396
  const nativeSpawnFn = this._native?.spawn || nativeSpawn;
3036
3397
  let proc;
3398
+ let workspaceProcessClosed = null;
3037
3399
  try {
3038
3400
  if (mcpStdioExecutableIdentityAuthority !== undefined) {
3039
3401
  const admitted = consumeMcpStdioExecutableIdentityAuthority(
@@ -3047,16 +3409,20 @@ class ProcessExecutionBroker extends EventEmitter {
3047
3409
  auditEntry.mcpStdioExecutableIdentityDigest = admitted.identityDigest;
3048
3410
  }
3049
3411
  proc = nativeSpawnFn(command, args, optsForSpawn);
3412
+ workspaceProcessClosed = this._createWorkspaceProcessCloseFence(
3413
+ auditEntry,
3414
+ proc,
3415
+ );
3050
3416
  this._bindWorkspaceTransactionProcess(auditEntry, proc);
3051
3417
  } catch (spawnError) {
3052
- if (proc && typeof proc.kill === "function") {
3053
- try {
3054
- proc.kill("SIGKILL");
3055
- } catch {
3056
- // The transaction remains unsettled if shutdown cannot be proven.
3057
- }
3058
- }
3059
- if (!proc) {
3418
+ if (proc) {
3419
+ throw this._postSpawnOwnershipError(spawnError, {
3420
+ proc,
3421
+ auditEntry,
3422
+ workspaceProcessClosed,
3423
+ cleanup: sandboxPlan.cleanup,
3424
+ });
3425
+ } else {
3060
3426
  try {
3061
3427
  this._settleWorkspaceTransactionSpawn(auditEntry, {
3062
3428
  error: spawnError.message,
@@ -3069,102 +3435,105 @@ class ProcessExecutionBroker extends EventEmitter {
3069
3435
  throw spawnError;
3070
3436
  }
3071
3437
  auditEntry.pid = proc.pid;
3072
- const workspaceProcessClosed = this._createWorkspaceProcessCloseFence(
3073
- auditEntry,
3074
- proc,
3075
- );
3076
- const cleanupSandbox = this._scheduleSandboxCleanup(
3077
- proc,
3078
- sandboxPlan.cleanup,
3079
- );
3080
- if (
3081
- sandboxPlan.applied === true &&
3082
- (sandboxPlan.backend === "linux-bwrap-workspace" ||
3083
- sandboxPlan.backend === "linux-bwrap" ||
3084
- sandboxPlan.backend === "linux-fd-code-snapshot") &&
3085
- sandboxPlan.postSpawn.required === false
3086
- ) {
3087
- // child_process.spawn() has synchronously duplicated every stdio entry
3088
- // into the launched process before returning. The generic/direct bwrap
3089
- // backends and the MCP anonymous-code snapshot backend are complete
3090
- // pre-spawn plans with no parent-side enforcement handle. Retaining
3091
- // their pinned descriptors until a long-lived process exits would turn
3092
- // each child into an avoidable FD leak.
3093
- cleanupSandbox();
3094
- }
3438
+ let cleanupSandbox = sandboxPlan.cleanup;
3095
3439
  try {
3096
- this._runPostSpawnSandbox(proc, sandboxPlan, auditEntry);
3097
- this._updateWorkspaceTransactionProcessGuarantees(auditEntry);
3098
- auditEntry.pid = proc.pid;
3099
- if (Number.isSafeInteger(proc.sandboxWrapperPid)) {
3100
- auditEntry.sandboxWrapperPid = proc.sandboxWrapperPid;
3101
- }
3102
- if (Number.isSafeInteger(proc.sandboxTargetPid)) {
3103
- auditEntry.sandboxTargetPid = proc.sandboxTargetPid;
3440
+ cleanupSandbox = this._scheduleSandboxCleanup(proc, sandboxPlan.cleanup);
3441
+ if (
3442
+ sandboxPlan.applied === true &&
3443
+ (sandboxPlan.backend === "linux-bwrap-workspace" ||
3444
+ sandboxPlan.backend === "linux-bwrap" ||
3445
+ sandboxPlan.backend === "linux-fd-code-snapshot") &&
3446
+ sandboxPlan.postSpawn.required === false
3447
+ ) {
3448
+ // child_process.spawn() has synchronously duplicated every stdio entry
3449
+ // into the launched process before returning. The generic/direct bwrap
3450
+ // backends and the MCP anonymous-code snapshot backend are complete
3451
+ // pre-spawn plans with no parent-side enforcement handle. Retaining
3452
+ // their pinned descriptors until a long-lived process exits would turn
3453
+ // each child into an avoidable FD leak.
3454
+ cleanupSandbox();
3104
3455
  }
3105
- } catch (postSpawnError) {
3106
- if (postSpawnError.processTerminationRequested !== true) {
3107
- try {
3108
- proc.kill?.("SIGKILL");
3109
- } catch {
3110
- // The close fence remains pending and recovery stays fail-closed.
3456
+ try {
3457
+ this._runPostSpawnSandbox(proc, sandboxPlan, auditEntry);
3458
+ this._updateWorkspaceTransactionProcessGuarantees(auditEntry);
3459
+ auditEntry.pid = proc.pid;
3460
+ if (Number.isSafeInteger(proc.sandboxWrapperPid)) {
3461
+ auditEntry.sandboxWrapperPid = proc.sandboxWrapperPid;
3462
+ }
3463
+ if (Number.isSafeInteger(proc.sandboxTargetPid)) {
3464
+ auditEntry.sandboxTargetPid = proc.sandboxTargetPid;
3465
+ }
3466
+ } catch (postSpawnError) {
3467
+ if (postSpawnError.processTerminationRequested !== true) {
3468
+ try {
3469
+ proc.kill?.("SIGKILL");
3470
+ } catch {
3471
+ // The close fence remains pending and recovery stays fail-closed.
3472
+ }
3111
3473
  }
3474
+ cleanupSandbox();
3475
+ this._recordSandboxDenial(auditEntry, postSpawnError, startTime);
3476
+ postSpawnError.auditEntry = auditEntry;
3477
+ postSpawnError.spawnedProcess = proc;
3478
+ postSpawnError.workspaceProcessClosed = workspaceProcessClosed;
3479
+ postSpawnError.workspaceTerminationRequested = true;
3480
+ throw postSpawnError;
3112
3481
  }
3113
- cleanupSandbox();
3114
- this._recordSandboxDenial(auditEntry, postSpawnError, startTime);
3115
- postSpawnError.auditEntry = auditEntry;
3116
- postSpawnError.spawnedProcess = proc;
3117
- postSpawnError.workspaceProcessClosed = workspaceProcessClosed;
3118
- postSpawnError.workspaceTerminationRequested = true;
3119
- throw postSpawnError;
3120
- }
3121
3482
 
3122
- this._recordAudit(auditEntry);
3123
- this._writeRplEntry(auditEntry, "started");
3124
- this._emitHooksEvent("tool:start", {
3125
- executionId,
3126
- toolName: origin,
3127
- command,
3128
- args: [...auditEntry.args],
3129
- cwd,
3130
- pid: proc.pid,
3131
- component: origin,
3132
- });
3133
-
3134
- proc.on("exit", (code, signal) => {
3135
- const endTime = Date.now();
3136
- auditEntry.exitCode = code;
3137
- auditEntry.signal = signal;
3138
- auditEntry.endTime = endTime;
3139
- auditEntry.durationMs = endTime - startTime;
3140
- this._writeRplEntry(auditEntry, "completed");
3141
- this._emitHooksEvent("tool:end", {
3483
+ this._recordAudit(auditEntry);
3484
+ this._writeRplEntry(auditEntry, "started");
3485
+ this._emitHooksEvent("tool:start", {
3142
3486
  executionId,
3143
- success: code === 0,
3144
- exitCode: code,
3145
- signal,
3146
- durationMs: auditEntry.durationMs,
3487
+ toolName: origin,
3488
+ command,
3489
+ args: [...auditEntry.args],
3490
+ cwd,
3491
+ pid: proc.pid,
3147
3492
  component: origin,
3148
3493
  });
3149
- this.emit("exit", auditEntry);
3150
- });
3151
- proc.on("error", (err) => {
3152
- auditEntry.error = err.message;
3153
- auditEntry.endTime = Date.now();
3154
- auditEntry.durationMs = auditEntry.endTime - startTime;
3155
- this._writeRplEntry(auditEntry, "error", err);
3156
- this._emitHooksEvent("tool:end", {
3157
- executionId,
3158
- success: false,
3159
- error: err.message,
3160
- component: origin,
3494
+
3495
+ proc.on("exit", (code, signal) => {
3496
+ const endTime = Date.now();
3497
+ auditEntry.exitCode = code;
3498
+ auditEntry.signal = signal;
3499
+ auditEntry.endTime = endTime;
3500
+ auditEntry.durationMs = endTime - startTime;
3501
+ this._writeRplEntry(auditEntry, "completed");
3502
+ this._emitHooksEvent("tool:end", {
3503
+ executionId,
3504
+ success: code === 0,
3505
+ exitCode: code,
3506
+ signal,
3507
+ durationMs: auditEntry.durationMs,
3508
+ component: origin,
3509
+ });
3510
+ this.emit("exit", auditEntry);
3161
3511
  });
3162
- // EventEmitter treats an unhandled `error` event as an exception. A
3163
- // process ENOENT must reach the child-process listener/callback instead
3164
- // of crashing callers that did not subscribe to broker diagnostics.
3165
- if (this.listenerCount("error") > 0) this.emit("error", auditEntry);
3166
- });
3167
- return proc;
3512
+ proc.on("error", (err) => {
3513
+ auditEntry.error = err.message;
3514
+ auditEntry.endTime = Date.now();
3515
+ auditEntry.durationMs = auditEntry.endTime - startTime;
3516
+ this._writeRplEntry(auditEntry, "error", err);
3517
+ this._emitHooksEvent("tool:end", {
3518
+ executionId,
3519
+ success: false,
3520
+ error: err.message,
3521
+ component: origin,
3522
+ });
3523
+ // EventEmitter treats an unhandled `error` event as an exception. A
3524
+ // process ENOENT must reach the child-process listener/callback instead
3525
+ // of crashing callers that did not subscribe to broker diagnostics.
3526
+ if (this.listenerCount("error") > 0) this.emit("error", auditEntry);
3527
+ });
3528
+ return proc;
3529
+ } catch (postNativeError) {
3530
+ throw this._postSpawnOwnershipError(postNativeError, {
3531
+ proc,
3532
+ auditEntry,
3533
+ workspaceProcessClosed,
3534
+ cleanup: cleanupSandbox,
3535
+ });
3536
+ }
3168
3537
  }
3169
3538
 
3170
3539
  spawnSync(command, args, options = {}) {