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.
- package/package.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-DnGsmsqJ.js → AIOps-DIY6KxV-.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CrO6fcQ7.js → ActionButton-BXH0muPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DsBdOaP2.js → Analytics-D39w8vmg.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DmQslKUB.js → AppLayout-CUU1zvLs.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BvyrT_QQ.js → Artifacts-D8azHO3T.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-prndoLiC.js → Audit-D0tAwXTg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-DOTFyA55.js → BackgroundAgents-C29mhJll.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-9zOnPp_8.js → Backup-D6Aa1Gro.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C4PC6sAG.js → BaseInput-B1uoHbPQ.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BBAF-W9v.js → Chat-Bs4DLOR5.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CWhvvloO.js → ChatBubbleRenderer-DUYIBSle.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BI9iculd.js → Checkbox-CNcBPEa4.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-3DP9BCKb.js → Codegen-CIY89763.js} +1 -1
- package/src/assets/web-panel/assets/{Col-df-2TnJX.js → Col-CsPYFmgo.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CHs8gG_A.js → Community-BBocEge4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CDfF6q7V.js → Compact-TQ8Ux0Lf.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-CthrDnO7.js → Compliance-DfBsZTft.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-HjIqsWJw.js → Cowork-Cg-I6FVH.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-BKc196fb.js → Cron-DTQzfRTp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CJnjdc8W.js → Crosschain-DGxEKGpR.js} +1 -1
- package/src/assets/web-panel/assets/{DID-BHUy-8Q2.js → DID-DLZ1RDSr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-QDB7sKeY.js → Dashboard-XneICKNI.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DStcVK11.js → Dropdown-BRU00J1-.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-BwSUskMJ.js → EmailListRenderer-BzCbJuAy.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgF_kurT.js → FamilyGuardDashboard-D7BRyNBG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-k8wvv-wN.js → Federation-D9DeFIhW.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C11qoq2r.js → FormItemContext-6rAtA9Ko.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BIIpr9kg.js → GenericCardRenderer-8Hqy80pU.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BlA1exTe.js → Git-Cmvkj1rt.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CYa1ZYM4.js → Governance-BRiQUjjw.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-6_kKVIt6.js → Inference-CKN_EzVf.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CB4AwxYy.js → KnowledgeGraph-BCDHwpwQ.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-xf7nHFAe.js → Logs-DK9ovhaB.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-D53pHiHy.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-aPg8e4Jt.js → Marketplace-BILPGakp.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C-TNlVUf.js → McpTools-DTVCOr9-.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-DMTPIpGa.js → Memory-Hzc1iow4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BxyvveRq.js → MobileBridge-GXfgnGYV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DyPCrAyQ.js → MobileProjects-B-3TJWsS.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BdhqNXp6.js → Mtc-DwSJMWtK.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-Cz-HFxzW.js → MtcAudit-D2uxf-DA.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CIUaFNsx.js → Multisig-DG6kbKK0.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CWe6-J17.js → NLProgramming-f5p49EDU.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-vYAKeviU.js → Notes-FUgHyzSD.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-DQL9-2d7.js → NotificationSettings-DCHGee7e.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DnXj1jBn.js → OrderTableRenderer-Dm6LlIoa.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-B3spU8AN.js → Organization-BawA7qAg.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DgEwo7vm.js → Overflow-D6a5wZU1.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-hujMRH8X.js → P2P-K8WavwnA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-C_qhdg9h.js → PdhVaultBrowser-4JAJYaqx.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-vp7LMcSf.js → Permissions-BDWvi8PD.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-Dxr5Hgnq.js → PersonalDataHub-DF_K4E9i.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-DEPZqqH4.js → Pipeline-BYeZlesI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DTpDiKPe.js → Privacy-BIHlgnIc.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-jDJsRWmF.js → ProjectInit-Cvf-6B5P.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Dhor5RWH.js → ProjectSettings-CnwfJrRz.js} +2 -2
- package/src/assets/web-panel/assets/Projects-b-jPXzF4.js +1 -0
- package/src/assets/web-panel/assets/{Providers-BGs47s1a.js → Providers-CxC7JuYv.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CjIw-wIb.js → QrScannerModal-VLf3gAfL.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DX6jcqoi.js → QuickAsk-B5vGjRiF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DopXkhar.js → Recommend--UW4iYs-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BLvqJE9c.js → RemoteSession-DMSPcfKR.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DhRj2Me7.js → Reputation-kQXlEHmj.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DQ53vuPq.js → Row-CxGu3pit.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-BslvwiBD.js → RssFeed-Bi5lo8MZ.js} +2 -2
- package/src/assets/web-panel/assets/{Search-DJKBDNKZ.js → Search-Cq_H5RTh.js} +1 -1
- package/src/assets/web-panel/assets/{Security-COhpxHER.js → Security-YFbmkaQS.js} +3 -3
- package/src/assets/web-panel/assets/{Services-dbulD6cI.js → Services-DLOGdaLm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ciIVNfhC.js → Skeleton-CteIxb3D.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-AiRTkTCk.js → Skills-CccopaIZ.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-DDMcWYBc.js → Sla-AzfEQKX1.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BPxWbpXO.js → SpeechSettings-ekGo5l77.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-BoEOXCyg.js → SyncSettings-BmZmAgYH.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-D7q3Z4uL.js +1 -0
- package/src/assets/web-panel/assets/{Templates-UowY8Ixe.js → Templates-CX1Tgeka.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DauSLQXN.js → Tenant-Zc59jF4i.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DBj7Jneq.js → Terminal-CXOD7y83.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-UMrPwoZC.js → TimelineRenderer-B1aBHPBo.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-4ea96-bn.js → Tokens-zALlOq_U.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger--ISCqt4b.js → Trigger-CQfJMXaq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-D4GVimRJ.js → Trust-BU0RcM-w.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Ci-nXRQc.js → UkeySign-IFZ3HsbH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-Cjk7mKPw.js → VideoEditing-BzslgW0v.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BgOLOO3Z.js → Wallet-BZAcQNS6.js} +2 -2
- package/src/assets/web-panel/assets/{WebAuthn-CCX0K29i.js → WebAuthn-CBK1DMi0.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Ch3zgIsE.js → WorkflowEditor-BlMTPzO-.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DKOU_2Vb.js → chat-B51-8QVn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-6OVo4u3C.js → colors-504RTByU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BH78Hlsq.js → compact-item-C-GP6IQM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CFRpSbbC.js → createContext-DOkIj7ID.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DNQhgcVi.js → hasIn-DN7V-_FW.js} +1 -1
- package/src/assets/web-panel/assets/{index-dDkoL4Pe.js → index--n-ZciV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-CaYqY35Z.js → index-7jIqHbzw.js} +1 -1
- package/src/assets/web-panel/assets/{index-BYrnm5Ob.js → index-B2-x9bS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-sm7W8AXy.js → index-BFIFJ5Dr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DKN2noIF.js → index-B_vGU-Js.js} +1 -1
- package/src/assets/web-panel/assets/{index--rq90rL9.js → index-Bkxhaeta.js} +1 -1
- package/src/assets/web-panel/assets/{index-CFPJ8yzF.js → index-By6u5IV1.js} +1 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +1 -0
- package/src/assets/web-panel/assets/{index-caye7AOE.js → index-C0k_Nqg9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxRy5LPR.js → index-C3TSohzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BepBTpa8.js → index-C4DV_5uA.js} +1 -1
- package/src/assets/web-panel/assets/{index-D84id2Y4.js → index-C8UltbXK.js} +1 -1
- package/src/assets/web-panel/assets/{index-Y9MJv28e.js → index-C9_48OzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bb6kDrUx.js → index-CKTfZyfU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CrcLHN2F.js → index-CP2A226L.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkNEMANR.js → index-CUhQpiYv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cu3L4r5l.js → index-CX78k-qf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DaDgdJYj.js → index-CYopNjcJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du0rnksC.js → index-Cao5DZy7.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaVE6E8y.js → index-CfiEM64n.js} +3 -3
- package/src/assets/web-panel/assets/{index-BujsOZfc.js → index-ConjaJro.js} +1 -1
- package/src/assets/web-panel/assets/{index-hPY0fFrd.js → index-CqcLo5WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDSR7ecX.js → index-CySTJKeq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHa4WyTZ.js → index-D1phyca8.js} +1 -1
- package/src/assets/web-panel/assets/{index-nbfHl1oV.js → index-D9LyiN-L.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkqHgG-b.js → index-DA2f1UV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Eag8e_mA.js → index-DAOpQvl_.js} +1 -1
- package/src/assets/web-panel/assets/{index-cdf7fd7d.js → index-DE-hmI2l.js} +1 -1
- package/src/assets/web-panel/assets/{index-bKm3TUDp.js → index-DEMPUVa3.js} +1 -1
- package/src/assets/web-panel/assets/index-D_FWpDZM.js +1 -0
- package/src/assets/web-panel/assets/{index-7TC3S8SN.js → index-DaA__-q1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DvHGtNnF.js → index-FcV8J_Qz.js} +1 -1
- package/src/assets/web-panel/assets/{index-B_Z-YoxX.js → index-P2mfY27Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-AuBhLqOm.js → index-et5JIPHO.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3RtTT4K.js → index-f4Bo4KXm.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNuCHHQT.js → index-kAqQXfP6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkN3iTTp.js → index-kt_5rfcZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnnXQot_.js → index-so8eIIxF.js} +1 -1
- package/src/assets/web-panel/assets/{index-HNGGVF6o.js → index-yYTQZypF.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BZ9w6jPL.js → initDefaultProps-CPYEIdpV.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BnwUiOho.js → motion-Cjim9pxs.js} +1 -1
- package/src/assets/web-panel/assets/{move-ByUKJnNm.js → move-DuU-cgz3.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-V7yK1KEl.js → mtc-parser-DJHNAkta.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BEXBBt4j.js → omit-DSQBgdBj.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qDm_46Gi.js → pickAttrs-CTmT_j5m.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BIbhEo_0.js → placementArrow-Lfm6v-r4.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-NbPbDgA_.js → responsiveObserve-CgrcPcDi.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BnUWglZ0.js → slide-CuE3110i.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BX8_wZI5.js → statusUtils-DiK5nbpf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BV4OycQX.js → styleChecker-DPEJdQaZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Od_H5llY.js → useFlexGapSupport-CnnodGmp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BWI-PyiX.js → useFs-DWF84Yav.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BqMuhG3g.js → usePersonalDataHub-a5b5Dw2f.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DNd23z6f.js → vnode-DAluzXOq.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-Cjaxnm7l.js → zoom-Dokg7uAZ.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/agenda.js +6 -5
- package/src/commands/agent.js +237 -75
- package/src/commands/agents.js +6 -0
- package/src/commands/background-session.js +11 -1
- package/src/commands/mcp.js +217 -30
- package/src/data/changelog.json +14 -1
- package/src/harness/mcp-client.js +242 -42
- package/src/harness/worktree-isolator.js +26 -3
- package/src/lib/agent-worktree.js +41 -2
- package/src/lib/background-agent-supervisor.js +790 -186
- package/src/lib/background-command-argv.js +318 -0
- package/src/lib/background-launch-profile.js +1 -0
- package/src/lib/background-phase-reporter.js +22 -2
- package/src/lib/background-worktree-policy.js +109 -0
- package/src/lib/cowork-mcp-tools.js +51 -6
- package/src/lib/cowork-task-runner.js +1 -0
- package/src/lib/mcp-headers-helper.js +76 -8
- package/src/lib/mcp-sandbox-policy.js +155 -0
- package/src/lib/mcp-stdio-execution-authority.js +163 -0
- package/src/lib/mcp-stdio-workspace-authority.js +252 -0
- package/src/lib/plugin-runtime/sandbox-policy.js +42 -2
- package/src/lib/process-execution-broker/index.js +179 -104
- package/src/lib/process-execution-broker/platform-sandbox.js +942 -168
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/skill-mcp.js +65 -4
- package/src/lib/web-fetch.js +4 -4
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/mcp-config.js +235 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/MarkdownRenderer-Bf3DEqP6.js +0 -1
- package/src/assets/web-panel/assets/Projects-08xUDZGt.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CEYFJwmr.js +0 -1
- package/src/assets/web-panel/assets/devWarning-DEMblAIm.js +0 -1
- package/src/assets/web-panel/assets/index-BTvq-36D.js +0 -1
- 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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
|
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 });
|