chainlesschain 0.163.2 → 0.163.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DIY6KxV-.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-BXH0muPt.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-D39w8vmg.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-CUU1zvLs.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-D8azHO3T.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-D0tAwXTg.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-C29mhJll.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-D6Aa1Gro.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-B1uoHbPQ.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-Bs4DLOR5.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-DUYIBSle.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-CNcBPEa4.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-CIY89763.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-CsPYFmgo.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-BBocEge4.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-TQ8Ux0Lf.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-DfBsZTft.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-Cg-I6FVH.js} +2 -2
  20. package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-DTQzfRTp.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-DGxEKGpR.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-DLZ1RDSr.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-XneICKNI.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-BRU00J1-.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BzCbJuAy.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-D7BRyNBG.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-D9DeFIhW.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-6rAtA9Ko.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-8Hqy80pU.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-Cmvkj1rt.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-BRiQUjjw.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-CKN_EzVf.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-BCDHwpwQ.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-DK9ovhaB.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BILPGakp.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DTVCOr9-.js} +5 -5
  38. package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-Hzc1iow4.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-GXfgnGYV.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-B-3TJWsS.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-DwSJMWtK.js} +5 -5
  42. package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-D2uxf-DA.js} +5 -5
  43. package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-DG6kbKK0.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-f5p49EDU.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-FUgHyzSD.js} +3 -3
  46. package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-DCHGee7e.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer-Dm6LlIoa.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-BawA7qAg.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-D6a5wZU1.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-K8WavwnA.js} +2 -2
  51. package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-4JAJYaqx.js} +3 -3
  52. package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-BDWvi8PD.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-DF_K4E9i.js} +3 -3
  54. package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-BYeZlesI.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-BIHlgnIc.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Cvf-6B5P.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-CnwfJrRz.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-b-jPXzF4.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-BGs47s1a.js → Providers-CxC7JuYv.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-VLf3gAfL.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-B5vGjRiF.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend--UW4iYs-.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-DMSPcfKR.js} +2 -2
  64. package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-kQXlEHmj.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-CxGu3pit.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-Bi5lo8MZ.js} +2 -2
  67. package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-Cq_H5RTh.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-YFbmkaQS.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DLOGdaLm.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CteIxb3D.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-CccopaIZ.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-AzfEQKX1.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-ekGo5l77.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-BmZmAgYH.js} +2 -2
  75. package/src/assets/web-panel/assets/Tasks-D7q3Z4uL.js +1 -0
  76. package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-CX1Tgeka.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Zc59jF4i.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-CXOD7y83.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-B1aBHPBo.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-zALlOq_U.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-CQfJMXaq.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-BU0RcM-w.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-IFZ3HsbH.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-BzslgW0v.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-BZAcQNS6.js} +2 -2
  86. package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-CBK1DMi0.js} +4 -4
  87. package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-BlMTPzO-.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-B51-8QVn.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-504RTByU.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-C-GP6IQM.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-DOkIj7ID.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-DN7V-_FW.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index--n-ZciV3.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-7jIqHbzw.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-B2-x9bS8.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-BFIFJ5Dr.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-B_vGU-Js.js} +1 -1
  99. package/src/assets/web-panel/assets/{index--rq90rL9.js → index-Bkxhaeta.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-By6u5IV1.js} +1 -1
  101. package/src/assets/web-panel/assets/index-Bya64sZE.js +1 -0
  102. package/src/assets/web-panel/assets/{index-caye7AOE.js → index-C0k_Nqg9.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-C3TSohzc.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-C4DV_5uA.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-C8UltbXK.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-C9_48OzU.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CKTfZyfU.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-CP2A226L.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-CUhQpiYv.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-CX78k-qf.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-CYopNjcJ.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-Cao5DZy7.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-CfiEM64n.js} +3 -3
  114. package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-ConjaJro.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-CqcLo5WV.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-CySTJKeq.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-D1phyca8.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-D9LyiN-L.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-DA2f1UV3.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-DAOpQvl_.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DE-hmI2l.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-DEMPUVa3.js} +1 -1
  123. package/src/assets/web-panel/assets/index-D_FWpDZM.js +1 -0
  124. package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-DaA__-q1.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-FcV8J_Qz.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-P2mfY27Z.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-et5JIPHO.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-f4Bo4KXm.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-kAqQXfP6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-kt_5rfcZ.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-so8eIIxF.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-yYTQZypF.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-CPYEIdpV.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-Cjim9pxs.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-DuU-cgz3.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-DJHNAkta.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-DSQBgdBj.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-CTmT_j5m.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-Lfm6v-r4.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-CgrcPcDi.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CuE3110i.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-DiK5nbpf.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-DPEJdQaZ.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-CnnodGmp.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DWF84Yav.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-a5b5Dw2f.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-DAluzXOq.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-Dokg7uAZ.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/command-help-index.json +1 -1
  151. package/src/commands/agenda.js +6 -5
  152. package/src/commands/agent.js +237 -75
  153. package/src/commands/agents.js +6 -0
  154. package/src/commands/background-session.js +11 -1
  155. package/src/commands/mcp.js +217 -30
  156. package/src/data/changelog.json +14 -1
  157. package/src/harness/mcp-client.js +242 -42
  158. package/src/harness/worktree-isolator.js +26 -3
  159. package/src/lib/agent-worktree.js +41 -2
  160. package/src/lib/background-agent-supervisor.js +790 -186
  161. package/src/lib/background-command-argv.js +318 -0
  162. package/src/lib/background-launch-profile.js +1 -0
  163. package/src/lib/background-phase-reporter.js +22 -2
  164. package/src/lib/background-worktree-policy.js +109 -0
  165. package/src/lib/cowork-mcp-tools.js +51 -6
  166. package/src/lib/cowork-task-runner.js +1 -0
  167. package/src/lib/mcp-headers-helper.js +76 -8
  168. package/src/lib/mcp-sandbox-policy.js +155 -0
  169. package/src/lib/mcp-stdio-execution-authority.js +163 -0
  170. package/src/lib/mcp-stdio-workspace-authority.js +252 -0
  171. package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
  172. package/src/lib/process-execution-broker/index.js +179 -104
  173. package/src/lib/process-execution-broker/platform-sandbox.js +942 -168
  174. package/src/lib/process-tree-termination.js +17 -1
  175. package/src/lib/skill-mcp.js +65 -4
  176. package/src/lib/web-fetch.js +4 -4
  177. package/src/runtime/agent-runtime.js +61 -8
  178. package/src/runtime/mcp-config.js +235 -28
  179. package/src/workers/background-agent-worker.js +492 -71
  180. package/src/assets/web-panel/assets/MarkdownRenderer-Bf3DEqP6.js +0 -1
  181. package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
  182. package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
  183. package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
  184. package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
  185. package/src/assets/web-panel/assets/index-hUpvk6Jl.js +0 -1
