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
@@ -0,0 +1,318 @@
1
+ /**
2
+ * Capture the Commander option grammar needed to safely transform a parsed
3
+ * command's original argv. Keeping option arity is important: an option value
4
+ * can be byte-for-byte identical to a prompt token or another option name.
5
+ */
6
+ /** Keep untrusted prompt text inside one argv token, even when it starts `-`. */
7
+ export function agentPrintArgument(prompt) {
8
+ return `--print=${String(prompt)}`;
9
+ }
10
+
11
+ export function captureCommandArgvGrammar(command) {
12
+ const hierarchy = [];
13
+ for (let cursor = command; cursor; cursor = cursor.parent) {
14
+ hierarchy.unshift(cursor);
15
+ }
16
+ const optionSpecs = hierarchy.flatMap((owner) =>
17
+ (owner?.options || []).map((option) => ({
18
+ short: option.short || null,
19
+ long: option.long || null,
20
+ required: option.required === true,
21
+ optional: option.optional === true,
22
+ variadic: option.variadic === true,
23
+ })),
24
+ );
25
+ const commandNames = [
26
+ command?.name?.(),
27
+ ...(command?.aliases?.() || []),
28
+ ].filter(Boolean);
29
+ return { optionSpecs, commandNames };
30
+ }
31
+
32
+ export function captureCommandOptionSpecs(command) {
33
+ return captureCommandArgvGrammar(command).optionSpecs;
34
+ }
35
+
36
+ function normalizedOptionSpecs(optionSpecs) {
37
+ return (optionSpecs || [])
38
+ .map((spec) => ({
39
+ short: spec?.short || null,
40
+ long: spec?.long || null,
41
+ required: spec?.required === true,
42
+ optional: spec?.optional === true,
43
+ variadic: spec?.variadic === true,
44
+ }))
45
+ .filter((spec) => spec.short || spec.long);
46
+ }
47
+
48
+ function consumesValue(spec) {
49
+ return spec?.required === true || spec?.optional === true;
50
+ }
51
+
52
+ function looksLikeOption(value) {
53
+ return typeof value === "string" && value.length > 1 && value[0] === "-";
54
+ }
55
+
56
+ function consumedValueCount(argv, start, spec) {
57
+ if (!consumesValue(spec) || start >= argv.length || argv[start] === "--") {
58
+ return 0;
59
+ }
60
+ if (spec.optional && looksLikeOption(argv[start])) return 0;
61
+ if (!spec.variadic) return 1;
62
+ let count = 0;
63
+ for (let index = start; index < argv.length; index++) {
64
+ if (argv[index] === "--" || looksLikeOption(argv[index])) break;
65
+ count += 1;
66
+ }
67
+ return count;
68
+ }
69
+
70
+ function optionGrammar(optionSpecs) {
71
+ const specs = normalizedOptionSpecs(optionSpecs);
72
+ const aliases = new Map();
73
+ for (const spec of specs) {
74
+ if (spec.short) aliases.set(spec.short, spec);
75
+ if (spec.long) aliases.set(spec.long, spec);
76
+ }
77
+ return { specs, aliases };
78
+ }
79
+
80
+ function parseShortCluster(token, aliases, argv, index) {
81
+ if (!/^-[^-].+/.test(token)) return null;
82
+ const body = token.slice(1);
83
+ const parts = [];
84
+ for (let offset = 0; offset < body.length; offset++) {
85
+ const alias = `-${body[offset]}`;
86
+ const spec = aliases.get(alias);
87
+ if (!spec) return null;
88
+ if (!consumesValue(spec)) {
89
+ parts.push({ alias, spec, attachedValue: null });
90
+ continue;
91
+ }
92
+ const attachedValue = body.slice(offset + 1);
93
+ parts.push({
94
+ alias,
95
+ spec,
96
+ attachedValue: attachedValue || null,
97
+ });
98
+ const valueCount = attachedValue
99
+ ? 0
100
+ : consumedValueCount(argv, index + 1, spec);
101
+ return {
102
+ kind: "short-cluster",
103
+ parts,
104
+ values: argv.slice(index + 1, index + 1 + valueCount),
105
+ consumed: 1 + valueCount,
106
+ };
107
+ }
108
+ return {
109
+ kind: "short-cluster",
110
+ parts,
111
+ values: [],
112
+ consumed: 1,
113
+ };
114
+ }
115
+
116
+ function parseOption(argv, index, aliases) {
117
+ const token = argv[index];
118
+ const equalsIndex = token.startsWith("--") ? token.indexOf("=") : -1;
119
+ const name = equalsIndex > 0 ? token.slice(0, equalsIndex) : token;
120
+ const spec = aliases.get(name);
121
+ if (spec) {
122
+ const inlineValue = equalsIndex > 0 ? token.slice(equalsIndex + 1) : null;
123
+ const valueCount =
124
+ inlineValue !== null ? 0 : consumedValueCount(argv, index + 1, spec);
125
+ return {
126
+ kind: "option",
127
+ spec,
128
+ token,
129
+ inlineValue,
130
+ values: argv.slice(index + 1, index + 1 + valueCount),
131
+ consumed: 1 + valueCount,
132
+ };
133
+ }
134
+ return parseShortCluster(token, aliases, argv, index);
135
+ }
136
+
137
+ function parseCommandArgv(argv, optionSpecs, commandNames) {
138
+ const source = [...(argv || [])];
139
+ const { aliases } = optionGrammar(optionSpecs);
140
+ const segments = [];
141
+ let index = 0;
142
+ const names = new Set(commandNames || []);
143
+ let foundCommand = names.size === 0;
144
+ if (foundCommand && source.length > 0) {
145
+ // The background launcher always receives process.argv.slice(2), whose
146
+ // first token is the command name (`agent`/`a`). It is not an operand.
147
+ segments.push({ kind: "command", tokens: [source[0]] });
148
+ index = 1;
149
+ }
150
+ while (index < source.length) {
151
+ if (foundCommand && source[index] === "--") {
152
+ segments.push({ kind: "terminator", tokens: source.slice(index) });
153
+ break;
154
+ }
155
+ const option = parseOption(source, index, aliases);
156
+ if (option) {
157
+ segments.push(option);
158
+ index += option.consumed;
159
+ continue;
160
+ }
161
+ if (!foundCommand && names.has(source[index])) {
162
+ foundCommand = true;
163
+ segments.push({ kind: "command", tokens: [source[index]] });
164
+ } else {
165
+ // Unknown pre-command tokens are preserved fail-closed. A successfully
166
+ // parsed Commander invocation should only reach this for the command
167
+ // token itself, but retaining an unexpected prefix is safer than
168
+ // silently launching a different command.
169
+ segments.push({
170
+ kind: foundCommand ? "operand" : "prefix",
171
+ tokens: [source[index]],
172
+ });
173
+ }
174
+ index += 1;
175
+ }
176
+ return segments;
177
+ }
178
+
179
+ function renderShortCluster(segment, keepPart) {
180
+ const kept = segment.parts.filter(keepPart);
181
+ if (kept.length === 0) return [];
182
+ let token = "-";
183
+ let keepValues = false;
184
+ for (const part of kept) {
185
+ token += part.alias.slice(1);
186
+ if (consumesValue(part.spec)) {
187
+ if (part.attachedValue !== null) token += part.attachedValue;
188
+ else keepValues = true;
189
+ break;
190
+ }
191
+ }
192
+ return [token, ...(keepValues ? segment.values : [])];
193
+ }
194
+
195
+ const BACKGROUND_PARENT_OPTIONS = new Set([
196
+ "--background",
197
+ "--worktree",
198
+ "--no-worktree",
199
+ ]);
200
+
201
+ /**
202
+ * Remove parent-only background/worktree decisions and replace every parsed
203
+ * --add-dir occurrence with the already validated canonical roots. A token is
204
+ * changed only when the command grammar identifies it as that option; equal
205
+ * values belonging to another option stay byte-for-byte intact.
206
+ */
207
+ export function transformBackgroundLaunchArgv(
208
+ argv,
209
+ { directories = [], optionSpecs, commandNames } = {},
210
+ ) {
211
+ const out = [];
212
+ let positionalTail = [];
213
+ for (const segment of parseCommandArgv(argv, optionSpecs, commandNames)) {
214
+ if (segment.kind === "option") {
215
+ if (
216
+ segment.spec?.long !== "--add-dir" &&
217
+ !BACKGROUND_PARENT_OPTIONS.has(segment.spec?.long)
218
+ ) {
219
+ out.push(segment.token, ...segment.values);
220
+ }
221
+ } else if (segment.kind === "short-cluster") {
222
+ out.push(
223
+ ...renderShortCluster(
224
+ segment,
225
+ (part) =>
226
+ part.spec?.long !== "--add-dir" &&
227
+ !BACKGROUND_PARENT_OPTIONS.has(part.spec?.long),
228
+ ),
229
+ );
230
+ } else if (segment.kind === "terminator") {
231
+ positionalTail = segment.tokens;
232
+ } else {
233
+ out.push(...segment.tokens);
234
+ }
235
+ }
236
+ for (const directory of directories || []) {
237
+ out.push("--add-dir", directory);
238
+ }
239
+ return [...out, ...positionalTail];
240
+ }
241
+
242
+ const SESSION_PARENT_OPTIONS = new Set([
243
+ "--session",
244
+ "--continue",
245
+ "--resume",
246
+ "--fork-session",
247
+ "--fork-request-id",
248
+ ]);
249
+
250
+ function isParentSessionOption(spec) {
251
+ return SESSION_PARENT_OPTIONS.has(spec?.long);
252
+ }
253
+
254
+ /**
255
+ * Remove every parent-owned resume/fork spelling and insert one resolved
256
+ * session id. Literal prompt tokens after `--`, and values belonging to other
257
+ * options, are preserved exactly.
258
+ */
259
+ export function canonicalizeBackgroundSessionArgv(
260
+ argv,
261
+ { sessionId, optionSpecs, commandNames } = {},
262
+ ) {
263
+ if (!sessionId) {
264
+ throw new Error("Cannot launch background agent without a session id");
265
+ }
266
+ const out = [];
267
+ let positionalTail = [];
268
+ for (const segment of parseCommandArgv(argv, optionSpecs, commandNames)) {
269
+ if (segment.kind === "option") {
270
+ if (!isParentSessionOption(segment.spec)) {
271
+ out.push(segment.token, ...segment.values);
272
+ }
273
+ } else if (segment.kind === "short-cluster") {
274
+ out.push(
275
+ ...renderShortCluster(
276
+ segment,
277
+ (part) => !isParentSessionOption(part.spec),
278
+ ),
279
+ );
280
+ } else if (segment.kind === "terminator") {
281
+ positionalTail = segment.tokens;
282
+ } else {
283
+ out.push(...segment.tokens);
284
+ }
285
+ }
286
+ return [...out, "--session", String(sessionId), ...positionalTail];
287
+ }
288
+
289
+ function isPrintOption(spec) {
290
+ return spec?.long === "--print";
291
+ }
292
+
293
+ /**
294
+ * Build the reusable argv for an attached follow-up turn. Option grammar, not
295
+ * token equality, distinguishes option values from first-turn operands.
296
+ */
297
+ export function stripFirstTurnPromptArgv(
298
+ argv,
299
+ { optionSpecs, commandNames } = {},
300
+ ) {
301
+ const out = [];
302
+ for (const segment of parseCommandArgv(argv, optionSpecs, commandNames)) {
303
+ if (segment.kind === "command" || segment.kind === "prefix") {
304
+ out.push(...segment.tokens);
305
+ } else if (segment.kind === "option") {
306
+ if (!isPrintOption(segment.spec)) {
307
+ out.push(segment.token, ...segment.values);
308
+ }
309
+ } else if (segment.kind === "short-cluster") {
310
+ out.push(
311
+ ...renderShortCluster(segment, (part) => !isPrintOption(part.spec)),
312
+ );
313
+ }
314
+ // All operands, the option terminator, and its literal tail belong to the
315
+ // first turn and are intentionally omitted.
316
+ }
317
+ return out;
318
+ }
@@ -593,6 +593,7 @@ export function captureBackgroundLaunchProfile({
593
593
  case "--bg":
594
594
  case "--background":
595
595
  case "--worktree":
596
+ case "--no-worktree":
596
597
  case "--ephemeral":
597
598
  case "--capabilities":
598
599
  case "--disable-slash-commands":
@@ -31,6 +31,7 @@
31
31
  */
32
32
 
33
33
  import {
34
+ mutateBackgroundAgentState,
34
35
  readBackgroundAgentState,
35
36
  writeBackgroundAgentState,
36
37
  } from "./background-agent-supervisor.js";
@@ -43,6 +44,8 @@ import {
43
44
  * `process.env.CC_BACKGROUND_AGENT_ID`. Falsy → disabled no-op reporter.
44
45
  * @param {(id:string)=>object|null} [opts.readState] injectable state reader
45
46
  * @param {(state:object)=>void} [opts.writeState] injectable state writer
47
+ * @param {(id:string, updater:Function)=>object} [opts.mutateState] injectable
48
+ * atomic mutator; production defaults to the strict supervisor transaction
46
49
  */
47
50
  export function createBackgroundPhaseReporter(opts = {}) {
48
51
  const agentId =
@@ -51,16 +54,33 @@ export function createBackgroundPhaseReporter(opts = {}) {
51
54
  : process.env.CC_BACKGROUND_AGENT_ID;
52
55
  const readState = opts.readState || readBackgroundAgentState;
53
56
  const writeState = opts.writeState || writeBackgroundAgentState;
57
+ const mutateState =
58
+ opts.mutateState ||
59
+ (opts.readState || opts.writeState ? null : mutateBackgroundAgentState);
54
60
  const enabled = typeof agentId === "string" && agentId.trim() !== "";
55
61
  let pending = 0;
56
62
 
57
63
  function patch(fields) {
58
64
  if (!enabled) return false;
59
65
  try {
60
- const current = readState(agentId) || { id: agentId };
66
+ if (mutateState) {
67
+ return mutateState(agentId, (current) => {
68
+ if (
69
+ !current ||
70
+ current.status !== "running" ||
71
+ current.stopRequestedAt
72
+ ) {
73
+ return null;
74
+ }
75
+ return { ...current, id: agentId, ...fields };
76
+ }).applied;
77
+ }
78
+ const current = readState(agentId);
61
79
  // Terminal states are owned by the worker/stopper — never write a
62
80
  // "waiting" phase over a completed/failed/stopped record.
63
- if (current.status && current.status !== "running") return false;
81
+ if (!current || current.status !== "running" || current.stopRequestedAt) {
82
+ return false;
83
+ }
64
84
  writeState({ ...current, id: agentId, ...fields });
65
85
  return true;
66
86
  } catch {
@@ -0,0 +1,109 @@
1
+ import { lstatSync, realpathSync } from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Locate a Git marker without `existsSync()`'s fail-open error swallowing.
6
+ * ENOENT/ENOTDIR means "keep walking"; access, I/O, and indeterminate marker
7
+ * failures propagate so a mutation-capable background task never silently
8
+ * falls back to the shared checkout.
9
+ */
10
+ export function findGitRepositoryRootStrict(
11
+ cwd = process.cwd(),
12
+ {
13
+ stat = lstatSync,
14
+ realpath = realpathSync.native || realpathSync,
15
+ pathApi = path,
16
+ } = {},
17
+ ) {
18
+ // Resolve the starting directory before walking ancestors. A lexical cwd
19
+ // reached through a symlink/junction may sit outside the repository even
20
+ // though its canonical target is inside it; walking the alias would
21
+ // otherwise classify a real Git checkout as non-Git and share it.
22
+ let dir = realpath(pathApi.resolve(cwd));
23
+ for (;;) {
24
+ try {
25
+ stat(pathApi.join(dir, ".git"));
26
+ return dir;
27
+ } catch (error) {
28
+ if (error?.code !== "ENOENT" && error?.code !== "ENOTDIR") throw error;
29
+ }
30
+ const parent = pathApi.dirname(dir);
31
+ if (parent === dir) return null;
32
+ dir = parent;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Decide whether an agent session must use an isolated git worktree.
38
+ *
39
+ * Background prompts and tool declarations are deliberately not inspected.
40
+ * An unqualified detached agent is treated as mutation-capable and isolated
41
+ * whenever it starts in a git repository. `--no-worktree` is the only explicit
42
+ * escape hatch: a nominally read-only permission mode is not enough because
43
+ * host/MCP extension tools may widen the effective tool surface.
44
+ */
45
+ export function resolveBackgroundWorktreePolicy({
46
+ background = false,
47
+ worktree,
48
+ inputFormat = "text",
49
+ cwd = process.cwd(),
50
+ findProjectRoot = findGitRepositoryRootStrict,
51
+ } = {}) {
52
+ if (worktree === true) {
53
+ const repoRoot = findProjectRoot(cwd);
54
+ if (!repoRoot) {
55
+ throw new Error("--worktree requires a Git repository");
56
+ }
57
+ return {
58
+ enabled: true,
59
+ source: "explicit",
60
+ reason: "worktree explicitly requested",
61
+ repoRoot,
62
+ };
63
+ }
64
+ if (worktree === false) {
65
+ return {
66
+ enabled: false,
67
+ source: "explicit-disable",
68
+ reason: "worktree explicitly disabled",
69
+ repoRoot: null,
70
+ };
71
+ }
72
+ if (!background) {
73
+ return {
74
+ enabled: false,
75
+ source: "foreground-default",
76
+ reason: "foreground sessions preserve the existing default",
77
+ repoRoot: null,
78
+ };
79
+ }
80
+ // stream-json is a foreground, long-lived stdin protocol in the current
81
+ // command contract even if callers also pass --background. Do not silently
82
+ // change that established dispatch path in the worktree policy layer.
83
+ if (String(inputFormat || "text").toLowerCase() === "stream-json") {
84
+ return {
85
+ enabled: false,
86
+ source: "stream-json-foreground",
87
+ reason: "stream-json uses the foreground stream driver",
88
+ repoRoot: null,
89
+ };
90
+ }
91
+ // Detection errors propagate: once a caller asks for mutation-capable
92
+ // background isolation, an indeterminate repository state must not silently
93
+ // fall back to sharing the main checkout.
94
+ const repoRoot = findProjectRoot(cwd);
95
+ if (!repoRoot) {
96
+ return {
97
+ enabled: false,
98
+ source: "non-git-default",
99
+ reason: "current directory is not inside a git repository",
100
+ repoRoot: null,
101
+ };
102
+ }
103
+ return {
104
+ enabled: true,
105
+ source: "background-git-default",
106
+ reason: "mutation-capable background agent in a git repository",
107
+ repoRoot,
108
+ };
109
+ }
@@ -19,12 +19,29 @@
19
19
  *
20
20
  * @module cowork-mcp-tools
21
21
  */
22
+ import { isProxy } from "node:util/types";
22
23
  import { validateMcpServerConfig } from "./skill-mcp.js";
23
24
  import { mcpEffectDescriptorFields } from "./mcp-effect-contract.js";
24
25
  import {
25
26
  MCP_STDIO_LOCAL_CODE_TRUST_REQUIRED_CODE,
26
27
  issueMcpStdioExecutionAuthority,
27
28
  } from "./mcp-stdio-execution-authority.js";
29
+ import {
30
+ currentHostHooksV2WorkspaceBinding,
31
+ registerHostHooksV2Workspace,
32
+ resolveHostHooksV2WorkspaceBinding,
33
+ } from "./hooks-v2-workspace-context.js";
34
+
35
+ function safeServerName(entry) {
36
+ if (!entry || typeof entry !== "object" || isProxy(entry)) return "(invalid)";
37
+ const descriptor = Object.getOwnPropertyDescriptor(entry, "name");
38
+ return descriptor &&
39
+ "value" in descriptor &&
40
+ typeof descriptor.value === "string" &&
41
+ descriptor.value.length > 0
42
+ ? descriptor.value
43
+ : "(invalid)";
44
+ }
28
45
 
29
46
  export const _deps = {
30
47
  importMcpClient: async () => {
@@ -121,12 +138,31 @@ export async function mountTemplateMcpTools(template, opts = {}) {
121
138
  : [];
122
139
  if (declared.length === 0) return empty;
123
140
 
124
- const validated = declared
125
- .map((entry) => validateMcpServerConfig(entry))
126
- .filter(Boolean);
127
- if (validated.length === 0) return empty;
128
-
129
141
  const skipped = [];
142
+ const validated = [];
143
+ for (const entry of declared) {
144
+ try {
145
+ const normalized = validateMcpServerConfig(entry);
146
+ if (normalized) validated.push(normalized);
147
+ } catch (error) {
148
+ const name = safeServerName(entry);
149
+ const message = error?.message || String(error);
150
+ skipped.push({ name, error: message });
151
+ opts.onWarn?.(`[cowork-mcp] Skipped "${name}": ${message}`, error);
152
+ }
153
+ }
154
+ if (validated.length === 0) return { ...empty, skipped };
155
+
156
+ const workspaceBinding = opts.workspaceRoot
157
+ ? registerHostHooksV2Workspace(opts.workspaceRoot)
158
+ : opts.workspaceBinding || currentHostHooksV2WorkspaceBinding();
159
+ const resolvedWorkspace = workspaceBinding
160
+ ? resolveHostHooksV2WorkspaceBinding(workspaceBinding)
161
+ : null;
162
+ if (workspaceBinding && !resolvedWorkspace) {
163
+ throw new TypeError("Cowork MCP workspace authority is invalid or stale");
164
+ }
165
+
130
166
  const authorized = [];
131
167
  for (const server of validated) {
132
168
  let approved = false;
@@ -141,6 +177,7 @@ export async function mountTemplateMcpTools(template, opts = {}) {
141
177
  runtimeKind: server.runtimeKind || null,
142
178
  cwd: server.cwd || null,
143
179
  envKeys: Object.keys(server.env || {}).sort(),
180
+ sandboxPolicy: server.sandboxPolicy || null,
144
181
  })) === true;
145
182
  } catch (error) {
146
183
  const message = error?.message || String(error);
@@ -163,13 +200,21 @@ export async function mountTemplateMcpTools(template, opts = {}) {
163
200
  config,
164
201
  approvalKind: "explicit-config",
165
202
  approvalSource: `cowork-template:${template?.id || "unknown"}:${server.name}`,
203
+ workspaceBinding,
166
204
  });
167
205
  authorized.push(config);
168
206
  }
169
207
  if (authorized.length === 0) return { ...empty, skipped };
170
208
 
171
209
  const MCPClient = await _deps.importMcpClient();
172
- const mcpClient = new MCPClient();
210
+ const mcpClient = new MCPClient(
211
+ resolvedWorkspace
212
+ ? {
213
+ workspaceBinding,
214
+ roots: [resolvedWorkspace.workspaceRoot],
215
+ }
216
+ : {},
217
+ );
173
218
  const mounted = [];
174
219
  const extraToolDefinitions = [];
175
220
  const externalToolDescriptors = {};
@@ -465,6 +465,7 @@ export async function runCoworkTask(options = {}) {
465
465
 
466
466
  // Mount template-declared MCP servers (best-effort, failures are tolerated)
467
467
  const mcp = await mountTemplateMcpTools(template, {
468
+ workspaceRoot: cwd,
468
469
  approveLocalCodeExecution: approveMcpLocalCodeExecution,
469
470
  onWarn: (msg) => {
470
471
  if (onProgress) onProgress({ type: "mcp-warning", message: msg });