chainlesschain 0.162.197 → 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 (233) 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-D3Gu1a5d.js → AIOps-DcQ0bWKy.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-DrwAoKaz.js → ActionButton-GecwfFPt.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-C7Q-gd_3.js → Analytics-CD6iSt9v.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-DgBtae-n.js → AppLayout-CDEtuEYf.js} +5 -5
  8. package/src/assets/web-panel/assets/{Artifacts-pQ6LcA-W.js → Artifacts-DS3jAjeW.js} +1 -1
  9. package/src/assets/web-panel/assets/{Audit-DK61tocB.js → Audit-CcSJA048.js} +1 -1
  10. package/src/assets/web-panel/assets/{BackgroundAgents-BOKgDFRh.js → BackgroundAgents-DRo9Morb.js} +1 -1
  11. package/src/assets/web-panel/assets/{Backup-Bo9G4vTP.js → Backup-Vw5MrWft.js} +1 -1
  12. package/src/assets/web-panel/assets/{BaseInput-Do_dE9_v.js → BaseInput-DNHIu823.js} +1 -1
  13. package/src/assets/web-panel/assets/{Chat-COVvZzOL.js → Chat-DvAsO8-b.js} +4 -4
  14. package/src/assets/web-panel/assets/ChatBubbleRenderer-9aDuObay.js +1 -0
  15. package/src/assets/web-panel/assets/{Checkbox-Z9MJMolt.js → Checkbox-CKCa7tvn.js} +1 -1
  16. package/src/assets/web-panel/assets/{Codegen-BVzWKVbf.js → Codegen-CIFvZdaT.js} +1 -1
  17. package/src/assets/web-panel/assets/{Col-C8MnYcbT.js → Col-C79yz7bK.js} +1 -1
  18. package/src/assets/web-panel/assets/{Community-DQxqw15l.js → Community-DVvZskLJ.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compact-BAUmrh3y.js → Compact-oaew61hV.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compliance-jPAkQ3zK.js → Compliance-DqA7wmRx.js} +1 -1
  21. package/src/assets/web-panel/assets/{Cowork-BBQyxbh8.js → Cowork-B-qGadxa.js} +3 -3
  22. package/src/assets/web-panel/assets/{Cron-W5koq8Z5.js → Cron-CUlCl4sS.js} +2 -2
  23. package/src/assets/web-panel/assets/{Crosschain-BQhnu5hS.js → Crosschain-CNOlr9DU.js} +1 -1
  24. package/src/assets/web-panel/assets/{DID-DD7PlI18.js → DID-DJqFCT_3.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dashboard-w_ILuwKI.js → Dashboard-CznGpphf.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dropdown-qiDUAyAo.js → Dropdown-_cJw218E.js} +1 -1
  27. package/src/assets/web-panel/assets/{EmailListRenderer-PDeBcI5H.js → EmailListRenderer-Dr-1BZ9C.js} +1 -1
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-SuArscRM.js → FamilyGuardDashboard-Dxf4xtSk.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-BCovZaUn.js → Federation-DOiG3i_1.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-DjlFdVFk.js → FormItemContext-BzbMcFB9.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-DoEZ7fcS.js → GenericCardRenderer-VbBrYttP.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-CV0-6GPI.js → Git-CLG8E6ED.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-B_SFN50w.js → Governance-LGJQfk_q.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-heI0Eo6k.js → Inference-DlQKx9lF.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-Cz5INS5p.js → KnowledgeGraph-DU8vEouV.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-BSFxiHkg.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-0TWTpE7k.js → Marketplace-C_xd-zZI.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-CV4-wBLm.js → McpTools-q-YktphS.js} +5 -5
  40. package/src/assets/web-panel/assets/{Memory-x5bH5rw5.js → Memory-Cq9OhDin.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-DaBmUu9G.js → MobileBridge-B03_R98T.js} +1 -1
  42. package/src/assets/web-panel/assets/MobileProjects-DZrk-qmF.js +1 -0
  43. package/src/assets/web-panel/assets/{Mtc-B_zKEBif.js → Mtc-BidCKW28.js} +5 -5
  44. package/src/assets/web-panel/assets/{MtcAudit-BCfKD2yg.js → MtcAudit-r-iaMVM7.js} +2 -2
  45. package/src/assets/web-panel/assets/{Multisig-DgYfj6c_.js → Multisig-CgTUFvff.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-Dz09lFUm.js → NLProgramming-B7NvzfAV.js} +1 -1
  47. package/src/assets/web-panel/assets/{Notes-Cuj6ugBE.js → Notes-80CWNqJI.js} +3 -3
  48. package/src/assets/web-panel/assets/{NotificationSettings-DK2WXjhg.js → NotificationSettings-0H6xdiY3.js} +1 -1
  49. package/src/assets/web-panel/assets/{OrderTableRenderer-D1IAOQoo.js → OrderTableRenderer-CKP4AFWy.js} +1 -1
  50. package/src/assets/web-panel/assets/{Organization-9fqnPvTw.js → Organization-B8sxCV_h.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-n5xHkhow.js → Overflow-CW9JBBhQ.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-B5AUWwH-.js → P2P-bdSQV7oZ.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-CoUDV6dy.js → PdhVaultBrowser-DQad42nW.js} +4 -4
  54. package/src/assets/web-panel/assets/{Permissions-DPRmQf_H.js → Permissions-CueexxTR.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-B37bsbEN.js → PersonalDataHub-fqxflhR8.js} +6 -6
  56. package/src/assets/web-panel/assets/{Pipeline-CCAuPqu9.js → Pipeline-KFwwnIMC.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-B81msZdv.js → Privacy-ClnD9buY.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-B-Py2NbF.js → ProjectInit-CZEz6sm3.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-CYsneiRB.js → ProjectSettings-B6NzpmOI.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-Y975Mh6R.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-Bj_6gJ0l.js → QrScannerModal-BzuOBbqB.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-BgpBCMPE.js → QuickAsk-CmNO9qSj.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-DhUW3YUA.js → Recommend-VJNj3_NF.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-DdeyfbkA.js → RemoteSession-CrbdbSBF.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-fxW421xd.js → Reputation-DTyMd45j.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-De73w1og.js → Row-DHrQ-Icz.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-3US_Q_lj.js → RssFeed-DqWxY9c5.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-DbZ_DU74.js → Search-ctF1Ks3J.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-DoIU1vMF.js → Security-BrQz8uo5.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-DB_RGqpc.js → Services-D4scVFar.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DnzLaayC.js → Skeleton-oUScaOjv.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-VzVXHENF.js → Skills-CL-y0eLt.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-DMfvjCAI.js → Sla-DQExhwXj.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-DQQOYWbF.js → SpeechSettings-iNp_I7kN.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-Bmx3FG1P.js → SyncSettings-CTDlMo0A.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-DOQyfb-2.js → Tasks-4kb8l3sc.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-D6hTRT7c.js → Templates-BXcL9oXW.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CTPQZAzs.js → Tenant-DLE8eS0D.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-B7vII3oH.js → Terminal-DGpUZPPK.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-BkiFJeXV.js → TimelineRenderer-C1pWXGPC.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-BPPdQMNo.js → Tokens-BYfbfqhZ.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-BxG8JAEI.js → Trigger-K9YfLOCw.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-DHD7nMpM.js → Trust-Ctkq77Hy.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-B4BJpU47.js → UkeySign-C6_CSe9J.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CRiGpFx2.js → VideoEditing-Be3rpOWs.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-BSDSNiem.js → Wallet-D0UIplxk.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-DLTUh5Zv.js → WebAuthn-CF9bM9I3.js} +5 -5
  89. package/src/assets/web-panel/assets/{WorkflowEditor-tW2MwjhD.js → WorkflowEditor-D0H1XVPY.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-SgsLQ3FC.js → chat-Cgzzbe0o.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-BHjjLIKO.js → colors-BwNxEVS0.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-VceErdKq.js → compact-item-D60Z4Hhs.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-BZ332MYl.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-CBTjJOjn.js → hasIn-CRj2SvGO.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DaCCy6lq.js → index--s45_1cb.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CMVvxIS8.js → index--tquQ2ZG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BhXZEUAc.js → index-1T1rhokn.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BQSmZGgs.js → index-B-M82_rx.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BeiqkxJG.js → index-BE_el1Zs.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-29wVzi-k.js → index-BhRkR7IO.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-iYQNU9za.js → index-Bo0y96-K.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BRWhLbWF.js → index-CJgu5JUK.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BPXFLYdW.js → index-CLWwdebO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Cr4rjlW5.js → index-CNAK5QG_.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CaIS_TNX.js → index-CQjJjPRD.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-1n7xjKDU.js → index-CTDbAfae.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-BETEjuNr.js → index-CVI_9sRK.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-By5jBtkV.js → index-CY14oMQK.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-DgPvuIn2.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-CawpdGTW.js → index-CqWpxMqx.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-_PHrIO4O.js → index-CvB8lYUR.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BOtvPdHt.js → index-D51XwtCY.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-CrUgDQkf.js → index-D6HvK_1O.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BY1wexYG.js → index-D9jVIwLJ.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-C_B9rFQE.js → index-DA4tE98u.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-B5KAGxNo.js → index-DBwOtXw0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BC1xuQgX.js → index-DKdNzTIb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-B2u26AS3.js → index-DRySdmvN.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-DePOeMDM.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-BTlc-Bve.js → index-DYjs7jAP.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CLc19alH.js → index-DbROzMBp.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-fU7wNHDB.js → index-DhFtk5PR.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D_SePDWj.js → index-Di2AbMLg.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-vIXOpcjD.js → index-DvmocQsM.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BcUenmCX.js → index-Dw-o0zEF.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DUkVN794.js → index-DysPoP38.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CtjLcCNU.js → index-LX9pJxm_.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-B40dQn0G.js → index-OI_fXWx0.js} +3 -3
  132. package/src/assets/web-panel/assets/{index-FJ3zC9LK.js → index-eDTOX8Xj.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-DdefYBSw.js → index-gWKDsTzF.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-CpumS98o.js → index-q_GDUVAA.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-D1oZNz0D.js → initDefaultProps-C61kZqEd.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-C8k6doMO.js → motion-DZnT5U1L.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-DJ4qSbYt.js → move-L8wzwbel.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-CdpJD85j.js → mtc-parser-DLdYSejc.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-B0V4slKA.js → omit-D53cbS59.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-Di5NiW1F.js → pickAttrs-Bbc5HPtu.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-B5OMHgEM.js → placementArrow-BkvmO860.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-C6U9IWql.js → responsiveObserve-u2y1ryS1.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-BMyTH3rN.js → slide-BBqj7svU.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-C7d6BG7Z.js → statusUtils-GN0mqu2d.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-CR50QnQy.js → styleChecker-BvwPKoBT.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-DBEeoMb8.js → useFlexGapSupport-CCt8tnfY.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-3r099cjv.js → useFs-DXAxl2SV.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-C5IYNdTK.js → usePersonalDataHub-DihlR2dj.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-DmEOLRHf.js → vnode-D7BOX2gi.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-DnpUiVCB.js → zoom-CVbO-nL7.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +4 -4
  153. package/src/commands/agent.js +3 -3
  154. package/src/commands/background-session.js +145 -0
  155. package/src/commands/mcp.js +244 -7
  156. package/src/commands/serve.js +1 -1
  157. package/src/commands/session-show.js +129 -0
  158. package/src/commands/session.js +97 -145
  159. package/src/commands/skill.js +87 -50
  160. package/src/data/changelog.json +40 -1
  161. package/src/gateways/ws/session-protocol.js +14 -4
  162. package/src/gateways/ws/ws-agent-handler.js +39 -1
  163. package/src/harness/jsonl-session-store.js +1547 -127
  164. package/src/harness/mcp-client.js +2690 -409
  165. package/src/harness/session-index.js +80 -80
  166. package/src/harness/session-list-index.js +210 -7
  167. package/src/lazy-dispatch.js +76 -8
  168. package/src/lib/abort-utils.js +68 -0
  169. package/src/lib/agent-core.js +1 -59
  170. package/src/lib/background-interaction-journal.js +2 -2
  171. package/src/lib/chat-core.js +24 -6
  172. package/src/lib/command-lifecycle-report.js +516 -0
  173. package/src/lib/cowork-mcp-tools.js +47 -2
  174. package/src/lib/cowork-task-runner.js +11 -3
  175. package/src/lib/doctor-checkup.js +9 -7
  176. package/src/lib/learning/skill-improver.js +21 -2
  177. package/src/lib/mcp-call-ledger-store.js +116 -3
  178. package/src/lib/mcp-call-ledger.js +59 -14
  179. package/src/lib/mcp-client.js +1 -0
  180. package/src/lib/mcp-stdio-environment.js +48 -0
  181. package/src/lib/mcp-stdio-executable-identity.js +1133 -0
  182. package/src/lib/mcp-stdio-execution-authority.js +363 -0
  183. package/src/lib/mcp-stdio-package-materialization.js +1424 -0
  184. package/src/lib/mcp-tool-metadata.js +436 -0
  185. package/src/lib/mcp-tool-result.js +367 -0
  186. package/src/lib/observability/index.js +71 -0
  187. package/src/lib/packer/native-update-state.js +979 -1
  188. package/src/lib/packer/pack-update-applier.js +1050 -125
  189. package/src/lib/packer/pkg-config-generator.js +1 -0
  190. package/src/lib/paths.js +53 -0
  191. package/src/lib/pr-link-ledger.js +8 -27
  192. package/src/lib/pr-link-store.js +26 -0
  193. package/src/lib/process-execution-broker/index.js +192 -35
  194. package/src/lib/process-execution-broker/platform-sandbox.js +291 -24
  195. package/src/lib/process-tree-termination.js +253 -0
  196. package/src/lib/recent-session.js +17 -17
  197. package/src/lib/secure-file-identity.js +47 -0
  198. package/src/lib/secure-fs.js +21 -2
  199. package/src/lib/session-anti-rollback-anchor.js +766 -0
  200. package/src/lib/session-host-lease.js +492 -0
  201. package/src/lib/session-host-snapshot.js +9 -5
  202. package/src/lib/session-persistence-failure.js +109 -0
  203. package/src/lib/session-picker.js +26 -3
  204. package/src/lib/session-projection.js +5 -1
  205. package/src/lib/session-search.js +25 -11
  206. package/src/lib/session-tail.js +31 -1
  207. package/src/lib/side-effect-ledger-store.js +2 -2
  208. package/src/lib/skill-budget.js +222 -0
  209. package/src/lib/skill-execution-authority.js +385 -0
  210. package/src/lib/skill-execution-identity.js +39 -2
  211. package/src/lib/skill-loader.js +322 -86
  212. package/src/lib/skill-mcp.js +43 -1
  213. package/src/lib/turn-binding-store.js +2 -2
  214. package/src/repl/agent-repl.js +483 -77
  215. package/src/repl/bg-dashboard.js +81 -28
  216. package/src/repl/chat-repl.js +77 -12
  217. package/src/runtime/agent-core.js +428 -113
  218. package/src/runtime/agent-runtime.js +26 -3
  219. package/src/runtime/cleanup-deadline.js +143 -0
  220. package/src/runtime/headless-runner.js +494 -149
  221. package/src/runtime/headless-stream.js +504 -75
  222. package/src/runtime/mcp-config.js +157 -10
  223. package/src/runtime/mcp-tool-search.js +0 -0
  224. package/src/runtime/output-backpressure.js +419 -0
  225. package/src/runtime/pipe-safety.js +56 -21
  226. package/src/assets/web-panel/assets/ChatBubbleRenderer-BUUyJ5QO.js +0 -1
  227. package/src/assets/web-panel/assets/MarkdownRenderer-1mNozZ8v.js +0 -1
  228. package/src/assets/web-panel/assets/MobileProjects-DqnoFPgR.js +0 -1
  229. package/src/assets/web-panel/assets/Providers-uO-10aP1.js +0 -1
  230. package/src/assets/web-panel/assets/devWarning-DosMaL-k.js +0 -1
  231. package/src/assets/web-panel/assets/index-0O5ePnUq.js +0 -1
  232. package/src/assets/web-panel/assets/index-ChAzPigF.js +0 -1
  233. package/src/lib/skill-process-broker.js +0 -85
@@ -0,0 +1,1133 @@
1
+ /**
2
+ * Durable byte identity for MCP stdio launchers and interpreter entrypoints.
3
+ *
4
+ * A trusted config names code; it does not prove which bytes PATH or an
5
+ * interpreter will open. This module resolves those paths, attests regular
6
+ * files through open descriptors, persists the first explicitly trusted
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.
10
+ */
11
+
12
+ import crypto from "node:crypto";
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { getMachineSecurityAnchorDir } from "./paths.js";
16
+ import { withFileLock } from "./with-file-lock.js";
17
+ import {
18
+ readSecurityStore,
19
+ writeSecurityStore,
20
+ } from "./durable-security-store.js";
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";
31
+
32
+ export const MCP_STDIO_EXECUTABLE_TRUST_REQUIRED_CODE =
33
+ "CC_MCP_STDIO_EXECUTABLE_TRUST_REQUIRED";
34
+ export const MCP_STDIO_EXECUTABLE_CHANGED_CODE =
35
+ "CC_MCP_STDIO_EXECUTABLE_CHANGED";
36
+ export const MCP_STDIO_EXECUTABLE_UNATTESTED_CODE =
37
+ "CC_MCP_STDIO_EXECUTABLE_UNATTESTED";
38
+ export const MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE =
39
+ "CC_MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED";
40
+ export const MCP_STDIO_EXECUTABLE_AUTHORITY_REPLAYED_CODE =
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";
47
+
48
+ const STORE_LABEL = "MCP stdio executable identity";
49
+ const WITNESS_LABEL = "MCP stdio executable identity anti-rollback witness";
50
+ const WITNESS_VERSION = 1;
51
+ const MAX_EXECUTABLE_BYTES = 256 * 1024 * 1024;
52
+ const MAX_ENTRYPOINT_BYTES = 64 * 1024 * 1024;
53
+ const HASH_CHUNK_BYTES = 1024 * 1024;
54
+ const issuedLaunchAuthorities = new WeakMap();
55
+ const issuedCapsuleSandboxContracts = new WeakMap();
56
+
57
+ const DYNAMIC_LAUNCHERS = new Set([
58
+ "bunx",
59
+ "corepack",
60
+ "npm",
61
+ "npx",
62
+ "pipx",
63
+ "pnpm",
64
+ "pnpx",
65
+ "uvx",
66
+ "yarn",
67
+ "yarnpkg",
68
+ ]);
69
+
70
+ export const _deps = {
71
+ fs,
72
+ withFileLock,
73
+ };
74
+
75
+ function identityError(code, message, details = {}) {
76
+ const error = new Error(message);
77
+ error.name = "McpStdioExecutableIdentityError";
78
+ error.code = code;
79
+ Object.assign(error, details);
80
+ return error;
81
+ }
82
+
83
+ function sha256(value) {
84
+ return crypto.createHash("sha256").update(value).digest("hex");
85
+ }
86
+
87
+ export function getMcpStdioExecutableTrustStorePath(options = {}) {
88
+ return (
89
+ options.storePath ||
90
+ process.env.CC_MCP_EXECUTABLE_TRUST_STORE ||
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 },
379
+ );
380
+ }
381
+
382
+ function retrustRequested(env = process.env) {
383
+ const value = String(env.CC_MCP_EXECUTABLE_TRUST || "").toLowerCase();
384
+ return value === "1" || value === "true";
385
+ }
386
+
387
+ function executableBasename(value) {
388
+ return path
389
+ .basename(String(value || ""))
390
+ .replace(/\.(?:exe|com|cmd|bat)$/i, "")
391
+ .toLowerCase();
392
+ }
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
+
426
+ function candidateFiles(command, cwd, env, platform) {
427
+ const value = String(command || "");
428
+ if (!value || value.includes("\0")) return [];
429
+ if (path.isAbsolute(value)) return [path.resolve(value)];
430
+ if (value.includes("/") || value.includes("\\")) {
431
+ return [path.resolve(cwd, value)];
432
+ }
433
+ const searchPath = String(env.PATH || env.Path || env.path || "");
434
+ const directories = searchPath.split(path.delimiter).filter(Boolean);
435
+ if (platform !== "win32") {
436
+ return directories.map((directory) => path.resolve(directory, value));
437
+ }
438
+ const extensions = String(env.PATHEXT || ".COM;.EXE;.BAT;.CMD")
439
+ .split(";")
440
+ .filter(Boolean);
441
+ const hasExtension = path.extname(value) !== "";
442
+ return directories.flatMap((directory) =>
443
+ (hasExtension ? [""] : extensions).map((extension) =>
444
+ path.resolve(directory, `${value}${extension}`),
445
+ ),
446
+ );
447
+ }
448
+
449
+ function resolveExecutable(command, cwd, env, platform) {
450
+ for (const candidate of candidateFiles(command, cwd, env, platform)) {
451
+ try {
452
+ const candidateStat = _deps.fs.statSync(candidate);
453
+ if (!candidateStat.isFile()) continue;
454
+ if (platform !== "win32") {
455
+ _deps.fs.accessSync(candidate, _deps.fs.constants.X_OK);
456
+ }
457
+ const realPath =
458
+ _deps.fs.realpathSync.native?.(candidate) ||
459
+ _deps.fs.realpathSync(candidate);
460
+ if (
461
+ platform === "win32" &&
462
+ ![".exe", ".com"].includes(path.extname(realPath).toLowerCase())
463
+ ) {
464
+ continue;
465
+ }
466
+ return realPath;
467
+ } catch {
468
+ // Keep searching PATH/PATHEXT.
469
+ }
470
+ }
471
+ throw identityError(
472
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
473
+ `MCP stdio command could not be resolved to an executable file: ${String(command)}`,
474
+ );
475
+ }
476
+
477
+ function statIdentity(stat) {
478
+ const time = (ns, ms) =>
479
+ stat[ns] !== undefined
480
+ ? String(stat[ns])
481
+ : String(Math.trunc(Number(stat[ms] || 0) * 1_000_000));
482
+ return {
483
+ dev: String(stat.dev),
484
+ ino: String(stat.ino),
485
+ bytes: Number(stat.size),
486
+ mode: Number(stat.mode),
487
+ nlink: Number(stat.nlink),
488
+ ctimeNs: time("ctimeNs", "ctimeMs"),
489
+ mtimeNs: time("mtimeNs", "mtimeMs"),
490
+ };
491
+ }
492
+
493
+ function sameOpenIdentity(left, right) {
494
+ return (
495
+ left.dev === right.dev &&
496
+ left.ino === right.ino &&
497
+ left.bytes === right.bytes &&
498
+ left.mode === right.mode &&
499
+ left.nlink === right.nlink &&
500
+ left.ctimeNs === right.ctimeNs &&
501
+ left.mtimeNs === right.mtimeNs
502
+ );
503
+ }
504
+
505
+ function hashOpenFile(fd, bytes) {
506
+ const digest = crypto.createHash("sha256");
507
+ const chunk = Buffer.allocUnsafe(
508
+ Math.max(1, Math.min(bytes, HASH_CHUNK_BYTES)),
509
+ );
510
+ let offset = 0;
511
+ while (offset < bytes) {
512
+ const read = _deps.fs.readSync(
513
+ fd,
514
+ chunk,
515
+ 0,
516
+ Math.min(chunk.length, bytes - offset),
517
+ offset,
518
+ );
519
+ if (read <= 0) throw new Error("file ended before its attested size");
520
+ digest.update(chunk.subarray(0, read));
521
+ offset += read;
522
+ }
523
+ return digest.digest("hex");
524
+ }
525
+
526
+ function attestFile(file, maxBytes, role, platform, observation = null) {
527
+ let fd;
528
+ try {
529
+ const realPath =
530
+ _deps.fs.realpathSync.native?.(path.resolve(file)) ||
531
+ _deps.fs.realpathSync(path.resolve(file));
532
+ const lst = _deps.fs.lstatSync(realPath);
533
+ if (lst.isSymbolicLink() || !lst.isFile()) {
534
+ throw new Error("path is not a regular non-symlink file");
535
+ }
536
+ fd = _deps.fs.openSync(
537
+ realPath,
538
+ Number(_deps.fs.constants.O_RDONLY) |
539
+ Number(_deps.fs.constants.O_NOFOLLOW || 0) |
540
+ Number(_deps.fs.constants.O_NONBLOCK || 0),
541
+ );
542
+ const before = _deps.fs.fstatSync(fd, { bigint: true });
543
+ if (
544
+ !before.isFile() ||
545
+ before.size < 0n ||
546
+ before.size > BigInt(maxBytes)
547
+ ) {
548
+ throw new Error("file exceeds the attestation size limit");
549
+ }
550
+ const beforeIdentity = statIdentity(before);
551
+ if (observation) {
552
+ const header = Buffer.allocUnsafe(
553
+ Math.max(1, Math.min(Number(before.size), 4096)),
554
+ );
555
+ const headerBytes =
556
+ before.size > 0n
557
+ ? _deps.fs.readSync(fd, header, 0, header.length, 0)
558
+ : 0;
559
+ observation.hasShebang =
560
+ headerBytes >= 2 && header[0] === 0x23 && header[1] === 0x21;
561
+ }
562
+ const digest = hashOpenFile(fd, Number(before.size));
563
+ const afterIdentity = statIdentity(
564
+ _deps.fs.fstatSync(fd, { bigint: true }),
565
+ );
566
+ if (!sameOpenIdentity(beforeIdentity, afterIdentity)) {
567
+ throw new Error("file changed while it was being hashed");
568
+ }
569
+ const pathIdentity = statIdentity(
570
+ _deps.fs.statSync(realPath, { bigint: true }),
571
+ );
572
+ if (
573
+ platform !== "win32" &&
574
+ (pathIdentity.dev !== afterIdentity.dev ||
575
+ pathIdentity.ino !== afterIdentity.ino)
576
+ ) {
577
+ throw new Error("path no longer names the opened file");
578
+ }
579
+ return Object.freeze({
580
+ role,
581
+ realPath,
582
+ sha256: digest,
583
+ ...afterIdentity,
584
+ });
585
+ } catch (cause) {
586
+ throw identityError(
587
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
588
+ `MCP stdio ${role} identity could not be attested: ${cause.message}`,
589
+ { cause },
590
+ );
591
+ } finally {
592
+ if (fd !== undefined) {
593
+ try {
594
+ _deps.fs.closeSync(fd);
595
+ } catch {
596
+ // Preserve the attestation result.
597
+ }
598
+ }
599
+ }
600
+ }
601
+
602
+ function localEntrypoint(value, cwd, label) {
603
+ if (typeof value !== "string" || !value || value === "-") {
604
+ throw identityError(
605
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
606
+ `MCP stdio ${label} must name a local file`,
607
+ );
608
+ }
609
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value)) {
610
+ throw identityError(
611
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
612
+ `MCP stdio ${label} cannot be a remote URL`,
613
+ );
614
+ }
615
+ return path.isAbsolute(value)
616
+ ? path.resolve(value)
617
+ : path.resolve(cwd, value);
618
+ }
619
+
620
+ function interpreterEntrypoints(commandPath, args, cwd) {
621
+ const name = executableBasename(commandPath);
622
+ const values = [...args];
623
+ const entry = (index, role = "entrypoint") => [
624
+ {
625
+ argIndex: index,
626
+ path: localEntrypoint(values[index], cwd, role),
627
+ role,
628
+ },
629
+ ];
630
+ if (["node", "nodejs"].includes(name)) {
631
+ for (let index = 0; index < values.length; index += 1) {
632
+ const value = values[index];
633
+ if (["-e", "--eval", "-p", "--print"].includes(value)) return [];
634
+ if (
635
+ value === "-r" ||
636
+ value === "--require" ||
637
+ value === "--import" ||
638
+ value === "--loader" ||
639
+ value === "--experimental-loader" ||
640
+ value.startsWith("--require=") ||
641
+ value.startsWith("--import=") ||
642
+ value.startsWith("--loader=") ||
643
+ value.startsWith("--experimental-loader=")
644
+ ) {
645
+ throw identityError(
646
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
647
+ "MCP stdio Node preload/import flags require a transitive code lock and are not supported",
648
+ );
649
+ }
650
+ if (value === "--") return entry(index + 1);
651
+ if (!value.startsWith("-")) return entry(index);
652
+ }
653
+ throw identityError(
654
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
655
+ "MCP stdio Node launch must provide a script or inline program",
656
+ );
657
+ }
658
+ if (["python", "python3", "pythonw", "pythonw3"].includes(name)) {
659
+ for (let index = 0; index < values.length; index += 1) {
660
+ const value = values[index];
661
+ if (value === "-c") return [];
662
+ if (value === "-m") {
663
+ throw identityError(
664
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
665
+ "MCP stdio Python -m modules are not bound to one entrypoint file",
666
+ );
667
+ }
668
+ if (value === "--") return entry(index + 1);
669
+ if (!value.startsWith("-")) return entry(index);
670
+ }
671
+ throw identityError(
672
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
673
+ "MCP stdio Python launch must provide a script or inline program",
674
+ );
675
+ }
676
+ if (["bash", "dash", "ksh", "sh", "zsh"].includes(name)) {
677
+ const inline = values.findIndex((value) => value === "-c");
678
+ if (inline >= 0) return [];
679
+ const index = values.findIndex((value) => !value.startsWith("-"));
680
+ if (index >= 0) return entry(index, "shell-script");
681
+ throw identityError(
682
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
683
+ "MCP stdio shell launch must provide a script or inline command",
684
+ );
685
+ }
686
+ if (["powershell", "pwsh"].includes(name)) {
687
+ const fileIndex = values.findIndex((value) => /^-file$/i.test(value));
688
+ if (fileIndex >= 0) return entry(fileIndex + 1, "powershell-script");
689
+ if (values.some((value) => /^-(?:command|encodedcommand)$/i.test(value))) {
690
+ return [];
691
+ }
692
+ throw identityError(
693
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
694
+ "MCP stdio PowerShell launch must use -File or an inline command",
695
+ );
696
+ }
697
+ if (name === "java") {
698
+ const jarIndex = values.findIndex((value) => value === "-jar");
699
+ if (jarIndex >= 0) return entry(jarIndex + 1, "java-jar");
700
+ throw identityError(
701
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
702
+ "MCP stdio Java classpath launches are not bound to one entrypoint file",
703
+ );
704
+ }
705
+ if (name === "dotnet") {
706
+ const index = values.findIndex((value) => !value.startsWith("-"));
707
+ if (index >= 0 && /\.dll$/i.test(values[index])) {
708
+ return entry(index, "dotnet-assembly");
709
+ }
710
+ throw identityError(
711
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
712
+ "MCP stdio dotnet tool/project launches are not bound to one assembly file",
713
+ );
714
+ }
715
+ return [];
716
+ }
717
+
718
+ function comparableIdentity(identity) {
719
+ return {
720
+ command: identity.command,
721
+ entrypoints: identity.entrypoints,
722
+ ...(identity.materialization
723
+ ? { materialization: identity.materialization }
724
+ : {}),
725
+ };
726
+ }
727
+
728
+ function identityDigest(identity) {
729
+ return sha256(JSON.stringify(comparableIdentity(identity)));
730
+ }
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
+
841
+ export function attestMcpStdioExecutableIdentity({
842
+ command,
843
+ args = [],
844
+ env = {},
845
+ cwd = process.cwd(),
846
+ platform = process.platform,
847
+ }) {
848
+ const name = executableBasename(command);
849
+ if (DYNAMIC_LAUNCHERS.has(name)) {
850
+ throw identityError(
851
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
852
+ `MCP stdio dynamic launcher "${name}" can execute code not identified by its own bytes`,
853
+ );
854
+ }
855
+ const launchEnv = { ...process.env, ...env };
856
+ const commandPath = resolveExecutable(command, cwd, launchEnv, platform);
857
+ const commandObservation = {};
858
+ const commandIdentity = attestFile(
859
+ commandPath,
860
+ MAX_EXECUTABLE_BYTES,
861
+ "command",
862
+ platform,
863
+ commandObservation,
864
+ );
865
+ if (commandObservation.hasShebang) {
866
+ throw identityError(
867
+ MCP_STDIO_DYNAMIC_LAUNCHER_UNPINNED_CODE,
868
+ "MCP stdio direct shebang commands do not bind the interpreter bytes; configure the interpreter and script as command + args",
869
+ );
870
+ }
871
+ const launchArgs = [...args];
872
+ const entrypoints = interpreterEntrypoints(commandPath, launchArgs, cwd).map(
873
+ ({ argIndex, path: entryPath, role }) => {
874
+ const identity = attestFile(
875
+ entryPath,
876
+ MAX_ENTRYPOINT_BYTES,
877
+ role,
878
+ platform,
879
+ );
880
+ launchArgs[argIndex] = identity.realPath;
881
+ return Object.freeze({ argIndex, ...identity });
882
+ },
883
+ );
884
+ const identity = Object.freeze({
885
+ version: 1,
886
+ command: commandIdentity,
887
+ entrypoints: Object.freeze(entrypoints),
888
+ });
889
+ return Object.freeze({
890
+ launchCommand: commandIdentity.realPath,
891
+ launchArgs: Object.freeze(launchArgs),
892
+ identity,
893
+ identityDigest: identityDigest(identity),
894
+ });
895
+ }
896
+
897
+ function trustKey(approvalRecord) {
898
+ return sha256(
899
+ `${approvalRecord.approvalKind}\0${approvalRecord.approvalSource}\0${approvalRecord.fingerprint}`,
900
+ );
901
+ }
902
+
903
+ function trustMessage(serverName, attestation, status) {
904
+ const entrypoints = attestation.identity.entrypoints
905
+ .map((entry) => `${entry.realPath} sha256:${entry.sha256}`)
906
+ .join(", ");
907
+ const materialization = attestation.identity.materialization;
908
+ return (
909
+ `MCP stdio executable identity ${status} for "${serverName}": ` +
910
+ `${attestation.identity.command.realPath} sha256:${attestation.identity.command.sha256}` +
911
+ (entrypoints ? `; ${entrypoints}` : "") +
912
+ (materialization
913
+ ? `; npm ${materialization.package.name}@${materialization.package.version} closure sha256:${materialization.closureDigest}`
914
+ : "") +
915
+ ". Review these bytes, then run 'cc mcp trust-executable <name>' or set CC_MCP_EXECUTABLE_TRUST=1 for this run"
916
+ );
917
+ }
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
+
935
+ function checkOrRecordTrust(
936
+ serverName,
937
+ approvalRecord,
938
+ attestation,
939
+ options = {},
940
+ ) {
941
+ const target = getMcpStdioExecutableTrustStorePath(options);
942
+ const key = trustKey(approvalRecord);
943
+ const requested =
944
+ options.retrust === true || retrustRequested(options.env || process.env);
945
+ return mutateExecutableTrustStore(
946
+ target,
947
+ (store) => {
948
+ const existing = store[key] || null;
949
+ if (
950
+ existing?.identityDigest === attestation.identityDigest &&
951
+ !requested
952
+ ) {
953
+ return { status: "trusted", key };
954
+ }
955
+ if (!requested) {
956
+ const changed = Boolean(existing);
957
+ throw identityError(
958
+ changed
959
+ ? MCP_STDIO_EXECUTABLE_CHANGED_CODE
960
+ : MCP_STDIO_EXECUTABLE_TRUST_REQUIRED_CODE,
961
+ trustMessage(
962
+ serverName,
963
+ attestation,
964
+ changed ? "changed" : "requires trust",
965
+ ),
966
+ {
967
+ executableIdentityDigest: attestation.identityDigest,
968
+ previousExecutableIdentityDigest: existing?.identityDigest || null,
969
+ },
970
+ );
971
+ }
972
+ store[key] = {
973
+ approvalKind: approvalRecord.approvalKind,
974
+ approvalSource: approvalRecord.approvalSource,
975
+ invocationFingerprint: approvalRecord.fingerprint,
976
+ identityDigest: attestation.identityDigest,
977
+ identity: comparableIdentity(attestation.identity),
978
+ trustedAt: new Date(
979
+ typeof options.now === "number" ? options.now : Date.now(),
980
+ ).toISOString(),
981
+ };
982
+ return {
983
+ status: existing ? "retrusted" : "trusted-first-use",
984
+ key,
985
+ };
986
+ },
987
+ options,
988
+ );
989
+ }
990
+
991
+ export function prepareMcpStdioExecutableIdentity({
992
+ serverName,
993
+ config,
994
+ approval,
995
+ cwd = process.cwd(),
996
+ env = process.env,
997
+ retrust = false,
998
+ storePath: explicitStorePath,
999
+ witnessPath: explicitWitnessPath,
1000
+ materializationRoot,
1001
+ materializationIndexPath,
1002
+ }) {
1003
+ const approvalRecord = resolveMcpStdioExecutionApproval(approval);
1004
+ if (!approvalRecord) {
1005
+ throw identityError(
1006
+ MCP_STDIO_EXECUTABLE_UNATTESTED_CODE,
1007
+ `MCP stdio executable identity for "${serverName}" requires a valid execution approval`,
1008
+ );
1009
+ }
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 || {});
1045
+ const trust = checkOrRecordTrust(serverName, approvalRecord, attestation, {
1046
+ env,
1047
+ retrust,
1048
+ ...(explicitStorePath ? { storePath: explicitStorePath } : {}),
1049
+ ...(explicitWitnessPath ? { witnessPath: explicitWitnessPath } : {}),
1050
+ });
1051
+ const sandboxExecutionContract = issueCapsuleSandboxExecutionContract({
1052
+ serverName,
1053
+ config,
1054
+ attestation,
1055
+ materialization,
1056
+ });
1057
+ const authority = Object.freeze({});
1058
+ issuedLaunchAuthorities.set(
1059
+ authority,
1060
+ Object.freeze({
1061
+ command: attestation.launchCommand,
1062
+ args: Object.freeze([...attestation.launchArgs]),
1063
+ identity: attestation.identity,
1064
+ identityDigest: attestation.identityDigest,
1065
+ materialization,
1066
+ }),
1067
+ );
1068
+ return Object.freeze({
1069
+ command: attestation.launchCommand,
1070
+ args: Object.freeze([...attestation.launchArgs]),
1071
+ identity: attestation.identity,
1072
+ identityDigest: attestation.identityDigest,
1073
+ env: launchEnv,
1074
+ workingDirectory:
1075
+ sandboxExecutionContract?.workingDirectory ||
1076
+ materialization?.capsuleRoot ||
1077
+ cwd,
1078
+ ...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
1079
+ authority,
1080
+ trustStatus: trust.status,
1081
+ });
1082
+ }
1083
+
1084
+ export function consumeMcpStdioExecutableIdentityAuthority(
1085
+ authority,
1086
+ { command, args = [], env = {} },
1087
+ ) {
1088
+ const issued = issuedLaunchAuthorities.get(authority);
1089
+ if (!issued) {
1090
+ throw identityError(
1091
+ MCP_STDIO_EXECUTABLE_AUTHORITY_REPLAYED_CODE,
1092
+ "MCP stdio executable identity authority is invalid or already consumed",
1093
+ );
1094
+ }
1095
+ issuedLaunchAuthorities.delete(authority);
1096
+ assertMcpStdioExecutableEnvironmentSafe(env);
1097
+ if (
1098
+ command !== issued.command ||
1099
+ JSON.stringify(args) !== JSON.stringify(issued.args)
1100
+ ) {
1101
+ throw identityError(
1102
+ MCP_STDIO_EXECUTABLE_CHANGED_CODE,
1103
+ "MCP stdio launch changed after executable identity approval",
1104
+ );
1105
+ }
1106
+ let current = attestMcpStdioExecutableIdentity({
1107
+ command,
1108
+ args,
1109
+ cwd: process.cwd(),
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
+ }
1126
+ if (current.identityDigest !== issued.identityDigest) {
1127
+ throw identityError(
1128
+ MCP_STDIO_EXECUTABLE_CHANGED_CODE,
1129
+ "MCP stdio executable or entrypoint bytes changed before spawn",
1130
+ );
1131
+ }
1132
+ return Object.freeze({ identityDigest: issued.identityDigest });
1133
+ }