@@ -15,9 +15,13 @@ 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
+ MCP_SANDBOX_POLICY_INVALID_CODE,
20
+ normalizeMcpSandboxPolicy,
21
+ readMcpStdioCwd,
22
+ } from "../lib/mcp-sandbox-policy.js";
18
23
  import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
19
24
  import { currentHostHooksV2WorkspaceBinding } from "../lib/hooks-v2-workspace-context.js";
20
- import { resolvePluginWorkspaceAuthority } from "../lib/plugin-runtime/sandbox-policy.js";
21
25
  import {
22
26
  admitMcpToolList,
23
27
  isMcpToolMetadataError,
@@ -33,11 +37,13 @@ import {
33
37
  consumeMcpStdioExecutionAuthority,
34
38
  materializeApprovedMcpStdioInvocation,
35
39
  renewMcpStdioExecutionAuthority,
40
+ verifyMcpStdioApprovedWorkingDirectory,
36
41
  } from "../lib/mcp-stdio-execution-authority.js";
37
42
  import {
38
43
  MCP_STDIO_CAPSULE_CODE_SNAPSHOT_BOUNDARY,
39
44
  prepareMcpStdioExecutableIdentity,
40
45
  } from "../lib/mcp-stdio-executable-identity.js";
46
+ import { resolveMcpStdioSandboxContext } from "../lib/mcp-stdio-workspace-authority.js";
41
47
 
42
48
  /**
43
49
  * Injectable dependencies — overridable from tests.
@@ -53,7 +59,9 @@ export const _deps = {
53
59
  consumeMcpStdioExecutionAuthority,
54
60
  materializeApprovedMcpStdioInvocation,
55
61
  renewMcpStdioExecutionAuthority,
62
+ verifyMcpStdioApprovedWorkingDirectory,
56
63
  prepareMcpStdioExecutableIdentity,
64
+ resolveMcpStdioSandboxContext,
57
65
  // Backoff sleep seam (tests override with a no-op so retries don't wait).
58
66
  sleep: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
59
67
  };
@@ -116,6 +124,60 @@ export function normalizeMcpConfigScope(scope = "user") {
116
124
  return normalized;
117
125
  }
118
126
 
127
+ const INVALID_MCP_CONFIG_ROW = Symbol("invalidMcpConfigRow");
128
+ const INVALID_MCP_CONFIG_ERROR = Symbol("invalidMcpConfigError");
129
+ const INVALID_MCP_CONFIG_RAW_ROW = Symbol("invalidMcpConfigRawRow");
130
+
131
+ function rawMcpRowValue(row, key) {
132
+ if (!row || typeof row !== "object" || isProxy(row)) return undefined;
133
+ const descriptor = Object.getOwnPropertyDescriptor(row, key);
134
+ return descriptor && "value" in descriptor ? descriptor.value : undefined;
135
+ }
136
+
137
+ function mcpRowVisibility(row, { invalid = false, error = null } = {}) {
138
+ const displayName = rawMcpRowValue(row, "display_name");
139
+ const storageName = rawMcpRowValue(row, "name");
140
+ const name =
141
+ typeof displayName === "string" && displayName.length > 0
142
+ ? displayName
143
+ : typeof storageName === "string" && storageName.length > 0
144
+ ? storageName
145
+ : null;
146
+ if (!name) return null;
147
+
148
+ let configScope;
149
+ try {
150
+ configScope = normalizeMcpConfigScope(
151
+ rawMcpRowValue(row, "config_scope") || "user",
152
+ );
153
+ } catch {
154
+ // An unparseable scope cannot safely be treated as the lowest tier.
155
+ configScope = "managed";
156
+ }
157
+ const rawProjectPath = rawMcpRowValue(row, "project_path");
158
+ const visibility = {
159
+ name,
160
+ configScope,
161
+ projectPath:
162
+ typeof rawProjectPath === "string" && rawProjectPath.length > 0
163
+ ? rawProjectPath
164
+ : null,
165
+ autoConnect: rawMcpRowValue(row, "auto_connect") === 1,
166
+ [INVALID_MCP_CONFIG_ROW]: invalid,
167
+ };
168
+ if (invalid) {
169
+ Object.defineProperties(visibility, {
170
+ [INVALID_MCP_CONFIG_ERROR]: { value: error },
171
+ [INVALID_MCP_CONFIG_RAW_ROW]: { value: row },
172
+ });
173
+ }
174
+ return visibility;
175
+ }
176
+
177
+ function isInvalidMcpConfigRow(config) {
178
+ return config?.[INVALID_MCP_CONFIG_ROW] === true;
179
+ }
180
+
119
181
  /**
120
182
  * Default per-call timeout for HTTP MCP requests, mirroring the 30s stdio
121
183
  * timeout so a hung/dead HTTP server can't block a request forever. Servers
@@ -1775,6 +1837,11 @@ export class MCPClient extends EventEmitter {
1775
1837
  else this._reconnectors.delete(name);
1776
1838
  }
1777
1839
 
1840
+ /** Return the opaque host authority used to resolve policy-bearing MCP cwd. */
1841
+ executionWorkspaceBinding() {
1842
+ return this._workspaceBinding;
1843
+ }
1844
+
1778
1845
  async _connectionHeaders(name, config, transportKind) {
1779
1846
  const staticHeaders = { ...(config?.headers || {}) };
1780
1847
  if (!config?.headersHelper) return staticHeaders;
@@ -1853,7 +1920,8 @@ export class MCPClient extends EventEmitter {
1853
1920
  mcpStdioExecutionAuthority: _stdioExecutionAuthority,
1854
1921
  ...untrustedSourceConfig
1855
1922
  } = config;
1856
- const sourceConfig = approvedStdioInvocation
1923
+ void _stdioExecutionAuthority;
1924
+ let sourceConfig = approvedStdioInvocation
1857
1925
  ? { ...untrustedSourceConfig, ...approvedStdioInvocation }
1858
1926
  : untrustedSourceConfig;
1859
1927
  let pluginWorkspaceRoot = null;
@@ -1863,34 +1931,34 @@ export class MCPClient extends EventEmitter {
1863
1931
  let stdioExecutableWorkingDirectory = null;
1864
1932
  let stdioExecutableIdentityDigest = null;
1865
1933
  let stdioCapsuleSandboxExecutionContract = null;
1934
+ let stdioTrustedWorkingDirectory = null;
1866
1935
  let runtimeConfig = sourceConfig;
1867
1936
  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`,
1937
+ const sandboxContext = _deps.resolveMcpStdioSandboxContext({
1938
+ serverName: name,
1939
+ config: sourceConfig,
1940
+ workspaceBinding: this._workspaceBinding,
1941
+ });
1942
+ pluginWorkspaceRoot = sandboxContext.pluginWorkspaceRoot;
1943
+ stdioTrustedWorkingDirectory = sandboxContext.workingDirectory;
1944
+ if (sandboxContext.sandboxPolicy && sourceConfig.cwd) {
1945
+ _deps.verifyMcpStdioApprovedWorkingDirectory(
1946
+ stdioExecutionApproval,
1947
+ stdioTrustedWorkingDirectory,
1887
1948
  );
1888
1949
  }
1950
+ if (sandboxContext.sandboxPolicy) {
1951
+ sourceConfig = {
1952
+ ...sourceConfig,
1953
+ sandboxPolicy: sandboxContext.sandboxPolicy,
1954
+ };
1955
+ }
1889
1956
  const prepared = _deps.prepareMcpStdioExecutableIdentity({
1890
1957
  serverName: name,
1891
1958
  config: sourceConfig,
1892
1959
  approval: stdioExecutionApproval,
1893
- cwd: pluginWorkspaceRoot || process.cwd(),
1960
+ cwd:
1961
+ stdioTrustedWorkingDirectory || pluginWorkspaceRoot || process.cwd(),
1894
1962
  env: { ...process.env, ...this._agentIdentityEnv() },
1895
1963
  });
1896
1964
  runtimeConfig = {
@@ -2306,6 +2374,7 @@ export class MCPClient extends EventEmitter {
2306
2374
  const spawnOptions = {
2307
2375
  cwd:
2308
2376
  stdioExecutableWorkingDirectory ||
2377
+ stdioTrustedWorkingDirectory ||
2309
2378
  pluginWorkspaceRoot ||
2310
2379
  process.cwd(),
2311
2380
  stdio: ["pipe", "pipe", "pipe"],
@@ -2353,6 +2422,7 @@ export class MCPClient extends EventEmitter {
2353
2422
  config.args || [],
2354
2423
  { ...spawnOptions, detached: false },
2355
2424
  stdioExecutableWorkingDirectory ||
2425
+ stdioTrustedWorkingDirectory ||
2356
2426
  pluginWorkspaceRoot ||
2357
2427
  process.cwd(),
2358
2428
  )
@@ -4933,6 +5003,8 @@ export class MCPServerConfig {
4933
5003
  args TEXT DEFAULT '[]',
4934
5004
  env TEXT DEFAULT '{}',
4935
5005
  runtime_kind TEXT,
5006
+ cwd TEXT,
5007
+ sandbox_policy TEXT,
4936
5008
  auto_connect INTEGER DEFAULT 0,
4937
5009
  url TEXT,
4938
5010
  transport TEXT DEFAULT 'stdio',
@@ -4949,9 +5021,9 @@ export class MCPServerConfig {
4949
5021
  }
4950
5022
 
4951
5023
  /**
4952
- * Idempotent schema migration for databases that predate url/transport
4953
- * columns. Silently skipped on mocks or anything that doesn't expose
4954
- * `pragma()`.
5024
+ * Idempotent schema migration for databases that predate runtime, transport,
5025
+ * sandbox-policy, and scope columns. Silently skipped on mocks or anything
5026
+ * that doesn't expose `pragma()`.
4955
5027
  */
4956
5028
  _migrateSchema() {
4957
5029
  try {
@@ -4967,6 +5039,12 @@ export class MCPServerConfig {
4967
5039
  if (!cols.has("runtime_kind")) {
4968
5040
  this.db.exec("ALTER TABLE mcp_servers ADD COLUMN runtime_kind TEXT");
4969
5041
  }
5042
+ if (!cols.has("cwd")) {
5043
+ this.db.exec("ALTER TABLE mcp_servers ADD COLUMN cwd TEXT");
5044
+ }
5045
+ if (!cols.has("sandbox_policy")) {
5046
+ this.db.exec("ALTER TABLE mcp_servers ADD COLUMN sandbox_policy TEXT");
5047
+ }
4970
5048
  if (!cols.has("transport")) {
4971
5049
  this.db.exec(
4972
5050
  "ALTER TABLE mcp_servers ADD COLUMN transport TEXT DEFAULT 'stdio'",
@@ -5000,6 +5078,13 @@ export class MCPServerConfig {
5000
5078
  }
5001
5079
 
5002
5080
  add(name, config) {
5081
+ if (isProxy(config)) {
5082
+ const error = new TypeError(
5083
+ `MCP server "${name}" config must not be a Proxy`,
5084
+ );
5085
+ error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
5086
+ throw error;
5087
+ }
5003
5088
  const url = config.url || null;
5004
5089
  const transport =
5005
5090
  config.transport || (url ? inferTransport({ url }) : "stdio");
@@ -5027,6 +5112,17 @@ export class MCPServerConfig {
5027
5112
  typeof config.headersHelper === "string" && config.headersHelper.trim()
5028
5113
  ? config.headersHelper
5029
5114
  : null;
5115
+ const sandboxPolicyDescriptor = Object.getOwnPropertyDescriptor(
5116
+ config,
5117
+ "sandboxPolicy",
5118
+ );
5119
+ if (sandboxPolicyDescriptor && !("value" in sandboxPolicyDescriptor)) {
5120
+ const error = new TypeError(
5121
+ `MCP server "${name}" sandboxPolicy must be an own data property`,
5122
+ );
5123
+ error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
5124
+ throw error;
5125
+ }
5030
5126
  // The legacy table uses `name` as its primary key. Encode workspace-bound
5031
5127
  // identities in that key so `foo` can coexist at local/project/user scope
5032
5128
  // while retaining the old key for user rows and backwards compatibility.
@@ -5034,9 +5130,32 @@ export class MCPServerConfig {
5034
5130
  configScope === "user"
5035
5131
  ? name
5036
5132
  : `${configScope}:${Buffer.from(projectPath || "managed", "utf8").toString("base64url")}:${name}`;
5133
+ const existingRow = this.db
5134
+ .prepare("SELECT * FROM mcp_servers WHERE name = ?")
5135
+ .get(storageName);
5136
+ const cwdUpdate = readMcpStdioCwd(config, {
5137
+ label: `MCP server "${name}"`,
5138
+ });
5139
+ const storedCwd = existingRow
5140
+ ? readMcpStdioCwd(existingRow, {
5141
+ label: `Stored MCP server "${name}"`,
5142
+ }).cwd
5143
+ : null;
5144
+ let workingDirectory = storedCwd;
5145
+ if (cwdUpdate.present) workingDirectory = cwdUpdate.cwd;
5146
+ let sandboxPolicy;
5147
+ if (sandboxPolicyDescriptor) {
5148
+ sandboxPolicy = normalizeMcpSandboxPolicy(sandboxPolicyDescriptor.value, {
5149
+ label: `MCP server "${name}" sandboxPolicy`,
5150
+ });
5151
+ } else {
5152
+ sandboxPolicy = existingRow
5153
+ ? this._rowToConfig(existingRow).sandboxPolicy || null
5154
+ : null;
5155
+ }
5037
5156
  this.db
5038
5157
  .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'))",
5158
+ "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
5159
  )
5041
5160
  .run(
5042
5161
  storageName,
@@ -5044,6 +5163,8 @@ export class MCPServerConfig {
5044
5163
  JSON.stringify(config.args || []),
5045
5164
  JSON.stringify(config.env || {}),
5046
5165
  config.runtimeKind || null,
5166
+ workingDirectory,
5167
+ sandboxPolicy ? JSON.stringify(sandboxPolicy) : null,
5047
5168
  config.autoConnect ? 1 : 0,
5048
5169
  url,
5049
5170
  transport,
@@ -5080,6 +5201,26 @@ export class MCPServerConfig {
5080
5201
  }
5081
5202
 
5082
5203
  _rowToConfig(row) {
5204
+ const { cwd } = readMcpStdioCwd(row, {
5205
+ label: `Stored MCP server "${row?.display_name || row?.name || "unknown"}"`,
5206
+ });
5207
+ let sandboxPolicy = null;
5208
+ if (row.sandbox_policy != null && row.sandbox_policy !== "") {
5209
+ let parsedPolicy;
5210
+ try {
5211
+ parsedPolicy = JSON.parse(row.sandbox_policy);
5212
+ } catch (cause) {
5213
+ const error = new TypeError(
5214
+ `Stored MCP sandbox policy for "${row.display_name || row.name}" is not valid JSON`,
5215
+ { cause },
5216
+ );
5217
+ error.code = MCP_SANDBOX_POLICY_INVALID_CODE;
5218
+ throw error;
5219
+ }
5220
+ sandboxPolicy = normalizeMcpSandboxPolicy(parsedPolicy, {
5221
+ label: `Stored MCP sandbox policy for "${row.display_name || row.name}"`,
5222
+ });
5223
+ }
5083
5224
  const config = {
5084
5225
  name: row.display_name || row.name,
5085
5226
  command: row.command || null,
@@ -5089,6 +5230,8 @@ export class MCPServerConfig {
5089
5230
  args: safeJsonParse(row.args, []),
5090
5231
  env: safeJsonParse(row.env, {}),
5091
5232
  runtimeKind: row.runtime_kind || null,
5233
+ ...(cwd ? { cwd } : {}),
5234
+ ...(sandboxPolicy ? { sandboxPolicy } : {}),
5092
5235
  autoConnect: row.auto_connect === 1,
5093
5236
  url: row.url || null,
5094
5237
  transport:
@@ -5116,39 +5259,96 @@ export class MCPServerConfig {
5116
5259
  return this.list(options).find((config) => config.name === name) || null;
5117
5260
  }
5118
5261
 
5262
+ _rowsToConfigs(rows, options = {}) {
5263
+ const configs = [];
5264
+ for (const row of rows) {
5265
+ const visibility = mcpRowVisibility(row);
5266
+ if (!visibility || !this._isVisible(visibility, options)) continue;
5267
+ let config;
5268
+ try {
5269
+ config = this._rowToConfig(row);
5270
+ } catch (error) {
5271
+ const invalid = mcpRowVisibility(row, { invalid: true, error });
5272
+ if (invalid) configs.push(invalid);
5273
+ continue;
5274
+ }
5275
+ configs.push(config);
5276
+ }
5277
+ return configs;
5278
+ }
5279
+
5280
+ _publicConfigs(configs) {
5281
+ return configs.filter((config) => !isInvalidMcpConfigRow(config));
5282
+ }
5283
+
5284
+ _reportInvalidConfigs(configs, options = {}) {
5285
+ if (typeof options.onInvalidRow !== "function") return;
5286
+ for (const config of configs) {
5287
+ if (!isInvalidMcpConfigRow(config)) continue;
5288
+ try {
5289
+ options.onInvalidRow(
5290
+ config[INVALID_MCP_CONFIG_ERROR],
5291
+ config[INVALID_MCP_CONFIG_RAW_ROW],
5292
+ );
5293
+ } catch {
5294
+ // Diagnostics must not turn one corrupt row into a global outage.
5295
+ }
5296
+ }
5297
+ }
5298
+
5119
5299
  list(options = {}) {
5120
5300
  const rows = this.db
5121
5301
  .prepare("SELECT * FROM mcp_servers ORDER BY name")
5122
5302
  .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);
5303
+ const visible = this._rowsToConfigs(rows, options);
5304
+ const selected =
5305
+ options.allScopes === true ? visible : this._effectiveByName(visible);
5306
+ this._reportInvalidConfigs(selected, options);
5307
+ return this._publicConfigs(selected);
5129
5308
  }
5130
5309
 
5131
5310
  getAutoConnect(options = {}) {
5132
5311
  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);
5312
+ .prepare("SELECT * FROM mcp_servers ORDER BY name")
5313
+ .all();
5314
+ const visible = this._rowsToConfigs(rows, options);
5315
+ const selected =
5316
+ options.allScopes === true ? visible : this._effectiveByName(visible);
5317
+ this._reportInvalidConfigs(selected, options);
5318
+ return this._publicConfigs(selected).filter(
5319
+ (config) => config.autoConnect === true,
5320
+ );
5141
5321
  }
5142
5322
 
5143
5323
  _effectiveByName(configs) {
5144
5324
  const priority = { managed: 4, local: 3, project: 2, user: 1 };
5325
+ const specificity = (config) => {
5326
+ if (config.configScope !== "local" && config.configScope !== "project") {
5327
+ return 0;
5328
+ }
5329
+ if (typeof config.projectPath !== "string") return 0;
5330
+ try {
5331
+ return pathToFileURL(config.projectPath).href.length;
5332
+ } catch {
5333
+ return 0;
5334
+ }
5335
+ };
5145
5336
  const selected = new Map();
5146
5337
  for (const config of configs) {
5147
5338
  const current = selected.get(config.name);
5339
+ const candidatePriority = priority[config.configScope] || 0;
5340
+ const currentPriority = current ? priority[current.configScope] || 0 : -1;
5341
+ const candidateSpecificity = specificity(config);
5342
+ const currentSpecificity = current ? specificity(current) : -1;
5148
5343
  if (
5149
5344
  !current ||
5150
- (priority[config.configScope] || 0) >
5151
- (priority[current.configScope] || 0)
5345
+ candidatePriority > currentPriority ||
5346
+ (candidatePriority === currentPriority &&
5347
+ candidateSpecificity > currentSpecificity) ||
5348
+ (candidatePriority === currentPriority &&
5349
+ candidateSpecificity === currentSpecificity &&
5350
+ isInvalidMcpConfigRow(config) &&
5351
+ !isInvalidMcpConfigRow(current))
5152
5352
  ) {
5153
5353
  selected.set(config.name, config);
5154
5354
  }
@@ -9,7 +9,9 @@
9
9
 
10
10
  import {
11
11
  existsSync,
12
+ appendFileSync,
12
13
  lstatSync,
14
+ readFileSync,
13
15
  realpathSync,
14
16
  rmdirSync,
15
17
  symlinkSync,
@@ -97,17 +99,36 @@ function gitRepositoryIdentity(repoDir) {
97
99
  const absoluteCommonDir = isAbsolute(commonDir)
98
100
  ? commonDir
99
101
  : resolve(repoDir, commonDir);
102
+ const commonDirPath = canonicalExistingPrefixPath(absoluteCommonDir);
100
103
  return {
101
104
  topLevel,
102
105
  topLevelIdentity: normalizeFilesystemPath(
103
106
  canonicalExistingPrefixPath(topLevel),
104
107
  ),
105
- commonDirIdentity: normalizeFilesystemPath(
106
- canonicalExistingPrefixPath(absoluteCommonDir),
107
- ),
108
+ commonDirPath,
109
+ commonDirIdentity: normalizeFilesystemPath(commonDirPath),
108
110
  };
109
111
  }
110
112
 
113
+ function ensureManagedWorktreeExcluded(context) {
114
+ const infoDir = resolve(context.repository.commonDirPath, "info");
115
+ const excludeFile = resolve(infoDir, "exclude");
116
+ mkdirSync(infoDir, { recursive: true });
117
+ try {
118
+ assertNotSymlink(infoDir, "git info directory");
119
+ assertNotSymlink(excludeFile, "git exclude file");
120
+ } catch (error) {
121
+ if (error?.code !== "ENOENT") throw error;
122
+ }
123
+ const pattern = `/${WORKTREE_DIR}/`;
124
+ const current = existsSync(excludeFile)
125
+ ? readFileSync(excludeFile, "utf8")
126
+ : "";
127
+ if (current.split(/\r?\n/).includes(pattern)) return;
128
+ const separator = current.length > 0 && !/\r?\n$/.test(current) ? "\n" : "";
129
+ appendFileSync(excludeFile, `${separator}${pattern}\n`, "utf8");
130
+ }
131
+
111
132
  function assertSameGitRepository(repoDir, expectedCommonDir, expectedRoot) {
112
133
  const identity = gitRepositoryIdentity(repoDir);
113
134
  if (
@@ -381,6 +402,8 @@ export function createWorktree(repoDir, branchName, baseBranch, options = {}) {
381
402
  throw new Error("Not a git repository");
382
403
  }
383
404
 
405
+ const context = repositoryManagementContext(repoDir);
406
+ ensureManagedWorktreeExcluded(context);
384
407
  const worktreePath = assertManagedWorktreePath(
385
408
  repoDir,
386
409
  managedWorktreePath(repoDir, branchName),
@@ -164,18 +164,57 @@ function removeVerifiedAgentWorktree(info, deps, expectedBranchOid) {
164
164
  export function setupAgentWorktree({
165
165
  cwd = process.cwd(),
166
166
  now = new Date(),
167
+ requireCleanSource = false,
167
168
  deps = _deps,
168
169
  } = {}) {
169
170
  const repoRoot = findProjectRoot(cwd);
170
171
  if (!repoRoot) {
171
172
  throw new Error("--worktree requires running inside a git repository");
172
173
  }
174
+ if (requireCleanSource) {
175
+ const status = git(
176
+ [
177
+ "status",
178
+ "--porcelain=v1",
179
+ "--untracked-files=all",
180
+ "--ignore-submodules=none",
181
+ "--",
182
+ ".",
183
+ ":(exclude).worktrees",
184
+ ],
185
+ repoRoot,
186
+ deps,
187
+ ).trim();
188
+ if (status) {
189
+ throw new Error(
190
+ "Background worktree isolation requires a clean source checkout; commit/stash the current changes or pass --no-worktree explicitly.",
191
+ );
192
+ }
193
+ }
173
194
  const stamp = now.toISOString().slice(0, 19).replace(/[-:T]/g, "");
174
195
  const suffix = Math.random().toString(36).slice(2, 6);
175
196
  const branch = `cc-agent-${stamp}-${suffix}`;
176
197
  const info = (deps.createWorktree || createWorktree)(repoRoot, branch);
177
- const baseSha = git(["rev-parse", "HEAD"], info.path, deps).trim();
178
- return { ...info, repoRoot, baseSha };
198
+ try {
199
+ const baseSha = git(["rev-parse", "HEAD"], info.path, deps).trim();
200
+ return { ...info, repoRoot, baseSha };
201
+ } catch (error) {
202
+ // createWorktree is transactional internally, but ownership has already
203
+ // transferred to this wrapper when the follow-up base-SHA query runs. Roll
204
+ // that partial setup back here so callers never receive `_worktree = null`
205
+ // while a branch and linked checkout have already been created.
206
+ try {
207
+ (deps.removeWorktree || removeWorktree)(repoRoot, info.path, {
208
+ deleteBranch: true,
209
+ branchName: branch,
210
+ force: false,
211
+ });
212
+ } catch (cleanupError) {
213
+ error.retainedWorktree = { ...info, repoRoot, branch };
214
+ error.worktreeCleanupError = cleanupError;
215
+ }
216
+ throw error;
217
+ }
179
218
  }
180
219
 
181
220
  /**