chainlesschain 0.162.149 → 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 (273) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-BJif14yR.js → AIOps-CBLvrjeG.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-CXek6gJY.js → ActionButton-DYv2GtE0.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-BgQhdAJi.js → Analytics-CL6PZ-Ww.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DTYl5NtR.js → AppLayout-xuyHt4uA.js} +3 -3
  6. package/src/assets/web-panel/assets/{Audit-BzATQAeR.js → Audit-DOvSVycq.js} +1 -1
  7. package/src/assets/web-panel/assets/{Backup-BzdPEQhL.js → Backup-DMhfTswr.js} +1 -1
  8. package/src/assets/web-panel/assets/{BaseInput-BM0KVh8E.js → BaseInput-CQxDdm63.js} +1 -1
  9. package/src/assets/web-panel/assets/{Chat-D7fHXHkz.js → Chat-BEeNhK2a.js} +5 -5
  10. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Wm34RR-b.js → ChatBubbleRenderer-8nul8eTq.js} +1 -1
  11. package/src/assets/web-panel/assets/{Checkbox-CAiSQ9vO.js → Checkbox-CL04O-ER.js} +1 -1
  12. package/src/assets/web-panel/assets/{Codegen-Df40CrEe.js → Codegen-C50o-n7x.js} +1 -1
  13. package/src/assets/web-panel/assets/{Col-DIT2fNFU.js → Col-CH-bDkzs.js} +1 -1
  14. package/src/assets/web-panel/assets/{Community-CSTIbW2k.js → Community-DqAIMvSe.js} +1 -1
  15. package/src/assets/web-panel/assets/{Compact-DtqXZZUi.js → Compact-C43fo_my.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compliance-CUtzw6o7.js → Compliance-BERnMrdP.js} +1 -1
  17. package/src/assets/web-panel/assets/{Cowork-CezmlnmU.js → Cowork-Baw3w1gp.js} +4 -4
  18. package/src/assets/web-panel/assets/{Cron-P4BITarg.js → Cron-BEqAHF1-.js} +2 -2
  19. package/src/assets/web-panel/assets/{Crosschain-BRRiYwvq.js → Crosschain-CvnmKwK4.js} +1 -1
  20. package/src/assets/web-panel/assets/{DID-L5ijB9i4.js → DID-iMPxgVdt.js} +2 -2
  21. package/src/assets/web-panel/assets/{Dashboard-CFxro4ob.js → Dashboard--4SJX3YR.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dropdown-BmAFCQ71.js → Dropdown-BgLV6xgz.js} +1 -1
  23. package/src/assets/web-panel/assets/{EmailListRenderer-DJBCEuon.js → EmailListRenderer-Bvb8oUD8.js} +1 -1
  24. package/src/assets/web-panel/assets/{FamilyGuardDashboard-z4oLq6eT.js → FamilyGuardDashboard-DpXPjJB5.js} +1 -1
  25. package/src/assets/web-panel/assets/{Federation-DsVCpHMF.js → Federation-BbFfJ1Xi.js} +1 -1
  26. package/src/assets/web-panel/assets/{FormItemContext-DP3bP5th.js → FormItemContext-DeGq1B-q.js} +1 -1
  27. package/src/assets/web-panel/assets/{GenericCardRenderer-BAXP2Gc6.js → GenericCardRenderer-CNW7s9zM.js} +1 -1
  28. package/src/assets/web-panel/assets/{Git-I3g_bAw9.js → Git-L1PjW-lT.js} +2 -2
  29. package/src/assets/web-panel/assets/{Governance-jHS5-ioS.js → Governance-PK-X58to.js} +1 -1
  30. package/src/assets/web-panel/assets/{Inference-CPL7sfx9.js → Inference-BePWEH-d.js} +1 -1
  31. package/src/assets/web-panel/assets/{KnowledgeGraph-Jf236h4C.js → KnowledgeGraph-LF_sBvdL.js} +1 -1
  32. package/src/assets/web-panel/assets/{Logs-Cm2tcSKg.js → Logs-BKkfk9hy.js} +2 -2
  33. package/src/assets/web-panel/assets/{Marketplace-CZK82rhi.js → Marketplace-BNMqUDla.js} +1 -1
  34. package/src/assets/web-panel/assets/{McpTools-CZCeji7a.js → McpTools-w4WPiyz2.js} +5 -5
  35. package/src/assets/web-panel/assets/{Memory-3l2KVS9k.js → Memory-DUKMVGNi.js} +2 -2
  36. package/src/assets/web-panel/assets/{MobileBridge-VYdpoLh6.js → MobileBridge-BWhiEyTJ.js} +1 -1
  37. package/src/assets/web-panel/assets/{MobileProjects-CiB9cmm1.js → MobileProjects-Ocf5JEkX.js} +1 -1
  38. package/src/assets/web-panel/assets/{Mtc-wCFZbBuL.js → Mtc-D2B6MMhU.js} +4 -4
  39. package/src/assets/web-panel/assets/{MtcAudit-1a3THIyG.js → MtcAudit-DdYjCq1O.js} +2 -2
  40. package/src/assets/web-panel/assets/{Multisig-DMzLB2hG.js → Multisig-Cb1hfuUZ.js} +3 -3
  41. package/src/assets/web-panel/assets/{NLProgramming-D9wZbf6l.js → NLProgramming-CTeImGp7.js} +1 -1
  42. package/src/assets/web-panel/assets/{Notes-hrFe2ZM-.js → Notes-BjYNc7eQ.js} +4 -4
  43. package/src/assets/web-panel/assets/{NotificationSettings-CHGX5Jwm.js → NotificationSettings-Dn_NtRXQ.js} +1 -1
  44. package/src/assets/web-panel/assets/OrderTableRenderer-DVYYIizh.js +1 -0
  45. package/src/assets/web-panel/assets/{Organization-By7FkhtY.js → Organization-Be2Kmr5j.js} +4 -4
  46. package/src/assets/web-panel/assets/{Overflow--khGSzJn.js → Overflow-Cu_-qRlR.js} +1 -1
  47. package/src/assets/web-panel/assets/{P2P-CAG9dH35.js → P2P-DD2HoGzE.js} +2 -2
  48. package/src/assets/web-panel/assets/{PdhVaultBrowser-B3SQZmK9.js → PdhVaultBrowser-BRI7DVth.js} +3 -3
  49. package/src/assets/web-panel/assets/{Permissions-D35ec6JA.js → Permissions-C9Srcs2M.js} +3 -3
  50. package/src/assets/web-panel/assets/{PersonalDataHub-CxBF7hW_.js → PersonalDataHub-CUqcMgo1.js} +3 -3
  51. package/src/assets/web-panel/assets/{Pipeline-s6EtOO1s.js → Pipeline-HBrQGLXt.js} +1 -1
  52. package/src/assets/web-panel/assets/{Privacy-BbJYmWmO.js → Privacy-D-B0vjwh.js} +1 -1
  53. package/src/assets/web-panel/assets/{ProjectInit-VGQq1jMT.js → ProjectInit-eTeLIu9e.js} +2 -2
  54. package/src/assets/web-panel/assets/{ProjectSettings-B1ew3Teh.js → ProjectSettings-DrRqCsC6.js} +2 -2
  55. package/src/assets/web-panel/assets/Projects-1G9DNOhI.js +1 -0
  56. package/src/assets/web-panel/assets/{Providers-mrO47FIK.js → Providers-RkTZzpxP.js} +1 -1
  57. package/src/assets/web-panel/assets/{QrScannerModal-IJF2mHyw.js → QrScannerModal-BSCUgsgC.js} +1 -1
  58. package/src/assets/web-panel/assets/{QuickAsk-CU0wN_Z0.js → QuickAsk-CHgyM3Bz.js} +1 -1
  59. package/src/assets/web-panel/assets/{Recommend-DhN37R6G.js → Recommend-0lry4OZq.js} +1 -1
  60. package/src/assets/web-panel/assets/{RemoteSession-D_xOX_hu.js → RemoteSession-Cn45UroZ.js} +2 -2
  61. package/src/assets/web-panel/assets/{Reputation-D_Ssv7uH.js → Reputation-Q-Zjb707.js} +1 -1
  62. package/src/assets/web-panel/assets/{Row-Kx5dKxX7.js → Row-BdM70oda.js} +1 -1
  63. package/src/assets/web-panel/assets/{RssFeed-BlGwqZkY.js → RssFeed-DmOAKKap.js} +2 -2
  64. package/src/assets/web-panel/assets/{Search-hLFQzxpb.js → Search-Dwavbm07.js} +1 -1
  65. package/src/assets/web-panel/assets/{Security-DMlvsVt4.js → Security-DukXFCnk.js} +3 -3
  66. package/src/assets/web-panel/assets/{Services-Bd0Qsj2m.js → Services-BIlRnITu.js} +2 -2
  67. package/src/assets/web-panel/assets/{Skeleton-DFclq9X3.js → Skeleton-YYVQdhhQ.js} +1 -1
  68. package/src/assets/web-panel/assets/{Skills-BNtln2qY.js → Skills-B8_iYHz2.js} +1 -1
  69. package/src/assets/web-panel/assets/{Sla-Dch5H19G.js → Sla-BdVrhT31.js} +1 -1
  70. package/src/assets/web-panel/assets/{SpeechSettings-2UfQcU1g.js → SpeechSettings-CD3ggRx7.js} +1 -1
  71. package/src/assets/web-panel/assets/{SyncSettings-CD9YQr4i.js → SyncSettings-Bp02VZFH.js} +2 -2
  72. package/src/assets/web-panel/assets/{Tasks-BNu-7MzI.js → Tasks-DEVmCEsr.js} +1 -1
  73. package/src/assets/web-panel/assets/{Templates-D1n9CaIR.js → Templates-B6czWc4N.js} +1 -1
  74. package/src/assets/web-panel/assets/{Tenant-DenL3iBW.js → Tenant-BCJLv17r.js} +1 -1
  75. package/src/assets/web-panel/assets/{Terminal-ZzU0Io1Y.js → Terminal-CD132d2Y.js} +2 -2
  76. package/src/assets/web-panel/assets/{TimelineRenderer-D87IfukO.js → TimelineRenderer-ZB-CvkZl.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tokens-BkiZc8E3.js → Tokens-BnSY0S-s.js} +1 -1
  78. package/src/assets/web-panel/assets/{Trigger-BNRSytGN.js → Trigger-VZw9Oy2w.js} +1 -1
  79. package/src/assets/web-panel/assets/{Trust-Bwbd4X2m.js → Trust-pZq7Hjd2.js} +1 -1
  80. package/src/assets/web-panel/assets/{UkeySign-B8O2bs0o.js → UkeySign-DnQaqk4q.js} +1 -1
  81. package/src/assets/web-panel/assets/{VideoEditing-C2fL8zmH.js → VideoEditing-BEUfmvJV.js} +1 -1
  82. package/src/assets/web-panel/assets/{Wallet-BORYDdE1.js → Wallet-CoZKMXJ2.js} +4 -4
  83. package/src/assets/web-panel/assets/{WebAuthn-C0V5S2FM.js → WebAuthn-CBOGVx1I.js} +5 -5
  84. package/src/assets/web-panel/assets/{WorkflowEditor-2dR0fcHL.js → WorkflowEditor-huSKn7TT.js} +1 -1
  85. package/src/assets/web-panel/assets/{chat-BmuAFAn8.js → chat-A_3w6FBO.js} +1 -1
  86. package/src/assets/web-panel/assets/{colors-CN3smMcK.js → colors-D1Bem9Oy.js} +1 -1
  87. package/src/assets/web-panel/assets/{compact-item-DCnxb4kW.js → compact-item-CJYJj0oO.js} +1 -1
  88. package/src/assets/web-panel/assets/{createContext-vlR43pHn.js → createContext-BkbFiKT4.js} +1 -1
  89. package/src/assets/web-panel/assets/devWarning-WDr3_M_N.js +1 -0
  90. package/src/assets/web-panel/assets/{hasIn-D04tHYFd.js → hasIn-CKvUafVY.js} +1 -1
  91. package/src/assets/web-panel/assets/{index-0DnaZGkC.js → index-AjrSPnvv.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-BudvKQfG.js → index-B8i0lViZ.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-BeGDEXFs.js → index-B9Svn0zc.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-D5LZVZ0L.js → index-BHdHSX06.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BTPEZTk1.js → index-BHdailyo.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DIpY0qM5.js → index-BJwlEnYo.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-CsAsCPJ6.js → index-BOoAQjJz.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-ljuBiQW9.js → index-BSgWxAOG.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-hpvvtAZ3.js → index-BdutMsne.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-_FIkN3jd.js → index-BiDWxzbn.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C-6NO5il.js → index-By5a0T_W.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-B78xL3s2.js → index-C-WIY-FQ.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-ClhAHDK3.js → index-C37f6iey.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-ToUh2b1-.js → index-C9bWmTcO.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-D28DeAAB.js → index-CBaosWRO.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-D8vwp4qp.js → index-CBlBVJ_m.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BqhASEL4.js → index-CD62EEGo.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DDwLgQY9.js → index-CUj-l7GM.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DbVsQBOH.js → index-CVS8Ymuq.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-Cr-A0FYJ.js → index-ChkXUj3f.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-D65_XseV.js → index-Cj0OZ-37.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BXRg8GFQ.js → index-Co3OFL0t.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-430S68MN.js → index-Crp8CbRg.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CngCC8hC.js → index-D22zNXiS.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-HhNjnCfe.js → index-DTs_D1OL.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CP9m9Ggr.js → index-DfgCdFrN.js} +3 -3
  117. package/src/assets/web-panel/assets/{index-jzR7Ujuw.js → index-DjYrecNb.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CTOCnIQ7.js → index-DpW9cf3e.js} +1 -1
  119. package/src/assets/web-panel/assets/index-Dwix3p4g.js +1 -0
  120. package/src/assets/web-panel/assets/{index-D_n8_vfI.js → index-F--HuPG1.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BasvsWDM.js → index-Fuk6t_3K.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CnXebZLL.js → index-JEAGMii9.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DYAtmqiv.js → index-PF-mpv8K.js} +1 -1
  124. package/src/assets/web-panel/assets/index-SHaoZ0CA.js +1 -0
  125. package/src/assets/web-panel/assets/{index-DTWg-18U.js → index-b-sbVdjQ.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-C5Sxb1sE.js → index-pyvMVX8r.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-TslMTwNy.js → index-qNTtOVi_.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DTtRscUa.js → index-tgO8iza6.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DbADHwhr.js → index-ttDQVhZy.js} +1 -1
  130. package/src/assets/web-panel/assets/{initDefaultProps-C3wUb4je.js → initDefaultProps-DtrnfsZH.js} +1 -1
  131. package/src/assets/web-panel/assets/{motion-D-jYFUNA.js → motion-D4zqSaX3.js} +1 -1
  132. package/src/assets/web-panel/assets/{move-tqb8nwJ2.js → move-CGFGOUQj.js} +1 -1
  133. package/src/assets/web-panel/assets/{mtc-parser-DSOjMJMz.js → mtc-parser-DH2HvkJl.js} +1 -1
  134. package/src/assets/web-panel/assets/{omit-CJ7VimIW.js → omit-jJ2at5HX.js} +1 -1
  135. package/src/assets/web-panel/assets/{pickAttrs-qXiG1iT3.js → pickAttrs-CRyMT1Fv.js} +1 -1
  136. package/src/assets/web-panel/assets/{placementArrow-CkGBcy1Z.js → placementArrow-DZ_CX0Pt.js} +1 -1
  137. package/src/assets/web-panel/assets/{responsiveObserve-D3WvTlLO.js → responsiveObserve-wruHk-h5.js} +1 -1
  138. package/src/assets/web-panel/assets/{slide-BEE2ftge.js → slide-BGgx8q67.js} +1 -1
  139. package/src/assets/web-panel/assets/{statusUtils-CsfBpxiG.js → statusUtils-Do5M7gKm.js} +1 -1
  140. package/src/assets/web-panel/assets/{styleChecker-xPrIiJPI.js → styleChecker-CRa-GZpX.js} +1 -1
  141. package/src/assets/web-panel/assets/{useFlexGapSupport-C5Yd-SCR.js → useFlexGapSupport-BxPcq6pj.js} +1 -1
  142. package/src/assets/web-panel/assets/{useFs-D_MDS8HI.js → useFs-v5O2ZcJm.js} +1 -1
  143. package/src/assets/web-panel/assets/{usePersonalDataHub-BqJh-usA.js → usePersonalDataHub--z2FU0Px.js} +1 -1
  144. package/src/assets/web-panel/assets/{vnode-chm_p-gz.js → vnode-1emidVKd.js} +1 -1
  145. package/src/assets/web-panel/assets/{zoom-3-YDNz0Z.js → zoom-CcJaRyHs.js} +1 -1
  146. package/src/assets/web-panel/index.html +1 -1
  147. package/src/commands/a2a.js +19 -4
  148. package/src/commands/activitypub.js +20 -3
  149. package/src/commands/agent.js +27 -5
  150. package/src/commands/audit.js +40 -24
  151. package/src/commands/codeintel.js +68 -0
  152. package/src/commands/collab.js +15 -2
  153. package/src/commands/compliance.js +5 -0
  154. package/src/commands/config.js +4 -1
  155. package/src/commands/dao.js +86 -14
  156. package/src/commands/dev.js +2 -0
  157. package/src/commands/did.js +7 -1
  158. package/src/commands/dlp.js +23 -1
  159. package/src/commands/economy.js +29 -3
  160. package/src/commands/eval.js +7 -0
  161. package/src/commands/evomap.js +26 -0
  162. package/src/commands/governance.js +17 -3
  163. package/src/commands/hardening.js +18 -0
  164. package/src/commands/incentive.js +5 -0
  165. package/src/commands/init.js +46 -2
  166. package/src/commands/kg.js +4 -0
  167. package/src/commands/loop.js +6 -1
  168. package/src/commands/lowcode.js +15 -2
  169. package/src/commands/marketplace.js +40 -6
  170. package/src/commands/matrix.js +20 -1
  171. package/src/commands/memory.js +4 -1
  172. package/src/commands/mtc.js +7 -1
  173. package/src/commands/nostr.js +31 -4
  174. package/src/commands/note.js +7 -4
  175. package/src/commands/plugin.js +125 -5
  176. package/src/commands/pqc.js +5 -0
  177. package/src/commands/reputation.js +10 -1
  178. package/src/commands/scim.js +6 -0
  179. package/src/commands/session.js +32 -10
  180. package/src/commands/siem.js +11 -0
  181. package/src/commands/sla.js +18 -3
  182. package/src/commands/social.js +38 -5
  183. package/src/commands/sso.js +10 -2
  184. package/src/commands/stress.js +23 -4
  185. package/src/commands/team.js +9 -2
  186. package/src/commands/tech.js +2 -0
  187. package/src/commands/tenant.js +10 -1
  188. package/src/commands/terraform.js +6 -0
  189. package/src/commands/update.js +1 -1
  190. package/src/commands/zkp.js +20 -0
  191. package/src/gateways/ws/message-dispatcher.js +5 -2
  192. package/src/gateways/ws/remote-session-protocol.js +103 -42
  193. package/src/harness/jsonl-session-store.js +12 -5
  194. package/src/harness/plugin-manager.js +16 -4
  195. package/src/harness/remote-command-ledger.js +46 -15
  196. package/src/harness/worktree-isolator.js +16 -6
  197. package/src/lib/__tests__/logger.test.js +5 -2
  198. package/src/lib/a2a-protocol.js +25 -1
  199. package/src/lib/activitypub-bridge.js +61 -0
  200. package/src/lib/agent-core.js +2 -0
  201. package/src/lib/agent-economy.js +262 -29
  202. package/src/lib/agent-network.js +7 -1
  203. package/src/lib/agent-team/team-runner.js +25 -9
  204. package/src/lib/agent-team/team-worktree.js +36 -4
  205. package/src/lib/async-hook-supervisor.cjs +102 -16
  206. package/src/lib/audit-logger.js +7 -6
  207. package/src/lib/autonomous-developer.js +60 -0
  208. package/src/lib/chat-core.js +17 -4
  209. package/src/lib/collaboration-governance.js +56 -0
  210. package/src/lib/community-governance.js +68 -0
  211. package/src/lib/compliance-manager.js +63 -0
  212. package/src/lib/cross-chain.js +5 -0
  213. package/src/lib/dao-governance.js +151 -2
  214. package/src/lib/did-manager.js +23 -10
  215. package/src/lib/dlp-engine.js +70 -0
  216. package/src/lib/eval/runner.js +89 -1
  217. package/src/lib/eval/tasks.js +170 -0
  218. package/src/lib/eval/trend.js +16 -1
  219. package/src/lib/evolution-system.js +92 -0
  220. package/src/lib/evomap-federation.js +55 -0
  221. package/src/lib/evomap-governance.js +94 -0
  222. package/src/lib/fatal-handler.js +17 -1
  223. package/src/lib/hardening-manager.js +73 -0
  224. package/src/lib/hierarchical-memory.js +14 -8
  225. package/src/lib/knowledge-exporter.js +8 -2
  226. package/src/lib/knowledge-graph.js +79 -0
  227. package/src/lib/llm-providers.js +23 -14
  228. package/src/lib/logger.js +4 -1
  229. package/src/lib/lsp/__tests__/benchmark.test.js +88 -0
  230. package/src/lib/lsp/__tests__/lsp-client.test.js +11 -0
  231. package/src/lib/lsp/__tests__/lsp-manager.test.js +69 -0
  232. package/src/lib/lsp/benchmark.js +257 -0
  233. package/src/lib/lsp/lsp-client.js +30 -9
  234. package/src/lib/lsp/lsp-manager.js +54 -2
  235. package/src/lib/matrix-bridge.js +84 -0
  236. package/src/lib/memory-manager.js +5 -3
  237. package/src/lib/nostr-bridge.js +53 -0
  238. package/src/lib/permission-engine.js +22 -4
  239. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -0
  240. package/src/lib/plugin-runtime/install.js +9 -1
  241. package/src/lib/plugin-runtime/policy.js +9 -1
  242. package/src/lib/plugin-runtime/remote-source.js +240 -0
  243. package/src/lib/pqc-manager.js +64 -0
  244. package/src/lib/reputation-optimizer.js +101 -0
  245. package/src/lib/sandbox-egress-proxy.js +159 -59
  246. package/src/lib/sandbox-fs-policy.js +112 -0
  247. package/src/lib/sandbox-network-policy.js +18 -1
  248. package/src/lib/sandbox-v2.js +14 -14
  249. package/src/lib/scim-manager.js +36 -0
  250. package/src/lib/session-manager.js +5 -2
  251. package/src/lib/settings-hooks.cjs +1 -0
  252. package/src/lib/siem-exporter.js +45 -0
  253. package/src/lib/skill-loader.js +20 -2
  254. package/src/lib/skill-marketplace.js +83 -0
  255. package/src/lib/sla-manager.js +88 -0
  256. package/src/lib/social-manager.js +74 -0
  257. package/src/lib/stress-tester.js +65 -0
  258. package/src/lib/tech-learning-engine.js +75 -0
  259. package/src/lib/tenant-saas.js +107 -0
  260. package/src/lib/terraform-manager.js +67 -0
  261. package/src/lib/token-incentive.js +180 -29
  262. package/src/lib/wallet-manager.js +81 -35
  263. package/src/lib/zkp-engine.js +87 -0
  264. package/src/repl/agent-repl.js +17 -2
  265. package/src/runtime/__tests__/auto-pin.test.js +104 -0
  266. package/src/runtime/agent-core.js +241 -68
  267. package/src/runtime/auto-pin.js +117 -0
  268. package/src/runtime/headless-runner.js +35 -0
  269. package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +0 -1
  270. package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +0 -1
  271. package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +0 -1
  272. package/src/assets/web-panel/assets/index-Cg2ldRfU.js +0 -1
  273. package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +0 -1
