chainlesschain 0.162.149 → 0.162.151

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 (285) hide show
  1. package/README.md +1 -1
  2. package/package.json +3 -2
  3. package/src/assets/web-panel/assets/{AIOps-BJif14yR.js → AIOps-DuEOzebi.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-CXek6gJY.js → ActionButton-C6GiJa5H.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BgQhdAJi.js → Analytics-BBIiL_pO.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-DTYl5NtR.js → AppLayout-CuI2gZRy.js} +3 -3
  7. package/src/assets/web-panel/assets/{Audit-BzATQAeR.js → Audit-CbUjfr-K.js} +1 -1
  8. package/src/assets/web-panel/assets/{Backup-BzdPEQhL.js → Backup-CsSW9ljf.js} +1 -1
  9. package/src/assets/web-panel/assets/{BaseInput-BM0KVh8E.js → BaseInput-kM3rLe7F.js} +1 -1
  10. package/src/assets/web-panel/assets/{Chat-D7fHXHkz.js → Chat-kfE51YN3.js} +4 -4
  11. package/src/assets/web-panel/assets/{ChatBubbleRenderer-Wm34RR-b.js → ChatBubbleRenderer-Bg2FTW6A.js} +1 -1
  12. package/src/assets/web-panel/assets/{Checkbox-CAiSQ9vO.js → Checkbox-jYb1uj-M.js} +1 -1
  13. package/src/assets/web-panel/assets/{Codegen-Df40CrEe.js → Codegen-X9XiD5Lj.js} +1 -1
  14. package/src/assets/web-panel/assets/{Col-DIT2fNFU.js → Col-BfbHu1xI.js} +1 -1
  15. package/src/assets/web-panel/assets/{Community-CSTIbW2k.js → Community-c4oJMFS-.js} +1 -1
  16. package/src/assets/web-panel/assets/{Compact-DtqXZZUi.js → Compact-BFmnZpH7.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compliance-CUtzw6o7.js → Compliance-s9shVHBS.js} +1 -1
  18. package/src/assets/web-panel/assets/{Cowork-CezmlnmU.js → Cowork-C5da--TC.js} +2 -2
  19. package/src/assets/web-panel/assets/{Cron-P4BITarg.js → Cron-9kNbyNE4.js} +2 -2
  20. package/src/assets/web-panel/assets/{Crosschain-BRRiYwvq.js → Crosschain-C1k3NJD4.js} +1 -1
  21. package/src/assets/web-panel/assets/{DID-L5ijB9i4.js → DID-DkheEvNK.js} +2 -2
  22. package/src/assets/web-panel/assets/{Dashboard-CFxro4ob.js → Dashboard-C3FuSGmJ.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dropdown-BmAFCQ71.js → Dropdown-CAV6FMFD.js} +1 -1
  24. package/src/assets/web-panel/assets/{EmailListRenderer-DJBCEuon.js → EmailListRenderer-Cc-Ulc4l.js} +1 -1
  25. package/src/assets/web-panel/assets/{FamilyGuardDashboard-z4oLq6eT.js → FamilyGuardDashboard-8CitHEtv.js} +1 -1
  26. package/src/assets/web-panel/assets/{Federation-DsVCpHMF.js → Federation-DFSZ5KDt.js} +1 -1
  27. package/src/assets/web-panel/assets/{FormItemContext-DP3bP5th.js → FormItemContext-CEWCtnG2.js} +1 -1
  28. package/src/assets/web-panel/assets/{GenericCardRenderer-BAXP2Gc6.js → GenericCardRenderer-CwRzNzmX.js} +1 -1
  29. package/src/assets/web-panel/assets/{Git-I3g_bAw9.js → Git-0rgDM7cJ.js} +2 -2
  30. package/src/assets/web-panel/assets/{Governance-jHS5-ioS.js → Governance-CfBKtK9F.js} +1 -1
  31. package/src/assets/web-panel/assets/{Inference-CPL7sfx9.js → Inference-CzcUVvud.js} +1 -1
  32. package/src/assets/web-panel/assets/{KnowledgeGraph-Jf236h4C.js → KnowledgeGraph-Dwc4YL4A.js} +1 -1
  33. package/src/assets/web-panel/assets/{Logs-Cm2tcSKg.js → Logs-DgFPwR2F.js} +2 -2
  34. package/src/assets/web-panel/assets/{Marketplace-CZK82rhi.js → Marketplace-DnpPTbGU.js} +1 -1
  35. package/src/assets/web-panel/assets/{McpTools-CZCeji7a.js → McpTools-DRxxINyW.js} +4 -4
  36. package/src/assets/web-panel/assets/{Memory-3l2KVS9k.js → Memory-OhmW_6Z3.js} +2 -2
  37. package/src/assets/web-panel/assets/{MobileBridge-VYdpoLh6.js → MobileBridge-DkTW-RK5.js} +1 -1
  38. package/src/assets/web-panel/assets/{MobileProjects-CiB9cmm1.js → MobileProjects-DpCGV_lI.js} +1 -1
  39. package/src/assets/web-panel/assets/{Mtc-wCFZbBuL.js → Mtc--vmkL0AS.js} +2 -2
  40. package/src/assets/web-panel/assets/{MtcAudit-1a3THIyG.js → MtcAudit-DGoFWjD4.js} +4 -4
  41. package/src/assets/web-panel/assets/{Multisig-DMzLB2hG.js → Multisig-B4kWgq95.js} +3 -3
  42. package/src/assets/web-panel/assets/{NLProgramming-D9wZbf6l.js → NLProgramming-YQwvommE.js} +1 -1
  43. package/src/assets/web-panel/assets/{Notes-hrFe2ZM-.js → Notes-CgjtUADJ.js} +3 -3
  44. package/src/assets/web-panel/assets/{NotificationSettings-CHGX5Jwm.js → NotificationSettings-6JDrpBG5.js} +1 -1
  45. package/src/assets/web-panel/assets/OrderTableRenderer-PM6IlxjO.js +1 -0
  46. package/src/assets/web-panel/assets/{Organization-By7FkhtY.js → Organization-8McOT_OF.js} +4 -4
  47. package/src/assets/web-panel/assets/{Overflow--khGSzJn.js → Overflow-DAZpSCGc.js} +1 -1
  48. package/src/assets/web-panel/assets/{P2P-CAG9dH35.js → P2P-B7l2ZWLy.js} +2 -2
  49. package/src/assets/web-panel/assets/{PdhVaultBrowser-B3SQZmK9.js → PdhVaultBrowser-W1Ud-NAL.js} +3 -3
  50. package/src/assets/web-panel/assets/{Permissions-D35ec6JA.js → Permissions-Czm_w9u_.js} +4 -4
  51. package/src/assets/web-panel/assets/{PersonalDataHub-CxBF7hW_.js → PersonalDataHub-B4F0_ZVM.js} +2 -2
  52. package/src/assets/web-panel/assets/{Pipeline-s6EtOO1s.js → Pipeline-B37aFiWv.js} +1 -1
  53. package/src/assets/web-panel/assets/{Privacy-BbJYmWmO.js → Privacy-94KBnRrD.js} +1 -1
  54. package/src/assets/web-panel/assets/{ProjectInit-VGQq1jMT.js → ProjectInit-B5h8_dlh.js} +2 -2
  55. package/src/assets/web-panel/assets/{ProjectSettings-B1ew3Teh.js → ProjectSettings-lmeI3Lpm.js} +2 -2
  56. package/src/assets/web-panel/assets/Projects-D5AYOasl.js +1 -0
  57. package/src/assets/web-panel/assets/{Providers-mrO47FIK.js → Providers-B3cNWwJg.js} +1 -1
  58. package/src/assets/web-panel/assets/{QrScannerModal-IJF2mHyw.js → QrScannerModal-CHRvOw1R.js} +1 -1
  59. package/src/assets/web-panel/assets/{QuickAsk-CU0wN_Z0.js → QuickAsk-D1JMgFEd.js} +1 -1
  60. package/src/assets/web-panel/assets/{Recommend-DhN37R6G.js → Recommend-EYWGR79p.js} +1 -1
  61. package/src/assets/web-panel/assets/{RemoteSession-D_xOX_hu.js → RemoteSession-BAMC2pJt.js} +2 -2
  62. package/src/assets/web-panel/assets/{Reputation-D_Ssv7uH.js → Reputation-X2Hk1XG_.js} +1 -1
  63. package/src/assets/web-panel/assets/{Row-Kx5dKxX7.js → Row-Eq98LRtU.js} +1 -1
  64. package/src/assets/web-panel/assets/{RssFeed-BlGwqZkY.js → RssFeed-i76s6z_o.js} +2 -2
  65. package/src/assets/web-panel/assets/{Search-hLFQzxpb.js → Search-D_Es7CAI.js} +1 -1
  66. package/src/assets/web-panel/assets/{Security-DMlvsVt4.js → Security-kayD2e94.js} +3 -3
  67. package/src/assets/web-panel/assets/{Services-Bd0Qsj2m.js → Services-Ctsm6ul8.js} +2 -2
  68. package/src/assets/web-panel/assets/{Skeleton-DFclq9X3.js → Skeleton-CRNYlfdf.js} +1 -1
  69. package/src/assets/web-panel/assets/{Skills-BNtln2qY.js → Skills-DkZYpF4-.js} +1 -1
  70. package/src/assets/web-panel/assets/{Sla-Dch5H19G.js → Sla-DXkoBcOP.js} +1 -1
  71. package/src/assets/web-panel/assets/{SpeechSettings-2UfQcU1g.js → SpeechSettings-DXi_GI1d.js} +1 -1
  72. package/src/assets/web-panel/assets/{SyncSettings-CD9YQr4i.js → SyncSettings-rNKpfomn.js} +2 -2
  73. package/src/assets/web-panel/assets/{Tasks-BNu-7MzI.js → Tasks-XF4uPtG-.js} +1 -1
  74. package/src/assets/web-panel/assets/{Templates-D1n9CaIR.js → Templates-i0Hzfeyu.js} +1 -1
  75. package/src/assets/web-panel/assets/{Tenant-DenL3iBW.js → Tenant-sMpHAi27.js} +1 -1
  76. package/src/assets/web-panel/assets/Terminal-BoZvaYAl.js +3 -0
  77. package/src/assets/web-panel/assets/{TimelineRenderer-D87IfukO.js → TimelineRenderer-yuC6K7Rf.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tokens-BkiZc8E3.js → Tokens-B8O8i0s6.js} +1 -1
  79. package/src/assets/web-panel/assets/{Trigger-BNRSytGN.js → Trigger-Bv6yrlCr.js} +1 -1
  80. package/src/assets/web-panel/assets/{Trust-Bwbd4X2m.js → Trust-C4mW95Ev.js} +1 -1
  81. package/src/assets/web-panel/assets/{UkeySign-B8O2bs0o.js → UkeySign-DV6yKaHn.js} +1 -1
  82. package/src/assets/web-panel/assets/{VideoEditing-C2fL8zmH.js → VideoEditing-UI479-sD.js} +1 -1
  83. package/src/assets/web-panel/assets/{Wallet-BORYDdE1.js → Wallet-M5oV9EVP.js} +4 -4
  84. package/src/assets/web-panel/assets/{WebAuthn-C0V5S2FM.js → WebAuthn-DDyDHPDR.js} +4 -4
  85. package/src/assets/web-panel/assets/{WorkflowEditor-2dR0fcHL.js → WorkflowEditor-RoBv4rqk.js} +1 -1
  86. package/src/assets/web-panel/assets/{chat-BmuAFAn8.js → chat-Cw1mE5LS.js} +1 -1
  87. package/src/assets/web-panel/assets/{colors-CN3smMcK.js → colors-BFP62Gwf.js} +1 -1
  88. package/src/assets/web-panel/assets/{compact-item-DCnxb4kW.js → compact-item-17HL6pyC.js} +1 -1
  89. package/src/assets/web-panel/assets/{createContext-vlR43pHn.js → createContext-D55X4fZX.js} +1 -1
  90. package/src/assets/web-panel/assets/devWarning-CqbHYuRc.js +1 -0
  91. package/src/assets/web-panel/assets/{hasIn-D04tHYFd.js → hasIn-D3E5mNsP.js} +1 -1
  92. package/src/assets/web-panel/assets/{index-DbVsQBOH.js → index--44J50mc.js} +1 -1
  93. package/src/assets/web-panel/assets/{index-TslMTwNy.js → index-ASPYTzMK.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-CTOCnIQ7.js → index-BC6zxOlU.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-ljuBiQW9.js → index-BEa1RW4c.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-jzR7Ujuw.js → index-BQmZg5si.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-BudvKQfG.js → index-BY_xZ2jy.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CP9m9Ggr.js → index-BgN5G5vF.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-Cr-A0FYJ.js → index-BnGpft-Z.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-D5LZVZ0L.js → index-Bq83UBDr.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-ToUh2b1-.js → index-Bzpp2z2S.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-DbADHwhr.js → index-CFYGn88j.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-ClhAHDK3.js → index-CGyTos4p.js} +1 -1
  104. package/src/assets/web-panel/assets/index-CKBI1U5K.js +1 -0
  105. package/src/assets/web-panel/assets/{index-C-6NO5il.js → index-CLEzsZ8A.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-D8vwp4qp.js → index-CR9rNUvi.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-_FIkN3jd.js → index-CW6LLoJO.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D65_XseV.js → index-C_MfcrLf.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CnXebZLL.js → index-Cdvk2_2q.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D_n8_vfI.js → index-CjCwi7we.js} +1 -1
  111. package/src/assets/web-panel/assets/index-CkLp6DTO.js +1 -0
  112. package/src/assets/web-panel/assets/{index-DDwLgQY9.js → index-CvCFKojj.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-430S68MN.js → index-D4LHSde4.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-BXRg8GFQ.js → index-DCLu84x-.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-DTtRscUa.js → index-DD_qGBrt.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-BasvsWDM.js → index-DIRvdwBX.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BeGDEXFs.js → index-DOjA8bvj.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CsAsCPJ6.js → index-Da27u6j0.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-D28DeAAB.js → index-DkBVYlE4.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CngCC8hC.js → index-DtAnPBWP.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-B78xL3s2.js → index-DzG7mkgW.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-DIpY0qM5.js → index-H9_X1Cg_.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-C5Sxb1sE.js → index-ITeEHb8L.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BTPEZTk1.js → index-KtLJXn4s.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-hpvvtAZ3.js → index-RYZgFIwo.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-HhNjnCfe.js → index-gAVxDiTZ.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DTWg-18U.js → index-paVubXzn.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-BqhASEL4.js → index-tUH_6bdJ.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DYAtmqiv.js → index-tVRPgYGr.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-0DnaZGkC.js → index-uSsiwgZ2.js} +1 -1
  131. package/src/assets/web-panel/assets/{initDefaultProps-C3wUb4je.js → initDefaultProps-Ckp7xVZS.js} +1 -1
  132. package/src/assets/web-panel/assets/{motion-D-jYFUNA.js → motion-DUZgCpn6.js} +1 -1
  133. package/src/assets/web-panel/assets/{move-tqb8nwJ2.js → move-B2rttglH.js} +1 -1
  134. package/src/assets/web-panel/assets/{mtc-parser-DSOjMJMz.js → mtc-parser-BYWcLTuN.js} +1 -1
  135. package/src/assets/web-panel/assets/{omit-CJ7VimIW.js → omit-DhHqKeFx.js} +1 -1
  136. package/src/assets/web-panel/assets/{pickAttrs-qXiG1iT3.js → pickAttrs-ANpJaWMO.js} +1 -1
  137. package/src/assets/web-panel/assets/{placementArrow-CkGBcy1Z.js → placementArrow-CfZWe7CA.js} +1 -1
  138. package/src/assets/web-panel/assets/{responsiveObserve-D3WvTlLO.js → responsiveObserve-D7Xz2y-R.js} +1 -1
  139. package/src/assets/web-panel/assets/{slide-BEE2ftge.js → slide-DekqCBY9.js} +1 -1
  140. package/src/assets/web-panel/assets/{statusUtils-CsfBpxiG.js → statusUtils-BET6XU7h.js} +1 -1
  141. package/src/assets/web-panel/assets/{styleChecker-xPrIiJPI.js → styleChecker-qHmJjV62.js} +1 -1
  142. package/src/assets/web-panel/assets/{useFlexGapSupport-C5Yd-SCR.js → useFlexGapSupport-BoMCTNyu.js} +1 -1
  143. package/src/assets/web-panel/assets/{useFs-D_MDS8HI.js → useFs-TdW-NgFC.js} +1 -1
  144. package/src/assets/web-panel/assets/{usePersonalDataHub-BqJh-usA.js → usePersonalDataHub-Dyox7wCu.js} +1 -1
  145. package/src/assets/web-panel/assets/{vnode-chm_p-gz.js → vnode-XUn5S8CP.js} +1 -1
  146. package/src/assets/web-panel/assets/{zoom-3-YDNz0Z.js → zoom-Ls77i8hn.js} +1 -1
  147. package/src/assets/web-panel/index.html +1 -1
  148. package/src/command-manifest.json +8 -1
  149. package/src/commands/a2a.js +19 -4
  150. package/src/commands/activitypub.js +20 -3
  151. package/src/commands/agent.js +27 -5
  152. package/src/commands/audit.js +40 -24
  153. package/src/commands/changelog.js +148 -0
  154. package/src/commands/codeintel.js +68 -0
  155. package/src/commands/collab.js +15 -2
  156. package/src/commands/compliance.js +5 -0
  157. package/src/commands/config.js +4 -1
  158. package/src/commands/dao.js +86 -14
  159. package/src/commands/dev.js +2 -0
  160. package/src/commands/did.js +7 -1
  161. package/src/commands/dlp.js +23 -1
  162. package/src/commands/economy.js +29 -3
  163. package/src/commands/eval.js +7 -0
  164. package/src/commands/evomap.js +26 -0
  165. package/src/commands/governance.js +17 -3
  166. package/src/commands/hardening.js +18 -0
  167. package/src/commands/incentive.js +5 -0
  168. package/src/commands/init.js +46 -2
  169. package/src/commands/kg.js +4 -0
  170. package/src/commands/loop.js +6 -1
  171. package/src/commands/lowcode.js +15 -2
  172. package/src/commands/marketplace.js +40 -6
  173. package/src/commands/matrix.js +20 -1
  174. package/src/commands/memory.js +4 -1
  175. package/src/commands/mtc.js +7 -1
  176. package/src/commands/nostr.js +31 -4
  177. package/src/commands/note.js +7 -4
  178. package/src/commands/plugin.js +125 -5
  179. package/src/commands/pqc.js +5 -0
  180. package/src/commands/reputation.js +10 -1
  181. package/src/commands/scim.js +6 -0
  182. package/src/commands/session.js +32 -10
  183. package/src/commands/siem.js +11 -0
  184. package/src/commands/sla.js +18 -3
  185. package/src/commands/social.js +38 -5
  186. package/src/commands/sso.js +10 -2
  187. package/src/commands/stress.js +23 -4
  188. package/src/commands/team.js +9 -2
  189. package/src/commands/tech.js +2 -0
  190. package/src/commands/tenant.js +10 -1
  191. package/src/commands/terraform.js +6 -0
  192. package/src/commands/update.js +1 -1
  193. package/src/commands/zkp.js +20 -0
  194. package/src/data/changelog.json +189 -0
  195. package/src/gateways/ws/message-dispatcher.js +5 -2
  196. package/src/gateways/ws/remote-session-protocol.js +110 -42
  197. package/src/harness/jsonl-session-store.js +12 -5
  198. package/src/harness/plugin-manager.js +16 -4
  199. package/src/harness/remote-command-ledger.js +46 -15
  200. package/src/harness/worktree-isolator.js +16 -6
  201. package/src/index.js +2 -0
  202. package/src/lib/__tests__/logger.test.js +5 -2
  203. package/src/lib/a2a-protocol.js +25 -1
  204. package/src/lib/activitypub-bridge.js +61 -0
  205. package/src/lib/agent-core.js +2 -0
  206. package/src/lib/agent-economy.js +262 -29
  207. package/src/lib/agent-network.js +7 -1
  208. package/src/lib/agent-team/team-runner.js +25 -9
  209. package/src/lib/agent-team/team-worktree.js +36 -4
  210. package/src/lib/async-hook-supervisor.cjs +102 -16
  211. package/src/lib/audit-logger.js +7 -6
  212. package/src/lib/autonomous-developer.js +60 -0
  213. package/src/lib/changelog.js +252 -0
  214. package/src/lib/chat-core.js +17 -4
  215. package/src/lib/collaboration-governance.js +56 -0
  216. package/src/lib/community-governance.js +68 -0
  217. package/src/lib/compliance-manager.js +63 -0
  218. package/src/lib/cross-chain.js +5 -0
  219. package/src/lib/dao-governance.js +151 -2
  220. package/src/lib/did-manager.js +23 -10
  221. package/src/lib/dlp-engine.js +70 -0
  222. package/src/lib/eval/runner.js +89 -1
  223. package/src/lib/eval/tasks.js +170 -0
  224. package/src/lib/eval/trend.js +16 -1
  225. package/src/lib/evolution-system.js +92 -0
  226. package/src/lib/evomap-federation.js +55 -0
  227. package/src/lib/evomap-governance.js +94 -0
  228. package/src/lib/fatal-handler.js +17 -1
  229. package/src/lib/hardening-manager.js +73 -0
  230. package/src/lib/hierarchical-memory.js +14 -8
  231. package/src/lib/knowledge-exporter.js +8 -2
  232. package/src/lib/knowledge-graph.js +79 -0
  233. package/src/lib/llm-providers.js +23 -14
  234. package/src/lib/logger.js +4 -1
  235. package/src/lib/lsp/__tests__/benchmark.test.js +88 -0
  236. package/src/lib/lsp/__tests__/lsp-client.test.js +11 -0
  237. package/src/lib/lsp/__tests__/lsp-manager.test.js +69 -0
  238. package/src/lib/lsp/benchmark.js +257 -0
  239. package/src/lib/lsp/lsp-client.js +30 -9
  240. package/src/lib/lsp/lsp-manager.js +54 -2
  241. package/src/lib/lsp/lsp-server-registry.js +15 -4
  242. package/src/lib/matrix-bridge.js +84 -0
  243. package/src/lib/memory-manager.js +5 -3
  244. package/src/lib/nostr-bridge.js +53 -0
  245. package/src/lib/permission-engine.js +22 -4
  246. package/src/lib/plugin-runtime/__tests__/remote-source.test.js +256 -0
  247. package/src/lib/plugin-runtime/hooks.js +20 -4
  248. package/src/lib/plugin-runtime/install.js +9 -1
  249. package/src/lib/plugin-runtime/monitors.js +20 -4
  250. package/src/lib/plugin-runtime/policy.js +9 -1
  251. package/src/lib/plugin-runtime/remote-source.js +240 -0
  252. package/src/lib/plugin-runtime/signature.js +16 -0
  253. package/src/lib/pqc-manager.js +64 -0
  254. package/src/lib/reputation-optimizer.js +101 -0
  255. package/src/lib/sandbox-egress-proxy.js +195 -59
  256. package/src/lib/sandbox-fs-policy.js +112 -0
  257. package/src/lib/sandbox-network-policy.js +44 -5
  258. package/src/lib/sandbox-v2.js +14 -14
  259. package/src/lib/scim-manager.js +36 -0
  260. package/src/lib/session-manager.js +5 -2
  261. package/src/lib/settings-hooks.cjs +1 -0
  262. package/src/lib/siem-exporter.js +45 -0
  263. package/src/lib/skill-loader.js +20 -2
  264. package/src/lib/skill-marketplace.js +83 -0
  265. package/src/lib/sla-manager.js +88 -0
  266. package/src/lib/social-manager.js +74 -0
  267. package/src/lib/stress-tester.js +65 -0
  268. package/src/lib/tech-learning-engine.js +75 -0
  269. package/src/lib/telemetry/span-recorder.js +12 -1
  270. package/src/lib/tenant-saas.js +107 -0
  271. package/src/lib/terraform-manager.js +67 -0
  272. package/src/lib/token-incentive.js +180 -29
  273. package/src/lib/wallet-manager.js +81 -35
  274. package/src/lib/zkp-engine.js +87 -0
  275. package/src/repl/agent-repl.js +17 -2
  276. package/src/runtime/__tests__/auto-pin.test.js +104 -0
  277. package/src/runtime/agent-core.js +241 -68
  278. package/src/runtime/auto-pin.js +117 -0
  279. package/src/runtime/headless-runner.js +35 -0
  280. package/src/assets/web-panel/assets/OrderTableRenderer-CRIFE2Tj.js +0 -1
  281. package/src/assets/web-panel/assets/Projects-CGcNIs_g.js +0 -1
  282. package/src/assets/web-panel/assets/Terminal-ZzU0Io1Y.js +0 -3
  283. package/src/assets/web-panel/assets/devWarning-DxwmsL4j.js +0 -1
  284. package/src/assets/web-panel/assets/index-Cg2ldRfU.js +0 -1
  285. package/src/assets/web-panel/assets/index-Dwzo-dtJ.js +0 -1
