chainlesschain 0.162.148 → 0.162.150
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-CBLvrjeG.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-DYv2GtE0.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-CL6PZ-Ww.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-xuyHt4uA.js} +3 -3
- package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-DOvSVycq.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-DMhfTswr.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-CQxDdm63.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-BEeNhK2a.js} +5 -5
- package/src/assets/web-panel/assets/ChatBubbleRenderer-8nul8eTq.js +1 -0
- package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CL04O-ER.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-C50o-n7x.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-CH-bDkzs.js} +1 -1
- package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-DqAIMvSe.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-C43fo_my.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-BERnMrdP.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-Baw3w1gp.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-BEqAHF1-.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-CvnmKwK4.js} +1 -1
- package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-iMPxgVdt.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard--4SJX3YR.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BgLV6xgz.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-Bvb8oUD8.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-DpXPjJB5.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-BbFfJ1Xi.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DeGq1B-q.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-CNW7s9zM.js} +1 -1
- package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-L1PjW-lT.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-PK-X58to.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-BePWEH-d.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-LF_sBvdL.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-BKkfk9hy.js} +2 -2
- package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-BNMqUDla.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-w4WPiyz2.js} +5 -5
- package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-DUKMVGNi.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-BWhiEyTJ.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-Ocf5JEkX.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-D2B6MMhU.js} +4 -4
- package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-DdYjCq1O.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-Cb1hfuUZ.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-CTeImGp7.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-BjYNc7eQ.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-Dn_NtRXQ.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-DIp8Xcbd.js → OrderTableRenderer-DVYYIizh.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-Be2Kmr5j.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow-Cu_-qRlR.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-DD2HoGzE.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-BRI7DVth.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-C9Srcs2M.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CUqcMgo1.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-HBrQGLXt.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-D-B0vjwh.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-eTeLIu9e.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-DrRqCsC6.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-Dpid9CDg.js → Projects-1G9DNOhI.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-RkTZzpxP.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-BSCUgsgC.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CHgyM3Bz.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-0lry4OZq.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-Cn45UroZ.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-Q-Zjb707.js} +1 -1
- package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-BdM70oda.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-DmOAKKap.js} +2 -2
- package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-Dwavbm07.js} +1 -1
- package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DukXFCnk.js} +3 -3
- package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-BIlRnITu.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-YYVQdhhQ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-B8_iYHz2.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-BdVrhT31.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-CD3ggRx7.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-Bp02VZFH.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-DEVmCEsr.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-B6czWc4N.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-BCJLv17r.js} +1 -1
- package/src/assets/web-panel/assets/Terminal-CD132d2Y.js +3 -0
- package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-ZB-CvkZl.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BnSY0S-s.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-VZw9Oy2w.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-pZq7Hjd2.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-DnQaqk4q.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-BEUfmvJV.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-CoZKMXJ2.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-CBOGVx1I.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-huSKn7TT.js} +1 -1
- package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-A_3w6FBO.js} +1 -1
- package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-D1Bem9Oy.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-CJYJj0oO.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-BkbFiKT4.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-WDr3_M_N.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-CKvUafVY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-AjrSPnvv.js} +1 -1
- package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-B8i0lViZ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-B9Svn0zc.js} +1 -1
- package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-BHdHSX06.js} +1 -1
- package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BHdailyo.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-BJwlEnYo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-BOoAQjJz.js} +1 -1
- package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-BSgWxAOG.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-BdutMsne.js} +1 -1
- package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-BiDWxzbn.js} +1 -1
- package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-By5a0T_W.js} +1 -1
- package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-C-WIY-FQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-C37f6iey.js} +1 -1
- package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-C9bWmTcO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-CBaosWRO.js} +1 -1
- package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-CBlBVJ_m.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-CD62EEGo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-CUj-l7GM.js} +1 -1
- package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-CVS8Ymuq.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-ChkXUj3f.js} +1 -1
- package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-Cj0OZ-37.js} +1 -1
- package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-Co3OFL0t.js} +1 -1
- package/src/assets/web-panel/assets/{index-308gCGh7.js → index-Crp8CbRg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-D22zNXiS.js} +1 -1
- package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-DTs_D1OL.js} +1 -1
- package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-DfgCdFrN.js} +3 -3
- package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-DjYrecNb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-DpW9cf3e.js} +1 -1
- package/src/assets/web-panel/assets/index-Dwix3p4g.js +1 -0
- package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-F--HuPG1.js} +1 -1
- package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-Fuk6t_3K.js} +1 -1
- package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-JEAGMii9.js} +1 -1
- package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-PF-mpv8K.js} +1 -1
- package/src/assets/web-panel/assets/index-SHaoZ0CA.js +1 -0
- package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-b-sbVdjQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-pyvMVX8r.js} +1 -1
- package/src/assets/web-panel/assets/{index-DISWAYce.js → index-qNTtOVi_.js} +1 -1
- package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-tgO8iza6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CahryTX0.js → index-ttDQVhZy.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-DtrnfsZH.js} +1 -1
- package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D4zqSaX3.js} +1 -1
- package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-CGFGOUQj.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DH2HvkJl.js} +1 -1
- package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-jJ2at5HX.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-CRyMT1Fv.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-DZ_CX0Pt.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-wruHk-h5.js} +1 -1
- package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BGgx8q67.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-Do5M7gKm.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-CRa-GZpX.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-BxPcq6pj.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-v5O2ZcJm.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub--z2FU0Px.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-1emidVKd.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-CcJaRyHs.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/a2a.js +19 -4
- package/src/commands/activitypub.js +20 -3
- package/src/commands/agent.js +71 -10
- package/src/commands/audit.js +40 -24
- package/src/commands/codeintel.js +320 -0
- package/src/commands/collab.js +15 -2
- package/src/commands/compliance.js +5 -0
- package/src/commands/config.js +4 -1
- package/src/commands/dao.js +86 -14
- package/src/commands/dev.js +2 -0
- package/src/commands/did.js +7 -1
- package/src/commands/dlp.js +23 -1
- package/src/commands/economy.js +29 -3
- package/src/commands/eval.js +255 -0
- package/src/commands/evomap.js +26 -0
- package/src/commands/governance.js +17 -3
- package/src/commands/hardening.js +18 -0
- package/src/commands/incentive.js +5 -0
- package/src/commands/init.js +46 -2
- package/src/commands/kg.js +4 -0
- package/src/commands/loop.js +6 -1
- package/src/commands/lowcode.js +15 -2
- package/src/commands/marketplace.js +40 -6
- package/src/commands/matrix.js +20 -1
- package/src/commands/memory.js +4 -1
- package/src/commands/mtc.js +7 -1
- package/src/commands/nostr.js +31 -4
- package/src/commands/note.js +7 -4
- package/src/commands/plugin.js +474 -0
- package/src/commands/pqc.js +5 -0
- package/src/commands/reputation.js +10 -1
- package/src/commands/scim.js +6 -0
- package/src/commands/session.js +32 -10
- package/src/commands/siem.js +11 -0
- package/src/commands/sla.js +18 -3
- package/src/commands/social.js +38 -5
- package/src/commands/sso.js +10 -2
- package/src/commands/stress.js +23 -4
- package/src/commands/team.js +463 -0
- package/src/commands/tech.js +2 -0
- package/src/commands/tenant.js +10 -1
- package/src/commands/terraform.js +6 -0
- package/src/commands/update.js +1 -1
- package/src/commands/zkp.js +20 -0
- package/src/gateways/ws/message-dispatcher.js +48 -2
- package/src/gateways/ws/remote-session-protocol.js +140 -42
- package/src/gateways/ws/ws-server.js +1 -1
- package/src/harness/jsonl-session-store.js +12 -5
- package/src/harness/plugin-manager.js +16 -4
- package/src/harness/prompt-compressor.js +27 -1
- package/src/harness/remote-command-ledger.js +189 -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/harness/worktree-isolator.js +16 -6
- package/src/index.js +6 -0
- package/src/lib/__tests__/logger.test.js +5 -2
- package/src/lib/a2a-protocol.js +25 -1
- package/src/lib/activitypub-bridge.js +61 -0
- package/src/lib/agent-core.js +4 -0
- package/src/lib/agent-economy.js +262 -29
- package/src/lib/agent-network.js +7 -1
- 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 +282 -0
- package/src/lib/agent-team/team-worktree.js +236 -0
- package/src/lib/agents.js +18 -0
- package/src/lib/async-hook-supervisor.cjs +391 -0
- package/src/lib/audit-logger.js +7 -6
- package/src/lib/autonomous-developer.js +60 -0
- package/src/lib/chat-core.js +17 -4
- package/src/lib/collaboration-governance.js +56 -0
- package/src/lib/community-governance.js +68 -0
- package/src/lib/compliance-manager.js +63 -0
- package/src/lib/cross-chain.js +5 -0
- package/src/lib/dao-governance.js +151 -2
- package/src/lib/did-manager.js +23 -10
- package/src/lib/dlp-engine.js +70 -0
- package/src/lib/eval/runner.js +251 -0
- package/src/lib/eval/tasks.js +626 -0
- package/src/lib/eval/trend.js +200 -0
- package/src/lib/evolution-system.js +92 -0
- package/src/lib/evomap-federation.js +55 -0
- package/src/lib/evomap-governance.js +94 -0
- package/src/lib/fatal-handler.js +17 -1
- package/src/lib/hardening-manager.js +73 -0
- package/src/lib/hierarchical-memory.js +14 -8
- package/src/lib/knowledge-exporter.js +8 -2
- package/src/lib/knowledge-graph.js +79 -0
- package/src/lib/llm-providers.js +23 -14
- package/src/lib/logger.js +4 -1
- package/src/lib/lsp/__tests__/benchmark.test.js +88 -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 +269 -0
- package/src/lib/lsp/__tests__/lsp-manager.test.js +333 -0
- package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
- package/src/lib/lsp/benchmark.js +257 -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 +339 -0
- package/src/lib/lsp/lsp-manager.js +375 -0
- package/src/lib/lsp/lsp-server-registry.js +196 -0
- package/src/lib/matrix-bridge.js +84 -0
- package/src/lib/memory-manager.js +5 -3
- package/src/lib/nostr-bridge.js +53 -0
- package/src/lib/permission-engine.js +22 -4
- package/src/lib/plugin-monitor-supervisor.js +238 -0
- package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -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 +396 -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 +152 -0
- package/src/lib/plugin-runtime/reload.js +79 -0
- package/src/lib/plugin-runtime/remote-source.js +240 -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/pqc-manager.js +64 -0
- package/src/lib/reputation-optimizer.js +101 -0
- package/src/lib/sandbox-egress-proxy.js +262 -0
- package/src/lib/sandbox-fs-policy.js +112 -0
- package/src/lib/sandbox-network-policy.js +151 -0
- package/src/lib/sandbox-v2.js +24 -18
- package/src/lib/scim-manager.js +36 -0
- package/src/lib/session-manager.js +5 -2
- package/src/lib/settings-hook-events.cjs +78 -6
- package/src/lib/settings-hooks.cjs +30 -3
- package/src/lib/settings-loader.cjs +35 -1
- package/src/lib/siem-exporter.js +45 -0
- package/src/lib/skill-loader.js +38 -2
- package/src/lib/skill-marketplace.js +83 -0
- package/src/lib/sla-manager.js +88 -0
- package/src/lib/social-manager.js +74 -0
- package/src/lib/stress-tester.js +65 -0
- package/src/lib/tech-learning-engine.js +75 -0
- package/src/lib/telemetry/span-recorder.js +237 -0
- package/src/lib/tenant-saas.js +107 -0
- package/src/lib/terraform-manager.js +67 -0
- package/src/lib/token-incentive.js +180 -29
- package/src/lib/wallet-manager.js +81 -35
- package/src/lib/zkp-engine.js +87 -0
- package/src/repl/agent-repl.js +417 -6
- package/src/runtime/__tests__/auto-pin.test.js +104 -0
- package/src/runtime/agent-core.js +771 -104
- package/src/runtime/auto-pin.js +117 -0
- 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 +388 -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/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,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TeamWorktreeCoordinator (Phase 4) — give each teammate its OWN git worktree so
|
|
3
|
+
* parallel task execution never fights over the working tree, then integrate the
|
|
4
|
+
* results by SEQUENTIALLY previewing + merging each branch back to base. Two
|
|
5
|
+
* tasks that touch different files both merge clean; two that touch the same
|
|
6
|
+
* file surface a conflict on the second merge (the first having moved base) —
|
|
7
|
+
* the Phase 4 acceptance "并行 Worktree 修改可预览冲突并安全合并".
|
|
8
|
+
*
|
|
9
|
+
* Unlike `isolateTask` (which removes the worktree as soon as its fn returns),
|
|
10
|
+
* the coordinator KEEPS every worktree until integration is done — merge preview
|
|
11
|
+
* needs the branch's worktree present — then cleans them all up.
|
|
12
|
+
*
|
|
13
|
+
* The git surface (`worktree-isolator`) is injected via `_deps` so the
|
|
14
|
+
* coordinator's orchestration is unit-testable without a real repo; a real-git
|
|
15
|
+
* integration test exercises the actual worktree/commit/merge path.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
createWorktree as _createWorktree,
|
|
20
|
+
removeWorktree as _removeWorktree,
|
|
21
|
+
previewWorktreeMerge as _previewWorktreeMerge,
|
|
22
|
+
mergeWorktree as _mergeWorktree,
|
|
23
|
+
} from "../../harness/worktree-isolator.js";
|
|
24
|
+
import { isGitRepo as _isGitRepo } from "../../lib/git-integration.js";
|
|
25
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
26
|
+
|
|
27
|
+
function defaultRunShell(command, cwd) {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const child = spawn(command, { cwd, shell: true, env: process.env });
|
|
30
|
+
let err = "";
|
|
31
|
+
child.stderr?.on("data", (d) => (err += d.toString("utf8")));
|
|
32
|
+
child.on("error", (e) => reject(new Error(e.message)));
|
|
33
|
+
child.on("close", (code) =>
|
|
34
|
+
code === 0
|
|
35
|
+
? resolve({ code })
|
|
36
|
+
: reject(new Error(err.trim() || `command exited ${code}`)),
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Stage + commit everything in a worktree. Returns true if a commit was made. */
|
|
42
|
+
function defaultCommit(worktreePath, message) {
|
|
43
|
+
execFileSync("git", ["add", "-A"], { cwd: worktreePath, stdio: "ignore" });
|
|
44
|
+
try {
|
|
45
|
+
execFileSync(
|
|
46
|
+
"git",
|
|
47
|
+
[
|
|
48
|
+
"-c",
|
|
49
|
+
"user.email=team@chainlesschain.local",
|
|
50
|
+
"-c",
|
|
51
|
+
"user.name=cc team",
|
|
52
|
+
"commit",
|
|
53
|
+
"-m",
|
|
54
|
+
message,
|
|
55
|
+
],
|
|
56
|
+
{ cwd: worktreePath, stdio: "ignore" },
|
|
57
|
+
);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false; // nothing to commit (no changes)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const _deps = {
|
|
65
|
+
createWorktree: _createWorktree,
|
|
66
|
+
removeWorktree: _removeWorktree,
|
|
67
|
+
previewWorktreeMerge: _previewWorktreeMerge,
|
|
68
|
+
mergeWorktree: _mergeWorktree,
|
|
69
|
+
isGitRepo: _isGitRepo,
|
|
70
|
+
runShell: defaultRunShell,
|
|
71
|
+
commit: defaultCommit,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export class TeamWorktreeCoordinator {
|
|
75
|
+
constructor(repoDir) {
|
|
76
|
+
this.repoDir = repoDir;
|
|
77
|
+
this._created = new Map(); // key → { branch, path, committed }
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
isGitRepo() {
|
|
81
|
+
return _deps.isGitRepo(this.repoDir);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
branchFor(key) {
|
|
85
|
+
return `team/${key}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* A TeamRunner `runTask` that runs a task inside a fresh per-task worktree and
|
|
90
|
+
* commits the result. By default it runs the task's shell `command`; pass
|
|
91
|
+
* `runInWorktree({ key, task, holder, cwd })` to instead drive an agent turn
|
|
92
|
+
* (its `prompt`) with cwd set to the worktree, so `--agent --worktree` gets the
|
|
93
|
+
* same parallel isolation as `--exec --worktree`. Throws (→ task failure) on a
|
|
94
|
+
* non-zero command / agent exit so retry/cancel still work.
|
|
95
|
+
*/
|
|
96
|
+
makeRunTask({ runInWorktree = null } = {}) {
|
|
97
|
+
return async ({ key, task, holder }) => {
|
|
98
|
+
const branch = this.branchFor(key);
|
|
99
|
+
// A retry (the prior attempt failed and TaskLeaseRegistry re-queued the
|
|
100
|
+
// task) would collide with its own leftover worktree: createWorktree
|
|
101
|
+
// derives a DETERMINISTIC path from the branch and throws "Worktree
|
|
102
|
+
// already exists" (and `worktree add -b` rejects the existing branch), so
|
|
103
|
+
// the retry can never recover — it just re-fails on the collision instead
|
|
104
|
+
// of the real task. Tear down the prior attempt's worktree + branch first
|
|
105
|
+
// so a retry starts clean.
|
|
106
|
+
const prior = this._created.get(key);
|
|
107
|
+
if (prior) {
|
|
108
|
+
try {
|
|
109
|
+
_deps.removeWorktree(this.repoDir, prior.path, {
|
|
110
|
+
deleteBranch: true,
|
|
111
|
+
});
|
|
112
|
+
} catch {
|
|
113
|
+
/* best-effort — a real problem will resurface in createWorktree */
|
|
114
|
+
}
|
|
115
|
+
this._created.delete(key);
|
|
116
|
+
}
|
|
117
|
+
const { path: worktreePath } = _deps.createWorktree(this.repoDir, branch);
|
|
118
|
+
this._created.set(key, { branch, path: worktreePath, committed: false });
|
|
119
|
+
if (typeof runInWorktree === "function") {
|
|
120
|
+
await runInWorktree({ key, task, holder, cwd: worktreePath });
|
|
121
|
+
} else {
|
|
122
|
+
const command = task.metadata?.command || task?.command;
|
|
123
|
+
if (!command) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`task "${key}" has no \`command\` to run in a worktree`,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
await _deps.runShell(command, worktreePath);
|
|
129
|
+
}
|
|
130
|
+
const committed = _deps.commit(worktreePath, `team task ${key}`);
|
|
131
|
+
this._created.get(key).committed = committed;
|
|
132
|
+
return { branch, committed };
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Sequentially preview (and optionally merge) each committed branch back to
|
|
138
|
+
* base. Merging is in-order, so a later branch that conflicts with an
|
|
139
|
+
* already-merged one is reported as a conflict rather than silently clobbering.
|
|
140
|
+
*
|
|
141
|
+
* @param {object} [opts] { merge:boolean } actually merge clean branches
|
|
142
|
+
* @returns {Array<{key,branch,committed,clean,merged,conflicts,error?}>}
|
|
143
|
+
*/
|
|
144
|
+
integrate({ merge = false } = {}) {
|
|
145
|
+
const results = [];
|
|
146
|
+
for (const [key, info] of this._created) {
|
|
147
|
+
if (!info.committed) {
|
|
148
|
+
results.push({
|
|
149
|
+
key,
|
|
150
|
+
branch: info.branch,
|
|
151
|
+
committed: false,
|
|
152
|
+
clean: true,
|
|
153
|
+
merged: false,
|
|
154
|
+
conflicts: [],
|
|
155
|
+
note: "no changes to merge",
|
|
156
|
+
});
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
let preview;
|
|
160
|
+
try {
|
|
161
|
+
preview = _deps.previewWorktreeMerge(this.repoDir, info.branch);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
results.push({
|
|
164
|
+
key,
|
|
165
|
+
branch: info.branch,
|
|
166
|
+
committed: true,
|
|
167
|
+
clean: false,
|
|
168
|
+
merged: false,
|
|
169
|
+
conflicts: [],
|
|
170
|
+
error: err.message,
|
|
171
|
+
});
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const clean = preview?.success === true;
|
|
175
|
+
const conflicts = preview?.conflicts || [];
|
|
176
|
+
let merged = false;
|
|
177
|
+
if (clean && merge) {
|
|
178
|
+
// mergeWorktree does NOT throw on a failed merge — it catches internally
|
|
179
|
+
// and RETURNS {success:false, conflicts?, message} (a conflict the clean
|
|
180
|
+
// preview didn't predict, or any non-conflict git failure). The old
|
|
181
|
+
// try/catch was dead for that path and set merged:true unconditionally,
|
|
182
|
+
// reporting a real merge failure as success with empty conflicts. Inspect
|
|
183
|
+
// the return value. (The try still catches assertSafeGitRef, the only
|
|
184
|
+
// throw path — a malformed branch ref.)
|
|
185
|
+
let mergeResult;
|
|
186
|
+
try {
|
|
187
|
+
mergeResult = _deps.mergeWorktree(this.repoDir, info.branch, {
|
|
188
|
+
message: `Merge team task ${key}`,
|
|
189
|
+
});
|
|
190
|
+
} catch (err) {
|
|
191
|
+
mergeResult = { success: false, message: err.message };
|
|
192
|
+
}
|
|
193
|
+
if (mergeResult?.success === true) {
|
|
194
|
+
merged = true;
|
|
195
|
+
} else {
|
|
196
|
+
results.push({
|
|
197
|
+
key,
|
|
198
|
+
branch: info.branch,
|
|
199
|
+
committed: true,
|
|
200
|
+
clean: false,
|
|
201
|
+
merged: false,
|
|
202
|
+
conflicts: mergeResult?.conflicts?.length
|
|
203
|
+
? mergeResult.conflicts
|
|
204
|
+
: conflicts,
|
|
205
|
+
error: `merge failed: ${mergeResult?.message || "unknown error"}`,
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
results.push({
|
|
211
|
+
key,
|
|
212
|
+
branch: info.branch,
|
|
213
|
+
committed: true,
|
|
214
|
+
clean,
|
|
215
|
+
merged,
|
|
216
|
+
conflicts,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return results;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Remove every worktree created for this run (branches left intact). */
|
|
223
|
+
cleanupAll({ deleteBranch = false } = {}) {
|
|
224
|
+
for (const [, info] of this._created) {
|
|
225
|
+
try {
|
|
226
|
+
_deps.removeWorktree(this.repoDir, info.path, { deleteBranch });
|
|
227
|
+
} catch {
|
|
228
|
+
/* best-effort */
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
branches() {
|
|
234
|
+
return Array.from(this._created.values()).map((i) => i.branch);
|
|
235
|
+
}
|
|
236
|
+
}
|
package/src/lib/agents.js
CHANGED
|
@@ -22,6 +22,7 @@ import pathDefault from "node:path";
|
|
|
22
22
|
import { homedir } from "node:os";
|
|
23
23
|
import yaml from "js-yaml";
|
|
24
24
|
import { projectRootBase } from "./project-root.cjs";
|
|
25
|
+
import { discoverPluginAgentLayers } from "./plugin-runtime/agents.js";
|
|
25
26
|
|
|
26
27
|
const _deps = { fs: fsDefault, path: pathDefault };
|
|
27
28
|
|
|
@@ -76,6 +77,23 @@ export function agentDirs(cwd = process.cwd(), opts = {}) {
|
|
|
76
77
|
dirs.push({ dir: path.join(root, ".claude", "agents"), scope: "project" });
|
|
77
78
|
}
|
|
78
79
|
dirs.push({ dir: path.join(home, ".claude", "agents"), scope: "personal" });
|
|
80
|
+
// Installed-plugin agents (Phase 3.3h) — LOWEST precedence: appended last so
|
|
81
|
+
// that after discoverAgents' reverse() they are scanned FIRST and a user's own
|
|
82
|
+
// project/personal agent of the same name shadows them. Declarative (system
|
|
83
|
+
// prompt only), so not trust-gated. Best-effort: a discovery failure just
|
|
84
|
+
// contributes no plugin dirs.
|
|
85
|
+
if (opts.includePlugins !== false) {
|
|
86
|
+
try {
|
|
87
|
+
for (const layer of discoverPluginAgentLayers({
|
|
88
|
+
cwd,
|
|
89
|
+
scopes: opts.scopes,
|
|
90
|
+
})) {
|
|
91
|
+
dirs.push({ dir: layer.dir, scope: "plugin" });
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
/* best-effort — plugin discovery never breaks core agent loading */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
79
97
|
return dirs;
|
|
80
98
|
}
|
|
81
99
|
|
|
@@ -0,0 +1,391 @@
|
|
|
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 TREE-kills the child (shell + the real command it
|
|
27
|
+
* spawned — a bare child.kill would orphan the grandchild);
|
|
28
|
+
* - tree-reaping of every child on stopAll() / process 'exit', so a session
|
|
29
|
+
* never leaves an orphan hook process.
|
|
30
|
+
*
|
|
31
|
+
* `_deps.spawn` / `_deps.now` are injected so the whole supervisor is unit-
|
|
32
|
+
* testable with fake child processes (no real shell).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const cpDefault = require("node:child_process");
|
|
36
|
+
const { tryParseDecision } = require("./hook-runner.cjs");
|
|
37
|
+
|
|
38
|
+
const DEFAULT_TIMEOUT_MS = 60000;
|
|
39
|
+
const MAX_TIMEOUT_MS = 600000;
|
|
40
|
+
const DEFAULT_MAX_CONCURRENT = 4;
|
|
41
|
+
// Ring cap on undrained records (parity with PluginMonitorSupervisor's
|
|
42
|
+
// OUTPUT_RING): drainResults()/drainRewakes() normally clear each turn, but a
|
|
43
|
+
// stalled/absent drain over a high-frequency hook would otherwise grow these
|
|
44
|
+
// arrays without bound. Keep only the most recent RESULT_RING; older records
|
|
45
|
+
// are dropped oldest-first.
|
|
46
|
+
const RESULT_RING = 500;
|
|
47
|
+
|
|
48
|
+
class AsyncHookSupervisor {
|
|
49
|
+
constructor(opts = {}) {
|
|
50
|
+
this.maxConcurrent =
|
|
51
|
+
Number(opts.maxConcurrent) > 0
|
|
52
|
+
? Number(opts.maxConcurrent)
|
|
53
|
+
: DEFAULT_MAX_CONCURRENT;
|
|
54
|
+
this._deps = {
|
|
55
|
+
spawn: opts.spawn || cpDefault.spawn,
|
|
56
|
+
spawnSync: opts.spawnSync || cpDefault.spawnSync,
|
|
57
|
+
now: opts.now || (() => Date.now()),
|
|
58
|
+
};
|
|
59
|
+
this._running = new Map(); // key -> { child, timer }
|
|
60
|
+
this._results = []; // completed records awaiting drainResults()
|
|
61
|
+
this._rewakes = []; // failed asyncRewake records awaiting drainRewakes()
|
|
62
|
+
this._stopped = false;
|
|
63
|
+
// The exit reaper is installed LAZILY (on first spawn), not in the ctor:
|
|
64
|
+
// a supervisor that never dispatches a hook (or the many built in tests)
|
|
65
|
+
// must not leave a permanent `process.once('exit')` listener behind —
|
|
66
|
+
// that accumulates across sessions/instances into a MaxListenersExceeded
|
|
67
|
+
// leak. Mirrors PluginMonitorSupervisor._installExitHook.
|
|
68
|
+
this._exitHook = () => this.stopAll();
|
|
69
|
+
this._exitHookInstalled = false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_key(hook) {
|
|
73
|
+
return `${hook.event || ""}::${hook.command}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Kill a hook child's WHOLE process tree. Hooks run with `shell: true`, so the
|
|
78
|
+
* real command is a grandchild of the shell — a plain `child.kill()` only
|
|
79
|
+
* signals the shell (POSIX: reparents + keeps the command running; Windows:
|
|
80
|
+
* killing cmd.exe never touches its children), orphaning the actual hook
|
|
81
|
+
* process. POSIX: the child is spawned detached (its own group), so a negative
|
|
82
|
+
* pid signals the whole group. Windows: `taskkill /T` walks the tree. Both are
|
|
83
|
+
* SYNCHRONOUS (spawnSync / process.kill) so this is safe inside stopAll() and
|
|
84
|
+
* the process 'exit' reaper, where async work would be cut off. A child with no
|
|
85
|
+
* real pid (the unit-test fakes) falls back to its own `.kill()`.
|
|
86
|
+
*/
|
|
87
|
+
_killChildTree(child, signal = "SIGTERM") {
|
|
88
|
+
if (!child) return;
|
|
89
|
+
const pid = child.pid;
|
|
90
|
+
if (typeof pid !== "number" || pid <= 0) {
|
|
91
|
+
try {
|
|
92
|
+
child.kill(signal);
|
|
93
|
+
} catch {
|
|
94
|
+
/* already gone / fake with no kill */
|
|
95
|
+
}
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
if (process.platform === "win32") {
|
|
100
|
+
const r = this._deps.spawnSync(
|
|
101
|
+
"taskkill",
|
|
102
|
+
["/pid", String(pid), "/T", "/F"],
|
|
103
|
+
{ windowsHide: true },
|
|
104
|
+
);
|
|
105
|
+
// If taskkill couldn't launch, fall back to a direct kill.
|
|
106
|
+
if (r && r.error) child.kill(signal);
|
|
107
|
+
} else {
|
|
108
|
+
// Negative pid → the whole process group (requires the detached spawn).
|
|
109
|
+
try {
|
|
110
|
+
process.kill(-pid, signal);
|
|
111
|
+
} catch {
|
|
112
|
+
child.kill(signal);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
try {
|
|
117
|
+
child.kill(signal);
|
|
118
|
+
} catch {
|
|
119
|
+
/* already gone */
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Install the process-exit reaper exactly once, only when a child exists. */
|
|
125
|
+
_installExitHook() {
|
|
126
|
+
if (this._exitHookInstalled) return;
|
|
127
|
+
this._exitHookInstalled = true;
|
|
128
|
+
process.once("exit", this._exitHook);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Append a result record, bounded to the most recent RESULT_RING. */
|
|
132
|
+
_pushResult(rec) {
|
|
133
|
+
this._results.push(rec);
|
|
134
|
+
if (this._results.length > RESULT_RING) this._results.shift();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Dispatch a batch of async hooks fire-and-forget. Returns IMMEDIATELY with a
|
|
139
|
+
* per-hook disposition (`{command, dispatched, reason?}`) — it never awaits a
|
|
140
|
+
* hook. `payload` is JSON-written to each hook's stdin (same protocol as the
|
|
141
|
+
* sync runner).
|
|
142
|
+
*
|
|
143
|
+
* @param {Array<{command:string, event?:string, timeout?:number,
|
|
144
|
+
* asyncRewake?:boolean}>} hooks
|
|
145
|
+
* @param {object} payload the hook event payload (hook_event_name, …)
|
|
146
|
+
* @param {object} [opts] { cwd, timeout } (per-hook timeout wins)
|
|
147
|
+
*/
|
|
148
|
+
dispatch(hooks, payload = {}, opts = {}) {
|
|
149
|
+
const dispositions = [];
|
|
150
|
+
for (const hook of hooks || []) {
|
|
151
|
+
if (!hook || !hook.command) continue;
|
|
152
|
+
if (this._stopped) {
|
|
153
|
+
dispositions.push({
|
|
154
|
+
command: hook.command,
|
|
155
|
+
dispatched: false,
|
|
156
|
+
reason: "supervisor stopped",
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const key = this._key(hook);
|
|
161
|
+
if (this._running.has(key)) {
|
|
162
|
+
dispositions.push({
|
|
163
|
+
command: hook.command,
|
|
164
|
+
dispatched: false,
|
|
165
|
+
reason: "already running (deduped)",
|
|
166
|
+
});
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (this._running.size >= this.maxConcurrent) {
|
|
170
|
+
dispositions.push({
|
|
171
|
+
command: hook.command,
|
|
172
|
+
dispatched: false,
|
|
173
|
+
reason: "max concurrent reached",
|
|
174
|
+
});
|
|
175
|
+
// Record the skip so the caller/agent can SEE that a hook was dropped
|
|
176
|
+
// (silent truncation would read as "the hook ran and said nothing").
|
|
177
|
+
this._pushResult({
|
|
178
|
+
command: hook.command,
|
|
179
|
+
event: hook.event || null,
|
|
180
|
+
ok: false,
|
|
181
|
+
skipped: true,
|
|
182
|
+
exitCode: null,
|
|
183
|
+
error: "async hook skipped: max concurrent reached",
|
|
184
|
+
additionalContext: null,
|
|
185
|
+
blocked: false,
|
|
186
|
+
ts: this._deps.now(),
|
|
187
|
+
ms: 0,
|
|
188
|
+
});
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
this._spawnOne(hook, payload, opts);
|
|
192
|
+
dispositions.push({ command: hook.command, dispatched: true });
|
|
193
|
+
}
|
|
194
|
+
return dispositions;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_spawnOne(hook, payload, opts) {
|
|
198
|
+
// A live child now exists — arm the exit reaper so a session never leaves
|
|
199
|
+
// an orphan hook process (installed once, only from here).
|
|
200
|
+
this._installExitHook();
|
|
201
|
+
const key = this._key(hook);
|
|
202
|
+
const started = this._deps.now();
|
|
203
|
+
const rawTimeout = Number(
|
|
204
|
+
hook.timeout != null ? hook.timeout * 1000 : opts.timeout,
|
|
205
|
+
);
|
|
206
|
+
const timeout =
|
|
207
|
+
Number.isFinite(rawTimeout) && rawTimeout > 0
|
|
208
|
+
? Math.min(rawTimeout, MAX_TIMEOUT_MS)
|
|
209
|
+
: DEFAULT_TIMEOUT_MS;
|
|
210
|
+
|
|
211
|
+
let child;
|
|
212
|
+
try {
|
|
213
|
+
child = this._deps.spawn(hook.command, {
|
|
214
|
+
cwd: opts.cwd || process.cwd(),
|
|
215
|
+
shell: true,
|
|
216
|
+
// POSIX: own process group so a timeout / stopAll can signal the whole
|
|
217
|
+
// tree (shell + the real hook command), not just the shell wrapper.
|
|
218
|
+
// No-op on Windows, where the tree is reaped via `taskkill /T` instead.
|
|
219
|
+
detached: process.platform !== "win32",
|
|
220
|
+
env: {
|
|
221
|
+
...process.env,
|
|
222
|
+
CLAUDE_HOOK_EVENT: hook.event || payload.hook_event_name || "",
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
} catch (err) {
|
|
226
|
+
this._record(hook, {
|
|
227
|
+
ok: false,
|
|
228
|
+
exitCode: null,
|
|
229
|
+
error: `hook spawn failed: ${err.message}`,
|
|
230
|
+
started,
|
|
231
|
+
});
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
let out = "";
|
|
236
|
+
let errOut = "";
|
|
237
|
+
let killedByTimeout = false;
|
|
238
|
+
const rec = { child, timer: null };
|
|
239
|
+
this._running.set(key, rec);
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
// `.end(data)` flushes ASYNCHRONOUSLY: if the hook exited before consuming
|
|
243
|
+
// stdin (a fast command), the write fails with an async EPIPE emitted as an
|
|
244
|
+
// 'error' event on the stream — NOT a sync throw this catch can see. With no
|
|
245
|
+
// listener that becomes an uncaught exception that crashes the process (seen
|
|
246
|
+
// on Linux CI). Attach a no-op error handler first so a closed-stdin write
|
|
247
|
+
// is swallowed as the non-fatal condition it is.
|
|
248
|
+
child.stdin?.on("error", () => {});
|
|
249
|
+
child.stdin?.end(JSON.stringify(payload));
|
|
250
|
+
} catch {
|
|
251
|
+
/* stdin may already be closed — non-fatal */
|
|
252
|
+
}
|
|
253
|
+
child.stdout?.on("data", (d) => {
|
|
254
|
+
out += d.toString("utf8");
|
|
255
|
+
});
|
|
256
|
+
child.stderr?.on("data", (d) => {
|
|
257
|
+
errOut += d.toString("utf8");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
const timer = setTimeout(() => {
|
|
261
|
+
killedByTimeout = true;
|
|
262
|
+
// Tree-kill: a hook that spawned a subprocess (e.g. `npm test`) must not
|
|
263
|
+
// leave that subprocess orphaned when it exceeds its timeout.
|
|
264
|
+
this._killChildTree(child, "SIGTERM");
|
|
265
|
+
}, timeout);
|
|
266
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
267
|
+
rec.timer = timer;
|
|
268
|
+
|
|
269
|
+
let settled = false;
|
|
270
|
+
const finalize = (exitCode) => {
|
|
271
|
+
if (settled) return;
|
|
272
|
+
settled = true;
|
|
273
|
+
clearTimeout(timer);
|
|
274
|
+
this._running.delete(key);
|
|
275
|
+
const parsed = tryParseDecision(out);
|
|
276
|
+
const blocked = !!(parsed && parsed.decision === "block");
|
|
277
|
+
const ok = !killedByTimeout && exitCode === 0 && !blocked;
|
|
278
|
+
const additionalContext =
|
|
279
|
+
(parsed && parsed.additionalContext) ||
|
|
280
|
+
(exitCode === 0 && out.trim() && out.trim()[0] !== "{"
|
|
281
|
+
? out.trim()
|
|
282
|
+
: null);
|
|
283
|
+
this._record(hook, {
|
|
284
|
+
ok,
|
|
285
|
+
exitCode,
|
|
286
|
+
started,
|
|
287
|
+
blocked,
|
|
288
|
+
additionalContext,
|
|
289
|
+
error: killedByTimeout
|
|
290
|
+
? `timed out after ${timeout}ms`
|
|
291
|
+
: ok
|
|
292
|
+
? null
|
|
293
|
+
: errOut.trim() ||
|
|
294
|
+
(blocked
|
|
295
|
+
? parsed.reason || "hook requested block"
|
|
296
|
+
: `hook exited ${exitCode}`),
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
child.on("error", (e) => {
|
|
301
|
+
if (settled) return;
|
|
302
|
+
settled = true;
|
|
303
|
+
clearTimeout(timer);
|
|
304
|
+
this._running.delete(key);
|
|
305
|
+
this._record(hook, {
|
|
306
|
+
ok: false,
|
|
307
|
+
exitCode: null,
|
|
308
|
+
error: `hook error: ${e.message}`,
|
|
309
|
+
started,
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
child.on("close", (code) => finalize(code));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
_record(hook, fields) {
|
|
316
|
+
const now = this._deps.now();
|
|
317
|
+
const rec = {
|
|
318
|
+
command: hook.command,
|
|
319
|
+
event: hook.event || null,
|
|
320
|
+
ok: fields.ok === true,
|
|
321
|
+
exitCode: fields.exitCode ?? null,
|
|
322
|
+
error: fields.error || null,
|
|
323
|
+
additionalContext: fields.additionalContext || null,
|
|
324
|
+
blocked: !!fields.blocked,
|
|
325
|
+
asyncRewake: hook.asyncRewake === true,
|
|
326
|
+
ts: now,
|
|
327
|
+
ms: fields.started != null ? now - fields.started : 0,
|
|
328
|
+
};
|
|
329
|
+
this._pushResult(rec);
|
|
330
|
+
// A rewake fires only for a hook that OPTED IN and finished in a failure
|
|
331
|
+
// state — a passing async check should not re-engage the agent.
|
|
332
|
+
if (hook.asyncRewake && !rec.ok) {
|
|
333
|
+
this._rewakes.push(rec);
|
|
334
|
+
if (this._rewakes.length > RESULT_RING) this._rewakes.shift();
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Completed result records since the last drain (cleared on read). */
|
|
339
|
+
drainResults() {
|
|
340
|
+
const r = this._results;
|
|
341
|
+
this._results = [];
|
|
342
|
+
return r;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
peekResults() {
|
|
346
|
+
return this._results.slice();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Failed asyncRewake records since the last drain (cleared on read). */
|
|
350
|
+
drainRewakes() {
|
|
351
|
+
const r = this._rewakes;
|
|
352
|
+
this._rewakes = [];
|
|
353
|
+
return r;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
hasRewake() {
|
|
357
|
+
return this._rewakes.length > 0;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
runningCount() {
|
|
361
|
+
return this._running.size;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Kill every in-flight hook and detach the exit reaper. Idempotent. */
|
|
365
|
+
stopAll() {
|
|
366
|
+
this._stopped = true;
|
|
367
|
+
for (const [key, rec] of this._running) {
|
|
368
|
+
try {
|
|
369
|
+
clearTimeout(rec.timer);
|
|
370
|
+
} catch {
|
|
371
|
+
/* ignore */
|
|
372
|
+
}
|
|
373
|
+
// Tree-kill (not a bare child.kill) so a session end / process exit never
|
|
374
|
+
// leaves an orphaned hook subprocess — the guarantee this class documents.
|
|
375
|
+
this._killChildTree(rec.child, "SIGTERM");
|
|
376
|
+
this._running.delete(key);
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
process.removeListener("exit", this._exitHook);
|
|
380
|
+
} catch {
|
|
381
|
+
/* ignore */
|
|
382
|
+
}
|
|
383
|
+
this._exitHookInstalled = false;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
module.exports = {
|
|
388
|
+
AsyncHookSupervisor,
|
|
389
|
+
DEFAULT_TIMEOUT_MS,
|
|
390
|
+
DEFAULT_MAX_CONCURRENT,
|
|
391
|
+
};
|
package/src/lib/audit-logger.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import crypto from "crypto";
|
|
7
7
|
import { safeJsonParse } from "./safe-json.js";
|
|
8
|
+
import { escapeLike } from "./sql-like.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Event types for audit logging.
|
|
@@ -183,13 +184,13 @@ export function queryLogs(db, filters = {}) {
|
|
|
183
184
|
}
|
|
184
185
|
|
|
185
186
|
if (filters.operation) {
|
|
186
|
-
sql += " AND operation LIKE ?";
|
|
187
|
-
params.push(`%${filters.operation}%`);
|
|
187
|
+
sql += " AND operation LIKE ? ESCAPE '\\'";
|
|
188
|
+
params.push(`%${escapeLike(filters.operation)}%`);
|
|
188
189
|
}
|
|
189
190
|
|
|
190
191
|
if (filters.actor) {
|
|
191
|
-
sql += " AND actor LIKE ?";
|
|
192
|
-
params.push(`%${filters.actor}%`);
|
|
192
|
+
sql += " AND actor LIKE ? ESCAPE '\\'";
|
|
193
|
+
params.push(`%${escapeLike(filters.actor)}%`);
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
if (filters.riskLevel) {
|
|
@@ -214,8 +215,8 @@ export function queryLogs(db, filters = {}) {
|
|
|
214
215
|
|
|
215
216
|
if (filters.search) {
|
|
216
217
|
// Search in operation field (primary search field for CLI)
|
|
217
|
-
sql += " AND operation LIKE ?";
|
|
218
|
-
params.push(`%${filters.search}%`);
|
|
218
|
+
sql += " AND operation LIKE ? ESCAPE '\\'";
|
|
219
|
+
params.push(`%${escapeLike(filters.search)}%`);
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
sql += " ORDER BY created_at DESC";
|