chainlesschain 0.163.2 → 0.163.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/package.json +3 -2
  2. package/scripts/command-lifecycle-report.mjs +330 -38
  3. package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DB5X7qqV.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-DxHRtKLk.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-DIH5QrxT.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-uu0oxVz7.js} +5 -5
  7. package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-BNSMiW9Y.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-DcAzB4_x.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-ClSsvjIc.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-BMciY0js.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-Hn3b57Sh.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-CoSlC53V.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-CFevJ3So.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-B4L4Eu4w.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-C77FgRxF.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-BrLtJLH3.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-Be7pXXhA.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-DuGPecjo.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-CzS66mjP.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-CEhjCoyK.js} +2 -2
  21. package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-D7ir8Vja.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-BcXmuLxf.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-Cto1dtBy.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-B7KjgwOX.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-COMqxijg.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BZrya-nf.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-AmVeMq0J.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-kZU8c-27.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-sOV22v2-.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-BUuLtGCz.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-K-ZxsLsH.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-D3OEnawI.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-Bll0gRmP.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-DBXwjr53.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-CEzPwEfP.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-Bf3DEqP6.js → MarkdownRenderer-CLmCbHx6.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BYyRyOPz.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DVu8w_rM.js} +5 -5
  39. package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-tenuZ_hO.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-Bxgy4ozS.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-Dgx1D6sc.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-CRfJ8r8j.js} +5 -5
  43. package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-BBJq4Aiz.js} +5 -5
  44. package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-B3ii9lai.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-FIi-Cubv.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-DIq8MPf1.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-BFOD9eGd.js} +1 -1
  48. package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer--41u7vre.js} +1 -1
  49. package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-B_0AWztW.js} +2 -2
  50. package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-B6I7TuXv.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-Cdf6qens.js} +2 -2
  52. package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-B_0FcQJY.js} +3 -3
  53. package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-6nFBjsQp.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-nQYV7xpn.js} +3 -3
  55. package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-CBVod5jm.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-D52jW1Nt.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Bdx3BYhf.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-b7xr4DUa.js} +2 -2
  59. package/src/assets/web-panel/assets/Projects-DhfifCKx.js +1 -0
  60. package/src/assets/web-panel/assets/Providers-Dy_PgDMr.js +1 -0
  61. package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-FQKSmc3e.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-CSTrhzcF.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend-CxMABOUh.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-Cv2T8yRa.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-CE1k4Vvu.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-BNgNcAV_.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-5ZiBZ3ph.js} +3 -3
  68. package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-mDOIF1UV.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-CtuCTC-e.js} +3 -3
  70. package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DxFO2Zg7.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CKmKkH9y.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-Bmzl9qkk.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-9RqaWHOC.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-CGdzFIYw.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-CvNuCeBT.js} +2 -2
  76. package/src/assets/web-panel/assets/Tasks-B_sLdcnw.js +1 -0
  77. package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-D9_YijdK.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Cuc7BUSP.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-6VYRnJgd.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-GtK8xd8Y.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-BZ94Lt5W.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-DEcZgDaX.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-CC3z9THe.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-Cvtw8cV3.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-CL4e6Bn3.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-ycEemP27.js} +2 -2
  87. package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-DgZ4X_aw.js} +5 -5
  88. package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-D0qMhSv7.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-DsAg01Zn.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-B0oqAhrQ.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-CmX0Rabz.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-Be4cn1Ub.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-CP2qzG6D.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-D5v12kVq.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-0eWLkQKT.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-301ogenv.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-5LGY0JbG.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index-7VKDn-t1.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-8WvYGnON.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-B1YGjOWC.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-B3mUHuVc.js} +1 -1
  102. package/src/assets/web-panel/assets/{index--rq90rL9.js → index-BA4oulaP.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-BBREnn1V.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-BBvyHguy.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-BKmsS2jj.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-BRIqLy1m.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-BXGSbelg.js} +3 -3
  108. package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-BaE0Ui07.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-BoAIAZ5C.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-BqltWojQ.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-CGiIyTZw.js} +1 -1
  112. package/src/assets/web-panel/assets/index-CKB0vbJU.js +1 -0
  113. package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-CZ2wUEgr.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-CZpfzcuy.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-Ckrk3pIV.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-CqoCZ5vR.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-CyWBir7N.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CyyGTMKd.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-D0BSkxYQ.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-D5YVopyr.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-DKUBj36_.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-DM724NgU.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-DQRsQen0.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DgoXmwqB.js +1 -0
  125. package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-DikJ0Qqe.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-caye7AOE.js → index-Dn2dSdXx.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DyUaFWEk.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-Hfe_dEGx.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-_JjRLSD0.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-frSojpHj.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-jK3ax6Z6.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-qMuZ-NRR.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-q_fQ94SE.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-D4VdhJ9_.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-CZJA0MQW.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-BqEs7Sb1.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-BpHEtsu1.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-BW94tY9W.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-BnBjPWP9.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-uUkjQ_hu.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-B-klUqXK.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CvY1cNAs.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-Ix5sEDT2.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-C8qcXOne.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-KNF6Kj8f.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DGJr345n.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-BboN99rI.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-Bgt8Vgte.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-BhJtErgt.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +2 -2
  152. package/src/commands/agenda.js +6 -5
  153. package/src/commands/agent.js +237 -75
  154. package/src/commands/agents.js +6 -0
  155. package/src/commands/artifacts.js +98 -0
  156. package/src/commands/background-session.js +11 -1
  157. package/src/commands/mcp.js +218 -31
  158. package/src/commands/review.js +221 -22
  159. package/src/commands/session-mcp-recovery.js +5 -5
  160. package/src/data/changelog.json +27 -1
  161. package/src/gateways/ws/ws-agent-handler.js +492 -14
  162. package/src/harness/jsonl-session-store.js +39 -1
  163. package/src/harness/mcp-client.js +414 -71
  164. package/src/harness/prompt-compressor.js +24 -0
  165. package/src/harness/provider-backed-compaction.js +205 -0
  166. package/src/harness/worktree-isolator.js +26 -3
  167. package/src/lib/agent-worktree.js +41 -2
  168. package/src/lib/app-preview.js +3 -0
  169. package/src/lib/background-agent-supervisor.js +790 -186
  170. package/src/lib/background-command-argv.js +318 -0
  171. package/src/lib/background-launch-profile.js +1 -0
  172. package/src/lib/background-phase-reporter.js +22 -2
  173. package/src/lib/background-worktree-policy.js +109 -0
  174. package/src/lib/command-lifecycle-report.js +1340 -145
  175. package/src/lib/cowork-mcp-tools.js +51 -6
  176. package/src/lib/cowork-task-runner.js +1 -0
  177. package/src/lib/delivery-coordinator.js +30 -5
  178. package/src/lib/delivery-production-adapter.js +1777 -0
  179. package/src/lib/delivery-production-runner.js +240 -0
  180. package/src/lib/durable-security-store.js +29 -17
  181. package/src/lib/execution-trace/trace-context.cjs +179 -0
  182. package/src/lib/execution-trace/trace-context.js +8 -175
  183. package/src/lib/mcp-call-ledger-store.js +58 -6
  184. package/src/lib/mcp-headers-helper.js +76 -8
  185. package/src/lib/mcp-host-recovery-runtime.js +2 -0
  186. package/src/lib/mcp-ledger-recovery-admission.js +39 -3
  187. package/src/lib/mcp-recovery-adjudication.js +256 -19
  188. package/src/lib/mcp-sandbox-policy.js +184 -0
  189. package/src/lib/mcp-stdio-capsule-builder-worker.cjs +495 -0
  190. package/src/lib/mcp-stdio-executable-identity.js +33 -2
  191. package/src/lib/mcp-stdio-execution-authority.js +163 -0
  192. package/src/lib/mcp-stdio-immutable-vfs-resolver.cjs +830 -0
  193. package/src/lib/mcp-stdio-package-materialization.js +721 -333
  194. package/src/lib/mcp-stdio-workspace-authority.js +252 -0
  195. package/src/lib/observability/index.js +2 -0
  196. package/src/lib/observability/otlp-exporter.js +2 -2
  197. package/src/lib/packer/pack-update-applier.js +8 -1
  198. package/src/lib/packer/pkg-config-generator.js +34 -0
  199. package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
  200. package/src/lib/process-execution-broker/index.js +213 -109
  201. package/src/lib/process-execution-broker/platform-sandbox.js +1024 -169
  202. package/src/lib/process-tree-termination.js +17 -1
  203. package/src/lib/scheduler-kernel/contract.js +315 -0
  204. package/src/lib/scheduler-kernel/store.js +1345 -0
  205. package/src/lib/session-host-lease.js +403 -30
  206. package/src/lib/session-persistence-failure.js +4 -1
  207. package/src/lib/skill-mcp.js +65 -4
  208. package/src/lib/sub-agent-context.js +8 -0
  209. package/src/lib/web-fetch.js +4 -4
  210. package/src/repl/agent-repl.js +285 -38
  211. package/src/runtime/agent-core.js +1044 -90
  212. package/src/runtime/agent-runtime.js +61 -8
  213. package/src/runtime/headless-runner.js +260 -166
  214. package/src/runtime/headless-stream.js +254 -25
  215. package/src/runtime/mcp-config.js +235 -28
  216. package/src/workers/background-agent-worker.js +492 -71
  217. package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
  218. package/src/assets/web-panel/assets/Providers-BGs47s1a.js +0 -1
  219. package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
  220. package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
  221. package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
  222. package/src/assets/web-panel/assets/index-hUpvk6Jl.js +0 -1
