chainlesschain 0.162.180 → 0.162.182
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 +7 -3
- package/scripts/build-windows-sandbox-helper.ps1 +163 -0
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-D_TIL54O.js → AIOps-DzZ5_xoj.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-eQwy9r-f.js → ActionButton-DeLXddIg.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-D2eNuDgT.js → Analytics-rvDCmet1.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CFhwOFsf.js → AppLayout-Yt6QVDnp.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-CJUMw6PB.js → Artifacts-BS112K5L.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-CDB9V58o.js → Audit-0wJuQrNu.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-TBppYFjN.js → BackgroundAgents-B90PMgbn.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-CYEOUpzJ.js → Backup-TkgMglQQ.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-C88fFT2u.js → BaseInput-CQVS9p1l.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-mkr1vC3N.js → Chat-YTjQHoC7.js} +6 -6
- package/src/assets/web-panel/assets/ChatBubbleRenderer-PR074hUN.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-CVU4xqti.js → Checkbox-5bwaOabq.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-rrNcByz3.js → Codegen-BFmNQ_Xe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-Cqu-zCvh.js → Col-BT_u4Dzp.js} +1 -1
- package/src/assets/web-panel/assets/{Community-RGRlSgXV.js → Community-CJP52u5b.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-DBm6epOK.js → Compact-BpzhJE9i.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BgKw5iaW.js → Compliance-Cg7rfslp.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BqQcNO3Z.js → Cowork-JisxL6Yy.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DO5gTFNb.js → Cron-Bu7Ug_MC.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-B8hbJNuN.js → Crosschain-CvuqoLdj.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B08bFJ2C.js → DID-C2u9G8Pu.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-xNdy_ifo.js → Dashboard-BFt5Yv3d.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-D-KIZgWL.js → Dropdown-DIaT9Wxq.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DKCvsaGo.js → EmailListRenderer-D_A601Y4.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-D_cfqama.js → FamilyGuardDashboard--IyQ8I8w.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-B-UqQD68.js → Federation-BihWHxuY.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BWWvWuJW.js → FormItemContext-BB8dOXc_.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-CPchx6N7.js → GenericCardRenderer-DQYhj1HH.js} +1 -1
- package/src/assets/web-panel/assets/{Git-Vx4co9Ff.js → Git-B-2QZxGf.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-D4x0pp_m.js → Governance-CusSb-nG.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-T3KvwPYR.js → Inference-BAjmvPPL.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-Ddv3KUv3.js → KnowledgeGraph-DoNYWKYq.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DfH-Dkae.js → Logs-B8fKWD-n.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DHW3PbCq.js → MarkdownRenderer-BlJm1J7E.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-tjgb0R3D.js → Marketplace-CyOscbSL.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-DHAk5nYX.js → McpTools-Ex6Om5da.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-A2Yx7thM.js → Memory-CDcD8l8r.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BEmTVfN5.js → MobileBridge-xfBqoFDg.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-DuFHUgE4.js → MobileProjects-BwObMMA2.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BDSll0OZ.js → Mtc-CIWd36OV.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CuBvluaS.js → MtcAudit-CPX0ouuA.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-BmXAA-Sb.js → Multisig-BDJsIUuW.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-ci2stZAe.js → NLProgramming-DEkbuD3N.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-D-1RpqnS.js → Notes-DJpHRhH1.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C0GyitIT.js → NotificationSettings-C1UZ-Qd5.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-U2bN-hj5.js → OrderTableRenderer-GlJtqLjT.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-BNZgpPci.js → Organization-Bbhj0fac.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-CDidKmUN.js → Overflow-1H04x3kx.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BnreBaXq.js → P2P-CDpPe3et.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-DwJUxO9j.js → PdhVaultBrowser-MUX0Bpvw.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BQGgJPH0.js → Permissions-A07-0Ci1.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-k_fSkCwL.js → PersonalDataHub-DhhDWxAZ.js} +5 -5
- package/src/assets/web-panel/assets/{Pipeline-WGqKMqau.js → Pipeline-CyfbJfe-.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-DtHj9Ymq.js → Privacy-CNFps3ED.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-B3wH2SFC.js → ProjectInit--T-u3Dr5.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-Cd0cgcA1.js → ProjectSettings-DJmrK2fC.js} +2 -2
- package/src/assets/web-panel/assets/Projects-Qx_MdX9k.js +1 -0
- package/src/assets/web-panel/assets/{Providers-DTpxEXur.js → Providers-DLzzbOZd.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-B2v6gR-C.js → QrScannerModal-CexOomKS.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-D3Nr9xXR.js → QuickAsk-QklGi19_.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-C9inoMky.js → Recommend-Bb9kwSw8.js} +1 -1
- package/src/assets/web-panel/assets/RemoteSession-DZuVNNN5.js +5 -0
- package/src/assets/web-panel/assets/{Reputation-41KFG646.js → Reputation-Dr5cLP0N.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DqAaqqCv.js → Row-oCQHYv5t.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CMDpCDKc.js → RssFeed-IFDEykIR.js} +3 -3
- package/src/assets/web-panel/assets/{Search-B6mtegNL.js → Search-CspVIxDc.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BaePFVZ8.js → Security-Bm66o_DH.js} +3 -3
- package/src/assets/web-panel/assets/{Services-DeG3Qscl.js → Services-DvanNqXl.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-D_QP7F90.js → Skeleton-CHjEe9_O.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DLGyUgIA.js → Skills-Cb8jWjMX.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BN2pxW1Q.js → Sla-DxZm1491.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-BWsZPd8B.js → SpeechSettings-DWxEEclC.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-VOZRPODW.js → SyncSettings-FIvOFw5p.js} +2 -2
- package/src/assets/web-panel/assets/Tasks-CyuLnDFR.js +1 -0
- package/src/assets/web-panel/assets/{Templates-eJSob-q0.js → Templates-CaNjzV_t.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-Bdwk8Rwy.js → Tenant-DjlP6TmH.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-DwzGMniO.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-D5qkbPKT.js → TimelineRenderer-DoZ8y7y8.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-DKgwn8hI.js → Tokens-390cg2TC.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-HlEHniGM.js → Trigger-CLrtCj5_.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-CdPcsBpN.js → Trust-DpESfSPn.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-vAdXd-vG.js → UkeySign-CN6279O0.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-9hHBYFnH.js → VideoEditing-DPtAUlSe.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Dxh5d1yR.js → Wallet-nKOMV_1n.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-BsFmLHkp.js → WebAuthn-HuXlSOc4.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-Cn0OIwLn.js → WorkflowEditor-CUfFjARi.js} +1 -1
- package/src/assets/web-panel/assets/chat-6Eq0PcZJ.js +1 -0
- package/src/assets/web-panel/assets/{colors-Du9jzLN0.js → colors-BP_Sf6Iy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DhEscfzw.js → compact-item-BufMKIh5.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CwzEfF3i.js → createContext-BBMXxWSW.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DgVKduH2.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DT_GSE6J.js → hasIn-fUK_tRO2.js} +1 -1
- package/src/assets/web-panel/assets/{index-tND8-TDU.js → index-2A5ph8b2.js} +1 -1
- package/src/assets/web-panel/assets/{index-B8s5FxJY.js → index-3FuwBewR.js} +1 -1
- package/src/assets/web-panel/assets/{index-53z1YUxX.js → index-6GmBa4Kg.js} +1 -1
- package/src/assets/web-panel/assets/{index-BFD0cEna.js → index-B2vS66eI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQWxDOvM.js → index-B7Etk0Te.js} +1 -1
- package/src/assets/web-panel/assets/{index-VeHs6qH0.js → index-B9WYSn4m.js} +1 -1
- package/src/assets/web-panel/assets/{index-CGvgnfl9.js → index-BHy6yNiF.js} +1 -1
- package/src/assets/web-panel/assets/{index-D3llLkYM.js → index-BMLHv389.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dny_ZnAO.js → index-BWKoBU8R.js} +1 -1
- package/src/assets/web-panel/assets/{index-DzjzrOli.js → index-Bkg4ZePE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DN60VBn5.js → index-C44atv4k.js} +1 -1
- package/src/assets/web-panel/assets/{index-v5HLYCVa.js → index-CE2DdTP2.js} +1 -1
- package/src/assets/web-panel/assets/{index-BF-ZCVGE.js → index-CGjmILDm.js} +1 -1
- package/src/assets/web-panel/assets/{index-gFjYdstW.js → index-CW_G-MXw.js} +1 -1
- package/src/assets/web-panel/assets/{index-NA2oBLSB.js → index-CZ6Yt17L.js} +1 -1
- package/src/assets/web-panel/assets/{index-Mobw3PHs.js → index-CeASi4fr.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWL3SYN3.js → index-CgiECHu2.js} +1 -1
- package/src/assets/web-panel/assets/{index-1LoutPJd.js → index-Cp8vhyZy.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnHhFnwt.js → index-CwSf5pnf.js} +1 -1
- package/src/assets/web-panel/assets/{index-B6QDWRLk.js → index-D4_MuGkd.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-QkW1fp.js → index-D9vU25ij.js} +1 -1
- package/src/assets/web-panel/assets/{index-C4SN3by5.js → index-DASluSqY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyEhUJcu.js → index-DLpIs_P_.js} +1 -1
- package/src/assets/web-panel/assets/index-DOZTSoS7.js +1 -0
- package/src/assets/web-panel/assets/{index-BrkJ7JPm.js → index-DPdUNk1i.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cnok8mhZ.js → index-DVsqvI4M.js} +1 -1
- package/src/assets/web-panel/assets/index-DhpvjpqI.js +1 -0
- package/src/assets/web-panel/assets/{index-CAVW0cWM.js → index-DmkMXwnR.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cc9aPkv-.js → index-DocdRcbd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DnVVYKCE.js → index-Dyngbd29.js} +20 -20
- package/src/assets/web-panel/assets/{index-D8gt0j7r.js → index-GwdRUZ-x.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIYTjbv_.js → index-LvCBfTLo.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhXay17G.js → index-cpEli44X.js} +1 -1
- package/src/assets/web-panel/assets/{index-BQWENaZf.js → index-fzaMtSJ8.js} +1 -1
- package/src/assets/web-panel/assets/{index-DfRDyY1q.js → index-pum7HYBA.js} +1 -1
- package/src/assets/web-panel/assets/{index-BgSR2WMG.js → index-t8N1tSeX.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5IytTND.js → index-uCbEN7cY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DtRwLhee.js → index-wKTLbKyc.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4kZoXNr.js → index-wV6iGdpY.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-Cvt59c10.js → initDefaultProps-V5Fo95FM.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D15pPEzl.js → motion-BYWU0hUT.js} +1 -1
- package/src/assets/web-panel/assets/{move-N55KeXJ7.js → move-Cr1M-J2h.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-CyFO1bYP.js → mtc-parser-BysQKi2N.js} +1 -1
- package/src/assets/web-panel/assets/{omit-B5IXfxU2.js → omit-D1zHU8GC.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CpVqsulV.js → pickAttrs-CUArxtfi.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-ME36sMF3.js → placementArrow-ChoYEys0.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-BXAIR1r-.js → responsiveObserve-BQRs3Mo7.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BkOX3-Dp.js → slide-B8zbsCkg.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Cy4Aklpt.js → statusUtils-C2U8Gs6h.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-gUd2T206.js → styleChecker-Dd8MgL_V.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-CaVD4o99.js → useFlexGapSupport-hknqY5NH.js} +1 -1
- package/src/assets/web-panel/assets/{useFs--pFGl0KD.js → useFs-CLTAmcfw.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-D45iP86i.js → usePersonalDataHub-ByegHbiB.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BayoChH_.js → vnode-DqlAy5DP.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CpP38poi.js → zoom-DiXiztQn.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/agenda.js +115 -3
- package/src/commands/agent.js +10 -1
- package/src/commands/background-session.js +2 -1
- package/src/commands/context.js +69 -2
- package/src/data/changelog.json +26 -0
- package/src/gateways/terminal/PtyManager.js +45 -4
- package/src/gateways/ws/background-agent-protocol.js +38 -0
- package/src/gateways/ws/message-dispatcher.js +1 -0
- package/src/gateways/ws/remote-session-protocol.js +65 -6
- package/src/gateways/ws/ws-server.js +18 -2
- package/src/harness/background-task-manager.js +70 -2
- package/src/harness/feature-flags.js +15 -8
- package/src/harness/mcp-client.js +618 -43
- package/src/harness/worktree-isolator.js +14 -3
- package/src/lazy-dispatch.js +30 -1
- package/src/lib/agent-authority.js +22 -0
- package/src/lib/agent-core.js +3 -0
- package/src/lib/agent-sandbox.js +161 -0
- package/src/lib/agent-schedule-store.js +45 -22
- package/src/lib/agent-team/team-runner.js +22 -0
- package/src/lib/artifact-store.js +35 -0
- package/src/lib/async-hook-supervisor.cjs +13 -2
- package/src/lib/audit-mtc.js +3 -2
- package/src/lib/background-agent-supervisor.js +104 -3
- package/src/lib/background-interaction-journal.js +407 -0
- package/src/lib/background-interaction-resolver.js +23 -34
- package/src/lib/background-session-transport.js +12 -2
- package/src/lib/capability-manifest.js +41 -3
- package/src/lib/capability-negotiation.js +1 -1
- package/src/lib/channels/channel-manager.js +33 -0
- package/src/lib/channels/telegram-channel.js +23 -5
- package/src/lib/channels/webhook-channel.js +38 -15
- package/src/lib/chrome-connector.js +169 -23
- package/src/lib/config-manager.js +23 -9
- package/src/lib/context-breakdown.js +31 -0
- package/src/lib/cowork-cron.js +274 -32
- package/src/lib/cross-chain-mtc.js +3 -2
- package/src/lib/durable-security-store.js +124 -0
- package/src/lib/event-runtime-host.js +67 -6
- package/src/lib/event-runtime-store.js +163 -0
- package/src/lib/goal-store.js +32 -12
- package/src/lib/headless-manifest.js +2 -1
- package/src/lib/hook-environment.cjs +58 -0
- package/src/lib/hook-manager.js +12 -5
- package/src/lib/hook-runner.cjs +122 -28
- package/src/lib/hook-runner.js +7 -2
- package/src/lib/hooks-v2-producers.js +36 -0
- package/src/lib/hooks-v2-runtime.js +329 -52
- package/src/lib/interaction-adapter.js +60 -19
- package/src/lib/interaction-binding.js +64 -0
- package/src/lib/json-schema-output.js +439 -14
- package/src/lib/json-schema-validate.js +514 -883
- package/src/lib/lan-pairing-tokens.js +115 -57
- package/src/lib/lsp/__tests__/lsp-client.test.js +32 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +25 -0
- package/src/lib/lsp/lsp-client.js +4 -0
- package/src/lib/lsp/lsp-manager.js +3 -0
- package/src/lib/lsp/lsp-server-registry.js +18 -0
- package/src/lib/mcp-client.js +1 -0
- package/src/lib/mcp-oauth.js +35 -21
- package/src/lib/packer/pkg-config-generator.js +3 -0
- package/src/lib/permission-decision.js +99 -0
- package/src/lib/plugin-monitor-supervisor.js +3 -0
- package/src/lib/plugin-runtime/bin.js +1148 -70
- package/src/lib/plugin-runtime/capabilities.js +50 -0
- package/src/lib/plugin-runtime/capability-consent.js +42 -35
- package/src/lib/plugin-runtime/hooks.js +36 -9
- package/src/lib/plugin-runtime/install.js +23 -7
- package/src/lib/plugin-runtime/lsp.js +4 -0
- package/src/lib/plugin-runtime/manifest.js +112 -8
- package/src/lib/plugin-runtime/mcp.js +30 -5
- package/src/lib/plugin-runtime/monitors.js +11 -0
- package/src/lib/plugin-runtime/plugin-options.js +94 -38
- package/src/lib/plugin-runtime/policy.js +29 -2
- package/src/lib/plugin-runtime/sandbox-policy.js +84 -0
- package/src/lib/plugin-runtime/scopes.js +27 -13
- package/src/lib/plugin-runtime/trust.js +36 -31
- package/src/lib/process-execution-broker/credential-agent.js +114 -16
- package/src/lib/process-execution-broker/credential-transport-worker.js +312 -0
- package/src/lib/process-execution-broker/credential-transport.js +378 -0
- package/src/lib/process-execution-broker/index.js +765 -49
- package/src/lib/process-execution-broker/platform-sandbox.js +4233 -138
- 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 +3856 -0
- package/src/lib/project-mcp-trust.js +21 -26
- package/src/lib/protocol-replay.js +19 -12
- package/src/lib/repl-bang-memorize.js +67 -10
- package/src/lib/settings-hooks.cjs +13 -0
- package/src/lib/skill-loader.js +83 -10
- package/src/lib/slash-commands.js +27 -4
- package/src/lib/sync-credentials.js +5 -4
- package/src/lib/turn-binding.js +8 -6
- package/src/repl/agent-repl.js +117 -31
- package/src/repl/repl-turn-binding.js +5 -2
- package/src/runtime/agent-core.js +699 -70
- package/src/runtime/agent-runtime.js +13 -1
- package/src/runtime/coding-agent-contract-shared.cjs +3 -3
- package/src/runtime/headless-runner.js +105 -7
- package/src/runtime/headless-stream.js +209 -23
- package/src/runtime/mcp-config.js +29 -17
- package/src/runtime/policies/agent-policy.js +2 -0
- package/src/workers/background-agent-worker.js +128 -13
- package/src/assets/web-panel/assets/ChatBubbleRenderer-C7bofJuA.js +0 -1
- package/src/assets/web-panel/assets/Projects-BbgmyQnS.js +0 -1
- package/src/assets/web-panel/assets/RemoteSession-Bqk3EYSu.js +0 -4
- package/src/assets/web-panel/assets/Tasks-3bDAUz8K.js +0 -1
- package/src/assets/web-panel/assets/Terminal-CsMLKIoV.js +0 -3
- package/src/assets/web-panel/assets/chat-CJ19O6Dh.js +0 -1
- package/src/assets/web-panel/assets/devWarning-BBMFnsja.js +0 -1
- package/src/assets/web-panel/assets/index-Do8JMV4N.js +0 -1
- package/src/assets/web-panel/assets/index-UYAJ6iNM.js +0 -1
|
@@ -45,6 +45,8 @@ export class EventRuntimeStore {
|
|
|
45
45
|
leaseMs = 120000,
|
|
46
46
|
lockTimeoutMs = 2000,
|
|
47
47
|
maxQueueLength = 10000,
|
|
48
|
+
hostStaleMs = 15000,
|
|
49
|
+
maxHostRecords = 100,
|
|
48
50
|
} = {}) {
|
|
49
51
|
this.dir = dir;
|
|
50
52
|
this._now = typeof now === "function" ? now : () => Number(now);
|
|
@@ -53,6 +55,8 @@ export class EventRuntimeStore {
|
|
|
53
55
|
this.leaseMs = Math.max(1, Number(leaseMs) || 120000);
|
|
54
56
|
this.lockTimeoutMs = Math.max(0, Number(lockTimeoutMs) || 2000);
|
|
55
57
|
this.maxQueueLength = Math.max(1, Number(maxQueueLength) || 10000);
|
|
58
|
+
this.hostStaleMs = Math.max(1, Number(hostStaleMs) || 15000);
|
|
59
|
+
this.maxHostRecords = Math.max(1, Number(maxHostRecords) || 100);
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
_assertQueue(queue) {
|
|
@@ -64,6 +68,10 @@ export class EventRuntimeStore {
|
|
|
64
68
|
return path.join(this.dir, `${queue}.json`);
|
|
65
69
|
}
|
|
66
70
|
|
|
71
|
+
_hostsFile() {
|
|
72
|
+
return path.join(this.dir, "hosts.json");
|
|
73
|
+
}
|
|
74
|
+
|
|
67
75
|
_ensureDir() {
|
|
68
76
|
fs.mkdirSync(this.dir, { recursive: true, mode: 0o700 });
|
|
69
77
|
}
|
|
@@ -86,6 +94,23 @@ export class EventRuntimeStore {
|
|
|
86
94
|
fs.renameSync(tmp, file);
|
|
87
95
|
}
|
|
88
96
|
|
|
97
|
+
_readHosts() {
|
|
98
|
+
try {
|
|
99
|
+
const value = JSON.parse(fs.readFileSync(this._hostsFile(), "utf8"));
|
|
100
|
+
return Array.isArray(value) ? value : [];
|
|
101
|
+
} catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_writeHosts(records) {
|
|
107
|
+
this._ensureDir();
|
|
108
|
+
const file = this._hostsFile();
|
|
109
|
+
const tmp = `${file}.${process.pid}.${randomUUID()}.tmp`;
|
|
110
|
+
fs.writeFileSync(tmp, JSON.stringify(records, null, 2), "utf8");
|
|
111
|
+
fs.renameSync(tmp, file);
|
|
112
|
+
}
|
|
113
|
+
|
|
89
114
|
_mutate(queue, fn) {
|
|
90
115
|
this._ensureDir();
|
|
91
116
|
return withFileLock(
|
|
@@ -100,6 +125,121 @@ export class EventRuntimeStore {
|
|
|
100
125
|
);
|
|
101
126
|
}
|
|
102
127
|
|
|
128
|
+
_mutateHosts(fn) {
|
|
129
|
+
this._ensureDir();
|
|
130
|
+
return withFileLock(
|
|
131
|
+
path.join(this.dir, ".event-runtime"),
|
|
132
|
+
() => {
|
|
133
|
+
const records = this._readHosts();
|
|
134
|
+
const result = fn(records);
|
|
135
|
+
this._writeHosts(records);
|
|
136
|
+
return result;
|
|
137
|
+
},
|
|
138
|
+
{ timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Publish a process-host heartbeat in the same lock domain as the queues.
|
|
144
|
+
* The registry is bounded and additive: it is safe for an older observer to
|
|
145
|
+
* ignore hosts.json while newer `cc status --json` consumers can distinguish
|
|
146
|
+
* a live worker from queued work with no owner.
|
|
147
|
+
*/
|
|
148
|
+
reportHost(
|
|
149
|
+
{
|
|
150
|
+
id = this.owner,
|
|
151
|
+
pid = process.pid,
|
|
152
|
+
role = "cli",
|
|
153
|
+
state = "running",
|
|
154
|
+
startedAt = null,
|
|
155
|
+
heartbeatAt = this._now(),
|
|
156
|
+
staleAfterMs = this.hostStaleMs,
|
|
157
|
+
lastStats = null,
|
|
158
|
+
lastError = null,
|
|
159
|
+
} = {},
|
|
160
|
+
) {
|
|
161
|
+
const hostId = String(id || "").trim();
|
|
162
|
+
if (!hostId) throw new Error("Event Runtime host id is required");
|
|
163
|
+
const observedAt = Number(heartbeatAt);
|
|
164
|
+
const timestamp = Number.isFinite(observedAt) ? observedAt : this._now();
|
|
165
|
+
return this._mutateHosts((records) => {
|
|
166
|
+
const existing = records.find((record) => record.id === hostId);
|
|
167
|
+
const record =
|
|
168
|
+
existing ||
|
|
169
|
+
(() => {
|
|
170
|
+
const created = { id: hostId };
|
|
171
|
+
records.push(created);
|
|
172
|
+
return created;
|
|
173
|
+
})();
|
|
174
|
+
record.pid =
|
|
175
|
+
pid != null && Number.isFinite(Number(pid)) ? Number(pid) : null;
|
|
176
|
+
record.role = String(role || "cli");
|
|
177
|
+
record.state = state === "stopped" ? "stopped" : "running";
|
|
178
|
+
record.startedAt =
|
|
179
|
+
existing?.startedAt != null &&
|
|
180
|
+
Number.isFinite(Number(existing.startedAt))
|
|
181
|
+
? Number(existing.startedAt)
|
|
182
|
+
: startedAt != null && Number.isFinite(Number(startedAt))
|
|
183
|
+
? Number(startedAt)
|
|
184
|
+
: timestamp;
|
|
185
|
+
record.heartbeatAt = timestamp;
|
|
186
|
+
record.staleAfterMs = Math.max(
|
|
187
|
+
1,
|
|
188
|
+
Number(staleAfterMs) || this.hostStaleMs,
|
|
189
|
+
);
|
|
190
|
+
record.lastStats = clone(lastStats);
|
|
191
|
+
record.lastError =
|
|
192
|
+
lastError == null ? null : String(lastError).slice(0, 1000);
|
|
193
|
+
if (record.state === "stopped") record.stoppedAt = timestamp;
|
|
194
|
+
else delete record.stoppedAt;
|
|
195
|
+
|
|
196
|
+
if (records.length > this.maxHostRecords) {
|
|
197
|
+
records.sort((a, b) => {
|
|
198
|
+
const aRunning = a.state === "running" ? 1 : 0;
|
|
199
|
+
const bRunning = b.state === "running" ? 1 : 0;
|
|
200
|
+
return (
|
|
201
|
+
bRunning - aRunning ||
|
|
202
|
+
Number(b.heartbeatAt || 0) - Number(a.heartbeatAt || 0)
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
records.splice(this.maxHostRecords);
|
|
206
|
+
}
|
|
207
|
+
return clone(record);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
stopHost(id = this.owner, options = {}) {
|
|
212
|
+
return this.reportHost({
|
|
213
|
+
...options,
|
|
214
|
+
id,
|
|
215
|
+
state: "stopped",
|
|
216
|
+
heartbeatAt: options.heartbeatAt ?? this._now(),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
listHosts({ now = this._now(), includeStopped = true } = {}) {
|
|
221
|
+
const t = Number(now);
|
|
222
|
+
this._ensureDir();
|
|
223
|
+
return withFileLock(
|
|
224
|
+
path.join(this.dir, ".event-runtime"),
|
|
225
|
+
() =>
|
|
226
|
+
this._readHosts()
|
|
227
|
+
.filter((record) => includeStopped || record.state !== "stopped")
|
|
228
|
+
.map((record) => ({
|
|
229
|
+
...clone(record),
|
|
230
|
+
stale:
|
|
231
|
+
record.state === "running" &&
|
|
232
|
+
Number(record.heartbeatAt || 0) +
|
|
233
|
+
Math.max(
|
|
234
|
+
1,
|
|
235
|
+
Number(record.staleAfterMs) || this.hostStaleMs,
|
|
236
|
+
) <=
|
|
237
|
+
t,
|
|
238
|
+
})),
|
|
239
|
+
{ timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
103
243
|
enqueue(
|
|
104
244
|
queue,
|
|
105
245
|
event,
|
|
@@ -418,6 +558,28 @@ export class EventRuntimeStore {
|
|
|
418
558
|
: Object.values(queues).some((queue) => queue.pressure === "high")
|
|
419
559
|
? "high"
|
|
420
560
|
: "normal";
|
|
561
|
+
const hostEntries = this._readHosts().map((record) => ({
|
|
562
|
+
...clone(record),
|
|
563
|
+
stale:
|
|
564
|
+
record.state === "running" &&
|
|
565
|
+
Number(record.heartbeatAt || 0) +
|
|
566
|
+
Math.max(
|
|
567
|
+
1,
|
|
568
|
+
Number(record.staleAfterMs) || this.hostStaleMs,
|
|
569
|
+
) <=
|
|
570
|
+
t,
|
|
571
|
+
}));
|
|
572
|
+
const hosts = {
|
|
573
|
+
total: hostEntries.length,
|
|
574
|
+
running: hostEntries.filter(
|
|
575
|
+
(record) => record.state === "running" && !record.stale,
|
|
576
|
+
).length,
|
|
577
|
+
stale: hostEntries.filter((record) => record.stale).length,
|
|
578
|
+
stopped: hostEntries.filter(
|
|
579
|
+
(record) => record.state === "stopped",
|
|
580
|
+
).length,
|
|
581
|
+
entries: hostEntries,
|
|
582
|
+
};
|
|
421
583
|
return {
|
|
422
584
|
schema: "chainlesschain.event-runtime-health.v1",
|
|
423
585
|
observedAt: new Date(t).toISOString(),
|
|
@@ -425,6 +587,7 @@ export class EventRuntimeStore {
|
|
|
425
587
|
highWatermarkRatio: highWatermark,
|
|
426
588
|
pressure,
|
|
427
589
|
queues,
|
|
590
|
+
hosts,
|
|
428
591
|
};
|
|
429
592
|
},
|
|
430
593
|
{ timeoutMs: this.lockTimeoutMs, failIfUnavailable: true },
|
package/src/lib/goal-store.js
CHANGED
|
@@ -146,8 +146,18 @@ export function createGoal(input = {}, opts = {}) {
|
|
|
146
146
|
updatedAt: nowIso(),
|
|
147
147
|
};
|
|
148
148
|
ensureDir(root);
|
|
149
|
-
|
|
150
|
-
return
|
|
149
|
+
const file = goalFile(root, id);
|
|
150
|
+
return withFileLock(
|
|
151
|
+
file,
|
|
152
|
+
() => {
|
|
153
|
+
if (fs.existsSync(file)) {
|
|
154
|
+
throw new Error(`goal already exists: ${id}`);
|
|
155
|
+
}
|
|
156
|
+
atomicWriteFileSync(file, JSON.stringify(goal, null, 2));
|
|
157
|
+
return goal;
|
|
158
|
+
},
|
|
159
|
+
{ failIfUnavailable: true },
|
|
160
|
+
);
|
|
151
161
|
}
|
|
152
162
|
|
|
153
163
|
function normalizeKr(kr) {
|
|
@@ -212,19 +222,22 @@ export function listGoals(opts = {}) {
|
|
|
212
222
|
function mutate(id, fn, opts = {}) {
|
|
213
223
|
// Serialize the read-modify-write across processes (cc+cc, or cc+desktop
|
|
214
224
|
// sharing the goals dir) so concurrent goal edits don't lose an update.
|
|
215
|
-
//
|
|
216
|
-
// so the CLI never hangs.
|
|
225
|
+
// Durable goal state never proceeds without exclusion after bounded retry.
|
|
217
226
|
// Guard before deriving the lock path: an unsafe id would otherwise place the
|
|
218
227
|
// lock file at a traversal path (`<root>/../../x.json.lock`) before getGoal
|
|
219
228
|
// even runs. getGoal/saveGoal also guard, but keep the lock inside the dir.
|
|
220
229
|
if (isUnsafeGoalId(id)) throw new Error(`no such goal: ${id}`);
|
|
221
230
|
const root = opts.root || defaultRoot();
|
|
222
|
-
return withFileLock(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
231
|
+
return withFileLock(
|
|
232
|
+
goalFile(root, id),
|
|
233
|
+
() => {
|
|
234
|
+
const goal = getGoal(id, opts);
|
|
235
|
+
if (!goal) throw new Error(`no such goal: ${id}`);
|
|
236
|
+
fn(goal);
|
|
237
|
+
return saveGoal(goal, opts);
|
|
238
|
+
},
|
|
239
|
+
{ failIfUnavailable: true },
|
|
240
|
+
);
|
|
228
241
|
}
|
|
229
242
|
|
|
230
243
|
/** Add a key result to a goal. */
|
|
@@ -368,8 +381,15 @@ export function deleteGoal(id, opts = {}) {
|
|
|
368
381
|
const root = opts.root || defaultRoot();
|
|
369
382
|
const file = goalFile(root, id);
|
|
370
383
|
if (!fs.existsSync(file)) return false;
|
|
371
|
-
|
|
372
|
-
|
|
384
|
+
return withFileLock(
|
|
385
|
+
file,
|
|
386
|
+
() => {
|
|
387
|
+
if (!fs.existsSync(file)) return false;
|
|
388
|
+
fs.rmSync(file);
|
|
389
|
+
return true;
|
|
390
|
+
},
|
|
391
|
+
{ failIfUnavailable: true },
|
|
392
|
+
);
|
|
373
393
|
}
|
|
374
394
|
|
|
375
395
|
/**
|
|
@@ -150,7 +150,8 @@ export function buildAgentCapabilities() {
|
|
|
150
150
|
// P1-9: permission modes, formats and the feature flags all project from the
|
|
151
151
|
// single canonical [[capability-manifest.js]] so `cc agent --capabilities`,
|
|
152
152
|
// the negotiator and the docs can no longer drift. Additive protocol-v1
|
|
153
|
-
// stream fields (event_seq / tool_use_id / trace_id)
|
|
153
|
+
// stream fields (event_seq / tool_use_id / permission_decision / trace_id)
|
|
154
|
+
// come in via the wire
|
|
154
155
|
// features; the mcp object and sandbox_engines come from the manifest too.
|
|
155
156
|
permission_modes: [...CAPABILITY_MANIFEST.permissionModes],
|
|
156
157
|
output_formats: [...CAPABILITY_MANIFEST.outputFormats],
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Environment boundary shared by every CLI hook surface.
|
|
5
|
+
*
|
|
6
|
+
* Hooks receive only the small set needed to locate executables and temporary
|
|
7
|
+
* files. Extra inherited variables require both an administrator-managed
|
|
8
|
+
* allowlist and an explicit request from the hook definition.
|
|
9
|
+
*/
|
|
10
|
+
const SAFE_HOOK_ENV_KEYS = Object.freeze([
|
|
11
|
+
"PATH",
|
|
12
|
+
"PATHEXT",
|
|
13
|
+
"SystemRoot",
|
|
14
|
+
"WINDIR",
|
|
15
|
+
"COMSPEC",
|
|
16
|
+
"TMP",
|
|
17
|
+
"TEMP",
|
|
18
|
+
"TMPDIR",
|
|
19
|
+
"LANG",
|
|
20
|
+
"LC_ALL",
|
|
21
|
+
"HOME",
|
|
22
|
+
"USERPROFILE",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
function normalizeNames(values) {
|
|
26
|
+
return new Set(
|
|
27
|
+
(Array.isArray(values) ? values : [])
|
|
28
|
+
.map((value) => String(value || "").trim())
|
|
29
|
+
.filter(Boolean),
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function buildManagedHookEnvironment({
|
|
34
|
+
source = process.env,
|
|
35
|
+
managedAllowlist = [],
|
|
36
|
+
requestedAllowlist = [],
|
|
37
|
+
values = {},
|
|
38
|
+
} = {}) {
|
|
39
|
+
const env = {};
|
|
40
|
+
for (const key of SAFE_HOOK_ENV_KEYS) {
|
|
41
|
+
if (source[key] != null) env[key] = source[key];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const managed = normalizeNames(managedAllowlist);
|
|
45
|
+
for (const key of normalizeNames(requestedAllowlist)) {
|
|
46
|
+
if (managed.has(key) && source[key] != null) env[key] = source[key];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (const [key, value] of Object.entries(values || {})) {
|
|
50
|
+
if (value != null) env[key] = String(value);
|
|
51
|
+
}
|
|
52
|
+
return env;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
SAFE_HOOK_ENV_KEYS,
|
|
57
|
+
buildManagedHookEnvironment,
|
|
58
|
+
};
|
package/src/lib/hook-manager.js
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
|
|
6
6
|
import crypto from "crypto";
|
|
7
7
|
import broker from "./process-execution-broker/index.js";
|
|
8
|
+
import hookEnvironment from "./hook-environment.cjs";
|
|
9
|
+
|
|
10
|
+
const { buildManagedHookEnvironment } = hookEnvironment;
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* Hook priority levels — lower values run first.
|
|
@@ -285,11 +288,15 @@ export async function executeHook(hook, context = {}) {
|
|
|
285
288
|
executionTime: 0,
|
|
286
289
|
};
|
|
287
290
|
}
|
|
288
|
-
const env = {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
291
|
+
const env = buildManagedHookEnvironment({
|
|
292
|
+
managedAllowlist: hook.managedEnvironmentAllowlist,
|
|
293
|
+
requestedAllowlist:
|
|
294
|
+
hook.environmentAllowlist || hook.envAllowlist,
|
|
295
|
+
values: {
|
|
296
|
+
HOOK_EVENT: hook.event,
|
|
297
|
+
HOOK_CONTEXT: JSON.stringify(context),
|
|
298
|
+
},
|
|
299
|
+
});
|
|
293
300
|
const output = broker.execSync(cmd, {
|
|
294
301
|
origin: "hook-manager:command",
|
|
295
302
|
scope: "hook",
|
package/src/lib/hook-runner.cjs
CHANGED
|
@@ -26,6 +26,9 @@ const {
|
|
|
26
26
|
loadShellConfig,
|
|
27
27
|
} = require("./shell-selector.cjs");
|
|
28
28
|
const { mergeHookDecisions } = require("./hook-event-bus.cjs");
|
|
29
|
+
const {
|
|
30
|
+
buildManagedHookEnvironment,
|
|
31
|
+
} = require("./hook-environment.cjs");
|
|
29
32
|
|
|
30
33
|
const _deps = { runSync: null, run: null };
|
|
31
34
|
Object.defineProperties(_deps, {
|
|
@@ -56,6 +59,86 @@ const HOOK_DECISIONS = Object.freeze({
|
|
|
56
59
|
CONTINUE: "continue",
|
|
57
60
|
});
|
|
58
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Resolve the sandbox contract for one legacy command hook. Requirements from
|
|
64
|
+
* the caller (for example managed settings) and from the hook entry are
|
|
65
|
+
* additive; the caller's profile wins so a hook cannot weaken managed policy.
|
|
66
|
+
*/
|
|
67
|
+
function resolveHookSandboxPolicy(hook = {}, opts = {}) {
|
|
68
|
+
const hookPolicy = hook.sandboxPolicy;
|
|
69
|
+
const callerPolicy = opts.sandboxPolicy;
|
|
70
|
+
for (const [label, value] of [
|
|
71
|
+
["hook sandboxPolicy", hookPolicy],
|
|
72
|
+
["managed hook sandboxPolicy", callerPolicy],
|
|
73
|
+
]) {
|
|
74
|
+
if (
|
|
75
|
+
value !== undefined &&
|
|
76
|
+
(value === null || typeof value !== "object" || Array.isArray(value))
|
|
77
|
+
) {
|
|
78
|
+
const error = new Error(`${label} must be an object`);
|
|
79
|
+
error.code = "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const requiredBoundaries = [];
|
|
85
|
+
for (const [label, value] of [
|
|
86
|
+
["managed requiredBoundaries", opts.requiredBoundaries],
|
|
87
|
+
[
|
|
88
|
+
"managed sandboxPolicy.requiredBoundaries",
|
|
89
|
+
callerPolicy?.requiredBoundaries,
|
|
90
|
+
],
|
|
91
|
+
["hook requiredBoundaries", hook.requiredBoundaries],
|
|
92
|
+
["hook sandboxPolicy.requiredBoundaries", hookPolicy?.requiredBoundaries],
|
|
93
|
+
]) {
|
|
94
|
+
if (value === undefined) continue;
|
|
95
|
+
if (!Array.isArray(value)) {
|
|
96
|
+
const error = new Error(`${label} must be an array`);
|
|
97
|
+
error.code = "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
for (const boundary of value) {
|
|
101
|
+
if (!requiredBoundaries.includes(boundary)) {
|
|
102
|
+
requiredBoundaries.push(boundary);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const configured =
|
|
108
|
+
hookPolicy !== undefined ||
|
|
109
|
+
callerPolicy !== undefined ||
|
|
110
|
+
hook.requiredBoundaries !== undefined ||
|
|
111
|
+
opts.requiredBoundaries !== undefined;
|
|
112
|
+
if (!configured) return null;
|
|
113
|
+
const resolved = {
|
|
114
|
+
...(hookPolicy || {}),
|
|
115
|
+
...(callerPolicy || {}),
|
|
116
|
+
};
|
|
117
|
+
delete resolved.requiredBoundaries;
|
|
118
|
+
if (requiredBoundaries.length > 0) {
|
|
119
|
+
resolved.requiredBoundaries = requiredBoundaries;
|
|
120
|
+
}
|
|
121
|
+
return resolved;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function hookSpawnFailure(error) {
|
|
125
|
+
const sandboxDenied =
|
|
126
|
+
error?.code === "ERR_PROCESS_SANDBOX_BOUNDARY_UNSATISFIED";
|
|
127
|
+
return {
|
|
128
|
+
decision: sandboxDenied
|
|
129
|
+
? HOOK_DECISIONS.BLOCK
|
|
130
|
+
: HOOK_DECISIONS.CONTINUE,
|
|
131
|
+
reason: sandboxDenied
|
|
132
|
+
? `hook sandbox denied: ${error.message}`
|
|
133
|
+
: `hook spawn failed: ${error.message}`,
|
|
134
|
+
exitCode: null,
|
|
135
|
+
error: error.message,
|
|
136
|
+
...(sandboxDenied
|
|
137
|
+
? { sandboxDenied: true }
|
|
138
|
+
: { nonBlockingError: true }),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
59
142
|
/** JSON.parse that returns undefined (not throws) on failure. */
|
|
60
143
|
function tryJson(text) {
|
|
61
144
|
try {
|
|
@@ -237,6 +320,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
237
320
|
const usePowershell = shellKind === "powershell" || shellKind === "pwsh";
|
|
238
321
|
let res;
|
|
239
322
|
try {
|
|
323
|
+
const sandboxPolicy = resolveHookSandboxPolicy({}, opts);
|
|
240
324
|
const common = {
|
|
241
325
|
input: JSON.stringify({
|
|
242
326
|
...input,
|
|
@@ -246,10 +330,13 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
246
330
|
encoding: "utf-8",
|
|
247
331
|
timeout,
|
|
248
332
|
maxBuffer: 8 * 1024 * 1024,
|
|
249
|
-
env: {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
333
|
+
env: buildManagedHookEnvironment({
|
|
334
|
+
managedAllowlist: opts.managedEnvironmentAllowlist,
|
|
335
|
+
requestedAllowlist: opts.environmentAllowlist,
|
|
336
|
+
values: {
|
|
337
|
+
CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
|
|
338
|
+
},
|
|
339
|
+
}),
|
|
253
340
|
};
|
|
254
341
|
const processOptions = {
|
|
255
342
|
...common,
|
|
@@ -259,6 +346,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
259
346
|
pluginId: opts.pluginId || null,
|
|
260
347
|
pluginVersion: opts.pluginVersion || null,
|
|
261
348
|
pluginSource: opts.pluginSource || null,
|
|
349
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
262
350
|
};
|
|
263
351
|
const brokerRunSync =
|
|
264
352
|
opts.broker &&
|
|
@@ -293,13 +381,7 @@ function _runCommandHookInner(command, input = {}, opts = {}) {
|
|
|
293
381
|
res = _deps.runSync(command, { ...processOptions, shell: true });
|
|
294
382
|
}
|
|
295
383
|
} catch (err) {
|
|
296
|
-
return
|
|
297
|
-
decision: HOOK_DECISIONS.CONTINUE,
|
|
298
|
-
reason: `hook spawn failed: ${err.message}`,
|
|
299
|
-
exitCode: null,
|
|
300
|
-
error: err.message,
|
|
301
|
-
nonBlockingError: true,
|
|
302
|
-
};
|
|
384
|
+
return hookSpawnFailure(err);
|
|
303
385
|
}
|
|
304
386
|
return interpretHookOutcome(res);
|
|
305
387
|
}
|
|
@@ -390,10 +472,19 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
390
472
|
...input,
|
|
391
473
|
schema_version: input.schema_version ?? HOOK_PAYLOAD_SCHEMA_VERSION,
|
|
392
474
|
});
|
|
393
|
-
const spawnEnv = {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
475
|
+
const spawnEnv = buildManagedHookEnvironment({
|
|
476
|
+
managedAllowlist: opts.managedEnvironmentAllowlist,
|
|
477
|
+
requestedAllowlist: opts.environmentAllowlist,
|
|
478
|
+
values: {
|
|
479
|
+
CLAUDE_HOOK_EVENT: event || input.hook_event_name || "",
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
let sandboxPolicy;
|
|
483
|
+
try {
|
|
484
|
+
sandboxPolicy = resolveHookSandboxPolicy({}, opts);
|
|
485
|
+
} catch (error) {
|
|
486
|
+
return hookSpawnFailure(error);
|
|
487
|
+
}
|
|
397
488
|
const processOptions = {
|
|
398
489
|
cwd: wd,
|
|
399
490
|
env: spawnEnv,
|
|
@@ -403,6 +494,7 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
403
494
|
pluginId: opts.pluginId || null,
|
|
404
495
|
pluginVersion: opts.pluginVersion || null,
|
|
405
496
|
pluginSource: opts.pluginSource || null,
|
|
497
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
406
498
|
};
|
|
407
499
|
|
|
408
500
|
let child;
|
|
@@ -427,13 +519,7 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
427
519
|
: run(command, { ...processOptions, shell: true });
|
|
428
520
|
}
|
|
429
521
|
} catch (err) {
|
|
430
|
-
return
|
|
431
|
-
decision: HOOK_DECISIONS.CONTINUE,
|
|
432
|
-
reason: `hook spawn failed: ${err.message}`,
|
|
433
|
-
exitCode: null,
|
|
434
|
-
error: err.message,
|
|
435
|
-
nonBlockingError: true,
|
|
436
|
-
};
|
|
522
|
+
return hookSpawnFailure(err);
|
|
437
523
|
}
|
|
438
524
|
|
|
439
525
|
return await new Promise((resolve) => {
|
|
@@ -511,19 +597,23 @@ async function _runCommandHookInnerAsync(command, input = {}, opts = {}) {
|
|
|
511
597
|
function runHooks(commandHooks, input = {}, opts = {}) {
|
|
512
598
|
const results = [];
|
|
513
599
|
const hooks = commandHooks || [];
|
|
514
|
-
const runOne = (h) =>
|
|
515
|
-
|
|
600
|
+
const runOne = (h) => {
|
|
601
|
+
const sandboxPolicy = resolveHookSandboxPolicy(h, opts);
|
|
602
|
+
return runCommandHook(h.command, input, {
|
|
516
603
|
...opts,
|
|
604
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
517
605
|
broker: opts.broker,
|
|
518
606
|
origin: h.origin,
|
|
519
607
|
pluginId: h.pluginId,
|
|
520
608
|
pluginVersion: h.pluginVersion,
|
|
521
609
|
pluginSource: h.pluginSource,
|
|
522
610
|
timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
|
|
611
|
+
environmentAllowlist: h.environmentAllowlist || h.envAllowlist,
|
|
523
612
|
// per-hook shell selection (P1 #8): `{ "type":"command", "command":…,
|
|
524
613
|
// "shell":"powershell" }` in the settings hook entry
|
|
525
614
|
shell: h.shell != null ? h.shell : opts.shell,
|
|
526
615
|
});
|
|
616
|
+
};
|
|
527
617
|
|
|
528
618
|
if (opts.mergeStrict) {
|
|
529
619
|
const decisions = [];
|
|
@@ -576,18 +666,21 @@ function runHooks(commandHooks, input = {}, opts = {}) {
|
|
|
576
666
|
async function runHooksParallel(commandHooks, input = {}, opts = {}) {
|
|
577
667
|
const hooks = commandHooks || [];
|
|
578
668
|
const settled = await Promise.all(
|
|
579
|
-
hooks.map((h) =>
|
|
580
|
-
|
|
669
|
+
hooks.map((h) => {
|
|
670
|
+
const sandboxPolicy = resolveHookSandboxPolicy(h, opts);
|
|
671
|
+
return runCommandHookAsync(h.command, input, {
|
|
581
672
|
...opts,
|
|
673
|
+
...(sandboxPolicy ? { sandboxPolicy } : {}),
|
|
582
674
|
broker: opts.broker,
|
|
583
675
|
origin: h.origin,
|
|
584
676
|
pluginId: h.pluginId,
|
|
585
677
|
pluginVersion: h.pluginVersion,
|
|
586
678
|
pluginSource: h.pluginSource,
|
|
587
679
|
timeout: h.timeout != null ? h.timeout * 1000 : opts.timeout,
|
|
680
|
+
environmentAllowlist: h.environmentAllowlist || h.envAllowlist,
|
|
588
681
|
shell: h.shell != null ? h.shell : opts.shell,
|
|
589
|
-
}).then((r) => ({ h, r }))
|
|
590
|
-
),
|
|
682
|
+
}).then((r) => ({ h, r }));
|
|
683
|
+
}),
|
|
591
684
|
);
|
|
592
685
|
const results = settled.map(({ h, r }) => ({ command: h.command, ...r }));
|
|
593
686
|
const decisions = settled.map(({ h, r }) => ({
|
|
@@ -615,6 +708,7 @@ module.exports = {
|
|
|
615
708
|
HOOK_DECISIONS,
|
|
616
709
|
HOOK_PAYLOAD_SCHEMA_VERSION,
|
|
617
710
|
hookBreakerConfig,
|
|
711
|
+
resolveHookSandboxPolicy,
|
|
618
712
|
_resetHookBreaker,
|
|
619
713
|
_deps,
|
|
620
714
|
};
|
package/src/lib/hook-runner.js
CHANGED
|
@@ -35,8 +35,12 @@ function runHookProcessSync(file, argsOrOptions, maybeOptions) {
|
|
|
35
35
|
});
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
runner._deps.
|
|
38
|
+
export function _restoreProcessRunners() {
|
|
39
|
+
runner._deps.run = runHookProcess;
|
|
40
|
+
runner._deps.runSync = runHookProcessSync;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_restoreProcessRunners();
|
|
40
44
|
|
|
41
45
|
export const {
|
|
42
46
|
runCommandHook,
|
|
@@ -48,6 +52,7 @@ export const {
|
|
|
48
52
|
HOOK_DECISIONS,
|
|
49
53
|
HOOK_PAYLOAD_SCHEMA_VERSION,
|
|
50
54
|
hookBreakerConfig,
|
|
55
|
+
resolveHookSandboxPolicy,
|
|
51
56
|
_resetHookBreaker,
|
|
52
57
|
_deps,
|
|
53
58
|
} = runner;
|
|
@@ -38,3 +38,39 @@ export async function executeHooksV2Event(
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
export function resolvePromptExpansion(prompt, outcome = {}) {
|
|
43
|
+
const base = String(prompt ?? "");
|
|
44
|
+
const updates = [];
|
|
45
|
+
const context = [];
|
|
46
|
+
for (const record of Array.isArray(outcome.results) ? outcome.results : []) {
|
|
47
|
+
if (record?.status !== "success" || !record.result) continue;
|
|
48
|
+
if (typeof record.result.updatedPrompt === "string") {
|
|
49
|
+
updates.push(record.result.updatedPrompt);
|
|
50
|
+
}
|
|
51
|
+
if (typeof record.result.additionalContext === "string") {
|
|
52
|
+
const value = record.result.additionalContext.trim();
|
|
53
|
+
if (value) context.push(value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const distinctUpdates = [...new Set(updates)];
|
|
57
|
+
if (distinctUpdates.length > 1) {
|
|
58
|
+
return {
|
|
59
|
+
prompt: base,
|
|
60
|
+
blocked: true,
|
|
61
|
+
reason: "conflicting UserPromptExpansion updatedPrompt results",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
let resolved = distinctUpdates[0] ?? base;
|
|
65
|
+
if (context.length > 0) {
|
|
66
|
+
resolved += `\n\n[hook context]\n${context.join("\n")}`;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
prompt: resolved,
|
|
70
|
+
blocked: outcome.blocked === true || outcome.decision === "block",
|
|
71
|
+
reason:
|
|
72
|
+
outcome.blockingResult?.reason ||
|
|
73
|
+
outcome.blockingResult?.message ||
|
|
74
|
+
null,
|
|
75
|
+
};
|
|
76
|
+
}
|