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.
Files changed (322) hide show
  1. package/README.md +1 -1
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/.build-hash +1 -1
  4. package/src/assets/web-panel/assets/{AIOps-DwpCCp64.js → AIOps-CBLvrjeG.js} +1 -1
  5. package/src/assets/web-panel/assets/{ActionButton-C9pBYocA.js → ActionButton-DYv2GtE0.js} +1 -1
  6. package/src/assets/web-panel/assets/{Analytics-BWAkeXxK.js → Analytics-CL6PZ-Ww.js} +3 -3
  7. package/src/assets/web-panel/assets/{AppLayout-CivFGH6B.js → AppLayout-xuyHt4uA.js} +3 -3
  8. package/src/assets/web-panel/assets/{Audit-Cm8hRpZm.js → Audit-DOvSVycq.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-DXdFMsE8.js → Backup-DMhfTswr.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-uAwSTeql.js → BaseInput-CQxDdm63.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-KU8eeJJE.js → Chat-BEeNhK2a.js} +5 -5
  12. package/src/assets/web-panel/assets/ChatBubbleRenderer-8nul8eTq.js +1 -0
  13. package/src/assets/web-panel/assets/{Checkbox-C6AMDkjd.js → Checkbox-CL04O-ER.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-t2moDxcO.js → Codegen-C50o-n7x.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-DCcsRRhN.js → Col-CH-bDkzs.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-DtB-8SAC.js → Community-DqAIMvSe.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-CZm8THYA.js → Compact-C43fo_my.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-LiQgC7g1.js → Compliance-BERnMrdP.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CA8SAxht.js → Cowork-Baw3w1gp.js} +4 -4
  20. package/src/assets/web-panel/assets/{Cron-CtRaF1wD.js → Cron-BEqAHF1-.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-6-br6Hub.js → Crosschain-CvnmKwK4.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-Do2EccrL.js → DID-iMPxgVdt.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-wrXcbzGe.js → Dashboard--4SJX3YR.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-p3FsT245.js → Dropdown-BgLV6xgz.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-D95A2L8q.js → EmailListRenderer-Bvb8oUD8.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-cLuJW2zo.js → FamilyGuardDashboard-DpXPjJB5.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-Brgq_cbN.js → Federation-BbFfJ1Xi.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-DWLCkNgh.js → FormItemContext-DeGq1B-q.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-BBqUfQd6.js → GenericCardRenderer-CNW7s9zM.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-8F090w4I.js → Git-L1PjW-lT.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-CCogEbxb.js → Governance-PK-X58to.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CfLD7v7C.js → Inference-BePWEH-d.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DDuHJewd.js → KnowledgeGraph-LF_sBvdL.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-CE8KSEVC.js → Logs-BKkfk9hy.js} +2 -2
  35. package/src/assets/web-panel/assets/{Marketplace-DmwpZmhT.js → Marketplace-BNMqUDla.js} +1 -1
  36. package/src/assets/web-panel/assets/{McpTools-Bf7AazU8.js → McpTools-w4WPiyz2.js} +5 -5
  37. package/src/assets/web-panel/assets/{Memory-D0QU_00S.js → Memory-DUKMVGNi.js} +2 -2
  38. package/src/assets/web-panel/assets/{MobileBridge-yXS9xdhx.js → MobileBridge-BWhiEyTJ.js} +1 -1
  39. package/src/assets/web-panel/assets/{MobileProjects-BuDUoGUP.js → MobileProjects-Ocf5JEkX.js} +1 -1
  40. package/src/assets/web-panel/assets/{Mtc-BYyHy28p.js → Mtc-D2B6MMhU.js} +4 -4
  41. package/src/assets/web-panel/assets/{MtcAudit-CK0aqpiZ.js → MtcAudit-DdYjCq1O.js} +4 -4
  42. package/src/assets/web-panel/assets/{Multisig-mNimKYeb.js → Multisig-Cb1hfuUZ.js} +3 -3
  43. package/src/assets/web-panel/assets/{NLProgramming-AioAJ0YA.js → NLProgramming-CTeImGp7.js} +1 -1
  44. package/src/assets/web-panel/assets/{Notes-CVVNCLHE.js → Notes-BjYNc7eQ.js} +4 -4
  45. package/src/assets/web-panel/assets/{NotificationSettings-C4ABj-TK.js → NotificationSettings-Dn_NtRXQ.js} +1 -1
  46. package/src/assets/web-panel/assets/{OrderTableRenderer-DIp8Xcbd.js → OrderTableRenderer-DVYYIizh.js} +1 -1
  47. package/src/assets/web-panel/assets/{Organization-uozl_gWK.js → Organization-Be2Kmr5j.js} +4 -4
  48. package/src/assets/web-panel/assets/{Overflow-BCZOM2hK.js → Overflow-Cu_-qRlR.js} +1 -1
  49. package/src/assets/web-panel/assets/{P2P-CArcaiaj.js → P2P-DD2HoGzE.js} +2 -2
  50. package/src/assets/web-panel/assets/{PdhVaultBrowser-Sgq2Srr8.js → PdhVaultBrowser-BRI7DVth.js} +3 -3
  51. package/src/assets/web-panel/assets/{Permissions-BYrQrXnH.js → Permissions-C9Srcs2M.js} +4 -4
  52. package/src/assets/web-panel/assets/{PersonalDataHub-BzHStAbz.js → PersonalDataHub-CUqcMgo1.js} +3 -3
  53. package/src/assets/web-panel/assets/{Pipeline-k7KqxX1M.js → Pipeline-HBrQGLXt.js} +1 -1
  54. package/src/assets/web-panel/assets/{Privacy-IumTUWqx.js → Privacy-D-B0vjwh.js} +1 -1
  55. package/src/assets/web-panel/assets/{ProjectInit-BRyImYTf.js → ProjectInit-eTeLIu9e.js} +2 -2
  56. package/src/assets/web-panel/assets/{ProjectSettings-BFIqTBFk.js → ProjectSettings-DrRqCsC6.js} +2 -2
  57. package/src/assets/web-panel/assets/{Projects-Dpid9CDg.js → Projects-1G9DNOhI.js} +1 -1
  58. package/src/assets/web-panel/assets/{Providers-CyyFnUPs.js → Providers-RkTZzpxP.js} +1 -1
  59. package/src/assets/web-panel/assets/{QrScannerModal-C82cRx-X.js → QrScannerModal-BSCUgsgC.js} +1 -1
  60. package/src/assets/web-panel/assets/{QuickAsk-DGxDF521.js → QuickAsk-CHgyM3Bz.js} +1 -1
  61. package/src/assets/web-panel/assets/{Recommend-Cns-Am1y.js → Recommend-0lry4OZq.js} +1 -1
  62. package/src/assets/web-panel/assets/{RemoteSession-R7OqAIlg.js → RemoteSession-Cn45UroZ.js} +2 -2
  63. package/src/assets/web-panel/assets/{Reputation-D7mgPIYV.js → Reputation-Q-Zjb707.js} +1 -1
  64. package/src/assets/web-panel/assets/{Row-B3lEURyd.js → Row-BdM70oda.js} +1 -1
  65. package/src/assets/web-panel/assets/{RssFeed-9_UVrpBt.js → RssFeed-DmOAKKap.js} +2 -2
  66. package/src/assets/web-panel/assets/{Search-ClZeO80q.js → Search-Dwavbm07.js} +1 -1
  67. package/src/assets/web-panel/assets/{Security-BNNJczEp.js → Security-DukXFCnk.js} +3 -3
  68. package/src/assets/web-panel/assets/{Services-C5SjrWUj.js → Services-BIlRnITu.js} +2 -2
  69. package/src/assets/web-panel/assets/{Skeleton-Ci_obQ-g.js → Skeleton-YYVQdhhQ.js} +1 -1
  70. package/src/assets/web-panel/assets/{Skills-DdebN15P.js → Skills-B8_iYHz2.js} +1 -1
  71. package/src/assets/web-panel/assets/{Sla-OinZP2vi.js → Sla-BdVrhT31.js} +1 -1
  72. package/src/assets/web-panel/assets/{SpeechSettings-CxzbNF51.js → SpeechSettings-CD3ggRx7.js} +1 -1
  73. package/src/assets/web-panel/assets/{SyncSettings-D06N_66b.js → SyncSettings-Bp02VZFH.js} +2 -2
  74. package/src/assets/web-panel/assets/{Tasks-BdEdW0AZ.js → Tasks-DEVmCEsr.js} +1 -1
  75. package/src/assets/web-panel/assets/{Templates-F1ctKmPa.js → Templates-B6czWc4N.js} +1 -1
  76. package/src/assets/web-panel/assets/{Tenant-CVz1Urot.js → Tenant-BCJLv17r.js} +1 -1
  77. package/src/assets/web-panel/assets/Terminal-CD132d2Y.js +3 -0
  78. package/src/assets/web-panel/assets/{TimelineRenderer-Doitzjmf.js → TimelineRenderer-ZB-CvkZl.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tokens-BpyVRpVA.js → Tokens-BnSY0S-s.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trigger-nWhWYTbU.js → Trigger-VZw9Oy2w.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trust-DK_G-wLx.js → Trust-pZq7Hjd2.js} +1 -1
  82. package/src/assets/web-panel/assets/{UkeySign-B5yBUojO.js → UkeySign-DnQaqk4q.js} +1 -1
  83. package/src/assets/web-panel/assets/{VideoEditing-C5D51qAe.js → VideoEditing-BEUfmvJV.js} +1 -1
  84. package/src/assets/web-panel/assets/{Wallet-CLbL9K7v.js → Wallet-CoZKMXJ2.js} +4 -4
  85. package/src/assets/web-panel/assets/{WebAuthn-DZUelI3V.js → WebAuthn-CBOGVx1I.js} +5 -5
  86. package/src/assets/web-panel/assets/{WorkflowEditor-8RPxt4KW.js → WorkflowEditor-huSKn7TT.js} +1 -1
  87. package/src/assets/web-panel/assets/{chat-4N4tOA3d.js → chat-A_3w6FBO.js} +1 -1
  88. package/src/assets/web-panel/assets/{colors-zbbGVrua.js → colors-D1Bem9Oy.js} +1 -1
  89. package/src/assets/web-panel/assets/{compact-item-D7iMkbnE.js → compact-item-CJYJj0oO.js} +1 -1
  90. package/src/assets/web-panel/assets/{createContext-CBzPJv-w.js → createContext-BkbFiKT4.js} +1 -1
  91. package/src/assets/web-panel/assets/devWarning-WDr3_M_N.js +1 -0
  92. package/src/assets/web-panel/assets/{hasIn-bHdrQSqZ.js → hasIn-CKvUafVY.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-DbE5D0WL.js → index-AjrSPnvv.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-BCr0geV9.js → index-B8i0lViZ.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-CiG1IZao.js → index-B9Svn0zc.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-B1s0Bz9O.js → index-BHdHSX06.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-QFObYeo1.js → index-BHdailyo.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BnBOpqv3.js → index-BJwlEnYo.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CdmMoYN1.js → index-BOoAQjJz.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DUyjUkY7.js → index-BSgWxAOG.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-CqdPyWm5.js → index-BdutMsne.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DmqlJed-.js → index-BiDWxzbn.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-S74FpAIn.js → index-By5a0T_W.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-kmh1TxRa.js → index-C-WIY-FQ.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-BTIjjXry.js → index-C37f6iey.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BS4_Mwk6.js → index-C9bWmTcO.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BBNr77E1.js → index-CBaosWRO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-QNAG4JtR.js → index-CBlBVJ_m.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-Bqmx24kh.js → index-CD62EEGo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CQfu1U78.js → index-CUj-l7GM.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-BJ4ZGyW0.js → index-CVS8Ymuq.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Ci009ijp.js → index-ChkXUj3f.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-oDKNgCsP.js → index-Cj0OZ-37.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BZb8F8YG.js → index-Co3OFL0t.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-308gCGh7.js → index-Crp8CbRg.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CPxkoKgA.js → index-D22zNXiS.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-xYCm6W2h.js → index-DTs_D1OL.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BtbbdLnf.js → index-DfgCdFrN.js} +3 -3
  119. package/src/assets/web-panel/assets/{index-BsKehmmS.js → index-DjYrecNb.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CkxK86vf.js → index-DpW9cf3e.js} +1 -1
  121. package/src/assets/web-panel/assets/index-Dwix3p4g.js +1 -0
  122. package/src/assets/web-panel/assets/{index-CvRMA9uT.js → index-F--HuPG1.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DE9j5YgT.js → index-Fuk6t_3K.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-B7b1hGry.js → index-JEAGMii9.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-D1YDh7Wr.js → index-PF-mpv8K.js} +1 -1
  126. package/src/assets/web-panel/assets/index-SHaoZ0CA.js +1 -0
  127. package/src/assets/web-panel/assets/{index-CQ6NcfWz.js → index-b-sbVdjQ.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DyoNgbXl.js → index-pyvMVX8r.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DISWAYce.js → index-qNTtOVi_.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-D4XKpj6c.js → index-tgO8iza6.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CahryTX0.js → index-ttDQVhZy.js} +1 -1
  132. package/src/assets/web-panel/assets/{initDefaultProps-BD55yNBt.js → initDefaultProps-DtrnfsZH.js} +1 -1
  133. package/src/assets/web-panel/assets/{motion-B5Bbe77U.js → motion-D4zqSaX3.js} +1 -1
  134. package/src/assets/web-panel/assets/{move-CTvIqHEH.js → move-CGFGOUQj.js} +1 -1
  135. package/src/assets/web-panel/assets/{mtc-parser-BkNyPQKB.js → mtc-parser-DH2HvkJl.js} +1 -1
  136. package/src/assets/web-panel/assets/{omit-BaXJXgHA.js → omit-jJ2at5HX.js} +1 -1
  137. package/src/assets/web-panel/assets/{pickAttrs-Bw6-YTxH.js → pickAttrs-CRyMT1Fv.js} +1 -1
  138. package/src/assets/web-panel/assets/{placementArrow-BLdbkLqJ.js → placementArrow-DZ_CX0Pt.js} +1 -1
  139. package/src/assets/web-panel/assets/{responsiveObserve-IVYkzntU.js → responsiveObserve-wruHk-h5.js} +1 -1
  140. package/src/assets/web-panel/assets/{slide-DFMFwwtY.js → slide-BGgx8q67.js} +1 -1
  141. package/src/assets/web-panel/assets/{statusUtils-CURYRtZ1.js → statusUtils-Do5M7gKm.js} +1 -1
  142. package/src/assets/web-panel/assets/{styleChecker-Cfz63s1r.js → styleChecker-CRa-GZpX.js} +1 -1
  143. package/src/assets/web-panel/assets/{useFlexGapSupport-COJL0KE3.js → useFlexGapSupport-BxPcq6pj.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFs-YLLojQQf.js → useFs-v5O2ZcJm.js} +1 -1
  145. package/src/assets/web-panel/assets/{usePersonalDataHub-BF_21qUC.js → usePersonalDataHub--z2FU0Px.js} +1 -1
  146. package/src/assets/web-panel/assets/{vnode-9ZpkA7bb.js → vnode-1emidVKd.js} +1 -1
  147. package/src/assets/web-panel/assets/{zoom-DhtAfhl8.js → zoom-CcJaRyHs.js} +1 -1
  148. package/src/assets/web-panel/index.html +1 -1
  149. package/src/assets/web-panel/remote-session-sw.js +43 -0
  150. package/src/command-manifest.json +22 -1
  151. package/src/commands/a2a.js +19 -4
  152. package/src/commands/activitypub.js +20 -3
  153. package/src/commands/agent.js +71 -10
  154. package/src/commands/audit.js +40 -24
  155. package/src/commands/codeintel.js +320 -0
  156. package/src/commands/collab.js +15 -2
  157. package/src/commands/compliance.js +5 -0
  158. package/src/commands/config.js +4 -1
  159. package/src/commands/dao.js +86 -14
  160. package/src/commands/dev.js +2 -0
  161. package/src/commands/did.js +7 -1
  162. package/src/commands/dlp.js +23 -1
  163. package/src/commands/economy.js +29 -3
  164. package/src/commands/eval.js +255 -0
  165. package/src/commands/evomap.js +26 -0
  166. package/src/commands/governance.js +17 -3
  167. package/src/commands/hardening.js +18 -0
  168. package/src/commands/incentive.js +5 -0
  169. package/src/commands/init.js +46 -2
  170. package/src/commands/kg.js +4 -0
  171. package/src/commands/loop.js +6 -1
  172. package/src/commands/lowcode.js +15 -2
  173. package/src/commands/marketplace.js +40 -6
  174. package/src/commands/matrix.js +20 -1
  175. package/src/commands/memory.js +4 -1
  176. package/src/commands/mtc.js +7 -1
  177. package/src/commands/nostr.js +31 -4
  178. package/src/commands/note.js +7 -4
  179. package/src/commands/plugin.js +474 -0
  180. package/src/commands/pqc.js +5 -0
  181. package/src/commands/reputation.js +10 -1
  182. package/src/commands/scim.js +6 -0
  183. package/src/commands/session.js +32 -10
  184. package/src/commands/siem.js +11 -0
  185. package/src/commands/sla.js +18 -3
  186. package/src/commands/social.js +38 -5
  187. package/src/commands/sso.js +10 -2
  188. package/src/commands/stress.js +23 -4
  189. package/src/commands/team.js +463 -0
  190. package/src/commands/tech.js +2 -0
  191. package/src/commands/tenant.js +10 -1
  192. package/src/commands/terraform.js +6 -0
  193. package/src/commands/update.js +1 -1
  194. package/src/commands/zkp.js +20 -0
  195. package/src/gateways/ws/message-dispatcher.js +48 -2
  196. package/src/gateways/ws/remote-session-protocol.js +140 -42
  197. package/src/gateways/ws/ws-server.js +1 -1
  198. package/src/harness/jsonl-session-store.js +12 -5
  199. package/src/harness/plugin-manager.js +16 -4
  200. package/src/harness/prompt-compressor.js +27 -1
  201. package/src/harness/remote-command-ledger.js +189 -0
  202. package/src/harness/remote-session-push-apns.js +246 -0
  203. package/src/harness/remote-session-push-errors.js +15 -0
  204. package/src/harness/remote-session-push-fcm.js +9 -25
  205. package/src/harness/remote-session-push-huawei.js +174 -0
  206. package/src/harness/remote-session-push-oppo.js +171 -0
  207. package/src/harness/remote-session-push-senders.js +42 -0
  208. package/src/harness/remote-session-push-vivo.js +179 -0
  209. package/src/harness/remote-session-push-web.js +299 -0
  210. package/src/harness/remote-session-push-xiaomi.js +99 -0
  211. package/src/harness/worktree-isolator.js +16 -6
  212. package/src/index.js +6 -0
  213. package/src/lib/__tests__/logger.test.js +5 -2
  214. package/src/lib/a2a-protocol.js +25 -1
  215. package/src/lib/activitypub-bridge.js +61 -0
  216. package/src/lib/agent-core.js +4 -0
  217. package/src/lib/agent-economy.js +262 -29
  218. package/src/lib/agent-network.js +7 -1
  219. package/src/lib/agent-sandbox.js +161 -5
  220. package/src/lib/agent-team/task-lease.js +434 -0
  221. package/src/lib/agent-team/team-budget.js +165 -0
  222. package/src/lib/agent-team/team-mailbox.js +106 -0
  223. package/src/lib/agent-team/team-runner.js +282 -0
  224. package/src/lib/agent-team/team-worktree.js +236 -0
  225. package/src/lib/agents.js +18 -0
  226. package/src/lib/async-hook-supervisor.cjs +391 -0
  227. package/src/lib/audit-logger.js +7 -6
  228. package/src/lib/autonomous-developer.js +60 -0
  229. package/src/lib/chat-core.js +17 -4
  230. package/src/lib/collaboration-governance.js +56 -0
  231. package/src/lib/community-governance.js +68 -0
  232. package/src/lib/compliance-manager.js +63 -0
  233. package/src/lib/cross-chain.js +5 -0
  234. package/src/lib/dao-governance.js +151 -2
  235. package/src/lib/did-manager.js +23 -10
  236. package/src/lib/dlp-engine.js +70 -0
  237. package/src/lib/eval/runner.js +251 -0
  238. package/src/lib/eval/tasks.js +626 -0
  239. package/src/lib/eval/trend.js +200 -0
  240. package/src/lib/evolution-system.js +92 -0
  241. package/src/lib/evomap-federation.js +55 -0
  242. package/src/lib/evomap-governance.js +94 -0
  243. package/src/lib/fatal-handler.js +17 -1
  244. package/src/lib/hardening-manager.js +73 -0
  245. package/src/lib/hierarchical-memory.js +14 -8
  246. package/src/lib/knowledge-exporter.js +8 -2
  247. package/src/lib/knowledge-graph.js +79 -0
  248. package/src/lib/llm-providers.js +23 -14
  249. package/src/lib/logger.js +4 -1
  250. package/src/lib/lsp/__tests__/benchmark.test.js +88 -0
  251. package/src/lib/lsp/__tests__/code-intelligence.integration.test.js +129 -0
  252. package/src/lib/lsp/__tests__/code-intelligence.test.js +228 -0
  253. package/src/lib/lsp/__tests__/jsonrpc-stream.test.js +104 -0
  254. package/src/lib/lsp/__tests__/lsp-client.test.js +269 -0
  255. package/src/lib/lsp/__tests__/lsp-manager.test.js +333 -0
  256. package/src/lib/lsp/__tests__/lsp-server-registry.test.js +128 -0
  257. package/src/lib/lsp/benchmark.js +257 -0
  258. package/src/lib/lsp/code-intelligence.js +356 -0
  259. package/src/lib/lsp/jsonrpc-stream.js +139 -0
  260. package/src/lib/lsp/lsp-client.js +339 -0
  261. package/src/lib/lsp/lsp-manager.js +375 -0
  262. package/src/lib/lsp/lsp-server-registry.js +196 -0
  263. package/src/lib/matrix-bridge.js +84 -0
  264. package/src/lib/memory-manager.js +5 -3
  265. package/src/lib/nostr-bridge.js +53 -0
  266. package/src/lib/permission-engine.js +22 -4
  267. package/src/lib/plugin-monitor-supervisor.js +238 -0
  268. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -0
  269. package/src/lib/plugin-runtime/agents.js +51 -0
  270. package/src/lib/plugin-runtime/bin.js +100 -0
  271. package/src/lib/plugin-runtime/hooks.js +100 -0
  272. package/src/lib/plugin-runtime/install.js +396 -0
  273. package/src/lib/plugin-runtime/lsp.js +75 -0
  274. package/src/lib/plugin-runtime/manifest.js +458 -0
  275. package/src/lib/plugin-runtime/mcp.js +89 -0
  276. package/src/lib/plugin-runtime/monitors.js +100 -0
  277. package/src/lib/plugin-runtime/policy.js +152 -0
  278. package/src/lib/plugin-runtime/reload.js +79 -0
  279. package/src/lib/plugin-runtime/remote-source.js +240 -0
  280. package/src/lib/plugin-runtime/scopes.js +197 -0
  281. package/src/lib/plugin-runtime/settings.js +119 -0
  282. package/src/lib/plugin-runtime/signature.js +107 -0
  283. package/src/lib/plugin-runtime/skills.js +43 -0
  284. package/src/lib/plugin-runtime/trust.js +140 -0
  285. package/src/lib/pqc-manager.js +64 -0
  286. package/src/lib/reputation-optimizer.js +101 -0
  287. package/src/lib/sandbox-egress-proxy.js +262 -0
  288. package/src/lib/sandbox-fs-policy.js +112 -0
  289. package/src/lib/sandbox-network-policy.js +151 -0
  290. package/src/lib/sandbox-v2.js +24 -18
  291. package/src/lib/scim-manager.js +36 -0
  292. package/src/lib/session-manager.js +5 -2
  293. package/src/lib/settings-hook-events.cjs +78 -6
  294. package/src/lib/settings-hooks.cjs +30 -3
  295. package/src/lib/settings-loader.cjs +35 -1
  296. package/src/lib/siem-exporter.js +45 -0
  297. package/src/lib/skill-loader.js +38 -2
  298. package/src/lib/skill-marketplace.js +83 -0
  299. package/src/lib/sla-manager.js +88 -0
  300. package/src/lib/social-manager.js +74 -0
  301. package/src/lib/stress-tester.js +65 -0
  302. package/src/lib/tech-learning-engine.js +75 -0
  303. package/src/lib/telemetry/span-recorder.js +237 -0
  304. package/src/lib/tenant-saas.js +107 -0
  305. package/src/lib/terraform-manager.js +67 -0
  306. package/src/lib/token-incentive.js +180 -29
  307. package/src/lib/wallet-manager.js +81 -35
  308. package/src/lib/zkp-engine.js +87 -0
  309. package/src/repl/agent-repl.js +417 -6
  310. package/src/runtime/__tests__/auto-pin.test.js +104 -0
  311. package/src/runtime/agent-core.js +771 -104
  312. package/src/runtime/auto-pin.js +117 -0
  313. package/src/runtime/coding-agent-contract-shared.cjs +82 -8
  314. package/src/runtime/coding-agent-policy.cjs +32 -7
  315. package/src/runtime/fallback-model.js +134 -7
  316. package/src/runtime/headless-runner.js +388 -108
  317. package/src/runtime/mcp-config.js +46 -0
  318. package/src/assets/web-panel/assets/ChatBubbleRenderer-DCNfbdlx.js +0 -1
  319. package/src/assets/web-panel/assets/Terminal-BvAK_Zel.js +0 -3
  320. package/src/assets/web-panel/assets/devWarning-C2Z5LRgq.js +0 -1
  321. package/src/assets/web-panel/assets/index-BGp6Yr-Y.js +0 -1
  322. 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
+ };
@@ -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";