package/src/lib/logger.js CHANGED
@@ -24,7 +24,10 @@ export function success(message, ...args) {
24
24
  }
25
25
 
26
26
  export function warn(message, ...args) {
27
- console.log(chalk.yellow("⚠"), message, ...args);
27
+ // stderr, not stdout: a warning must never interleave with a command's
28
+ // machine-readable stdout (e.g. `--json` payloads), which `| jq` and other
29
+ // consumers parse. Like error(), warn is never gated by quiet mode.
30
+ console.error(chalk.yellow("⚠"), message, ...args);
28
31
  }
29
32
 
30
33
  export function error(message, ...args) {
@@ -0,0 +1,88 @@
1
+ /**
2
+ * benchmark stats tests — the pure percentile/summarize helpers that turn raw
3
+ * latency samples into a report. No spawning; the harness itself is exercised by
4
+ * the live `cc code-intel bench` run.
5
+ */
6
+
7
+ import { describe, it, expect } from "vitest";
8
+ import {
9
+ percentile,
10
+ summarize,
11
+ sumSubtreeRss,
12
+ sumSubtreeRssFromWmicCsv,
13
+ } from "../benchmark.js";
14
+
15
+ describe("percentile", () => {
16
+ it("returns the value for a single sample", () => {
17
+ expect(percentile([5], 50)).toBe(5);
18
+ expect(percentile([5], 95)).toBe(5);
19
+ });
20
+ it("computes median and p95 with interpolation", () => {
21
+ const s = [10, 20, 30, 40, 50];
22
+ expect(percentile(s, 50)).toBe(30);
23
+ // p95 of 5 points: rank = 0.95*4 = 3.8 → 40 + (50-40)*0.8 = 48
24
+ expect(percentile(s, 95)).toBeCloseTo(48, 5);
25
+ });
26
+ it("is order-independent (sorts internally)", () => {
27
+ expect(percentile([50, 10, 30, 40, 20], 50)).toBe(30);
28
+ });
29
+ it("returns null for an empty sample", () => {
30
+ expect(percentile([], 50)).toBeNull();
31
+ });
32
+ });
33
+
34
+ describe("summarize", () => {
35
+ it("reports n/min/median/p95/max/mean rounded to 0.1ms", () => {
36
+ const s = summarize([12.34, 5.6, 8.9, 20.1, 3.33]);
37
+ expect(s.n).toBe(5);
38
+ expect(s.min).toBe(3.3);
39
+ expect(s.max).toBe(20.1);
40
+ expect(s.median).toBe(8.9);
41
+ expect(s.mean).toBe(10.1); // (12.34+5.6+8.9+20.1+3.33)/5 = 10.054 → 10.1
42
+ });
43
+ it("handles an empty sample", () => {
44
+ expect(summarize([])).toEqual({ n: 0 });
45
+ });
46
+ });
47
+
48
+ describe("sumSubtreeRss", () => {
49
+ // launcher(100) → node server(200) → worker(300); sibling(400) unrelated
50
+ const rows = [
51
+ { pid: 100, ppid: 1, val: 5 },
52
+ { pid: 200, ppid: 100, val: 120 },
53
+ { pid: 300, ppid: 200, val: 40 },
54
+ { pid: 400, ppid: 1, val: 999 },
55
+ ];
56
+ it("sums a pid and all its transitive descendants (not the launcher alone)", () => {
57
+ expect(sumSubtreeRss(rows, 100)).toBe(165); // 5 + 120 + 40
58
+ });
59
+ it("returns just the leaf when it has no children", () => {
60
+ expect(sumSubtreeRss(rows, 300)).toBe(40);
61
+ });
62
+ it("returns null when the root pid is absent", () => {
63
+ expect(sumSubtreeRss(rows, 555)).toBeNull();
64
+ });
65
+ it("does not loop on a cyclic parent relationship", () => {
66
+ const cyclic = [
67
+ { pid: 1, ppid: 2, val: 10 },
68
+ { pid: 2, ppid: 1, val: 20 },
69
+ ];
70
+ expect(sumSubtreeRss(cyclic, 1)).toBe(30);
71
+ });
72
+ });
73
+
74
+ describe("sumSubtreeRssFromWmicCsv", () => {
75
+ it("parses wmic CSV and sums the subtree in bytes", () => {
76
+ const csv = [
77
+ "Node,ParentProcessId,ProcessId,WorkingSetSize",
78
+ "HOST,1,100,5000",
79
+ "HOST,100,200,120000",
80
+ "HOST,200,300,40000",
81
+ "HOST,1,400,999000",
82
+ ].join("\r\n");
83
+ expect(sumSubtreeRssFromWmicCsv(csv, 100)).toBe(165000);
84
+ });
85
+ it("returns null on malformed input", () => {
86
+ expect(sumSubtreeRssFromWmicCsv("garbage\r\n", 100)).toBeNull();
87
+ });
88
+ });
@@ -90,6 +90,17 @@ describe("LSPClient handshake", () => {
90
90
  expect.objectContaining({ shell: false }),
91
91
  );
92
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
+ });
93
104
  });
