chainlesschain 0.162.182 → 0.162.184
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-DzZ5_xoj.js → AIOps-DzAl5fGx.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-DeLXddIg.js → ActionButton-DAHtMGeC.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-rvDCmet1.js → Analytics-BHQaexv5.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-Yt6QVDnp.js → AppLayout-Phz_pD0n.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BS112K5L.js → Artifacts-DHt47kCA.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-0wJuQrNu.js → Audit--Y_8FULl.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-B90PMgbn.js → BackgroundAgents-N1zchH7D.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-TkgMglQQ.js → Backup-B_BoyN30.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CQVS9p1l.js → BaseInput-Gl4uQHcT.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-YTjQHoC7.js → Chat-Cg-eIyAZ.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-BzeUi3JH.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-5bwaOabq.js → Checkbox-D0MGR2wA.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-BFmNQ_Xe.js → Codegen-BdObJEFn.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BT_u4Dzp.js → Col--ghxXT3z.js} +1 -1
- package/src/assets/web-panel/assets/{Community-CJP52u5b.js → Community-BUFeXOqz.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-BpzhJE9i.js → Compact-DhQO2_Mr.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Cg7rfslp.js → Compliance-3HoTqKYd.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-JisxL6Yy.js → Cowork-B3zmO4VW.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-Bu7Ug_MC.js → Cron-CJDrZOcc.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-CvuqoLdj.js → Crosschain-SMl15obd.js} +1 -1
- package/src/assets/web-panel/assets/{DID-C2u9G8Pu.js → DID-CgTrTWrl.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-BFt5Yv3d.js → Dashboard-dXFKaKol.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-DIaT9Wxq.js → Dropdown-qQHEhCvI.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D_A601Y4.js → EmailListRenderer-C_ej0MWH.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard--IyQ8I8w.js → FamilyGuardDashboard-D6ajoE7I.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BihWHxuY.js → Federation-Cio2y49h.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BB8dOXc_.js → FormItemContext-ClJz1cYp.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-DQYhj1HH.js → GenericCardRenderer-Dr_8Maca.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B-2QZxGf.js → Git-CevLwp5u.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CusSb-nG.js → Governance-CW9YyQUY.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-BAjmvPPL.js → Inference-CQqeAyER.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DoNYWKYq.js → KnowledgeGraph-cuGVxHrw.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-B8fKWD-n.js → Logs-DIuHri5d.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-BlJm1J7E.js → MarkdownRenderer-R91Qw5JA.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-CyOscbSL.js → Marketplace-CY1VhFGx.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Ex6Om5da.js → McpTools-BBAbi1vt.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-CDcD8l8r.js → Memory-BWYGHxJA.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-xfBqoFDg.js → MobileBridge-CPIFkW7A.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BwObMMA2.js → MobileProjects-DQyWxCho.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CIWd36OV.js → Mtc-Bo3GnQbx.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CPX0ouuA.js → MtcAudit-C8-c4rSW.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BDJsIUuW.js → Multisig-ChqMTtoq.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-DEkbuD3N.js → NLProgramming-C48TyhvI.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-DJpHRhH1.js → Notes-zogUFWxp.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C1UZ-Qd5.js → NotificationSettings-v-K9XpSB.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-P94JGGNz.js +1 -0
- package/src/assets/web-panel/assets/{Organization-Bbhj0fac.js → Organization-Boi1dQfR.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-1H04x3kx.js → Overflow-DM5YWwgA.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CDpPe3et.js → P2P-C57LKpKK.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-MUX0Bpvw.js → PdhVaultBrowser-BFkPqu6Q.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-A07-0Ci1.js → Permissions-D6fB9YSp.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-DhhDWxAZ.js → PersonalDataHub-Bp7EefEo.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-CyfbJfe-.js → Pipeline-BVln3hZf.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-CNFps3ED.js → Privacy-Bf8P3nF6.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit--T-u3Dr5.js → ProjectInit-DM6wA2E2.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DJmrK2fC.js → ProjectSettings-CGbc5Iho.js} +2 -2
- package/src/assets/web-panel/assets/Projects-D0p9EOxm.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DLzzbOZd.js → Providers-B2kJcxS6.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-CexOomKS.js → QrScannerModal-Cxu7dXYy.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-QklGi19_.js → QuickAsk-DTlrTPms.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Bb9kwSw8.js → Recommend-Cm3X-cCj.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DZuVNNN5.js → RemoteSession-DNb2kjvt.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-Dr5cLP0N.js → Reputation-CZUFFjc1.js} +1 -1
- package/src/assets/web-panel/assets/{Row-oCQHYv5t.js → Row-DAPgloxA.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-IFDEykIR.js → RssFeed-dsx9S793.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CspVIxDc.js → Search-DnnC8kz-.js} +1 -1
- package/src/assets/web-panel/assets/{Security-Bm66o_DH.js → Security-VuZknRMK.js} +4 -4
- package/src/assets/web-panel/assets/{Services-DvanNqXl.js → Services-B3XNX8Vx.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CHjEe9_O.js → Skeleton-Dl1nVtCj.js} +1 -1
- package/src/assets/web-panel/assets/Skills-CY09CZ_W.js +1 -0
- package/src/assets/web-panel/assets/{Sla-DxZm1491.js → Sla-HzU56n1z.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DWxEEclC.js → SpeechSettings-CRle7Jl2.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-FIvOFw5p.js → SyncSettings-DWadz7Sl.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CNVTDGg8.js +1 -0
- package/src/assets/web-panel/assets/{Templates-CaNjzV_t.js → Templates-BBAgvrlM.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DjlP6TmH.js → Tenant-BsbbvGN8.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-uscpZI4q.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-DoZ8y7y8.js → TimelineRenderer-mTyjNE6r.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-390cg2TC.js → Tokens-Bz97oBgs.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CLrtCj5_.js → Trigger-BTRKC140.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DpESfSPn.js → Trust-B4A_KuyX.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-CN6279O0.js → UkeySign-DES9TmLU.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-DPtAUlSe.js → VideoEditing-ClXs7Qxp.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-nKOMV_1n.js → Wallet-IzZqFz-7.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-HuXlSOc4.js → WebAuthn-BtOP04g_.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-CUfFjARi.js → WorkflowEditor-_0cqnopY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-6Eq0PcZJ.js → chat-ax7193uU.js} +1 -1
- package/src/assets/web-panel/assets/{colors-BP_Sf6Iy.js → colors-Bu4WGJah.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-BufMKIh5.js → compact-item-BX1dlTW0.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-BBMXxWSW.js → createContext-CktA_Dj7.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-x877iKKS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-fUK_tRO2.js → hasIn-D5wd817l.js} +1 -1
- package/src/assets/web-panel/assets/{index-t8N1tSeX.js → index-5PYYkr8S.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLpIs_P_.js → index-A4uBPQCz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DVsqvI4M.js → index-BAtBNfkr.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bkg4ZePE.js → index-BLLsUNDc.js} +1 -1
- package/src/assets/web-panel/assets/{index-cpEli44X.js → index-BQtoRyTO.js} +1 -1
- package/src/assets/web-panel/assets/{index-wKTLbKyc.js → index-BRfXoRuh.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7Etk0Te.js → index-Bad2CYeH.js} +1 -1
- package/src/assets/web-panel/assets/{index-CwSf5pnf.js → index-BcGdL3sG.js} +1 -1
- package/src/assets/web-panel/assets/{index-3FuwBewR.js → index-C3HmzCLt.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHy6yNiF.js → index-C8DkWyNr.js} +1 -1
- package/src/assets/web-panel/assets/{index-CgiECHu2.js → index-C8tLR2TG.js} +1 -1
- package/src/assets/web-panel/assets/{index-C44atv4k.js → index-CP8ELJBc.js} +1 -1
- package/src/assets/web-panel/assets/{index-CW_G-MXw.js → index-CUForzsG.js} +1 -1
- package/src/assets/web-panel/assets/{index-pum7HYBA.js → index-CYRE0NJA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BWKoBU8R.js → index-CeVZaJSC.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4_MuGkd.js → index-CfzxR61U.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dyngbd29.js → index-Ci3y1ZAv.js} +19 -19
- package/src/assets/web-panel/assets/{index-fzaMtSJ8.js → index-CjEJjl5C.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cp8vhyZy.js → index-Cv7bVWJ5.js} +1 -1
- package/src/assets/web-panel/assets/{index-DPdUNk1i.js → index-D-o4Rwjk.js} +1 -1
- package/src/assets/web-panel/assets/index-D7pfPouB.js +1 -0
- package/src/assets/web-panel/assets/{index-CeASi4fr.js → index-D9op8poE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DASluSqY.js → index-DF1TysIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-DocdRcbd.js → index-DYo9S5OT.js} +1 -1
- package/src/assets/web-panel/assets/{index-2A5ph8b2.js → index-D_cycOET.js} +1 -1
- package/src/assets/web-panel/assets/{index-wV6iGdpY.js → index-Db3CpWTr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BMLHv389.js → index-DkD1K-fF.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9WYSn4m.js → index-DmPJ6T6F.js} +1 -1
- package/src/assets/web-panel/assets/{index-uCbEN7cY.js → index-DmVUeWUk.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmkMXwnR.js → index-Dzdr0zDf.js} +1 -1
- package/src/assets/web-panel/assets/{index-CZ6Yt17L.js → index-PBLzwYnu.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2DdTP2.js → index-bFCaM7Eq.js} +1 -1
- package/src/assets/web-panel/assets/index-e4i-VLBd.js +1 -0
- package/src/assets/web-panel/assets/{index-GwdRUZ-x.js → index-h3KJZr95.js} +1 -1
- package/src/assets/web-panel/assets/{index-B2vS66eI.js → index-iHcPtQ2e.js} +1 -1
- package/src/assets/web-panel/assets/{index-6GmBa4Kg.js → index-jzlzm4VK.js} +1 -1
- package/src/assets/web-panel/assets/{index-D9vU25ij.js → index-o14NNnxw.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGjmILDm.js → index-tYPSfGbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-LvCBfTLo.js → index-ytp2H5Zx.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-V5Fo95FM.js → initDefaultProps-D2Iut83B.js} +1 -1
- package/src/assets/web-panel/assets/{motion-BYWU0hUT.js → motion-BuaJVdK9.js} +1 -1
- package/src/assets/web-panel/assets/{move-Cr1M-J2h.js → move-CaEuM92y.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BysQKi2N.js → mtc-parser-wuKNBwys.js} +1 -1
- package/src/assets/web-panel/assets/{omit-D1zHU8GC.js → omit-7a3eklaq.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CUArxtfi.js → pickAttrs-Dn4z7KvJ.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ChoYEys0.js → placementArrow-DVn_qT4-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BQRs3Mo7.js → responsiveObserve-Dodz1OMQ.js} +1 -1
- package/src/assets/web-panel/assets/{slide-B8zbsCkg.js → slide-CxoGskbp.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-C2U8Gs6h.js → statusUtils-DBh_epQH.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Dd8MgL_V.js → styleChecker-RXQ4gG5P.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-hknqY5NH.js → useFlexGapSupport-1Wxm0HEG.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-CLTAmcfw.js → useFs-CI_ySNxJ.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-ByegHbiB.js → usePersonalDataHub-C6973rZP.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DqlAy5DP.js → vnode-CalynwKr.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DiXiztQn.js → zoom-CM1cd-Q0.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +74 -1
- package/src/commands/agent.js +9 -0
- package/src/commands/background-session.js +60 -1
- package/src/commands/batch.js +160 -5
- package/src/commands/plugin.js +318 -43
- package/src/commands/session.js +44 -1
- package/src/commands/team.js +212 -6
- package/src/data/changelog.json +26 -0
- package/src/harness/jsonl-session-store.js +59 -6
- package/src/harness/mcp-client.js +65 -43
- package/src/lib/agent-batch.js +4 -1
- package/src/lib/agent-team/team-runner.js +19 -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 +49 -0
- package/src/lib/collaboration-run-store.js +362 -0
- package/src/lib/hook-runner.cjs +98 -17
- package/src/lib/hook-runner.js +77 -6
- package/src/lib/hook-shell-command.cjs +148 -0
- package/src/lib/hooks-v2-runtime.js +188 -77
- package/src/lib/jsonl-session-store.js +2 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +18 -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 +1 -1
- package/src/lib/plugin-runtime/install.js +465 -54
- 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 +99 -0
- package/src/lib/plugin-runtime/scopes.js +13 -2
- package/src/lib/plugin-usage-attribution.js +44 -0
- package/src/lib/process-execution-broker/index.js +705 -13
- package/src/lib/process-execution-broker/linux-generic-bwrap-runtime.js +1347 -0
- package/src/lib/process-execution-broker/linux-generic-bwrap.js +1260 -0
- package/src/lib/process-execution-broker/platform-sandbox.js +1831 -157
- package/src/lib/session-usage.js +278 -1
- package/src/lib/stream-retry.js +29 -0
- package/src/repl/agent-repl.js +38 -9
- package/src/runtime/agent-core.js +32 -4
- package/src/runtime/headless-runner.js +46 -1
- package/src/runtime/headless-stream.js +54 -3
- package/src/runtime/mcp-config.js +15 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-GlJtqLjT.js +0 -1
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +0 -1
- package/src/assets/web-panel/assets/Skills-Cb8jWjMX.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +0 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +0 -3
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +0 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +0 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +0 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
|
|
5
|
+
const STRONG_HOOK_BOUNDARIES = new Set(["filesystem", "network"]);
|
|
6
|
+
|
|
7
|
+
function requiredHookBoundaries(sandboxPolicy) {
|
|
8
|
+
const configured = sandboxPolicy?.requiredBoundaries;
|
|
9
|
+
return Array.isArray(configured) ? configured : [];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function requiresExplicitHookShell(sandboxPolicy) {
|
|
13
|
+
return requiredHookBoundaries(sandboxPolicy).some((boundary) =>
|
|
14
|
+
STRONG_HOOK_BOUNDARIES.has(boundary),
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sandboxBoundaryError(message) {
|
|
19
|
+
const error = new Error(message);
|
|
20
|
+
error.code = "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
|
|
21
|
+
error.sandboxFailClosed = true;
|
|
22
|
+
return error;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function asSandboxBoundaryError(error, prefix) {
|
|
26
|
+
if (error?.code === "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED") {
|
|
27
|
+
return error;
|
|
28
|
+
}
|
|
29
|
+
const detail =
|
|
30
|
+
error && typeof error.message === "string" ? error.message : String(error);
|
|
31
|
+
return sandboxBoundaryError(`${prefix}: ${detail}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function issueTrustedHookSandboxContract({
|
|
35
|
+
issuer,
|
|
36
|
+
receiver = null,
|
|
37
|
+
file,
|
|
38
|
+
args,
|
|
39
|
+
options,
|
|
40
|
+
trustedRoot,
|
|
41
|
+
sync = false,
|
|
42
|
+
label = "trusted hook sandbox contract issuance failed",
|
|
43
|
+
}) {
|
|
44
|
+
if (typeof issuer !== "function") return null;
|
|
45
|
+
try {
|
|
46
|
+
return issuer.call(
|
|
47
|
+
receiver,
|
|
48
|
+
file,
|
|
49
|
+
args,
|
|
50
|
+
options,
|
|
51
|
+
trustedRoot,
|
|
52
|
+
...(sync ? [{ sync: true }] : []),
|
|
53
|
+
);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw asSandboxBoundaryError(error, label);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function requireTrustedHookRoot(value, label = "hook working directory") {
|
|
60
|
+
if (
|
|
61
|
+
typeof value !== "string" ||
|
|
62
|
+
!value ||
|
|
63
|
+
value.includes("\0") ||
|
|
64
|
+
!path.isAbsolute(value)
|
|
65
|
+
) {
|
|
66
|
+
throw sandboxBoundaryError(
|
|
67
|
+
`${label} must be an absolute trusted path for filesystem/network sandboxing`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return path.resolve(value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isPathInside(root, candidate) {
|
|
74
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
75
|
+
return (
|
|
76
|
+
relative === "" ||
|
|
77
|
+
(relative !== ".." &&
|
|
78
|
+
!relative.startsWith(`..${path.sep}`) &&
|
|
79
|
+
!path.isAbsolute(relative))
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function quotePosixShellArgument(value) {
|
|
84
|
+
const text = String(value);
|
|
85
|
+
if (text.length === 0) return "''";
|
|
86
|
+
return `'${text.replace(/'/g, `'"'"'`)}'`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function quoteCmdArgument(value) {
|
|
90
|
+
const text = String(value);
|
|
91
|
+
if (text.length === 0) return '""';
|
|
92
|
+
// The command and its arguments are all hook-controlled, but quote cmd.exe
|
|
93
|
+
// metacharacters anyway so an argv entry remains one argument.
|
|
94
|
+
return `"${text
|
|
95
|
+
.replace(/"/g, '""')
|
|
96
|
+
.replace(/([&|<>^])/g, "^$1")
|
|
97
|
+
.replace(/%/g, "%%")}"`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function appendShellArguments(command, args, platform) {
|
|
101
|
+
if (!Array.isArray(args) || args.length === 0) return command;
|
|
102
|
+
const quote =
|
|
103
|
+
platform === "win32" ? quoteCmdArgument : quotePosixShellArgument;
|
|
104
|
+
return `${command} ${args.map((arg) => quote(arg)).join(" ")}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Turn a settings-style shell command into an explicit executable + argv.
|
|
109
|
+
* This is used only when filesystem/network boundaries are requested. Keeping
|
|
110
|
+
* shell selection explicit lets the Broker bind its one-shot contract to the
|
|
111
|
+
* actual launch tuple and guarantees the child spawn itself uses shell:false.
|
|
112
|
+
*/
|
|
113
|
+
function buildExplicitHookShellInvocation(
|
|
114
|
+
command,
|
|
115
|
+
{
|
|
116
|
+
args = [],
|
|
117
|
+
platform = process.platform,
|
|
118
|
+
shellKind = null,
|
|
119
|
+
buildPowershellArgv = null,
|
|
120
|
+
powershellOptions = {},
|
|
121
|
+
} = {},
|
|
122
|
+
) {
|
|
123
|
+
const rendered = appendShellArguments(String(command), args, platform);
|
|
124
|
+
if (
|
|
125
|
+
(shellKind === "powershell" || shellKind === "pwsh") &&
|
|
126
|
+
typeof buildPowershellArgv === "function"
|
|
127
|
+
) {
|
|
128
|
+
return buildPowershellArgv(rendered, shellKind, powershellOptions);
|
|
129
|
+
}
|
|
130
|
+
if (platform === "win32") {
|
|
131
|
+
return {
|
|
132
|
+
file: process.env.ComSpec || "cmd.exe",
|
|
133
|
+
argv: ["/d", "/s", "/c", rendered],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return { file: "/bin/sh", argv: ["-c", rendered] };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = {
|
|
140
|
+
buildExplicitHookShellInvocation,
|
|
141
|
+
issueTrustedHookSandboxContract,
|
|
142
|
+
isPathInside,
|
|
143
|
+
requireTrustedHookRoot,
|
|
144
|
+
requiredHookBoundaries,
|
|
145
|
+
requiresExplicitHookShell,
|
|
146
|
+
asSandboxBoundaryError,
|
|
147
|
+
sandboxBoundaryError,
|
|
148
|
+
};
|
|
@@ -26,35 +26,72 @@ import fs from "node:fs/promises";
|
|
|
26
26
|
import path from "node:path";
|
|
27
27
|
import broker from "./process-execution-broker/index.js";
|
|
28
28
|
import hookEnvironment from "./hook-environment.cjs";
|
|
29
|
+
import hookShellCommand from "./hook-shell-command.cjs";
|
|
29
30
|
import permissionRules from "./permission-rules.cjs";
|
|
30
31
|
import { EventRuntimeStore } from "./event-runtime-store.js";
|
|
31
32
|
import { getDefaultEventRuntimeHost } from "./event-runtime-host.js";
|
|
32
33
|
|
|
33
34
|
const { globToRegExp } = permissionRules;
|
|
34
35
|
const { buildManagedHookEnvironment } = hookEnvironment;
|
|
36
|
+
const {
|
|
37
|
+
buildExplicitHookShellInvocation,
|
|
38
|
+
issueTrustedHookSandboxContract,
|
|
39
|
+
isPathInside,
|
|
40
|
+
requireTrustedHookRoot,
|
|
41
|
+
requiresExplicitHookShell,
|
|
42
|
+
sandboxBoundaryError,
|
|
43
|
+
} = hookShellCommand;
|
|
35
44
|
|
|
36
45
|
const VALID_HOOK_EVENTS = new Set([
|
|
37
46
|
"Setup",
|
|
38
47
|
// Session
|
|
39
|
-
"PreToolUse",
|
|
40
|
-
"
|
|
48
|
+
"PreToolUse",
|
|
49
|
+
"PostToolUse",
|
|
50
|
+
"Notification",
|
|
51
|
+
"Stop",
|
|
52
|
+
"SubagentStart",
|
|
53
|
+
"SubagentStop",
|
|
54
|
+
"SessionResume",
|
|
55
|
+
"SessionPause",
|
|
56
|
+
"PostCompact",
|
|
57
|
+
"StopFailure",
|
|
41
58
|
// Auth
|
|
42
|
-
"PreCommit",
|
|
59
|
+
"PreCommit",
|
|
60
|
+
"PostCommit",
|
|
43
61
|
// Skill
|
|
44
|
-
"UserPromptSubmit",
|
|
62
|
+
"UserPromptSubmit",
|
|
63
|
+
"UserPromptExpansion",
|
|
64
|
+
"SessionStart",
|
|
65
|
+
"SessionEnd",
|
|
45
66
|
// Model
|
|
46
|
-
"PreCompact",
|
|
67
|
+
"PreCompact",
|
|
68
|
+
"ModelSelection",
|
|
47
69
|
// Config
|
|
48
|
-
"ConfigChange",
|
|
70
|
+
"ConfigChange",
|
|
71
|
+
"PermissionAllow",
|
|
72
|
+
"PermissionDeny",
|
|
73
|
+
"PermissionRequest",
|
|
74
|
+
"PermissionDenied",
|
|
49
75
|
// Timeline
|
|
50
76
|
"TimelineEntry",
|
|
51
77
|
// MCP
|
|
52
|
-
"McpRequest",
|
|
78
|
+
"McpRequest",
|
|
79
|
+
"McpResponse",
|
|
80
|
+
"MCPElicitation",
|
|
81
|
+
"Elicitation",
|
|
82
|
+
"ElicitationResult",
|
|
53
83
|
// Task / workspace lifecycle
|
|
54
|
-
"TaskCreated",
|
|
55
|
-
"
|
|
84
|
+
"TaskCreated",
|
|
85
|
+
"TaskCompleted",
|
|
86
|
+
"InstructionsLoaded",
|
|
87
|
+
"CwdChanged",
|
|
88
|
+
"WorktreeCreate",
|
|
89
|
+
"WorktreeRemove",
|
|
90
|
+
"TeammateIdle",
|
|
56
91
|
// Tool aggregate / failure / filesystem lifecycle
|
|
57
|
-
"PostToolUseFailure",
|
|
92
|
+
"PostToolUseFailure",
|
|
93
|
+
"PostToolBatch",
|
|
94
|
+
"FileChanged",
|
|
58
95
|
]);
|
|
59
96
|
|
|
60
97
|
const VALID_EXECUTOR_TYPES = new Set([
|
|
@@ -137,7 +174,9 @@ function strictestDecision(results) {
|
|
|
137
174
|
|
|
138
175
|
function hostnameMatches(hostname, pattern) {
|
|
139
176
|
const host = String(hostname || "").toLowerCase();
|
|
140
|
-
const rule = String(pattern || "")
|
|
177
|
+
const rule = String(pattern || "")
|
|
178
|
+
.trim()
|
|
179
|
+
.toLowerCase();
|
|
141
180
|
if (!host || !rule) return false;
|
|
142
181
|
if (rule.startsWith("*.")) {
|
|
143
182
|
const suffix = rule.slice(1);
|
|
@@ -259,10 +298,7 @@ export function assertManagedHookPolicy(
|
|
|
259
298
|
);
|
|
260
299
|
}
|
|
261
300
|
const executable = String(hook.command || "");
|
|
262
|
-
const commandCandidates = new Set([
|
|
263
|
-
executable,
|
|
264
|
-
path.basename(executable),
|
|
265
|
-
]);
|
|
301
|
+
const commandCandidates = new Set([executable, path.basename(executable)]);
|
|
266
302
|
if (
|
|
267
303
|
Array.isArray(policy.commandAllowlist) &&
|
|
268
304
|
![...commandCandidates].some((value) =>
|
|
@@ -315,7 +351,11 @@ export function fileChangedHookMatches(hook, context = {}) {
|
|
|
315
351
|
hook?.globs ?? hook?.paths ?? hook?.glob ?? hook?.if ?? null;
|
|
316
352
|
if (configured == null) return true;
|
|
317
353
|
const patterns = (Array.isArray(configured) ? configured : [configured])
|
|
318
|
-
.map((value) =>
|
|
354
|
+
.map((value) =>
|
|
355
|
+
String(value || "")
|
|
356
|
+
.trim()
|
|
357
|
+
.replace(/\\/g, "/"),
|
|
358
|
+
)
|
|
319
359
|
.filter(Boolean);
|
|
320
360
|
if (patterns.length === 0) return true;
|
|
321
361
|
|
|
@@ -392,6 +432,10 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
392
432
|
constructor(configDir, options = {}) {
|
|
393
433
|
super();
|
|
394
434
|
this.configDir = configDir;
|
|
435
|
+
// Both values are host constructor inputs. Hook definitions may select a
|
|
436
|
+
// cwd inside this root, but can never replace the writable root used to
|
|
437
|
+
// issue a Linux one-shot execution contract.
|
|
438
|
+
this.workspaceRoot = options.workspaceRoot || configDir || null;
|
|
395
439
|
this.durableStore = options.durableStore || null;
|
|
396
440
|
this.durableOwner =
|
|
397
441
|
options.durableOwner ||
|
|
@@ -457,14 +501,18 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
457
501
|
const config = JSON.parse(content);
|
|
458
502
|
this.hooks.clear();
|
|
459
503
|
|
|
460
|
-
const hookDefs = Array.isArray(config) ? config :
|
|
504
|
+
const hookDefs = Array.isArray(config) ? config : config.hooks || [];
|
|
461
505
|
for (const def of hookDefs) {
|
|
462
506
|
if (!VALID_HOOK_EVENTS.has(def.event)) {
|
|
463
|
-
process.emitWarning(
|
|
507
|
+
process.emitWarning(
|
|
508
|
+
`[hooks-v2] Unknown event type: ${def.event}, skipping`,
|
|
509
|
+
);
|
|
464
510
|
continue;
|
|
465
511
|
}
|
|
466
512
|
if (!VALID_EXECUTOR_TYPES.has(def.type)) {
|
|
467
|
-
process.emitWarning(
|
|
513
|
+
process.emitWarning(
|
|
514
|
+
`[hooks-v2] Unknown executor type: ${def.type} for ${def.event}, skipping`,
|
|
515
|
+
);
|
|
468
516
|
continue;
|
|
469
517
|
}
|
|
470
518
|
const id = def.id || crypto.randomUUID();
|
|
@@ -472,7 +520,10 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
472
520
|
this.hooks.get(def.event).push({ id, ...def });
|
|
473
521
|
}
|
|
474
522
|
this._loaded = true;
|
|
475
|
-
this.emit("loaded", {
|
|
523
|
+
this.emit("loaded", {
|
|
524
|
+
count: hookDefs.length,
|
|
525
|
+
events: Array.from(this.hooks.keys()),
|
|
526
|
+
});
|
|
476
527
|
} catch (e) {
|
|
477
528
|
if (e.code !== "ENOENT") {
|
|
478
529
|
this.emit("load:error", e);
|
|
@@ -484,8 +535,10 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
484
535
|
* Register a hook programmatically
|
|
485
536
|
*/
|
|
486
537
|
registerHook(def) {
|
|
487
|
-
if (!VALID_HOOK_EVENTS.has(def.event))
|
|
488
|
-
|
|
538
|
+
if (!VALID_HOOK_EVENTS.has(def.event))
|
|
539
|
+
throw new Error(`Invalid event: ${def.event}`);
|
|
540
|
+
if (!VALID_EXECUTOR_TYPES.has(def.type))
|
|
541
|
+
throw new Error(`Invalid executor type: ${def.type}`);
|
|
489
542
|
const contract = HOOK_EVENT_CONTRACTS[def.event];
|
|
490
543
|
if (!contract.allowedExecutors.includes(def.type)) {
|
|
491
544
|
throw new Error(
|
|
@@ -501,7 +554,7 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
501
554
|
|
|
502
555
|
unregisterHook(id) {
|
|
503
556
|
for (const [event, hooks] of this.hooks.entries()) {
|
|
504
|
-
const idx = hooks.findIndex(h => h.id === id);
|
|
557
|
+
const idx = hooks.findIndex((h) => h.id === id);
|
|
505
558
|
if (idx >= 0) {
|
|
506
559
|
hooks.splice(idx, 1);
|
|
507
560
|
return true;
|
|
@@ -536,20 +589,20 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
536
589
|
);
|
|
537
590
|
const durableRecord =
|
|
538
591
|
this.durableStore && options.skipDurable !== true
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
592
|
+
? this.durableStore.enqueueInbox(
|
|
593
|
+
{
|
|
594
|
+
runtime_type: "hooks.v2",
|
|
595
|
+
requiresHandler: true,
|
|
596
|
+
event: eventName,
|
|
597
|
+
context,
|
|
598
|
+
},
|
|
599
|
+
{
|
|
600
|
+
id: context.event_id || context.eventId || null,
|
|
601
|
+
claimOwner: this.durableOwner,
|
|
602
|
+
leaseMs: longestHookTimeout + this.durableRecoveryBufferMs,
|
|
603
|
+
},
|
|
604
|
+
)
|
|
605
|
+
: null;
|
|
553
606
|
if (durableRecord?.duplicate) {
|
|
554
607
|
if (durableRecord.status === "done" && durableRecord.result) {
|
|
555
608
|
return { ...durableRecord.result, duplicate: true };
|
|
@@ -577,7 +630,13 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
577
630
|
const runOne = async (hook) => {
|
|
578
631
|
const execId = crypto.randomUUID();
|
|
579
632
|
const start = Date.now();
|
|
580
|
-
const record = {
|
|
633
|
+
const record = {
|
|
634
|
+
execId,
|
|
635
|
+
hookId: hook.id,
|
|
636
|
+
event: eventName,
|
|
637
|
+
type: hook.type,
|
|
638
|
+
startedAt: new Date(),
|
|
639
|
+
};
|
|
581
640
|
|
|
582
641
|
try {
|
|
583
642
|
assertManagedHookPolicy(
|
|
@@ -629,9 +688,10 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
629
688
|
this.executionLog.push(record);
|
|
630
689
|
return record;
|
|
631
690
|
};
|
|
632
|
-
const results =
|
|
633
|
-
|
|
634
|
-
|
|
691
|
+
const results =
|
|
692
|
+
options.parallel === false
|
|
693
|
+
? []
|
|
694
|
+
: await Promise.all(uniqueHooks.map(runOne));
|
|
635
695
|
if (options.parallel === false) {
|
|
636
696
|
for (const hook of uniqueHooks) results.push(await runOne(hook));
|
|
637
697
|
}
|
|
@@ -643,8 +703,7 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
643
703
|
const blocked = decision === "block";
|
|
644
704
|
const blockingResult = blocking[0]?.result || null;
|
|
645
705
|
const outcome = {
|
|
646
|
-
success:
|
|
647
|
-
!blocked && results.every((record) => record.status !== "error"),
|
|
706
|
+
success: !blocked && results.every((record) => record.status !== "error"),
|
|
648
707
|
results,
|
|
649
708
|
blocked,
|
|
650
709
|
requiresApproval: decision === "ask",
|
|
@@ -689,24 +748,72 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
689
748
|
throw new Error("command hook requires a command");
|
|
690
749
|
}
|
|
691
750
|
const budget = hookBudget(hook);
|
|
692
|
-
const sandboxPolicy = resolveHookSandboxPolicy(
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
751
|
+
const sandboxPolicy = resolveHookSandboxPolicy(hook, this.managedPolicy);
|
|
752
|
+
const requiresContract = requiresExplicitHookShell(sandboxPolicy);
|
|
753
|
+
const trustedRoot = requiresContract
|
|
754
|
+
? requireTrustedHookRoot(
|
|
755
|
+
this.workspaceRoot,
|
|
756
|
+
"Hooks v2 trusted workspace root",
|
|
757
|
+
)
|
|
758
|
+
: null;
|
|
759
|
+
const commandCwd =
|
|
760
|
+
hook.cwd || this.configDir || trustedRoot || process.cwd();
|
|
761
|
+
if (requiresContract) {
|
|
762
|
+
requireTrustedHookRoot(commandCwd, "Hooks v2 hook working directory");
|
|
763
|
+
if (!isPathInside(trustedRoot, commandCwd)) {
|
|
764
|
+
throw sandboxBoundaryError(
|
|
765
|
+
"Hooks v2 hook working directory escapes the trusted workspace root",
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
const invocation =
|
|
770
|
+
requiresContract && hook.shell === true
|
|
771
|
+
? buildExplicitHookShellInvocation(hook.command, {
|
|
772
|
+
args: hook.args || [],
|
|
773
|
+
})
|
|
774
|
+
: {
|
|
775
|
+
file: hook.command,
|
|
776
|
+
argv: hook.args || [],
|
|
777
|
+
};
|
|
778
|
+
const commandOptions = {
|
|
779
|
+
cwd: commandCwd,
|
|
780
|
+
env: buildHookEnvironment(hook, this.managedPolicy),
|
|
781
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
782
|
+
timeout: budget.timeoutMs,
|
|
783
|
+
shell: requiresContract ? false : hook.shell === true,
|
|
784
|
+
origin: "hook",
|
|
785
|
+
scope: "hook",
|
|
786
|
+
policy: "allow",
|
|
787
|
+
hookName: hook.id,
|
|
788
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
789
|
+
};
|
|
790
|
+
const sandboxExecutionContract = requiresContract
|
|
791
|
+
? issueTrustedHookSandboxContract({
|
|
792
|
+
issuer:
|
|
793
|
+
this.executionBroker.issueLinuxWorkspaceSandboxExecutionContract,
|
|
794
|
+
receiver: this.executionBroker,
|
|
795
|
+
file: invocation.file,
|
|
796
|
+
args: invocation.argv,
|
|
797
|
+
options: commandOptions,
|
|
798
|
+
trustedRoot,
|
|
799
|
+
label: "trusted Hooks v2 sandbox contract issuance failed",
|
|
800
|
+
})
|
|
801
|
+
: null;
|
|
802
|
+
if (
|
|
803
|
+
process.platform === "linux" &&
|
|
804
|
+
requiresContract &&
|
|
805
|
+
!sandboxExecutionContract
|
|
806
|
+
) {
|
|
807
|
+
throw sandboxBoundaryError(
|
|
808
|
+
"trusted Linux Hooks v2 sandbox contract could not be issued",
|
|
809
|
+
);
|
|
810
|
+
}
|
|
696
811
|
const child = await this.executionBroker.spawn(
|
|
697
|
-
|
|
698
|
-
|
|
812
|
+
invocation.file,
|
|
813
|
+
invocation.argv,
|
|
699
814
|
{
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
703
|
-
timeout: budget.timeoutMs,
|
|
704
|
-
shell: hook.shell === true,
|
|
705
|
-
origin: "hook",
|
|
706
|
-
scope: "hook",
|
|
707
|
-
policy: "allow",
|
|
708
|
-
hookName: hook.id,
|
|
709
|
-
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
815
|
+
...commandOptions,
|
|
816
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
710
817
|
},
|
|
711
818
|
);
|
|
712
819
|
const payload = JSON.stringify({
|
|
@@ -716,14 +823,19 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
716
823
|
});
|
|
717
824
|
child.stdin?.end(payload);
|
|
718
825
|
return new Promise((resolve, reject) => {
|
|
719
|
-
let stdout = "",
|
|
720
|
-
|
|
721
|
-
child.
|
|
826
|
+
let stdout = "",
|
|
827
|
+
stderr = "";
|
|
828
|
+
child.stdout?.on("data", (d) => (stdout += d));
|
|
829
|
+
child.stderr?.on("data", (d) => (stderr += d));
|
|
722
830
|
child.on("error", reject);
|
|
723
831
|
child.on("exit", (code) => {
|
|
724
832
|
if (code === 0) {
|
|
725
833
|
let parsed = {};
|
|
726
|
-
try {
|
|
834
|
+
try {
|
|
835
|
+
parsed = JSON.parse(stdout);
|
|
836
|
+
} catch {
|
|
837
|
+
parsed = { raw: stdout };
|
|
838
|
+
}
|
|
727
839
|
resolve({ ...parsed, exitCode: code, stderr });
|
|
728
840
|
} else {
|
|
729
841
|
reject(new Error(`Hook command failed with exit ${code}: ${stderr}`));
|
|
@@ -763,7 +875,10 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
763
875
|
const res = await this.fetchImpl(target, {
|
|
764
876
|
method,
|
|
765
877
|
headers,
|
|
766
|
-
body:
|
|
878
|
+
body:
|
|
879
|
+
method !== "GET"
|
|
880
|
+
? JSON.stringify({ event: hook.event, context })
|
|
881
|
+
: undefined,
|
|
767
882
|
signal: controller.signal,
|
|
768
883
|
});
|
|
769
884
|
clearTimeout(timer);
|
|
@@ -782,10 +897,7 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
782
897
|
throw new Error("prompt hook executor is not configured");
|
|
783
898
|
}
|
|
784
899
|
const budget = hookBudget(hook);
|
|
785
|
-
const sandboxPolicy = resolveHookSandboxPolicy(
|
|
786
|
-
hook,
|
|
787
|
-
this.managedPolicy,
|
|
788
|
-
);
|
|
900
|
+
const sandboxPolicy = resolveHookSandboxPolicy(hook, this.managedPolicy);
|
|
789
901
|
return executeWithHookTimeout("prompt", budget, (signal) =>
|
|
790
902
|
executor({
|
|
791
903
|
hook,
|
|
@@ -805,10 +917,7 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
805
917
|
throw new Error("agent hook executor is not configured");
|
|
806
918
|
}
|
|
807
919
|
const budget = hookBudget(hook);
|
|
808
|
-
const sandboxPolicy = resolveHookSandboxPolicy(
|
|
809
|
-
hook,
|
|
810
|
-
this.managedPolicy,
|
|
811
|
-
);
|
|
920
|
+
const sandboxPolicy = resolveHookSandboxPolicy(hook, this.managedPolicy);
|
|
812
921
|
return executeWithHookTimeout("agent", budget, (signal) =>
|
|
813
922
|
executor({
|
|
814
923
|
hook,
|
|
@@ -858,10 +967,7 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
858
967
|
}
|
|
859
968
|
}
|
|
860
969
|
const budget = hookBudget(hook);
|
|
861
|
-
const sandboxPolicy = resolveHookSandboxPolicy(
|
|
862
|
-
hook,
|
|
863
|
-
this.managedPolicy,
|
|
864
|
-
);
|
|
970
|
+
const sandboxPolicy = resolveHookSandboxPolicy(hook, this.managedPolicy);
|
|
865
971
|
return executeWithHookTimeout("mcp_tool", budget, (signal) =>
|
|
866
972
|
executor({
|
|
867
973
|
server: hook.server,
|
|
@@ -889,7 +995,12 @@ class HooksV2Runtime extends EventEmitter {
|
|
|
889
995
|
getHookRegistry() {
|
|
890
996
|
const registry = {};
|
|
891
997
|
for (const [event, hooks] of this.hooks.entries()) {
|
|
892
|
-
registry[event] = hooks.map(h => ({
|
|
998
|
+
registry[event] = hooks.map((h) => ({
|
|
999
|
+
id: h.id,
|
|
1000
|
+
type: h.type,
|
|
1001
|
+
description: h.description,
|
|
1002
|
+
blocking: h.blocking,
|
|
1003
|
+
}));
|
|
893
1004
|
}
|
|
894
1005
|
return registry;
|
|
895
1006
|
}
|
|
@@ -100,6 +100,13 @@ describe("LSPClient handshake", () => {
|
|
|
100
100
|
|
|
101
101
|
it("passes plugin sandbox requirements to ProcessExecutionBroker", async () => {
|
|
102
102
|
const child = new FakeChild();
|
|
103
|
+
const contract = Object.freeze({ kind: "test-workspace-contract" });
|
|
104
|
+
const issueAuthority = vi
|
|
105
|
+
.spyOn(
|
|
106
|
+
executionBroker,
|
|
107
|
+
"issueLinuxWorkspaceSandboxExecutionContract",
|
|
108
|
+
)
|
|
109
|
+
.mockReturnValue(contract);
|
|
103
110
|
const brokerSpawn = vi
|
|
104
111
|
.spyOn(executionBroker, "spawn")
|
|
105
112
|
.mockReturnValue(child);
|
|
@@ -123,8 +130,19 @@ describe("LSPClient handshake", () => {
|
|
|
123
130
|
sandboxPolicy: {
|
|
124
131
|
requiredBoundaries: ["filesystem", "network"],
|
|
125
132
|
},
|
|
133
|
+
sandboxExecutionContract: contract,
|
|
126
134
|
}),
|
|
127
135
|
);
|
|
136
|
+
expect(issueAuthority).toHaveBeenCalledWith(
|
|
137
|
+
"fake-lsp",
|
|
138
|
+
["--stdio"],
|
|
139
|
+
expect.objectContaining({
|
|
140
|
+
cwd: "/proj",
|
|
141
|
+
shell: false,
|
|
142
|
+
origin: "plugin:lsp",
|
|
143
|
+
}),
|
|
144
|
+
"/proj",
|
|
145
|
+
);
|
|
128
146
|
expect(_deps.spawn).not.toHaveBeenCalled();
|
|
129
147
|
});
|
|
130
148
|
|
|
@@ -83,7 +83,7 @@ export class LSPClient extends EventEmitter {
|
|
|
83
83
|
const spawnFn = String(this.origin || "").startsWith("plugin:")
|
|
84
84
|
? executionBroker.spawn.bind(executionBroker)
|
|
85
85
|
: _deps.spawn;
|
|
86
|
-
|
|
86
|
+
const spawnOptions = {
|
|
87
87
|
cwd: this.rootPath,
|
|
88
88
|
env: { ...process.env, ...this.env },
|
|
89
89
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -103,6 +103,17 @@ export class LSPClient extends EventEmitter {
|
|
|
103
103
|
: {}),
|
|
104
104
|
}
|
|
105
105
|
: {}),
|
|
106
|
+
};
|
|
107
|
+
const sandboxExecutionContract =
|
|
108
|
+
executionBroker.issueLinuxWorkspaceSandboxExecutionContract(
|
|
109
|
+
spawnSpec.file,
|
|
110
|
+
spawnSpec.args,
|
|
111
|
+
spawnOptions,
|
|
112
|
+
this.rootPath,
|
|
113
|
+
);
|
|
114
|
+
this._child = spawnFn(spawnSpec.file, spawnSpec.args, {
|
|
115
|
+
...spawnOptions,
|
|
116
|
+
...(sandboxExecutionContract ? { sandboxExecutionContract } : {}),
|
|
106
117
|
});
|
|
107
118
|
|
|
108
119
|
this._child.on("error", (err) => {
|