@@ -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) ----
@@ -70,6 +70,13 @@ function tsServer() {
70
70
  // Registry of plugin-contributed servers, keyed by languageId. Wins over
71
71
  // builtins if a plugin explicitly overrides.
72
72
  const pluginServers = new Map();
73
+ // Plugin-contributed extension→languageId overrides, kept SEPARATE from the
74
+ // builtin EXTENSION_LANGUAGE map so `_resetPluginServers` (used by
75
+ // `/reload-plugins`) can fully roll them back. Mutating the builtin map
76
+ // directly meant an uninstalled/untrusted plugin's `.py`→"mylang" remap
77
+ // survived a reload — languageIdForFile kept returning the dead languageId and
78
+ // the builtin server for that extension became unreachable until process exit.
79
+ const pluginExtensions = new Map();
73
80
 
74
81
  /**
75
82
  * Register an extra language server (from a plugin `.lsp.json`).
@@ -87,21 +94,25 @@ export function registerLanguageServer(def) {
87
94
  });
88
95
  if (Array.isArray(def.extensions)) {
89
96
  for (const ext of def.extensions) {
90
- EXTENSION_LANGUAGE[ext.startsWith(".") ? ext : "." + ext] =
91
- def.languageId;
97
+ pluginExtensions.set(
98
+ ext.startsWith(".") ? ext : "." + ext,
99
+ def.languageId,
100
+ );
92
101
  }
93
102
  }
94
103
  }
95
104
 
96
- /** Test/reset hook — clears plugin registrations. */
105
+ /** Test/reset hook — clears plugin registrations (servers AND extension maps). */
97
106
  export function _resetPluginServers() {
98
107
  pluginServers.clear();
108
+ pluginExtensions.clear();
99
109
  }
100
110
 
101
111
  /** Map a file path to its LSP languageId, or null if unsupported. */
102
112
  export function languageIdForFile(filePath) {
103
113
  const ext = path.extname(String(filePath || "")).toLowerCase();
104
- return EXTENSION_LANGUAGE[ext] || null;
114
+ // Plugin overrides win over builtins (same precedence as pluginServers).
115
+ return pluginExtensions.get(ext) || EXTENSION_LANGUAGE[ext] || null;
105
116
  }
106
117
 
107
118
  /**
@@ -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) {
@@ -7,7 +7,7 @@
7
7
 
8
8
  import fs from "fs";
9
9
  import path from "path";
10
- import { likePrefix } from "./sql-like.js";
10
+ import { likePrefix, escapeLike } from "./sql-like.js";
11
11
 
12
12
  /**
13
13
  * Atomically write a UTF-8 file: a crash mid-write must not truncate MEMORY.md
@@ -98,12 +98,14 @@ export function searchMemory(db, query, options = {}) {
98
98
  if (!query || !query.trim()) return [];
99
99
 
100
100
  const limit = Math.max(1, parseInt(options.limit) || 20);
101
- const pattern = `%${query}%`;
101
+ // Escape LIKE wildcards so a query containing % / _ is matched literally
102
+ // rather than as a wildcard (`search "%"` would otherwise match every entry).
103
+ const pattern = `%${escapeLike(query)}%`;
102
104
 
103
105
  return db
104
106
  .prepare(
105
107
  `SELECT * FROM memory_entries
106
- WHERE content LIKE ?
108
+ WHERE content LIKE ? ESCAPE '\\'
107
109
  ORDER BY importance DESC, created_at DESC
108
110
  LIMIT ?`,
109
111
  )
@@ -59,6 +59,59 @@ export function ensureNostrTables(db) {
59
59
  `);
60
60
  }
61
61
 
62
+ /**
63
+ * Rehydrate the module-level _relays/_events Maps from the persisted
64
+ * nostr_relays/nostr_events tables. The `cc` CLI runs every command in a
65
+ * fresh process, so without this the Maps are empty on read and a relay/event
66
+ * added in a prior invocation is invisible (`cc nostr relays`/`events` show
67
+ * nothing; `dm-decrypt` can't find the DM). Call after ensureNostrTables(db).
68
+ * (_keypairs/_didMappings are intentionally ephemeral — no DB table.)
69
+ */
70
+ export function loadFromDb(db) {
71
+ if (!db) return 0;
72
+ _relays.clear();
73
+ _events.clear();
74
+
75
+ const relayRows = db.prepare(`SELECT * FROM nostr_relays`).all();
76
+ for (const row of relayRows) {
77
+ _relays.set(row.id, {
78
+ id: row.id,
79
+ url: row.url,
80
+ status: row.status,
81
+ lastConnected: row.last_connected ?? null,
82
+ eventCount: row.event_count ?? 0,
83
+ readEnabled: !!row.read_enabled,
84
+ writeEnabled: !!row.write_enabled,
85
+ });
86
+ }
87
+
88
+ const eventRows = db.prepare(`SELECT * FROM nostr_events`).all();
89
+ for (const row of eventRows) {
90
+ let tags = [];
91
+ try {
92
+ tags = row.tags ? JSON.parse(row.tags) : [];
93
+ } catch {
94
+ tags = [];
95
+ }
96
+ _events.set(row.id, {
97
+ id: row.id,
98
+ pubkey: row.pubkey,
99
+ kind: row.kind,
100
+ content: row.content,
101
+ tags,
102
+ sig: row.sig ?? "",
103
+ // write side stores createdAt as unix seconds; the column is an ISO
104
+ // string — convert back so the two process paths agree.
105
+ createdAt: row.created_at
106
+ ? Math.floor(new Date(row.created_at).getTime() / 1000)
107
+ : null,
108
+ relayUrl: row.relay_url ?? null,
109
+ });
110
+ }
111
+
112
+ return _relays.size + _events.size;
113
+ }
114
+
62
115
  /* ── Relay Management ─────────────────────────────────────── */
63
116
 
64
117
  export function listRelays() {
@@ -275,6 +275,24 @@ export function revokePermission(db, userDid, permission) {
275
275
  return result.changes > 0;
276
276
  }
277
277
 
278
+ /**
279
+ * Is a grant still active given its stored `expires_at`?
280
+ *
281
+ * The previous check compared `expires_at > now` as ISO STRINGS. That is
282
+ * fail-open on unvalidated input: an unpadded date ("2026-7-5") or any
283
+ * non-numeric-leading garbage ("next year") sorts lexicographically AFTER a
284
+ * canonical `now` ('7'/'n' > '0'/'2'), so an already-expired or malformed
285
+ * time-limited grant silently became a PERMANENT grant. Compare numerically and
286
+ * treat an unparseable expiry as expired (fail closed) so a typo can never widen
287
+ * access. A null/empty `expires_at` means no expiry.
288
+ */
289
+ function _grantActive(expiresAt, nowMs) {
290
+ if (!expiresAt) return true;
291
+ const t = Date.parse(expiresAt);
292
+ if (Number.isNaN(t)) return false;
293
+ return t > nowMs;
294
+ }
295
+
278
296
  /**
279
297
  * Get all roles and direct permissions for a user.
280
298
  */
@@ -287,15 +305,15 @@ export function getUserPermissions(db, userDid) {
287
305
  .all(userDid);
288
306
 
289
307
  // Filter out expired grants
290
- const now = new Date().toISOString();
291
- const grants = allGrants.filter((g) => !g.expires_at || g.expires_at > now);
308
+ const nowMs = Date.now();
309
+ const grants = allGrants.filter((g) => _grantActive(g.expires_at, nowMs));
292
310
 
293
311
  // Get direct permissions (not expired)
294
312
  const allDirectPerms = db
295
313
  .prepare("SELECT * FROM rbac_direct_permissions WHERE user_did = ?")
296
314
  .all(userDid);
297
- const directPerms = allDirectPerms.filter(
298
- (d) => !d.expires_at || d.expires_at > now,
315
+ const directPerms = allDirectPerms.filter((d) =>
316
+ _grantActive(d.expires_at, nowMs),
299
317
  );
300
318
 
301
319
  // Collect all permissions by looking up each role