94
105
 
95
106
  describe("LSPClient request/response", () => {
@@ -141,6 +141,38 @@ describe("ensureFor", () => {
141
141
  await mgr.disposeAll();
142
142
  });
143
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
+
144
176
  it("returns unavailable for an unsupported extension", async () => {
145
177
  const mgr = new LSPManager();
146
178
  const ready = await mgr.ensureFor("/proj/readme.txt");
@@ -251,6 +283,43 @@ describe("diagnostics", () => {
251
283
  expect(diags[0].message).toBe("boom");
252
284
  await mgr.disposeAll();
253
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
+ });
254
323
  });
255
324
 
256
325
  describe("disposeAll", () => {
@@ -0,0 +1,257 @@
1
+ /**
2
+ * LSP code-intelligence latency benchmark.
3
+ *
4
+ * Measures the two numbers the Phase 2 acceptance criteria call for on real
5
+ * repos: **startup latency** (cold spawn + project index + first answer) and
6
+ * **per-query latency** (warm def / refs / hover / diagnostics / symbols), plus
7
+ * best-effort server-process RSS. Pure timing/stat helpers are separated out so
8
+ * they are unit-testable without spawning anything.
9
+ *
10
+ * Deliberately drives the SAME `CodeIntelligence` the agent tool uses, so the
11
+ * numbers reflect real agent-path latency, not a synthetic micro-path.
12
+ */
13
+
14
+ import { CodeIntelligence } from "./code-intelligence.js";
15
+
16
+ /** now() in fractional milliseconds — injectable for deterministic tests. */
17
+ export const _deps = {
18
+ now: () => Number(process.hrtime.bigint() / 1000n) / 1000,
19
+ sampleRss: sampleRssDefault,
20
+ };
21
+
22
+ /** p-th percentile (0..100) of an UNSORTED sample via linear interpolation. */
23
+ export function percentile(samples, p) {
24
+ if (!samples.length) return null;
25
+ const sorted = [...samples].sort((a, b) => a - b);
26
+ if (sorted.length === 1) return sorted[0];
27
+ const rank = (p / 100) * (sorted.length - 1);
28
+ const lo = Math.floor(rank);
29
+ const hi = Math.ceil(rank);
30
+ if (lo === hi) return sorted[lo];
31
+ return sorted[lo] + (sorted[hi] - sorted[lo]) * (rank - lo);
32
+ }
33
+
34
+ /** Summary stats for a latency sample (all in ms), rounded to 0.1ms. */
35
+ export function summarize(samples) {
36
+ if (!samples.length) return { n: 0 };
37
+ const round = (x) => (x == null ? null : Math.round(x * 10) / 10);
38
+ const sum = samples.reduce((a, b) => a + b, 0);
39
+ return {
40
+ n: samples.length,
41
+ min: round(Math.min(...samples)),
42
+ median: round(percentile(samples, 50)),
43
+ p95: round(percentile(samples, 95)),
44
+ max: round(Math.max(...samples)),
45
+ mean: round(sum / samples.length),
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Run the benchmark. Returns a structured report; never throws for the
51
+ * "no server installed" case (returns { available:false, reason }).
52
+ *
53
+ * @param {object} opts
54
+ * @param {string} opts.projectRoot
55
+ * @param {string} opts.file a real source file in the project
56
+ * @param {number} [opts.runs=20] timed iterations per operation
57
+ * @param {number} [opts.warmup=2] untimed warmup iterations per operation
58
+ * @param {(msg:string)=>void} [opts.onProgress]
59
+ */
60
+ export async function runLatencyBenchmark(opts) {
61
+ const {
62
+ projectRoot,
63
+ file,
64
+ runs = 20,
65
+ warmup = 2,
66
+ onProgress = () => {},
67
+ } = opts;
68
+ const ci = new CodeIntelligence({ projectRoot, coldStart: true });
69
+ const report = {
70
+ file,
71
+ runs,
72
+ available: true,
73
+ coldStartMs: null,
74
+ probe: null,
75
+ ops: {},
76
+ server: null,
77
+ };
78
+ try {
79
+ // --- cold start: first query pays spawn + project index ---
80
+ onProgress("cold start (spawn + index + first query)…");
81
+ const t0 = _deps.now();
82
+ const first = await ci.documentSymbols(file);
83
+ report.coldStartMs = round1(_deps.now() - t0);
84
+ if (!first.available) {
85
+ return { available: false, reason: first.reason };
86
+ }
87
+
88
+ // --- choose a probe position from a real symbol (so def/refs have a target) ---
89
+ const probe = pickProbe(first.symbols);
90
+ report.probe = probe;
91
+
92
+ // best-effort server memory now that it's warm
93
+ report.server = await captureServerInfo(ci);
94
+
95
+ // --- timed operations (warm) ---
96
+ const ops = [
97
+ { name: "document_symbols", run: () => ci.documentSymbols(file) },
98
+ {
99
+ name: "definition",
100
+ run: () => ci.definition(file, probe.line, probe.col),
101
+ needsProbe: true,
102
+ },
103
+ {
104
+ name: "references",
105
+ run: () => ci.references(file, probe.line, probe.col),
106
+ needsProbe: true,
107
+ },
108
+ {
109
+ name: "hover",
110
+ run: () => ci.hover(file, probe.line, probe.col),
111
+ needsProbe: true,
112
+ },
113
+ {
114
+ name: "diagnostics",
115
+ run: () => ci.diagnostics(file, { timeoutMs: 4000 }),
116
+ },
117
+ ];
118
+
119
+ for (const op of ops) {
120
+ if (op.needsProbe && !probe) {
121
+ report.ops[op.name] = { skipped: "no probe symbol" };
122
+ continue;
123
+ }
124
+ onProgress(`${op.name} …`);
125
+ for (let i = 0; i < warmup; i++) await op.run();
126
+ const samples = [];
127
+ for (let i = 0; i < runs; i++) {
128
+ const s = _deps.now();
129
+ await op.run();
130
+ samples.push(_deps.now() - s);
131
+ }
132
+ report.ops[op.name] = summarize(samples);
133
+ }
134
+ return report;
135
+ } finally {
136
+ await ci.dispose();
137
+ }
138
+ }
139
+
140
+ function pickProbe(symbols) {
141
+ if (!Array.isArray(symbols) || !symbols.length) return null;
142
+ // Prefer a function/method/class — those have meaningful def/refs.
143
+ const preferred = symbols.find((s) =>
144
+ ["function", "method", "class"].includes(s.kind),
145
+ );
146
+ const s = preferred || symbols[0];
147
+ return { line: s.line, col: s.col, name: s.name, kind: s.kind };
148
+ }
149
+
150
+ async function captureServerInfo(ci) {
151
+ try {
152
+ const servers = ci.manager.listServers();
153
+ if (!servers.length) return null;
154
+ const s = servers[0];
155
+ const rssMb = s.pid != null ? await _deps.sampleRss(s.pid) : null;
156
+ return { serverId: s.serverId, pid: s.pid, rssMb };
157
+ } catch {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Best-effort resident-set-size (MB) of the server, summed over the pid AND its
164
+ * descendants. This matters on Windows: a `.cmd` server is launched through
165
+ * `cmd.exe /c`, so the tracked pid is the launcher and the real language server
166
+ * (node/tsserver) is a child — measuring only the launcher reports a few MB and
167
+ * lies. We sum the whole subtree. Returns null if it can't be read (never throws
168
+ * — memory is a bonus metric, not load-bearing).
169
+ */
170
+ async function sampleRssDefault(pid) {
171
+ const { execFileSync } = await import("child_process");
172
+ try {
173
+ if (process.platform === "win32") {
174
+ // One snapshot of every process → parent map → sum the subtree at `pid`.
175
+ const csv = execFileSync(
176
+ "wmic",
177
+ [
178
+ "process",
179
+ "get",
180
+ "ProcessId,ParentProcessId,WorkingSetSize",
181
+ "/format:csv",
182
+ ],
183
+ { encoding: "utf8", windowsHide: true, maxBuffer: 16 * 1024 * 1024 },
184
+ );
185
+ const bytes = sumSubtreeRssFromWmicCsv(csv, pid);
186
+ return bytes != null ? Math.round(bytes / (1024 * 1024)) : null;
187
+ }
188
+ // POSIX: not wrapped, so the pid is the real server. Sum pid + children.
189
+ const out = execFileSync("ps", ["-o", "pid=,ppid=,rss="], {
190
+ encoding: "utf8",
191
+ });
192
+ const rows = out
193
+ .trim()
194
+ .split(/\r?\n/)
195
+ .map((l) => l.trim().split(/\s+/).map(Number));
196
+ const kb = sumSubtreeRss(
197
+ rows.map(([p, pp, rss]) => ({ pid: p, ppid: pp, val: rss })),
198
+ pid,
199
+ );
200
+ return kb != null ? Math.round(kb / 1024) : null;
201
+ } catch {
202
+ return null;
203
+ }
204
+ }
205
+
206
+ /** Parse `wmic ... /format:csv` output and sum WorkingSetSize over pid's subtree (bytes). */
207
+ export function sumSubtreeRssFromWmicCsv(csv, rootPid) {
208
+ const lines = csv
209
+ .split(/\r?\n/)
210
+ .map((l) => l.trim())
211
+ .filter(Boolean);
212
+ const header = lines.find((l) => /ProcessId/i.test(l));
213
+ if (!header) return null;
214
+ const cols = header.split(",");
215
+ const iPid = cols.findIndex((c) => /^ProcessId$/i.test(c));
216
+ const iPpid = cols.findIndex((c) => /^ParentProcessId$/i.test(c));
217
+ const iRss = cols.findIndex((c) => /^WorkingSetSize$/i.test(c));
218
+ if (iPid < 0 || iPpid < 0 || iRss < 0) return null;
219
+ const rows = [];
220
+ for (const l of lines) {
221
+ if (l === header || /ProcessId/i.test(l)) continue;
222
+ const f = l.split(",");
223
+ const pid = Number(f[iPid]);
224
+ const ppid = Number(f[iPpid]);
225
+ const val = Number(f[iRss]);
226
+ if (Number.isFinite(pid))
227
+ rows.push({ pid, ppid, val: Number.isFinite(val) ? val : 0 });
228
+ }
229
+ return sumSubtreeRss(rows, rootPid);
230
+ }
231
+
232
+ /** Sum `val` over `rootPid` and all its transitive descendants. */
233
+ export function sumSubtreeRss(rows, rootPid) {
234
+ const childrenOf = new Map();
235
+ const valOf = new Map();
236
+ for (const r of rows) {
237
+ valOf.set(r.pid, r.val);
238
+ if (!childrenOf.has(r.ppid)) childrenOf.set(r.ppid, []);
239
+ childrenOf.get(r.ppid).push(r.pid);
240
+ }
241
+ if (!valOf.has(rootPid)) return null;
242
+ let total = 0;
243
+ const seen = new Set();
244
+ const stack = [rootPid];
245
+ while (stack.length) {
246
+ const p = stack.pop();
247
+ if (seen.has(p)) continue;
248
+ seen.add(p);
249
+ total += valOf.get(p) || 0;
250
+ for (const c of childrenOf.get(p) || []) stack.push(c);
251
+ }
252
+ return total;
253
+ }
254
+
255
+ function round1(x) {
256
+ return Math.round(x * 10) / 10;
257
+ }
@@ -57,6 +57,11 @@ export class LSPClient extends EventEmitter {
57
57
  return Boolean(this._child) && !this._closed;
58
58
  }
59
59
 
60
+ /** OS pid of the language-server process, or null before start / after exit. */
61
+ get pid() {
62
+ return this._child ? (this._child.pid ?? null) : null;
63
+ }
64
+
60
65
  /** Spawn the process and run the initialize handshake. Resolves when ready. */
61
66
  async start({ initTimeoutMs = DEFAULT_INIT_TIMEOUT_MS } = {}) {
62
67
  if (this._child) throw new Error("LSPClient already started");
@@ -100,15 +105,31 @@ export class LSPClient extends EventEmitter {
100
105
  this.emit("stderr", chunk.toString("utf8"));
101
106
  });
102
107
 
103
- const initResult = await this.request(
104
- "initialize",
105
- this._initializeParams(),
106
- { timeoutMs: initTimeoutMs },
107
- );
108
- this.serverCapabilities = initResult?.capabilities || {};
109
- this.notify("initialized", {});
110
- this._initialized = true;
111
- return this.serverCapabilities;
108
+ try {
109
+ const initResult = await this.request(
110
+ "initialize",
111
+ this._initializeParams(),
112
+ { timeoutMs: initTimeoutMs },
113
+ );
114
+ this.serverCapabilities = initResult?.capabilities || {};
115
+ this.notify("initialized", {});
116
+ this._initialized = true;
117
+ return this.serverCapabilities;
118
+ } catch (err) {
119
+ // The process spawned but the initialize handshake failed or timed out (a
120
+ // hung/slow/broken server). Don't leave it orphaned: an init HANG never
121
+ // fires 'exit', and the manager's crash-loop guard only counts exits, so
122
+ // the process would linger forever holding a port/file locks. Tear it down
123
+ // before rethrowing so the caller's failure path doesn't leak a server.
124
+ this._closed = true;
125
+ this._failAll(err);
126
+ try {
127
+ this._child.kill();
128
+ } catch {
129
+ /* already dead */
130
+ }
131
+ throw err;
132
+ }
112
133
  }
113
134
 
114
135
  _initializeParams() {
@@ -39,6 +39,16 @@ export class LSPManager {
39
39
  this.projectRoot = opts.projectRoot || process.cwd();
40
40
  /** key `${root}::${serverId}` → { client, languageId, root, restarts } */
41
41
  this._servers = new Map();
42
+ /**
43
+ * key → in-flight `_startServer` promise. A server start awaits a real
44
+ * process spawn + `initialize` handshake, and the read-only agent batch
45
+ * fires several `code_intelligence` calls at once (see agent-core.js), so a
46
+ * bare Map.get(miss)→await→Map.set pool double-spawns when two callers race
47
+ * the same key: the second `_servers.set` orphans the first process (leaked
48
+ * OS process + a still-attached diagnostics listener writing stale state).
49
+ * Sharing one in-flight promise per key collapses the race to a single spawn.
50
+ */
51
+ this._starting = new Map();
42
52
  /** open document uri → { version, languageId } */
43
53
  this._openDocs = new Map();
44
54
  /** uri → latest diagnostics array */
@@ -115,7 +125,7 @@ export class LSPManager {
115
125
  quarantined: true,
116
126
  };
117
127
  }
118
- entry = await this._startServer(key, resolved, projectRoot);
128
+ entry = await this._getOrStartServer(key, resolved, projectRoot);
119
129
  }
120
130
 
121
131
  const uri = pathToFileUri(abs);
@@ -152,6 +162,22 @@ export class LSPManager {
152
162
  });
153
163
  }
154
164
 
165
+ /**
166
+ * Dedupe concurrent starts for `key`: return the in-flight start promise if
167
+ * one exists, otherwise begin one and register it so racing callers share it.
168
+ * The promise self-evicts on settle (resolve OR reject), so a failed start
169
+ * doesn't wedge the key — the next request retries cleanly.
170
+ */
171
+ _getOrStartServer(key, resolved, projectRoot) {
172
+ const inflight = this._starting.get(key);
173
+ if (inflight) return inflight;
174
+ const p = this._startServer(key, resolved, projectRoot).finally(() => {
175
+ this._starting.delete(key);
176
+ });
177
+ this._starting.set(key, p);
178
+ return p;
179
+ }
180
+
155
181
  async _startServer(key, resolved, projectRoot) {
156
182
  const client = _deps.createClient({
157
183
  command: resolved.command,
@@ -184,8 +210,21 @@ export class LSPManager {
184
210
  const log = this._crashLog.get(key) || [];
185
211
  log.push(this._now());
186
212
  this._crashLog.set(key, log);
213
+ // Drop this project's open docs AND their published-diagnostics
214
+ // bookkeeping. Leaving stale _diagMeta/_diagnostics behind makes the
215
+ // readiness waiters resolve on the PRE-CRASH publish — _waitForFirstPublish
216
+ // sees count>0 and returns immediately; waitForDiagnostics sees an old
217
+ // `at` and settles at once — so a query after a crash+restart returns
218
+ // stale results and races the fresh server's initial empty set, the very
219
+ // thing the publish-count machinery exists to prevent. (_openDocs keys are
220
+ // the raw file URI; _diagMeta/_diagnostics use the normalized key.)
187
221
  for (const [uri, meta] of this._openDocs) {
188
- if (meta.root === projectRoot) this._openDocs.delete(uri);
222
+ if (meta.root === projectRoot) {
223
+ this._openDocs.delete(uri);
224
+ const nuri = normalizeUri(uri);
225
+ this._diagnostics.delete(nuri);
226
+ this._diagMeta.delete(nuri);
227
+ }
189
228
  }
190
229
  });
191
230
  await client.start();
@@ -305,6 +344,19 @@ export class LSPManager {
305
344
  get activeServerCount() {
306
345
  return this._servers.size;
307
346
  }
347
+
348
+ /** Snapshot of running servers: `[{ serverId, root, pid }]` (for bench / doctor). */
349
+ listServers() {
350
+ const out = [];
351
+ for (const entry of this._servers.values()) {
352
+ out.push({
353
+ serverId: entry.serverId,
354
+ root: entry.root,
355
+ pid: entry.client.pid,
356
+ });
357
+ }
358
+ return out;
359
+ }
308
360
  }
309
361
 
310
362
  // ---- position helpers (the ONLY place that converts between coordinate spaces) ----
@@ -57,6 +57,90 @@ export function ensureMatrixTables(db) {
57
57
  `);
58
58
  }
59
59
 
60
+ /**
61
+ * Rehydrate _rooms/_messages/_spaceChildren from the persisted matrix_rooms/
62
+ * matrix_events tables. The `cc` CLI runs every command in a fresh process, so
63
+ * without this the Maps start empty and a room joined / message sent / space
64
+ * child added in a prior invocation is invisible (`cc matrix rooms`/`messages`/
65
+ * `space children` show nothing; sending to a persisted room can't find it).
66
+ * Call after ensureMatrixTables(db).
67
+ *
68
+ * Caveats reflecting the write side: room `type` is not a persisted column, so
69
+ * spaces are re-identified by their `!space_` roomId prefix (matching
70
+ * createSpace). removeSpaceChild is not persisted (no tombstone event), so a
71
+ * removed child re-appears after rehydration — a separate pre-existing gap,
72
+ * out of scope here.
73
+ */
74
+ export function loadFromDb(db) {
75
+ if (!db) return 0;
76
+ _rooms.clear();
77
+ _messages.clear();
78
+ _spaceChildren.clear();
79
+
80
+ const roomRows = db.prepare(`SELECT * FROM matrix_rooms`).all();
81
+ for (const row of roomRows) {
82
+ const isSpace =
83
+ typeof row.room_id === "string" && row.room_id.startsWith("!space_");
84
+ const room = {
85
+ id: row.id,
86
+ roomId: row.room_id,
87
+ name: row.name,
88
+ topic: row.topic ?? null,
89
+ isEncrypted: !!row.is_encrypted,
90
+ memberCount: row.member_count ?? 0,
91
+ lastEventAt: row.last_event_at ?? null,
92
+ joinedAt: row.joined_at ?? null,
93
+ status: row.status,
94
+ createdAt: row.created_at ?? null,
95
+ };
96
+ if (isSpace) {
97
+ room.type = "m.space";
98
+ if (!_spaceChildren.has(row.room_id))
99
+ _spaceChildren.set(row.room_id, new Map());
100
+ }
101
+ _rooms.set(row.id, room);
102
+ }
103
+
104
+ const eventRows = db.prepare(`SELECT * FROM matrix_events`).all();
105
+ for (const row of eventRows) {
106
+ let content = {};
107
+ try {
108
+ content = row.content ? JSON.parse(row.content) : {};
109
+ } catch {
110
+ content = {};
111
+ }
112
+
113
+ if (row.event_type === "m.space.child") {
114
+ // Space child state event: content is { state_key, content: { via } }.
115
+ const childRoomId = content.state_key;
116
+ const via =
117
+ content.content && Array.isArray(content.content.via)
118
+ ? content.content.via
119
+ : ["matrix.org"];
120
+ if (childRoomId) {
121
+ if (!_spaceChildren.has(row.room_id))
122
+ _spaceChildren.set(row.room_id, new Map());
123
+ _spaceChildren.get(row.room_id).set(childRoomId, { via });
124
+ }
125
+ continue; // space-child events are not messages
126
+ }
127
+
128
+ _messages.set(row.id, {
129
+ id: row.id,
130
+ eventId: row.event_id,
131
+ roomId: row.room_id,
132
+ sender: row.sender,
133
+ eventType: row.event_type,
134
+ content,
135
+ originServerTs: row.origin_server_ts ?? null,
136
+ isEncrypted: !!row.is_encrypted,
137
+ createdAt: row.created_at ?? null,
138
+ });
139
+ }
140
+
141
+ return _rooms.size + _messages.size + _spaceChildren.size;
142
+ }
143
+
60
144
  /* ── Login ─────────────────────────────────────────────────── */
61
145
 
62
146
  export function login(db, homeserver, userId, password) {