chainlesschain 0.163.1 → 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-Cn7quRgL.js → AIOps-DIY6KxV-.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-YqMX3ueH.js → ActionButton-BXH0muPt.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Bxmy1b7Y.js → Analytics-D39w8vmg.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-DBOw69jE.js → AppLayout-CUU1zvLs.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-mq1nyzN1.js → Artifacts-D8azHO3T.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-mMiKtZ0D.js → Audit-D0tAwXTg.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-WBUjpYVh.js → BackgroundAgents-C29mhJll.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-c25OoJgs.js → Backup-D6Aa1Gro.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BbfWhZ4-.js → BaseInput-B1uoHbPQ.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CVVzsTwU.js → Chat-Bs4DLOR5.js} +5 -5
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CVCjZJ04.js → ChatBubbleRenderer-DUYIBSle.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-C9o2HK-1.js → Checkbox-CNcBPEa4.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-B7X5LfKc.js → Codegen-CIY89763.js} +1 -1
- package/src/assets/web-panel/assets/{Col-jpPj2Pur.js → Col-CsPYFmgo.js} +1 -1
- package/src/assets/web-panel/assets/{Community-Dti6bHxN.js → Community-BBocEge4.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-S4ZsLt3W.js → Compact-TQ8Ux0Lf.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bge0sQ3O.js → Compliance-DfBsZTft.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-C4XyzVI7.js → Cowork-Cg-I6FVH.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CPR0v2xy.js → Cron-DTQzfRTp.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DAX8lDaj.js → Crosschain-DGxEKGpR.js} +1 -1
- package/src/assets/web-panel/assets/{DID-fBHPa0QH.js → DID-DLZ1RDSr.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-vvaUTafU.js → Dashboard-XneICKNI.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DyaQM63v.js → Dropdown-BRU00J1-.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CAF0wiU2.js → EmailListRenderer-BzCbJuAy.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-ia-WzDRs.js → FamilyGuardDashboard-D7BRyNBG.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Cz3gHoT5.js → Federation-D9DeFIhW.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-CJQDJr4Q.js → FormItemContext-6rAtA9Ko.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-B05BZlQp.js → GenericCardRenderer-8Hqy80pU.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DT0YcQnI.js → Git-Cmvkj1rt.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-Bqbl6m5Q.js → Governance-BRiQUjjw.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BdebLR0N.js → Inference-CKN_EzVf.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-M36b8YYV.js → KnowledgeGraph-BCDHwpwQ.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-5LN_XL43.js → Logs-DK9ovhaB.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BCb1Dm8l.js → MarkdownRenderer-D53pHiHy.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-BkIin-QY.js → Marketplace-BILPGakp.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bkppx8Uj.js → McpTools-DTVCOr9-.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-Qk9wg_Ib.js → Memory-Hzc1iow4.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BsTDU3Aj.js → MobileBridge-GXfgnGYV.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CrZW5I28.js → MobileProjects-B-3TJWsS.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BMkmgtsA.js → Mtc-DwSJMWtK.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-6m4CC0Yn.js → MtcAudit-D2uxf-DA.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-BZ8yB7QV.js → Multisig-DG6kbKK0.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CxitAMIO.js → NLProgramming-f5p49EDU.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-Bv0msnHu.js → Notes-FUgHyzSD.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-DeVMqM3P.js → NotificationSettings-DCHGee7e.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CXBSWa3R.js → OrderTableRenderer-Dm6LlIoa.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-b_G5Vs1Q.js → Organization-BawA7qAg.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DW7riOmA.js → Overflow-D6a5wZU1.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CtpQNYjL.js → P2P-K8WavwnA.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Ba63yT4l.js → PdhVaultBrowser-4JAJYaqx.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-DnzyeB67.js → Permissions-BDWvi8PD.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DbPZ7HWw.js → PersonalDataHub-DF_K4E9i.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-gs_HOThJ.js → Pipeline-BYeZlesI.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CZlQ1EjE.js → Privacy-BIHlgnIc.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-DQp__Ccb.js → ProjectInit-Cvf-6B5P.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-C7h2sOWZ.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-CxC7JuYv.js +1 -0
- package/src/assets/web-panel/assets/{QrScannerModal-B_8M1-FC.js → QrScannerModal-VLf3gAfL.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-Bpycr8WD.js → QuickAsk-B5vGjRiF.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Z4TrcNzs.js → Recommend--UW4iYs-.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-BD6TsJlS.js → RemoteSession-DMSPcfKR.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-IKxDXHEu.js → Reputation-kQXlEHmj.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DbmR20tJ.js → Row-CxGu3pit.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CLjA5PPf.js → RssFeed-Bi5lo8MZ.js} +2 -2
- package/src/assets/web-panel/assets/{Search-Bm3owbIU.js → Search-Cq_H5RTh.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CY4RyOuq.js → Security-YFbmkaQS.js} +2 -2
- package/src/assets/web-panel/assets/{Services-CYO9qrd_.js → Services-DLOGdaLm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-ClF51Go6.js → Skeleton-CteIxb3D.js} +1 -1
- package/src/assets/web-panel/assets/Skills-CccopaIZ.js +1 -0
- package/src/assets/web-panel/assets/{Sla-zqpDUxzP.js → Sla-AzfEQKX1.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DPSi8Qco.js → SpeechSettings-ekGo5l77.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CqABopsQ.js → SyncSettings-BmZmAgYH.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-D7q3Z4uL.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Dzl6f0M2.js → Templates-CX1Tgeka.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-XeZkw40a.js → Tenant-Zc59jF4i.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-CYw2YzPm.js → Terminal-CXOD7y83.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-Bc2pko4i.js → TimelineRenderer-B1aBHPBo.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-CfCenOHx.js → Tokens-zALlOq_U.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DTz-_YqV.js → Trigger-CQfJMXaq.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CB3tt3Z6.js → Trust-BU0RcM-w.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-y1ykmMvU.js → UkeySign-IFZ3HsbH.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-UlZ_lX0e.js → VideoEditing-BzslgW0v.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DMBaHTha.js → Wallet-BZAcQNS6.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Cksm-hBI.js → WebAuthn-CBK1DMi0.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-BhOa40sX.js → WorkflowEditor-BlMTPzO-.js} +1 -1
- package/src/assets/web-panel/assets/{chat-DNbeSTDO.js → chat-B51-8QVn.js} +1 -1
- package/src/assets/web-panel/assets/{colors-9zz8gBmW.js → colors-504RTByU.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7aoVv-5.js → compact-item-C-GP6IQM.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DYg7B8nd.js → createContext-DOkIj7ID.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-kB0nCNwN.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-BNbhkcoI.js → hasIn-DN7V-_FW.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeD1wWA8.js → index--n-ZciV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-Da0SysnX.js → index-7jIqHbzw.js} +1 -1
- package/src/assets/web-panel/assets/{index--TvoQ6UC.js → index-B2-x9bS8.js} +1 -1
- package/src/assets/web-panel/assets/{index-m2pXS3W2.js → index-BFIFJ5Dr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzDDeZBo.js → index-B_vGU-Js.js} +1 -1
- package/src/assets/web-panel/assets/{index-gNKj00Fx.js → index-Bkxhaeta.js} +1 -1
- package/src/assets/web-panel/assets/{index-C8LC0nvw.js → index-By6u5IV1.js} +1 -1
- package/src/assets/web-panel/assets/index-Bya64sZE.js +1 -0
- package/src/assets/web-panel/assets/{index-B8irlIUK.js → index-C0k_Nqg9.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9zUawcE.js → index-C3TSohzc.js} +1 -1
- package/src/assets/web-panel/assets/{index-BPojutcZ.js → index-C4DV_5uA.js} +1 -1
- package/src/assets/web-panel/assets/{index-ZPDbEh-L.js → index-C8UltbXK.js} +1 -1
- package/src/assets/web-panel/assets/{index-jS6NASar.js → index-C9_48OzU.js} +1 -1
- package/src/assets/web-panel/assets/{index-67GdrUx2.js → index-CKTfZyfU.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYu-F-gp.js → index-CP2A226L.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cq3l0L7C.js → index-CUhQpiYv.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtQzs0hH.js → index-CX78k-qf.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bdxe0nrD.js → index-CYopNjcJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-uc8Rz96X.js → index-Cao5DZy7.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnTM8FWK.js → index-CfiEM64n.js} +3 -3
- package/src/assets/web-panel/assets/{index-DHskwmwI.js → index-ConjaJro.js} +1 -1
- package/src/assets/web-panel/assets/{index-CA72qB1n.js → index-CqcLo5WV.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dn-JFqEi.js → index-CySTJKeq.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUWmZ059.js → index-D1phyca8.js} +1 -1
- package/src/assets/web-panel/assets/{index-_-Vt1Wqk.js → index-D9LyiN-L.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIljkSt9.js → index-DA2f1UV3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9TzVW7i.js → index-DAOpQvl_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRKukX1W.js → index-DE-hmI2l.js} +1 -1
- package/src/assets/web-panel/assets/{index-DIMVe3HJ.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-FlBuITVG.js → index-DaA__-q1.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cqy-hqD9.js → index-FcV8J_Qz.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpEIwp4A.js → index-P2mfY27Z.js} +1 -1
- package/src/assets/web-panel/assets/{index-nnmo3ipi.js → index-et5JIPHO.js} +1 -1
- package/src/assets/web-panel/assets/{index-SmEIFaRm.js → index-f4Bo4KXm.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMrqEsOS.js → index-kAqQXfP6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CV06YdPO.js → index-kt_5rfcZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN6-OEgS.js → index-so8eIIxF.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRd1zkLB.js → index-yYTQZypF.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-D0mTMUGz.js → initDefaultProps-CPYEIdpV.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DK16p4-o.js → motion-Cjim9pxs.js} +1 -1
- package/src/assets/web-panel/assets/{move-DalC20HC.js → move-DuU-cgz3.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-B5RusszF.js → mtc-parser-DJHNAkta.js} +1 -1
- package/src/assets/web-panel/assets/{omit-C9Mhitht.js → omit-DSQBgdBj.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-51xQLgwM.js → pickAttrs-CTmT_j5m.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CJzIqp8u.js → placementArrow-Lfm6v-r4.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-HQvC19u4.js → responsiveObserve-CgrcPcDi.js} +1 -1
- package/src/assets/web-panel/assets/{slide-C6hhabOp.js → slide-CuE3110i.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Q1r48-oc.js → statusUtils-DiK5nbpf.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-UakVWrHs.js → styleChecker-DPEJdQaZ.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dmv94cxx.js → useFlexGapSupport-CnnodGmp.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DrxOnt_V.js → useFs-DWF84Yav.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BVf2M-Hg.js → usePersonalDataHub-a5b5Dw2f.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BzzW9qKH.js → vnode-DAluzXOq.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C1ZvMgAZ.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 +245 -30
- package/src/data/changelog.json +28 -2
- package/src/harness/mcp-client.js +256 -43
- 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 +52 -6
- package/src/lib/cowork-task-runner.js +1 -0
- package/src/lib/cowork-task-templates.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-executable-identity.js +75 -11
- package/src/lib/mcp-stdio-execution-authority.js +168 -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 +495 -126
- package/src/lib/process-execution-broker/platform-sandbox.js +1424 -189
- package/src/lib/process-execution-broker/windows-sandbox-helper.dll +0 -0
- package/src/lib/process-execution-broker/windows-sandbox-helper.exe +0 -0
- package/src/lib/process-execution-broker/windows-sandbox.cs +5 -4
- package/src/lib/process-tree-termination.js +17 -1
- package/src/lib/skill-mcp.js +74 -3
- package/src/lib/web-fetch.js +4 -4
- package/src/runtime/agent-runtime.js +61 -8
- package/src/runtime/mcp-config.js +238 -28
- package/src/workers/background-agent-worker.js +492 -71
- package/src/assets/web-panel/assets/Projects-DdCpNRNt.js +0 -1
- package/src/assets/web-panel/assets/Providers-CyvtUkUf.js +0 -1
- package/src/assets/web-panel/assets/Skills-CCCEmSf5.js +0 -1
- package/src/assets/web-panel/assets/Tasks-4dbAJng4.js +0 -1
- package/src/assets/web-panel/assets/devWarning-uadaGF_y.js +0 -1
- package/src/assets/web-panel/assets/index-C7zhie9M.js +0 -1
- package/src/assets/web-panel/assets/index-Cmbg0Wbr.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
|
-
|
|
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
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
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:
|
|
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,13 +2422,21 @@ 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
|
)
|
|
2359
2429
|
: null);
|
|
2430
|
+
const capsuleUsesLinuxProcessTreeSandbox =
|
|
2431
|
+
Boolean(stdioCapsuleSandboxExecutionContract) &&
|
|
2432
|
+
process.platform === "linux" &&
|
|
2433
|
+
sourceConfig.sandboxPolicy?.requiredBoundaries?.some((boundary) =>
|
|
2434
|
+
["filesystem", "network", "process-tree"].includes(boundary),
|
|
2435
|
+
);
|
|
2360
2436
|
const capsuleUsesPosixProcessGroup =
|
|
2361
2437
|
Boolean(stdioCapsuleSandboxExecutionContract) &&
|
|
2362
|
-
process.platform !== "win32"
|
|
2438
|
+
process.platform !== "win32" &&
|
|
2439
|
+
!capsuleUsesLinuxProcessTreeSandbox;
|
|
2363
2440
|
const launchOptions = {
|
|
2364
2441
|
...spawnOptions,
|
|
2365
2442
|
detached:
|
|
@@ -4925,6 +5002,9 @@ export class MCPServerConfig {
|
|
|
4925
5002
|
command TEXT,
|
|
4926
5003
|
args TEXT DEFAULT '[]',
|
|
4927
5004
|
env TEXT DEFAULT '{}',
|
|
5005
|
+
runtime_kind TEXT,
|
|
5006
|
+
cwd TEXT,
|
|
5007
|
+
sandbox_policy TEXT,
|
|
4928
5008
|
auto_connect INTEGER DEFAULT 0,
|
|
4929
5009
|
url TEXT,
|
|
4930
5010
|
transport TEXT DEFAULT 'stdio',
|
|
@@ -4941,9 +5021,9 @@ export class MCPServerConfig {
|
|
|
4941
5021
|
}
|
|
4942
5022
|
|
|
4943
5023
|
/**
|
|
4944
|
-
* Idempotent schema migration for databases that predate
|
|
4945
|
-
* columns. Silently skipped on mocks or anything
|
|
4946
|
-
* `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()`.
|
|
4947
5027
|
*/
|
|
4948
5028
|
_migrateSchema() {
|
|
4949
5029
|
try {
|
|
@@ -4956,6 +5036,15 @@ export class MCPServerConfig {
|
|
|
4956
5036
|
if (!cols.has("url")) {
|
|
4957
5037
|
this.db.exec("ALTER TABLE mcp_servers ADD COLUMN url TEXT");
|
|
4958
5038
|
}
|
|
5039
|
+
if (!cols.has("runtime_kind")) {
|
|
5040
|
+
this.db.exec("ALTER TABLE mcp_servers ADD COLUMN runtime_kind TEXT");
|
|
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
|
+
}
|
|
4959
5048
|
if (!cols.has("transport")) {
|
|
4960
5049
|
this.db.exec(
|
|
4961
5050
|
"ALTER TABLE mcp_servers ADD COLUMN transport TEXT DEFAULT 'stdio'",
|
|
@@ -4989,6 +5078,13 @@ export class MCPServerConfig {
|
|
|
4989
5078
|
}
|
|
4990
5079
|
|
|
4991
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
|
+
}
|
|
4992
5088
|
const url = config.url || null;
|
|
4993
5089
|
const transport =
|
|
4994
5090
|
config.transport || (url ? inferTransport({ url }) : "stdio");
|
|
@@ -5016,6 +5112,17 @@ export class MCPServerConfig {
|
|
|
5016
5112
|
typeof config.headersHelper === "string" && config.headersHelper.trim()
|
|
5017
5113
|
? config.headersHelper
|
|
5018
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
|
+
}
|
|
5019
5126
|
// The legacy table uses `name` as its primary key. Encode workspace-bound
|
|
5020
5127
|
// identities in that key so `foo` can coexist at local/project/user scope
|
|
5021
5128
|
// while retaining the old key for user rows and backwards compatibility.
|
|
@@ -5023,15 +5130,41 @@ export class MCPServerConfig {
|
|
|
5023
5130
|
configScope === "user"
|
|
5024
5131
|
? name
|
|
5025
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
|
+
}
|
|
5026
5156
|
this.db
|
|
5027
5157
|
.prepare(
|
|
5028
|
-
"INSERT OR REPLACE INTO mcp_servers (name, command, args, env, 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'))",
|
|
5029
5159
|
)
|
|
5030
5160
|
.run(
|
|
5031
5161
|
storageName,
|
|
5032
5162
|
config.command || null,
|
|
5033
5163
|
JSON.stringify(config.args || []),
|
|
5034
5164
|
JSON.stringify(config.env || {}),
|
|
5165
|
+
config.runtimeKind || null,
|
|
5166
|
+
workingDirectory,
|
|
5167
|
+
sandboxPolicy ? JSON.stringify(sandboxPolicy) : null,
|
|
5035
5168
|
config.autoConnect ? 1 : 0,
|
|
5036
5169
|
url,
|
|
5037
5170
|
transport,
|
|
@@ -5068,6 +5201,26 @@ export class MCPServerConfig {
|
|
|
5068
5201
|
}
|
|
5069
5202
|
|
|
5070
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
|
+
}
|
|
5071
5224
|
const config = {
|
|
5072
5225
|
name: row.display_name || row.name,
|
|
5073
5226
|
command: row.command || null,
|
|
@@ -5076,6 +5229,9 @@ export class MCPServerConfig {
|
|
|
5076
5229
|
// MCP server list (`|| "[]"` only guards NULL, not a corrupt non-empty string).
|
|
5077
5230
|
args: safeJsonParse(row.args, []),
|
|
5078
5231
|
env: safeJsonParse(row.env, {}),
|
|
5232
|
+
runtimeKind: row.runtime_kind || null,
|
|
5233
|
+
...(cwd ? { cwd } : {}),
|
|
5234
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
5079
5235
|
autoConnect: row.auto_connect === 1,
|
|
5080
5236
|
url: row.url || null,
|
|
5081
5237
|
transport:
|
|
@@ -5103,39 +5259,96 @@ export class MCPServerConfig {
|
|
|
5103
5259
|
return this.list(options).find((config) => config.name === name) || null;
|
|
5104
5260
|
}
|
|
5105
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
|
+
|
|
5106
5299
|
list(options = {}) {
|
|
5107
5300
|
const rows = this.db
|
|
5108
5301
|
.prepare("SELECT * FROM mcp_servers ORDER BY name")
|
|
5109
5302
|
.all();
|
|
5110
|
-
const visible = rows
|
|
5111
|
-
|
|
5112
|
-
.
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
: 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);
|
|
5116
5308
|
}
|
|
5117
5309
|
|
|
5118
5310
|
getAutoConnect(options = {}) {
|
|
5119
5311
|
const rows = this.db
|
|
5120
|
-
.prepare("SELECT * FROM mcp_servers
|
|
5121
|
-
.all(
|
|
5122
|
-
const visible = rows
|
|
5123
|
-
|
|
5124
|
-
.
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
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
|
+
);
|
|
5128
5321
|
}
|
|
5129
5322
|
|
|
5130
5323
|
_effectiveByName(configs) {
|
|
5131
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
|
+
};
|
|
5132
5336
|
const selected = new Map();
|
|
5133
5337
|
for (const config of configs) {
|
|
5134
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;
|
|
5135
5343
|
if (
|
|
5136
5344
|
!current ||
|
|
5137
|
-
|
|
5138
|
-
|
|
5345
|
+
candidatePriority > currentPriority ||
|
|
5346
|
+
(candidatePriority === currentPriority &&
|
|
5347
|
+
candidateSpecificity > currentSpecificity) ||
|
|
5348
|
+
(candidatePriority === currentPriority &&
|
|
5349
|
+
candidateSpecificity === currentSpecificity &&
|
|
5350
|
+
isInvalidMcpConfigRow(config) &&
|
|
5351
|
+
!isInvalidMcpConfigRow(current))
|
|
5139
5352
|
) {
|
|
5140
5353
|
selected.set(config.name, config);
|
|
5141
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
|
-
|
|
106
|
-
|
|
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
|
-
|
|
178
|
-
|
|
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
|
/**
|