chainlesschain 0.163.7 → 0.164.0

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 (252) hide show
  1. package/package.json +5 -1
  2. package/scripts/macos-mcp-launcher-build.mjs +317 -0
  3. package/scripts/macos-mcp-launcher-postinstall.sh +54 -0
  4. package/scripts/macos-mcp-launcher-preinstall.sh +61 -0
  5. package/src/assets/web-panel/assets/{AIOps-axqQfwv8.js → AIOps-D2MEn7fI.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-DV-Ro0By.js → ActionButton-lCW_4Gf0.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-Bt72WEVu.js → Analytics-Bs7i-nsS.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-BtepG55E.js → AppLayout-CK7B6S_j.js} +5 -5
  9. package/src/assets/web-panel/assets/{Artifacts-B7KaJ7S_.js → Artifacts-C8ILjpyo.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-FNFaUg7A.js → Audit-Bb9cNaWc.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-DFb6J2wQ.js → BackgroundAgents-sC9ooXIf.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CC7YBhyj.js → Backup-D-DrsplT.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-RkgTNVtj.js → BaseInput-uCNZpwSK.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-hHEmGZy-.js → Chat-CsDK1uE9.js} +6 -6
  15. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CAP9YhHv.js → ChatBubbleRenderer-OqSDt50w.js} +1 -1
  16. package/src/assets/web-panel/assets/{Checkbox-z5wXOQP1.js → Checkbox-JhTXM1-K.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-DVaDJnxy.js → Codegen-BbzZLnyD.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-CJmaRDR5.js → Col-D4VW4m4j.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-DF29_2bI.js → Community-BXrYawxV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-DtkQkBzn.js → Compact-DCihGy1u.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-xoHDxp7I.js → Compliance-DlyZ-lk8.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-BUm-R7ht.js → Cowork-DvGZrhbO.js} +2 -2
  23. package/src/assets/web-panel/assets/{Cron-Cgl14ZkB.js → Cron-DUncZi8h.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-BNcbmoqh.js → Crosschain-C_hLuUcm.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-Bwja0Zo9.js → DID-b6YSUqxr.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-Bk5XjEdJ.js → Dashboard-uvM15Cfb.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-Ci36Oitq.js → Dropdown-D1-Nd4qY.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer--U04ZN4q.js → EmailListRenderer-8ApvL6Zc.js} +1 -1
  29. package/src/assets/web-panel/assets/{FamilyGuardDashboard-B2gNPR2s.js → FamilyGuardDashboard-LYJt8811.js} +1 -1
  30. package/src/assets/web-panel/assets/{Federation-C4KghYn7.js → Federation-BbI3HGpv.js} +1 -1
  31. package/src/assets/web-panel/assets/{FormItemContext-QL7x_Q_0.js → FormItemContext-DSoEKuFu.js} +1 -1
  32. package/src/assets/web-panel/assets/{GenericCardRenderer-F3Ryltwm.js → GenericCardRenderer-B0S9OWd5.js} +1 -1
  33. package/src/assets/web-panel/assets/{Git-2HassK5k.js → Git-DIs9Zhf3.js} +2 -2
  34. package/src/assets/web-panel/assets/{Governance-DoBIp7oe.js → Governance-jzZXrxcl.js} +1 -1
  35. package/src/assets/web-panel/assets/{Inference--W01QhZ-.js → Inference-JUPjf2ZI.js} +1 -1
  36. package/src/assets/web-panel/assets/{KnowledgeGraph-DmvwqGpT.js → KnowledgeGraph-DxnfJvlP.js} +1 -1
  37. package/src/assets/web-panel/assets/{Logs-BBJ4yAel.js → Logs-iXiz8BEE.js} +2 -2
  38. package/src/assets/web-panel/assets/{MarkdownRenderer-BLCZMzhJ.js → MarkdownRenderer-GmB6DOr6.js} +1 -1
  39. package/src/assets/web-panel/assets/{Marketplace-D5lNngSI.js → Marketplace-Y3Xch312.js} +1 -1
  40. package/src/assets/web-panel/assets/{McpTools-CkEVOMKH.js → McpTools-D7kGGbO7.js} +4 -4
  41. package/src/assets/web-panel/assets/{Memory-B1Ow1xVE.js → Memory-Do_hVxzh.js} +2 -2
  42. package/src/assets/web-panel/assets/{MobileBridge-SgYbUfb6.js → MobileBridge-CtrR0Tas.js} +1 -1
  43. package/src/assets/web-panel/assets/MobileProjects-BL0Czcoj.js +1 -0
  44. package/src/assets/web-panel/assets/{Mtc-DcTXSSma.js → Mtc-qo5QCTGd.js} +2 -2
  45. package/src/assets/web-panel/assets/{MtcAudit-Dt3WG4oj.js → MtcAudit-cq5mnjYj.js} +2 -2
  46. package/src/assets/web-panel/assets/{Multisig-B9B38UZx.js → Multisig-Bq0MAQHw.js} +3 -3
  47. package/src/assets/web-panel/assets/{NLProgramming-B0S4GGvG.js → NLProgramming-DdWE6Vi2.js} +1 -1
  48. package/src/assets/web-panel/assets/{Notes-DZ5U58Ng.js → Notes-BxMBFcIp.js} +3 -3
  49. package/src/assets/web-panel/assets/{NotificationSettings-PjL5ZW34.js → NotificationSettings-RTrAcbUe.js} +1 -1
  50. package/src/assets/web-panel/assets/{OrderTableRenderer-DrAX2ESt.js → OrderTableRenderer-DJbpGqzT.js} +1 -1
  51. package/src/assets/web-panel/assets/{Organization-Cv1GYs-c.js → Organization-SQzIPdqC.js} +4 -4
  52. package/src/assets/web-panel/assets/{Overflow-Bbkp_PL3.js → Overflow-02yECh22.js} +1 -1
  53. package/src/assets/web-panel/assets/{P2P-BD8QUgoZ.js → P2P-BNg4pNCM.js} +2 -2
  54. package/src/assets/web-panel/assets/{PdhVaultBrowser-BO-2PeaB.js → PdhVaultBrowser-BxPJZsD3.js} +3 -3
  55. package/src/assets/web-panel/assets/{Permissions-D_OPVa02.js → Permissions-CLnDnPAz.js} +4 -4
  56. package/src/assets/web-panel/assets/{PersonalDataHub-mh-pEmaB.js → PersonalDataHub-C5m0AjGf.js} +4 -4
  57. package/src/assets/web-panel/assets/{Pipeline-6JC2EDdb.js → Pipeline-VwOf5Dhv.js} +1 -1
  58. package/src/assets/web-panel/assets/{Privacy-CUshzUAG.js → Privacy-CC0W2hZd.js} +1 -1
  59. package/src/assets/web-panel/assets/{ProjectInit-Cs2Aalri.js → ProjectInit-Dz893Bob.js} +2 -2
  60. package/src/assets/web-panel/assets/{ProjectSettings-1ak49tcj.js → ProjectSettings-CxQkvl0k.js} +2 -2
  61. package/src/assets/web-panel/assets/Projects-DDAjkUkT.js +1 -0
  62. package/src/assets/web-panel/assets/{Providers-CXXxvafH.js → Providers-DeB9IkxK.js} +1 -1
  63. package/src/assets/web-panel/assets/{QrScannerModal-PTcDsF58.js → QrScannerModal-OCeoaswn.js} +1 -1
  64. package/src/assets/web-panel/assets/{QuickAsk-CDwucrJw.js → QuickAsk-DenotaAj.js} +1 -1
  65. package/src/assets/web-panel/assets/{Recommend-CJlLVRXR.js → Recommend-Cies_jjI.js} +1 -1
  66. package/src/assets/web-panel/assets/{RemoteSession-DlN-ETsO.js → RemoteSession-Dxzl5n2T.js} +3 -3
  67. package/src/assets/web-panel/assets/{Reputation-DI23LR6m.js → Reputation-CW0mvxgZ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Row-Cl7UQMrM.js → Row-SEd32qZU.js} +1 -1
  69. package/src/assets/web-panel/assets/{RssFeed-CexHNfh8.js → RssFeed-BSwnJ3Lm.js} +3 -3
  70. package/src/assets/web-panel/assets/{Search-BneBWvRN.js → Search-xe2m8bkC.js} +1 -1
  71. package/src/assets/web-panel/assets/{Security-DX7Phgrp.js → Security-Mq2F9Nro.js} +4 -4
  72. package/src/assets/web-panel/assets/{Services-BWXO8Mte.js → Services-BKGw2V3P.js} +2 -2
  73. package/src/assets/web-panel/assets/{Skeleton-DCyOjKPJ.js → Skeleton-DW95-Rxf.js} +1 -1
  74. package/src/assets/web-panel/assets/{Skills-CXqERsTB.js → Skills-CHgChCv_.js} +1 -1
  75. package/src/assets/web-panel/assets/{Sla-bhbrbz8E.js → Sla-tKh6k3R7.js} +1 -1
  76. package/src/assets/web-panel/assets/{SpeechSettings-3IPU0HBZ.js → SpeechSettings-D6UL3dA5.js} +1 -1
  77. package/src/assets/web-panel/assets/{SyncSettings-DBNTQLbM.js → SyncSettings-D0Fw8fVL.js} +2 -2
  78. package/src/assets/web-panel/assets/{Tasks-quP0bkN-.js → Tasks-20x1GB3D.js} +1 -1
  79. package/src/assets/web-panel/assets/{Templates-BouWmgho.js → Templates-DJ-lts-a.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tenant-qv0D9s7z.js → Tenant-BFdGpbs7.js} +1 -1
  81. package/src/assets/web-panel/assets/{Terminal-BxvbE8BB.js → Terminal-Xpx61vHL.js} +2 -2
  82. package/src/assets/web-panel/assets/{TimelineRenderer-D-IeLnJ2.js → TimelineRenderer-C6UIvyZQ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Tokens-CFMKKyVG.js → Tokens-Dellq08f.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trigger-L8as3UuJ.js → Trigger-VKw8d0J-.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trust-MxmA4VZG.js → Trust-DNftdKT5.js} +1 -1
  86. package/src/assets/web-panel/assets/{UkeySign-C0VvJYRi.js → UkeySign-DX9VAKJI.js} +1 -1
  87. package/src/assets/web-panel/assets/{VideoEditing-Bf5QZj8N.js → VideoEditing-B2h3jO8q.js} +1 -1
  88. package/src/assets/web-panel/assets/{Wallet-BIJ9riRz.js → Wallet-CjGkof7f.js} +4 -4
  89. package/src/assets/web-panel/assets/{WebAuthn-B8p9Mhkw.js → WebAuthn-2Zfcu55P.js} +4 -4
  90. package/src/assets/web-panel/assets/{WorkflowEditor-BVsuaPen.js → WorkflowEditor-B9eZ8UxZ.js} +1 -1
  91. package/src/assets/web-panel/assets/{chat-BfkjoKpV.js → chat-CQNfD-YO.js} +1 -1
  92. package/src/assets/web-panel/assets/{colors-DeAlsWK8.js → colors-D4usyFT2.js} +1 -1
  93. package/src/assets/web-panel/assets/{compact-item-Cs97bz5j.js → compact-item-s4TpjvjX.js} +1 -1
  94. package/src/assets/web-panel/assets/{createContext-BqbG60LH.js → createContext-BRCSvYxj.js} +1 -1
  95. package/src/assets/web-panel/assets/devWarning-DKJjSWRn.js +1 -0
  96. package/src/assets/web-panel/assets/{hasIn-i_RY0b1X.js → hasIn-AdkUbkV-.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BOVkf1K3.js → index--00U6vXZ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-B-3tEpTc.js → index-B2V-eKfK.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-D2B4VPyP.js → index-B4JJCfQ2.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BkBi5TmC.js → index-BIoG5yYf.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-79V_zv1s.js → index-BMcimU-2.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-U4f5izHz.js → index-BPhkFZ1I.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DiSkx4UB.js → index-BQ3DXBtD.js} +3 -3
  104. package/src/assets/web-panel/assets/{index-dIHr4B_u.js → index-Bad4VNvz.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CIG8fQ2_.js → index-BgiXR_-_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DqBgBpgp.js → index-BtobShro.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-ZxO_-E8a.js → index-Bw2MEnUk.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DUUrv141.js → index-C1mJtE6q.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bm7lLR4c.js → index-C484EJiS.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-COdGA-8n.js → index-C5KAQFPQ.js} +1 -1
  111. package/src/assets/web-panel/assets/index-C61cnl3K.js +1 -0
  112. package/src/assets/web-panel/assets/{index-CED2PL_y.js → index-C7Y8Aluc.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-YXgLng0K.js → index-CCsM_Ybx.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-0-agYlWL.js → index-CJCFe2Qf.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-B5UkyA3c.js → index-CJVEOa4i.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-B1CbOH1i.js → index-CJdQrZth.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-s1opAL-M.js → index-CKC1t3Ic.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-yObj1O4n.js → index-CQhAVU5t.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CgsqdnRV.js → index-CYdoQsa1.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Dk7-v4hP.js → index-Cduu2Hhr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-B-JVTrpr.js → index-CkwiSLhU.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CeWoNHau.js → index-Cvyt-zhm.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-CE2VtTlp.js → index-D2GjI2oe.js} +1 -1
  124. package/src/assets/web-panel/assets/index-D4XFHxBr.js +1 -0
  125. package/src/assets/web-panel/assets/{index-DmbfhJUP.js → index-DARsAyUp.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BESLFt4q.js → index-DVKfQ0Ux.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-PT_5sBMA.js → index-DZT36ZYq.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-gyFpvL7l.js → index-D_XBHopw.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-BYUVuwKt.js → index-DdCB70SS.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BcUXAB2s.js → index-DzRjHxEv.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Dk2qouAz.js → index-MMN9V6JF.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D9evtvg2.js → index-RfMr79MW.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BuLQobzW.js → index-_IRVsV9X.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-Cf3WCLmy.js → index-nXyk8GZ9.js} +1 -1
  135. package/src/assets/web-panel/assets/{index-CG3PRyAL.js → index-s_buFMEG.js} +1 -1
  136. package/src/assets/web-panel/assets/{initDefaultProps-C9h9yKL3.js → initDefaultProps-Blmpk7HL.js} +1 -1
  137. package/src/assets/web-panel/assets/{motion-BvmLsPdu.js → motion-DPBJMv1n.js} +1 -1
  138. package/src/assets/web-panel/assets/{move-CoL4ekU1.js → move-ButdzCM7.js} +1 -1
  139. package/src/assets/web-panel/assets/{mtc-parser-C4pMy6xV.js → mtc-parser-cyRC_QCe.js} +1 -1
  140. package/src/assets/web-panel/assets/{omit-DPuJfNxx.js → omit-1epSV7hz.js} +1 -1
  141. package/src/assets/web-panel/assets/{pickAttrs-D0KEeaK2.js → pickAttrs-DuPY8xJs.js} +1 -1
  142. package/src/assets/web-panel/assets/{placementArrow-BpkyINxt.js → placementArrow-6XZqarhe.js} +1 -1
  143. package/src/assets/web-panel/assets/{responsiveObserve-DQI_wOq8.js → responsiveObserve-B40pXBWM.js} +1 -1
  144. package/src/assets/web-panel/assets/{slide-DYfdDiRg.js → slide-jbZ9Wu4n.js} +1 -1
  145. package/src/assets/web-panel/assets/{statusUtils-B5dtvb0D.js → statusUtils-BKQQcVy4.js} +1 -1
  146. package/src/assets/web-panel/assets/{styleChecker-CF310mSj.js → styleChecker-NiKfWjUT.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFlexGapSupport-D1Qp4BLp.js → useFlexGapSupport-bQUCvPeb.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFs-C7_iyvJK.js → useFs-CUGji7ph.js} +1 -1
  149. package/src/assets/web-panel/assets/{usePersonalDataHub-DZUt_fIm.js → usePersonalDataHub-4veyN2N2.js} +1 -1
  150. package/src/assets/web-panel/assets/{vnode-D3dsi0lZ.js → vnode-1-nLug-Z.js} +1 -1
  151. package/src/assets/web-panel/assets/{zoom-WdbOlvdQ.js → zoom-dhCeRDMo.js} +1 -1
  152. package/src/assets/web-panel/index.html +1 -1
  153. package/src/command-help-index.json +6 -6
  154. package/src/commands/automation.js +103 -1
  155. package/src/commands/cowork.js +174 -19
  156. package/src/commands/permissions.js +240 -16
  157. package/src/commands/plugin.js +1293 -56
  158. package/src/commands/session-location.js +276 -0
  159. package/src/commands/session.js +2 -0
  160. package/src/commands/team-merge-review.js +937 -0
  161. package/src/commands/team.js +2 -0
  162. package/src/data/changelog.json +27 -1
  163. package/src/gateways/ws/action-protocol.js +35 -10
  164. package/src/gateways/ws/remote-session-protocol.js +24 -1
  165. package/src/gateways/ws/ws-agent-handler.js +12 -0
  166. package/src/gateways/ws/ws-server.js +48 -12
  167. package/src/harness/jsonl-session-store.js +341 -24
  168. package/src/harness/mcp-client.js +8 -1
  169. package/src/harness/remote-session-audit.js +1 -0
  170. package/src/harness/remote-session-crypto.js +210 -2
  171. package/src/harness/remote-session-registry.js +159 -6
  172. package/src/harness/session-list-index.js +233 -14
  173. package/src/lib/agent-authority.js +6 -0
  174. package/src/lib/agent-schedule-store.js +139 -28
  175. package/src/lib/agent-team/team-merge-review-store.js +765 -0
  176. package/src/lib/agent-team/team-merge-review-transaction.js +1223 -0
  177. package/src/lib/agent-team/team-merge-review.js +1847 -0
  178. package/src/lib/automation-center-incidents.js +314 -0
  179. package/src/lib/automation-center-runtime.js +448 -0
  180. package/src/lib/automation-center.js +182 -38
  181. package/src/lib/automation-engine.js +41 -0
  182. package/src/lib/automation-execution-authority.js +241 -12
  183. package/src/lib/automation-execution-incident.js +737 -0
  184. package/src/lib/background-agent-keeper-client.js +248 -0
  185. package/src/lib/background-agent-keeper-protocol.js +147 -0
  186. package/src/lib/background-agent-supervisor.js +520 -40
  187. package/src/lib/background-interaction-resolver.js +12 -0
  188. package/src/lib/background-turn-bootstrap-protocol.js +126 -0
  189. package/src/lib/cowork-cron.js +85 -14
  190. package/src/lib/cowork-workflow.js +336 -16
  191. package/src/lib/dynamic-workflow-facade.js +648 -0
  192. package/src/lib/execution-location-contract.js +551 -0
  193. package/src/lib/execution-location-runtime.js +130 -0
  194. package/src/lib/ide-context.js +94 -6
  195. package/src/lib/mcp-stdio-package-materialization.js +166 -14
  196. package/src/lib/packer/pkg-config-generator.js +2 -0
  197. package/src/lib/permission-authority.js +77 -0
  198. package/src/lib/permission-side-effect-center.js +495 -0
  199. package/src/lib/plugin-runtime/install.js +514 -13
  200. package/src/lib/plugin-runtime/marketplace-artifact-readback.js +900 -0
  201. package/src/lib/plugin-runtime/marketplace-catalog.js +1181 -0
  202. package/src/lib/plugin-runtime/marketplace-impact.js +408 -0
  203. package/src/lib/plugin-runtime/marketplace-remote-artifacts.js +1567 -0
  204. package/src/lib/plugin-security.js +60 -3
  205. package/src/lib/process-execution-broker/index.js +1095 -57
  206. package/src/lib/process-execution-broker/linux-bwrap-descriptor-launch.js +375 -0
  207. package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +90 -16
  208. package/src/lib/process-execution-broker/linux-generic-bwrap.js +159 -18
  209. package/src/lib/process-execution-broker/macos-mcp-launcher-contract.js +278 -0
  210. package/src/lib/process-execution-broker/macos-mcp-launcher-protocol.json +23 -0
  211. package/src/lib/process-execution-broker/macos-mcp-launcher.c +1497 -0
  212. package/src/lib/process-execution-broker/mcp-fd-entry-bootstrap.js +37 -0
  213. package/src/lib/process-execution-broker/platform-sandbox.js +2330 -279
  214. package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
  215. package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
  216. package/src/lib/process-execution-broker/windows-sandbox.cs +50 -4
  217. package/src/lib/public-ide-capability-manifest.js +3 -0
  218. package/src/lib/remote-approval-bridge.js +123 -17
  219. package/src/lib/remote-membership-authority.js +632 -0
  220. package/src/lib/remote-membership-coordinator.js +2409 -0
  221. package/src/lib/remote-membership-host-store.js +1750 -0
  222. package/src/lib/scheduler-kernel/agenda-adapter.js +17 -7
  223. package/src/lib/scheduler-kernel/automation-adapter.js +105 -11
  224. package/src/lib/scheduler-kernel/automation-event-adapter.js +87 -11
  225. package/src/lib/scheduler-kernel/contract.js +91 -0
  226. package/src/lib/scheduler-kernel/cowork-cron-adapter.js +12 -1
  227. package/src/lib/scheduler-kernel/loop-adapter.js +7 -1
  228. package/src/lib/scheduler-kernel/routine-adapter.js +7 -1
  229. package/src/lib/scheduler-kernel/runtime-control-capabilities.js +31 -0
  230. package/src/lib/scheduler-kernel/runtime.js +176 -3
  231. package/src/lib/scheduler-kernel/store.js +1253 -45
  232. package/src/lib/scoped-permission-store.js +369 -0
  233. package/src/lib/session-anti-rollback-anchor.js +262 -5
  234. package/src/lib/sub-agent-context.js +4 -0
  235. package/src/lib/workflow-definition-contract.js +149 -0
  236. package/src/repl/agent-repl.js +41 -7
  237. package/src/repl/chat-repl.js +2 -0
  238. package/src/repl/clipboard-image.js +735 -16
  239. package/src/repl/ide-status.js +1 -1
  240. package/src/repl/prompt-interactions.js +59 -6
  241. package/src/runtime/agent-core.js +93 -3
  242. package/src/runtime/headless-runner.js +30 -25
  243. package/src/runtime/headless-stream.js +32 -25
  244. package/src/runtime/mcp-config.js +81 -10
  245. package/src/workers/background-agent-keeper.js +498 -0
  246. package/src/workers/background-agent-worker.js +358 -13
  247. package/src/workers/background-turn-bootstrap.js +143 -0
  248. package/src/assets/web-panel/assets/MobileProjects-BWdOvQAj.js +0 -1
  249. package/src/assets/web-panel/assets/Projects-BV4oApsm.js +0 -1
  250. package/src/assets/web-panel/assets/devWarning-DPty0mGN.js +0 -1
  251. package/src/assets/web-panel/assets/index-Bt7g0Vq_.js +0 -1
  252. package/src/assets/web-panel/assets/index-ChVT0p9u.js +0 -1
