chainlesschain 0.162.198 → 0.162.200

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 (195) hide show
  1. package/bin/chainlesschain.js +14 -6
  2. package/package.json +4 -1
  3. package/scripts/command-lifecycle-report.mjs +94 -0
  4. package/src/assets/web-panel/assets/{AIOps-CSN-D2xb.js → AIOps-DcQ0bWKy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-CBSMlGzv.js → ActionButton-GecwfFPt.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-3yr3Nt2t.js → Analytics-CD6iSt9v.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-1b-o3lMH.js → AppLayout-CDEtuEYf.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-Dr8rJi17.js → Artifacts-DS3jAjeW.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-2V689HC2.js → Audit-CcSJA048.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-DIxkNrD_.js → BackgroundAgents-DRo9Morb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Pg6BcwrE.js → Backup-Vw5MrWft.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-CLUpqyfH.js → BaseInput-DNHIu823.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-qVS8rD4G.js → Chat-DvAsO8-b.js} +5 -5
  14. package/src/assets/web-panel/assets/{ChatBubbleRenderer-ti49X12Q.js → ChatBubbleRenderer-9aDuObay.js} +1 -1
  15. package/src/assets/web-panel/assets/{Checkbox-Bs9AaEay.js → Checkbox-CKCa7tvn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-Dvc1mq8q.js → Codegen-CIFvZdaT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C4_biO7A.js → Col-C79yz7bK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-al25XQUV.js → Community-DVvZskLJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-D-ZDB8M1.js → Compact-oaew61hV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-6lIfDz1W.js → Compliance-DqA7wmRx.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-DdI48XMJ.js → Cowork-B-qGadxa.js} +2 -2
  22. package/src/assets/web-panel/assets/{Cron-DF5HZd2g.js → Cron-CUlCl4sS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-DuxeSvt3.js → Crosschain-CNOlr9DU.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-B9yHkUTd.js → DID-DJqFCT_3.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-Z5EuI3IM.js → Dashboard-CznGpphf.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-vuRfO3dY.js → Dropdown-_cJw218E.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-QFLCc1uL.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-BZ7EXkFJ.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-DycIEg2s.js → Federation-DOiG3i_1.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-Ba0H_7yG.js → FormItemContext-BzbMcFB9.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer--JgSTK-C.js → GenericCardRenderer-VbBrYttP.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DEvTbldE.js → Git-CLG8E6ED.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-BJIljy18.js → Governance-LGJQfk_q.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-CVyW3G3q.js → Inference-DlQKx9lF.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-v8-borge.js → KnowledgeGraph-DU8vEouV.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BCdS7gGo.js → Logs-FZIQOdln.js} +2 -2
  37. package/src/assets/web-panel/assets/MarkdownRenderer-BJNWIrbw.js +1 -0
  38. package/src/assets/web-panel/assets/{Marketplace-AN6tGA2B.js → Marketplace-C_xd-zZI.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-DGViXASN.js → McpTools-q-YktphS.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-Bpvmx9X4.js → Memory-Cq9OhDin.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-CTP-DOTC.js → MobileBridge-B03_R98T.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-C8qfktxn.js → MobileProjects-DZrk-qmF.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-ndYr0M1s.js → Mtc-BidCKW28.js} +2 -2
  44. package/src/assets/web-panel/assets/{MtcAudit-Ci0-YK7W.js → MtcAudit-r-iaMVM7.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-BehSTFW7.js → Multisig-CgTUFvff.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-B91_YBLv.js → NLProgramming-B7NvzfAV.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-BQlJ0-gq.js → Notes-80CWNqJI.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-CDgYMjYK.js → NotificationSettings-0H6xdiY3.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-BJkSlKeH.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-B-b3cWCp.js → Organization-B8sxCV_h.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-CILUL4nL.js → Overflow-CW9JBBhQ.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-BTCFBkF0.js → P2P-bdSQV7oZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-BAR6AUx5.js → PdhVaultBrowser-DQad42nW.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-RRf6HbWJ.js → Permissions-CueexxTR.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-BGTMiICa.js → PersonalDataHub-fqxflhR8.js} +6 -6
  56. package/src/assets/web-panel/assets/{Pipeline-945qY9YF.js → Pipeline-KFwwnIMC.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DPZwI6lJ.js → Privacy-ClnD9buY.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-BPH2NRKc.js → ProjectInit-CZEz6sm3.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BZCRNCvb.js → ProjectSettings-B6NzpmOI.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-BTI__tBt.js → Projects-DYyuwPCs.js} +1 -1
  61. package/src/assets/web-panel/assets/Providers-C3tz_jdh.js +1 -0
  62. package/src/assets/web-panel/assets/{QrScannerModal-N6rbqt1r.js → QrScannerModal-BzuOBbqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-DrwjPS7M.js → QuickAsk-CmNO9qSj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-Cog9Wd99.js → Recommend-VJNj3_NF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DC9pynjM.js → RemoteSession-CrbdbSBF.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-D4th4gKS.js → Reputation-DTyMd45j.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-ogiWR6v9.js → Row-DHrQ-Icz.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-uYlk-2Oj.js → RssFeed-DqWxY9c5.js} +3 -3
  69. package/src/assets/web-panel/assets/{Search-DYzzwvtM.js → Search-ctF1Ks3J.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Da7YOyX6.js → Security-BrQz8uo5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-CLI1VM1X.js → Services-D4scVFar.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DK77dzDC.js → Skeleton-oUScaOjv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-C_z7Lmhl.js → Skills-CL-y0eLt.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-D6413Q8L.js → Sla-DQExhwXj.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-C07ydbj2.js → SpeechSettings-iNp_I7kN.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-DGOm2W22.js → SyncSettings-CTDlMo0A.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-Cr_PVyLM.js → Tasks-4kb8l3sc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-BhL2mZ0t.js → Templates-BXcL9oXW.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CPk5oyUr.js → Tenant-DLE8eS0D.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-ClhqUsqV.js → Terminal-DGpUZPPK.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BCC7MV5a.js → TimelineRenderer-C1pWXGPC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-DD1Jt4oO.js → Tokens-BYfbfqhZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BCydFvN2.js → Trigger-K9YfLOCw.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DDHT1noa.js → Trust-Ctkq77Hy.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-CWPZC7oI.js → UkeySign-C6_CSe9J.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-9Ea0nJR0.js → VideoEditing-Be3rpOWs.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BjxhLxTf.js → Wallet-D0UIplxk.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-C6uCye67.js → WebAuthn-CF9bM9I3.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-d2g4reYo.js → WorkflowEditor-D0H1XVPY.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-frTc6ScX.js → chat-Cgzzbe0o.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-B5K6lG3v.js → colors-BwNxEVS0.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-Cdn46r8D.js → compact-item-D60Z4Hhs.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-DU1jqVQi.js → createContext-MFk58XB0.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BJiw369i.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DdNH-aW_.js → hasIn-CRj2SvGO.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DM5FmPZU.js → index--s45_1cb.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D4l82Me1.js → index--tquQ2ZG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CQorP1Ng.js → index-1T1rhokn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CHmkkdfT.js → index-B-M82_rx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CGiwfwB9.js → index-BE_el1Zs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BwlIxAmX.js → index-BhRkR7IO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Cj6P9Nst.js → index-Bo0y96-K.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BoFak7le.js → index-CJgu5JUK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-Dca_i7H8.js → index-CLWwdebO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BVLL-mq_.js → index-CNAK5QG_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-LaivuXyF.js → index-CQjJjPRD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-t_OBzIP4.js → index-CTDbAfae.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BcGwGIaR.js → index-CVI_9sRK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-D23xGYjM.js → index-CY14oMQK.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-VDM2KKo9.js → index-CbiTJkZf.js} +1 -1
  111. package/src/assets/web-panel/assets/index-CiMouosn.js +1 -0
  112. package/src/assets/web-panel/assets/{index-32ajXmTb.js → index-CqWpxMqx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-PjAS0sIZ.js → index-CvB8lYUR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-7ehytj1K.js → index-D51XwtCY.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DtBCCkC4.js → index-D6HvK_1O.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-Be4OtL-N.js → index-D9jVIwLJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CUoNAmE8.js → index-DA4tE98u.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-cYaWV4Mq.js → index-DBwOtXw0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-MM4Zz2OE.js → index-DKdNzTIb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-DIVyTmZ3.js → index-DRySdmvN.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-CtRPi4YC.js → index-DWzyTev7.js} +1 -1
  122. package/src/assets/web-panel/assets/index-DXcKlwy7.js +1 -0
  123. package/src/assets/web-panel/assets/{index-kVO_MDm-.js → index-DYjs7jAP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-D4miNkWv.js → index-DbROzMBp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-yZRuZpwK.js → index-DhFtk5PR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-BXKhhx9V.js → index-Di2AbMLg.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-BC4eGkar.js → index-DvmocQsM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-NmEXVjzt.js → index-Dw-o0zEF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DRzBBdQ0.js → index-DysPoP38.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-HjCvXHf5.js → index-LX9pJxm_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-gg3QPC3i.js → index-OI_fXWx0.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-mgU3Q-lN.js → index-eDTOX8Xj.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-D1qIC4GR.js → index-gWKDsTzF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-DftZuyA7.js → index-q_GDUVAA.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-DwLTRNdo.js → initDefaultProps-C61kZqEd.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-BchZDXnh.js → motion-DZnT5U1L.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-e8M-zZFl.js → move-L8wzwbel.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-Ci40fQb3.js → mtc-parser-DLdYSejc.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-C7110_f2.js → omit-D53cbS59.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-skteMgnM.js → pickAttrs-Bbc5HPtu.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-MGZ-lh2B.js → placementArrow-BkvmO860.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-N4Sd7uy-.js → responsiveObserve-u2y1ryS1.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-C7HDGRaa.js → slide-BBqj7svU.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-BhRexlgy.js → statusUtils-GN0mqu2d.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-ChEoQkN2.js → styleChecker-BvwPKoBT.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-CDbG1s2Y.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CUIx0tcJ.js → useFs-DXAxl2SV.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-bkO7L7ec.js → usePersonalDataHub-DihlR2dj.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-B_ZvABIG.js → vnode-D7BOX2gi.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-D5shjpS8.js → zoom-CVbO-nL7.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +1 -1
  153. package/src/commands/mcp.js +119 -0
  154. package/src/commands/session-show.js +8 -10
  155. package/src/commands/session.js +60 -52
  156. package/src/data/changelog.json +27 -1
  157. package/src/gateways/ws/session-protocol.js +14 -4
  158. package/src/gateways/ws/ws-agent-handler.js +31 -0
  159. package/src/harness/jsonl-session-store.js +1147 -130
  160. package/src/harness/mcp-client.js +44 -11
  161. package/src/harness/session-index.js +80 -80
  162. package/src/harness/session-list-index.js +147 -2
  163. package/src/lib/background-interaction-journal.js +2 -2
  164. package/src/lib/command-lifecycle-report.js +516 -0
  165. package/src/lib/mcp-stdio-environment.js +48 -0
  166. package/src/lib/mcp-stdio-executable-identity.js +552 -17
  167. package/src/lib/mcp-stdio-package-materialization.js +1424 -0
  168. package/src/lib/packer/native-update-state.js +979 -1
  169. package/src/lib/packer/pack-update-applier.js +1050 -125
  170. package/src/lib/packer/pkg-config-generator.js +1 -0
  171. package/src/lib/paths.js +53 -0
  172. package/src/lib/process-execution-broker/index.js +180 -37
  173. package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
  174. package/src/lib/recent-session.js +17 -17
  175. package/src/lib/secure-file-identity.js +47 -0
  176. package/src/lib/secure-fs.js +21 -2
  177. package/src/lib/session-anti-rollback-anchor.js +766 -0
  178. package/src/lib/session-host-lease.js +492 -0
  179. package/src/lib/session-persistence-failure.js +109 -0
  180. package/src/lib/session-picker.js +26 -3
  181. package/src/lib/session-search.js +25 -11
  182. package/src/lib/side-effect-ledger-store.js +2 -2
  183. package/src/lib/skill-execution-authority.js +13 -9
  184. package/src/lib/turn-binding-store.js +2 -2
  185. package/src/repl/agent-repl.js +309 -39
  186. package/src/runtime/agent-core.js +88 -5
  187. package/src/runtime/cleanup-deadline.js +143 -0
  188. package/src/runtime/headless-runner.js +357 -139
  189. package/src/runtime/headless-stream.js +207 -28
  190. package/src/runtime/output-backpressure.js +53 -0
  191. package/src/assets/web-panel/assets/MarkdownRenderer-CU-JB2Ns.js +0 -1
  192. package/src/assets/web-panel/assets/Providers-DDCf_nIw.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-CP-jEUyw.js +0 -1
  194. package/src/assets/web-panel/assets/index-CzrCHrFy.js +0 -1
  195. package/src/assets/web-panel/assets/index-OJsGskZw.js +0 -1
@@ -4,16 +4,30 @@
4
4
  * A trusted config names code; it does not prove which bytes PATH or an
5
5
  * interpreter will open. This module resolves those paths, attests regular
6
6
  * files through open descriptors, persists the first explicitly trusted
7
- * identity, and issues a one-shot broker token for the final pre-spawn check.
7
+ * identity in OS user security state outside CHAINLESSCHAIN_HOME, and issues a
8
+ * one-shot broker token for the final pre-spawn check. Restoring the CLI home
9
+ * therefore cannot silently restore an older executable trust decision.
8
10
  */
9
11
 
10
12
  import crypto from "node:crypto";
11
13
  import fs from "node:fs";
12
14
  import path from "node:path";
13
- import { getStatePath } from "./paths.js";
15
+ import { getMachineSecurityAnchorDir } from "./paths.js";
14
16
  import { withFileLock } from "./with-file-lock.js";
15
- import { mutateSecurityStore } from "./durable-security-store.js";
17
+ import {
18
+ readSecurityStore,
19
+ writeSecurityStore,
20
+ } from "./durable-security-store.js";
16
21
  import { resolveMcpStdioExecutionApproval } from "./mcp-stdio-execution-authority.js";
22
+ import {
23
+ MCP_STDIO_PACKAGE_MATERIALIZATION_REQUIRED_CODE,
24
+ reattestMcpStdioPackageMaterialization,
25
+ resolveMcpStdioPackageMaterialization,
26
+ } from "./mcp-stdio-package-materialization.js";
27
+ import {
28
+ isMcpStdioCodeInjectionEnvironmentKey,
29
+ sanitizeMcpStdioHostEnvironment,
30
+ } from "./mcp-stdio-environment.js";
17
31
 
18
32
  export const MCP_STDIO_EXECUTABLE_TRUST_REQUIRED_CODE =
19
33
  "CC_MCP_STDIO_EXECUTABLE_TRUST_REQUIRED";
@@ -25,12 +39,20 @@ export const MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE =
25
39
  "CC_MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED";
26
40
  export const MCP_STDIO_EXECUTABLE_AUTHORITY_REPLAYED_CODE =
27
41
  "CC_MCP_STDIO_EXECUTABLE_AUTHORITY_REPLAYED";
42
+ export const MCP_STDIO_EXECUTABLE_IDENTITY_ROLLBACK_CODE =
43
+ "CC_MCP_STDIO_EXECUTABLE_IDENTITY_ROLLBACK";
44
+ export const MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND =
45
+ "strict-mcp-node-capsule";
46
+ export const MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY = "code-snapshot";
28
47
 
29
48
  const STORE_LABEL = "MCP stdio executable identity";
49
+ const WITNESS_LABEL = "MCP stdio executable identity anti-rollback witness";
50
+ const WITNESS_VERSION = 1;
30
51
  const MAX_EXECUTABLE_BYTES = 256 * 1024 * 1024;
31
52
  const MAX_ENTRYPOINT_BYTES = 64 * 1024 * 1024;
32
53
  const HASH_CHUNK_BYTES = 1024 * 1024;
33
54
  const issuedLaunchAuthorities = new WeakMap();
55
+ const issuedCapsuleSandboxContracts = new WeakMap();
34
56
 
35
57
  const DYNAMIC_LAUNCHERS = new Set([
36
58
  "bunx",
@@ -39,6 +61,7 @@ const DYNAMIC_LAUNCHERS = new Set([
39
61
  "npx",
40
62
  "pipx",
41
63
  "pnpm",
64
+ "pnpx",
42
65
  "uvx",
43
66
  "yarn",
44
67
  "yarnpkg",
@@ -61,11 +84,298 @@ function sha256(value) {
61
84
  return crypto.createHash("sha256").update(value).digest("hex");
62
85
  }
63
86
 
64
- function storePath(options = {}) {
87
+ export function getMcpStdioExecutableTrustStorePath(options = {}) {
65
88
  return (
66
89
  options.storePath ||
67
90
  process.env.CC_MCP_EXECUTABLE_TRUST_STORE ||
68
- path.join(getStatePath(), "mcp-stdio-executable-identities.json")
91
+ path.join(
92
+ getMachineSecurityAnchorDir(),
93
+ "mcp-stdio-executable-identities-v1.json",
94
+ )
95
+ );
96
+ }
97
+
98
+ export function getMcpStdioExecutableTrustWitnessPath(options = {}) {
99
+ return (
100
+ options.witnessPath ||
101
+ process.env.CC_MCP_EXECUTABLE_TRUST_WITNESS ||
102
+ `${getMcpStdioExecutableTrustStorePath(options)}.anti-rollback-v1.json`
103
+ );
104
+ }
105
+
106
+ function storeDigest(store) {
107
+ return sha256(JSON.stringify(store));
108
+ }
109
+
110
+ function parseWitnessGeneration(value, label) {
111
+ if (typeof value !== "string" || !/^(?:0|[1-9]\d*)$/.test(value)) {
112
+ throw new TypeError(`${label} must be a canonical non-negative integer`);
113
+ }
114
+ return BigInt(value);
115
+ }
116
+
117
+ function validateStoreDigest(value, label) {
118
+ if (typeof value !== "string" || !/^[a-f0-9]{64}$/.test(value)) {
119
+ throw new TypeError(`${label} must be a SHA-256 digest`);
120
+ }
121
+ return value;
122
+ }
123
+
124
+ function validateWitnessTransition(value, expectedPrevious, label) {
125
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
126
+ throw new TypeError(`${label} must be an object`);
127
+ }
128
+ const generation = parseWitnessGeneration(
129
+ value.generation,
130
+ `${label}.generation`,
131
+ );
132
+ const previousGeneration = parseWitnessGeneration(
133
+ value.previousGeneration,
134
+ `${label}.previousGeneration`,
135
+ );
136
+ if (
137
+ previousGeneration !== expectedPrevious.generation ||
138
+ generation !== previousGeneration + 1n
139
+ ) {
140
+ throw new TypeError(`${label} breaks the generation chain`);
141
+ }
142
+ const previousStoreDigest = validateStoreDigest(
143
+ value.previousStoreDigest,
144
+ `${label}.previousStoreDigest`,
145
+ );
146
+ if (previousStoreDigest !== expectedPrevious.storeDigest) {
147
+ throw new TypeError(`${label} breaks the store digest chain`);
148
+ }
149
+ const nextStoreDigest = validateStoreDigest(
150
+ value.storeDigest,
151
+ `${label}.storeDigest`,
152
+ );
153
+ if (nextStoreDigest === previousStoreDigest) {
154
+ throw new TypeError(`${label} does not advance the store digest`);
155
+ }
156
+ if (
157
+ typeof value.mutationId !== "string" ||
158
+ !/^[a-f0-9-]{16,64}$/i.test(value.mutationId)
159
+ ) {
160
+ throw new TypeError(`${label}.mutationId is invalid`);
161
+ }
162
+ if (
163
+ typeof value.preparedAt !== "string" ||
164
+ !Number.isFinite(Date.parse(value.preparedAt))
165
+ ) {
166
+ throw new TypeError(`${label}.preparedAt is invalid`);
167
+ }
168
+ if (
169
+ value.committedAt !== undefined &&
170
+ (typeof value.committedAt !== "string" ||
171
+ !Number.isFinite(Date.parse(value.committedAt)))
172
+ ) {
173
+ throw new TypeError(`${label}.committedAt is invalid`);
174
+ }
175
+ return Object.freeze({
176
+ generation,
177
+ previousGeneration,
178
+ previousStoreDigest,
179
+ storeDigest: nextStoreDigest,
180
+ mutationId: value.mutationId,
181
+ preparedAt: value.preparedAt,
182
+ ...(value.committedAt ? { committedAt: value.committedAt } : {}),
183
+ });
184
+ }
185
+
186
+ function validateWitness(witness) {
187
+ if (witness.version !== WITNESS_VERSION) {
188
+ throw new TypeError(
189
+ `unsupported MCP executable trust witness version: ${String(witness.version)}`,
190
+ );
191
+ }
192
+ const generation = parseWitnessGeneration(
193
+ witness.generation,
194
+ "witness.generation",
195
+ );
196
+ const initialStoreDigest = validateStoreDigest(
197
+ witness.initialStoreDigest,
198
+ "witness.initialStoreDigest",
199
+ );
200
+ if (!Array.isArray(witness.events)) {
201
+ throw new TypeError("witness.events must be an array");
202
+ }
203
+ let previous = Object.freeze({
204
+ generation: 0n,
205
+ storeDigest: initialStoreDigest,
206
+ });
207
+ for (const [index, event] of witness.events.entries()) {
208
+ const transition = validateWitnessTransition(
209
+ event,
210
+ previous,
211
+ `witness.events[${index}]`,
212
+ );
213
+ if (!transition.committedAt) {
214
+ throw new TypeError(`witness.events[${index}] is not committed`);
215
+ }
216
+ previous = Object.freeze({
217
+ generation: transition.generation,
218
+ storeDigest: transition.storeDigest,
219
+ });
220
+ }
221
+ if (generation !== previous.generation) {
222
+ throw new TypeError("witness generation does not match its event chain");
223
+ }
224
+ const committedStoreDigest = validateStoreDigest(
225
+ witness.storeDigest,
226
+ "witness.storeDigest",
227
+ );
228
+ if (committedStoreDigest !== previous.storeDigest) {
229
+ throw new TypeError("witness store digest does not match its event chain");
230
+ }
231
+ const pending =
232
+ witness.pending == null
233
+ ? null
234
+ : validateWitnessTransition(witness.pending, previous, "witness.pending");
235
+ if (pending?.committedAt) {
236
+ throw new TypeError("witness.pending must not already be committed");
237
+ }
238
+ return Object.freeze({
239
+ generation,
240
+ initialStoreDigest,
241
+ storeDigest: committedStoreDigest,
242
+ events: Object.freeze([...witness.events]),
243
+ pending,
244
+ });
245
+ }
246
+
247
+ function initialWitness(digest) {
248
+ return {
249
+ version: WITNESS_VERSION,
250
+ generation: "0",
251
+ initialStoreDigest: digest,
252
+ storeDigest: digest,
253
+ events: [],
254
+ pending: null,
255
+ };
256
+ }
257
+
258
+ function rollbackError(message, details = {}) {
259
+ return identityError(
260
+ MCP_STDIO_EXECUTABLE_IDENTITY_ROLLBACK_CODE,
261
+ `MCP stdio executable trust rollback detected: ${message}`,
262
+ details,
263
+ );
264
+ }
265
+
266
+ function recoverWitness(witnessPath, witness, currentStoreDigest) {
267
+ const snapshot = validateWitness(witness);
268
+ if (!snapshot.pending) {
269
+ if (currentStoreDigest !== snapshot.storeDigest) {
270
+ throw rollbackError(
271
+ "the trust store does not match its committed witness",
272
+ {
273
+ expectedStoreDigest: snapshot.storeDigest,
274
+ observedStoreDigest: currentStoreDigest,
275
+ },
276
+ );
277
+ }
278
+ return snapshot;
279
+ }
280
+
281
+ if (currentStoreDigest === snapshot.storeDigest) {
282
+ witness.pending = null;
283
+ writeSecurityStore(witnessPath, WITNESS_LABEL, witness);
284
+ return validateWitness(witness);
285
+ }
286
+ if (currentStoreDigest !== snapshot.pending.storeDigest) {
287
+ throw rollbackError(
288
+ "the trust store matches neither side of a pending mutation",
289
+ {
290
+ previousStoreDigest: snapshot.storeDigest,
291
+ pendingStoreDigest: snapshot.pending.storeDigest,
292
+ observedStoreDigest: currentStoreDigest,
293
+ },
294
+ );
295
+ }
296
+
297
+ const committed = {
298
+ ...witness.pending,
299
+ committedAt: new Date().toISOString(),
300
+ };
301
+ witness.generation = committed.generation;
302
+ witness.storeDigest = committed.storeDigest;
303
+ witness.events = [...witness.events, committed];
304
+ witness.pending = null;
305
+ writeSecurityStore(witnessPath, WITNESS_LABEL, witness);
306
+ return validateWitness(witness);
307
+ }
308
+
309
+ function mutateExecutableTrustStore(target, mutator, options = {}) {
310
+ const witnessPath = getMcpStdioExecutableTrustWitnessPath({
311
+ ...options,
312
+ storePath: target,
313
+ });
314
+ if (path.resolve(witnessPath) === path.resolve(target)) {
315
+ throw new TypeError(
316
+ "MCP stdio executable trust store and witness must use different paths",
317
+ );
318
+ }
319
+ try {
320
+ _deps.fs.mkdirSync(path.dirname(witnessPath), {
321
+ recursive: true,
322
+ mode: 0o700,
323
+ });
324
+ } catch (cause) {
325
+ throw identityError(
326
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
327
+ "MCP stdio executable trust witness could not be prepared",
328
+ { cause, witnessPath },
329
+ );
330
+ }
331
+
332
+ return _deps.withFileLock(
333
+ witnessPath,
334
+ () => {
335
+ const store = readSecurityStore(target, STORE_LABEL);
336
+ const currentStoreDigest = storeDigest(store);
337
+ const witnessExists = _deps.fs.existsSync(witnessPath);
338
+ const witness = witnessExists
339
+ ? readSecurityStore(witnessPath, WITNESS_LABEL)
340
+ : initialWitness(currentStoreDigest);
341
+ const snapshot = witnessExists
342
+ ? recoverWitness(witnessPath, witness, currentStoreDigest)
343
+ : validateWitness(witness);
344
+ const draft = structuredClone(store);
345
+ const result = mutator(draft);
346
+ const nextStoreDigest = storeDigest(draft);
347
+
348
+ if (nextStoreDigest === currentStoreDigest) {
349
+ if (!witnessExists) {
350
+ writeSecurityStore(witnessPath, WITNESS_LABEL, witness);
351
+ }
352
+ return result;
353
+ }
354
+
355
+ const transition = {
356
+ generation: String(snapshot.generation + 1n),
357
+ previousGeneration: String(snapshot.generation),
358
+ previousStoreDigest: currentStoreDigest,
359
+ storeDigest: nextStoreDigest,
360
+ mutationId: crypto.randomUUID(),
361
+ preparedAt: new Date().toISOString(),
362
+ };
363
+ witness.pending = transition;
364
+ writeSecurityStore(witnessPath, WITNESS_LABEL, witness);
365
+ writeSecurityStore(target, STORE_LABEL, draft);
366
+
367
+ const committed = {
368
+ ...transition,
369
+ committedAt: new Date().toISOString(),
370
+ };
371
+ witness.generation = committed.generation;
372
+ witness.storeDigest = committed.storeDigest;
373
+ witness.events = [...witness.events, committed];
374
+ witness.pending = null;
375
+ writeSecurityStore(witnessPath, WITNESS_LABEL, witness);
376
+ return result;
377
+ },
378
+ { timeoutMs: 2000, staleMs: 30000, failIfUnavailable: true },
69
379
  );
70
380
  }
71
381
 
@@ -81,6 +391,38 @@ function executableBasename(value) {
81
391
  .toLowerCase();
82
392
  }
83
393
 
394
+ function materializeLaunchEnvironment(hostEnv, configEnv) {
395
+ const launch = sanitizeMcpStdioHostEnvironment(hostEnv);
396
+ for (const [key, value] of Object.entries(configEnv || {})) {
397
+ if (isMcpStdioCodeInjectionEnvironmentKey(key)) {
398
+ throw identityError(
399
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
400
+ `MCP stdio config environment may load unattested code through ${key}`,
401
+ );
402
+ }
403
+ if (typeof value !== "string") {
404
+ throw identityError(
405
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
406
+ `MCP stdio config environment value must be a string: ${key}`,
407
+ );
408
+ }
409
+ launch[key] = value;
410
+ }
411
+ return Object.freeze(launch);
412
+ }
413
+
414
+ export function assertMcpStdioExecutableEnvironmentSafe(env) {
415
+ for (const key of Object.keys(env || {})) {
416
+ if (isMcpStdioCodeInjectionEnvironmentKey(key)) {
417
+ throw identityError(
418
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
419
+ `MCP stdio launch environment may load unattested code through ${key}`,
420
+ );
421
+ }
422
+ }
423
+ return true;
424
+ }
425
+
84
426
  function candidateFiles(command, cwd, env, platform) {
85
427
  const value = String(command || "");
86
428
  if (!value || value.includes("\0")) return [];
@@ -377,6 +719,9 @@ function comparableIdentity(identity) {
377
719
  return {
378
720
  command: identity.command,
379
721
  entrypoints: identity.entrypoints,
722
+ ...(identity.materialization
723
+ ? { materialization: identity.materialization }
724
+ : {}),
380
725
  };
381
726
  }
382
727
 
@@ -384,6 +729,115 @@ function identityDigest(identity) {
384
729
  return sha256(JSON.stringify(comparableIdentity(identity)));
385
730
  }
386
731
 
732
+ function capsuleSandboxFileIdentity(identity, requestedPath) {
733
+ return Object.freeze({
734
+ ...(requestedPath ? { requestedPath } : {}),
735
+ realPath: identity.realPath,
736
+ sha256: identity.sha256,
737
+ bytes: identity.bytes,
738
+ dev: identity.dev,
739
+ ino: identity.ino,
740
+ mtimeMs: Number(identity.mtimeNs) / 1_000_000,
741
+ mode: identity.mode,
742
+ });
743
+ }
744
+
745
+ function attestCapsuleRoot(capsuleRoot) {
746
+ const realpath = _deps.fs.realpathSync.native || _deps.fs.realpathSync;
747
+ const root = realpath(path.resolve(capsuleRoot));
748
+ const before = _deps.fs.lstatSync(root, { bigint: true });
749
+ const after = _deps.fs.statSync(root, { bigint: true });
750
+ if (
751
+ before.isSymbolicLink() ||
752
+ !before.isDirectory() ||
753
+ !after.isDirectory() ||
754
+ before.dev !== after.dev ||
755
+ before.ino !== after.ino
756
+ ) {
757
+ throw new Error("MCP capsule root identity changed during attestation");
758
+ }
759
+ return Object.freeze({
760
+ realPath: root,
761
+ dev: String(after.dev),
762
+ ino: String(after.ino),
763
+ });
764
+ }
765
+
766
+ function issueCapsuleSandboxExecutionContract({
767
+ serverName,
768
+ config,
769
+ attestation,
770
+ materialization,
771
+ }) {
772
+ if (!materialization) return null;
773
+ const capsuleRoot = attestCapsuleRoot(materialization.capsuleRoot);
774
+ const entryIdentity = attestation.identity.entrypoints.find(
775
+ (entry) => entry.argIndex === 0,
776
+ );
777
+ if (
778
+ attestation.identity.entrypoints.length !== 1 ||
779
+ !entryIdentity ||
780
+ path.dirname(entryIdentity.realPath) !== capsuleRoot.realPath ||
781
+ entryIdentity.realPath !== attestation.launchArgs[0]
782
+ ) {
783
+ throw new Error(
784
+ "MCP materialized capsule launch is not one direct Node entrypoint",
785
+ );
786
+ }
787
+ const contract = Object.freeze({
788
+ contractVersion: 1,
789
+ kind: MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND,
790
+ pluginRoot: capsuleRoot.realPath,
791
+ workingDirectory: capsuleRoot.realPath,
792
+ runtimePath: attestation.identity.command.realPath,
793
+ rootIdentity: capsuleRoot,
794
+ entryIdentity: capsuleSandboxFileIdentity(entryIdentity),
795
+ runtimeIdentity: capsuleSandboxFileIdentity(
796
+ attestation.identity.command,
797
+ path.resolve(process.execPath),
798
+ ),
799
+ });
800
+ issuedCapsuleSandboxContracts.set(
801
+ contract,
802
+ Object.freeze({
803
+ origin: config.origin || `mcp:server:${serverName}`,
804
+ command: attestation.launchCommand,
805
+ args: Object.freeze([...attestation.launchArgs]),
806
+ cwd: capsuleRoot.realPath,
807
+ identityDigest: attestation.identityDigest,
808
+ }),
809
+ );
810
+ return contract;
811
+ }
812
+
813
+ export function consumeMcpStdioCapsuleSandboxExecutionContract(
814
+ contract,
815
+ provenance = {},
816
+ ) {
817
+ const issued =
818
+ contract && typeof contract === "object"
819
+ ? issuedCapsuleSandboxContracts.get(contract)
820
+ : null;
821
+ if (!issued) return false;
822
+ issuedCapsuleSandboxContracts.delete(contract);
823
+ const args = Array.isArray(provenance.args) ? provenance.args : [];
824
+ const requiredBoundaries = Array.isArray(provenance.requiredBoundaries)
825
+ ? provenance.requiredBoundaries
826
+ : [];
827
+ return (
828
+ contract.kind === MCP_STDIO_CAPSULE_SANDBOX_CONTRACT_KIND &&
829
+ provenance.origin === issued.origin &&
830
+ provenance.command === issued.command &&
831
+ provenance.cwd === issued.cwd &&
832
+ provenance.shell === false &&
833
+ provenance.sync === false &&
834
+ provenance.identityDigest === issued.identityDigest &&
835
+ requiredBoundaries.includes(MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY) &&
836
+ args.length === issued.args.length &&
837
+ args.every((value, index) => value === issued.args[index])
838
+ );
839
+ }
840
+
387
841
  export function attestMcpStdioExecutableIdentity({
388
842
  command,
389
843
  args = [],
@@ -450,27 +904,46 @@ function trustMessage(serverName, attestation, status) {
450
904
  const entrypoints = attestation.identity.entrypoints
451
905
  .map((entry) => `${entry.realPath} sha256:${entry.sha256}`)
452
906
  .join(", ");
907
+ const materialization = attestation.identity.materialization;
453
908
  return (
454
909
  `MCP stdio executable identity ${status} for "${serverName}": ` +
455
910
  `${attestation.identity.command.realPath} sha256:${attestation.identity.command.sha256}` +
456
911
  (entrypoints ? `; ${entrypoints}` : "") +
912
+ (materialization
913
+ ? `; npm ${materialization.package.name}@${materialization.package.version} closure sha256:${materialization.closureDigest}`
914
+ : "") +
457
915
  ". Review these bytes, then run 'cc mcp trust-executable <name>' or set CC_MCP_EXECUTABLE_TRUST=1 for this run"
458
916
  );
459
917
  }
460
918
 
919
+ function materializedAttestation(attestation, materialization) {
920
+ if (!materialization) return attestation;
921
+ const identity = Object.freeze({
922
+ version: 2,
923
+ command: attestation.identity.command,
924
+ entrypoints: attestation.identity.entrypoints,
925
+ materialization: materialization.identity,
926
+ });
927
+ return Object.freeze({
928
+ launchCommand: attestation.launchCommand,
929
+ launchArgs: attestation.launchArgs,
930
+ identity,
931
+ identityDigest: identityDigest(identity),
932
+ });
933
+ }
934
+
461
935
  function checkOrRecordTrust(
462
936
  serverName,
463
937
  approvalRecord,
464
938
  attestation,
465
939
  options = {},
466
940
  ) {
467
- const target = storePath(options);
941
+ const target = getMcpStdioExecutableTrustStorePath(options);
468
942
  const key = trustKey(approvalRecord);
469
943
  const requested =
470
944
  options.retrust === true || retrustRequested(options.env || process.env);
471
- return mutateSecurityStore(
945
+ return mutateExecutableTrustStore(
472
946
  target,
473
- STORE_LABEL,
474
947
  (store) => {
475
948
  const existing = store[key] || null;
476
949
  if (
@@ -511,7 +984,7 @@ function checkOrRecordTrust(
511
984
  key,
512
985
  };
513
986
  },
514
- { lock: _deps.withFileLock },
987
+ options,
515
988
  );
516
989
  }
517
990
 
@@ -523,6 +996,9 @@ export function prepareMcpStdioExecutableIdentity({
523
996
  env = process.env,
524
997
  retrust = false,
525
998
  storePath: explicitStorePath,
999
+ witnessPath: explicitWitnessPath,
1000
+ materializationRoot,
1001
+ materializationIndexPath,
526
1002
  }) {
527
1003
  const approvalRecord = resolveMcpStdioExecutionApproval(approval);
528
1004
  if (!approvalRecord) {
@@ -531,16 +1007,52 @@ export function prepareMcpStdioExecutableIdentity({
531
1007
  `MCP stdio executable identity for "${serverName}" requires a valid execution approval`,
532
1008
  );
533
1009
  }
534
- const attestation = attestMcpStdioExecutableIdentity({
535
- command: config.command,
536
- args: config.args || [],
537
- env: config.env || {},
538
- cwd,
539
- });
1010
+ let materialization = null;
1011
+ let effectiveCommand = config.command;
1012
+ let effectiveArgs = config.args || [];
1013
+ if (DYNAMIC_LAUNCHERS.has(executableBasename(config.command))) {
1014
+ try {
1015
+ materialization = resolveMcpStdioPackageMaterialization({
1016
+ approvalRecord,
1017
+ ...(materializationRoot ? { root: materializationRoot } : {}),
1018
+ ...(materializationIndexPath
1019
+ ? { indexPath: materializationIndexPath }
1020
+ : {}),
1021
+ });
1022
+ effectiveCommand = materialization.command;
1023
+ effectiveArgs = [...materialization.args];
1024
+ } catch (cause) {
1025
+ if (cause?.code === MCP_STDIO_PACKAGE_MATERIALIZATION_REQUIRED_CODE) {
1026
+ throw identityError(
1027
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
1028
+ cause.message,
1029
+ { cause },
1030
+ );
1031
+ }
1032
+ throw cause;
1033
+ }
1034
+ }
1035
+ const attestation = materializedAttestation(
1036
+ attestMcpStdioExecutableIdentity({
1037
+ command: effectiveCommand,
1038
+ args: effectiveArgs,
1039
+ env: config.env || {},
1040
+ cwd,
1041
+ }),
1042
+ materialization,
1043
+ );
1044
+ const launchEnv = materializeLaunchEnvironment(env, config.env || {});
540
1045
  const trust = checkOrRecordTrust(serverName, approvalRecord, attestation, {
541
1046
  env,
542
1047
  retrust,
543
1048
  ...(explicitStorePath ? { storePath: explicitStorePath } : {}),
1049
+ ...(explicitWitnessPath ? { witnessPath: explicitWitnessPath } : {}),
1050
+ });
1051
+ const sandboxExecutionContract = issueCapsuleSandboxExecutionContract({
1052
+ serverName,
1053
+ config,
1054
+ attestation,
1055
+ materialization,
544
1056
  });
545
1057
  const authority = Object.freeze({});
546
1058
  issuedLaunchAuthorities.set(
@@ -550,6 +1062,7 @@ export function prepareMcpStdioExecutableIdentity({
550
1062
  args: Object.freeze([...attestation.launchArgs]),
551
1063
  identity: attestation.identity,
552
1064
  identityDigest: attestation.identityDigest,
1065
+ materialization,
553
1066
  }),
554
1067
  );
555
1068
  return Object.freeze({
@@ -557,6 +1070,12 @@ export function prepareMcpStdioExecutableIdentity({
557
1070
  args: Object.freeze([...attestation.launchArgs]),
558
1071
  identity: attestation.identity,
559
1072
  identityDigest: attestation.identityDigest,
1073
+ env: launchEnv,
1074
+ workingDirectory:
1075
+ sandboxExecutionContract?.workingDirectory ||
1076
+ materialization?.capsuleRoot ||
1077
+ cwd,
1078
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
560
1079
  authority,
561
1080
  trustStatus: trust.status,
562
1081
  });
@@ -564,7 +1083,7 @@ export function prepareMcpStdioExecutableIdentity({
564
1083
 
565
1084
  export function consumeMcpStdioExecutableIdentityAuthority(
566
1085
  authority,
567
- { command, args = [] },
1086
+ { command, args = [], env = {} },
568
1087
  ) {
569
1088
  const issued = issuedLaunchAuthorities.get(authority);
570
1089
  if (!issued) {
@@ -574,6 +1093,7 @@ export function consumeMcpStdioExecutableIdentityAuthority(
574
1093
  );
575
1094
  }
576
1095
  issuedLaunchAuthorities.delete(authority);
1096
+ assertMcpStdioExecutableEnvironmentSafe(env);
577
1097
  if (
578
1098
  command !== issued.command ||
579
1099
  JSON.stringify(args) !== JSON.stringify(issued.args)
@@ -583,11 +1103,26 @@ export function consumeMcpStdioExecutableIdentityAuthority(
583
1103
  "MCP stdio launch changed after executable identity approval",
584
1104
  );
585
1105
  }
586
- const current = attestMcpStdioExecutableIdentity({
1106
+ let current = attestMcpStdioExecutableIdentity({
587
1107
  command,
588
1108
  args,
589
1109
  cwd: process.cwd(),
590
1110
  });
1111
+ if (issued.materialization) {
1112
+ const verified = reattestMcpStdioPackageMaterialization(
1113
+ issued.materialization,
1114
+ );
1115
+ if (
1116
+ JSON.stringify(verified.identity) !==
1117
+ JSON.stringify(issued.materialization.identity)
1118
+ ) {
1119
+ throw identityError(
1120
+ MCP_STDIO_EXECUTABLE_CHANGED_CODE,
1121
+ "MCP stdio materialized package identity changed before spawn",
1122
+ );
1123
+ }
1124
+ current = materializedAttestation(current, issued.materialization);
1125
+ }
591
1126
  if (current.identityDigest !== issued.identityDigest) {
592
1127
  throw identityError(
593
1128
  MCP_STDIO_EXECUTABLE_CHANGED_CODE,