chainlesschain 0.162.148 → 0.162.149
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/README.md +1 -1
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AIOps-DwpCCp64.js → AIOps-BJif14yR.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-CXek6gJY.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-BgQhdAJi.js} +2 -2
- package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-DTYl5NtR.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-BzATQAeR.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-BzdPEQhL.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-BM0KVh8E.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-D7fHXHkz.js} +4 -4
- package/src/assets/web-panel/assets/ChatBubbleRenderer-Wm34RR-b.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CAiSQ9vO.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-Df40CrEe.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-DIT2fNFU.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-CSTIbW2k.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-DtqXZZUi.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-CUtzw6o7.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-CezmlnmU.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-P4BITarg.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-BRRiYwvq.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-L5ijB9i4.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard-CFxro4ob.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BmAFCQ71.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-DJBCEuon.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-z4oLq6eT.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-DsVCpHMF.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DP3bP5th.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-BAXP2Gc6.js} +1 -1
- package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-I3g_bAw9.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-jHS5-ioS.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-CPL7sfx9.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-Jf236h4C.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-Cm2tcSKg.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-CZK82rhi.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-CZCeji7a.js} +4 -4
- package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-3l2KVS9k.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-VYdpoLh6.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-CiB9cmm1.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-wCFZbBuL.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-1a3THIyG.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-DMzLB2hG.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-D9wZbf6l.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-hrFe2ZM-.js} +3 -3
- package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-CHGX5Jwm.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +1 -0
- package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-By7FkhtY.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow--khGSzJn.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-CAG9dH35.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-B3SQZmK9.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-D35ec6JA.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CxBF7hW_.js} +2 -2
- package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-s6EtOO1s.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-BbJYmWmO.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-VGQq1jMT.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-B1ew3Teh.js} +2 -2
- package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +1 -0
- package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-mrO47FIK.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-IJF2mHyw.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CU0wN_Z0.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-DhN37R6G.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-D_xOX_hu.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-D_Ssv7uH.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-Kx5dKxX7.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-BlGwqZkY.js} +2 -2
- package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-hLFQzxpb.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DMlvsVt4.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-Bd0Qsj2m.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-DFclq9X3.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-BNtln2qY.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-Dch5H19G.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-2UfQcU1g.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-CD9YQr4i.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-BNu-7MzI.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-D1n9CaIR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-DenL3iBW.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-D87IfukO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BkiZc8E3.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-BNRSytGN.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-Bwbd4X2m.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-B8O2bs0o.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-C2fL8zmH.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-BORYDdE1.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-C0V5S2FM.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-2dR0fcHL.js} +1 -1
- package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-BmuAFAn8.js} +1 -1
- package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-CN3smMcK.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-DCnxb4kW.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-vlR43pHn.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-D04tHYFd.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-0DnaZGkC.js} +1 -1
- package/src/assets/web-panel/assets/{index-308gCGh7.js → index-430S68MN.js} +1 -1
- package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-B78xL3s2.js} +1 -1
- package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BTPEZTk1.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-BXRg8GFQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-BasvsWDM.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-BeGDEXFs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-BqhASEL4.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-BudvKQfG.js} +1 -1
- package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-C-6NO5il.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-C5Sxb1sE.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-CP9m9Ggr.js} +3 -3
- package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-CTOCnIQ7.js} +1 -1
- package/src/assets/web-panel/assets/index-Cg2ldRfU.js +1 -0
- package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-ClhAHDK3.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-CnXebZLL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-CngCC8hC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-Cr-A0FYJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-CsAsCPJ6.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-D28DeAAB.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-D5LZVZ0L.js} +1 -1
- package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-D65_XseV.js} +1 -1
- package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-D8vwp4qp.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-DDwLgQY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-DIpY0qM5.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-DTWg-18U.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-DTtRscUa.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-DYAtmqiv.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-D_n8_vfI.js} +1 -1
- package/src/assets/web-panel/assets/{index-CahryTX0.js → index-DbADHwhr.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-DbVsQBOH.js} +1 -1
- package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +1 -0
- package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-HhNjnCfe.js} +1 -1
- package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-ToUh2b1-.js} +1 -1
- package/src/assets/web-panel/assets/{index-DISWAYce.js → index-TslMTwNy.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-_FIkN3jd.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-hpvvtAZ3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-jzR7Ujuw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-ljuBiQW9.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-C3wUb4je.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D-jYFUNA.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-tqb8nwJ2.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DSOjMJMz.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-CJ7VimIW.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-qXiG1iT3.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-CkGBcy1Z.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-D3WvTlLO.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BEE2ftge.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-CsfBpxiG.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-xPrIiJPI.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-C5Yd-SCR.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-D_MDS8HI.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub-BqJh-usA.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-chm_p-gz.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-3-YDNz0Z.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/assets/web-panel/remote-session-sw.js +43 -0
- package/src/command-manifest.json +22 -1
- package/src/commands/agent.js +44 -5
- package/src/commands/codeintel.js +252 -0
- package/src/commands/eval.js +248 -0
- package/src/commands/plugin.js +354 -0
- package/src/commands/team.js +456 -0
- package/src/gateways/ws/message-dispatcher.js +45 -2
- package/src/gateways/ws/remote-session-protocol.js +37 -0
- package/src/gateways/ws/ws-server.js +1 -1
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +158 -0
- package/src/harness/remote-session-push-apns.js +246 -0
- package/src/harness/remote-session-push-errors.js +15 -0
- package/src/harness/remote-session-push-fcm.js +9 -25
- package/src/harness/remote-session-push-huawei.js +174 -0
- package/src/harness/remote-session-push-oppo.js +171 -0
- package/src/harness/remote-session-push-senders.js +42 -0
- package/src/harness/remote-session-push-vivo.js +179 -0
- package/src/harness/remote-session-push-web.js +299 -0
- package/src/harness/remote-session-push-xiaomi.js +99 -0
- package/src/index.js +6 -0
- package/src/lib/agent-core.js +2 -0
- package/src/lib/agent-sandbox.js +161 -5
- package/src/lib/agent-team/task-lease.js +434 -0
- package/src/lib/agent-team/team-budget.js +165 -0
- package/src/lib/agent-team/team-mailbox.js +106 -0
- package/src/lib/agent-team/team-runner.js +266 -0
- package/src/lib/agent-team/team-worktree.js +204 -0
- package/src/lib/agents.js +18 -0
- package/src/lib/async-hook-supervisor.cjs +305 -0
- package/src/lib/eval/runner.js +163 -0
- package/src/lib/eval/tasks.js +456 -0
- package/src/lib/eval/trend.js +185 -0
- package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
- package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
- package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
- package/src/lib/lsp/__tests__/lsp-client.test.js +258 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +264 -0
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
- package/src/lib/lsp/code-intelligence.js +356 -0
- package/src/lib/lsp/jsonrpc-stream.js +139 -0
- package/src/lib/lsp/lsp-client.js +318 -0
- package/src/lib/lsp/lsp-manager.js +323 -0
- package/src/lib/lsp/lsp-server-registry.js +196 -0
- package/src/lib/plugin-monitor-supervisor.js +238 -0
- package/src/lib/plugin-runtime/agents.js +51 -0
- package/src/lib/plugin-runtime/bin.js +100 -0
- package/src/lib/plugin-runtime/hooks.js +100 -0
- package/src/lib/plugin-runtime/install.js +388 -0
- package/src/lib/plugin-runtime/lsp.js +75 -0
- package/src/lib/plugin-runtime/manifest.js +458 -0
- package/src/lib/plugin-runtime/mcp.js +89 -0
- package/src/lib/plugin-runtime/monitors.js +100 -0
- package/src/lib/plugin-runtime/policy.js +144 -0
- package/src/lib/plugin-runtime/reload.js +79 -0
- package/src/lib/plugin-runtime/scopes.js +197 -0
- package/src/lib/plugin-runtime/settings.js +119 -0
- package/src/lib/plugin-runtime/signature.js +107 -0
- package/src/lib/plugin-runtime/skills.js +43 -0
- package/src/lib/plugin-runtime/trust.js +140 -0
- package/src/lib/sandbox-egress-proxy.js +162 -0
- package/src/lib/sandbox-network-policy.js +134 -0
- package/src/lib/sandbox-v2.js +10 -4
- package/src/lib/settings-hook-events.cjs +78 -6
- package/src/lib/settings-hooks.cjs +29 -3
- package/src/lib/settings-loader.cjs +35 -1
- package/src/lib/skill-loader.js +18 -0
- package/src/lib/telemetry/span-recorder.js +237 -0
- package/src/repl/agent-repl.js +400 -4
- package/src/runtime/agent-core.js +546 -52
- package/src/runtime/coding-agent-contract-shared.cjs +82 -8
- package/src/runtime/coding-agent-policy.cjs +32 -7
- package/src/runtime/fallback-model.js +134 -7
- package/src/runtime/headless-runner.js +353 -108
- package/src/runtime/mcp-config.js +46 -0
- package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-DIp8Xcbd.js +0 -1
- package/src/assets/web-panel/assets/Projects-Dpid9CDg.js +0 -1
- package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
- package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
- package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
- package/src/assets/web-panel/assets/index-BXiw9dQf.js +0 -1
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* async-hook-supervisor — run `async: true` settings hooks FIRE-AND-FORGET so a
|
|
5
|
+
* long-running hook (a test suite, a log scan, an external status probe) never
|
|
6
|
+
* blocks the agent's tool chain. The sync path (hook-runner.cjs `runHooks`) uses
|
|
7
|
+
* `spawnSync` and gates the turn; this path uses `spawn` and returns
|
|
8
|
+
* immediately, collecting each hook's result in the background.
|
|
9
|
+
*
|
|
10
|
+
* Two consumption modes on the collected results:
|
|
11
|
+
* - drainResults() → every completed hook since the last drain, injected into
|
|
12
|
+
* the NEXT turn's additionalContext (like the monitor
|
|
13
|
+
* supervisor). Cleared on read.
|
|
14
|
+
* - drainRewakes() → the subset whose hook set `asyncRewake: true` AND that
|
|
15
|
+
* finished in a FAILURE state (non-zero / blocked /
|
|
16
|
+
* timed-out). A pending rewake is the signal to proactively
|
|
17
|
+
* re-engage the agent with the structured error, rather
|
|
18
|
+
* than waiting for the user's next prompt.
|
|
19
|
+
*
|
|
20
|
+
* Guardrails (parity with PluginMonitorSupervisor, and the Phase 6 acceptance
|
|
21
|
+
* "高频文件写入不会造成无限 Hook 并发"):
|
|
22
|
+
* - per-(event+command) dedupe while a prior run of the same hook is still in
|
|
23
|
+
* flight — a rapid PostToolUse hook can't stack N copies of itself;
|
|
24
|
+
* - a maxConcurrent cap — overflow is DROPPED with a recorded skip, never
|
|
25
|
+
* queued unbounded;
|
|
26
|
+
* - a per-hook timeout that kills the child;
|
|
27
|
+
* - reaping of every child on stopAll() / process 'exit', so a session never
|
|
28
|
+
* leaves an orphan hook process.
|
|
29
|
+
*
|
|
30
|
+
* `_deps.spawn` / `_deps.now` are injected so the whole supervisor is unit-
|
|
31
|
+
* testable with fake child processes (no real shell).
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const cpDefault = require("node:child_process");
|
|
35
|
+
const { tryParseDecision } = require("./hook-runner.cjs");
|
|
36
|
+
|
|
37
|
+
const DEFAULT_TIMEOUT_MS = 60000;
|
|
38
|
+
const MAX_TIMEOUT_MS = 600000;
|
|
39
|
+
const DEFAULT_MAX_CONCURRENT = 4;
|
|
40
|
+
|
|
41
|
+
class AsyncHookSupervisor {
|
|
42
|
+
constructor(opts = {}) {
|
|
43
|
+
this.maxConcurrent =
|
|
44
|
+
Number(opts.maxConcurrent) > 0
|
|
45
|
+
? Number(opts.maxConcurrent)
|
|
46
|
+
: DEFAULT_MAX_CONCURRENT;
|
|
47
|
+
this._deps = {
|
|
48
|
+
spawn: opts.spawn || cpDefault.spawn,
|
|
49
|
+
now: opts.now || (() => Date.now()),
|
|
50
|
+
};
|
|
51
|
+
this._running = new Map(); // key -> { child, timer }
|
|
52
|
+
this._results = []; // completed records awaiting drainResults()
|
|
53
|
+
this._rewakes = []; // failed asyncRewake records awaiting drainRewakes()
|
|
54
|
+
this._stopped = false;
|
|
55
|
+
this._exitHook = () => this.stopAll();
|
|
56
|
+
process.once("exit", this._exitHook);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_key(hook) {
|
|
60
|
+
return `${hook.event || ""}::${hook.command}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Dispatch a batch of async hooks fire-and-forget. Returns IMMEDIATELY with a
|
|
65
|
+
* per-hook disposition (`{command, dispatched, reason?}`) — it never awaits a
|
|
66
|
+
* hook. `payload` is JSON-written to each hook's stdin (same protocol as the
|
|
67
|
+
* sync runner).
|
|
68
|
+
*
|
|
69
|
+
* @param {Array<{command:string, event?:string, timeout?:number,
|
|
70
|
+
* asyncRewake?:boolean}>} hooks
|
|
71
|
+
* @param {object} payload the hook event payload (hook_event_name, …)
|
|
72
|
+
* @param {object} [opts] { cwd, timeout } (per-hook timeout wins)
|
|
73
|
+
*/
|
|
74
|
+
dispatch(hooks, payload = {}, opts = {}) {
|
|
75
|
+
const dispositions = [];
|
|
76
|
+
for (const hook of hooks || []) {
|
|
77
|
+
if (!hook || !hook.command) continue;
|
|
78
|
+
if (this._stopped) {
|
|
79
|
+
dispositions.push({
|
|
80
|
+
command: hook.command,
|
|
81
|
+
dispatched: false,
|
|
82
|
+
reason: "supervisor stopped",
|
|
83
|
+
});
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const key = this._key(hook);
|
|
87
|
+
if (this._running.has(key)) {
|
|
88
|
+
dispositions.push({
|
|
89
|
+
command: hook.command,
|
|
90
|
+
dispatched: false,
|
|
91
|
+
reason: "already running (deduped)",
|
|
92
|
+
});
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (this._running.size >= this.maxConcurrent) {
|
|
96
|
+
dispositions.push({
|
|
97
|
+
command: hook.command,
|
|
98
|
+
dispatched: false,
|
|
99
|
+
reason: "max concurrent reached",
|
|
100
|
+
});
|
|
101
|
+
// Record the skip so the caller/agent can SEE that a hook was dropped
|
|
102
|
+
// (silent truncation would read as "the hook ran and said nothing").
|
|
103
|
+
this._results.push({
|
|
104
|
+
command: hook.command,
|
|
105
|
+
event: hook.event || null,
|
|
106
|
+
ok: false,
|
|
107
|
+
skipped: true,
|
|
108
|
+
exitCode: null,
|
|
109
|
+
error: "async hook skipped: max concurrent reached",
|
|
110
|
+
additionalContext: null,
|
|
111
|
+
blocked: false,
|
|
112
|
+
ts: this._deps.now(),
|
|
113
|
+
ms: 0,
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
this._spawnOne(hook, payload, opts);
|
|
118
|
+
dispositions.push({ command: hook.command, dispatched: true });
|
|
119
|
+
}
|
|
120
|
+
return dispositions;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_spawnOne(hook, payload, opts) {
|
|
124
|
+
const key = this._key(hook);
|
|
125
|
+
const started = this._deps.now();
|
|
126
|
+
const rawTimeout = Number(
|
|
127
|
+
hook.timeout != null ? hook.timeout * 1000 : opts.timeout,
|
|
128
|
+
);
|
|
129
|
+
const timeout =
|
|
130
|
+
Number.isFinite(rawTimeout) && rawTimeout > 0
|
|
131
|
+
? Math.min(rawTimeout, MAX_TIMEOUT_MS)
|
|
132
|
+
: DEFAULT_TIMEOUT_MS;
|
|
133
|
+
|
|
134
|
+
let child;
|
|
135
|
+
try {
|
|
136
|
+
child = this._deps.spawn(hook.command, {
|
|
137
|
+
cwd: opts.cwd || process.cwd(),
|
|
138
|
+
shell: true,
|
|
139
|
+
env: {
|
|
140
|
+
...process.env,
|
|
141
|
+
CLAUDE_HOOK_EVENT: hook.event || payload.hook_event_name || "",
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
} catch (err) {
|
|
145
|
+
this._record(hook, {
|
|
146
|
+
ok: false,
|
|
147
|
+
exitCode: null,
|
|
148
|
+
error: `hook spawn failed: ${err.message}`,
|
|
149
|
+
started,
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let out = "";
|
|
155
|
+
let errOut = "";
|
|
156
|
+
let killedByTimeout = false;
|
|
157
|
+
const rec = { child, timer: null };
|
|
158
|
+
this._running.set(key, rec);
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
child.stdin?.end(JSON.stringify(payload));
|
|
162
|
+
} catch {
|
|
163
|
+
/* stdin may already be closed — non-fatal */
|
|
164
|
+
}
|
|
165
|
+
child.stdout?.on("data", (d) => {
|
|
166
|
+
out += d.toString("utf8");
|
|
167
|
+
});
|
|
168
|
+
child.stderr?.on("data", (d) => {
|
|
169
|
+
errOut += d.toString("utf8");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const timer = setTimeout(() => {
|
|
173
|
+
killedByTimeout = true;
|
|
174
|
+
try {
|
|
175
|
+
child.kill("SIGTERM");
|
|
176
|
+
} catch {
|
|
177
|
+
/* already gone */
|
|
178
|
+
}
|
|
179
|
+
}, timeout);
|
|
180
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
181
|
+
rec.timer = timer;
|
|
182
|
+
|
|
183
|
+
let settled = false;
|
|
184
|
+
const finalize = (exitCode) => {
|
|
185
|
+
if (settled) return;
|
|
186
|
+
settled = true;
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
this._running.delete(key);
|
|
189
|
+
const parsed = tryParseDecision(out);
|
|
190
|
+
const blocked = !!(parsed && parsed.decision === "block");
|
|
191
|
+
const ok = !killedByTimeout && exitCode === 0 && !blocked;
|
|
192
|
+
const additionalContext =
|
|
193
|
+
(parsed && parsed.additionalContext) ||
|
|
194
|
+
(exitCode === 0 && out.trim() && out.trim()[0] !== "{"
|
|
195
|
+
? out.trim()
|
|
196
|
+
: null);
|
|
197
|
+
this._record(hook, {
|
|
198
|
+
ok,
|
|
199
|
+
exitCode,
|
|
200
|
+
started,
|
|
201
|
+
blocked,
|
|
202
|
+
additionalContext,
|
|
203
|
+
error: killedByTimeout
|
|
204
|
+
? `timed out after ${timeout}ms`
|
|
205
|
+
: ok
|
|
206
|
+
? null
|
|
207
|
+
: errOut.trim() ||
|
|
208
|
+
(blocked
|
|
209
|
+
? parsed.reason || "hook requested block"
|
|
210
|
+
: `hook exited ${exitCode}`),
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
child.on("error", (e) => {
|
|
215
|
+
if (settled) return;
|
|
216
|
+
settled = true;
|
|
217
|
+
clearTimeout(timer);
|
|
218
|
+
this._running.delete(key);
|
|
219
|
+
this._record(hook, {
|
|
220
|
+
ok: false,
|
|
221
|
+
exitCode: null,
|
|
222
|
+
error: `hook error: ${e.message}`,
|
|
223
|
+
started,
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
child.on("close", (code) => finalize(code));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
_record(hook, fields) {
|
|
230
|
+
const now = this._deps.now();
|
|
231
|
+
const rec = {
|
|
232
|
+
command: hook.command,
|
|
233
|
+
event: hook.event || null,
|
|
234
|
+
ok: fields.ok === true,
|
|
235
|
+
exitCode: fields.exitCode ?? null,
|
|
236
|
+
error: fields.error || null,
|
|
237
|
+
additionalContext: fields.additionalContext || null,
|
|
238
|
+
blocked: !!fields.blocked,
|
|
239
|
+
asyncRewake: hook.asyncRewake === true,
|
|
240
|
+
ts: now,
|
|
241
|
+
ms: fields.started != null ? now - fields.started : 0,
|
|
242
|
+
};
|
|
243
|
+
this._results.push(rec);
|
|
244
|
+
// A rewake fires only for a hook that OPTED IN and finished in a failure
|
|
245
|
+
// state — a passing async check should not re-engage the agent.
|
|
246
|
+
if (hook.asyncRewake && !rec.ok) {
|
|
247
|
+
this._rewakes.push(rec);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Completed result records since the last drain (cleared on read). */
|
|
252
|
+
drainResults() {
|
|
253
|
+
const r = this._results;
|
|
254
|
+
this._results = [];
|
|
255
|
+
return r;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
peekResults() {
|
|
259
|
+
return this._results.slice();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Failed asyncRewake records since the last drain (cleared on read). */
|
|
263
|
+
drainRewakes() {
|
|
264
|
+
const r = this._rewakes;
|
|
265
|
+
this._rewakes = [];
|
|
266
|
+
return r;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
hasRewake() {
|
|
270
|
+
return this._rewakes.length > 0;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
runningCount() {
|
|
274
|
+
return this._running.size;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/** Kill every in-flight hook and detach the exit reaper. Idempotent. */
|
|
278
|
+
stopAll() {
|
|
279
|
+
this._stopped = true;
|
|
280
|
+
for (const [key, rec] of this._running) {
|
|
281
|
+
try {
|
|
282
|
+
clearTimeout(rec.timer);
|
|
283
|
+
} catch {
|
|
284
|
+
/* ignore */
|
|
285
|
+
}
|
|
286
|
+
try {
|
|
287
|
+
rec.child.kill("SIGTERM");
|
|
288
|
+
} catch {
|
|
289
|
+
/* already gone */
|
|
290
|
+
}
|
|
291
|
+
this._running.delete(key);
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
process.removeListener("exit", this._exitHook);
|
|
295
|
+
} catch {
|
|
296
|
+
/* ignore */
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
module.exports = {
|
|
302
|
+
AsyncHookSupervisor,
|
|
303
|
+
DEFAULT_TIMEOUT_MS,
|
|
304
|
+
DEFAULT_MAX_CONCURRENT,
|
|
305
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reliability eval harness (Phase 7) — measure the agent's real task-success
|
|
3
|
+
* rate, not command coverage.
|
|
4
|
+
*
|
|
5
|
+
* An eval task is a self-checking coding problem:
|
|
6
|
+
* { id, description, prompt, setup(dir), check(dir) → {pass, detail}, timeoutMs? }
|
|
7
|
+
*
|
|
8
|
+
* runEvalSuite gives each task a fresh temp workspace, runs `setup` to lay down
|
|
9
|
+
* the starting files, hands `prompt` to the agent (via an injected `runAgent` so
|
|
10
|
+
* the harness is testable without a live LLM), then runs `check` to decide
|
|
11
|
+
* pass/fail deterministically. The score is the fraction of tasks whose check
|
|
12
|
+
* passed — a verifiable success-rate metric, per the plan's north star.
|
|
13
|
+
*
|
|
14
|
+
* The real `cc eval` command wires `runAgent` to a headless `cc agent -p` run;
|
|
15
|
+
* unit tests inject a fake runAgent to exercise scoring/report logic offline.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import os from "os";
|
|
20
|
+
import path from "path";
|
|
21
|
+
|
|
22
|
+
export const _deps = {
|
|
23
|
+
mkdtempSync: fs.mkdtempSync,
|
|
24
|
+
rmSync: fs.rmSync,
|
|
25
|
+
now: () => Date.now(),
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run a suite of eval tasks and score them.
|
|
30
|
+
*
|
|
31
|
+
* @param {Array<object>} tasks
|
|
32
|
+
* @param {object} opts
|
|
33
|
+
* runAgent async ({prompt, cwd, timeoutMs}) => { ok?, output?, error? }
|
|
34
|
+
* cwdBase optional base dir for the per-task temp workspaces
|
|
35
|
+
* keepWorkspaces don't delete temp dirs (debugging)
|
|
36
|
+
* onResult optional (result) => void progress callback
|
|
37
|
+
* @returns {Promise<{results, passed, failed, total, passRate, ms}>}
|
|
38
|
+
*/
|
|
39
|
+
export async function runEvalSuite(tasks, opts = {}) {
|
|
40
|
+
if (!Array.isArray(tasks)) throw new Error("tasks must be an array");
|
|
41
|
+
if (typeof opts.runAgent !== "function") {
|
|
42
|
+
throw new Error("opts.runAgent is required");
|
|
43
|
+
}
|
|
44
|
+
const runAgent = opts.runAgent;
|
|
45
|
+
const base = opts.cwdBase || os.tmpdir();
|
|
46
|
+
const results = [];
|
|
47
|
+
const suiteStart = _deps.now();
|
|
48
|
+
|
|
49
|
+
const recorder = opts.recorder || null;
|
|
50
|
+
for (const task of tasks) {
|
|
51
|
+
const started = _deps.now();
|
|
52
|
+
let dir = null;
|
|
53
|
+
const rec = {
|
|
54
|
+
id: task?.id || "(unnamed)",
|
|
55
|
+
description: task?.description || "",
|
|
56
|
+
pass: false,
|
|
57
|
+
ms: 0,
|
|
58
|
+
detail: "",
|
|
59
|
+
error: null,
|
|
60
|
+
};
|
|
61
|
+
// One telemetry span per task (duration + pass/fail + failure category),
|
|
62
|
+
// feeding the OTel-shaped recorder when the caller supplies one.
|
|
63
|
+
const span = recorder
|
|
64
|
+
? recorder.startSpan("eval.task", { "task.id": rec.id })
|
|
65
|
+
: null;
|
|
66
|
+
try {
|
|
67
|
+
if (!task || typeof task.id !== "string") {
|
|
68
|
+
throw new Error("task.id is required");
|
|
69
|
+
}
|
|
70
|
+
if (typeof task.prompt !== "string" || !task.prompt) {
|
|
71
|
+
throw new Error(`task "${rec.id}": prompt is required`);
|
|
72
|
+
}
|
|
73
|
+
if (typeof task.check !== "function") {
|
|
74
|
+
throw new Error(`task "${rec.id}": check() is required`);
|
|
75
|
+
}
|
|
76
|
+
dir = _deps.mkdtempSync(path.join(base, `cc-eval-${task.id}-`));
|
|
77
|
+
if (typeof task.setup === "function") {
|
|
78
|
+
await task.setup(dir);
|
|
79
|
+
}
|
|
80
|
+
let agentResult;
|
|
81
|
+
try {
|
|
82
|
+
agentResult = await runAgent({
|
|
83
|
+
prompt: task.prompt,
|
|
84
|
+
cwd: dir,
|
|
85
|
+
timeoutMs: task.timeoutMs,
|
|
86
|
+
});
|
|
87
|
+
} catch (agentErr) {
|
|
88
|
+
// An agent crash is a task failure, not a harness failure — record it
|
|
89
|
+
// and still run check() (the workspace may be partially done).
|
|
90
|
+
rec.error = `agent error: ${agentErr.message}`;
|
|
91
|
+
}
|
|
92
|
+
rec.agentOk = agentResult ? agentResult.ok !== false : false;
|
|
93
|
+
const verdict = await task.check(dir, agentResult);
|
|
94
|
+
rec.pass = verdict?.pass === true;
|
|
95
|
+
rec.detail = verdict?.detail || "";
|
|
96
|
+
} catch (err) {
|
|
97
|
+
// A harness-level error (bad task def / setup threw) — mark failed.
|
|
98
|
+
rec.error = rec.error || err.message;
|
|
99
|
+
rec.pass = false;
|
|
100
|
+
} finally {
|
|
101
|
+
rec.ms = _deps.now() - started;
|
|
102
|
+
if (span) {
|
|
103
|
+
span.setAttribute("pass", rec.pass);
|
|
104
|
+
if (!rec.pass) {
|
|
105
|
+
// Classify the failure: an agent crash vs a wrong-answer (check
|
|
106
|
+
// failed) — the plan's "失败分类".
|
|
107
|
+
const category = rec.error
|
|
108
|
+
? rec.error.startsWith("agent error")
|
|
109
|
+
? "agent_error"
|
|
110
|
+
: "harness_error"
|
|
111
|
+
: "check_failed";
|
|
112
|
+
span.recordException(
|
|
113
|
+
new Error(rec.error || rec.detail || "task failed"),
|
|
114
|
+
category,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
span.end();
|
|
118
|
+
}
|
|
119
|
+
if (dir && !opts.keepWorkspaces) {
|
|
120
|
+
try {
|
|
121
|
+
_deps.rmSync(dir, { recursive: true, force: true });
|
|
122
|
+
} catch {
|
|
123
|
+
/* best-effort cleanup */
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
results.push(rec);
|
|
128
|
+
if (typeof opts.onResult === "function") {
|
|
129
|
+
try {
|
|
130
|
+
opts.onResult(rec);
|
|
131
|
+
} catch {
|
|
132
|
+
/* progress callback is best-effort */
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const passed = results.filter((r) => r.pass).length;
|
|
138
|
+
const total = results.length;
|
|
139
|
+
return {
|
|
140
|
+
results,
|
|
141
|
+
passed,
|
|
142
|
+
failed: total - passed,
|
|
143
|
+
total,
|
|
144
|
+
passRate: total > 0 ? passed / total : 0,
|
|
145
|
+
ms: _deps.now() - suiteStart,
|
|
146
|
+
...(recorder ? { telemetry: recorder.summary() } : {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Render a suite summary as a compact text report. */
|
|
151
|
+
export function formatEvalReport(summary) {
|
|
152
|
+
const lines = [];
|
|
153
|
+
const pct = (summary.passRate * 100).toFixed(1);
|
|
154
|
+
lines.push(
|
|
155
|
+
`Eval: ${summary.passed}/${summary.total} passed (${pct}%) in ${summary.ms}ms`,
|
|
156
|
+
);
|
|
157
|
+
for (const r of summary.results) {
|
|
158
|
+
const mark = r.pass ? "✔" : "✗";
|
|
159
|
+
const detail = r.error ? ` — ${r.error}` : r.detail ? ` — ${r.detail}` : "";
|
|
160
|
+
lines.push(` ${mark} ${r.id} (${r.ms}ms)${detail}`);
|
|
161
|
+
}
|
|
162
|
+
return lines.join("\n");
|
|
163
|
+
}
|