@@ -36,16 +36,32 @@ const require = createRequire(import.meta.url);
36
36
  // platform-sandbox.js exports: applySandbox, postSpawnSandbox
37
37
  import {
38
38
  applySandbox as _applySandbox,
39
+ consumeMacMcpCodeSnapshotPlanBinding,
39
40
  consumeWindowsMcpCodeSnapshotPlanBinding,
40
41
  postSpawnSandbox as _postSpawnSandbox,
41
42
  MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256,
43
+ MCP_STDIO_MACOS_GATED_ENTRY_BOOTSTRAP_SHA256,
44
+ MACOS_PKG_EXECPATH_MAGIC,
42
45
  SANDBOX_BOUNDARIES,
43
46
  } from "./platform-sandbox.js";
47
+ import {
48
+ MACOS_MCP_LAUNCHER_INPUTS,
49
+ isMacosMcpLauncherPackageVersion,
50
+ } from "./macos-mcp-launcher-contract.js";
44
51
  import {
45
52
  admitLinuxGenericSandboxExecutionContract,
46
53
  issueLinuxGenericSandboxExecutionContract,
47
54
  LINUX_GENERIC_CONTRACT_KIND,
55
+ verifyLinuxGenericBubblewrapPlan,
48
56
  } from "./linux-generic-bwrap.js";
57
+ import {
58
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_KIND,
59
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_MECHANISM,
60
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_PASSES,
61
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_PATH,
62
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_SCRIPT_SHA256,
63
+ parseLinuxBwrapDescriptorScrubbedLaunch,
64
+ } from "./linux-bwrap-descriptor-launch.js";
49
65
  import { consumeIssuedPluginSandboxExecutionContract } from "../plugin-runtime/bin.js";
50
66
  import runtimeProvenanceLedger from "../runtime-provenance-ledger.js";
51
67
  import { credentialAgent } from "./credential-agent.js";
