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,104 @@
1
+ /**
2
+ * jsonrpc-stream tests — LSP Content-Length framing. Pure, no IO. Pins the
3
+ * split-chunk / multi-message / multibyte-length / lenient-LF edge cases that a
4
+ * naive framer gets wrong.
5
+ */
6
+
7
+ import { describe, it, expect } from "vitest";
8
+ import {
9
+ encodeMessage,
10
+ parseContentLength,
11
+ MessageBuffer,
12
+ } from "../jsonrpc-stream.js";
13
+
14
+ describe("encodeMessage", () => {
15
+ it("frames a message with a byte-accurate Content-Length", () => {
16
+ const buf = encodeMessage({ jsonrpc: "2.0", id: 1, method: "ping" });
17
+ const text = buf.toString("utf8");
18
+ const body = JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ping" });
19
+ expect(text).toBe(
20
+ `Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`,
21
+ );
22
+ });
23
+
24
+ it("counts BYTES not characters for multibyte content", () => {
25
+ const buf = encodeMessage({ text: "你好" }); // 6 UTF-8 bytes for the two chars
26
+ const headerLen = Number(
27
+ /Content-Length: (\d+)/.exec(buf.toString("utf8"))[1],
28
+ );
29
+ const body = JSON.stringify({ text: "你好" });
30
+ expect(headerLen).toBe(Buffer.byteLength(body, "utf8"));
31
+ // round-trips through the buffer intact
32
+ const mb = new MessageBuffer().append(buf);
33
+ expect(mb.read()).toEqual({ text: "你好" });
34
+ });
35
+ });
36
+
37
+ describe("parseContentLength", () => {
38
+ it("reads the header case-insensitively", () => {
39
+ expect(parseContentLength("content-length: 42")).toBe(42);
40
+ expect(parseContentLength("Content-Length: 7\r\nContent-Type: x")).toBe(
41
+ 7,
42
+ );
43
+ });
44
+ it("returns null when absent or invalid", () => {
45
+ expect(parseContentLength("Content-Type: application/json")).toBeNull();
46
+ expect(parseContentLength("Content-Length: abc")).toBeNull();
47
+ });
48
+ });
49
+
50
+ describe("MessageBuffer", () => {
51
+ it("decodes a single framed message", () => {
52
+ const mb = new MessageBuffer();
53
+ mb.append(encodeMessage({ id: 1, result: "ok" }));
54
+ expect(mb.read()).toEqual({ id: 1, result: "ok" });
55
+ expect(mb.read()).toBeNull();
56
+ });
57
+
58
+ it("decodes multiple messages in one chunk", () => {
59
+ const mb = new MessageBuffer();
60
+ const chunk = Buffer.concat([
61
+ encodeMessage({ id: 1 }),
62
+ encodeMessage({ id: 2 }),
63
+ encodeMessage({ id: 3 }),
64
+ ]);
65
+ mb.append(chunk);
66
+ expect(mb.readAll().map((m) => m.id)).toEqual([1, 2, 3]);
67
+ });
68
+
69
+ it("reassembles a message split across chunks (header and body)", () => {
70
+ const full = encodeMessage({ id: 7, method: "textDocument/definition" });
71
+ const mb = new MessageBuffer();
72
+ // split mid-header and mid-body
73
+ for (let i = 0; i < full.length; i += 5) {
74
+ mb.append(full.slice(i, i + 5));
75
+ }
76
+ expect(mb.read()).toEqual({ id: 7, method: "textDocument/definition" });
77
+ expect(mb.hasPartial).toBe(false);
78
+ });
79
+
80
+ it("retains a partial frame until the rest arrives", () => {
81
+ const full = encodeMessage({ id: 9 });
82
+ const mb = new MessageBuffer();
83
+ mb.append(full.slice(0, full.length - 3));
84
+ expect(mb.read()).toBeNull();
85
+ expect(mb.hasPartial).toBe(true);
86
+ mb.append(full.slice(full.length - 3));
87
+ expect(mb.read()).toEqual({ id: 9 });
88
+ });
89
+
90
+ it("tolerates LF-only header separators", () => {
91
+ const body = JSON.stringify({ id: 5 });
92
+ const mb = new MessageBuffer();
93
+ mb.append(`Content-Length: ${body.length}\n\n${body}`);
94
+ expect(mb.read()).toEqual({ id: 5 });
95
+ });
96
+
97
+ it("skips a body that isn't valid JSON but stays aligned for the next frame", () => {
98
+ const mb = new MessageBuffer();
99
+ const bad = "Content-Length: 3\r\n\r\n{{{";
100
+ mb.append(bad + encodeMessage({ id: 2 }).toString("utf8"));
101
+ // bad frame dropped, good one still decoded
102
+ expect(mb.read()).toEqual({ id: 2 });
103
+ });
104
+ });
@@ -0,0 +1,269 @@
1
+ /**
2
+ * LSPClient tests — JSON-RPC handshake, request/response correlation,
3
+ * notification dispatch, timeouts and crash propagation. A fake child process
4
+ * is injected through `_deps.spawn`; no real language server is spawned.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
8
+ import { EventEmitter } from "events";
9
+ import {
10
+ LSPClient,
11
+ _deps,
12
+ pathToFileUri,
13
+ fileUriToPath,
14
+ normalizeUri,
15
+ buildSpawnCommand,
16
+ } from "../lsp-client.js";
17
+ import { encodeMessage, MessageBuffer } from "../jsonrpc-stream.js";
18
+
19
+ /** A fake language server process. Auto-replies to `initialize`; test controls the rest. */
20
+ class FakeChild extends EventEmitter {
21
+ constructor({ autoInit = true } = {}) {
22
+ super();
23
+ this.stdout = new EventEmitter();
24
+ this.stderr = new EventEmitter();
25
+ this._inbound = new MessageBuffer();
26
+ this.received = [];
27
+ this.killed = false;
28
+ // Arrow keeps `this` bound to the FakeChild (no this-alias needed).
29
+ this.stdin = {
30
+ writable: true,
31
+ write: (buf) => {
32
+ this._inbound.append(buf);
33
+ for (const msg of this._inbound.readAll()) {
34
+ this.received.push(msg);
35
+ if (autoInit && msg.method === "initialize") {
36
+ this.reply(msg.id, { capabilities: { definitionProvider: true } });
37
+ }
38
+ }
39
+ return true;
40
+ },
41
+ };
42
+ }
43
+ reply(id, result) {
44
+ this.stdout.emit("data", encodeMessage({ jsonrpc: "2.0", id, result }));
45
+ }
46
+ replyError(id, error) {
47
+ this.stdout.emit("data", encodeMessage({ jsonrpc: "2.0", id, error }));
48
+ }
49
+ notify(method, params) {
50
+ this.stdout.emit("data", encodeMessage({ jsonrpc: "2.0", method, params }));
51
+ }
52
+ kill() {
53
+ this.killed = true;
54
+ }
55
+ }
56
+
57
+ let origSpawn;
58
+ beforeEach(() => {
59
+ origSpawn = _deps.spawn;
60
+ });
61
+ afterEach(() => {
62
+ _deps.spawn = origSpawn;
63
+ vi.restoreAllMocks();
64
+ });
65
+
66
+ function makeClient(child, opts = {}) {
67
+ _deps.spawn = vi.fn(() => child);
68
+ return new LSPClient({
69
+ command: "fake-lsp",
70
+ args: ["--stdio"],
71
+ rootPath: "/proj",
72
+ ...opts,
73
+ });
74
+ }
75
+
76
+ describe("LSPClient handshake", () => {
77
+ it("spawns, sends initialize, and captures server capabilities", async () => {
78
+ const child = new FakeChild();
79
+ const client = makeClient(child);
80
+ const caps = await client.start();
81
+ expect(caps).toEqual({ definitionProvider: true });
82
+ expect(client.running).toBe(true);
83
+ // sent initialize then initialized notification
84
+ expect(child.received[0].method).toBe("initialize");
85
+ expect(child.received[1].method).toBe("initialized");
86
+ // spawn used shell:false
87
+ expect(_deps.spawn).toHaveBeenCalledWith(
88
+ "fake-lsp",
89
+ ["--stdio"],
90
+ expect.objectContaining({ shell: false }),
91
+ );
92
+ });
93
+
94
+ it("kills the spawned server when the initialize handshake times out (no orphan)", async () => {
95
+ const child = new FakeChild({ autoInit: false }); // never answers initialize
96
+ const client = makeClient(child);
97
+ await expect(client.start({ initTimeoutMs: 20 })).rejects.toThrow(
98
+ /timed out/,
99
+ );
100
+ // The spawned process must be torn down, not left running forever.
101
+ expect(child.killed).toBe(true);
102
+ expect(client.running).toBe(false);
103
+ });
104
+ });
105
+
106
+ describe("LSPClient request/response", () => {
107
+ it("correlates a request to its response by id", async () => {
108
+ const child = new FakeChild();
109
+ const client = makeClient(child);
110
+ await client.start();
111
+ const p = client.request("textDocument/definition", { x: 1 });
112
+ // server replies to the definition request (id follows initialize=1 → 2)
113
+ const req = child.received.find(
114
+ (m) => m.method === "textDocument/definition",
115
+ );
116
+ child.reply(req.id, [
117
+ {
118
+ uri: "file:///proj/a.ts",
119
+ range: {
120
+ start: { line: 0, character: 0 },
121
+ end: { line: 0, character: 3 },
122
+ },
123
+ },
124
+ ]);
125
+ const result = await p;
126
+ expect(result[0].uri).toBe("file:///proj/a.ts");
127
+ });
128
+
129
+ it("rejects a request whose response carries an error", async () => {
130
+ const child = new FakeChild();
131
+ const client = makeClient(child);
132
+ await client.start();
133
+ const p = client.request("bad/method", {});
134
+ const req = child.received.find((m) => m.method === "bad/method");
135
+ child.replyError(req.id, { code: -32601, message: "method not found" });
136
+ await expect(p).rejects.toThrow(/method not found/);
137
+ });
138
+
139
+ it("times out a request with no response", async () => {
140
+ const child = new FakeChild();
141
+ const client = makeClient(child);
142
+ await client.start();
143
+ await expect(
144
+ client.request("slow/op", {}, { timeoutMs: 20 }),
145
+ ).rejects.toThrow(/timed out/);
146
+ });
147
+ });
148
+
149
+ describe("LSPClient notifications", () => {
150
+ it("emits server notifications by method", async () => {
151
+ const child = new FakeChild();
152
+ const client = makeClient(child);
153
+ await client.start();
154
+ const got = vi.fn();
155
+ client.on("notify:textDocument/publishDiagnostics", got);
156
+ child.notify("textDocument/publishDiagnostics", {
157
+ uri: "file:///proj/a.ts",
158
+ diagnostics: [],
159
+ });
160
+ expect(got).toHaveBeenCalledWith({
161
+ uri: "file:///proj/a.ts",
162
+ diagnostics: [],
163
+ });
164
+ });
165
+
166
+ it("auto-replies null to server→client requests so the server never blocks", async () => {
167
+ const child = new FakeChild();
168
+ const client = makeClient(child);
169
+ await client.start();
170
+ child.stdout.emit(
171
+ "data",
172
+ encodeMessage({
173
+ jsonrpc: "2.0",
174
+ id: 999,
175
+ method: "workspace/configuration",
176
+ params: {},
177
+ }),
178
+ );
179
+ const reply = child.received.find((m) => m.id === 999 && m.method == null);
180
+ expect(reply).toEqual({ jsonrpc: "2.0", id: 999, result: null });
181
+ });
182
+ });
183
+
184
+ describe("LSPClient lifecycle", () => {
185
+ it("fails all pending requests when the process exits", async () => {
186
+ const child = new FakeChild();
187
+ const client = makeClient(child);
188
+ await client.start();
189
+ const p = client.request("op", {});
190
+ child.emit("exit", 1, null);
191
+ await expect(p).rejects.toThrow(/exited|stopped/);
192
+ expect(client.running).toBe(false);
193
+ });
194
+
195
+ it("stop() shuts down gracefully and is idempotent", async () => {
196
+ const child = new FakeChild();
197
+ const client = makeClient(child);
198
+ await client.start();
199
+ // auto-reply to shutdown
200
+ child.stdin.write = (buf) => {
201
+ const mb = new MessageBuffer().append(buf);
202
+ for (const m of mb.readAll()) {
203
+ child.received.push(m);
204
+ if (m.method === "shutdown") child.reply(m.id, null);
205
+ }
206
+ return true;
207
+ };
208
+ await client.stop();
209
+ await client.stop(); // no throw second time
210
+ expect(child.killed).toBe(true);
211
+ });
212
+ });
213
+
214
+ describe("uri <-> path", () => {
215
+ it("round-trips a POSIX path", () => {
216
+ expect(fileUriToPath(pathToFileUri("/home/u/a b.ts"))).toBe(
217
+ process.platform === "win32" ? "\\home\\u\\a b.ts" : "/home/u/a b.ts",
218
+ );
219
+ });
220
+ it("builds a windows drive URI", () => {
221
+ expect(pathToFileUri("C:\\proj\\a.ts")).toBe("file:///C%3A/proj/a.ts");
222
+ });
223
+ });
224
+
225
+ describe("normalizeUri", () => {
226
+ it("lowercases the windows drive letter so client/server keys match", () => {
227
+ // path.resolve yields C:, servers echo c: — must collapse to one key
228
+ expect(normalizeUri("file:///C%3A/proj/a.ts")).toBe(
229
+ "file:///c%3A/proj/a.ts",
230
+ );
231
+ expect(normalizeUri("file:///c%3A/proj/a.ts")).toBe(
232
+ "file:///c%3A/proj/a.ts",
233
+ );
234
+ expect(normalizeUri("file:///C:/proj/a.ts")).toBe("file:///c:/proj/a.ts");
235
+ });
236
+ it("leaves POSIX file URIs unchanged", () => {
237
+ expect(normalizeUri("file:///home/u/a.ts")).toBe("file:///home/u/a.ts");
238
+ });
239
+ });
240
+
241
+ describe("buildSpawnCommand", () => {
242
+ const isWin = process.platform === "win32";
243
+ it("passes non-batch commands through verbatim", () => {
244
+ const spec = buildSpawnCommand("/usr/bin/gopls", []);
245
+ expect(spec).toEqual({
246
+ file: "/usr/bin/gopls",
247
+ args: [],
248
+ windowsVerbatimArguments: false,
249
+ });
250
+ });
251
+ it.runIf(isWin)(
252
+ "routes a .cmd shim through cmd.exe /d /s /c with a quoted path",
253
+ () => {
254
+ const spec = buildSpawnCommand(
255
+ "C:\\proj\\node_modules\\.bin\\typescript-language-server.cmd",
256
+ ["--stdio"],
257
+ );
258
+ expect(spec.windowsVerbatimArguments).toBe(true);
259
+ expect(spec.args.slice(0, 3)).toEqual(["/d", "/s", "/c"]);
260
+ expect(spec.args[3]).toBe(
261
+ '"C:\\proj\\node_modules\\.bin\\typescript-language-server.cmd" --stdio',
262
+ );
263
+ },
264
+ );
265
+ it.runIf(isWin)("quotes a path containing spaces", () => {
266
+ const spec = buildSpawnCommand("C:\\Program Files\\svr.cmd", []);
267
+ expect(spec.args[3]).toBe('"C:\\Program Files\\svr.cmd"');
268
+ });
269
+ });
@@ -0,0 +1,333 @@
1
+ /**
2
+ * LSPManager tests — server pooling, document open/change sync, diagnostics
3
+ * collection, and position conversion. A fake client is injected via
4
+ * `_deps.createClient`; the registry resolves a server via `_deps.existsSync`.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
8
+ import { EventEmitter } from "events";
9
+ import path from "path";
10
+ import {
11
+ LSPManager,
12
+ toLspPosition,
13
+ fromLspPosition,
14
+ _deps,
15
+ } from "../lsp-manager.js";
16
+ import { _deps as regDeps } from "../lsp-server-registry.js";
17
+
18
+ /** Minimal fake LSPClient: records notifications, canned requests, emits events. */
19
+ class FakeClient extends EventEmitter {
20
+ constructor(opts) {
21
+ super();
22
+ this.rootPath = opts.rootPath;
23
+ this.running = true;
24
+ this.notifications = [];
25
+ this.started = false;
26
+ }
27
+ async start() {
28
+ this.started = true;
29
+ return { definitionProvider: true };
30
+ }
31
+ notify(method, params) {
32
+ this.notifications.push({ method, params });
33
+ }
34
+ async request() {
35
+ return null;
36
+ }
37
+ async stop() {
38
+ this.running = false;
39
+ }
40
+ }
41
+
42
+ let origCreate, origRead, origExistsMgr, origExistsReg;
43
+ let lastClient;
44
+
45
+ beforeEach(() => {
46
+ origCreate = _deps.createClient;
47
+ origRead = _deps.readFileSync;
48
+ origExistsMgr = _deps.existsSync;
49
+ origExistsReg = regDeps.existsSync;
50
+
51
+ _deps.createClient = (opts) => {
52
+ lastClient = new FakeClient(opts);
53
+ return lastClient;
54
+ };
55
+ _deps.readFileSync = () => "const x = 1;\n";
56
+ // root inference: pretend the nearest /proj ancestor has package.json.
57
+ // Suffix match tolerates path.resolve prepending a drive letter on Windows.
58
+ _deps.existsSync = (p) =>
59
+ String(p).replace(/\\/g, "/").endsWith("/proj/package.json");
60
+ // registry: pretend the TS server bin exists in <root>/node_modules/.bin
61
+ const isWin = process.platform === "win32";
62
+ const binName = isWin
63
+ ? "typescript-language-server.cmd"
64
+ : "typescript-language-server";
65
+ regDeps.existsSync = (p) =>
66
+ String(p).replace(/\\/g, "/").endsWith(`/node_modules/.bin/${binName}`);
67
+ });
68
+
69
+ afterEach(() => {
70
+ _deps.createClient = origCreate;
71
+ _deps.readFileSync = origRead;
72
+ _deps.existsSync = origExistsMgr;
73
+ regDeps.existsSync = origExistsReg;
74
+ vi.restoreAllMocks();
75
+ });
76
+
77
+ describe("position helpers", () => {
78
+ it("converts 1-based ⇄ 0-based", () => {
79
+ expect(toLspPosition({ line: 1, col: 1 })).toEqual({
80
+ line: 0,
81
+ character: 0,
82
+ });
83
+ expect(toLspPosition({ line: 10, col: 5 })).toEqual({
84
+ line: 9,
85
+ character: 4,
86
+ });
87
+ expect(fromLspPosition({ line: 0, character: 0 })).toEqual({
88
+ line: 1,
89
+ col: 1,
90
+ });
91
+ expect(fromLspPosition({ line: 9, character: 4 })).toEqual({
92
+ line: 10,
93
+ col: 5,
94
+ });
95
+ });
96
+ it("clamps below 1 to 0 (no negatives to the server)", () => {
97
+ expect(toLspPosition({ line: 0, col: 0 })).toEqual({
98
+ line: 0,
99
+ character: 0,
100
+ });
101
+ });
102
+ });
103
+
104
+ describe("ensureFor", () => {
105
+ it("starts a server and sends didOpen the first time", async () => {
106
+ const mgr = new LSPManager();
107
+ const ready = await mgr.ensureFor("/proj/src/a.ts");
108
+ expect(ready.languageId).toBe("typescript");
109
+ expect(ready.root.replace(/\\/g, "/")).toMatch(/\/proj$/);
110
+ expect(lastClient.started).toBe(true);
111
+ const open = lastClient.notifications.find(
112
+ (n) => n.method === "textDocument/didOpen",
113
+ );
114
+ expect(open.params.textDocument.text).toBe("const x = 1;\n");
115
+ await mgr.disposeAll();
116
+ });
117
+
118
+ it("sends didChange (not a second didOpen) on the next access", async () => {
119
+ const mgr = new LSPManager();
120
+ await mgr.ensureFor("/proj/src/a.ts");
121
+ await mgr.ensureFor("/proj/src/a.ts");
122
+ const opens = lastClient.notifications.filter(
123
+ (n) => n.method === "textDocument/didOpen",
124
+ );
125
+ const changes = lastClient.notifications.filter(
126
+ (n) => n.method === "textDocument/didChange",
127
+ );
128
+ expect(opens).toHaveLength(1);
129
+ expect(changes).toHaveLength(1);
130
+ expect(changes[0].params.textDocument.version).toBe(2);
131
+ await mgr.disposeAll();
132
+ });
133
+
134
+ it("reuses one server for two files in the same project/language", async () => {
135
+ const mgr = new LSPManager();
136
+ await mgr.ensureFor("/proj/src/a.ts");
137
+ const first = lastClient;
138
+ await mgr.ensureFor("/proj/src/b.ts");
139
+ expect(lastClient).toBe(first); // same client instance
140
+ expect(mgr.activeServerCount).toBe(1);
141
+ await mgr.disposeAll();
142
+ });
143
+
144
+ it("spawns only ONE server when two concurrent calls race the same key", async () => {
145
+ // Regression: ensureFor did Map.get(miss)→await start→Map.set with no
146
+ // in-flight dedupe, so two concurrent calls (the read-only agent batch fires
147
+ // several code_intelligence calls at once) both spawned a server; the second
148
+ // set() orphaned the first process. Gate start() so both calls interleave
149
+ // inside the await window, then assert a single spawn.
150
+ let created = 0;
151
+ let releaseStart;
152
+ const gate = new Promise((r) => {
153
+ releaseStart = r;
154
+ });
155
+ _deps.createClient = (opts) => {
156
+ created += 1;
157
+ lastClient = new FakeClient(opts);
158
+ const origStart = lastClient.start.bind(lastClient);
159
+ lastClient.start = async () => {
160
+ await gate; // hold both starts open until released
161
+ return origStart();
162
+ };
163
+ return lastClient;
164
+ };
165
+ const mgr = new LSPManager();
166
+ const p1 = mgr.ensureFor("/proj/src/a.ts");
167
+ const p2 = mgr.ensureFor("/proj/src/b.ts");
168
+ releaseStart();
169
+ const [r1, r2] = await Promise.all([p1, p2]);
170
+ expect(created).toBe(1); // NOT two — the race is deduped
171
+ expect(mgr.activeServerCount).toBe(1);
172
+ expect(r1.client).toBe(r2.client);
173
+ await mgr.disposeAll();
174
+ });
175
+
176
+ it("returns unavailable for an unsupported extension", async () => {
177
+ const mgr = new LSPManager();
178
+ const ready = await mgr.ensureFor("/proj/readme.txt");
179
+ expect(ready.unavailable).toBe(true);
180
+ expect(ready.reason).toMatch(/no language mapping/);
181
+ await mgr.disposeAll();
182
+ });
183
+
184
+ it("returns unavailable when no server bin is installed", async () => {
185
+ regDeps.existsSync = () => false; // nothing installed
186
+ const mgr = new LSPManager();
187
+ const ready = await mgr.ensureFor("/proj/src/a.ts");
188
+ expect(ready.unavailable).toBe(true);
189
+ expect(ready.reason).toMatch(/no language server installed/);
190
+ await mgr.disposeAll();
191
+ });
192
+ });
193
+
194
+ describe("crash-loop restart guard", () => {
195
+ // Simulate a language server process dying: the client marks itself not
196
+ // running and emits `exit`, exactly as the real LSPClient does on child exit.
197
+ function crash(client) {
198
+ client.running = false;
199
+ client.emit("exit", 1, null);
200
+ }
201
+
202
+ it("re-spawns a crashed server on the next request (auto-restart)", async () => {
203
+ const mgr = new LSPManager();
204
+ await mgr.ensureFor("/proj/src/a.ts");
205
+ const first = lastClient;
206
+ crash(first);
207
+ const ready = await mgr.ensureFor("/proj/src/a.ts");
208
+ expect(ready.unavailable).toBeUndefined();
209
+ expect(lastClient).not.toBe(first); // a FRESH client was spawned
210
+ expect(lastClient.started).toBe(true);
211
+ expect(ready.client).toBe(lastClient);
212
+ await mgr.disposeAll();
213
+ });
214
+
215
+ it("quarantines a server that crash-loops past maxRestarts within the window", async () => {
216
+ let t = 1000;
217
+ const mgr = new LSPManager({
218
+ maxRestarts: 3,
219
+ restartWindowMs: 30000,
220
+ now: () => t,
221
+ });
222
+ for (let i = 0; i < 3; i++) {
223
+ await mgr.ensureFor("/proj/src/a.ts");
224
+ t += 100;
225
+ crash(lastClient);
226
+ }
227
+ // 3 crashes are now inside the window → the next request quarantines the
228
+ // server (degrade to text search) rather than thrash-respawning it.
229
+ const before = lastClient;
230
+ const ready = await mgr.ensureFor("/proj/src/a.ts");
231
+ expect(ready.unavailable).toBe(true);
232
+ expect(ready.quarantined).toBe(true);
233
+ expect(ready.reason).toMatch(/crash-looped/);
234
+ expect(lastClient).toBe(before); // did NOT spawn yet another
235
+ await mgr.disposeAll();
236
+ });
237
+
238
+ it("recovers and re-spawns once the crash window clears", async () => {
239
+ let t = 1000;
240
+ const mgr = new LSPManager({
241
+ maxRestarts: 3,
242
+ restartWindowMs: 30000,
243
+ now: () => t,
244
+ });
245
+ for (let i = 0; i < 3; i++) {
246
+ await mgr.ensureFor("/proj/src/a.ts");
247
+ t += 100;
248
+ crash(lastClient);
249
+ }
250
+ expect((await mgr.ensureFor("/proj/src/a.ts")).quarantined).toBe(true);
251
+ // Stay healthy past the window → crash history prunes → a fresh start is
252
+ // allowed again (a server isn't quarantined forever).
253
+ t += 30001;
254
+ const quarantined = lastClient;
255
+ const ready = await mgr.ensureFor("/proj/src/a.ts");
256
+ expect(ready.unavailable).toBeUndefined();
257
+ expect(lastClient).not.toBe(quarantined);
258
+ await mgr.disposeAll();
259
+ });
260
+ });
261
+
262
+ describe("diagnostics", () => {
263
+ it("collects pushed diagnostics keyed by file", async () => {
264
+ const mgr = new LSPManager();
265
+ await mgr.ensureFor("/proj/src/a.ts");
266
+ lastClient.emit("notify:textDocument/publishDiagnostics", {
267
+ uri: (await import("../lsp-client.js")).pathToFileUri(
268
+ path.resolve("/proj/src/a.ts"),
269
+ ),
270
+ diagnostics: [
271
+ {
272
+ message: "boom",
273
+ severity: 1,
274
+ range: {
275
+ start: { line: 0, character: 0 },
276
+ end: { line: 0, character: 1 },
277
+ },
278
+ },
279
+ ],
280
+ });
281
+ const diags = mgr.getDiagnostics("/proj/src/a.ts");
282
+ expect(diags).toHaveLength(1);
283
+ expect(diags[0].message).toBe("boom");
284
+ await mgr.disposeAll();
285
+ });
286
+
287
+ it("clears stale diagnostics + readiness state when the server crashes", async () => {
288
+ const { pathToFileUri } = await import("../lsp-client.js");
289
+ const mgr = new LSPManager();
290
+ await mgr.ensureFor("/proj/src/a.ts");
291
+ lastClient.emit("notify:textDocument/publishDiagnostics", {
292
+ uri: pathToFileUri(path.resolve("/proj/src/a.ts")),
293
+ diagnostics: [
294
+ {
295
+ message: "boom",
296
+ severity: 1,
297
+ range: {
298
+ start: { line: 0, character: 0 },
299
+ end: { line: 0, character: 1 },
300
+ },
301
+ },
302
+ ],
303
+ });
304
+ expect(mgr.getDiagnostics("/proj/src/a.ts")).toHaveLength(1);
305
+
306
+ // Server crashes. Its diagnostics + publish bookkeeping must NOT survive —
307
+ // otherwise the readiness waiters resolve on the pre-crash publish and a
308
+ // post-restart query returns these stale results.
309
+ lastClient.running = false;
310
+ lastClient.emit("exit", 1, null);
311
+ expect(mgr.getDiagnostics("/proj/src/a.ts")).toEqual([]);
312
+
313
+ // A fresh query re-spawns the server; before it publishes, waitForDiagnostics
314
+ // must not hand back the stale set (returns empty within the timeout budget).
315
+ await mgr.ensureFor("/proj/src/a.ts");
316
+ const afterRestart = await mgr.waitForDiagnostics("/proj/src/a.ts", {
317
+ timeoutMs: 60,
318
+ settleMs: 20,
319
+ });
320
+ expect(afterRestart).toEqual([]);
321
+ await mgr.disposeAll();
322
+ });
323
+ });
324
+
325
+ describe("disposeAll", () => {
326
+ it("stops all servers and blocks further use", async () => {
327
+ const mgr = new LSPManager();
328
+ await mgr.ensureFor("/proj/src/a.ts");
329
+ await mgr.disposeAll();
330
+ expect(lastClient.running).toBe(false);
331
+ await expect(mgr.ensureFor("/proj/src/a.ts")).rejects.toThrow(/disposed/);
332
+ });
333
+ });