@@ -15,9 +15,14 @@ import { pathToFileURL } from "url";
15
15
  import { isProxy } from "node:util/types";
16
16
  import WebSocket from "ws";
17
17
  import { safeJsonParse } from "../lib/safe-json.js";
18
+ import {
19
+ enforceMcpStdioCapsuleHostSandboxPolicy,
20
+ MCP_SANDBOX_POLICY_INVALID_CODE,
21
+ normalizeMcpSandboxPolicy,
22
+ readMcpStdioCwd,
23
+ } from "../lib/mcp-sandbox-policy.js";
18
24
  import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
19
25
  import { currentHostHooksV2WorkspaceBinding } from "../lib/hooks-v2-workspace-context.js";
20
- import { resolvePluginWorkspaceAuthority } from "../lib/plugin-runtime/sandbox-policy.js";
21
26
  import {
22
27
  admitMcpToolList,
23
28
  isMcpToolMetadataError,
@@ -33,11 +38,13 @@ import {
33
38
  consumeMcpStdioExecutionAuthority,
34
39
  materializeApprovedMcpStdioInvocation,
35
40
  renewMcpStdioExecutionAuthority,
41
+ verifyMcpStdioApprovedWorkingDirectory,
36
42
  } from "../lib/mcp-stdio-execution-authority.js";
37
43
  import {
38
- MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY,
44
+ MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES,
39
45
  prepareMcpStdioExecutableIdentity,
40
46
  } from "../lib/mcp-stdio-executable-identity.js";
47
+ import { resolveMcpStdioSandboxContext } from "../lib/mcp-stdio-workspace-authority.js";
41
48
 
42
49
  /**
43
50
  * Injectable dependencies — overridable from tests.
@@ -53,7 +60,9 @@ export const _deps = {
53
60
  consumeMcpStdioExecutionAuthority,
54
61
  materializeApprovedMcpStdioInvocation,
55
62
  renewMcpStdioExecutionAuthority,
63
+ verifyMcpStdioApprovedWorkingDirectory,
56
64
  prepareMcpStdioExecutableIdentity,
65
+ resolveMcpStdioSandboxContext,
57
66
  // Backoff sleep seam (tests override with a no-op so retries don't wait).
58
67
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
59
68
  };
@@ -116,6 +125,60 @@ export function normalizeMcpConfigScope(scope = "user") {
116
125
  return normalized;
117
126
  }
118
127
 
128
+ const INVALID_MCP_CONFIG_ROW = Symbol("invalidMcpConfigRow");
129
+ const INVALID_MCP_CONFIG_ERROR = Symbol("invalidMcpConfigError");
130
+ const INVALID_MCP_CONFIG_RAW_ROW = Symbol("invalidMcpConfigRawRow");
131
+
132
+ function rawMcpRowValue(row, key) {
133
+ if (!row || typeof row !== "object" || isProxy(row)) return undefined;
134
+ const descriptor = Object.getOwnPropertyDescriptor(row, key);
135
+ return descriptor && "value" in descriptor ? descriptor.value : undefined;
136
+ }
137
+
138
+ function mcpRowVisibility(row, { invalid = false, error = null } = {}) {
139
+ const displayName = rawMcpRowValue(row, "display_name");
140
+ const storageName = rawMcpRowValue(row, "name");
141
+ const name =
142
+ typeof displayName === "string" && displayName.length > 0
143
+ ? displayName
144
+ : typeof storageName === "string" && storageName.length > 0
145
+ ? storageName
146
+ : null;
147
+ if (!name) return null;
148
+
149
+ let configScope;
150
+ try {
151
+ configScope = normalizeMcpConfigScope(
152
+ rawMcpRowValue(row, "config_scope") || "user",
153
+ );
154
+ } catch {
155
+ // An unparseable scope cannot safely be treated as the lowest tier.
156
+ configScope = "managed";
157
+ }
158
+ const rawProjectPath = rawMcpRowValue(row, "project_path");
159
+ const visibility = {
160
+ name,
161
+ configScope,
162
+ projectPath:
163
+ typeof rawProjectPath === "string" && rawProjectPath.length > 0
164
+ ? rawProjectPath
165
+ : null,
166
+ autoConnect: rawMcpRowValue(row, "auto_connect") === 1,
167
+ [INVALID_MCP_CONFIG_ROW]: invalid,
168
+ };
169
+ if (invalid) {
170
+ Object.defineProperties(visibility, {
171
+ [INVALID_MCP_CONFIG_ERROR]: { value: error },
172
+ [INVALID_MCP_CONFIG_RAW_ROW]: { value: row },
173
+ });
174
+ }
175
+ return visibility;
176
+ }
177
+
178
+ function isInvalidMcpConfigRow(config) {
179
+ return config?.[INVALID_MCP_CONFIG_ROW] === true;
180
+ }
181
+
119
182
  /**
120
183
  * Default per-call timeout for HTTP MCP requests, mirroring the 30s stdio
121
184
  * timeout so a hung/dead HTTP server can't block a request forever. Servers
@@ -549,6 +612,102 @@ function mcpTransportError(code, message, details = {}) {
549
612
  return error;
550
613
  }
551
614
 
615
+ function dispatchMcpToolRequest(options, metadata, dispatch) {
616
+ const admission = options?.dispatchAdmission ?? null;
617
+ if (metadata.method !== "tools/call" || admission === null) {
618
+ return dispatch();
619
+ }
620
+ if (typeof admission !== "function") {
621
+ throw mcpTransportError(
622
+ "CC_MCP_DISPATCH_ADMISSION_INVALID",
623
+ "MCP tool dispatch authority is invalid",
624
+ {
625
+ transport: metadata.transport,
626
+ dispatched: false,
627
+ outcomeUnknown: false,
628
+ },
629
+ );
630
+ }
631
+
632
+ let admissionOpen = true;
633
+ let dispatched = false;
634
+ let dispatchResult;
635
+ const admittedDispatch = () => {
636
+ if (!admissionOpen || dispatched) {
637
+ throw mcpTransportError(
638
+ "CC_MCP_DISPATCH_ADMISSION_INVALID",
639
+ "MCP tool dispatch authority invoked its one-shot send outside the admission window",
640
+ { transport: metadata.transport },
641
+ );
642
+ }
643
+ dispatched = true;
644
+ dispatchResult = dispatch();
645
+ return dispatchResult;
646
+ };
647
+
648
+ try {
649
+ const admissionResult = admission(
650
+ Object.freeze({ ...metadata }),
651
+ admittedDispatch,
652
+ );
653
+ if (!dispatched) {
654
+ throw mcpTransportError(
655
+ "CC_MCP_DISPATCH_ADMISSION_REQUIRED",
656
+ "MCP tool dispatch authority did not synchronously admit the request",
657
+ { transport: metadata.transport },
658
+ );
659
+ }
660
+ if (admissionResult !== dispatchResult) {
661
+ throw mcpTransportError(
662
+ "CC_MCP_DISPATCH_AUTHORITY_OUTCOME_UNKNOWN",
663
+ "MCP tool dispatch started, but the host did not synchronously return the admitted send",
664
+ {
665
+ transport: metadata.transport,
666
+ dispatched: true,
667
+ outcomeUnknown: true,
668
+ },
669
+ );
670
+ }
671
+ return dispatchResult;
672
+ } catch (cause) {
673
+ if (!dispatched) {
674
+ if (safeRpcProperty(cause, "dispatched") === false) throw cause;
675
+ const causeCode = safeRpcProperty(cause, "code");
676
+ const error = mcpTransportError(
677
+ typeof causeCode === "string" && causeCode.startsWith("CC_")
678
+ ? causeCode
679
+ : "CC_MCP_DISPATCH_NOT_ADMITTED",
680
+ "MCP tool request was not dispatched because host authority denied admission",
681
+ {
682
+ transport: metadata.transport,
683
+ dispatched: false,
684
+ outcomeUnknown: false,
685
+ },
686
+ );
687
+ error.cause = cause;
688
+ throw error;
689
+ }
690
+ if (safeRpcProperty(cause, "outcomeUnknown") === true) {
691
+ throw cause;
692
+ }
693
+ const error = mcpTransportError(
694
+ "CC_MCP_DISPATCH_AUTHORITY_OUTCOME_UNKNOWN",
695
+ "MCP tool dispatch started, but the host could not durably confirm its dispatch authority",
696
+ {
697
+ transport: metadata.transport,
698
+ dispatched: true,
699
+ outcomeUnknown: true,
700
+ },
701
+ );
702
+ error.cause = cause;
703
+ throw error;
704
+ } finally {
705
+ // An admission callback must invoke the send before returning. This also
706
+ // prevents an async callback from retaining a usable dispatch capability.
707
+ admissionOpen = false;
708
+ }
709
+ }
710
+
552
711
  function mcpWebSocketPayloadTooLargeError(
553
712
  serverName,
554
713
  { transport, url, limitBytes, closeCode = 1009 },
@@ -1775,6 +1934,11 @@ export class MCPClient extends EventEmitter {
1775
1934
  else this._reconnectors.delete(name);
1776
1935
  }
1777
1936
 
1937
+ /** Return the opaque host authority used to resolve policy-bearing MCP cwd. */
1938
+ executionWorkspaceBinding() {
1939
+ return this._workspaceBinding;
1940
+ }
1941
+
1778
1942
  async _connectionHeaders(name, config, transportKind) {
1779
1943
  const staticHeaders = { ...(config?.headers || {}) };
1780
1944
  if (!config?.headersHelper) return staticHeaders;
@@ -1853,7 +2017,8 @@ export class MCPClient extends EventEmitter {
1853
2017
  mcpStdioExecutionAuthority: _stdioExecutionAuthority,
1854
2018
  ...untrustedSourceConfig
1855
2019
  } = config;
1856
- const sourceConfig = approvedStdioInvocation
2020
+ void _stdioExecutionAuthority;
2021
+ let sourceConfig = approvedStdioInvocation
1857
2022
  ? { ...untrustedSourceConfig, ...approvedStdioInvocation }
1858
2023
  : untrustedSourceConfig;
1859
2024
  let pluginWorkspaceRoot = null;
@@ -1863,34 +2028,34 @@ export class MCPClient extends EventEmitter {
1863
2028
  let stdioExecutableWorkingDirectory = null;
1864
2029
  let stdioExecutableIdentityDigest = null;
1865
2030
  let stdioCapsuleSandboxExecutionContract = null;
2031
+ let stdioTrustedWorkingDirectory = null;
1866
2032
  let runtimeConfig = sourceConfig;
1867
2033
  if (transportKind === "stdio") {
1868
- const isPlugin = sourceConfig.origin === "plugin:mcp";
1869
- pluginWorkspaceRoot = isPlugin
1870
- ? resolvePluginWorkspaceAuthority(
1871
- sourceConfig.pluginWorkspaceAuthority,
1872
- {
1873
- origin: sourceConfig.origin,
1874
- pluginId: sourceConfig.pluginId,
1875
- pluginVersion: sourceConfig.pluginVersion,
1876
- pluginSource: sourceConfig.pluginSource,
1877
- },
1878
- )
1879
- : null;
1880
- if (
1881
- isPlugin &&
1882
- sourceConfig.sandboxPolicy?.requiredBoundaries?.length > 0 &&
1883
- !pluginWorkspaceRoot
1884
- ) {
1885
- throw new Error(
1886
- `plugin MCP server "${name}" is missing its trusted workspace authority`,
2034
+ const sandboxContext = _deps.resolveMcpStdioSandboxContext({
2035
+ serverName: name,
2036
+ config: sourceConfig,
2037
+ workspaceBinding: this._workspaceBinding,
2038
+ });
2039
+ pluginWorkspaceRoot = sandboxContext.pluginWorkspaceRoot;
2040
+ stdioTrustedWorkingDirectory = sandboxContext.workingDirectory;
2041
+ if (sandboxContext.sandboxPolicy && sourceConfig.cwd) {
2042
+ _deps.verifyMcpStdioApprovedWorkingDirectory(
2043
+ stdioExecutionApproval,
2044
+ stdioTrustedWorkingDirectory,
1887
2045
  );
1888
2046
  }
2047
+ if (sandboxContext.sandboxPolicy) {
2048
+ sourceConfig = {
2049
+ ...sourceConfig,
2050
+ sandboxPolicy: sandboxContext.sandboxPolicy,
2051
+ };
2052
+ }
1889
2053
  const prepared = _deps.prepareMcpStdioExecutableIdentity({
1890
2054
  serverName: name,
1891
2055
  config: sourceConfig,
1892
2056
  approval: stdioExecutionApproval,
1893
- cwd: pluginWorkspaceRoot || process.cwd(),
2057
+ cwd:
2058
+ stdioTrustedWorkingDirectory || pluginWorkspaceRoot || process.cwd(),
1894
2059
  env: { ...process.env, ...this._agentIdentityEnv() },
1895
2060
  });
1896
2061
  runtimeConfig = {
@@ -1905,6 +2070,17 @@ export class MCPClient extends EventEmitter {
1905
2070
  stdioExecutableIdentityDigest = prepared.identityDigest;
1906
2071
  stdioCapsuleSandboxExecutionContract =
1907
2072
  prepared.sandboxExecutionContract || null;
2073
+ if (stdioCapsuleSandboxExecutionContract) {
2074
+ runtimeConfig = {
2075
+ ...runtimeConfig,
2076
+ sandboxPolicy: enforceMcpStdioCapsuleHostSandboxPolicy(
2077
+ sourceConfig.sandboxPolicy,
2078
+ {
2079
+ label: `MCP stdio server "${name}" capsule sandboxPolicy`,
2080
+ },
2081
+ ),
2082
+ };
2083
+ }
1908
2084
  }
1909
2085
  const connectionHeaders = await this._connectionHeaders(
1910
2086
  name,
@@ -2306,6 +2482,7 @@ export class MCPClient extends EventEmitter {
2306
2482
  const spawnOptions = {
2307
2483
  cwd:
2308
2484
  stdioExecutableWorkingDirectory ||
2485
+ stdioTrustedWorkingDirectory ||
2309
2486
  pluginWorkspaceRoot ||
2310
2487
  process.cwd(),
2311
2488
  stdio: ["pipe", "pipe", "pipe"],
@@ -2325,17 +2502,13 @@ export class MCPClient extends EventEmitter {
2325
2502
  // Broker's Job Object boundary so an unexpectedly exiting MCP root
2326
2503
  // cannot orphan grandchildren before taskkill can enumerate them.
2327
2504
  detached: process.platform !== "win32",
2328
- ...(process.platform === "win32" ||
2329
- stdioCapsuleSandboxExecutionContract
2505
+ ...(stdioCapsuleSandboxExecutionContract
2330
2506
  ? {
2331
- requiredBoundaries: [
2332
- ...(process.platform === "win32" ? ["process-tree"] : []),
2333
- ...(stdioCapsuleSandboxExecutionContract
2334
- ? [MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY]
2335
- : []),
2336
- ],
2507
+ requiredBoundaries: MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES,
2337
2508
  }
2338
- : {}),
2509
+ : process.platform === "win32"
2510
+ ? { requiredBoundaries: ["process-tree"] }
2511
+ : {}),
2339
2512
  pluginId: config.pluginId,
2340
2513
  pluginVersion: config.pluginVersion,
2341
2514
  pluginSource: config.pluginSource,
@@ -2353,15 +2526,20 @@ export class MCPClient extends EventEmitter {
2353
2526
  config.args || [],
2354
2527
  { ...spawnOptions, detached: false },
2355
2528
  stdioExecutableWorkingDirectory ||
2529
+ stdioTrustedWorkingDirectory ||
2356
2530
  pluginWorkspaceRoot ||
2357
2531
  process.cwd(),
2358
2532
  )
2359
2533
  : null);
2534
+ const effectiveRuntimeSandboxBoundaries = new Set([
2535
+ ...(spawnOptions.requiredBoundaries || []),
2536
+ ...(config.sandboxPolicy?.requiredBoundaries || []),
2537
+ ]);
2360
2538
  const capsuleUsesLinuxProcessTreeSandbox =
2361
2539
  Boolean(stdioCapsuleSandboxExecutionContract) &&
2362
2540
  process.platform === "linux" &&
2363
- sourceConfig.sandboxPolicy?.requiredBoundaries?.some((boundary) =>
2364
- ["filesystem", "network", "process-tree"].includes(boundary),
2541
+ MCP_STDIO_CAPSULE_REQUIRED_BOUNDARIES.every((boundary) =>
2542
+ effectiveRuntimeSandboxBoundaries.has(boundary),
2365
2543
  );
2366
2544
  const capsuleUsesPosixProcessGroup =
2367
2545
  Boolean(stdioCapsuleSandboxExecutionContract) &&
@@ -2825,7 +3003,7 @@ export class MCPClient extends EventEmitter {
2825
3003
  * @param {string} serverName - Server name
2826
3004
  * @param {string} toolName - Tool name
2827
3005
  * @param {object} args - Tool arguments
2828
- * @param {{signal?: AbortSignal}} options - Host-owned cancellation
3006
+ * @param {{signal?: AbortSignal, dispatchAdmission?: Function}} options - Host-owned cancellation and dispatch authority
2829
3007
  */
2830
3008
  async callTool(serverName, toolName, args = {}, options = {}) {
2831
3009
  if (options?.signal?.aborted) {
@@ -3311,8 +3489,19 @@ export class MCPClient extends EventEmitter {
3311
3489
  }
3312
3490
 
3313
3491
  try {
3314
- dispatched = true;
3315
- entry.process.stdin.write(message + "\n");
3492
+ dispatchMcpToolRequest(
3493
+ options,
3494
+ {
3495
+ serverName,
3496
+ method,
3497
+ transport: entry.transportKind || "stdio",
3498
+ requestId: id,
3499
+ },
3500
+ () => {
3501
+ dispatched = true;
3502
+ return entry.process.stdin.write(message + "\n");
3503
+ },
3504
+ );
3316
3505
  } catch (err) {
3317
3506
  settle(reject, err);
3318
3507
  }
@@ -3429,13 +3618,26 @@ export class MCPClient extends EventEmitter {
3429
3618
  );
3430
3619
  };
3431
3620
  try {
3432
- dispatched = true;
3433
- entry.socket.send(message, (cause) => {
3434
- if (!cause) return;
3435
- failDispatch();
3436
- });
3437
- } catch {
3438
- failDispatch();
3621
+ dispatchMcpToolRequest(
3622
+ options,
3623
+ {
3624
+ serverName,
3625
+ method,
3626
+ transport: entry.transportKind || "ws",
3627
+ requestId: id,
3628
+ },
3629
+ () => {
3630
+ dispatched = true;
3631
+ return entry.socket.send(message, (cause) => {
3632
+ if (!cause) return;
3633
+ failDispatch();
3634
+ });
3635
+ },
3636
+ );
3637
+ } catch (error) {
3638
+ if (error?.outcomeUnknown === true) settle(reject, error);
3639
+ else if (!dispatched) settle(reject, error);
3640
+ else failDispatch();
3439
3641
  }
3440
3642
  });
3441
3643
  }
@@ -3773,13 +3975,24 @@ export class MCPClient extends EventEmitter {
3773
3975
 
3774
3976
  try {
3775
3977
  throwIfAborted();
3776
- const responsePromise = _deps.fetch(entry.httpUrl, {
3777
- method: "POST",
3778
- headers,
3779
- body,
3780
- ...(controller ? { signal: controller.signal } : {}),
3781
- });
3782
- if (request) request.dispatched = true;
3978
+ const responsePromise = dispatchMcpToolRequest(
3979
+ options,
3980
+ {
3981
+ serverName,
3982
+ method,
3983
+ transport: entry.transportKind || "http",
3984
+ requestId: id,
3985
+ },
3986
+ () => {
3987
+ if (request) request.dispatched = true;
3988
+ return _deps.fetch(entry.httpUrl, {
3989
+ method: "POST",
3990
+ headers,
3991
+ body,
3992
+ ...(controller ? { signal: controller.signal } : {}),
3993
+ });
3994
+ },
3995
+ );
3783
3996
  const response = await responsePromise;
3784
3997
  if (request) request.response = response;
3785
3998
  throwIfAborted();
@@ -4933,6 +5146,8 @@ export class MCPServerConfig {
4933
5146
  args TEXT DEFAULT '[]',
4934
5147
  env TEXT DEFAULT '{}',
4935
5148
  runtime_kind TEXT,
5149
+ cwd TEXT,
5150
+ sandbox_policy TEXT,
4936
5151
  auto_connect INTEGER DEFAULT 0,
4937
5152
  url TEXT,
4938
5153
  transport TEXT DEFAULT 'stdio',
@@ -4949,9 +5164,9 @@ export class MCPServerConfig {
4949
5164
  }
4950
5165
 
4951
5166
  /**
4952
- * Idempotent schema migration for databases that predate url/transport
4953
- * columns. Silently skipped on mocks or anything that doesn't expose
4954
- * `pragma()`.
5167
+ * Idempotent schema migration for databases that predate runtime, transport,
5168
+ * sandbox-policy, and scope columns. Silently skipped on mocks or anything
5169
+ * that doesn't expose `pragma()`.
4955
5170
  */
4956
5171
  _migrateSchema() {
4957
5172
  try {
@@ -4967,6 +5182,12 @@ export class MCPServerConfig {
4967
5182
  if (!cols.has("runtime_kind")) {
4968
5183
  this.db.exec("ALTER TABLE mcp_servers ADD COLUMN runtime_kind TEXT");
4969
5184
  }
5185
+ if (!cols.has("cwd")) {
5186
+ this.db.exec("ALTER TABLE mcp_servers ADD COLUMN cwd TEXT");
5187
+ }
5188
+ if (!cols.has("sandbox_policy")) {
5189
+ this.db.exec("ALTER TABLE mcp_servers ADD COLUMN sandbox_policy TEXT");
5190
+ }
4970
5191
  if (!cols.has("transport")) {
4971
5192
  this.db.exec(
4972
5193
  "ALTER TABLE mcp_servers ADD COLUMN transport TEXT DEFAULT 'stdio'",
@@ -5000,6 +5221,13 @@ export class MCPServerConfig {
5000
5221
  }
5001
5222
 
5002
5223
  add(name, config) {
5224
+ if (isProxy(config)) {
5225
+ const error = new TypeError(
5226
+ `MCP server "${name}" config must not be a Proxy`,
5227
+ );
5228
+ error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
5229
+ throw error;
5230
+ }
5003
5231
  const url = config.url || null;
5004
5232
  const transport =
5005
5233
  config.transport || (url ? inferTransport({ url }) : "stdio");
@@ -5027,6 +5255,17 @@ export class MCPServerConfig {
5027
5255
  typeof config.headersHelper === "string" && config.headersHelper.trim()
5028
5256
  ? config.headersHelper
5029
5257
  : null;
5258
+ const sandboxPolicyDescriptor = Object.getOwnPropertyDescriptor(
5259
+ config,
5260
+ "sandboxPolicy",
5261
+ );
5262
+ if (sandboxPolicyDescriptor && !("value" in sandboxPolicyDescriptor)) {
5263
+ const error = new TypeError(
5264
+ `MCP server "${name}" sandboxPolicy must be an own data property`,
5265
+ );
5266
+ error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
5267
+ throw error;
5268
+ }
5030
5269
  // The legacy table uses `name` as its primary key. Encode workspace-bound
5031
5270
  // identities in that key so `foo` can coexist at local/project/user scope
5032
5271
  // while retaining the old key for user rows and backwards compatibility.
@@ -5034,9 +5273,32 @@ export class MCPServerConfig {
5034
5273
  configScope === "user"
5035
5274
  ? name
5036
5275
  : `${configScope}:${Buffer.from(projectPath || "managed", "utf8").toString("base64url")}:${name}`;
5276
+ const existingRow = this.db
5277
+ .prepare("SELECT * FROM mcp_servers WHERE name = ?")
5278
+ .get(storageName);
5279
+ const cwdUpdate = readMcpStdioCwd(config, {
5280
+ label: `MCP server "${name}"`,
5281
+ });
5282
+ const storedCwd = existingRow
5283
+ ? readMcpStdioCwd(existingRow, {
5284
+ label: `Stored MCP server "${name}"`,
5285
+ }).cwd
5286
+ : null;
5287
+ let workingDirectory = storedCwd;
5288
+ if (cwdUpdate.present) workingDirectory = cwdUpdate.cwd;
5289
+ let sandboxPolicy;
5290
+ if (sandboxPolicyDescriptor) {
5291
+ sandboxPolicy = normalizeMcpSandboxPolicy(sandboxPolicyDescriptor.value, {
5292
+ label: `MCP server "${name}" sandboxPolicy`,
5293
+ });
5294
+ } else {
5295
+ sandboxPolicy = existingRow
5296
+ ? this._rowToConfig(existingRow).sandboxPolicy || null
5297
+ : null;
5298
+ }
5037
5299
  this.db
5038
5300
  .prepare(
5039
- "INSERT OR REPLACE INTO mcp_servers (name, command, args, env, runtime_kind, auto_connect, url, transport, headers, headers_helper, config_scope, config_source, project_path, display_name, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))",
5301
+ "INSERT OR REPLACE INTO mcp_servers (name, command, args, env, runtime_kind, cwd, sandbox_policy, auto_connect, url, transport, headers, headers_helper, config_scope, config_source, project_path, display_name, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))",
5040
5302
  )
5041
5303
  .run(
5042
5304
  storageName,
@@ -5044,6 +5306,8 @@ export class MCPServerConfig {
5044
5306
  JSON.stringify(config.args || []),
5045
5307
  JSON.stringify(config.env || {}),
5046
5308
  config.runtimeKind || null,
5309
+ workingDirectory,
5310
+ sandboxPolicy ? JSON.stringify(sandboxPolicy) : null,
5047
5311
  config.autoConnect ? 1 : 0,
5048
5312
  url,
5049
5313
  transport,
@@ -5080,6 +5344,26 @@ export class MCPServerConfig {
5080
5344
  }
5081
5345
 
5082
5346
  _rowToConfig(row) {
5347
+ const { cwd } = readMcpStdioCwd(row, {
5348
+ label: `Stored MCP server "${row?.display_name || row?.name || "unknown"}"`,
5349
+ });
5350
+ let sandboxPolicy = null;
5351
+ if (row.sandbox_policy != null && row.sandbox_policy !== "") {
5352
+ let parsedPolicy;
5353
+ try {
5354
+ parsedPolicy = JSON.parse(row.sandbox_policy);
5355
+ } catch (cause) {
5356
+ const error = new TypeError(
5357
+ `Stored MCP sandbox policy for "${row.display_name || row.name}" is not valid JSON`,
5358
+ { cause },
5359
+ );
5360
+ error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
5361
+ throw error;
5362
+ }
5363
+ sandboxPolicy = normalizeMcpSandboxPolicy(parsedPolicy, {
5364
+ label: `Stored MCP sandbox policy for "${row.display_name || row.name}"`,
5365
+ });
5366
+ }
5083
5367
  const config = {
5084
5368
  name: row.display_name || row.name,
5085
5369
  command: row.command || null,
@@ -5089,6 +5373,8 @@ export class MCPServerConfig {
5089
5373
  args: safeJsonParse(row.args, []),
5090
5374
  env: safeJsonParse(row.env, {}),
5091
5375
  runtimeKind: row.runtime_kind || null,
5376
+ ...(cwd ? { cwd } : {}),
5377
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
5092
5378
  autoConnect: row.auto_connect === 1,
5093
5379
  url: row.url || null,
5094
5380
  transport:
@@ -5116,39 +5402,96 @@ export class MCPServerConfig {
5116
5402
  return this.list(options).find((config) => config.name === name) || null;
5117
5403
  }
5118
5404
 
5405
+ _rowsToConfigs(rows, options = {}) {
5406
+ const configs = [];
5407
+ for (const row of rows) {
5408
+ const visibility = mcpRowVisibility(row);
5409
+ if (!visibility || !this._isVisible(visibility, options)) continue;
5410
+ let config;
5411
+ try {
5412
+ config = this._rowToConfig(row);
5413
+ } catch (error) {
5414
+ const invalid = mcpRowVisibility(row, { invalid: true, error });
5415
+ if (invalid) configs.push(invalid);
5416
+ continue;
5417
+ }
5418
+ configs.push(config);
5419
+ }
5420
+ return configs;
5421
+ }
5422
+
5423
+ _publicConfigs(configs) {
5424
+ return configs.filter((config) => !isInvalidMcpConfigRow(config));
5425
+ }
5426
+
5427
+ _reportInvalidConfigs(configs, options = {}) {
5428
+ if (typeof options.onInvalidRow !== "function") return;
5429
+ for (const config of configs) {
5430
+ if (!isInvalidMcpConfigRow(config)) continue;
5431
+ try {
5432
+ options.onInvalidRow(
5433
+ config[INVALID_MCP_CONFIG_ERROR],
5434
+ config[INVALID_MCP_CONFIG_RAW_ROW],
5435
+ );
5436
+ } catch {
5437
+ // Diagnostics must not turn one corrupt row into a global outage.
5438
+ }
5439
+ }
5440
+ }
5441
+
5119
5442
  list(options = {}) {
5120
5443
  const rows = this.db
5121
5444
  .prepare("SELECT * FROM mcp_servers ORDER BY name")
5122
5445
  .all();
5123
- const visible = rows
5124
- .map((row) => this._rowToConfig(row))
5125
- .filter((config) => this._isVisible(config, options));
5126
- return options.allScopes === true || options.scope
5127
- ? visible
5128
- : this._effectiveByName(visible);
5446
+ const visible = this._rowsToConfigs(rows, options);
5447
+ const selected =
5448
+ options.allScopes === true ? visible : this._effectiveByName(visible);
5449
+ this._reportInvalidConfigs(selected, options);
5450
+ return this._publicConfigs(selected);
5129
5451
  }
5130
5452
 
5131
5453
  getAutoConnect(options = {}) {
5132
5454
  const rows = this.db
5133
- .prepare("SELECT * FROM mcp_servers WHERE auto_connect = ? ORDER BY name")
5134
- .all(1);
5135
- const visible = rows
5136
- .map((row) => this._rowToConfig(row))
5137
- .filter((config) => this._isVisible(config, options));
5138
- return options.allScopes === true || options.scope
5139
- ? visible
5140
- : this._effectiveByName(visible);
5455
+ .prepare("SELECT * FROM mcp_servers ORDER BY name")
5456
+ .all();
5457
+ const visible = this._rowsToConfigs(rows, options);
5458
+ const selected =
5459
+ options.allScopes === true ? visible : this._effectiveByName(visible);
5460
+ this._reportInvalidConfigs(selected, options);
5461
+ return this._publicConfigs(selected).filter(
5462
+ (config) => config.autoConnect === true,
5463
+ );
5141
5464
  }
5142
5465
 
5143
5466
  _effectiveByName(configs) {
5144
5467
  const priority = { managed: 4, local: 3, project: 2, user: 1 };
5468
+ const specificity = (config) => {
5469
+ if (config.configScope !== "local" && config.configScope !== "project") {
5470
+ return 0;
5471
+ }
5472
+ if (typeof config.projectPath !== "string") return 0;
5473
+ try {
5474
+ return pathToFileURL(config.projectPath).href.length;
5475
+ } catch {
5476
+ return 0;
5477
+ }
5478
+ };
5145
5479
  const selected = new Map();
5146
5480
  for (const config of configs) {
5147
5481
  const current = selected.get(config.name);
5482
+ const candidatePriority = priority[config.configScope] || 0;
5483
+ const currentPriority = current ? priority[current.configScope] || 0 : -1;
5484
+ const candidateSpecificity = specificity(config);
5485
+ const currentSpecificity = current ? specificity(current) : -1;
5148
5486
  if (
5149
5487
  !current ||
5150
- (priority[config.configScope] || 0) >
5151
- (priority[current.configScope] || 0)
5488
+ candidatePriority > currentPriority ||
5489
+ (candidatePriority === currentPriority &&
5490
+ candidateSpecificity > currentSpecificity) ||
5491
+ (candidatePriority === currentPriority &&
5492
+ candidateSpecificity === currentSpecificity &&
5493
+ isInvalidMcpConfigRow(config) &&
5494
+ !isInvalidMcpConfigRow(current))
5152
5495
  ) {
5153
5496
  selected.set(config.name, config);
5154
5497
  }