@@ -65,6 +81,424 @@ const SUPPORTED_SANDBOX_PROFILES = new Set([
65
81
  // Broker-private admission state. Public runtime-probe fields are audit
66
82
  // evidence, not authority to invoke a privileged post-spawn closure.
67
83
  const admittedWindowsMcpCodeSnapshotPlans = new WeakSet();
84
+ const admittedMacMcpCodeSnapshotPlans = new WeakSet();
85
+
86
+ const LINUX_RUNTIME_PATHNAME_POLICY_PREFIX = Object.freeze([
87
+ "--die-with-parent",
88
+ "--new-session",
89
+ "--unshare-user",
90
+ "--disable-userns",
91
+ "--assert-userns-disabled",
92
+ "--unshare-pid",
93
+ "--unshare-ipc",
94
+ "--unshare-net",
95
+ "--unshare-uts",
96
+ "--unshare-cgroup-try",
97
+ "--cap-drop",
98
+ "ALL",
99
+ "--hostname",
100
+ "chainless-sandbox",
101
+ "--clearenv",
102
+ ]);
103
+ const LINUX_RUNTIME_PATHNAME_POLICY_DIRECTORIES = Object.freeze([
104
+ "/opt",
105
+ "/opt/chainless",
106
+ "/opt/chainless/runtime",
107
+ "/opt/chainless/plugin",
108
+ "/home",
109
+ "/home/sandbox",
110
+ "/tmp",
111
+ "/run",
112
+ "/var",
113
+ "/var/tmp",
114
+ "/proc",
115
+ "/dev",
116
+ ]);
117
+ const LINUX_RUNTIME_PATHNAME_POLICY_ENVIRONMENT = Object.freeze([
118
+ ["CHAINLESS_SANDBOXED", "1"],
119
+ ["HOME", "/home/sandbox"],
120
+ ["LANG", "C.UTF-8"],
121
+ ["LC_ALL", "C.UTF-8"],
122
+ ["OPENSSL_CONF", "/dev/null"],
123
+ ["PATH", "/opt/chainless/runtime"],
124
+ ["TMPDIR", "/tmp"],
125
+ ["TZ", "UTC"],
126
+ ]);
127
+
128
+ function denseOwnDataArraySnapshot(value) {
129
+ if (
130
+ !Array.isArray(value) ||
131
+ Object.getPrototypeOf(value) !== Array.prototype
132
+ ) {
133
+ return null;
134
+ }
135
+ const descriptors = Object.getOwnPropertyDescriptors(value);
136
+ const length = descriptors.length?.value;
137
+ if (
138
+ !Number.isSafeInteger(length) ||
139
+ length < 0 ||
140
+ Reflect.ownKeys(descriptors).length !== length + 1
141
+ ) {
142
+ return null;
143
+ }
144
+ const snapshot = [];
145
+ for (let index = 0; index < length; index += 1) {
146
+ const descriptor = descriptors[String(index)];
147
+ if (!descriptor || !("value" in descriptor)) return null;
148
+ snapshot.push(descriptor.value);
149
+ }
150
+ return Object.freeze(snapshot);
151
+ }
152
+
153
+ function exactOwnDataDescriptors(value, expectedKeys) {
154
+ if (
155
+ !value ||
156
+ typeof value !== "object" ||
157
+ Array.isArray(value) ||
158
+ Object.getPrototypeOf(value) !== Object.prototype
159
+ ) {
160
+ return null;
161
+ }
162
+ const descriptors = Object.getOwnPropertyDescriptors(value);
163
+ const keys = Reflect.ownKeys(descriptors);
164
+ if (
165
+ keys.some((key) => typeof key !== "string") ||
166
+ keys.length !== expectedKeys.length ||
167
+ expectedKeys.some((key) => !("value" in (descriptors[key] || {})))
168
+ ) {
169
+ return null;
170
+ }
171
+ return descriptors;
172
+ }
173
+
174
+ function ownDataObjectSnapshot(value) {
175
+ if (
176
+ !value ||
177
+ typeof value !== "object" ||
178
+ Array.isArray(value) ||
179
+ Object.getPrototypeOf(value) !== Object.prototype
180
+ ) {
181
+ return null;
182
+ }
183
+ const descriptors = Object.getOwnPropertyDescriptors(value);
184
+ const snapshot = {};
185
+ for (const key of Reflect.ownKeys(descriptors)) {
186
+ if (typeof key !== "string" || !("value" in descriptors[key])) {
187
+ return null;
188
+ }
189
+ Object.defineProperty(snapshot, key, {
190
+ value: descriptors[key].value,
191
+ enumerable: true,
192
+ configurable: true,
193
+ writable: true,
194
+ });
195
+ }
196
+ return Object.freeze(snapshot);
197
+ }
198
+
199
+ const LINUX_DESCRIPTOR_SCRUBBER_EVIDENCE_KEYS = Object.freeze([
200
+ "kind",
201
+ "mechanism",
202
+ "scriptSha256",
203
+ "executableIdentity",
204
+ "executablePinned",
205
+ "argvFixed",
206
+ "callerEnvironmentFixed",
207
+ "nodeRuntimeEnvironmentInjection",
208
+ "nodeIpcChildFd",
209
+ "nodeIpcSerializationMode",
210
+ "procSelfFdPasses",
211
+ "closesUnknownInheritedDescriptors",
212
+ "verificationPassesFailClosed",
213
+ "policyBound",
214
+ "scrubberChildFd",
215
+ "preservedMaxFd",
216
+ "activeStdioThrough",
217
+ "executableChildFd",
218
+ ]);
219
+ const LINUX_DESCRIPTOR_SCRUBBER_IDENTITY_KEYS = Object.freeze([
220
+ "path",
221
+ "fileId",
222
+ "sha256",
223
+ "bytes",
224
+ "mtimeMs",
225
+ "mode",
226
+ "uid",
227
+ "gid",
228
+ ]);
229
+
230
+ function sanitizeLinuxDescriptorScrubberEvidence(value, launch) {
231
+ const descriptors = exactOwnDataDescriptors(
232
+ value,
233
+ LINUX_DESCRIPTOR_SCRUBBER_EVIDENCE_KEYS,
234
+ );
235
+ const identity = descriptors?.executableIdentity?.value;
236
+ const identityDescriptors = exactOwnDataDescriptors(
237
+ identity,
238
+ LINUX_DESCRIPTOR_SCRUBBER_IDENTITY_KEYS,
239
+ );
240
+ const fileId = identityDescriptors?.fileId?.value;
241
+ const fileIdDescriptors = exactOwnDataDescriptors(fileId, ["dev", "ino"]);
242
+ const nodeIpcChildFd = launch?.nodeIpcChildFd ?? null;
243
+ const expectedInjection =
244
+ nodeIpcChildFd === null ? "none" : "node-child-process-exact-ipc-v1";
245
+ const expectedSerialization = nodeIpcChildFd === null ? null : "json";
246
+ if (
247
+ !descriptors ||
248
+ !launch ||
249
+ !identityDescriptors ||
250
+ !fileIdDescriptors ||
251
+ descriptors.kind.value !== LINUX_BWRAP_DESCRIPTOR_SCRUBBER_KIND ||
252
+ descriptors.mechanism.value !== LINUX_BWRAP_DESCRIPTOR_SCRUBBER_MECHANISM ||
253
+ descriptors.scriptSha256.value !==
254
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_SCRIPT_SHA256 ||
255
+ descriptors.executablePinned.value !== true ||
256
+ descriptors.argvFixed.value !== true ||
257
+ descriptors.callerEnvironmentFixed.value !== true ||
258
+ descriptors.nodeRuntimeEnvironmentInjection.value !== expectedInjection ||
259
+ descriptors.nodeIpcChildFd.value !== nodeIpcChildFd ||
260
+ descriptors.nodeIpcSerializationMode.value !== expectedSerialization ||
261
+ descriptors.procSelfFdPasses.value !==
262
+ LINUX_BWRAP_DESCRIPTOR_SCRUBBER_PASSES ||
263
+ descriptors.closesUnknownInheritedDescriptors.value !== true ||
264
+ descriptors.verificationPassesFailClosed.value !== true ||
265
+ descriptors.policyBound.value !== true ||
266
+ descriptors.scrubberChildFd.value !== launch.scrubberChildFd ||
267
+ descriptors.preservedMaxFd.value !== launch.preservedMaxFd ||
268
+ descriptors.activeStdioThrough.value !== launch.activeStdioThrough ||
269
+ descriptors.executableChildFd.value !== launch.executableChildFd ||
270
+ identityDescriptors.path.value !== LINUX_BWRAP_DESCRIPTOR_SCRUBBER_PATH ||
271
+ typeof fileIdDescriptors.dev.value !== "string" ||
272
+ !/^(0|[1-9]\d*)$/.test(fileIdDescriptors.dev.value) ||
273
+ typeof fileIdDescriptors.ino.value !== "string" ||
274
+ !/^(0|[1-9]\d*)$/.test(fileIdDescriptors.ino.value) ||
275
+ typeof identityDescriptors.sha256.value !== "string" ||
276
+ !/^[a-f0-9]{64}$/.test(identityDescriptors.sha256.value) ||
277
+ !Number.isSafeInteger(identityDescriptors.bytes.value) ||
278
+ identityDescriptors.bytes.value <= 0 ||
279
+ identityDescriptors.bytes.value > 256 * 1024 * 1024 ||
280
+ !Number.isFinite(identityDescriptors.mtimeMs.value) ||
281
+ !Number.isSafeInteger(identityDescriptors.mode.value) ||
282
+ (identityDescriptors.mode.value & 0o170000) !== 0o100000 ||
283
+ (identityDescriptors.mode.value & 0o111) === 0 ||
284
+ (identityDescriptors.mode.value & 0o022) !== 0 ||
285
+ identityDescriptors.uid.value !== 0 ||
286
+ !Number.isSafeInteger(identityDescriptors.gid.value) ||
287
+ identityDescriptors.gid.value < 0
288
+ ) {
289
+ return null;
290
+ }
291
+ const sanitizedIdentity = Object.freeze({
292
+ path: identityDescriptors.path.value,
293
+ fileId: Object.freeze({
294
+ dev: fileIdDescriptors.dev.value,
295
+ ino: fileIdDescriptors.ino.value,
296
+ }),
297
+ sha256: identityDescriptors.sha256.value,
298
+ bytes: identityDescriptors.bytes.value,
299
+ mtimeMs: identityDescriptors.mtimeMs.value,
300
+ mode: identityDescriptors.mode.value,
301
+ uid: identityDescriptors.uid.value,
302
+ gid: identityDescriptors.gid.value,
303
+ });
304
+ return Object.freeze({
305
+ kind: descriptors.kind.value,
306
+ mechanism: descriptors.mechanism.value,
307
+ scriptSha256: descriptors.scriptSha256.value,
308
+ executableIdentity: sanitizedIdentity,
309
+ executablePinned: true,
310
+ argvFixed: true,
311
+ callerEnvironmentFixed: true,
312
+ nodeRuntimeEnvironmentInjection: expectedInjection,
313
+ nodeIpcChildFd,
314
+ nodeIpcSerializationMode: expectedSerialization,
315
+ procSelfFdPasses: LINUX_BWRAP_DESCRIPTOR_SCRUBBER_PASSES,
316
+ closesUnknownInheritedDescriptors: true,
317
+ verificationPassesFailClosed: true,
318
+ policyBound: true,
319
+ scrubberChildFd: launch.scrubberChildFd,
320
+ preservedMaxFd: launch.preservedMaxFd,
321
+ activeStdioThrough: launch.activeStdioThrough,
322
+ executableChildFd: launch.executableChildFd,
323
+ });
324
+ }
325
+
326
+ function parseLinuxRuntimePathnameClosurePolicyArgs(
327
+ args,
328
+ expectedLoadSetFiles,
329
+ ) {
330
+ if (
331
+ !Array.isArray(args) ||
332
+ !Number.isSafeInteger(expectedLoadSetFiles) ||
333
+ expectedLoadSetFiles < 1 ||
334
+ expectedLoadSetFiles > 512
335
+ ) {
336
+ return null;
337
+ }
338
+ const targetSeparator = args.indexOf("--");
339
+ if (targetSeparator < 1) return null;
340
+ const policyArgs = args.slice(0, targetSeparator);
341
+ let index = 0;
342
+ const consume = (...expected) => {
343
+ if (
344
+ expected.some(
345
+ (expectedValue, offset) => policyArgs[index + offset] !== expectedValue,
346
+ )
347
+ ) {
348
+ return false;
349
+ }
350
+ index += expected.length;
351
+ return true;
352
+ };
353
+ if (!consume(...LINUX_RUNTIME_PATHNAME_POLICY_PREFIX)) return null;
354
+
355
+ const directories = [];
356
+ while (policyArgs[index] === "--dir") {
357
+ const directory = policyArgs[index + 1];
358
+ if (
359
+ typeof directory !== "string" ||
360
+ !path.posix.isAbsolute(directory) ||
361
+ path.posix.normalize(directory) !== directory ||
362
+ directory === "/"
363
+ ) {
364
+ return null;
365
+ }
366
+ directories.push(directory);
367
+ index += 2;
368
+ }
369
+ const orderedDirectories = [...directories].sort((left, right) => {
370
+ const depth =
371
+ left.split("/").filter(Boolean).length -
372
+ right.split("/").filter(Boolean).length;
373
+ return depth || left.localeCompare(right);
374
+ });
375
+ if (
376
+ directories.length < LINUX_RUNTIME_PATHNAME_POLICY_DIRECTORIES.length ||
377
+ new Set(directories).size !== directories.length ||
378
+ JSON.stringify(directories) !== JSON.stringify(orderedDirectories) ||
379
+ !LINUX_RUNTIME_PATHNAME_POLICY_DIRECTORIES.every((directory) =>
380
+ directories.includes(directory),
381
+ )
382
+ ) {
383
+ return null;
384
+ }
385
+
386
+ if (
387
+ !consume(
388
+ "--perms",
389
+ "0000",
390
+ "--file",
391
+ "3",
392
+ "/run/.chainless-bwrap-supervisor",
393
+ )
394
+ ) {
395
+ return null;
396
+ }
397
+ const permittedLoadDestination = (destination) =>
398
+ typeof destination === "string" &&
399
+ path.posix.isAbsolute(destination) &&
400
+ path.posix.normalize(destination) === destination &&
401
+ (destination === "/opt/chainless/runtime/node" ||
402
+ destination === "/etc/ld.so.cache" ||
403
+ destination.startsWith("/opt/chainless/plugin/") ||
404
+ ["/lib/", "/lib64/", "/usr/lib/", "/usr/lib64/"].some((prefix) =>
405
+ destination.startsWith(prefix),
406
+ ));
407
+ const loadDestinations = [];
408
+ for (let mountIndex = 0; mountIndex < expectedLoadSetFiles; mountIndex += 1) {
409
+ const expectedChildFd = String(4 + mountIndex);
410
+ let destination;
411
+ let mountMode;
412
+ let permissions = null;
413
+ if (policyArgs[index] === "--ro-bind-fd") {
414
+ if (policyArgs[index + 1] !== expectedChildFd) return null;
415
+ mountMode = "ro-bind-fd";
416
+ destination = policyArgs[index + 2];
417
+ index += 3;
418
+ } else if (policyArgs[index] === "--perms") {
419
+ permissions = policyArgs[index + 1];
420
+ if (
421
+ (permissions !== "0400" && permissions !== "0500") ||
422
+ policyArgs[index + 2] !== "--ro-bind-data" ||
423
+ policyArgs[index + 3] !== expectedChildFd
424
+ ) {
425
+ return null;
426
+ }
427
+ mountMode = "ro-bind-data";
428
+ destination = policyArgs[index + 4];
429
+ index += 5;
430
+ } else {
431
+ return null;
432
+ }
433
+ if (!permittedLoadDestination(destination)) return null;
434
+ const runtimeSnapshotDestination =
435
+ destination === "/opt/chainless/runtime/node";
436
+ const pluginSnapshotDestination = destination.startsWith(
437
+ "/opt/chainless/plugin/",
438
+ );
439
+ if (
440
+ (runtimeSnapshotDestination &&
441
+ (mountMode !== "ro-bind-data" || permissions !== "0500")) ||
442
+ (pluginSnapshotDestination && mountMode !== "ro-bind-data") ||
443
+ (!runtimeSnapshotDestination &&
444
+ !pluginSnapshotDestination &&
445
+ mountMode !== "ro-bind-fd")
446
+ ) {
447
+ return null;
448
+ }
449
+ loadDestinations.push(destination);
450
+ }
451
+ if (new Set(loadDestinations).size !== loadDestinations.length) return null;
452
+
453
+ const requiredDirectories = new Set(
454
+ LINUX_RUNTIME_PATHNAME_POLICY_DIRECTORIES,
455
+ );
456
+ for (const destination of loadDestinations) {
457
+ let parent = path.posix.dirname(destination);
458
+ while (parent !== "/") {
459
+ requiredDirectories.add(parent);
460
+ parent = path.posix.dirname(parent);
461
+ }
462
+ }
463
+ if (
464
+ ![...requiredDirectories].every((directory) =>
465
+ directories.includes(directory),
466
+ ) ||
467
+ directories.some(
468
+ (directory) =>
469
+ !requiredDirectories.has(directory) &&
470
+ !directory.startsWith("/opt/chainless/plugin/"),
471
+ )
472
+ ) {
473
+ return null;
474
+ }
475
+
476
+ if (
477
+ !consume("--seccomp", String(4 + expectedLoadSetFiles)) ||
478
+ !consume("--remount-ro", "/") ||
479
+ !consume(
480
+ "--perms",
481
+ "0755",
482
+ "--size",
483
+ String(16 * 1024 * 1024),
484
+ "--tmpfs",
485
+ "/run",
486
+ "--remount-ro",
487
+ "/run",
488
+ )
489
+ ) {
490
+ return null;
491
+ }
492
+ for (const [key, value] of LINUX_RUNTIME_PATHNAME_POLICY_ENVIRONMENT) {
493
+ if (!consume("--setenv", key, value)) return null;
494
+ }
495
+ if (!consume("--chdir", "/opt/chainless/plugin")) return null;
496
+ if (index !== policyArgs.length) return null;
497
+ return Object.freeze({
498
+ targetSeparator,
499
+ loadDestinations: Object.freeze(loadDestinations),
500
+ });
501
+ }
68
502
 
69
503
  // Keep the broker on the shared CommonJS trace singleton so CommonJS and ESM
70
504
  // consumers observe the same AsyncLocalStorage without loader warnings.
@@ -1078,6 +1512,15 @@ class ProcessExecutionBroker extends EventEmitter {
1078
1512
  "Sandbox adapter returned no spawn plan",
1079
1513
  );
1080
1514
  }
1515
+ const authorityPlan = plan;
1516
+ const planSnapshot = ownDataObjectSnapshot(plan);
1517
+ if (!planSnapshot) {
1518
+ throw this._sandboxError(
1519
+ "invalid_sandbox_plan",
1520
+ "Sandbox spawn plan must use own data properties",
1521
+ );
1522
+ }
1523
+ plan = planSnapshot;
1081
1524
  if (plan.contractVersion !== 1) {
1082
1525
  throw this._sandboxError(
1083
1526
  "invalid_sandbox_plan",
@@ -1090,16 +1533,34 @@ class ProcessExecutionBroker extends EventEmitter {
1090
1533
  "Sandbox spawn plan is missing applied:boolean",
1091
1534
  );
1092
1535
  }
1093
- if (typeof plan.command !== "string" || !Array.isArray(plan.args)) {
1536
+ let planCommand = null;
1537
+ try {
1538
+ const commandDescriptor = Object.getOwnPropertyDescriptor(
1539
+ plan,
1540
+ "command",
1541
+ );
1542
+ if (
1543
+ commandDescriptor &&
1544
+ "value" in commandDescriptor &&
1545
+ typeof commandDescriptor.value === "string"
1546
+ ) {
1547
+ planCommand = commandDescriptor.value;
1548
+ }
1549
+ } catch {
1550
+ planCommand = null;
1551
+ }
1552
+ const planArgsSnapshot = denseOwnDataArraySnapshot(plan.args);
1553
+ if (planCommand === null || !planArgsSnapshot) {
1094
1554
  throw this._sandboxError(
1095
1555
  "invalid_sandbox_plan",
1096
- "Sandbox spawn plan must provide command and args",
1556
+ "Sandbox spawn plan must provide command and dense data-property args",
1097
1557
  );
1098
1558
  }
1099
- if (!plan.options || typeof plan.options !== "object") {
1559
+ const planOptionsSnapshot = ownDataObjectSnapshot(plan.options);
1560
+ if (!planOptionsSnapshot) {
1100
1561
  throw this._sandboxError(
1101
1562
  "invalid_sandbox_plan",
1102
- "Sandbox spawn plan must provide options",
1563
+ "Sandbox spawn plan must provide data-property options",
1103
1564
  );
1104
1565
  }
1105
1566
  if (plan.applied && typeof plan.enforcement !== "string") {
@@ -1159,6 +1620,129 @@ class ProcessExecutionBroker extends EventEmitter {
1159
1620
  "Sandbox policy digest must be a lowercase SHA-256 value",
1160
1621
  );
1161
1622
  }
1623
+ const linuxDescriptorBackend =
1624
+ plan.applied === true &&
1625
+ (backend === "linux-bwrap" || backend === "linux-bwrap-workspace");
1626
+ const linuxFdCodeSnapshotBackend =
1627
+ plan.applied === true &&
1628
+ (backend === "linux-fd-code-snapshot" ||
1629
+ plan.enforcement === "linux-fd-code-snapshot");
1630
+ let linuxDescriptorLaunch = null;
1631
+ let linuxDescriptorPtyLaunch = false;
1632
+ let linuxBubblewrapExecutableArgs = planArgsSnapshot;
1633
+ let sanitizedDescriptorScrubber = null;
1634
+ if (linuxFdCodeSnapshotBackend) {
1635
+ if (plan.platform !== "linux") {
1636
+ throw this._sandboxError(
1637
+ "invalid_sandbox_plan",
1638
+ "Reserved Linux FD code snapshot backend requires platform linux",
1639
+ );
1640
+ }
1641
+ if (
1642
+ plan.backend !== "linux-fd-code-snapshot" ||
1643
+ plan.enforcement !== "linux-fd-code-snapshot"
1644
+ ) {
1645
+ throw this._sandboxError(
1646
+ "invalid_sandbox_plan",
1647
+ "Reserved Linux FD code snapshot backend requires exact backend and enforcement names",
1648
+ );
1649
+ }
1650
+ if (launchContext.builtInSandboxAdapter !== true) {
1651
+ throw this._sandboxError(
1652
+ "invalid_sandbox_plan",
1653
+ "Applied Linux FD code snapshot plans require the built-in sandbox adapter",
1654
+ );
1655
+ }
1656
+ if (!guarantees.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT)) {
1657
+ throw this._sandboxError(
1658
+ "invalid_sandbox_plan",
1659
+ "Reserved Linux FD code snapshot backend requires the code-snapshot guarantee",
1660
+ );
1661
+ }
1662
+ }
1663
+ if (linuxDescriptorBackend) {
1664
+ if (plan.platform !== "linux") {
1665
+ throw this._sandboxError(
1666
+ "invalid_sandbox_plan",
1667
+ "Reserved Linux bubblewrap backends require platform linux",
1668
+ );
1669
+ }
1670
+ if (launchContext.builtInSandboxAdapter !== true) {
1671
+ throw this._sandboxError(
1672
+ "invalid_sandbox_plan",
1673
+ "Applied Linux bubblewrap plans require the built-in sandbox adapter",
1674
+ );
1675
+ }
1676
+ const descriptorEvidence = plan.runtimeProbe?.descriptorScrubber;
1677
+ const descriptorEvidenceDescriptors = exactOwnDataDescriptors(
1678
+ descriptorEvidence,
1679
+ LINUX_DESCRIPTOR_SCRUBBER_EVIDENCE_KEYS,
1680
+ );
1681
+ const activeStdioThrough =
1682
+ backend === "linux-bwrap-workspace"
1683
+ ? descriptorEvidenceDescriptors?.activeStdioThrough?.value
1684
+ : 2;
1685
+ linuxDescriptorPtyLaunch =
1686
+ backend === "linux-bwrap-workspace" &&
1687
+ plan.ptyPolicy !== null &&
1688
+ plan.ptyPolicy !== undefined;
1689
+ if (linuxDescriptorPtyLaunch) {
1690
+ const innerArgs = Object.freeze([...planArgsSnapshot.slice(2)]);
1691
+ linuxDescriptorLaunch =
1692
+ planArgsSnapshot[0] === "--ctty" &&
1693
+ typeof planArgsSnapshot[1] === "string"
1694
+ ? parseLinuxBwrapDescriptorScrubbedLaunch(
1695
+ planArgsSnapshot[1],
1696
+ innerArgs,
1697
+ planOptionsSnapshot,
1698
+ { activeStdioThrough },
1699
+ )
1700
+ : null;
1701
+ } else {
1702
+ linuxDescriptorLaunch = parseLinuxBwrapDescriptorScrubbedLaunch(
1703
+ planCommand,
1704
+ planArgsSnapshot,
1705
+ planOptionsSnapshot,
1706
+ { activeStdioThrough },
1707
+ );
1708
+ }
1709
+ sanitizedDescriptorScrubber = sanitizeLinuxDescriptorScrubberEvidence(
1710
+ descriptorEvidence,
1711
+ linuxDescriptorLaunch,
1712
+ );
1713
+ const expectedExecutableChildFd = linuxDescriptorPtyLaunch
1714
+ ? activeStdioThrough + 2
1715
+ : backend === "linux-bwrap-workspace"
1716
+ ? activeStdioThrough + 1
1717
+ : 3;
1718
+ const outerPtyDescriptorValid =
1719
+ !linuxDescriptorPtyLaunch ||
1720
+ (planCommand === `/proc/self/fd/${activeStdioThrough + 1}` &&
1721
+ Number.isSafeInteger(
1722
+ linuxDescriptorLaunch?.stdio?.[activeStdioThrough + 1],
1723
+ ));
1724
+ if (
1725
+ !linuxDescriptorLaunch ||
1726
+ !sanitizedDescriptorScrubber ||
1727
+ linuxDescriptorLaunch.executableChildFd !== expectedExecutableChildFd ||
1728
+ !outerPtyDescriptorValid ||
1729
+ (backend === "linux-bwrap" &&
1730
+ (linuxDescriptorLaunch.activeStdioThrough !== 2 ||
1731
+ linuxDescriptorLaunch.nodeIpcChildFd !== null ||
1732
+ (plan.ptyPolicy !== null && plan.ptyPolicy !== undefined)))
1733
+ ) {
1734
+ throw this._sandboxError(
1735
+ "invalid_sandbox_plan",
1736
+ "Applied Linux bubblewrap plans require the exact typed descriptor scrubber launch contract",
1737
+ );
1738
+ }
1739
+ linuxBubblewrapExecutableArgs = linuxDescriptorLaunch.executableArgs;
1740
+ } else if (plan.runtimeProbe?.descriptorScrubber !== undefined) {
1741
+ throw this._sandboxError(
1742
+ "invalid_sandbox_plan",
1743
+ "Linux descriptor scrubber evidence requires an applied bubblewrap backend",
1744
+ );
1745
+ }
1162
1746
  let runtimeProbe = null;
1163
1747
  let genericWorkspaceProbe = false;
1164
1748
  let sanitizedFilesystemPolicy = null;
@@ -1166,6 +1750,7 @@ class ProcessExecutionBroker extends EventEmitter {
1166
1750
  let sanitizedProcessTreePolicy = null;
1167
1751
  let sanitizedPtyPolicy = null;
1168
1752
  let windowsPlanBindingConsumed = false;
1753
+ let macPlanBindingConsumed = false;
1169
1754
  if (plan.runtimeProbe !== null && plan.runtimeProbe !== undefined) {
1170
1755
  if (
1171
1756
  typeof plan.runtimeProbe !== "object" ||
@@ -1196,6 +1781,14 @@ class ProcessExecutionBroker extends EventEmitter {
1196
1781
  "initialDynamicLoadClosureScope",
1197
1782
  "initialDynamicLoadClosureMechanism",
1198
1783
  "initialDynamicInterpreter",
1784
+ "runtimeSharedLibraryPathnameClosureExcludes",
1785
+ "runtimeSharedLibraryClosureScope",
1786
+ "runtimeSharedLibraryClosureMechanism",
1787
+ "targetDescriptorAllowlist",
1788
+ "targetRuntimeInvocationMode",
1789
+ "helperTeamIdentifier",
1790
+ "helperPackageIdentifier",
1791
+ "helperPackageVersion",
1199
1792
  ]) {
1200
1793
  if (
1201
1794
  plan.runtimeProbe[field] !== undefined &&
@@ -1220,6 +1813,35 @@ class ProcessExecutionBroker extends EventEmitter {
1220
1813
  "runtimeLaunchAtomic",
1221
1814
  "mcpCapsuleCodeSnapshot",
1222
1815
  "runtimeDetachedChildSpawnVerified",
1816
+ "runtimeSharedLibraryPathnameClosure",
1817
+ "runtimeLoadSetPolicyBound",
1818
+ "runtimeWritableFilesystems",
1819
+ "runtimeProcfsMounted",
1820
+ "runtimeDevfsMounted",
1821
+ "runtimeScratchWritable",
1822
+ "runtimeDescriptorReopenPaths",
1823
+ "rootProtectedRuntimeSnapshot",
1824
+ "entryRootOwnedAnonymousSnapshot",
1825
+ "entrySourcePrePostStat",
1826
+ "entryWriterClosedBeforeReadonlyReopen",
1827
+ "entryReadonlyIdentityRechecked",
1828
+ "entryUnlinkedAndDirectoryFsyncedBeforeTarget",
1829
+ "targetInheritedEntrySnapshotOnly",
1830
+ "runtimeAndCapsuleSlotsNullBeforeExec",
1831
+ "bootstrapClosesNullAndReadyDescriptors",
1832
+ "actualRuntimeReadyHandshake",
1833
+ "runtimeSnapshotUnlinkedBeforeEntryRelease",
1834
+ "callerCredentialDropIrreversible",
1835
+ "relayParentCredentialsDropped",
1836
+ "callerLifelineWatched",
1837
+ "signalRelayNonblocking",
1838
+ "pkgExecPathMagicBound",
1839
+ "capsuleRootDescriptorBound",
1840
+ "capsulePathObjectAtomic",
1841
+ "sandboxProfileFixedAndDigestBound",
1842
+ "processForkExplicitlyDenied",
1843
+ "sandboxExecLiveGateContract",
1844
+ "globalLaunchSerialization",
1223
1845
  ]) {
1224
1846
  if (
1225
1847
  plan.runtimeProbe[field] !== undefined &&
@@ -1236,10 +1858,14 @@ class ProcessExecutionBroker extends EventEmitter {
1236
1858
  "pluginTreeContentSnapshotBytes",
1237
1859
  "initialDynamicDependencyCount",
1238
1860
  "initialDynamicRuntimeFileCount",
1861
+ "initialDynamicRuntimeBytes",
1862
+ "runtimeSharedLibraryLoadSetFiles",
1863
+ "runtimeSharedLibraryLoadSetBytes",
1239
1864
  "runtimeSnapshotBytes",
1240
1865
  "runtimeAttestedBytes",
1241
1866
  "entrySnapshotBytes",
1242
1867
  "capabilityCount",
1868
+ "maximumStaleSnapshots",
1243
1869
  ]) {
1244
1870
  if (
1245
1871
  plan.runtimeProbe[field] !== undefined &&
@@ -1256,6 +1882,13 @@ class ProcessExecutionBroker extends EventEmitter {
1256
1882
  "runtimeSnapshotSha256",
1257
1883
  "runtimeAttestedSha256",
1258
1884
  "entrySnapshotSha256",
1885
+ "entrySnapshotBootstrapSha256",
1886
+ "helperSha256",
1887
+ "helperSourceSha256",
1888
+ "helperProtocolSha256",
1889
+ "helperInstallContractSha256",
1890
+ "helperDesignatedRequirementSha256",
1891
+ "installAttestationDigest",
1259
1892
  "planBindingDigest",
1260
1893
  ]) {
1261
1894
  if (
@@ -1272,17 +1905,22 @@ class ProcessExecutionBroker extends EventEmitter {
1272
1905
  const planBindingDeclared =
1273
1906
  plan.runtimeProbe.planBindingMechanism !== undefined ||
1274
1907
  plan.runtimeProbe.planBindingDigest !== undefined;
1908
+ const typedCodeSnapshotPlanBinding =
1909
+ (plan.platform === "win32" &&
1910
+ plan.runtimeProbe.planBindingMechanism ===
1911
+ "windows-mcp-code-snapshot-plan-binding-v1") ||
1912
+ (plan.platform === "darwin" &&
1913
+ plan.runtimeProbe.planBindingMechanism ===
1914
+ "macos-mcp-code-snapshot-plan-binding-v1");
1275
1915
  if (
1276
1916
  planBindingDeclared &&
1277
- (plan.platform !== "win32" ||
1917
+ (!typedCodeSnapshotPlanBinding ||
1278
1918
  !guarantees.includes(SANDBOX_BOUNDARIES.CODE_SNAPSHOT) ||
1279
- plan.runtimeProbe.planBindingMechanism !==
1280
- "windows-mcp-code-snapshot-plan-binding-v1" ||
1281
1919
  !/^[a-f0-9]{64}$/.test(plan.runtimeProbe.planBindingDigest || ""))
1282
1920
  ) {
1283
1921
  throw this._sandboxError(
1284
1922
  "invalid_sandbox_plan",
1285
- "Sandbox runtime probe plan binding must use the typed Windows MCP code-snapshot contract",
1923
+ "Sandbox runtime probe plan binding must use a typed platform MCP code-snapshot contract",
1286
1924
  );
1287
1925
  }
1288
1926
  if (
@@ -1311,6 +1949,19 @@ class ProcessExecutionBroker extends EventEmitter {
1311
1949
  "Sandbox runtime probe initialDynamicLoadClosureDigest must be a lowercase SHA-256 value",
1312
1950
  );
1313
1951
  }
1952
+ if (
1953
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetDigest !== undefined &&
1954
+ (typeof plan.runtimeProbe.runtimeSharedLibraryLoadSetDigest !==
1955
+ "string" ||
1956
+ !/^[a-f0-9]{64}$/.test(
1957
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetDigest,
1958
+ ))
1959
+ ) {
1960
+ throw this._sandboxError(
1961
+ "invalid_sandbox_plan",
1962
+ "Sandbox runtime probe runtimeSharedLibraryLoadSetDigest must be a lowercase SHA-256 value",
1963
+ );
1964
+ }
1314
1965
  const pluginTreeSnapshot =
1315
1966
  plan.runtimeProbe.pluginTreeContentSnapshot === true;
1316
1967
  const pluginTreeEvidenceFields = [
@@ -1486,6 +2137,8 @@ class ProcessExecutionBroker extends EventEmitter {
1486
2137
  }
1487
2138
  const supervisorBound =
1488
2139
  plan.runtimeProbe.supervisorDescriptorBound === true;
2140
+ const runtimeSharedLibraryPathnameClosure =
2141
+ plan.runtimeProbe.runtimeSharedLibraryPathnameClosure === true;
1489
2142
  if (
1490
2143
  supervisorBound &&
1491
2144
  (plan.runtimeProbe.supervisorExecutablePinned !== true ||
@@ -1494,7 +2147,10 @@ class ProcessExecutionBroker extends EventEmitter {
1494
2147
  "host-path-replacement" ||
1495
2148
  plan.runtimeProbe.supervisorDescriptorBindingMechanism !==
1496
2149
  "pinned-child-fd3-file-consume-run-overmount-v1" ||
1497
- plan.runtimeProbe.supervisorPid1ExecutableExposure !== "procfs")
2150
+ plan.runtimeProbe.supervisorPid1ExecutableExposure !==
2151
+ (runtimeSharedLibraryPathnameClosure
2152
+ ? "procfs-not-mounted"
2153
+ : "procfs"))
1498
2154
  ) {
1499
2155
  throw this._sandboxError(
1500
2156
  "invalid_sandbox_plan",
@@ -1527,6 +2183,7 @@ class ProcessExecutionBroker extends EventEmitter {
1527
2183
  "initialDynamicInterpreter",
1528
2184
  "initialDynamicDependencyCount",
1529
2185
  "initialDynamicRuntimeFileCount",
2186
+ "initialDynamicRuntimeBytes",
1530
2187
  "initialDynamicLoadClosureDigest",
1531
2188
  ];
1532
2189
  const initialDynamicInterpreter =
@@ -1535,6 +2192,8 @@ class ProcessExecutionBroker extends EventEmitter {
1535
2192
  plan.runtimeProbe.initialDynamicDependencyCount;
1536
2193
  const initialDynamicRuntimeFileCount =
1537
2194
  plan.runtimeProbe.initialDynamicRuntimeFileCount;
2195
+ const initialDynamicRuntimeBytes =
2196
+ plan.runtimeProbe.initialDynamicRuntimeBytes;
1538
2197
  const successfulDynamicProbe =
1539
2198
  plan.applied === true &&
1540
2199
  plan.runtimeProbe.kind ===
@@ -1564,11 +2223,12 @@ class ProcessExecutionBroker extends EventEmitter {
1564
2223
  plan.runtimeProbe.contentSnapshotMechanism !==
1565
2224
  "verified-o_tmpfile-copy-bwrap-ro-bind-data-v1" ||
1566
2225
  plan.runtimeProbe.handleAtomic !== false ||
2226
+ plan.runtimeProbe.sharedLibraryClosure !== false ||
1567
2227
  !supervisorBound ||
1568
2228
  plan.runtimeProbe.initialDynamicLoadClosureScope !==
1569
- "initial-pt_interp-and-direct-dt_needed-attested-system-set" ||
2229
+ "initial-pt_interp-and-recursive-dt_needed-attested-system-graph" ||
1570
2230
  plan.runtimeProbe.initialDynamicLoadClosureMechanism !==
1571
- "parsed-elf-direct-system-set-to-attested-node-runtime-fds-v1" ||
2231
+ "recursive-parsed-elf-system-graph-to-attested-runtime-fds-v1" ||
1572
2232
  typeof initialDynamicInterpreter !== "string" ||
1573
2233
  !path.posix.isAbsolute(initialDynamicInterpreter) ||
1574
2234
  path.posix.normalize(initialDynamicInterpreter) !==
@@ -1578,20 +2238,20 @@ class ProcessExecutionBroker extends EventEmitter {
1578
2238
  ) ||
1579
2239
  !Number.isSafeInteger(initialDynamicDependencyCount) ||
1580
2240
  initialDynamicDependencyCount < 0 ||
1581
- initialDynamicDependencyCount > 128 ||
2241
+ initialDynamicDependencyCount > 1024 ||
1582
2242
  !Number.isSafeInteger(initialDynamicRuntimeFileCount) ||
1583
2243
  initialDynamicRuntimeFileCount < 1 ||
1584
- initialDynamicRuntimeFileCount > 129 ||
1585
- initialDynamicRuntimeFileCount <
1586
- Math.max(1, initialDynamicDependencyCount) ||
1587
- initialDynamicRuntimeFileCount > initialDynamicDependencyCount + 1 ||
2244
+ initialDynamicRuntimeFileCount > 256 ||
2245
+ !Number.isSafeInteger(initialDynamicRuntimeBytes) ||
2246
+ initialDynamicRuntimeBytes < 1 ||
2247
+ initialDynamicRuntimeBytes > 512 * 1024 * 1024 ||
1588
2248
  !/^[a-f0-9]{64}$/.test(
1589
2249
  plan.runtimeProbe.initialDynamicLoadClosureDigest || "",
1590
2250
  ))
1591
2251
  ) {
1592
2252
  throw this._sandboxError(
1593
2253
  "invalid_sandbox_plan",
1594
- "Sandbox runtime probe initial dynamic direct system set evidence must use the typed descriptor-bound contract",
2254
+ "Sandbox runtime probe initial recursive dynamic system graph evidence must use the typed descriptor-bound contract",
1595
2255
  );
1596
2256
  }
1597
2257
  if (
@@ -1602,13 +2262,89 @@ class ProcessExecutionBroker extends EventEmitter {
1602
2262
  ) {
1603
2263
  throw this._sandboxError(
1604
2264
  "invalid_sandbox_plan",
1605
- "Sandbox runtime probe initial dynamic direct system set evidence requires initialDynamicLoadClosureDescriptorBound",
2265
+ "Sandbox runtime probe initial recursive dynamic system graph evidence requires initialDynamicLoadClosureDescriptorBound",
1606
2266
  );
1607
2267
  }
1608
2268
  if (successfulDynamicProbe && !initialDynamicLoadClosureBound) {
1609
2269
  throw this._sandboxError(
1610
2270
  "invalid_sandbox_plan",
1611
- "Sandbox runtime probe successful dynamic ELF evidence requires a descriptor-bound initial direct system set",
2271
+ "Sandbox runtime probe successful dynamic ELF evidence requires a descriptor-bound initial recursive system graph",
2272
+ );
2273
+ }
2274
+ const runtimePathnameClosureEvidenceFields = [
2275
+ "runtimeSharedLibraryPathnameClosureExcludes",
2276
+ "runtimeSharedLibraryClosureScope",
2277
+ "runtimeSharedLibraryClosureMechanism",
2278
+ "runtimeSharedLibraryLoadSetFiles",
2279
+ "runtimeSharedLibraryLoadSetBytes",
2280
+ "runtimeSharedLibraryLoadSetDigest",
2281
+ "runtimeLoadSetPolicyBound",
2282
+ "runtimeWritableFilesystems",
2283
+ "runtimeProcfsMounted",
2284
+ "runtimeDevfsMounted",
2285
+ "runtimeScratchWritable",
2286
+ "runtimeDescriptorReopenPaths",
2287
+ ];
2288
+ const runtimePathnameMountPolicy =
2289
+ parseLinuxRuntimePathnameClosurePolicyArgs(
2290
+ linuxBubblewrapExecutableArgs,
2291
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetFiles,
2292
+ ) !== null;
2293
+ if (
2294
+ runtimeSharedLibraryPathnameClosure &&
2295
+ (!successfulDynamicProbe ||
2296
+ !initialDynamicLoadClosureBound ||
2297
+ plan.runtimeProbe.sharedLibraryClosure !== false ||
2298
+ plan.runtimeProbe.runtimeSharedLibraryPathnameClosureExcludes !==
2299
+ "anonymous-jit-and-custom-in-process-loader" ||
2300
+ plan.runtimeProbe.runtimeSharedLibraryClosureScope !==
2301
+ "all-pathname-visible-regular-files-in-read-only-bwrap-namespace" ||
2302
+ plan.runtimeProbe.runtimeSharedLibraryClosureMechanism !==
2303
+ "descriptor-pinned-hashed-ro-mount-set-plus-loader-fd-and-namespace-mutation-seccomp-v2" ||
2304
+ !Number.isSafeInteger(
2305
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetFiles,
2306
+ ) ||
2307
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetFiles < 1 ||
2308
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetFiles > 512 ||
2309
+ !Number.isSafeInteger(
2310
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetBytes,
2311
+ ) ||
2312
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetBytes < 1 ||
2313
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetBytes >
2314
+ 1024 * 1024 * 1024 ||
2315
+ !/^[a-f0-9]{64}$/.test(
2316
+ plan.runtimeProbe.runtimeSharedLibraryLoadSetDigest || "",
2317
+ ) ||
2318
+ plan.runtimeProbe.runtimeLoadSetPolicyBound !== true ||
2319
+ plan.runtimeProbe.runtimeWritableFilesystems !== false ||
2320
+ plan.runtimeProbe.runtimeProcfsMounted !== false ||
2321
+ plan.runtimeProbe.runtimeDevfsMounted !== false ||
2322
+ plan.runtimeProbe.runtimeScratchWritable !== false ||
2323
+ plan.runtimeProbe.runtimeDescriptorReopenPaths !== false ||
2324
+ plan.runtimeProbe.supervisorPid1ExecutableExposure !==
2325
+ "procfs-not-mounted" ||
2326
+ !runtimePathnameMountPolicy)
2327
+ ) {
2328
+ throw this._sandboxError(
2329
+ "invalid_sandbox_plan",
2330
+ "Sandbox runtime probe pathname shared-library closure must use the typed read-only no-proc bwrap contract",
2331
+ );
2332
+ }
2333
+ if (
2334
+ !runtimeSharedLibraryPathnameClosure &&
2335
+ runtimePathnameClosureEvidenceFields.some(
2336
+ (field) => plan.runtimeProbe[field] !== undefined,
2337
+ )
2338
+ ) {
2339
+ throw this._sandboxError(
2340
+ "invalid_sandbox_plan",
2341
+ "Sandbox runtime probe pathname shared-library evidence requires runtimeSharedLibraryPathnameClosure",
2342
+ );
2343
+ }
2344
+ if (successfulDynamicProbe && !runtimeSharedLibraryPathnameClosure) {
2345
+ throw this._sandboxError(
2346
+ "invalid_sandbox_plan",
2347
+ "Successful dynamic ELF evidence requires a runtime pathname shared-library closure",
1612
2348
  );
1613
2349
  }
1614
2350
  const codeSnapshotGuaranteed = guarantees.includes(
@@ -1623,11 +2359,28 @@ class ProcessExecutionBroker extends EventEmitter {
1623
2359
  plan.platform === "win32" &&
1624
2360
  (plan.runtimeProbe.planBindingMechanism !== undefined ||
1625
2361
  plan.runtimeProbe.planBindingDigest !== undefined);
1626
- // Consume before evaluating any other Windows evidence. A genuine
2362
+ const macPlanBindingDeclared =
2363
+ plan.platform === "darwin" &&
2364
+ (plan.runtimeProbe.planBindingMechanism !== undefined ||
2365
+ plan.runtimeProbe.planBindingDigest !== undefined);
2366
+ // Consume before evaluating any other privileged-helper evidence. A genuine
1627
2367
  // one-launch plan is therefore burned by its first admission attempt,
1628
2368
  // including an attempt with a mismatched command or contract.
1629
2369
  windowsPlanBindingConsumed = windowsPlanBindingDeclared
1630
- ? consumeWindowsMcpCodeSnapshotPlanBinding(plan, {
2370
+ ? consumeWindowsMcpCodeSnapshotPlanBinding(authorityPlan, {
2371
+ command: launchContext.command,
2372
+ args: launchArgs,
2373
+ cwd: launchContext.cwd,
2374
+ shell: launchContext.shell,
2375
+ detached: launchContext.detached,
2376
+ executionContract,
2377
+ profile: launchContext.profile,
2378
+ requiredBoundaries: launchContext.requiredBoundaries,
2379
+ sync: launchContext.sync,
2380
+ })
2381
+ : false;
2382
+ macPlanBindingConsumed = macPlanBindingDeclared
2383
+ ? consumeMacMcpCodeSnapshotPlanBinding(authorityPlan, {
1631
2384
  command: launchContext.command,
1632
2385
  args: launchArgs,
1633
2386
  cwd: launchContext.cwd,
@@ -1674,6 +2427,8 @@ class ProcessExecutionBroker extends EventEmitter {
1674
2427
  executionContract?.runtimeIdentity?.bytes &&
1675
2428
  plan.runtimeProbe.runtimeAttestedSha256 === undefined &&
1676
2429
  plan.runtimeProbe.runtimeAttestedBytes === undefined;
2430
+ const macRuntimeSnapshotContractBound =
2431
+ linuxRuntimeSnapshotContractBound;
1677
2432
  const windowsRuntimeAttestationContractBound =
1678
2433
  plan.runtimeProbe.runtimeAttestedSha256 ===
1679
2434
  executionContract?.runtimeIdentity?.sha256 &&
@@ -1712,6 +2467,7 @@ class ProcessExecutionBroker extends EventEmitter {
1712
2467
  plan.runtimeProbe.entrySnapshotBytes >= 0 &&
1713
2468
  plan.runtimeProbe.entrySnapshotBytes <= 64 * 1024 * 1024;
1714
2469
  const linuxSnapshotEvidence =
2470
+ launchContext.builtInSandboxAdapter === true &&
1715
2471
  plan.platform === "linux" &&
1716
2472
  backend === "linux-fd-code-snapshot" &&
1717
2473
  plan.enforcement === "linux-fd-code-snapshot" &&
@@ -1736,16 +2492,18 @@ class ProcessExecutionBroker extends EventEmitter {
1736
2492
  Number.isSafeInteger(plan.runtimeProbe.runtimeSnapshotBytes) &&
1737
2493
  plan.runtimeProbe.runtimeSnapshotBytes > 0 &&
1738
2494
  plan.runtimeProbe.runtimeSnapshotBytes <= 256 * 1024 * 1024 &&
1739
- /^\/proc\/self\/fd\/\d+$/.test(plan.command) &&
1740
- plan.args[0] === "-e" &&
2495
+ /^\/proc\/self\/fd\/\d+$/.test(planCommand) &&
2496
+ planArgsSnapshot[0] === "-e" &&
1741
2497
  crypto
1742
2498
  .createHash("sha256")
1743
- .update(plan.args[1] || "")
2499
+ .update(planArgsSnapshot[1] || "")
1744
2500
  .digest("hex") === MCP_STDIO_FD_ENTRY_BOOTSTRAP_SHA256 &&
1745
- plan.args[2] === "--" &&
1746
- passthroughArgsMatch(plan.args.slice(3));
1747
- const linuxBubblewrapTargetSeparator = plan.args.indexOf("--");
2501
+ planArgsSnapshot[2] === "--" &&
2502
+ passthroughArgsMatch(planArgsSnapshot.slice(3));
2503
+ const linuxBubblewrapTargetSeparator =
2504
+ linuxBubblewrapExecutableArgs.indexOf("--");
1748
2505
  const linuxBubblewrapSnapshotEvidence =
2506
+ launchContext.builtInSandboxAdapter === true &&
1749
2507
  plan.platform === "linux" &&
1750
2508
  backend === "linux-bwrap" &&
1751
2509
  plan.enforcement === "linux-bwrap" &&
@@ -1776,15 +2534,137 @@ class ProcessExecutionBroker extends EventEmitter {
1776
2534
  Number.isSafeInteger(plan.runtimeProbe.runtimeSnapshotBytes) &&
1777
2535
  plan.runtimeProbe.runtimeSnapshotBytes > 0 &&
1778
2536
  plan.runtimeProbe.runtimeSnapshotBytes <= 256 * 1024 * 1024 &&
1779
- /^\/proc\/self\/fd\/\d+$/.test(plan.command) &&
2537
+ /^\/proc\/self\/fd\/\d+$/.test(planCommand) &&
1780
2538
  linuxBubblewrapTargetSeparator >= 0 &&
1781
- plan.args[linuxBubblewrapTargetSeparator + 1] ===
2539
+ linuxBubblewrapExecutableArgs[linuxBubblewrapTargetSeparator + 1] ===
1782
2540
  plan.runtimeProbe.runtimeLaunchPath &&
1783
- plan.args[linuxBubblewrapTargetSeparator + 2] ===
2541
+ linuxBubblewrapExecutableArgs[linuxBubblewrapTargetSeparator + 2] ===
1784
2542
  plan.runtimeProbe.entrySnapshotPath &&
1785
2543
  passthroughArgsMatch(
1786
- plan.args.slice(linuxBubblewrapTargetSeparator + 3),
2544
+ linuxBubblewrapExecutableArgs.slice(
2545
+ linuxBubblewrapTargetSeparator + 3,
2546
+ ),
1787
2547
  );
2548
+ const macProtocol = MACOS_MCP_LAUNCHER_INPUTS.protocol;
2549
+ const macStdio = planOptionsSnapshot.stdio;
2550
+ const macSnapshotPath = path.posix.join(
2551
+ macProtocol.snapshotRoot,
2552
+ planArgsSnapshot[1] || "invalid",
2553
+ "node",
2554
+ );
2555
+ const macSnapshotEvidence =
2556
+ plan.platform === "darwin" &&
2557
+ backend === macProtocol.backend &&
2558
+ plan.enforcement === macProtocol.backend &&
2559
+ policyAttested === true &&
2560
+ policyDigest !== null &&
2561
+ plan.runtimeProbe.kind === "darwin-mcp-capsule-code-snapshot-v2" &&
2562
+ plan.runtimeProbe.contentSnapshotScope ===
2563
+ "mcp-capsule-entry-and-node-runtime" &&
2564
+ plan.runtimeProbe.contentSnapshotMechanism ===
2565
+ "signed-root-runtime-path-and-anonymous-entry-fd-snapshots-v1" &&
2566
+ plan.runtimeProbe.handleAtomic === true &&
2567
+ plan.runtimeProbe.entrySnapshotAtomic === true &&
2568
+ plan.runtimeProbe.runtimeLaunchAtomic === true &&
2569
+ plan.runtimeProbe.runtimeLaunchMechanism ===
2570
+ "signed-root-helper-fd-copy-protected-path-ready-gate-v1" &&
2571
+ plan.runtimeProbe.entrySnapshotBootstrapSha256 ===
2572
+ MCP_STDIO_MACOS_GATED_ENTRY_BOOTSTRAP_SHA256 &&
2573
+ macRuntimeSnapshotContractBound &&
2574
+ plan.runtimeProbe.runtimeSnapshotBytes > 0 &&
2575
+ plan.runtimeProbe.runtimeSnapshotBytes <= 256 * 1024 * 1024 &&
2576
+ plan.runtimeProbe.entrySnapshotBytes <= 64 * 1024 * 1024 &&
2577
+ plan.runtimeProbe.rootProtectedRuntimeSnapshot === true &&
2578
+ plan.runtimeProbe.entryRootOwnedAnonymousSnapshot === true &&
2579
+ plan.runtimeProbe.entrySourcePrePostStat === true &&
2580
+ plan.runtimeProbe.entryWriterClosedBeforeReadonlyReopen === true &&
2581
+ plan.runtimeProbe.entryReadonlyIdentityRechecked === true &&
2582
+ plan.runtimeProbe.entryUnlinkedAndDirectoryFsyncedBeforeTarget ===
2583
+ true &&
2584
+ plan.runtimeProbe.targetInheritedEntrySnapshotOnly === true &&
2585
+ plan.runtimeProbe.runtimeAndCapsuleSlotsNullBeforeExec === true &&
2586
+ plan.runtimeProbe.bootstrapClosesNullAndReadyDescriptors === true &&
2587
+ plan.runtimeProbe.actualRuntimeReadyHandshake === true &&
2588
+ plan.runtimeProbe.runtimeSnapshotUnlinkedBeforeEntryRelease ===
2589
+ true &&
2590
+ plan.runtimeProbe.callerCredentialDropIrreversible === true &&
2591
+ plan.runtimeProbe.relayParentCredentialsDropped === true &&
2592
+ plan.runtimeProbe.callerLifelineWatched === true &&
2593
+ plan.runtimeProbe.signalRelayNonblocking === true &&
2594
+ ((plan.runtimeProbe.targetRuntimeInvocationMode ===
2595
+ "node-executable-eval-v1" &&
2596
+ plan.runtimeProbe.pkgExecPathMagicBound === false) ||
2597
+ (plan.runtimeProbe.targetRuntimeInvocationMode ===
2598
+ "pkg-copied-executable-eval-v1" &&
2599
+ plan.runtimeProbe.pkgExecPathMagicBound === true &&
2600
+ planOptionsSnapshot.env?.PKG_EXECPATH ===
2601
+ MACOS_PKG_EXECPATH_MAGIC)) &&
2602
+ plan.runtimeProbe.targetDescriptorAllowlist ===
2603
+ "stdio-fd3-null-fd4-entry-fd5-null-fd6-gate-fd7-ready" &&
2604
+ plan.runtimeProbe.capsuleRootDescriptorBound === true &&
2605
+ plan.runtimeProbe.capsulePathObjectAtomic === false &&
2606
+ plan.runtimeProbe.sandboxProfileFixedAndDigestBound === true &&
2607
+ plan.runtimeProbe.processForkExplicitlyDenied === true &&
2608
+ plan.runtimeProbe.sandboxExecLiveGateContract === true &&
2609
+ plan.runtimeProbe.globalLaunchSerialization === true &&
2610
+ plan.runtimeProbe.maximumStaleSnapshots === 8 &&
2611
+ plan.runtimeProbe.helperSourceSha256 ===
2612
+ MACOS_MCP_LAUNCHER_INPUTS.sourceSha256 &&
2613
+ plan.runtimeProbe.helperProtocolSha256 ===
2614
+ MACOS_MCP_LAUNCHER_INPUTS.protocolSha256 &&
2615
+ [
2616
+ "helperSha256",
2617
+ "helperInstallContractSha256",
2618
+ "helperDesignatedRequirementSha256",
2619
+ "installAttestationDigest",
2620
+ "planBindingDigest",
2621
+ ].every((field) =>
2622
+ /^[a-f0-9]{64}$/.test(plan.runtimeProbe[field] || ""),
2623
+ ) &&
2624
+ /^[A-Z0-9]{10}$/.test(plan.runtimeProbe.helperTeamIdentifier || "") &&
2625
+ plan.runtimeProbe.helperPackageIdentifier ===
2626
+ macProtocol.packageIdentifier &&
2627
+ isMacosMcpLauncherPackageVersion(
2628
+ plan.runtimeProbe.helperPackageVersion,
2629
+ ) &&
2630
+ plan.runtimeProbe.planBindingMechanism ===
2631
+ "macos-mcp-code-snapshot-plan-binding-v1" &&
2632
+ plan.runtimeProbe.runtimeLaunchPath === macSnapshotPath &&
2633
+ plan.runtimeProbe.entrySnapshotPath === "anonymous-root-owned-fd4" &&
2634
+ planCommand === macProtocol.helperInstallPath &&
2635
+ planArgsSnapshot.length >= 10 &&
2636
+ planArgsSnapshot[0] === "--launch-v1" &&
2637
+ /^[a-f0-9]{64}$/.test(planArgsSnapshot[1] || "") &&
2638
+ planArgsSnapshot[2] === MACOS_MCP_LAUNCHER_INPUTS.protocolSha256 &&
2639
+ planArgsSnapshot[3] === executionContract.runtimeIdentity.sha256 &&
2640
+ planArgsSnapshot[4] ===
2641
+ String(executionContract.runtimeIdentity.bytes) &&
2642
+ planArgsSnapshot[5] === executionContract.entryIdentity.sha256 &&
2643
+ planArgsSnapshot[6] ===
2644
+ String(executionContract.entryIdentity.bytes) &&
2645
+ /^[1-9]\d*$/.test(planArgsSnapshot[7] || "") &&
2646
+ /^[1-9]\d*$/.test(planArgsSnapshot[8] || "") &&
2647
+ planArgsSnapshot[9] === policyDigest &&
2648
+ passthroughArgsMatch(planArgsSnapshot.slice(10)) &&
2649
+ planOptionsSnapshot.cwd === "/" &&
2650
+ planOptionsSnapshot.shell === false &&
2651
+ planOptionsSnapshot.detached === false &&
2652
+ Array.isArray(macStdio) &&
2653
+ macStdio.length === 9 &&
2654
+ macStdio
2655
+ .slice(0, 3)
2656
+ .every((value) => ["pipe", "ignore"].includes(value)) &&
2657
+ macStdio.slice(3, 6).every(Number.isInteger) &&
2658
+ macStdio[6] === "ignore" &&
2659
+ macStdio[7] === "ignore" &&
2660
+ macStdio[8] === "pipe" &&
2661
+ launchContext.builtInSandboxAdapter === true &&
2662
+ macPlanBindingConsumed &&
2663
+ guarantees.includes(SANDBOX_BOUNDARIES.FILESYSTEM) &&
2664
+ guarantees.includes(SANDBOX_BOUNDARIES.NETWORK) &&
2665
+ guarantees.includes(SANDBOX_BOUNDARIES.PROCESS_EXEC) &&
2666
+ guarantees.includes(SANDBOX_BOUNDARIES.PROCESS_TREE) &&
2667
+ guarantees.includes(SANDBOX_BOUNDARIES.PRIVILEGE_REDUCTION);
1788
2668
  const windowsSnapshotEvidence =
1789
2669
  plan.platform === "win32" &&
1790
2670
  [
@@ -1826,6 +2706,7 @@ class ProcessExecutionBroker extends EventEmitter {
1826
2706
  !exactSnapshotEvidence ||
1827
2707
  (!linuxSnapshotEvidence &&
1828
2708
  !linuxBubblewrapSnapshotEvidence &&
2709
+ !macSnapshotEvidence &&
1829
2710
  !windowsSnapshotEvidence)
1830
2711
  ) {
1831
2712
  throw this._sandboxError(
@@ -1935,12 +2816,12 @@ class ProcessExecutionBroker extends EventEmitter {
1935
2816
  ptyPolicy.launcherDescriptorConsumedBeforeTarget === true &&
1936
2817
  ptyPolicy.launcherStagingPathHidden === true &&
1937
2818
  ptyPolicy.bwrapNewSession === false &&
1938
- plan.args[0] === "--ctty" &&
1939
- /^\/proc\/self\/fd\/\d+$/.test(plan.args[1] || "") &&
1940
- !plan.args.includes("--new-session")
2819
+ planArgsSnapshot[0] === "--ctty" &&
2820
+ /^\/proc\/self\/fd\/\d+$/.test(planArgsSnapshot[1] || "") &&
2821
+ !linuxBubblewrapExecutableArgs.includes("--new-session")
1941
2822
  : ptyPolicy === null &&
1942
- plan.args[0] !== "--ctty" &&
1943
- plan.args.includes("--new-session");
2823
+ planArgsSnapshot[0] !== "--ctty" &&
2824
+ linuxBubblewrapExecutableArgs.includes("--new-session");
1944
2825
  const workspaceRoot = filesystemPolicy?.workspaceRoot;
1945
2826
  const workingDirectory = filesystemPolicy?.workingDirectory;
1946
2827
  const workspaceRelative =
@@ -1948,7 +2829,19 @@ class ProcessExecutionBroker extends EventEmitter {
1948
2829
  typeof workingDirectory === "string"
1949
2830
  ? path.posix.relative(workspaceRoot, workingDirectory)
1950
2831
  : "..";
2832
+ const genericExecutionContract = launchContext.executionContract;
2833
+ const genericExecutionContractBound =
2834
+ launchContext.builtInSandboxAdapter === true &&
2835
+ verifyLinuxGenericBubblewrapPlan(authorityPlan) &&
2836
+ genericExecutionContract?.contractVersion === 1 &&
2837
+ genericExecutionContract.kind === LINUX_GENERIC_CONTRACT_KIND &&
2838
+ genericExecutionContract.contractDigest ===
2839
+ plan.runtimeProbe.contractDigest &&
2840
+ genericExecutionContract.workspaceRoot === workspaceRoot &&
2841
+ genericExecutionContract.workingDirectory === workingDirectory &&
2842
+ launchContext.cwd === workingDirectory;
1951
2843
  genericWorkspaceProbe =
2844
+ genericExecutionContractBound &&
1952
2845
  plan.applied === true &&
1953
2846
  plan.platform === "linux" &&
1954
2847
  plan.enforcement === "linux-bwrap-workspace" &&
@@ -2036,20 +2929,20 @@ class ProcessExecutionBroker extends EventEmitter {
2036
2929
  processTreePolicy?.parentDeathSignal === "SIGKILL" &&
2037
2930
  processTreePolicy?.asPid1 === false &&
2038
2931
  processTreePolicy?.closeFence === "pid-namespace-empty-or-killed" &&
2039
- plan.command.startsWith("/proc/self/fd/") &&
2040
- plan.options?.cwd === "/" &&
2041
- plan.options?.shell === false &&
2042
- plan.options?.detached === false &&
2043
- Array.isArray(plan.options?.stdio) &&
2044
- plan.args.includes("--bind-fd") &&
2045
- plan.args.includes("--ro-bind-fd") &&
2046
- plan.args.includes("--remount-ro") &&
2047
- plan.args.includes("--unshare-net") &&
2048
- plan.args.includes("--unshare-pid") &&
2049
- plan.args.includes("--die-with-parent") &&
2050
- !plan.args.includes("--as-pid-1") &&
2051
- plan.args.includes("--seccomp") &&
2052
- !plan.args.includes("--ro-bind") &&
2932
+ planCommand.startsWith("/proc/self/fd/") &&
2933
+ linuxDescriptorLaunch.options.cwd === "/" &&
2934
+ linuxDescriptorLaunch.options.shell === false &&
2935
+ linuxDescriptorLaunch.options.detached === false &&
2936
+ Array.isArray(linuxDescriptorLaunch.stdio) &&
2937
+ linuxBubblewrapExecutableArgs.includes("--bind-fd") &&
2938
+ linuxBubblewrapExecutableArgs.includes("--ro-bind-fd") &&
2939
+ linuxBubblewrapExecutableArgs.includes("--remount-ro") &&
2940
+ linuxBubblewrapExecutableArgs.includes("--unshare-net") &&
2941
+ linuxBubblewrapExecutableArgs.includes("--unshare-pid") &&
2942
+ linuxBubblewrapExecutableArgs.includes("--die-with-parent") &&
2943
+ !linuxBubblewrapExecutableArgs.includes("--as-pid-1") &&
2944
+ linuxBubblewrapExecutableArgs.includes("--seccomp") &&
2945
+ !linuxBubblewrapExecutableArgs.includes("--ro-bind") &&
2053
2946
  ptyPlanValid;
2054
2947
  if (!genericWorkspaceProbe) {
2055
2948
  throw this._sandboxError(
@@ -2230,11 +3123,62 @@ class ProcessExecutionBroker extends EventEmitter {
2230
3123
  runtimeProbe[field] = plan.runtimeProbe[field];
2231
3124
  }
2232
3125
  }
3126
+ for (const field of [
3127
+ "runtimeSharedLibraryPathnameClosure",
3128
+ ...runtimePathnameClosureEvidenceFields,
3129
+ ]) {
3130
+ if (plan.runtimeProbe[field] !== undefined) {
3131
+ runtimeProbe[field] = plan.runtimeProbe[field];
3132
+ }
3133
+ }
3134
+ for (const field of [
3135
+ "rootProtectedRuntimeSnapshot",
3136
+ "entryRootOwnedAnonymousSnapshot",
3137
+ "entrySourcePrePostStat",
3138
+ "entryWriterClosedBeforeReadonlyReopen",
3139
+ "entryReadonlyIdentityRechecked",
3140
+ "entryUnlinkedAndDirectoryFsyncedBeforeTarget",
3141
+ "targetInheritedEntrySnapshotOnly",
3142
+ "runtimeAndCapsuleSlotsNullBeforeExec",
3143
+ "bootstrapClosesNullAndReadyDescriptors",
3144
+ "actualRuntimeReadyHandshake",
3145
+ "runtimeSnapshotUnlinkedBeforeEntryRelease",
3146
+ "callerCredentialDropIrreversible",
3147
+ "relayParentCredentialsDropped",
3148
+ "callerLifelineWatched",
3149
+ "signalRelayNonblocking",
3150
+ "targetRuntimeInvocationMode",
3151
+ "pkgExecPathMagicBound",
3152
+ "targetDescriptorAllowlist",
3153
+ "capsuleRootDescriptorBound",
3154
+ "capsulePathObjectAtomic",
3155
+ "sandboxProfileFixedAndDigestBound",
3156
+ "processForkExplicitlyDenied",
3157
+ "sandboxExecLiveGateContract",
3158
+ "globalLaunchSerialization",
3159
+ "maximumStaleSnapshots",
3160
+ "helperSha256",
3161
+ "helperSourceSha256",
3162
+ "helperProtocolSha256",
3163
+ "helperInstallContractSha256",
3164
+ "helperDesignatedRequirementSha256",
3165
+ "helperTeamIdentifier",
3166
+ "helperPackageIdentifier",
3167
+ "helperPackageVersion",
3168
+ "installAttestationDigest",
3169
+ ]) {
3170
+ if (plan.runtimeProbe[field] !== undefined) {
3171
+ runtimeProbe[field] = plan.runtimeProbe[field];
3172
+ }
3173
+ }
2233
3174
  for (const field of genericWorkspaceEvidenceFields) {
2234
3175
  if (plan.runtimeProbe[field] !== undefined) {
2235
3176
  runtimeProbe[field] = plan.runtimeProbe[field];
2236
3177
  }
2237
3178
  }
3179
+ if (sanitizedDescriptorScrubber !== null) {
3180
+ runtimeProbe.descriptorScrubber = sanitizedDescriptorScrubber;
3181
+ }
2238
3182
  if (supervisorIdentity !== undefined) {
2239
3183
  runtimeProbe.supervisorExecutableIdentity = {
2240
3184
  path: supervisorIdentity.path,
@@ -2277,10 +3221,29 @@ class ProcessExecutionBroker extends EventEmitter {
2277
3221
  "Required post-spawn enforcement must declare sync or async mode",
2278
3222
  );
2279
3223
  }
3224
+ const validatedArgs = linuxDescriptorLaunch
3225
+ ? linuxDescriptorPtyLaunch
3226
+ ? Object.freeze([
3227
+ "--ctty",
3228
+ `/proc/self/fd/${linuxDescriptorLaunch.scrubberChildFd}`,
3229
+ ...linuxDescriptorLaunch.launchArgs,
3230
+ ])
3231
+ : linuxDescriptorLaunch.launchArgs
3232
+ : planArgsSnapshot;
3233
+ const validatedOptions = linuxDescriptorLaunch
3234
+ ? Object.freeze({
3235
+ ...linuxDescriptorLaunch.options,
3236
+ stdio: Object.freeze([...linuxDescriptorLaunch.stdio]),
3237
+ env: Object.freeze({
3238
+ ...linuxDescriptorLaunch.callerEnvironment,
3239
+ }),
3240
+ })
3241
+ : planOptionsSnapshot;
2280
3242
  const validatedPlan = {
2281
3243
  ...plan,
2282
- args: [...plan.args],
2283
- options: { ...plan.options },
3244
+ command: planCommand,
3245
+ args: [...validatedArgs],
3246
+ options: { ...validatedOptions },
2284
3247
  backend,
2285
3248
  guarantees: [...new Set(guarantees)],
2286
3249
  candidateBackend,
@@ -2305,6 +3268,14 @@ class ProcessExecutionBroker extends EventEmitter {
2305
3268
  };
2306
3269
  admittedWindowsMcpCodeSnapshotPlans.add(validatedPlan);
2307
3270
  }
3271
+ if (macPlanBindingConsumed) {
3272
+ const upstreamCleanup = validatedPlan.cleanup;
3273
+ validatedPlan.cleanup = (...args) => {
3274
+ admittedMacMcpCodeSnapshotPlans.delete(validatedPlan);
3275
+ return upstreamCleanup?.(...args);
3276
+ };
3277
+ admittedMacMcpCodeSnapshotPlans.add(validatedPlan);
3278
+ }
2308
3279
  return validatedPlan;
2309
3280
  }
2310
3281
 
@@ -2353,6 +3324,15 @@ class ProcessExecutionBroker extends EventEmitter {
2353
3324
  };
2354
3325
  admittedWindowsMcpCodeSnapshotPlans.add(assertedPlan);
2355
3326
  }
3327
+ if (admittedMacMcpCodeSnapshotPlans.has(plan)) {
3328
+ admittedMacMcpCodeSnapshotPlans.delete(plan);
3329
+ const upstreamCleanup = assertedPlan.cleanup;
3330
+ assertedPlan.cleanup = (...args) => {
3331
+ admittedMacMcpCodeSnapshotPlans.delete(assertedPlan);
3332
+ return upstreamCleanup?.(...args);
3333
+ };
3334
+ admittedMacMcpCodeSnapshotPlans.add(assertedPlan);
3335
+ }
2356
3336
  return assertedPlan;
2357
3337
  }
2358
3338
 
@@ -3432,6 +4412,21 @@ class ProcessExecutionBroker extends EventEmitter {
3432
4412
  let proc;
3433
4413
  let workspaceProcessClosed = null;
3434
4414
  try {
4415
+ const macPlanBindingDeclared =
4416
+ sandboxPlan.platform === "darwin" &&
4417
+ sandboxPlan.runtimeProbe?.planBindingMechanism ===
4418
+ "macos-mcp-code-snapshot-plan-binding-v1";
4419
+ const builtInMacMcpPlan =
4420
+ admittedMacMcpCodeSnapshotPlans.has(sandboxPlan);
4421
+ if (macPlanBindingDeclared && !builtInMacMcpPlan) {
4422
+ throw this._sandboxError(
4423
+ "macos_mcp_plan_binding_consumed",
4424
+ "macOS MCP sandbox plan binding is unavailable or already consumed",
4425
+ );
4426
+ }
4427
+ if (builtInMacMcpPlan) {
4428
+ admittedMacMcpCodeSnapshotPlans.delete(sandboxPlan);
4429
+ }
3435
4430
  if (mcpStdioExecutableIdentityAuthority !== undefined) {
3436
4431
  const admitted = consumeMcpStdioExecutableIdentityAuthority(
3437
4432
  mcpStdioExecutableIdentityAuthority,
@@ -3444,6 +4439,31 @@ class ProcessExecutionBroker extends EventEmitter {
3444
4439
  auditEntry.mcpStdioExecutableIdentityDigest = admitted.identityDigest;
3445
4440
  }
3446
4441
  proc = nativeSpawnFn(command, args, optsForSpawn);
4442
+ if (builtInMacMcpPlan) {
4443
+ const callerLifeline = proc?.stdio?.[8];
4444
+ if (
4445
+ !callerLifeline ||
4446
+ typeof callerLifeline !== "object" ||
4447
+ callerLifeline.destroyed === true ||
4448
+ typeof callerLifeline.destroy !== "function"
4449
+ ) {
4450
+ try {
4451
+ proc?.kill?.("SIGKILL");
4452
+ } catch {
4453
+ // The missing lifeline is already a fail-closed launch failure.
4454
+ }
4455
+ throw this._sandboxError(
4456
+ "macos_mcp_caller_lifeline_unavailable",
4457
+ "macOS MCP launch did not retain its Broker-side caller lifeline",
4458
+ );
4459
+ }
4460
+ Object.defineProperty(proc, "macosMcpCallerLifeline", {
4461
+ configurable: false,
4462
+ enumerable: false,
4463
+ writable: false,
4464
+ value: callerLifeline,
4465
+ });
4466
+ }
3447
4467
  workspaceProcessClosed = this._createWorkspaceProcessCloseFence(
3448
4468
  auditEntry,
3449
4469
  proc,
@@ -3477,15 +4497,16 @@ class ProcessExecutionBroker extends EventEmitter {
3477
4497
  sandboxPlan.applied === true &&
3478
4498
  (sandboxPlan.backend === "linux-bwrap-workspace" ||
3479
4499
  sandboxPlan.backend === "linux-bwrap" ||
3480
- sandboxPlan.backend === "linux-fd-code-snapshot") &&
4500
+ sandboxPlan.backend === "linux-fd-code-snapshot" ||
4501
+ sandboxPlan.backend === "macos-signed-root-fd-launcher") &&
3481
4502
  sandboxPlan.postSpawn.required === false
3482
4503
  ) {
3483
4504
  // child_process.spawn() has synchronously duplicated every stdio entry
3484
4505
  // into the launched process before returning. The generic/direct bwrap
3485
- // backends and the MCP anonymous-code snapshot backend are complete
3486
- // pre-spawn plans with no parent-side enforcement handle. Retaining
3487
- // their pinned descriptors until a long-lived process exits would turn
3488
- // each child into an avoidable FD leak.
4506
+ // backends and the MCP code-snapshot backends are complete pre-spawn
4507
+ // plans. macOS retains its independently created stdio[8] lifeline on
4508
+ // ChildProcess for the full process lifetime; cleanup closes only the
4509
+ // three already-duplicated source pins.
3489
4510
  cleanupSandbox();
3490
4511
  }
3491
4512
  try {
@@ -3735,6 +4756,23 @@ class ProcessExecutionBroker extends EventEmitter {
3735
4756
 
3736
4757
  const nativeSpawnSyncFn = this._native?.spawnSync || nativeSpawnSync;
3737
4758
  try {
4759
+ const macPlanBindingDeclared =
4760
+ sandboxPlan.platform === "darwin" &&
4761
+ sandboxPlan.runtimeProbe?.planBindingMechanism ===
4762
+ "macos-mcp-code-snapshot-plan-binding-v1";
4763
+ const builtInMacMcpPlan =
4764
+ admittedMacMcpCodeSnapshotPlans.has(sandboxPlan);
4765
+ if (macPlanBindingDeclared && !builtInMacMcpPlan) {
4766
+ throw this._sandboxError(
4767
+ "macos_mcp_plan_binding_consumed",
4768
+ "macOS MCP sandbox plan binding is unavailable or already consumed",
4769
+ );
4770
+ }
4771
+ if (builtInMacMcpPlan) {
4772
+ // spawnSync owns the parent endpoint for stdio[8] until the helper
4773
+ // exits; burning before entry prevents replay on every outcome.
4774
+ admittedMacMcpCodeSnapshotPlans.delete(sandboxPlan);
4775
+ }
3738
4776
  const result = nativeSpawnSyncFn(command, args, optsForSync);
3739
4777
  this._settleWorkspaceTransactionSpawn(auditEntry, {
3740
4778
  exitCode: result.status,