chainlesschain 0.162.183 → 0.162.185
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/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-BapS7lz4.js → AIOps-Co4ipAKx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CXYdXEUI.js → ActionButton-BpsoDrDq.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-CKR5Lexn.js → Analytics-DMUqeBik.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DATVUOXB.js → AppLayout-C6fD_dXr.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-vb_0ZlV1.js → Artifacts-DW0YlQlV.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-BI1l9GWt.js → Audit-DXtHiiFK.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BFXbaUEf.js → BackgroundAgents-BgmF62U-.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DEYBJJ6H.js → Backup-D8vhVLQC.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-COdzeO0M.js → BaseInput-D7nCtY2X.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-oSN6sfM5.js → Chat-CBZLEV2G.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-CI_jT0ru.js → ChatBubbleRenderer-CtDb-2Qu.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-BezEa4tm.js → Checkbox-DJLkeD4M.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-CNEL0Ife.js → Codegen-Cs6xO8Fd.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Bf2Y2Bn1.js → Col-Dtx9wxfT.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BfUlu4Hc.js → Community-DiigJg4n.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BN4xWP9e.js → Compact-B67oMDDL.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Bt6BKmEP.js → Compliance-BBDl-Jos.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-DKLp8YxO.js → Cowork-Ba98hSsa.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-o7Hhw6ky.js → Cron-DBWQZIz7.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-DaNXWJIw.js → Crosschain-OcrKzxAt.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B-Qa2mha.js → DID-mW2eSbjM.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CMX0Qnxw.js → Dashboard-BmsDPmKJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BLuLyRu_.js → Dropdown-CVzmCyRx.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-CFBAeMij.js → EmailListRenderer-vVgqKzhh.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-DWzXQ67V.js → FamilyGuardDashboard-CWY5t_ZJ.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-DB1QSLgr.js → Federation-ZGomhjxr.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-C-OKCToP.js → FormItemContext-DyaknbLr.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DMJFtFZf.js → GenericCardRenderer-CmIvN1qr.js} +1 -1
- package/src/assets/web-panel/assets/{Git-DsGSujOV.js → Git-DQOJIYkG.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-B0iS9NEW.js → Governance-CMiY-EEp.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CH004H7c.js → Inference-D6IXuucf.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Q9MTQ83c.js → KnowledgeGraph-6jkTL0hV.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B_fyjZK9.js → Logs-N0jD-349.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-BL1IVXm-.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-BIStF9d1.js → Marketplace-WhYuhBxm.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-pGfal768.js → McpTools-DTeO28xy.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-xbNqpIqB.js → Memory-MSHzPSE-.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-B5EQGrIK.js → MobileBridge-Cyqs3J3W.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-7IN2hWHg.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-a_rmfxJH.js → Mtc-MWBnezNM.js} +5 -5
- package/src/assets/web-panel/assets/{MtcAudit-BmKuofUr.js → MtcAudit-DQu35Nu-.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BQbJ-CAN.js → Multisig-CQS_M-DP.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-JtPJKX25.js → NLProgramming-WD16vQvY.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-ZFKznTSG.js → Notes-DqsiSwOq.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-B6aHRBBR.js → NotificationSettings-D3LoxO-P.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DdcsOqEz.js → OrderTableRenderer-C5jPoTd6.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BFDTAUgS.js → Organization-48aMBJso.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-0FoTfbWH.js → Overflow-j-a2XkhR.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CWd90etw.js → P2P-BYpBNYnh.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DtCGC-W-.js → PdhVaultBrowser-CYCsrrLg.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CvQ7nr7T.js → Permissions-ZRXGXDFb.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-rdNPcR-Z.js → PersonalDataHub-6OWUPHyL.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-CmrfiNOs.js → Pipeline-FcjlDK9E.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-D7ep1h_t.js → Privacy-vD_6Tldp.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-C0xKINHS.js → ProjectInit-B_CHOLXp.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-AOmZFp-F.js → ProjectSettings-rEiqQ2da.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-BWHSkXZP.js → Projects-Bg8obRAM.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-Cb7JCTPL.js → Providers-Dgtt97B4.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BDMehcXC.js → QrScannerModal-DaGVylZO.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-BF6_1nvx.js → QuickAsk-DP2_2x3u.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-BHfT-A28.js → Recommend-D29yj4ki.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-Ue2wnBLp.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-CsWHLEcj.js → Reputation-CrxIHqeC.js} +1 -1
- package/src/assets/web-panel/assets/{Row-D6srnWhm.js → Row-BgSzJPCn.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Xhr-KyVd.js → RssFeed-AwhYOrP9.js} +3 -3
- package/src/assets/web-panel/assets/{Search-vIbIJvwL.js → Search-D2shpEcm.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bzi5Aiko.js → Security-BtKzhTbT.js} +4 -4
- package/src/assets/web-panel/assets/{Services-BHtVX52P.js → Services-BY0DsUSm.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-C0R8B2jf.js → Skeleton-B3Sb0UI9.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DtT6rWOX.js → Skills-Ne5w0bDs.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-0B37mxVU.js → Sla-C8VkaP4T.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-0V2jE1Bs.js → SpeechSettings-BQdIEYP9.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-CYedRzaj.js → SyncSettings-CwFafrUg.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-yJuKoDlR.js +1 -0
- package/src/assets/web-panel/assets/{Templates-BKoHztGT.js → Templates-DYn9qCmx.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant--17nwVNZ.js → Tenant-DkKReHvn.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-r6bd-owt.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-C5jx4AE3.js → TimelineRenderer-Mq2juUC3.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DYJKYa9t.js → Tokens-BnMw7MRi.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-DChpWiQ9.js → Trigger-CTzKp4o9.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DfWG52EA.js → Trust-BBrSZm9U.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CMugYcyA.js → UkeySign-D9fDvEMq.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BOSmrHAd.js → VideoEditing-DUi5ve-0.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-D5g3k3Sa.js → Wallet-C8Fi1P8_.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-D-7u4Mkc.js → WebAuthn-s1LppYi2.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CFzZMSUJ.js → WorkflowEditor-DJL2C4lj.js} +1 -1
- package/src/assets/web-panel/assets/{chat-VbR6l6mc.js → chat-D3F2ndOB.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BSZn8EDS.js → colors-B7739-e3.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-y74obMEq.js → compact-item-CcJbFTF4.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-C326bjex.js → createContext-CuP_DEFS.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-Cj_36GHq.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-D_INJQT_.js → hasIn-6YEXfIik.js} +1 -1
- package/src/assets/web-panel/assets/{index-DuIkfnTF.js → index-4h3lpWJ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-DBkzrpiL.js → index-BIID4XcP.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7SrC-On.js → index-BOHUo3aM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnSfnSMP.js → index-BYYSLEjn.js} +1 -1
- package/src/assets/web-panel/assets/{index-KF9Zmqln.js → index-Bh4hcNWW.js} +1 -1
- package/src/assets/web-panel/assets/{index-C327pLbw.js → index-BhIKnQAW.js} +1 -1
- package/src/assets/web-panel/assets/{index-E_v1ZCTP.js → index-Bru_kpZl.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bfw2hf2s.js → index-BsdSnHQE.js} +1 -1
- package/src/assets/web-panel/assets/{index-jYaogFE8.js → index-BxSqVMGh.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dlxh9D8D.js → index-C3ZA_h1j.js} +1 -1
- package/src/assets/web-panel/assets/{index-DdyjrCAu.js → index-C3pctdCb.js} +1 -1
- package/src/assets/web-panel/assets/{index-abmn_8Yz.js → index-CRuoRapN.js} +1 -1
- package/src/assets/web-panel/assets/{index-qMA4GYdQ.js → index-CY0zd_jc.js} +1 -1
- package/src/assets/web-panel/assets/index-CezFrAmD.js +1 -0
- package/src/assets/web-panel/assets/index-Cxgguez6.js +1 -0
- package/src/assets/web-panel/assets/{index-Cr3uuz0C.js → index-CxradkS6.js} +19 -19
- package/src/assets/web-panel/assets/{index-DwHSZLXg.js → index-D0K_rFC1.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMHqs3Jb.js → index-D0zdPyTD.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bnua8IX3.js → index-D3haorUt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BY8-nCgs.js → index-D6Ij_2uw.js} +1 -1
- package/src/assets/web-panel/assets/{index-FQBsuExX.js → index-DDRlBaDt.js} +1 -1
- package/src/assets/web-panel/assets/{index-C-ZCaUJc.js → index-DGsE7MQz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BjcWKg1S.js → index-DHvxXXNA.js} +1 -1
- package/src/assets/web-panel/assets/{index-GA80ir_u.js → index-DV11jpeO.js} +1 -1
- package/src/assets/web-panel/assets/{index-CsovTpx9.js → index-DXdnfDVK.js} +1 -1
- package/src/assets/web-panel/assets/{index-nSn2-C4N.js → index-D_75jYWk.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJi3UqBt.js → index-DjI9MkId.js} +1 -1
- package/src/assets/web-panel/assets/{index-gINyI1rh.js → index-DlSvpNMf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CfLJ42kv.js → index-Dox44j29.js} +1 -1
- package/src/assets/web-panel/assets/{index-C2NyllnW.js → index-DpEKmjoa.js} +1 -1
- package/src/assets/web-panel/assets/{index-Iw0FfqUz.js → index-Dsy7_x4s.js} +1 -1
- package/src/assets/web-panel/assets/{index-4eyH-eC2.js → index-Gxw09e9R.js} +1 -1
- package/src/assets/web-panel/assets/{index-4PIeTM--.js → index-LfRlSmqe.js} +1 -1
- package/src/assets/web-panel/assets/{index-IZAWt0H8.js → index-Nz-MUHD1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUU2606o.js → index-b9cuvFEv.js} +1 -1
- package/src/assets/web-panel/assets/{index-CzE53HiB.js → index-eR6H6NR7.js} +1 -1
- package/src/assets/web-panel/assets/{index-9PIVuXqo.js → index-fWgUQZ9d.js} +1 -1
- package/src/assets/web-panel/assets/{index-DkH1JvEt.js → index-mfWQRSLn.js} +1 -1
- package/src/assets/web-panel/assets/{index-DNMxIsDg.js → index-yOZYSNmG.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DI0Zaf8-.js → initDefaultProps-CJ1Bq2L3.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BZDKSy3Z.js → motion-rYJN_vzf.js} +1 -1
- package/src/assets/web-panel/assets/{move-DsIW01Vp.js → move-DnDK2gTP.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CohkDhsf.js → mtc-parser-CKnXy7ES.js} +1 -1
- package/src/assets/web-panel/assets/{omit-DpiN5rzg.js → omit-3nRc3hWT.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-qbuqni3y.js → pickAttrs-Ddjj3sLu.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-mBky6714.js → placementArrow-CbsbphQY.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-Bas3eQ2K.js → responsiveObserve-D9aZ4VDJ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CBx_SKoK.js → slide-D4pvdFj0.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-BlYguJSV.js → statusUtils-C2W-vY-S.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BCYQmRGv.js → styleChecker-Cbq5150d.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-Dp5dybLM.js → useFlexGapSupport-BA2B7R82.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-DHAMBgA2.js → useFs-CLrwg3xg.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-W6jsSw76.js → usePersonalDataHub-DQeAI5qs.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BkI6aayc.js → vnode-8BDGy-YU.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C_IMGueq.js → zoom-B2SzFprl.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +74 -1
- package/src/commands/background-session.js +11 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +90 -27
- package/src/commands/session.js +31 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +26 -0
- package/src/gateways/terminal/PtyManager.js +45 -15
- package/src/gateways/ws/ws-agent-handler.js +15 -1
- package/src/gateways/ws/ws-session-gateway.js +88 -0
- package/src/harness/background-task-command-runner.js +234 -0
- package/src/harness/background-task-manager.js +150 -20
- package/src/harness/background-task-worker.js +17 -11
- package/src/harness/jsonl-session-store.js +57 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -1
- package/src/lib/agent-team/team-worktree.js +3 -0
- package/src/lib/async-hook-supervisor.cjs +82 -10
- package/src/lib/async-hook-supervisor.js +50 -4
- package/src/lib/background-agent-supervisor.js +46 -0
- package/src/lib/collaboration-run-store.js +362 -0
- package/src/lib/event-runtime-host.js +12 -10
- package/src/lib/hook-runner.cjs +105 -18
- package/src/lib/hook-runner.js +79 -6
- package/src/lib/hook-shell-command.cjs +148 -0
- package/src/lib/hooks-v2-runtime.js +280 -81
- package/src/lib/hooks-v2-workspace-context.js +258 -0
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +15 -0
- package/src/lib/lsp/lsp-client.js +12 -1
- package/src/lib/plugin-monitor-supervisor.js +53 -16
- package/src/lib/plugin-runtime/bin.js +21 -5
- package/src/lib/plugin-runtime/install.js +249 -57
- package/src/lib/plugin-runtime/mcp.js +21 -2
- package/src/lib/plugin-runtime/monitors.js +19 -3
- package/src/lib/plugin-runtime/sandbox-policy.js +100 -4
- package/src/lib/process-execution-broker/index.js +1151 -44
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1557 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +1378 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +586 -58
- package/src/lib/session-usage.js +202 -2
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +40 -3
- package/src/runtime/agent-core.js +304 -42
- package/src/runtime/headless-runner.js +48 -1
- package/src/runtime/headless-stream.js +42 -2
- package/src/workers/background-agent-worker.js +5 -11
- package/src/assets/web-panel/assets/MarkdownRenderer-C0o4FtpD.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-VTc3gT1y.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-B6EEf8hh.js +0 -5
- package/src/assets/web-panel/assets/Tasks-D-R1x8vm.js +0 -1
- package/src/assets/web-panel/assets/Terminal-Dnkos7vW.js +0 -3
- package/src/assets/web-panel/assets/devWarning-D3Bho_6u.js +0 -1
- package/src/assets/web-panel/assets/index-5iAFwYx1.js +0 -1
- package/src/assets/web-panel/assets/index-BfnB7LPx.js +0 -1
|
@@ -8,8 +8,15 @@
|
|
|
8
8
|
* Feature-flag gated: BACKGROUND_TASKS
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
existsSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
appendFileSync,
|
|
15
|
+
readFileSync,
|
|
16
|
+
realpathSync,
|
|
17
|
+
statSync,
|
|
18
|
+
} from "node:fs";
|
|
19
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
20
|
import { createHash } from "node:crypto";
|
|
14
21
|
import { EventEmitter } from "node:events";
|
|
15
22
|
import { getHomeDir } from "../lib/paths.js";
|
|
@@ -17,6 +24,7 @@ import executionBroker from "../lib/process-execution-broker/index.js";
|
|
|
17
24
|
|
|
18
25
|
export const _deps = {
|
|
19
26
|
spawn: executionBroker.spawn.bind(executionBroker),
|
|
27
|
+
platform: process.platform,
|
|
20
28
|
};
|
|
21
29
|
|
|
22
30
|
function getTasksDir() {
|
|
@@ -41,6 +49,7 @@ const RECOVERABLE_TASK_STATUSES = new Set([
|
|
|
41
49
|
TaskStatus.PENDING,
|
|
42
50
|
TaskStatus.RUNNING,
|
|
43
51
|
]);
|
|
52
|
+
const SUPPORTED_STRONG_BOUNDARIES = new Set(["filesystem", "network"]);
|
|
44
53
|
|
|
45
54
|
function validatePinnedSandboxPolicy(policy) {
|
|
46
55
|
if (policy == null) return null;
|
|
@@ -51,7 +60,9 @@ function validatePinnedSandboxPolicy(policy) {
|
|
|
51
60
|
!Array.isArray(policy.requiredBoundaries) ||
|
|
52
61
|
policy.requiredBoundaries.length === 0 ||
|
|
53
62
|
policy.requiredBoundaries.some(
|
|
54
|
-
(boundary) =>
|
|
63
|
+
(boundary) =>
|
|
64
|
+
typeof boundary !== "string" ||
|
|
65
|
+
!SUPPORTED_STRONG_BOUNDARIES.has(boundary),
|
|
55
66
|
) ||
|
|
56
67
|
!Object.isFrozen(policy) ||
|
|
57
68
|
!Object.isFrozen(policy.requiredBoundaries)
|
|
@@ -65,22 +76,88 @@ function validatePinnedSandboxPolicy(policy) {
|
|
|
65
76
|
return policy;
|
|
66
77
|
}
|
|
67
78
|
|
|
68
|
-
function
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
);
|
|
73
|
-
error.code = "ERR_BACKGROUND_TASK_SANDBOX_UNSUPPORTED";
|
|
74
|
-
error.sandboxReason = "background_execution_unsupported";
|
|
79
|
+
function createSandboxError(code, reason, message, requiredBoundaries = []) {
|
|
80
|
+
const error = new Error(message);
|
|
81
|
+
error.code = code;
|
|
82
|
+
error.sandboxReason = reason;
|
|
75
83
|
error.sandboxFailClosed = true;
|
|
76
|
-
error.requiredBoundaries = requiredBoundaries;
|
|
84
|
+
error.requiredBoundaries = [...requiredBoundaries];
|
|
77
85
|
error.actualGuarantees = [];
|
|
78
86
|
error.missingBoundaries = [...requiredBoundaries];
|
|
79
87
|
error.sandboxBackend = null;
|
|
80
|
-
error.sandboxCandidateBackend =
|
|
88
|
+
error.sandboxCandidateBackend =
|
|
89
|
+
_deps.platform === "linux" ? "linux-bwrap-workspace" : null;
|
|
81
90
|
return error;
|
|
82
91
|
}
|
|
83
92
|
|
|
93
|
+
function canonicalSandboxBinding(workspaceCwd, executionCwd) {
|
|
94
|
+
try {
|
|
95
|
+
if (
|
|
96
|
+
typeof workspaceCwd !== "string" ||
|
|
97
|
+
!isAbsolute(workspaceCwd) ||
|
|
98
|
+
typeof executionCwd !== "string"
|
|
99
|
+
) {
|
|
100
|
+
throw new Error("sandbox paths must be absolute");
|
|
101
|
+
}
|
|
102
|
+
const workspaceRoot = realpathSync.native(resolve(workspaceCwd));
|
|
103
|
+
const workingDirectory = realpathSync.native(resolve(executionCwd));
|
|
104
|
+
if (
|
|
105
|
+
!statSync(workspaceRoot).isDirectory() ||
|
|
106
|
+
!statSync(workingDirectory).isDirectory()
|
|
107
|
+
) {
|
|
108
|
+
throw new Error("sandbox paths must be directories");
|
|
109
|
+
}
|
|
110
|
+
const rel = relative(workspaceRoot, workingDirectory);
|
|
111
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
|
|
112
|
+
throw new Error("execution cwd escapes workspace");
|
|
113
|
+
}
|
|
114
|
+
return { workspaceRoot, workingDirectory };
|
|
115
|
+
} catch (cause) {
|
|
116
|
+
const error = createSandboxError(
|
|
117
|
+
"ERR_BACKGROUND_TASK_SANDBOX_BINDING_INVALID",
|
|
118
|
+
"background_sandbox_binding_invalid",
|
|
119
|
+
"Background task sandbox workspace/cwd binding is invalid.",
|
|
120
|
+
);
|
|
121
|
+
error.cause = cause;
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function sameBoundaries(left, right) {
|
|
127
|
+
const normalizedLeft = Array.isArray(left) ? [...left].sort() : null;
|
|
128
|
+
const normalizedRight = Array.isArray(right) ? [...right].sort() : null;
|
|
129
|
+
return (
|
|
130
|
+
normalizedLeft &&
|
|
131
|
+
normalizedRight &&
|
|
132
|
+
normalizedLeft.length === normalizedRight.length &&
|
|
133
|
+
normalizedLeft.every((value, index) => value === normalizedRight[index])
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function strongWorkerEnvironment(taskId) {
|
|
138
|
+
const blocked = new Set([
|
|
139
|
+
"BASH_ENV",
|
|
140
|
+
"CC_TASK_ID",
|
|
141
|
+
"ELECTRON_RUN_AS_NODE",
|
|
142
|
+
"ENV",
|
|
143
|
+
"GCONV_PATH",
|
|
144
|
+
"OPENSSL_CONF",
|
|
145
|
+
"RUBYOPT",
|
|
146
|
+
]);
|
|
147
|
+
const blockedPrefixes = ["DYLD_", "LD_", "NODE_", "PYTHON"];
|
|
148
|
+
return Object.fromEntries([
|
|
149
|
+
...Object.entries(process.env).filter(([key, value]) => {
|
|
150
|
+
const normalizedKey = key.toUpperCase();
|
|
151
|
+
return (
|
|
152
|
+
value != null &&
|
|
153
|
+
!blocked.has(normalizedKey) &&
|
|
154
|
+
!blockedPrefixes.some((prefix) => normalizedKey.startsWith(prefix))
|
|
155
|
+
);
|
|
156
|
+
}),
|
|
157
|
+
["CC_TASK_ID", taskId],
|
|
158
|
+
]);
|
|
159
|
+
}
|
|
160
|
+
|
|
84
161
|
export class BackgroundTaskManager extends EventEmitter {
|
|
85
162
|
constructor(options = {}) {
|
|
86
163
|
super();
|
|
@@ -123,14 +200,17 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
123
200
|
}
|
|
124
201
|
|
|
125
202
|
const cwd = spec.cwd || process.cwd();
|
|
126
|
-
this.
|
|
203
|
+
const sandboxPolicy = this._resolvePinnedSandboxPolicy(cwd);
|
|
204
|
+
const sandboxBinding = sandboxPolicy
|
|
205
|
+
? canonicalSandboxBinding(this._policyCwd, cwd)
|
|
206
|
+
: null;
|
|
127
207
|
|
|
128
208
|
const id = `task-${Date.now()}-${createHash("sha256").update(Math.random().toString()).digest("hex").slice(0, 6)}`;
|
|
129
209
|
const task = {
|
|
130
210
|
id,
|
|
131
211
|
type: spec.type || "shell",
|
|
132
212
|
command: spec.command,
|
|
133
|
-
cwd,
|
|
213
|
+
cwd: sandboxBinding?.workingDirectory || cwd,
|
|
134
214
|
description: spec.description || spec.command,
|
|
135
215
|
status: TaskStatus.PENDING,
|
|
136
216
|
createdAt: Date.now(),
|
|
@@ -145,6 +225,10 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
145
225
|
recoverySourceStatus: null,
|
|
146
226
|
ownerNodeId: spec.ownerNodeId || this.nodeId,
|
|
147
227
|
recoveryDecision: null,
|
|
228
|
+
sandboxWorkspaceCwd: sandboxBinding?.workspaceRoot || null,
|
|
229
|
+
sandboxRequiredBoundaries: sandboxPolicy
|
|
230
|
+
? [...sandboxPolicy.requiredBoundaries].sort()
|
|
231
|
+
: null,
|
|
148
232
|
_seq: ++this._createSeq,
|
|
149
233
|
};
|
|
150
234
|
|
|
@@ -164,7 +248,34 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
164
248
|
throw new Error(`Task ${taskId} is not pending (status: ${task.status})`);
|
|
165
249
|
}
|
|
166
250
|
|
|
167
|
-
this.
|
|
251
|
+
const sandboxPolicy = this._resolvePinnedSandboxPolicy(task.cwd);
|
|
252
|
+
const pinnedBoundaries = task.sandboxRequiredBoundaries;
|
|
253
|
+
if (
|
|
254
|
+
Boolean(sandboxPolicy) !== Array.isArray(pinnedBoundaries) ||
|
|
255
|
+
(sandboxPolicy &&
|
|
256
|
+
!sameBoundaries(sandboxPolicy.requiredBoundaries, pinnedBoundaries))
|
|
257
|
+
) {
|
|
258
|
+
throw createSandboxError(
|
|
259
|
+
"ERR_BACKGROUND_TASK_SANDBOX_POLICY_CHANGED",
|
|
260
|
+
"background_sandbox_policy_changed",
|
|
261
|
+
"Background task sandbox policy changed after creation; recreate the task.",
|
|
262
|
+
sandboxPolicy?.requiredBoundaries || pinnedBoundaries || [],
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (sandboxPolicy) {
|
|
266
|
+
const currentBinding = canonicalSandboxBinding(this._policyCwd, task.cwd);
|
|
267
|
+
if (
|
|
268
|
+
currentBinding.workspaceRoot !== task.sandboxWorkspaceCwd ||
|
|
269
|
+
currentBinding.workingDirectory !== task.cwd
|
|
270
|
+
) {
|
|
271
|
+
throw createSandboxError(
|
|
272
|
+
"ERR_BACKGROUND_TASK_SANDBOX_BINDING_CHANGED",
|
|
273
|
+
"background_sandbox_binding_changed",
|
|
274
|
+
"Background task sandbox workspace/cwd identity changed after creation; recreate the task.",
|
|
275
|
+
pinnedBoundaries,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
168
279
|
|
|
169
280
|
task.status = TaskStatus.RUNNING;
|
|
170
281
|
task.startedAt = Date.now();
|
|
@@ -179,11 +290,21 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
179
290
|
);
|
|
180
291
|
const child = _deps.spawn(
|
|
181
292
|
process.execPath,
|
|
182
|
-
[
|
|
293
|
+
[
|
|
294
|
+
...(sandboxPolicy ? [] : process.execArgv),
|
|
295
|
+
workerPath,
|
|
296
|
+
task.command,
|
|
297
|
+
task.cwd,
|
|
298
|
+
task.type,
|
|
299
|
+
task.sandboxWorkspaceCwd || "",
|
|
300
|
+
JSON.stringify(task.sandboxRequiredBoundaries || []),
|
|
301
|
+
],
|
|
183
302
|
{
|
|
184
303
|
cwd: task.cwd,
|
|
185
304
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
186
|
-
env:
|
|
305
|
+
env: sandboxPolicy
|
|
306
|
+
? strongWorkerEnvironment(taskId)
|
|
307
|
+
: { ...process.env, CC_TASK_ID: taskId },
|
|
187
308
|
origin: "background-task:worker",
|
|
188
309
|
policy: "allow",
|
|
189
310
|
scope: "background-task",
|
|
@@ -199,6 +320,9 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
199
320
|
} else if (msg.type === "result") {
|
|
200
321
|
this._complete(taskId, TaskStatus.COMPLETED, msg.data, null);
|
|
201
322
|
} else if (msg.type === "error") {
|
|
323
|
+
task.errorCode = msg.code || null;
|
|
324
|
+
task.sandboxReason = msg.sandboxReason || null;
|
|
325
|
+
task.sandboxFailClosed = msg.sandboxFailClosed === true;
|
|
202
326
|
this._complete(taskId, TaskStatus.FAILED, null, msg.error);
|
|
203
327
|
}
|
|
204
328
|
});
|
|
@@ -240,7 +364,7 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
240
364
|
return task;
|
|
241
365
|
}
|
|
242
366
|
|
|
243
|
-
|
|
367
|
+
_resolvePinnedSandboxPolicy(executionCwd) {
|
|
244
368
|
if (!this._resolveSandboxPolicy) return;
|
|
245
369
|
const sandboxPolicy = validatePinnedSandboxPolicy(
|
|
246
370
|
this._resolveSandboxPolicy({
|
|
@@ -248,9 +372,15 @@ export class BackgroundTaskManager extends EventEmitter {
|
|
|
248
372
|
executionCwd,
|
|
249
373
|
}),
|
|
250
374
|
);
|
|
251
|
-
if (sandboxPolicy) {
|
|
252
|
-
throw
|
|
375
|
+
if (sandboxPolicy && _deps.platform !== "linux") {
|
|
376
|
+
throw createSandboxError(
|
|
377
|
+
"ERR_BACKGROUND_TASK_SANDBOX_UNSUPPORTED",
|
|
378
|
+
"background_platform_backend_unavailable",
|
|
379
|
+
"Background task strong sandbox execution is only available on Linux.",
|
|
380
|
+
sandboxPolicy.requiredBoundaries,
|
|
381
|
+
);
|
|
253
382
|
}
|
|
383
|
+
return sandboxPolicy;
|
|
254
384
|
}
|
|
255
385
|
|
|
256
386
|
get(taskId) {
|
|
@@ -5,24 +5,27 @@
|
|
|
5
5
|
* Sends messages to parent: { type: "heartbeat"|"result"|"error", ... }
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import
|
|
8
|
+
import { executeBackgroundTaskCommand } from "./background-task-command-runner.js";
|
|
9
9
|
|
|
10
|
-
const [
|
|
10
|
+
const [
|
|
11
|
+
command,
|
|
12
|
+
cwd,
|
|
13
|
+
type,
|
|
14
|
+
sandboxWorkspaceCwd = "",
|
|
15
|
+
sandboxRequiredBoundaries = "",
|
|
16
|
+
] = process.argv.slice(2);
|
|
11
17
|
|
|
12
18
|
const heartbeat = setInterval(() => {
|
|
13
19
|
if (process.send) process.send({ type: "heartbeat" });
|
|
14
20
|
}, 5000);
|
|
15
21
|
|
|
16
22
|
try {
|
|
17
|
-
const result =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
policy: "allow",
|
|
24
|
-
scope: "background-task",
|
|
25
|
-
shell: true,
|
|
23
|
+
const result = await executeBackgroundTaskCommand({
|
|
24
|
+
command,
|
|
25
|
+
cwd,
|
|
26
|
+
type,
|
|
27
|
+
workspaceCwd: sandboxWorkspaceCwd,
|
|
28
|
+
requiredBoundaries: sandboxRequiredBoundaries,
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
if (process.send) {
|
|
@@ -33,6 +36,9 @@ try {
|
|
|
33
36
|
process.send({
|
|
34
37
|
type: "error",
|
|
35
38
|
error: err.stderr || err.message || String(err),
|
|
39
|
+
code: err.code || null,
|
|
40
|
+
sandboxReason: err.sandboxReason || null,
|
|
41
|
+
sandboxFailClosed: err.sandboxFailClosed === true,
|
|
36
42
|
});
|
|
37
43
|
}
|
|
38
44
|
process.exitCode = 1;
|
|
@@ -164,22 +164,73 @@ export function appendToolCall(sessionId, toolName, args) {
|
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
166
|
* Compact tool-call record for usage attribution (用量归因): tool name +
|
|
167
|
-
* error flag (+ optional skill
|
|
168
|
-
* the args, which can carry whole file bodies (write_file
|
|
169
|
-
* bloat the transcript. Written at tool-result time by the
|
|
170
|
-
* `cc session usage --by tool|mcp|plugin` and `cc insights`
|
|
171
|
-
* use for any persisted session.
|
|
167
|
+
* error flag (+ optional skill, plugin/version, and bounded observed duration)
|
|
168
|
+
* — deliberately NOT the args, which can carry whole file bodies (write_file
|
|
169
|
+
* content) and would bloat the transcript. Written at tool-result time by the
|
|
170
|
+
* agent drivers so `cc session usage --by tool|mcp|plugin` and `cc insights`
|
|
171
|
+
* can aggregate tool use for any persisted session.
|
|
172
172
|
*/
|
|
173
173
|
export function appendToolCallCompact(
|
|
174
174
|
sessionId,
|
|
175
|
-
{ tool, isError, skill, plugin, pluginVersion } = {},
|
|
175
|
+
{ tool, isError, skill, plugin, pluginVersion, durationMs } = {},
|
|
176
176
|
) {
|
|
177
|
+
const duration = normalizeCompactDuration(durationMs);
|
|
177
178
|
appendEvent(sessionId, "tool_call", {
|
|
178
179
|
tool: tool || "?",
|
|
179
180
|
is_error: Boolean(isError),
|
|
180
181
|
...(skill ? { skill: String(skill) } : {}),
|
|
181
182
|
...(plugin ? { plugin: String(plugin) } : {}),
|
|
182
183
|
...(pluginVersion ? { plugin_version: String(pluginVersion) } : {}),
|
|
184
|
+
...(duration !== null ? { duration_ms: duration } : {}),
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Hard cap for one observed failed LLM/tool attempt (seven days). */
|
|
189
|
+
export const MAX_COMPACT_DURATION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
190
|
+
|
|
191
|
+
function normalizeCompactDuration(value) {
|
|
192
|
+
if (value === null || value === undefined || value === "") return null;
|
|
193
|
+
const number = Number(value);
|
|
194
|
+
if (!Number.isFinite(number) || number < 0) return null;
|
|
195
|
+
return Math.min(MAX_COMPACT_DURATION_MS, Math.round(number));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Persist one automatic LLM stream retry without raw errors, prompts, URLs, or
|
|
200
|
+
* credentials. Provider/model are bounded identity labels; reason is a closed
|
|
201
|
+
* vocabulary produced by classifyStreamRetryReason().
|
|
202
|
+
*/
|
|
203
|
+
export function appendLlmRetryCompact(
|
|
204
|
+
sessionId,
|
|
205
|
+
{ attempt, durationMs, provider, model, reason } = {},
|
|
206
|
+
) {
|
|
207
|
+
const attemptNumber = Math.max(
|
|
208
|
+
1,
|
|
209
|
+
Math.min(15, Math.trunc(Number(attempt) || 1)),
|
|
210
|
+
);
|
|
211
|
+
const duration = normalizeCompactDuration(durationMs);
|
|
212
|
+
const cleanLabel = (value, max) => {
|
|
213
|
+
if (typeof value !== "string") return null;
|
|
214
|
+
const clean = value.replace(/\p{Cc}/gu, "").trim();
|
|
215
|
+
return clean ? clean.slice(0, max) : null;
|
|
216
|
+
};
|
|
217
|
+
const allowedReasons = new Set([
|
|
218
|
+
"timeout",
|
|
219
|
+
"dns",
|
|
220
|
+
"connection_refused",
|
|
221
|
+
"network_unreachable",
|
|
222
|
+
"connection_reset",
|
|
223
|
+
"unknown",
|
|
224
|
+
]);
|
|
225
|
+
const cleanReason = allowedReasons.has(reason) ? reason : "unknown";
|
|
226
|
+
const cleanProvider = cleanLabel(provider, 64);
|
|
227
|
+
const cleanModel = cleanLabel(model, 128);
|
|
228
|
+
appendEvent(sessionId, "llm_retry", {
|
|
229
|
+
attempt: attemptNumber,
|
|
230
|
+
duration_ms: duration ?? 0,
|
|
231
|
+
reason: cleanReason,
|
|
232
|
+
...(cleanProvider ? { provider: cleanProvider } : {}),
|
|
233
|
+
...(cleanModel ? { model: cleanModel } : {}),
|
|
183
234
|
});
|
|
184
235
|
}
|
|
185
236
|
|
|
@@ -14,6 +14,7 @@ import { EventEmitter } from "events";
|
|
|
14
14
|
import { pathToFileURL } from "url";
|
|
15
15
|
import { safeJsonParse } from "../lib/safe-json.js";
|
|
16
16
|
import { EventRuntimeProducer } from "../lib/event-runtime-producer.js";
|
|
17
|
+
import { resolvePluginWorkspaceAuthority } from "../lib/plugin-runtime/sandbox-policy.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Injectable dependencies — overridable from tests.
|
|
@@ -62,8 +63,7 @@ const URL_ELICITATION_REQUIRED = -32042;
|
|
|
62
63
|
function supportsUrlElicitationVersion(version) {
|
|
63
64
|
const normalized = String(version || "");
|
|
64
65
|
return (
|
|
65
|
-
/^\d{4}-\d{2}-\d{2}$/.test(normalized) &&
|
|
66
|
-
normalized >= MCP_PROTOCOL_VERSION
|
|
66
|
+
/^\d{4}-\d{2}-\d{2}$/.test(normalized) && normalized >= MCP_PROTOCOL_VERSION
|
|
67
67
|
);
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -130,9 +130,7 @@ export function normalizeMcpElicitationRequest(
|
|
|
130
130
|
|
|
131
131
|
const elicitationId = String(params.elicitationId || "").trim();
|
|
132
132
|
if (!elicitationId) {
|
|
133
|
-
const error = new Error(
|
|
134
|
-
"URL mode MCP elicitation requires elicitationId",
|
|
135
|
-
);
|
|
133
|
+
const error = new Error("URL mode MCP elicitation requires elicitationId");
|
|
136
134
|
error.code = "CC_MCP_ELICITATION_INVALID";
|
|
137
135
|
throw error;
|
|
138
136
|
}
|
|
@@ -267,10 +265,14 @@ export class MCPClient extends EventEmitter {
|
|
|
267
265
|
Number(options.maxConcurrentElicitations) > 0
|
|
268
266
|
? Math.min(128, Number(options.maxConcurrentElicitations))
|
|
269
267
|
: 32;
|
|
270
|
-
this._protocolVersion =
|
|
271
|
-
|
|
268
|
+
this._protocolVersion = String(
|
|
269
|
+
options.protocolVersion || MCP_PROTOCOL_VERSION,
|
|
270
|
+
);
|
|
272
271
|
this._runtimeProducer = options.eventRuntimeStore
|
|
273
|
-
? new EventRuntimeProducer({
|
|
272
|
+
? new EventRuntimeProducer({
|
|
273
|
+
store: options.eventRuntimeStore,
|
|
274
|
+
emitter: this,
|
|
275
|
+
})
|
|
274
276
|
: null;
|
|
275
277
|
}
|
|
276
278
|
|
|
@@ -279,11 +281,11 @@ export class MCPClient extends EventEmitter {
|
|
|
279
281
|
const sessionId = options.sessionId;
|
|
280
282
|
if (sessionId != null && sessionId !== "") {
|
|
281
283
|
const key = String(sessionId);
|
|
282
|
-
if (typeof handler === "function")
|
|
284
|
+
if (typeof handler === "function")
|
|
285
|
+
this._elicitationHandlers.set(key, handler);
|
|
283
286
|
else this._elicitationHandlers.delete(key);
|
|
284
287
|
} else {
|
|
285
|
-
this._elicitationHandler =
|
|
286
|
-
typeof handler === "function" ? handler : null;
|
|
288
|
+
this._elicitationHandler = typeof handler === "function" ? handler : null;
|
|
287
289
|
}
|
|
288
290
|
if (Number(options.timeoutMs) > 0) {
|
|
289
291
|
this._elicitationTimeoutMs = Number(options.timeoutMs);
|
|
@@ -389,8 +391,9 @@ export class MCPClient extends EventEmitter {
|
|
|
389
391
|
requestId,
|
|
390
392
|
params,
|
|
391
393
|
);
|
|
392
|
-
const negotiatedVersion =
|
|
393
|
-
|
|
394
|
+
const negotiatedVersion = this.servers.get(
|
|
395
|
+
String(serverName),
|
|
396
|
+
)?.protocolVersion;
|
|
394
397
|
if (
|
|
395
398
|
request.mode === "url" &&
|
|
396
399
|
negotiatedVersion &&
|
|
@@ -431,10 +434,7 @@ export class MCPClient extends EventEmitter {
|
|
|
431
434
|
direct !== undefined &&
|
|
432
435
|
String(direct?.action || "").toLowerCase() !== "defer"
|
|
433
436
|
) {
|
|
434
|
-
const response = this._normalizeElicitationResponse(
|
|
435
|
-
direct,
|
|
436
|
-
request,
|
|
437
|
-
);
|
|
437
|
+
const response = this._normalizeElicitationResponse(direct, request);
|
|
438
438
|
this._settleUrlElicitation(request, response);
|
|
439
439
|
try {
|
|
440
440
|
this._runtimeProducer?.store.acknowledgeInbox(
|
|
@@ -504,7 +504,8 @@ export class MCPClient extends EventEmitter {
|
|
|
504
504
|
});
|
|
505
505
|
this.emit("elicitation-request", {
|
|
506
506
|
...request,
|
|
507
|
-
respond: (response) =>
|
|
507
|
+
respond: (response) =>
|
|
508
|
+
this.respondElicitation(serverName, requestId, response),
|
|
508
509
|
cancel: () => this.cancelElicitation(serverName, requestId),
|
|
509
510
|
});
|
|
510
511
|
});
|
|
@@ -515,9 +516,7 @@ export class MCPClient extends EventEmitter {
|
|
|
515
516
|
if (!id) return;
|
|
516
517
|
const existing = this._urlElicitations.get(id);
|
|
517
518
|
if (existing && existing.server !== request.server) {
|
|
518
|
-
throw new Error(
|
|
519
|
-
`MCP URL elicitation id collision across servers: ${id}`,
|
|
520
|
-
);
|
|
519
|
+
throw new Error(`MCP URL elicitation id collision across servers: ${id}`);
|
|
521
520
|
}
|
|
522
521
|
if (existing && existing.request?.url !== request.url) {
|
|
523
522
|
throw new Error(
|
|
@@ -544,7 +543,9 @@ export class MCPClient extends EventEmitter {
|
|
|
544
543
|
const entry = this._urlElicitations.get(request.elicitationId);
|
|
545
544
|
if (!entry || entry.status === "completed") return;
|
|
546
545
|
entry.status =
|
|
547
|
-
response?.action === "accept"
|
|
546
|
+
response?.action === "accept"
|
|
547
|
+
? "accepted"
|
|
548
|
+
: response?.action || "decline";
|
|
548
549
|
if (entry.status === "accepted" && entry.completionNotified) {
|
|
549
550
|
this._completeUrlElicitation(entry);
|
|
550
551
|
}
|
|
@@ -757,7 +758,26 @@ export class MCPClient extends EventEmitter {
|
|
|
757
758
|
`stdio transport requires a command (server "${name}")`,
|
|
758
759
|
);
|
|
759
760
|
}
|
|
760
|
-
const
|
|
761
|
+
const isPlugin = config.origin === "plugin:mcp";
|
|
762
|
+
const pluginWorkspaceRoot = isPlugin
|
|
763
|
+
? resolvePluginWorkspaceAuthority(config.pluginWorkspaceAuthority, {
|
|
764
|
+
origin: config.origin,
|
|
765
|
+
pluginId: config.pluginId,
|
|
766
|
+
pluginVersion: config.pluginVersion,
|
|
767
|
+
pluginSource: config.pluginSource,
|
|
768
|
+
})
|
|
769
|
+
: null;
|
|
770
|
+
if (
|
|
771
|
+
isPlugin &&
|
|
772
|
+
config.sandboxPolicy?.requiredBoundaries?.length > 0 &&
|
|
773
|
+
!pluginWorkspaceRoot
|
|
774
|
+
) {
|
|
775
|
+
throw new Error(
|
|
776
|
+
`plugin MCP server "${name}" is missing its trusted workspace authority`,
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
const spawnOptions = {
|
|
780
|
+
cwd: pluginWorkspaceRoot || process.cwd(),
|
|
761
781
|
stdio: ["pipe", "pipe", "pipe"],
|
|
762
782
|
// process.env < agent identity (CLAUDECODE / session id) < the
|
|
763
783
|
// server's own config.env, so an explicit per-server override wins.
|
|
@@ -776,6 +796,19 @@ export class MCPClient extends EventEmitter {
|
|
|
776
796
|
...(config.sandboxPolicy
|
|
777
797
|
? { sandboxPolicy: config.sandboxPolicy }
|
|
778
798
|
: {}),
|
|
799
|
+
};
|
|
800
|
+
const sandboxExecutionContract =
|
|
801
|
+
!isPlugin || pluginWorkspaceRoot
|
|
802
|
+
? executionBroker.issueLinuxWorkspaceSandboxExecutionContract(
|
|
803
|
+
config.command,
|
|
804
|
+
config.args || [],
|
|
805
|
+
spawnOptions,
|
|
806
|
+
pluginWorkspaceRoot || process.cwd(),
|
|
807
|
+
)
|
|
808
|
+
: null;
|
|
809
|
+
const proc = _deps.spawn(config.command, config.args || [], {
|
|
810
|
+
...spawnOptions,
|
|
811
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
779
812
|
});
|
|
780
813
|
|
|
781
814
|
entry.process = proc;
|
|
@@ -1113,7 +1146,8 @@ export class MCPClient extends EventEmitter {
|
|
|
1113
1146
|
} catch {
|
|
1114
1147
|
return false;
|
|
1115
1148
|
}
|
|
1116
|
-
if (request.mode !== "url" || ids.has(request.elicitationId))
|
|
1149
|
+
if (request.mode !== "url" || ids.has(request.elicitationId))
|
|
1150
|
+
return false;
|
|
1117
1151
|
ids.add(request.elicitationId);
|
|
1118
1152
|
normalized.push(request);
|
|
1119
1153
|
}
|
|
@@ -1392,10 +1426,7 @@ export class MCPClient extends EventEmitter {
|
|
|
1392
1426
|
entry._httpMessageStream = stream;
|
|
1393
1427
|
stream.promise = (async () => {
|
|
1394
1428
|
try {
|
|
1395
|
-
while (
|
|
1396
|
-
!stream.stopped &&
|
|
1397
|
-
this.servers.get(serverName) === entry
|
|
1398
|
-
) {
|
|
1429
|
+
while (!stream.stopped && this.servers.get(serverName) === entry) {
|
|
1399
1430
|
stream.controller =
|
|
1400
1431
|
typeof AbortController === "function"
|
|
1401
1432
|
? new AbortController()
|
|
@@ -1427,9 +1458,7 @@ export class MCPClient extends EventEmitter {
|
|
|
1427
1458
|
);
|
|
1428
1459
|
}
|
|
1429
1460
|
const contentType = response.headers?.get
|
|
1430
|
-
? String(
|
|
1431
|
-
response.headers.get("content-type") || "",
|
|
1432
|
-
).toLowerCase()
|
|
1461
|
+
? String(response.headers.get("content-type") || "").toLowerCase()
|
|
1433
1462
|
: "";
|
|
1434
1463
|
if (!contentType.includes("text/event-stream")) return;
|
|
1435
1464
|
const cap = Number.isFinite(entry.config?.maxBufferChars)
|
|
@@ -1438,8 +1467,7 @@ export class MCPClient extends EventEmitter {
|
|
|
1438
1467
|
await _consumeSseMessageStream(response, {
|
|
1439
1468
|
cap,
|
|
1440
1469
|
stream,
|
|
1441
|
-
onMessage: (message) =>
|
|
1442
|
-
this._handleMessage(serverName, message),
|
|
1470
|
+
onMessage: (message) => this._handleMessage(serverName, message),
|
|
1443
1471
|
});
|
|
1444
1472
|
} catch (error) {
|
|
1445
1473
|
if (stream.stopped || stream.controller?.signal.aborted) return;
|
|
@@ -1564,11 +1592,8 @@ export class MCPClient extends EventEmitter {
|
|
|
1564
1592
|
|
|
1565
1593
|
let envelope;
|
|
1566
1594
|
if (contentType.includes("text/event-stream")) {
|
|
1567
|
-
envelope = await _extractSseResponse(
|
|
1568
|
-
|
|
1569
|
-
id,
|
|
1570
|
-
cap,
|
|
1571
|
-
(message) => this._handleMessage(serverName, message),
|
|
1595
|
+
envelope = await _extractSseResponse(response, id, cap, (message) =>
|
|
1596
|
+
this._handleMessage(serverName, message),
|
|
1572
1597
|
);
|
|
1573
1598
|
} else {
|
|
1574
1599
|
const text = await _readBodyCapped(response, cap);
|
|
@@ -1813,8 +1838,7 @@ export class MCPClient extends EventEmitter {
|
|
|
1813
1838
|
...(entry.httpSessionId
|
|
1814
1839
|
? { "Mcp-Session-Id": entry.httpSessionId }
|
|
1815
1840
|
: {}),
|
|
1816
|
-
"MCP-Protocol-Version":
|
|
1817
|
-
entry.protocolVersion || this._protocolVersion,
|
|
1841
|
+
"MCP-Protocol-Version": entry.protocolVersion || this._protocolVersion,
|
|
1818
1842
|
};
|
|
1819
1843
|
try {
|
|
1820
1844
|
const pending = _deps.fetch(entry.httpUrl, {
|
|
@@ -1986,9 +2010,7 @@ async function _consumeSseMessageStream(
|
|
|
1986
2010
|
if (done) break;
|
|
1987
2011
|
buffer += decoder.decode(value, { stream: true });
|
|
1988
2012
|
if (cap > 0 && buffer.length > cap) {
|
|
1989
|
-
throw new Error(
|
|
1990
|
-
`MCP HTTP SSE event exceeded the ${cap}-char cap`,
|
|
1991
|
-
);
|
|
2013
|
+
throw new Error(`MCP HTTP SSE event exceeded the ${cap}-char cap`);
|
|
1992
2014
|
}
|
|
1993
2015
|
let boundary;
|
|
1994
2016
|
while ((boundary = buffer.search(/\r?\n\r?\n/)) >= 0) {
|
package/src/lib/agent-batch.js
CHANGED
|
@@ -94,7 +94,10 @@ export async function runBatch(opts, deps) {
|
|
|
94
94
|
try {
|
|
95
95
|
worktreePath = createWorktree(unit.key, branch);
|
|
96
96
|
onEvent({ type: "unit:start", key: unit.key, branch });
|
|
97
|
-
await runAgent(unit.prompt, worktreePath
|
|
97
|
+
await runAgent(unit.prompt, worktreePath, {
|
|
98
|
+
key: unit.key,
|
|
99
|
+
branch,
|
|
100
|
+
});
|
|
98
101
|
|
|
99
102
|
const stat = diffStat(worktreePath);
|
|
100
103
|
record.filesChanged = stat.filesChanged;
|
|
@@ -126,6 +126,8 @@ export class TeamWorktreeCoordinator {
|
|
|
126
126
|
sparsePaths: options.sparsePaths ?? null,
|
|
127
127
|
symlinkDirectories: options.symlinkDirectories ?? null,
|
|
128
128
|
};
|
|
129
|
+
this._onWorktree =
|
|
130
|
+
typeof options.onWorktree === "function" ? options.onWorktree : null;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
isGitRepo() {
|
|
@@ -173,6 +175,7 @@ export class TeamWorktreeCoordinator {
|
|
|
173
175
|
wtOptions,
|
|
174
176
|
);
|
|
175
177
|
this._created.set(key, { branch, path: worktreePath, committed: false });
|
|
178
|
+
this._onWorktree?.({ key, branch, path: worktreePath, holder });
|
|
176
179
|
if (typeof runInWorktree === "function") {
|
|
177
180
|
await runInWorktree({ key, task, holder, cwd: worktreePath });
|
|
178
181
|
} else {
|