@remnic/core 1.1.12 → 1.1.14

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 (1324) hide show
  1. package/dist/access-cli.d.ts +2 -1
  2. package/dist/access-cli.js +263 -82
  3. package/dist/access-cli.js.map +1 -1
  4. package/dist/access-http.d.ts +26 -60
  5. package/dist/access-http.js +43 -29
  6. package/dist/access-mcp.d.ts +24 -6
  7. package/dist/access-mcp.js +35 -28
  8. package/dist/access-schema.d.ts +9 -6
  9. package/dist/access-schema.js +7 -5
  10. package/dist/access-service-DcCDmNYC.d.ts +1542 -0
  11. package/dist/access-service.d.ts +25 -7
  12. package/dist/access-service.js +33 -26
  13. package/dist/active-memory-bridge.js +2 -2
  14. package/dist/active-recall.js +11 -3
  15. package/dist/active-recall.js.map +1 -1
  16. package/dist/adapters/claude-code.d.ts +24 -0
  17. package/dist/adapters/claude-code.js +9 -0
  18. package/dist/adapters/codex.d.ts +25 -0
  19. package/dist/adapters/codex.js +9 -0
  20. package/dist/adapters/hermes.d.ts +35 -0
  21. package/dist/adapters/hermes.js +9 -0
  22. package/dist/adapters/index.d.ts +6 -0
  23. package/dist/adapters/index.js +26 -0
  24. package/dist/adapters/registry.d.ts +20 -0
  25. package/dist/adapters/registry.js +13 -0
  26. package/dist/adapters/replit.d.ts +28 -0
  27. package/dist/adapters/replit.js +9 -0
  28. package/dist/adapters/types.d.ts +43 -0
  29. package/dist/adapters/types.js +8 -0
  30. package/dist/bootstrap.d.ts +20 -5
  31. package/dist/boxes.d.ts +7 -0
  32. package/dist/boxes.js +1 -1
  33. package/dist/briefing.d.ts +5 -3
  34. package/dist/briefing.js +9 -6
  35. package/dist/buffer-surprise-report.js +1 -1
  36. package/dist/buffer.d.ts +18 -4
  37. package/dist/buffer.js +1 -1
  38. package/dist/calibration.js +4 -4
  39. package/dist/capsule-cli.d.ts +4 -4
  40. package/dist/capsule-cli.js +1 -1
  41. package/dist/capsule-crypto-5CYAGVC5.js +18 -0
  42. package/dist/capsule-merge-4MGKE7C5.js +189 -0
  43. package/dist/causal-behavior.d.ts +8 -28
  44. package/dist/causal-behavior.js +6 -3
  45. package/dist/causal-behavior.js.map +1 -1
  46. package/dist/causal-chain.js +3 -2
  47. package/dist/causal-consolidation.d.ts +1 -1
  48. package/dist/causal-consolidation.js +24 -13
  49. package/dist/causal-consolidation.js.map +1 -1
  50. package/dist/causal-retrieval.js +3 -3
  51. package/dist/causal-trajectory.js +1 -1
  52. package/dist/chunk-25MQ7IHJ.js +427 -0
  53. package/dist/chunk-25MQ7IHJ.js.map +1 -0
  54. package/dist/chunk-2F2W355T.js +256 -0
  55. package/dist/chunk-2F2W355T.js.map +1 -0
  56. package/dist/chunk-2KI4QFHU.js +228 -0
  57. package/dist/chunk-2KI4QFHU.js.map +1 -0
  58. package/dist/chunk-2PRQG7PV.js +86 -0
  59. package/dist/chunk-2PRQG7PV.js.map +1 -0
  60. package/dist/chunk-2QR3XXIC.js +2272 -0
  61. package/dist/chunk-2QR3XXIC.js.map +1 -0
  62. package/dist/chunk-2WWLHTZY.js +121 -0
  63. package/dist/chunk-326G7DJK.js +2185 -0
  64. package/dist/chunk-326G7DJK.js.map +1 -0
  65. package/dist/chunk-34DQE4KF.js +174 -0
  66. package/dist/chunk-34DQE4KF.js.map +1 -0
  67. package/dist/chunk-3APJ5EVB.js +601 -0
  68. package/dist/chunk-3APJ5EVB.js.map +1 -0
  69. package/dist/chunk-3HPAPHUK.js +51 -0
  70. package/dist/chunk-3HPAPHUK.js.map +1 -0
  71. package/dist/chunk-3JXBXXM2.js +69 -0
  72. package/dist/chunk-3JXBXXM2.js.map +1 -0
  73. package/dist/chunk-3KW65B36.js +681 -0
  74. package/dist/chunk-3KW65B36.js.map +1 -0
  75. package/dist/chunk-3UXOZBHV.js +20 -0
  76. package/dist/chunk-3UXOZBHV.js.map +1 -0
  77. package/dist/chunk-3VAL7ZL2.js +266 -0
  78. package/dist/chunk-3VAL7ZL2.js.map +1 -0
  79. package/dist/chunk-3Y4P7RXM.js +31 -0
  80. package/dist/chunk-3Y4P7RXM.js.map +1 -0
  81. package/dist/chunk-47VWKCAF.js +273 -0
  82. package/dist/chunk-47VWKCAF.js.map +1 -0
  83. package/dist/chunk-4CRG46BG.js +271 -0
  84. package/dist/chunk-5375UYTQ.js +914 -0
  85. package/dist/chunk-5375UYTQ.js.map +1 -0
  86. package/dist/chunk-56K5QLHX.js +506 -0
  87. package/dist/chunk-56K5QLHX.js.map +1 -0
  88. package/dist/chunk-5RGLBDQF.js +596 -0
  89. package/dist/chunk-5RGLBDQF.js.map +1 -0
  90. package/dist/chunk-5UZXUTVO.js +9 -0
  91. package/dist/chunk-5UZXUTVO.js.map +1 -0
  92. package/dist/chunk-65PG43EQ.js +105 -0
  93. package/dist/chunk-65PG43EQ.js.map +1 -0
  94. package/dist/chunk-66DHUKLO.js +57 -0
  95. package/dist/chunk-66DHUKLO.js.map +1 -0
  96. package/dist/chunk-6FC5EGNV.js +46 -0
  97. package/dist/chunk-6FC5EGNV.js.map +1 -0
  98. package/dist/chunk-6H2TESSP.js +62 -0
  99. package/dist/chunk-6H2TESSP.js.map +1 -0
  100. package/dist/chunk-6LVVDPJ4.js +32 -0
  101. package/dist/chunk-6LVVDPJ4.js.map +1 -0
  102. package/dist/chunk-6RVI47ZR.js +159 -0
  103. package/dist/chunk-6RVI47ZR.js.map +1 -0
  104. package/dist/chunk-7AAT6G4Q.js +5117 -0
  105. package/dist/chunk-7AAT6G4Q.js.map +1 -0
  106. package/dist/chunk-7DTASS5T.js +29 -0
  107. package/dist/chunk-7DTASS5T.js.map +1 -0
  108. package/dist/chunk-7IASACLB.js +596 -0
  109. package/dist/chunk-7MNMYOFP.js +32 -0
  110. package/dist/chunk-7MNMYOFP.js.map +1 -0
  111. package/dist/chunk-7N4KAIGN.js +133 -0
  112. package/dist/chunk-7N4KAIGN.js.map +1 -0
  113. package/dist/chunk-7OZ53EXP.js +101 -0
  114. package/dist/chunk-7OZ53EXP.js.map +1 -0
  115. package/dist/chunk-7XYTQGCC.js +134 -0
  116. package/dist/chunk-7XYTQGCC.js.map +1 -0
  117. package/dist/chunk-A2XUIMJ3.js +341 -0
  118. package/dist/chunk-A2XUIMJ3.js.map +1 -0
  119. package/dist/chunk-AGZQD76C.js +201 -0
  120. package/dist/chunk-AGZQD76C.js.map +1 -0
  121. package/dist/chunk-APO3DCMU.js +361 -0
  122. package/dist/chunk-APO3DCMU.js.map +1 -0
  123. package/dist/chunk-BFBF3XEF.js +283 -0
  124. package/dist/chunk-BFBF3XEF.js.map +1 -0
  125. package/dist/chunk-BJ3KMYTB.js +1974 -0
  126. package/dist/chunk-BJ3KMYTB.js.map +1 -0
  127. package/dist/chunk-CHEL3SKB.js +6758 -0
  128. package/dist/chunk-CHEL3SKB.js.map +1 -0
  129. package/dist/chunk-CQZRLNMV.js +1491 -0
  130. package/dist/chunk-CQZRLNMV.js.map +1 -0
  131. package/dist/chunk-D46YSIYX.js +892 -0
  132. package/dist/chunk-D46YSIYX.js.map +1 -0
  133. package/dist/chunk-DINWEURR.js +648 -0
  134. package/dist/chunk-DINWEURR.js.map +1 -0
  135. package/dist/chunk-DK5LDEQM.js +530 -0
  136. package/dist/chunk-DK5LDEQM.js.map +1 -0
  137. package/dist/chunk-DOM4GKSW.js +34 -0
  138. package/dist/chunk-DOM4GKSW.js.map +1 -0
  139. package/dist/chunk-EDTHC6UD.js +1075 -0
  140. package/dist/chunk-EFJ3MQ4V.js +721 -0
  141. package/dist/chunk-EHRTFRWW.js +89 -0
  142. package/dist/chunk-EHRTFRWW.js.map +1 -0
  143. package/dist/chunk-FAJ7FZYM.js +11 -0
  144. package/dist/chunk-FAJ7FZYM.js.map +1 -0
  145. package/dist/chunk-FBYESMQ2.js +570 -0
  146. package/dist/chunk-FDU6HUUL.js +147 -0
  147. package/dist/chunk-FF4KLI5W.js +99 -0
  148. package/dist/chunk-FF4KLI5W.js.map +1 -0
  149. package/dist/chunk-FIT6DMX6.js +310 -0
  150. package/dist/chunk-FIT6DMX6.js.map +1 -0
  151. package/dist/chunk-FJ43PRLT.js +272 -0
  152. package/dist/chunk-FJ43PRLT.js.map +1 -0
  153. package/dist/chunk-FKFMOY3N.js +32 -0
  154. package/dist/chunk-FKFMOY3N.js.map +1 -0
  155. package/dist/chunk-FLTNHQK6.js +262 -0
  156. package/dist/chunk-FLTNHQK6.js.map +1 -0
  157. package/dist/chunk-GA454ALV.js +12436 -0
  158. package/dist/chunk-GA454ALV.js.map +1 -0
  159. package/dist/chunk-GGKRUQOO.js +228 -0
  160. package/dist/chunk-GIF42EW3.js +63 -0
  161. package/dist/chunk-GIF42EW3.js.map +1 -0
  162. package/dist/chunk-GL6I6MEQ.js +647 -0
  163. package/dist/chunk-H3ME6L6D.js +709 -0
  164. package/dist/chunk-H3ME6L6D.js.map +1 -0
  165. package/dist/chunk-HHLLAQGZ.js +1 -0
  166. package/dist/chunk-HXXBL2KD.js +2040 -0
  167. package/dist/chunk-I5V2VDIW.js +219 -0
  168. package/dist/chunk-I5V2VDIW.js.map +1 -0
  169. package/dist/chunk-I6K5FBRQ.js +35 -0
  170. package/dist/chunk-I6K5FBRQ.js.map +1 -0
  171. package/dist/chunk-ICRIXAP2.js +121 -0
  172. package/dist/chunk-ICRIXAP2.js.map +1 -0
  173. package/dist/chunk-J4EB7DNW.js +11 -0
  174. package/dist/chunk-J4EB7DNW.js.map +1 -0
  175. package/dist/chunk-JLFA7DQG.js +62 -0
  176. package/dist/chunk-JLFA7DQG.js.map +1 -0
  177. package/dist/chunk-KJTKLXTH.js +9 -0
  178. package/dist/chunk-KJTKLXTH.js.map +1 -0
  179. package/dist/chunk-KLAO5DGL.js +917 -0
  180. package/dist/chunk-KLAO5DGL.js.map +1 -0
  181. package/dist/chunk-KNKUID7G.js +183 -0
  182. package/dist/chunk-KOSORCJG.js +624 -0
  183. package/dist/chunk-KOSORCJG.js.map +1 -0
  184. package/dist/chunk-KUJVMMZQ.js +1262 -0
  185. package/dist/chunk-KUJVMMZQ.js.map +1 -0
  186. package/dist/chunk-LCR46JY5.js +123 -0
  187. package/dist/chunk-LCR46JY5.js.map +1 -0
  188. package/dist/chunk-LLQ2LLWF.js +148 -0
  189. package/dist/chunk-LLQ2LLWF.js.map +1 -0
  190. package/dist/chunk-LPMVBPA3.js +236 -0
  191. package/dist/chunk-LT3NLYSI.js +50 -0
  192. package/dist/chunk-LT3NLYSI.js.map +1 -0
  193. package/dist/chunk-LUDTDZLK.js +287 -0
  194. package/dist/chunk-LUDTDZLK.js.map +1 -0
  195. package/dist/chunk-M23FSH32.js +3963 -0
  196. package/dist/chunk-M23FSH32.js.map +1 -0
  197. package/dist/chunk-MC26UJIM.js +118 -0
  198. package/dist/chunk-ME6ESPZU.js +119 -0
  199. package/dist/chunk-ME6ESPZU.js.map +1 -0
  200. package/dist/chunk-MGKYQQYF.js +272 -0
  201. package/dist/chunk-MJFNCJXV.js +66 -0
  202. package/dist/chunk-MJFNCJXV.js.map +1 -0
  203. package/dist/chunk-MSWG7JI6.js +237 -0
  204. package/dist/chunk-MSWG7JI6.js.map +1 -0
  205. package/dist/chunk-MT25YHYH.js +141 -0
  206. package/dist/chunk-MT25YHYH.js.map +1 -0
  207. package/dist/chunk-MT4HVDUZ.js +53 -0
  208. package/dist/chunk-MY6TPVXW.js +219 -0
  209. package/dist/chunk-N2D6GXBM.js +267 -0
  210. package/dist/chunk-N2D6GXBM.js.map +1 -0
  211. package/dist/chunk-NJ3MJQZX.js +46 -0
  212. package/dist/chunk-NJ3MJQZX.js.map +1 -0
  213. package/dist/chunk-NMZY542O.js +335 -0
  214. package/dist/chunk-NMZY542O.js.map +1 -0
  215. package/dist/chunk-NNVTUXEB.js +23 -0
  216. package/dist/chunk-NZL6GGQE.js +375 -0
  217. package/dist/chunk-NZL6GGQE.js.map +1 -0
  218. package/dist/chunk-P4NEIHUT.js +108 -0
  219. package/dist/chunk-P7FMDTKL.js +103 -0
  220. package/dist/chunk-P7FMDTKL.js.map +1 -0
  221. package/dist/chunk-PHK3HARR.js +32 -0
  222. package/dist/chunk-PHK3HARR.js.map +1 -0
  223. package/dist/chunk-PIRJPV5T.js +98 -0
  224. package/dist/chunk-PIRJPV5T.js.map +1 -0
  225. package/dist/chunk-PK7H5L6Y.js +159 -0
  226. package/dist/chunk-PK7H5L6Y.js.map +1 -0
  227. package/dist/chunk-PR5FBTFU.js +233 -0
  228. package/dist/chunk-PR5FBTFU.js.map +1 -0
  229. package/dist/chunk-PU63GXWS.js +174 -0
  230. package/dist/chunk-PU63GXWS.js.map +1 -0
  231. package/dist/chunk-PZIAX57I.js +124 -0
  232. package/dist/chunk-PZIAX57I.js.map +1 -0
  233. package/dist/chunk-Q7P4WJDP.js +26 -0
  234. package/dist/chunk-Q7P4WJDP.js.map +1 -0
  235. package/dist/chunk-QQUAB63I.js +63 -0
  236. package/dist/chunk-QQUAB63I.js.map +1 -0
  237. package/dist/chunk-QRNI5JBH.js +18 -0
  238. package/dist/chunk-RHY3HH7P.js +601 -0
  239. package/dist/chunk-RHY3HH7P.js.map +1 -0
  240. package/dist/chunk-RRF5UOBJ.js +91 -0
  241. package/dist/chunk-RXDLTSWT.js +124 -0
  242. package/dist/chunk-RXDLTSWT.js.map +1 -0
  243. package/dist/chunk-RYED3SPJ.js +42 -0
  244. package/dist/chunk-RYED3SPJ.js.map +1 -0
  245. package/dist/chunk-S7KDBTWT.js +106 -0
  246. package/dist/chunk-S7KDBTWT.js.map +1 -0
  247. package/dist/chunk-SEDEKFYQ.js +1 -0
  248. package/dist/chunk-TECVW3JP.js +36 -0
  249. package/dist/chunk-TECVW3JP.js.map +1 -0
  250. package/dist/chunk-TFO23QT4.js +88 -0
  251. package/dist/chunk-TFO23QT4.js.map +1 -0
  252. package/dist/chunk-TK4UEOSK.js +76 -0
  253. package/dist/chunk-TK4UEOSK.js.map +1 -0
  254. package/dist/chunk-TKWGAOLV.js +122 -0
  255. package/dist/chunk-TKWGAOLV.js.map +1 -0
  256. package/dist/chunk-TMM4S4IJ.js +597 -0
  257. package/dist/chunk-TMM4S4IJ.js.map +1 -0
  258. package/dist/chunk-TMQLARTH.js +188 -0
  259. package/dist/chunk-TMQLARTH.js.map +1 -0
  260. package/dist/chunk-TPDBFYEG.js +130 -0
  261. package/dist/chunk-TPDBFYEG.js.map +1 -0
  262. package/dist/chunk-TPMQ3G6Z.js +145 -0
  263. package/dist/chunk-TPMQ3G6Z.js.map +1 -0
  264. package/dist/chunk-TZOLIGIG.js +61 -0
  265. package/dist/chunk-TZOLIGIG.js.map +1 -0
  266. package/dist/chunk-U3PN77QT.js +113 -0
  267. package/dist/chunk-U3WSW6PZ.js +277 -0
  268. package/dist/chunk-U4SCL7B7.js +640 -0
  269. package/dist/chunk-U4SCL7B7.js.map +1 -0
  270. package/dist/chunk-UWK5OXUJ.js +156 -0
  271. package/dist/chunk-UWK5OXUJ.js.map +1 -0
  272. package/dist/chunk-UWVJF25J.js +74 -0
  273. package/dist/chunk-UXHQAFNA.js +1317 -0
  274. package/dist/chunk-UXHQAFNA.js.map +1 -0
  275. package/dist/chunk-V5OCT34X.js +1 -0
  276. package/dist/chunk-VLXA6PI2.js +304 -0
  277. package/dist/chunk-VLXA6PI2.js.map +1 -0
  278. package/dist/chunk-VNO6ZJ35.js +500 -0
  279. package/dist/chunk-VNO6ZJ35.js.map +1 -0
  280. package/dist/chunk-VW676BEI.js +827 -0
  281. package/dist/chunk-VW676BEI.js.map +1 -0
  282. package/dist/chunk-W3LR522O.js +2296 -0
  283. package/dist/chunk-W4L6CZKA.js +96 -0
  284. package/dist/chunk-W4L6CZKA.js.map +1 -0
  285. package/dist/chunk-W4RVMTHR.js +372 -0
  286. package/dist/chunk-W4RVMTHR.js.map +1 -0
  287. package/dist/chunk-WEHSQBFR.js +188 -0
  288. package/dist/chunk-WEHSQBFR.js.map +1 -0
  289. package/dist/chunk-WELDCG6C.js +380 -0
  290. package/dist/chunk-WELDCG6C.js.map +1 -0
  291. package/dist/chunk-WZYKANL3.js +2800 -0
  292. package/dist/chunk-WZYKANL3.js.map +1 -0
  293. package/dist/chunk-XIG5PDM7.js +48 -0
  294. package/dist/chunk-XJNBEDFE.js +193 -0
  295. package/dist/chunk-XJNBEDFE.js.map +1 -0
  296. package/dist/chunk-XVVIG67A.js +291 -0
  297. package/dist/chunk-XVVIG67A.js.map +1 -0
  298. package/dist/chunk-XVZ7B3HG.js +135 -0
  299. package/dist/chunk-YBPYIAA5.js +73 -0
  300. package/dist/chunk-YBPYIAA5.js.map +1 -0
  301. package/dist/chunk-Z734BLO3.js +21 -0
  302. package/dist/chunk-Z734BLO3.js.map +1 -0
  303. package/dist/chunk-ZKSK55RC.js +269 -0
  304. package/dist/chunk-ZKSK55RC.js.map +1 -0
  305. package/dist/chunk-ZTFCYYEZ.js +69 -0
  306. package/dist/chunk-ZTFCYYEZ.js.map +1 -0
  307. package/dist/chunk-ZY2MNJR6.js +329 -0
  308. package/dist/chunk-ZY2MNJR6.js.map +1 -0
  309. package/dist/cli-D3VpkVwB.d.ts +1136 -0
  310. package/dist/cli.d.ts +39 -10
  311. package/dist/cli.js +108 -49
  312. package/dist/commitment-ledger.js +1 -1
  313. package/dist/compat/checks.d.ts +5 -0
  314. package/dist/compat/checks.js +11 -0
  315. package/dist/compat/checks.js.map +1 -0
  316. package/dist/compat/types.d.ts +30 -0
  317. package/dist/compat/types.js +1 -0
  318. package/dist/compat/types.js.map +1 -0
  319. package/dist/compounding/engine.d.ts +221 -0
  320. package/dist/compounding/engine.js +32 -0
  321. package/dist/compounding/engine.js.map +1 -0
  322. package/dist/compounding/preference-consolidator.d.ts +92 -0
  323. package/dist/compounding/preference-consolidator.js +553 -0
  324. package/dist/compounding/preference-consolidator.js.map +1 -0
  325. package/dist/config.d.ts +4 -2
  326. package/dist/config.js +9 -4
  327. package/dist/conflict-policy-DyJ2wd-h.d.ts +4 -0
  328. package/dist/connectors/codex-materialize-runner.d.ts +64 -0
  329. package/dist/connectors/codex-materialize-runner.js +33 -0
  330. package/dist/connectors/codex-materialize-runner.js.map +1 -0
  331. package/dist/connectors/codex-materialize.d.ts +195 -0
  332. package/dist/connectors/codex-materialize.js +38 -0
  333. package/dist/connectors/codex-materialize.js.map +1 -0
  334. package/dist/connectors/index.d.ts +444 -0
  335. package/dist/connectors/index.js +115 -0
  336. package/dist/connectors/index.js.map +1 -0
  337. package/dist/connectors-cli-CwbyjGR7.d.ts +257 -0
  338. package/dist/connectors-cli.d.ts +1 -1
  339. package/dist/consolidation-provenance-check.d.ts +3 -1
  340. package/dist/consolidation-undo.d.ts +3 -1
  341. package/dist/contradiction/index.d.ts +258 -0
  342. package/dist/contradiction/index.js +43 -0
  343. package/dist/contradiction/index.js.map +1 -0
  344. package/dist/contradiction-review-ATP4S6IC.js +30 -0
  345. package/dist/contradiction-review-ATP4S6IC.js.map +1 -0
  346. package/dist/contradiction-scan-5A4IDZV5.js +13 -0
  347. package/dist/contradiction-scan-5A4IDZV5.js.map +1 -0
  348. package/dist/conversation-index/backend.d.ts +97 -0
  349. package/dist/conversation-index/backend.js +13 -0
  350. package/dist/conversation-index/backend.js.map +1 -0
  351. package/dist/conversation-index/chunker.d.ts +16 -0
  352. package/dist/conversation-index/chunker.js +8 -0
  353. package/dist/conversation-index/chunker.js.map +1 -0
  354. package/dist/conversation-index/cleanup.d.ts +11 -0
  355. package/dist/conversation-index/cleanup.js +9 -0
  356. package/dist/conversation-index/cleanup.js.map +1 -0
  357. package/dist/conversation-index/faiss-adapter.d.ts +6 -0
  358. package/dist/conversation-index/faiss-adapter.js +16 -0
  359. package/dist/conversation-index/faiss-adapter.js.map +1 -0
  360. package/dist/conversation-index/indexer.d.ts +23 -0
  361. package/dist/conversation-index/indexer.js +15 -0
  362. package/dist/conversation-index/indexer.js.map +1 -0
  363. package/dist/conversation-index/search.d.ts +6 -0
  364. package/dist/conversation-index/search.js +11 -0
  365. package/dist/conversation-index/search.js.map +1 -0
  366. package/dist/embedding-fallback.js +2 -2
  367. package/dist/enrichment/index.d.ts +163 -0
  368. package/dist/enrichment/index.js +18 -0
  369. package/dist/enrichment/index.js.map +1 -0
  370. package/dist/entity-retrieval.d.ts +4 -2
  371. package/dist/entity-retrieval.js +8 -5
  372. package/dist/evals.js +1 -1
  373. package/dist/explicit-capture.d.ts +20 -5
  374. package/dist/explicit-capture.js +2 -2
  375. package/dist/extraction-judge-training.js +1 -1
  376. package/dist/extraction.js +8 -8
  377. package/dist/faiss-adapter-CzPghc4C.d.ts +70 -0
  378. package/dist/fallback-llm.d.ts +2 -0
  379. package/dist/fallback-llm.js +4 -4
  380. package/dist/graph-edge-decay-5DI5GUNL.js +207 -0
  381. package/dist/index.d.ts +66 -711
  382. package/dist/index.js +556 -2680
  383. package/dist/index.js.map +1 -1
  384. package/dist/lcm/archive.d.ts +89 -0
  385. package/dist/lcm/archive.js +12 -0
  386. package/dist/lcm/archive.js.map +1 -0
  387. package/dist/lcm/dag.d.ts +48 -0
  388. package/dist/lcm/dag.js +8 -0
  389. package/dist/lcm/dag.js.map +1 -0
  390. package/dist/lcm/engine.d.ts +116 -0
  391. package/dist/lcm/engine.js +20 -0
  392. package/dist/lcm/engine.js.map +1 -0
  393. package/dist/lcm/index.d.ts +12 -0
  394. package/dist/lcm/index.js +44 -0
  395. package/dist/lcm/index.js.map +1 -0
  396. package/dist/lcm/queue.d.ts +62 -0
  397. package/dist/lcm/queue.js +8 -0
  398. package/dist/lcm/queue.js.map +1 -0
  399. package/dist/lcm/recall.d.ts +20 -0
  400. package/dist/lcm/recall.js +8 -0
  401. package/dist/lcm/recall.js.map +1 -0
  402. package/dist/lcm/schema.d.ts +16 -0
  403. package/dist/lcm/schema.js +14 -0
  404. package/dist/lcm/schema.js.map +1 -0
  405. package/dist/lcm/summarizer.d.ts +38 -0
  406. package/dist/lcm/summarizer.js +12 -0
  407. package/dist/lcm/summarizer.js.map +1 -0
  408. package/dist/lcm/tools.d.ts +29 -0
  409. package/dist/lcm/tools.js +8 -0
  410. package/dist/lcm/tools.js.map +1 -0
  411. package/dist/live-connectors-runner.js +5 -5
  412. package/dist/local-llm.js +3 -3
  413. package/dist/maintenance/archive-observations.d.ts +18 -0
  414. package/dist/maintenance/archive-observations.js +8 -0
  415. package/dist/maintenance/archive-observations.js.map +1 -0
  416. package/dist/maintenance/backup-stamp.d.ts +3 -0
  417. package/dist/maintenance/backup-stamp.js +8 -0
  418. package/dist/maintenance/backup-stamp.js.map +1 -0
  419. package/dist/maintenance/memory-governance-cron.d.ts +85 -0
  420. package/dist/maintenance/memory-governance-cron.js +22 -0
  421. package/dist/maintenance/memory-governance-cron.js.map +1 -0
  422. package/dist/maintenance/memory-governance.d.ts +137 -0
  423. package/dist/maintenance/memory-governance.js +40 -0
  424. package/dist/maintenance/memory-governance.js.map +1 -0
  425. package/dist/maintenance/migrate-observations.d.ts +18 -0
  426. package/dist/maintenance/migrate-observations.js +9 -0
  427. package/dist/maintenance/migrate-observations.js.map +1 -0
  428. package/dist/maintenance/observation-ledger-utils.d.ts +10 -0
  429. package/dist/maintenance/observation-ledger-utils.js +10 -0
  430. package/dist/maintenance/observation-ledger-utils.js.map +1 -0
  431. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +15 -0
  432. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +28 -0
  433. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js.map +1 -0
  434. package/dist/maintenance/rebuild-memory-projection.d.ts +77 -0
  435. package/dist/maintenance/rebuild-memory-projection.js +35 -0
  436. package/dist/maintenance/rebuild-memory-projection.js.map +1 -0
  437. package/dist/maintenance/rebuild-observations.d.ts +17 -0
  438. package/dist/maintenance/rebuild-observations.js +9 -0
  439. package/dist/maintenance/rebuild-observations.js.map +1 -0
  440. package/dist/mcp-memory-inspector-app.d.ts +24 -6
  441. package/dist/memory-projection-store.d.ts +108 -3
  442. package/dist/memory-projection-store.js +2 -1
  443. package/dist/memory-worth-outcomes.d.ts +4 -2
  444. package/dist/migrate/from-engram.d.ts +24 -0
  445. package/dist/migrate/from-engram.js +12 -0
  446. package/dist/migrate/from-engram.js.map +1 -0
  447. package/dist/namespaces/migrate.d.ts +50 -0
  448. package/dist/namespaces/migrate.js +50 -0
  449. package/dist/namespaces/migrate.js.map +1 -0
  450. package/dist/namespaces/principal.d.ts +17 -0
  451. package/dist/namespaces/principal.js +16 -0
  452. package/dist/namespaces/principal.js.map +1 -0
  453. package/dist/namespaces/search.d.ts +46 -0
  454. package/dist/namespaces/search.js +28 -0
  455. package/dist/namespaces/search.js.map +1 -0
  456. package/dist/namespaces/storage.d.ts +32 -0
  457. package/dist/namespaces/storage.js +28 -0
  458. package/dist/namespaces/storage.js.map +1 -0
  459. package/dist/network/tailscale.d.ts +41 -0
  460. package/dist/network/tailscale.js +9 -0
  461. package/dist/network/tailscale.js.map +1 -0
  462. package/dist/network/webdav.d.ts +39 -0
  463. package/dist/network/webdav.js +10 -0
  464. package/dist/network/webdav.js.map +1 -0
  465. package/dist/objective-state-writers.js +2 -2
  466. package/dist/operator-toolkit.d.ts +4 -2
  467. package/dist/operator-toolkit.js +32 -14
  468. package/dist/opik-exporter.js +2 -2
  469. package/dist/opik-exporter.js.map +1 -1
  470. package/dist/orchestrator-DuWl9Hwx.d.ts +1244 -0
  471. package/dist/orchestrator.d.ts +22 -7
  472. package/dist/orchestrator.js +79 -44
  473. package/dist/path-MR5JPYOP.js +9 -0
  474. package/dist/path-MR5JPYOP.js.map +1 -0
  475. package/dist/qmd-recall-cache.d.ts +1 -1
  476. package/dist/qmd.d.ts +102 -3
  477. package/dist/qmd.js +23 -5
  478. package/dist/recall-explain-renderer.js +3 -3
  479. package/dist/recall-xray-cli.js +4 -4
  480. package/dist/recall-xray-renderer.js +3 -3
  481. package/dist/recall-xray.js +2 -2
  482. package/dist/replay/normalizers/chatgpt.d.ts +6 -0
  483. package/dist/replay/normalizers/chatgpt.js +11 -0
  484. package/dist/replay/normalizers/chatgpt.js.map +1 -0
  485. package/dist/replay/normalizers/claude.d.ts +6 -0
  486. package/dist/replay/normalizers/claude.js +11 -0
  487. package/dist/replay/normalizers/claude.js.map +1 -0
  488. package/dist/replay/normalizers/openclaw.d.ts +6 -0
  489. package/dist/replay/normalizers/openclaw.js +11 -0
  490. package/dist/replay/normalizers/openclaw.js.map +1 -0
  491. package/dist/replay/normalizers/shared.d.ts +16 -0
  492. package/dist/replay/normalizers/shared.js +14 -0
  493. package/dist/replay/normalizers/shared.js.map +1 -0
  494. package/dist/replay/runner.d.ts +35 -0
  495. package/dist/replay/runner.js +16 -0
  496. package/dist/replay/runner.js.map +1 -0
  497. package/dist/replay/types.d.ts +57 -0
  498. package/dist/replay/types.js +19 -0
  499. package/dist/replay/types.js.map +1 -0
  500. package/dist/resolution-B7FNQSSP.js +12 -0
  501. package/dist/resolution-B7FNQSSP.js.map +1 -0
  502. package/dist/resolve-provider-secret.js +2 -2
  503. package/dist/resume-bundles.js +8 -6
  504. package/dist/retrieval-agents.d.ts +1 -1
  505. package/dist/routing/engine.d.ts +35 -0
  506. package/dist/routing/engine.js +16 -0
  507. package/dist/routing/engine.js.map +1 -0
  508. package/dist/routing/store.d.ts +27 -0
  509. package/dist/routing/store.js +10 -0
  510. package/dist/routing/store.js.map +1 -0
  511. package/dist/runtime/better-sqlite.d.ts +8 -0
  512. package/dist/runtime/better-sqlite.js +10 -0
  513. package/dist/runtime/better-sqlite.js.map +1 -0
  514. package/dist/runtime/child-process.d.ts +32 -0
  515. package/dist/runtime/child-process.js +10 -0
  516. package/dist/runtime/child-process.js.map +1 -0
  517. package/dist/runtime/env.d.ts +5 -0
  518. package/dist/runtime/env.js +12 -0
  519. package/dist/runtime/env.js.map +1 -0
  520. package/dist/schemas.d.ts +22 -22
  521. package/dist/sdk-compat.js +1 -1
  522. package/dist/search/document-scanner.d.ts +22 -0
  523. package/dist/search/document-scanner.js +8 -0
  524. package/dist/search/document-scanner.js.map +1 -0
  525. package/dist/search/embed-helper.d.ts +35 -0
  526. package/dist/search/embed-helper.js +9 -0
  527. package/dist/search/embed-helper.js.map +1 -0
  528. package/dist/search/factory.d.ts +32 -0
  529. package/dist/search/factory.js +29 -0
  530. package/dist/search/factory.js.map +1 -0
  531. package/dist/search/index.d.ts +15 -0
  532. package/dist/search/index.js +50 -0
  533. package/dist/search/index.js.map +1 -0
  534. package/dist/search/lancedb-backend.d.ts +51 -0
  535. package/dist/search/lancedb-backend.js +10 -0
  536. package/dist/search/lancedb-backend.js.map +1 -0
  537. package/dist/search/meilisearch-backend.d.ts +48 -0
  538. package/dist/search/meilisearch-backend.js +10 -0
  539. package/dist/search/meilisearch-backend.js.map +1 -0
  540. package/dist/search/noop-backend.d.ts +26 -0
  541. package/dist/search/noop-backend.js +8 -0
  542. package/dist/search/noop-backend.js.map +1 -0
  543. package/dist/search/orama-backend.d.ts +53 -0
  544. package/dist/search/orama-backend.js +10 -0
  545. package/dist/search/orama-backend.js.map +1 -0
  546. package/dist/search/port.d.ts +61 -0
  547. package/dist/search/port.js +1 -0
  548. package/dist/search/port.js.map +1 -0
  549. package/dist/search/remote-backend.d.ts +39 -0
  550. package/dist/search/remote-backend.js +9 -0
  551. package/dist/search/remote-backend.js.map +1 -0
  552. package/dist/secure-store/index.d.ts +890 -0
  553. package/dist/secure-store/index.js +156 -0
  554. package/dist/secure-store/index.js.map +1 -0
  555. package/dist/semantic-VwGI14Ok.d.ts +69 -0
  556. package/dist/semantic-consolidation-4HkHWgeI.d.ts +180 -0
  557. package/dist/semantic-consolidation.d.ts +2 -2
  558. package/dist/semantic-consolidation.js +13 -6
  559. package/dist/semantic-rule-promotion.js +8 -5
  560. package/dist/semantic-rule-verifier.js +8 -5
  561. package/dist/shared-context/manager.d.ts +131 -0
  562. package/dist/shared-context/manager.js +15 -0
  563. package/dist/shared-context/manager.js.map +1 -0
  564. package/dist/skills-registry.js +13 -1
  565. package/dist/skills-registry.js.map +1 -1
  566. package/dist/state-store-VZU2IA53.js +16 -0
  567. package/dist/state-store-VZU2IA53.js.map +1 -0
  568. package/dist/storage-paths.d.ts +9 -0
  569. package/dist/storage-paths.js +20 -0
  570. package/dist/storage-paths.js.map +1 -0
  571. package/dist/storage.d.ts +3 -1
  572. package/dist/storage.js +7 -4
  573. package/dist/summarizer.d.ts +5 -0
  574. package/dist/summarizer.js +9 -8
  575. package/dist/summary-snapshot.js +2 -1
  576. package/dist/surfaces/dreams.d.ts +16 -0
  577. package/dist/surfaces/dreams.js +282 -0
  578. package/dist/surfaces/dreams.js.map +1 -0
  579. package/dist/surfaces/heartbeat.d.ts +17 -0
  580. package/dist/surfaces/heartbeat.js +265 -0
  581. package/dist/surfaces/heartbeat.js.map +1 -0
  582. package/dist/temporal-supersession.d.ts +3 -1
  583. package/dist/threading.d.ts +5 -0
  584. package/dist/threading.js +2 -1
  585. package/dist/tier-migration.d.ts +4 -2
  586. package/dist/tokens.js +2 -2
  587. package/dist/transcript.d.ts +15 -1
  588. package/dist/transcript.js +2 -1
  589. package/dist/transfer/autodetect.d.ts +4 -0
  590. package/dist/transfer/autodetect.js +15 -0
  591. package/dist/transfer/autodetect.js.map +1 -0
  592. package/dist/transfer/backup.d.ts +21 -0
  593. package/dist/transfer/backup.js +17 -0
  594. package/dist/transfer/backup.js.map +1 -0
  595. package/dist/transfer/capsule-export.d.ts +113 -0
  596. package/dist/transfer/capsule-export.js +19 -0
  597. package/dist/transfer/capsule-export.js.map +1 -0
  598. package/dist/transfer/capsule-import.d.ts +124 -0
  599. package/dist/transfer/capsule-import.js +16 -0
  600. package/dist/transfer/capsule-import.js.map +1 -0
  601. package/dist/transfer/constants.d.ts +13 -0
  602. package/dist/transfer/constants.js +12 -0
  603. package/dist/transfer/constants.js.map +1 -0
  604. package/dist/transfer/export-json.d.ts +11 -0
  605. package/dist/transfer/export-json.js +11 -0
  606. package/dist/transfer/export-json.js.map +1 -0
  607. package/dist/transfer/export-md.d.ts +10 -0
  608. package/dist/transfer/export-md.js +13 -0
  609. package/dist/transfer/export-md.js.map +1 -0
  610. package/dist/transfer/export-sqlite.d.ts +9 -0
  611. package/dist/transfer/export-sqlite.js +12 -0
  612. package/dist/transfer/export-sqlite.js.map +1 -0
  613. package/dist/transfer/fs-utils.d.ts +61 -0
  614. package/dist/transfer/fs-utils.js +40 -0
  615. package/dist/transfer/fs-utils.js.map +1 -0
  616. package/dist/transfer/import-json.d.ts +16 -0
  617. package/dist/transfer/import-json.js +13 -0
  618. package/dist/transfer/import-json.js.map +1 -0
  619. package/dist/transfer/import-md.d.ts +14 -0
  620. package/dist/transfer/import-md.js +11 -0
  621. package/dist/transfer/import-md.js.map +1 -0
  622. package/dist/transfer/import-sqlite.d.ts +14 -0
  623. package/dist/transfer/import-sqlite.js +12 -0
  624. package/dist/transfer/import-sqlite.js.map +1 -0
  625. package/dist/transfer/sqlite-schema.d.ts +4 -0
  626. package/dist/transfer/sqlite-schema.js +10 -0
  627. package/dist/transfer/sqlite-schema.js.map +1 -0
  628. package/dist/transfer/types.d.ts +916 -0
  629. package/dist/transfer/types.js +30 -0
  630. package/dist/transfer/types.js.map +1 -0
  631. package/dist/types.d.ts +28 -1
  632. package/dist/types.js +1 -1
  633. package/dist/verified-recall.js +9 -6
  634. package/dist/work/board.d.ts +43 -0
  635. package/dist/work/board.js +14 -0
  636. package/dist/work/board.js.map +1 -0
  637. package/dist/work/boundary.d.ts +8 -0
  638. package/dist/work/boundary.js +14 -0
  639. package/dist/work/boundary.js.map +1 -0
  640. package/dist/work/storage.d.ts +39 -0
  641. package/dist/work/storage.js +11 -0
  642. package/dist/work/storage.js.map +1 -0
  643. package/dist/work/types.d.ts +75 -0
  644. package/dist/work/types.js +1 -0
  645. package/dist/work/types.js.map +1 -0
  646. package/package.json +2767 -6
  647. package/scripts/faiss_index.py +816 -0
  648. package/scripts/faiss_requirements.txt +3 -0
  649. package/skills/remnic-entities/SKILL.md +51 -0
  650. package/skills/remnic-memory-workflow/SKILL.md +61 -0
  651. package/skills/remnic-recall/SKILL.md +51 -0
  652. package/skills/remnic-remember/SKILL.md +56 -0
  653. package/skills/remnic-search/SKILL.md +51 -0
  654. package/skills/remnic-status/SKILL.md +51 -0
  655. package/src/abort-error.test.ts +49 -0
  656. package/src/abort-error.ts +46 -0
  657. package/src/abstraction-nodes.ts +162 -0
  658. package/src/access-audit.test.ts +178 -0
  659. package/src/access-audit.ts +125 -0
  660. package/src/access-cli.test.ts +439 -0
  661. package/src/access-cli.ts +438 -0
  662. package/src/access-http.test.ts +225 -0
  663. package/src/access-http.ts +1899 -0
  664. package/src/access-idempotency.ts +232 -0
  665. package/src/access-mcp.test.ts +568 -0
  666. package/src/access-mcp.ts +3056 -0
  667. package/src/access-schema-pi.test.ts +60 -0
  668. package/src/access-schema.ts +522 -0
  669. package/src/access-service-namespace.test.ts +123 -0
  670. package/src/access-service.ts +5629 -0
  671. package/src/action-confidence.test.ts +206 -0
  672. package/src/action-confidence.ts +466 -0
  673. package/src/active-memory-bridge.test.ts +285 -0
  674. package/src/active-memory-bridge.ts +217 -0
  675. package/src/active-recall.test.ts +484 -0
  676. package/src/active-recall.ts +459 -0
  677. package/src/adapters/claude-code.ts +56 -0
  678. package/src/adapters/codex.ts +57 -0
  679. package/src/adapters/hermes.ts +64 -0
  680. package/src/adapters/index.ts +6 -0
  681. package/src/adapters/registry.ts +41 -0
  682. package/src/adapters/replit.ts +55 -0
  683. package/src/adapters/types.ts +51 -0
  684. package/src/behavior-learner.ts +144 -0
  685. package/src/behavior-signals.ts +73 -0
  686. package/src/binary-lifecycle/backend.ts +117 -0
  687. package/src/binary-lifecycle/index.ts +35 -0
  688. package/src/binary-lifecycle/manifest.ts +79 -0
  689. package/src/binary-lifecycle/pipeline.ts +352 -0
  690. package/src/binary-lifecycle/scanner.ts +89 -0
  691. package/src/binary-lifecycle/types.ts +89 -0
  692. package/src/bootstrap.ts +178 -0
  693. package/src/boxes.ts +521 -0
  694. package/src/briefing.test.ts +1535 -0
  695. package/src/briefing.ts +1382 -0
  696. package/src/buffer-session.test.ts +443 -0
  697. package/src/buffer-surprise-report.ts +176 -0
  698. package/src/buffer-surprise-telemetry.test.ts +606 -0
  699. package/src/buffer-surprise-trigger.test.ts +766 -0
  700. package/src/buffer-surprise.test.ts +339 -0
  701. package/src/buffer-surprise.ts +203 -0
  702. package/src/buffer.ts +900 -0
  703. package/src/bulk-import/cli-command.test.ts +204 -0
  704. package/src/bulk-import/index.ts +34 -0
  705. package/src/bulk-import/pipeline.test.ts +445 -0
  706. package/src/bulk-import/pipeline.ts +178 -0
  707. package/src/bulk-import/registry.test.ts +151 -0
  708. package/src/bulk-import/registry.ts +72 -0
  709. package/src/bulk-import/types.test.ts +272 -0
  710. package/src/bulk-import/types.ts +145 -0
  711. package/src/calibration.ts +394 -0
  712. package/src/capsule-cli.test.ts +398 -0
  713. package/src/capsule-cli.ts +565 -0
  714. package/src/causal-behavior.ts +308 -0
  715. package/src/causal-chain.ts +419 -0
  716. package/src/causal-consolidation.ts +370 -0
  717. package/src/causal-retrieval.ts +286 -0
  718. package/src/causal-trajectory-graph.ts +60 -0
  719. package/src/causal-trajectory.ts +303 -0
  720. package/src/chunking.ts +220 -0
  721. package/src/citations.ts +232 -0
  722. package/src/cli.ts +9403 -0
  723. package/src/codex-cli-fallback.ts +162 -0
  724. package/src/codex-thread-key.ts +1 -0
  725. package/src/coding/access-coding-context.test.ts +197 -0
  726. package/src/coding/coding-branch-scope.test.ts +281 -0
  727. package/src/coding/coding-namespace.test.ts +360 -0
  728. package/src/coding/coding-namespace.ts +412 -0
  729. package/src/coding/coding-orchestrator.test.ts +249 -0
  730. package/src/coding/git-context.test.ts +507 -0
  731. package/src/coding/git-context.ts +336 -0
  732. package/src/coding/mcp-set-coding-context.test.ts +174 -0
  733. package/src/coding/review-context.test.ts +316 -0
  734. package/src/coding/review-context.ts +349 -0
  735. package/src/coding/wire-coding-context.test.ts +468 -0
  736. package/src/commitment-ledger.test.ts +78 -0
  737. package/src/commitment-ledger.ts +337 -0
  738. package/src/compat/checks.test.ts +206 -0
  739. package/src/compat/checks.ts +716 -0
  740. package/src/compat/types.ts +33 -0
  741. package/src/compounding/engine.ts +1686 -0
  742. package/src/compounding/preference-consolidator.ts +778 -0
  743. package/src/compression-optimizer.ts +312 -0
  744. package/src/config.test.ts +930 -0
  745. package/src/config.ts +3807 -0
  746. package/src/connectors/codex/instructions.md +160 -0
  747. package/src/connectors/codex/resources/namespace-cheatsheet.md +48 -0
  748. package/src/connectors/codex-marketplace.ts +500 -0
  749. package/src/connectors/codex-materialize-runner.ts +212 -0
  750. package/src/connectors/codex-materialize.ts +983 -0
  751. package/src/connectors/coerce.ts +62 -0
  752. package/src/connectors/index.test.ts +1570 -0
  753. package/src/connectors/index.ts +3222 -0
  754. package/src/connectors/live/framework.ts +164 -0
  755. package/src/connectors/live/github.test.ts +1218 -0
  756. package/src/connectors/live/github.ts +1068 -0
  757. package/src/connectors/live/gmail.test.ts +1706 -0
  758. package/src/connectors/live/gmail.ts +1293 -0
  759. package/src/connectors/live/google-drive.test.ts +696 -0
  760. package/src/connectors/live/google-drive.ts +724 -0
  761. package/src/connectors/live/index.ts +101 -0
  762. package/src/connectors/live/live-connectors.test.ts +689 -0
  763. package/src/connectors/live/notion.test.ts +1109 -0
  764. package/src/connectors/live/notion.ts +978 -0
  765. package/src/connectors/live/registry.ts +103 -0
  766. package/src/connectors/live/state-store.ts +399 -0
  767. package/src/connectors/live/transient-errors.ts +150 -0
  768. package/src/connectors/weclone-installer.test.ts +850 -0
  769. package/src/connectors-cli.ts +513 -0
  770. package/src/console/state.test.ts +224 -0
  771. package/src/console/state.ts +514 -0
  772. package/src/console/trace.test.ts +813 -0
  773. package/src/console/trace.ts +603 -0
  774. package/src/console/tui.test.ts +582 -0
  775. package/src/console/tui.ts +508 -0
  776. package/src/consolidation-operator.ts +182 -0
  777. package/src/consolidation-provenance-check.ts +551 -0
  778. package/src/consolidation-undo.ts +718 -0
  779. package/src/contradiction/contradiction-judge.test.ts +189 -0
  780. package/src/contradiction/contradiction-judge.ts +333 -0
  781. package/src/contradiction/contradiction-review.ts +574 -0
  782. package/src/contradiction/contradiction-scan.ts +504 -0
  783. package/src/contradiction/contradiction.test.ts +2230 -0
  784. package/src/contradiction/index.ts +37 -0
  785. package/src/contradiction/resolution.ts +383 -0
  786. package/src/conversation-index/backend.ts +323 -0
  787. package/src/conversation-index/chunker.ts +47 -0
  788. package/src/conversation-index/cleanup.ts +53 -0
  789. package/src/conversation-index/faiss-adapter.ts +384 -0
  790. package/src/conversation-index/indexer.test.ts +164 -0
  791. package/src/conversation-index/indexer.ts +192 -0
  792. package/src/conversation-index/search.ts +37 -0
  793. package/src/cross-namespace-budget.test.ts +275 -0
  794. package/src/cross-namespace-budget.ts +365 -0
  795. package/src/cue-anchors.ts +163 -0
  796. package/src/curation/index.ts +544 -0
  797. package/src/dashboard-runtime.ts +337 -0
  798. package/src/day-summary.ts +122 -0
  799. package/src/dedup/index.ts +330 -0
  800. package/src/dedup/semantic.test.ts +1577 -0
  801. package/src/dedup/semantic.ts +148 -0
  802. package/src/delinearize.ts +193 -0
  803. package/src/direct-answer-wiring.test.ts +473 -0
  804. package/src/direct-answer-wiring.ts +180 -0
  805. package/src/direct-answer.test.ts +484 -0
  806. package/src/direct-answer.ts +273 -0
  807. package/src/embedding-fallback.ts +565 -0
  808. package/src/enrichment/audit.ts +89 -0
  809. package/src/enrichment/index.ts +27 -0
  810. package/src/enrichment/pipeline.ts +197 -0
  811. package/src/enrichment/provider-registry.ts +85 -0
  812. package/src/enrichment/types.ts +100 -0
  813. package/src/enrichment/web-search-provider.ts +63 -0
  814. package/src/entity-retrieval.ts +774 -0
  815. package/src/entity-schema.ts +239 -0
  816. package/src/evals.ts +1312 -0
  817. package/src/event-order-recall.test.ts +4164 -0
  818. package/src/event-order-recall.ts +2802 -0
  819. package/src/evidence-pack.test.ts +89 -0
  820. package/src/evidence-pack.ts +388 -0
  821. package/src/explicit-capture.ts +530 -0
  822. package/src/explicit-cue-recall.test.ts +3019 -0
  823. package/src/explicit-cue-recall.ts +5545 -0
  824. package/src/extraction-judge-telemetry.ts +234 -0
  825. package/src/extraction-judge-training.ts +221 -0
  826. package/src/extraction-judge.ts +846 -0
  827. package/src/extraction-timeout.test.ts +265 -0
  828. package/src/extraction.ts +2719 -0
  829. package/src/fallback-llm.test.ts +1060 -0
  830. package/src/fallback-llm.ts +918 -0
  831. package/src/focused-list-recall.test.ts +734 -0
  832. package/src/focused-list-recall.ts +1160 -0
  833. package/src/graph-dashboard-diff.ts +35 -0
  834. package/src/graph-dashboard-key.ts +5 -0
  835. package/src/graph-dashboard-parser.ts +104 -0
  836. package/src/graph-edge-reinforcement.ts +192 -0
  837. package/src/graph-events.ts +151 -0
  838. package/src/graph-recall.test.ts +164 -0
  839. package/src/graph-recall.ts +189 -0
  840. package/src/graph-retrieval.test.ts +809 -0
  841. package/src/graph-retrieval.ts +823 -0
  842. package/src/graph-snapshot.ts +329 -0
  843. package/src/graph.ts +813 -0
  844. package/src/harmonic-retrieval.ts +223 -0
  845. package/src/himem.ts +154 -0
  846. package/src/hygiene.ts +87 -0
  847. package/src/identity-continuity.ts +333 -0
  848. package/src/importance.ts +328 -0
  849. package/src/importers/base.test.ts +294 -0
  850. package/src/importers/base.ts +436 -0
  851. package/src/importers/index.ts +21 -0
  852. package/src/index.ts +1204 -0
  853. package/src/intent.ts +154 -0
  854. package/src/json-extract.ts +85 -0
  855. package/src/json-store.ts +42 -0
  856. package/src/lcm/archive.ts +617 -0
  857. package/src/lcm/dag.ts +199 -0
  858. package/src/lcm/engine.ts +645 -0
  859. package/src/lcm/index.ts +7 -0
  860. package/src/lcm/queue.test.ts +178 -0
  861. package/src/lcm/queue.ts +200 -0
  862. package/src/lcm/recall.ts +117 -0
  863. package/src/lcm/schema.ts +154 -0
  864. package/src/lcm/summarizer.ts +235 -0
  865. package/src/lcm/tools.ts +191 -0
  866. package/src/lcm-engine.test.ts +660 -0
  867. package/src/legacy-hook-compat.test.ts +20 -0
  868. package/src/legacy-hook-compat.ts +45 -0
  869. package/src/lifecycle.ts +289 -0
  870. package/src/live-connectors-runner.ts +385 -0
  871. package/src/local-llm-qos.test.ts +303 -0
  872. package/src/local-llm-thinking.test.ts +292 -0
  873. package/src/local-llm.ts +1464 -0
  874. package/src/logger.ts +49 -0
  875. package/src/maintenance/archive-observations.ts +147 -0
  876. package/src/maintenance/backup-stamp.ts +3 -0
  877. package/src/maintenance/dreams-ledger.ts +516 -0
  878. package/src/maintenance/first-start-migration.ts +362 -0
  879. package/src/maintenance/forget.test.ts +206 -0
  880. package/src/maintenance/forget.ts +126 -0
  881. package/src/maintenance/graph-edge-decay.test.ts +409 -0
  882. package/src/maintenance/graph-edge-decay.ts +394 -0
  883. package/src/maintenance/memory-governance-cron.ts +447 -0
  884. package/src/maintenance/memory-governance.ts +1039 -0
  885. package/src/maintenance/migrate-observations.ts +216 -0
  886. package/src/maintenance/observation-ledger-utils.ts +54 -0
  887. package/src/maintenance/pattern-reinforcement.test.ts +875 -0
  888. package/src/maintenance/pattern-reinforcement.ts +369 -0
  889. package/src/maintenance/purge.ts +334 -0
  890. package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +78 -0
  891. package/src/maintenance/rebuild-memory-projection.ts +1234 -0
  892. package/src/maintenance/rebuild-observations.ts +178 -0
  893. package/src/maintenance/tier-stats.test.ts +378 -0
  894. package/src/maintenance/tier-stats.ts +222 -0
  895. package/src/mcp-memory-inspector-app.ts +421 -0
  896. package/src/memory-action-policy.ts +80 -0
  897. package/src/memory-cache.ts +208 -0
  898. package/src/memory-extension/claude-code-publisher.ts +51 -0
  899. package/src/memory-extension/codex-publisher.ts +149 -0
  900. package/src/memory-extension/hermes-publisher.ts +51 -0
  901. package/src/memory-extension/index.ts +100 -0
  902. package/src/memory-extension/shared-instructions.ts +133 -0
  903. package/src/memory-extension/types.ts +86 -0
  904. package/src/memory-extension-host/host-discovery.ts +276 -0
  905. package/src/memory-extension-host/index.ts +14 -0
  906. package/src/memory-extension-host/render-extensions-block.ts +73 -0
  907. package/src/memory-extension-host/types.ts +21 -0
  908. package/src/memory-lifecycle-ledger-utils.ts +116 -0
  909. package/src/memory-projection-format.ts +11 -0
  910. package/src/memory-projection-store.ts +951 -0
  911. package/src/memory-provenance.test.ts +196 -0
  912. package/src/memory-provenance.ts +484 -0
  913. package/src/memory-worth-bench.test.ts +71 -0
  914. package/src/memory-worth-bench.ts +265 -0
  915. package/src/memory-worth-filter.test.ts +209 -0
  916. package/src/memory-worth-filter.ts +204 -0
  917. package/src/memory-worth-frontmatter.test.ts +311 -0
  918. package/src/memory-worth-outcomes.test.ts +316 -0
  919. package/src/memory-worth-outcomes.ts +286 -0
  920. package/src/memory-worth.test.ts +317 -0
  921. package/src/memory-worth.ts +215 -0
  922. package/src/message-parts/index.ts +806 -0
  923. package/src/message-parts/message-parts.test.ts +421 -0
  924. package/src/migrate/from-engram.ts +789 -0
  925. package/src/model-registry.ts +313 -0
  926. package/src/models-json.ts +76 -0
  927. package/src/namespaces/migrate.ts +187 -0
  928. package/src/namespaces/path.ts +25 -0
  929. package/src/namespaces/principal.test.ts +195 -0
  930. package/src/namespaces/principal.ts +86 -0
  931. package/src/namespaces/search.test.ts +105 -0
  932. package/src/namespaces/search.ts +233 -0
  933. package/src/namespaces/storage.ts +74 -0
  934. package/src/native-knowledge.ts +1823 -0
  935. package/src/negative.ts +72 -0
  936. package/src/network/tailscale.ts +179 -0
  937. package/src/network/webdav.ts +385 -0
  938. package/src/objective-state-writers.ts +951 -0
  939. package/src/objective-state.ts +320 -0
  940. package/src/onboarding/index.ts +529 -0
  941. package/src/openai-chat-compat.ts +56 -0
  942. package/src/operator-toolkit.ts +2132 -0
  943. package/src/opik-exporter.test.ts +72 -0
  944. package/src/opik-exporter.ts +587 -0
  945. package/src/orchestrator-extraction-queue.test.ts +197 -0
  946. package/src/orchestrator-flush.test.ts +1171 -0
  947. package/src/orchestrator-pattern-reinforcement.test.ts +128 -0
  948. package/src/orchestrator-source-attribution.test.ts +701 -0
  949. package/src/orchestrator.ts +16368 -0
  950. package/src/page-versioning.ts +450 -0
  951. package/src/patterns-cli.ts +574 -0
  952. package/src/peers/index.ts +54 -0
  953. package/src/peers/migrate-from-identity-anchor.test.ts +291 -0
  954. package/src/peers/migrate-from-identity-anchor.ts +350 -0
  955. package/src/peers/peers.test.ts +419 -0
  956. package/src/peers/profile-reasoner.ts +694 -0
  957. package/src/peers/storage.ts +1350 -0
  958. package/src/peers/types.ts +138 -0
  959. package/src/plugin-id.ts +84 -0
  960. package/src/policy-runtime.ts +209 -0
  961. package/src/procedural/procedure-miner.ts +150 -0
  962. package/src/procedural/procedure-recall.ts +93 -0
  963. package/src/procedural/procedure-stats.ts +213 -0
  964. package/src/procedural/procedure-types.ts +132 -0
  965. package/src/procedural/reinforcement-core.test.ts +132 -0
  966. package/src/procedural/reinforcement-core.ts +73 -0
  967. package/src/profiling.test.ts +263 -0
  968. package/src/profiling.ts +435 -0
  969. package/src/projection/index.ts +398 -0
  970. package/src/qmd-recall-cache.test.ts +138 -0
  971. package/src/qmd-recall-cache.ts +111 -0
  972. package/src/qmd.test.ts +258 -0
  973. package/src/qmd.ts +2614 -0
  974. package/src/reasoning-trace-recall.ts +201 -0
  975. package/src/reasoning-trace-types.ts +235 -0
  976. package/src/recall-audit-anomaly.test.ts +246 -0
  977. package/src/recall-audit-anomaly.ts +297 -0
  978. package/src/recall-audit.test.ts +51 -0
  979. package/src/recall-audit.ts +72 -0
  980. package/src/recall-budget-config.test.ts +87 -0
  981. package/src/recall-disclosure-escalation.test.ts +196 -0
  982. package/src/recall-disclosure-escalation.ts +158 -0
  983. package/src/recall-disclosure-shaping.test.ts +146 -0
  984. package/src/recall-disclosure.test.ts +214 -0
  985. package/src/recall-explain-renderer.test.ts +140 -0
  986. package/src/recall-explain-renderer.ts +356 -0
  987. package/src/recall-mmr.test.ts +808 -0
  988. package/src/recall-mmr.ts +607 -0
  989. package/src/recall-qos.test.ts +85 -0
  990. package/src/recall-qos.ts +82 -0
  991. package/src/recall-query-policy.ts +221 -0
  992. package/src/recall-state.test.ts +233 -0
  993. package/src/recall-state.ts +456 -0
  994. package/src/recall-tag-filter.ts +143 -0
  995. package/src/recall-tokenization.ts +35 -0
  996. package/src/recall-xray-cli.test.ts +118 -0
  997. package/src/recall-xray-cli.ts +100 -0
  998. package/src/recall-xray-disclosure-telemetry.test.ts +183 -0
  999. package/src/recall-xray-renderer.test.ts +539 -0
  1000. package/src/recall-xray-renderer.ts +487 -0
  1001. package/src/recall-xray.test.ts +503 -0
  1002. package/src/recall-xray.ts +621 -0
  1003. package/src/reconstruct.ts +41 -0
  1004. package/src/release-changelog.ts +35 -0
  1005. package/src/relevance.ts +67 -0
  1006. package/src/replay/normalizers/chatgpt.ts +133 -0
  1007. package/src/replay/normalizers/claude.ts +102 -0
  1008. package/src/replay/normalizers/openclaw.ts +119 -0
  1009. package/src/replay/normalizers/shared.ts +69 -0
  1010. package/src/replay/runner.ts +197 -0
  1011. package/src/replay/types.ts +143 -0
  1012. package/src/rerank.test.ts +48 -0
  1013. package/src/rerank.ts +176 -0
  1014. package/src/resolve-auth-token.test.ts +226 -0
  1015. package/src/resolve-auth-token.ts +151 -0
  1016. package/src/resolve-provider-secret.test.ts +187 -0
  1017. package/src/resolve-provider-secret.ts +410 -0
  1018. package/src/response-guidance-recall.test.ts +3952 -0
  1019. package/src/response-guidance-recall.ts +4431 -0
  1020. package/src/resume-bundles.ts +415 -0
  1021. package/src/retrieval-agents.ts +623 -0
  1022. package/src/retrieval-tiers.ts +25 -0
  1023. package/src/retrieval.ts +104 -0
  1024. package/src/review/index.test.ts +201 -0
  1025. package/src/review/index.ts +536 -0
  1026. package/src/routing/engine.ts +162 -0
  1027. package/src/routing/store.ts +321 -0
  1028. package/src/runtime/better-sqlite.test.ts +32 -0
  1029. package/src/runtime/better-sqlite.ts +76 -0
  1030. package/src/runtime/child-process.ts +67 -0
  1031. package/src/runtime/env.ts +48 -0
  1032. package/src/sanitize.ts +58 -0
  1033. package/src/schemas.ts +449 -0
  1034. package/src/sdk-compat.ts +87 -0
  1035. package/src/search/document-scanner.ts +96 -0
  1036. package/src/search/embed-helper.ts +142 -0
  1037. package/src/search/factory.ts +189 -0
  1038. package/src/search/index.ts +10 -0
  1039. package/src/search/lancedb-backend.ts +342 -0
  1040. package/src/search/meilisearch-backend.ts +232 -0
  1041. package/src/search/noop-backend.ts +57 -0
  1042. package/src/search/orama-backend.ts +358 -0
  1043. package/src/search/port.ts +86 -0
  1044. package/src/search/remote-backend.ts +124 -0
  1045. package/src/secure-store/cipher.ts +271 -0
  1046. package/src/secure-store/cli-handlers.ts +355 -0
  1047. package/src/secure-store/cli-renderer.ts +131 -0
  1048. package/src/secure-store/header.ts +373 -0
  1049. package/src/secure-store/index.ts +137 -0
  1050. package/src/secure-store/kdf.ts +263 -0
  1051. package/src/secure-store/keyring.ts +106 -0
  1052. package/src/secure-store/metadata.ts +394 -0
  1053. package/src/secure-store/passphrase-reader.ts +252 -0
  1054. package/src/secure-store/secure-fs.ts +571 -0
  1055. package/src/secure-store/secure-store.test.ts +755 -0
  1056. package/src/semantic-chunking.ts +545 -0
  1057. package/src/semantic-consolidation.test.ts +182 -0
  1058. package/src/semantic-consolidation.ts +432 -0
  1059. package/src/semantic-rule-promotion.ts +183 -0
  1060. package/src/semantic-rule-verifier.ts +160 -0
  1061. package/src/session-integrity.ts +569 -0
  1062. package/src/session-observer-bands.ts +11 -0
  1063. package/src/session-observer-state.ts +346 -0
  1064. package/src/session-toggles.test.ts +96 -0
  1065. package/src/session-toggles.ts +159 -0
  1066. package/src/shared-context/manager.ts +810 -0
  1067. package/src/signal.ts +84 -0
  1068. package/src/skills-registry.test.ts +277 -0
  1069. package/src/skills-registry.ts +120 -0
  1070. package/src/source-attribution-roundtrip.test.ts +215 -0
  1071. package/src/source-attribution.test.ts +1425 -0
  1072. package/src/source-attribution.ts +639 -0
  1073. package/src/spaces/index.ts +627 -0
  1074. package/src/storage-paths.ts +117 -0
  1075. package/src/storage.ts +6657 -0
  1076. package/src/store-contract.ts +55 -0
  1077. package/src/summarizer.ts +844 -0
  1078. package/src/summary-snapshot.test.ts +681 -0
  1079. package/src/summary-snapshot.ts +238 -0
  1080. package/src/surfaces/dreams.test.ts +394 -0
  1081. package/src/surfaces/dreams.ts +346 -0
  1082. package/src/surfaces/heartbeat.test.ts +415 -0
  1083. package/src/surfaces/heartbeat.ts +325 -0
  1084. package/src/sync/index.ts +308 -0
  1085. package/src/targeted-fact-recall.test.ts +1694 -0
  1086. package/src/targeted-fact-recall.ts +2905 -0
  1087. package/src/taxonomy/default-taxonomy.ts +87 -0
  1088. package/src/taxonomy/index.ts +26 -0
  1089. package/src/taxonomy/resolver-doc-generator.ts +57 -0
  1090. package/src/taxonomy/resolver.ts +184 -0
  1091. package/src/taxonomy/taxonomy-loader.ts +186 -0
  1092. package/src/taxonomy/types.ts +48 -0
  1093. package/src/telemetry-transcript.ts +70 -0
  1094. package/src/temporal-index.ts +890 -0
  1095. package/src/temporal-supersession.test.ts +2703 -0
  1096. package/src/temporal-supersession.ts +493 -0
  1097. package/src/temporal-validity.test.ts +448 -0
  1098. package/src/temporal-validity.ts +123 -0
  1099. package/src/threading.ts +395 -0
  1100. package/src/tier-migration.ts +124 -0
  1101. package/src/tier-routing.ts +102 -0
  1102. package/src/tmt.ts +462 -0
  1103. package/src/tokens.test.ts +178 -0
  1104. package/src/tokens.ts +279 -0
  1105. package/src/topics.ts +147 -0
  1106. package/src/training-export/cli-date-validation.test.ts +258 -0
  1107. package/src/training-export/converter.test.ts +452 -0
  1108. package/src/training-export/converter.ts +319 -0
  1109. package/src/training-export/date-parse.ts +117 -0
  1110. package/src/training-export/index.ts +26 -0
  1111. package/src/training-export/registry.test.ts +85 -0
  1112. package/src/training-export/registry.ts +57 -0
  1113. package/src/training-export/types.ts +31 -0
  1114. package/src/transcript.ts +1179 -0
  1115. package/src/transfer/autodetect.ts +30 -0
  1116. package/src/transfer/backup.ts +138 -0
  1117. package/src/transfer/capsule-crypto.ts +485 -0
  1118. package/src/transfer/capsule-encrypt.test.ts +690 -0
  1119. package/src/transfer/capsule-export.ts +543 -0
  1120. package/src/transfer/capsule-fork.ts +375 -0
  1121. package/src/transfer/capsule-import.ts +564 -0
  1122. package/src/transfer/capsule-merge.ts +433 -0
  1123. package/src/transfer/conflict-policy.ts +16 -0
  1124. package/src/transfer/constants.ts +13 -0
  1125. package/src/transfer/exclusions.ts +37 -0
  1126. package/src/transfer/export-json.ts +65 -0
  1127. package/src/transfer/export-md.ts +59 -0
  1128. package/src/transfer/export-sqlite.ts +52 -0
  1129. package/src/transfer/fs-utils.ts +269 -0
  1130. package/src/transfer/import-json.ts +108 -0
  1131. package/src/transfer/import-md.ts +84 -0
  1132. package/src/transfer/import-sqlite.ts +100 -0
  1133. package/src/transfer/integrity.ts +71 -0
  1134. package/src/transfer/sqlite-schema.ts +16 -0
  1135. package/src/transfer/types.ts +297 -0
  1136. package/src/trust-zones.ts +1186 -0
  1137. package/src/types.ts +3074 -0
  1138. package/src/user-model.test.ts +124 -0
  1139. package/src/user-model.ts +162 -0
  1140. package/src/utility-learner.ts +353 -0
  1141. package/src/utility-runtime.ts +88 -0
  1142. package/src/utility-telemetry.ts +215 -0
  1143. package/src/utils/category-dir.ts +44 -0
  1144. package/src/utils/errno.ts +6 -0
  1145. package/src/utils/iso-timestamp.test.ts +37 -0
  1146. package/src/utils/iso-timestamp.ts +164 -0
  1147. package/src/utils/path.ts +26 -0
  1148. package/src/verified-recall.ts +138 -0
  1149. package/src/version-utils.test.ts +10 -0
  1150. package/src/version-utils.ts +9 -0
  1151. package/src/whitespace.ts +10 -0
  1152. package/src/work/board.ts +359 -0
  1153. package/src/work/boundary.ts +107 -0
  1154. package/src/work/storage.ts +436 -0
  1155. package/src/work/types.ts +82 -0
  1156. package/src/work-product-ledger.ts +265 -0
  1157. package/dist/access-service-DDjzFALq.d.ts +0 -2088
  1158. package/dist/capsule-crypto-SJS5VVAP.js +0 -18
  1159. package/dist/capsule-export-7QNCBZOQ.js +0 -17
  1160. package/dist/capsule-import-EPBHD2EN.js +0 -16
  1161. package/dist/capsule-merge-DI7PNQ2H.js +0 -189
  1162. package/dist/chunk-23ZZK64Y.js +0 -26
  1163. package/dist/chunk-23ZZK64Y.js.map +0 -1
  1164. package/dist/chunk-242S3I2A.js +0 -647
  1165. package/dist/chunk-2LGMW3DJ.js +0 -111
  1166. package/dist/chunk-3B6KIRBH.js +0 -5213
  1167. package/dist/chunk-3B6KIRBH.js.map +0 -1
  1168. package/dist/chunk-457A4P3L.js +0 -119
  1169. package/dist/chunk-457A4P3L.js.map +0 -1
  1170. package/dist/chunk-4IS4SXIQ.js +0 -2040
  1171. package/dist/chunk-4YM32CRU.js +0 -721
  1172. package/dist/chunk-6TBWYBJ3.js +0 -236
  1173. package/dist/chunk-74EMIVE4.js +0 -329
  1174. package/dist/chunk-74EMIVE4.js.map +0 -1
  1175. package/dist/chunk-767ODGE6.js +0 -183
  1176. package/dist/chunk-7V22HTMD.js +0 -623
  1177. package/dist/chunk-7V22HTMD.js.map +0 -1
  1178. package/dist/chunk-7ZM3BFKK.js +0 -9705
  1179. package/dist/chunk-7ZM3BFKK.js.map +0 -1
  1180. package/dist/chunk-AQJNPMOA.js +0 -643
  1181. package/dist/chunk-AQJNPMOA.js.map +0 -1
  1182. package/dist/chunk-ASAITVLA.js +0 -64
  1183. package/dist/chunk-ASAITVLA.js.map +0 -1
  1184. package/dist/chunk-BBE34QBJ.js +0 -275
  1185. package/dist/chunk-BBE34QBJ.js.map +0 -1
  1186. package/dist/chunk-BZSQEPRW.js +0 -14710
  1187. package/dist/chunk-BZSQEPRW.js.map +0 -1
  1188. package/dist/chunk-CPKTBRS2.js +0 -891
  1189. package/dist/chunk-CPKTBRS2.js.map +0 -1
  1190. package/dist/chunk-D4GAOFF6.js +0 -562
  1191. package/dist/chunk-D4GAOFF6.js.map +0 -1
  1192. package/dist/chunk-D54LZC5L.js +0 -147
  1193. package/dist/chunk-DF3RVK3X.js +0 -119
  1194. package/dist/chunk-DF3RVK3X.js.map +0 -1
  1195. package/dist/chunk-DZZPC36E.js +0 -1451
  1196. package/dist/chunk-DZZPC36E.js.map +0 -1
  1197. package/dist/chunk-E2UCDP5S.js +0 -570
  1198. package/dist/chunk-E6K4NIEU.js +0 -747
  1199. package/dist/chunk-E6K4NIEU.js.map +0 -1
  1200. package/dist/chunk-EEQLFRUM.js +0 -89
  1201. package/dist/chunk-ETOW6ACV.js +0 -158
  1202. package/dist/chunk-ETOW6ACV.js.map +0 -1
  1203. package/dist/chunk-FMEBPEAO.js +0 -347
  1204. package/dist/chunk-FMEBPEAO.js.map +0 -1
  1205. package/dist/chunk-FQDPCE3I.js +0 -1837
  1206. package/dist/chunk-FQDPCE3I.js.map +0 -1
  1207. package/dist/chunk-FYIYMQ5N.js +0 -221
  1208. package/dist/chunk-FYIYMQ5N.js.map +0 -1
  1209. package/dist/chunk-G2WADRQ3.js +0 -219
  1210. package/dist/chunk-G4SK7DSQ.js +0 -121
  1211. package/dist/chunk-GVPWB7EY.js +0 -390
  1212. package/dist/chunk-GVPWB7EY.js.map +0 -1
  1213. package/dist/chunk-HELQZFZO.js +0 -1075
  1214. package/dist/chunk-HL5LRPNA.js +0 -1914
  1215. package/dist/chunk-HL5LRPNA.js.map +0 -1
  1216. package/dist/chunk-HQZVVSVB.js +0 -147
  1217. package/dist/chunk-HQZVVSVB.js.map +0 -1
  1218. package/dist/chunk-HY3L4WKC.js +0 -2195
  1219. package/dist/chunk-HY3L4WKC.js.map +0 -1
  1220. package/dist/chunk-IB3BFHGN.js +0 -228
  1221. package/dist/chunk-IXEJRKCZ.js +0 -18
  1222. package/dist/chunk-JBMSGZEQ.js +0 -441
  1223. package/dist/chunk-JBMSGZEQ.js.map +0 -1
  1224. package/dist/chunk-JESOB2HO.js +0 -108
  1225. package/dist/chunk-JKDVIE52.js +0 -272
  1226. package/dist/chunk-JRNQ3RNA.js +0 -284
  1227. package/dist/chunk-JRNQ3RNA.js.map +0 -1
  1228. package/dist/chunk-K6WK37A6.js +0 -865
  1229. package/dist/chunk-K6WK37A6.js.map +0 -1
  1230. package/dist/chunk-MARWOCVP.js +0 -48
  1231. package/dist/chunk-MNU6ZBWT.js +0 -4454
  1232. package/dist/chunk-MNU6ZBWT.js.map +0 -1
  1233. package/dist/chunk-N5AKDXAI.js +0 -74
  1234. package/dist/chunk-OA3L7BFR.js +0 -183
  1235. package/dist/chunk-OA3L7BFR.js.map +0 -1
  1236. package/dist/chunk-OR64ZGRZ.js +0 -23
  1237. package/dist/chunk-P77UEOU2.js +0 -1521
  1238. package/dist/chunk-P77UEOU2.js.map +0 -1
  1239. package/dist/chunk-PH4C2U43.js +0 -239
  1240. package/dist/chunk-PH4C2U43.js.map +0 -1
  1241. package/dist/chunk-RVPLBATS.js +0 -1586
  1242. package/dist/chunk-RVPLBATS.js.map +0 -1
  1243. package/dist/chunk-U5JMRGKX.js +0 -340
  1244. package/dist/chunk-U5JMRGKX.js.map +0 -1
  1245. package/dist/chunk-URB2WSKZ.js +0 -350
  1246. package/dist/chunk-URB2WSKZ.js.map +0 -1
  1247. package/dist/chunk-UVMUAWVT.js +0 -596
  1248. package/dist/chunk-WEJG4TB5.js +0 -118
  1249. package/dist/chunk-X7HPGUVG.js +0 -271
  1250. package/dist/chunk-XAMBKFQS.js +0 -2777
  1251. package/dist/chunk-XAMBKFQS.js.map +0 -1
  1252. package/dist/chunk-XJKFSSDW.js +0 -726
  1253. package/dist/chunk-XJKFSSDW.js.map +0 -1
  1254. package/dist/chunk-XMHBH5H6.js +0 -283
  1255. package/dist/chunk-XMHBH5H6.js.map +0 -1
  1256. package/dist/chunk-XMVFHBHT.js +0 -277
  1257. package/dist/chunk-Y3VMVTYX.js +0 -53
  1258. package/dist/chunk-YNB73F22.js +0 -137
  1259. package/dist/chunk-YNB73F22.js.map +0 -1
  1260. package/dist/chunk-Z2E7VW55.js +0 -335
  1261. package/dist/chunk-Z2E7VW55.js.map +0 -1
  1262. package/dist/chunk-ZG7PTKBK.js +0 -2296
  1263. package/dist/chunk-ZNQN6ZTA.js +0 -135
  1264. package/dist/chunk-ZVTKDVVM.js +0 -827
  1265. package/dist/chunk-ZVTKDVVM.js.map +0 -1
  1266. package/dist/cli-BR8KpIU0.d.ts +0 -1259
  1267. package/dist/codex-materialize-CQlLTzke.d.ts +0 -139
  1268. package/dist/connectors-cli-DFGtY2DB.d.ts +0 -257
  1269. package/dist/contradiction-review-5LTTVDQV.js +0 -22
  1270. package/dist/contradiction-scan-QTXAMBUA.js +0 -414
  1271. package/dist/contradiction-scan-QTXAMBUA.js.map +0 -1
  1272. package/dist/engine-35M5BKQ7.js +0 -28
  1273. package/dist/fs-utils-IRVUFB6G.js +0 -30
  1274. package/dist/graph-edge-decay-PWB63GRE.js +0 -207
  1275. package/dist/memory-governance-IMPQZXFC.js +0 -37
  1276. package/dist/memory-projection-store-CY8TU40w.d.ts +0 -222
  1277. package/dist/orchestrator-DDMPqU6R.d.ts +0 -1792
  1278. package/dist/path-RMTY5Y5A.js +0 -9
  1279. package/dist/port-B6VEDIkC.d.ts +0 -53
  1280. package/dist/resolution-YGIBORXI.js +0 -101
  1281. package/dist/resolution-YGIBORXI.js.map +0 -1
  1282. package/dist/secure-store-4R2GSO7S.js +0 -156
  1283. package/dist/semantic-consolidation-ByBXb-sf.d.ts +0 -180
  1284. package/dist/state-store-3EH7HYIN.js +0 -16
  1285. package/dist/types-V3FJ26TF.js +0 -30
  1286. /package/dist/{capsule-crypto-SJS5VVAP.js.map → adapters/claude-code.js.map} +0 -0
  1287. /package/dist/{capsule-export-7QNCBZOQ.js.map → adapters/codex.js.map} +0 -0
  1288. /package/dist/{capsule-import-EPBHD2EN.js.map → adapters/hermes.js.map} +0 -0
  1289. /package/dist/{contradiction-review-5LTTVDQV.js.map → adapters/index.js.map} +0 -0
  1290. /package/dist/{engine-35M5BKQ7.js.map → adapters/registry.js.map} +0 -0
  1291. /package/dist/{fs-utils-IRVUFB6G.js.map → adapters/replit.js.map} +0 -0
  1292. /package/dist/{memory-governance-IMPQZXFC.js.map → adapters/types.js.map} +0 -0
  1293. /package/dist/{path-RMTY5Y5A.js.map → capsule-crypto-5CYAGVC5.js.map} +0 -0
  1294. /package/dist/{capsule-merge-DI7PNQ2H.js.map → capsule-merge-4MGKE7C5.js.map} +0 -0
  1295. /package/dist/{chunk-G4SK7DSQ.js.map → chunk-2WWLHTZY.js.map} +0 -0
  1296. /package/dist/{chunk-X7HPGUVG.js.map → chunk-4CRG46BG.js.map} +0 -0
  1297. /package/dist/{chunk-UVMUAWVT.js.map → chunk-7IASACLB.js.map} +0 -0
  1298. /package/dist/{chunk-HELQZFZO.js.map → chunk-EDTHC6UD.js.map} +0 -0
  1299. /package/dist/{chunk-4YM32CRU.js.map → chunk-EFJ3MQ4V.js.map} +0 -0
  1300. /package/dist/{chunk-E2UCDP5S.js.map → chunk-FBYESMQ2.js.map} +0 -0
  1301. /package/dist/{chunk-D54LZC5L.js.map → chunk-FDU6HUUL.js.map} +0 -0
  1302. /package/dist/{chunk-IB3BFHGN.js.map → chunk-GGKRUQOO.js.map} +0 -0
  1303. /package/dist/{chunk-242S3I2A.js.map → chunk-GL6I6MEQ.js.map} +0 -0
  1304. /package/dist/{secure-store-4R2GSO7S.js.map → chunk-HHLLAQGZ.js.map} +0 -0
  1305. /package/dist/{chunk-4IS4SXIQ.js.map → chunk-HXXBL2KD.js.map} +0 -0
  1306. /package/dist/{chunk-767ODGE6.js.map → chunk-KNKUID7G.js.map} +0 -0
  1307. /package/dist/{chunk-6TBWYBJ3.js.map → chunk-LPMVBPA3.js.map} +0 -0
  1308. /package/dist/{chunk-WEJG4TB5.js.map → chunk-MC26UJIM.js.map} +0 -0
  1309. /package/dist/{chunk-JKDVIE52.js.map → chunk-MGKYQQYF.js.map} +0 -0
  1310. /package/dist/{chunk-Y3VMVTYX.js.map → chunk-MT4HVDUZ.js.map} +0 -0
  1311. /package/dist/{chunk-G2WADRQ3.js.map → chunk-MY6TPVXW.js.map} +0 -0
  1312. /package/dist/{chunk-OR64ZGRZ.js.map → chunk-NNVTUXEB.js.map} +0 -0
  1313. /package/dist/{chunk-JESOB2HO.js.map → chunk-P4NEIHUT.js.map} +0 -0
  1314. /package/dist/{chunk-IXEJRKCZ.js.map → chunk-QRNI5JBH.js.map} +0 -0
  1315. /package/dist/{chunk-EEQLFRUM.js.map → chunk-RRF5UOBJ.js.map} +0 -0
  1316. /package/dist/{state-store-3EH7HYIN.js.map → chunk-SEDEKFYQ.js.map} +0 -0
  1317. /package/dist/{chunk-2LGMW3DJ.js.map → chunk-U3PN77QT.js.map} +0 -0
  1318. /package/dist/{chunk-XMVFHBHT.js.map → chunk-U3WSW6PZ.js.map} +0 -0
  1319. /package/dist/{chunk-N5AKDXAI.js.map → chunk-UWVJF25J.js.map} +0 -0
  1320. /package/dist/{types-V3FJ26TF.js.map → chunk-V5OCT34X.js.map} +0 -0
  1321. /package/dist/{chunk-ZG7PTKBK.js.map → chunk-W3LR522O.js.map} +0 -0
  1322. /package/dist/{chunk-MARWOCVP.js.map → chunk-XIG5PDM7.js.map} +0 -0
  1323. /package/dist/{chunk-ZNQN6ZTA.js.map → chunk-XVZ7B3HG.js.map} +0 -0
  1324. /package/dist/{graph-edge-decay-PWB63GRE.js.map → graph-edge-decay-5DI5GUNL.js.map} +0 -0
@@ -0,0 +1,1899 @@
1
+ import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
2
+ import { randomUUID, timingSafeEqual } from "node:crypto";
3
+ import { AsyncLocalStorage } from "node:async_hooks";
4
+ import { existsSync } from "node:fs";
5
+ import { readFile } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { fileURLToPath, URL } from "node:url";
8
+ import { log } from "./logger.js";
9
+ import { EngramAccessInputError, type EngramAccessService } from "./access-service.js";
10
+ import { EngramMcpServer } from "./access-mcp.js";
11
+ import { validateRequest, type SchemaName, type SchemaTypeFor } from "./access-schema.js";
12
+ import type { RecallDisclosure, RecallPlanMode } from "./types.js";
13
+ import { isRecallDisclosure } from "./types.js";
14
+ import { isTrustZoneName, type TrustZoneName, type TrustZoneRecordKind, type TrustZoneSourceClass } from "./trust-zones.js";
15
+ import { AdapterRegistry, type ResolvedIdentity } from "./adapters/index.js";
16
+ import type { CitationEntry } from "./citations.js";
17
+ import {
18
+ subscribeGraphEvents,
19
+ type GraphEvent,
20
+ } from "./graph-events.js";
21
+ import { expandTildePath } from "./utils/path.js";
22
+
23
+ export interface EngramAccessHttpServerOptions {
24
+ service: EngramAccessService;
25
+ host?: string;
26
+ port?: number;
27
+ authToken?: string;
28
+ /** Additional valid tokens (for multi-connector auth). Checked alongside authToken. */
29
+ authTokens?: string[];
30
+ /** Dynamic token loader — called on each auth check so new/revoked tokens take effect without restart. */
31
+ authTokensGetter?: () => string[];
32
+ principal?: string;
33
+ maxBodyBytes?: number;
34
+ adminConsoleEnabled?: boolean;
35
+ adminConsolePublicDir?: string;
36
+ trustPrincipalHeader?: boolean;
37
+ /** Enable adapter-based identity resolution from request headers */
38
+ enableAdapters?: boolean;
39
+ /** Custom adapter registry (defaults to built-in adapters) */
40
+ adapterRegistry?: AdapterRegistry;
41
+ /** Enable oai-mem-citation blocks in recall responses (issue #379). */
42
+ citationsEnabled?: boolean;
43
+ /** Auto-enable citations for Codex adapter connections (issue #379). */
44
+ citationsAutoDetect?: boolean;
45
+ }
46
+
47
+ export interface EngramAccessHttpServerStatus {
48
+ running: boolean;
49
+ host: string;
50
+ port: number;
51
+ maxBodyBytes: number;
52
+ }
53
+
54
+ function resolveDefaultAdminConsolePublicDir(): string {
55
+ const thisDir = path.dirname(fileURLToPath(import.meta.url));
56
+ const candidates = [
57
+ // Standard: admin-console sibling to src/ (development layout)
58
+ path.resolve(thisDir, "../admin-console/public"),
59
+ // Bundled: admin-console inside dist/ alongside the bundle
60
+ path.resolve(thisDir, "./admin-console/public"),
61
+ // Package root: walk up from dist/ to the package root
62
+ path.resolve(thisDir, "../../admin-console/public"),
63
+ ];
64
+ return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0];
65
+ }
66
+
67
+ const defaultAdminConsolePublicDir = resolveDefaultAdminConsolePublicDir();
68
+ const correlationIdStore = new AsyncLocalStorage<string>();
69
+
70
+ const WRITE_RATE_LIMIT_WINDOW_MS = 60_000;
71
+ const WRITE_RATE_LIMIT_MAX_REQUESTS = 30;
72
+ const TRUST_ZONE_RECORD_KINDS = ["memory", "artifact", "state", "trajectory", "external"] as const;
73
+ const TRUST_ZONE_SOURCE_CLASSES = ["tool_output", "web_content", "subagent_trace", "system_memory", "user_input", "manual"] as const;
74
+
75
+ class HttpError extends Error {
76
+ readonly code: string;
77
+ readonly details?: unknown;
78
+ constructor(readonly status: number, message: string, code?: string, details?: unknown) {
79
+ super(message);
80
+ this.code = code ?? `http_${status}`;
81
+ this.details = details;
82
+ }
83
+ }
84
+
85
+ function hostToUrlAuthority(host: string): string {
86
+ if (host.includes(":") && !host.startsWith("[") && !host.endsWith("]")) {
87
+ return `[${host}]`;
88
+ }
89
+ return host;
90
+ }
91
+
92
+ function parseHttpServerPort(port: number | undefined): number {
93
+ if (port === undefined) return 0;
94
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
95
+ throw new Error("access HTTP port must be an integer from 0 to 65535");
96
+ }
97
+ return port;
98
+ }
99
+
100
+ function parseTrustZoneKindFilter(raw: string | null): TrustZoneRecordKind | undefined {
101
+ if (raw === null) return undefined;
102
+ if ((TRUST_ZONE_RECORD_KINDS as readonly string[]).includes(raw)) {
103
+ return raw as TrustZoneRecordKind;
104
+ }
105
+ throw new HttpError(400, `kind must be one of ${TRUST_ZONE_RECORD_KINDS.join("|")}`, "invalid_kind_filter");
106
+ }
107
+
108
+ function parseTrustZoneSourceClassFilter(raw: string | null): TrustZoneSourceClass | undefined {
109
+ if (raw === null) return undefined;
110
+ if ((TRUST_ZONE_SOURCE_CLASSES as readonly string[]).includes(raw)) {
111
+ return raw as TrustZoneSourceClass;
112
+ }
113
+ throw new HttpError(400, `sourceClass must be one of ${TRUST_ZONE_SOURCE_CLASSES.join("|")}`, "invalid_source_class_filter");
114
+ }
115
+
116
+ function parseTrustZoneFilter(raw: string | null): TrustZoneName | undefined {
117
+ if (raw === null) return undefined;
118
+ if (isTrustZoneName(raw)) {
119
+ return raw;
120
+ }
121
+ throw new HttpError(400, "zone must be one of quarantine|working|trusted", "invalid_zone_filter");
122
+ }
123
+
124
+ /**
125
+ * Decode a `:peerId` URL path segment, converting malformed percent-encoded
126
+ * input (e.g., `%E0%A4%A`) into a 400 client error rather than letting
127
+ * `URIError` bubble up as a 500 `internal_error`.
128
+ */
129
+ function decodePeerIdSegment(raw: string): string {
130
+ try {
131
+ return decodeURIComponent(raw);
132
+ } catch {
133
+ throw new EngramAccessInputError("peerId path segment is not valid percent-encoded input");
134
+ }
135
+ }
136
+
137
+ export class EngramAccessHttpServer {
138
+ private readonly service: EngramAccessService;
139
+ private readonly host: string;
140
+ private readonly requestedPort: number;
141
+ private readonly authToken?: string;
142
+ private readonly authTokens: string[];
143
+ private readonly authTokensGetter?: () => string[];
144
+ private readonly authenticatedPrincipal?: string;
145
+ private readonly maxBodyBytes: number;
146
+ private readonly adminConsoleEnabled: boolean;
147
+ private readonly adminConsolePublicDir: string;
148
+ private readonly trustPrincipalHeader: boolean;
149
+ private readonly adapterRegistry: AdapterRegistry | null;
150
+ private readonly writeRequestTimestamps: number[] = [];
151
+ private readonly mcpServer: EngramMcpServer;
152
+ private server: Server | null = null;
153
+ private boundPort = 0;
154
+ /** Active SSE response objects for /engram/v1/graph/events. */
155
+ private readonly sseClients = new Set<ServerResponse>();
156
+ /** Throttle batch: pending SSE event batches per client. */
157
+ private readonly sseBatchTimers = new Map<ServerResponse, ReturnType<typeof setTimeout>>();
158
+ private readonly ssePendingBatches = new Map<ServerResponse, GraphEvent[]>();
159
+ /**
160
+ * Per-client cleanup callbacks: clear heartbeat interval, flush timer,
161
+ * unsubscribe from bus, and end the response. Stored here so `stop()`
162
+ * can invoke them even when the client hasn't disconnected yet
163
+ * (Cursor review thread `access-http.ts:232`).
164
+ */
165
+ private readonly sseCleanupFns = new Set<() => void>();
166
+
167
+ constructor(options: EngramAccessHttpServerOptions) {
168
+ this.service = options.service;
169
+ this.host = options.host?.trim() || "127.0.0.1";
170
+ this.requestedPort = parseHttpServerPort(options.port);
171
+ this.authToken = options.authToken?.trim() || undefined;
172
+ this.authTokens = (options.authTokens ?? []).map((t) => t.trim()).filter(Boolean);
173
+ this.authTokensGetter = options.authTokensGetter;
174
+ this.authenticatedPrincipal = options.principal?.trim() || undefined;
175
+ this.maxBodyBytes = Number.isFinite(options.maxBodyBytes)
176
+ ? Math.max(1, Math.floor(options.maxBodyBytes ?? 131072))
177
+ : 131072;
178
+ this.adminConsoleEnabled = options.adminConsoleEnabled !== false;
179
+ this.adminConsolePublicDir = options.adminConsolePublicDir ?? defaultAdminConsolePublicDir;
180
+ this.trustPrincipalHeader = options.trustPrincipalHeader === true;
181
+ this.adapterRegistry = options.enableAdapters !== false
182
+ ? (options.adapterRegistry ?? new AdapterRegistry())
183
+ : null;
184
+ this.mcpServer = new EngramMcpServer(this.service, {
185
+ principal: options.principal,
186
+ citationsEnabled: options.citationsEnabled,
187
+ citationsAutoDetect: options.citationsAutoDetect,
188
+ });
189
+ }
190
+
191
+ async start(): Promise<EngramAccessHttpServerStatus> {
192
+ if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter) {
193
+ throw new Error("engram access HTTP requires authToken or authTokens");
194
+ }
195
+ if (this.server) return this.status();
196
+
197
+ const server = createServer((req, res) => {
198
+ const correlationId = randomUUID();
199
+ correlationIdStore.run(correlationId, () => {
200
+ void this.handle(req, res, correlationId).catch((err) => {
201
+ log.debug(`engram access HTTP request failed [${correlationId}]: ${err}`);
202
+ if (err instanceof HttpError) {
203
+ const payload: Record<string, unknown> = { error: err.message, code: err.code };
204
+ if (err.details) payload.details = err.details;
205
+ this.respondJson(res, err.status, payload);
206
+ return;
207
+ }
208
+ if (err instanceof EngramAccessInputError) {
209
+ this.respondJson(res, 400, { error: err.message, code: "input_error" });
210
+ return;
211
+ }
212
+ if (res.headersSent) {
213
+ res.destroy(err as Error);
214
+ return;
215
+ }
216
+ this.respondJson(res, 500, { error: "internal_error", code: "internal_error" });
217
+ });
218
+ });
219
+ });
220
+
221
+ try {
222
+ await new Promise<void>((resolve, reject) => {
223
+ const onError = (err: Error) => {
224
+ server.off("listening", onListening);
225
+ reject(err);
226
+ };
227
+ const onListening = () => {
228
+ server.off("error", onError);
229
+ resolve();
230
+ };
231
+ server.once("error", onError);
232
+ server.once("listening", onListening);
233
+ server.listen(this.requestedPort, this.host);
234
+ });
235
+ } catch (err) {
236
+ server.close();
237
+ throw err;
238
+ }
239
+
240
+ this.server = server;
241
+ const address = server.address();
242
+ this.boundPort = typeof address === "object" && address ? address.port : this.requestedPort;
243
+ return this.status();
244
+ }
245
+
246
+ async stop(): Promise<void> {
247
+ if (!this.server) return;
248
+ const server = this.server;
249
+ this.server = null;
250
+ this.boundPort = 0;
251
+ // Invoke each SSE client's cleanup callback so heartbeat intervals,
252
+ // batch timers, and graph-bus subscriptions are all released before the
253
+ // HTTP server closes. Without this, long-running SSE connections leak
254
+ // setInterval handles and EventEmitter listeners (Cursor review thread
255
+ // `access-http.ts:232`).
256
+ for (const cleanup of this.sseCleanupFns) {
257
+ try { cleanup(); } catch { /* ignore */ }
258
+ }
259
+ this.sseCleanupFns.clear();
260
+ // Belt-and-suspenders: clear any state not yet reached by cleanup fns.
261
+ for (const [res, timer] of this.sseBatchTimers.entries()) {
262
+ clearTimeout(timer);
263
+ this.sseBatchTimers.delete(res);
264
+ }
265
+ this.ssePendingBatches.clear();
266
+ for (const res of this.sseClients) {
267
+ try { res.end(); } catch { /* ignore */ }
268
+ }
269
+ this.sseClients.clear();
270
+ await new Promise<void>((resolve, reject) => {
271
+ server.close((err) => (err ? reject(err) : resolve()));
272
+ });
273
+ }
274
+
275
+ status(): EngramAccessHttpServerStatus {
276
+ return {
277
+ running: this.server !== null,
278
+ host: this.host,
279
+ port: this.boundPort,
280
+ maxBodyBytes: this.maxBodyBytes,
281
+ };
282
+ }
283
+
284
+ /**
285
+ * Resolve the adapter identity for the incoming request.
286
+ * Includes MCP clientInfo from the last initialize handshake if available.
287
+ * Returns null if no adapter matches or adapters are disabled.
288
+ */
289
+ resolveAdapterIdentity(req: IncomingMessage): ResolvedIdentity | null {
290
+ if (!this.adapterRegistry) return null;
291
+ // Look up clientInfo for this specific MCP session to avoid cross-session leaks.
292
+ // Non-MCP requests (no mcp-session-id header) get undefined clientInfo and
293
+ // rely on HTTP headers for adapter matching.
294
+ const sessionId = (() => {
295
+ const raw = req.headers["mcp-session-id"];
296
+ return typeof raw === "string" ? raw.trim() : undefined;
297
+ })();
298
+ return this.adapterRegistry.resolve({
299
+ headers: req.headers as Record<string, string | string[] | undefined>,
300
+ clientInfo: this.mcpServer.getClientInfo(sessionId),
301
+ });
302
+ }
303
+
304
+ /** Cache for per-request identity resolution (avoids double adapter resolution) */
305
+ private identityCache = new WeakMap<IncomingMessage, { principal?: string; namespace?: string }>();
306
+
307
+ /** Resolve principal and namespace from request headers and adapter identity */
308
+ private resolveRequestIdentity(req: IncomingMessage): { principal?: string; namespace?: string } {
309
+ const cached = this.identityCache.get(req);
310
+ if (cached) return cached;
311
+ let principal: string | undefined;
312
+ let namespace: string | undefined;
313
+
314
+ // Explicit header override takes priority for principal
315
+ if (this.trustPrincipalHeader) {
316
+ const headerVal = req.headers["x-engram-principal"];
317
+ const raw = Array.isArray(headerVal) ? headerVal[0] : headerVal;
318
+ if (typeof raw === "string") {
319
+ const trimmed = raw.trim();
320
+ if (trimmed.length > 0) {
321
+ principal = trimmed;
322
+ }
323
+ }
324
+ }
325
+
326
+ // Try adapter-based identity resolution for both principal and namespace
327
+ const adapterIdentity = this.resolveAdapterIdentity(req);
328
+ if (adapterIdentity) {
329
+ if (!principal) {
330
+ principal = adapterIdentity.principal;
331
+ }
332
+ namespace = adapterIdentity.namespace;
333
+ }
334
+
335
+ if (!principal) {
336
+ principal = this.authenticatedPrincipal;
337
+ }
338
+
339
+ const result = { principal, namespace };
340
+ this.identityCache.set(req, result);
341
+ return result;
342
+ }
343
+
344
+ private resolveRequestPrincipal(req: IncomingMessage): string | undefined {
345
+ return this.resolveRequestIdentity(req).principal;
346
+ }
347
+
348
+ /** Resolve namespace: only use the explicit body value. Adapter-inferred namespace
349
+ * is intentionally NOT used as a fallback for REST requests — omitting namespace
350
+ * should default to the server's global namespace, not silently scope to an adapter. */
351
+ private resolveNamespace(_req: IncomingMessage, bodyNamespace?: string): string | undefined {
352
+ return bodyNamespace || undefined;
353
+ }
354
+
355
+ /**
356
+ * Resolve the recall disclosure depth from the request (issue #677 PR
357
+ * 2/4). Explicit body value wins; otherwise we accept a
358
+ * `?disclosure=...` query parameter so curl/browser tooling can use the
359
+ * three-tier surface without rewriting JSON. Invalid query values
360
+ * throw `EngramAccessInputError` (CLAUDE.md rule 51 — no silent
361
+ * fallback). An absent body field AND an absent query param yields
362
+ * `undefined`, which the service maps to `DEFAULT_RECALL_DISCLOSURE`.
363
+ */
364
+ private resolveRecallDisclosure(
365
+ bodyDisclosure: RecallDisclosure | undefined,
366
+ parsed: URL,
367
+ ): RecallDisclosure | undefined {
368
+ if (bodyDisclosure !== undefined) {
369
+ return bodyDisclosure;
370
+ }
371
+ const queryDisclosure = parsed.searchParams.get("disclosure");
372
+ if (queryDisclosure === null) {
373
+ return undefined;
374
+ }
375
+ if (!isRecallDisclosure(queryDisclosure)) {
376
+ throw new EngramAccessInputError(
377
+ `disclosure must be one of: chunk, section, raw (got: ${queryDisclosure})`,
378
+ );
379
+ }
380
+ return queryDisclosure;
381
+ }
382
+
383
+ private async handle(req: IncomingMessage, res: ServerResponse, correlationId: string): Promise<void> {
384
+ const parsed = new URL(req.url ?? "/", `http://${hostToUrlAuthority(this.host)}`);
385
+ const pathname = parsed.pathname;
386
+
387
+ if (this.adminConsoleEnabled && await this.handleAdminConsole(req, res, pathname)) {
388
+ return;
389
+ }
390
+
391
+ if (!this.isAuthorized(req, pathname)) {
392
+ const body = JSON.stringify({ error: "unauthorized", code: "unauthorized" });
393
+ res.writeHead(401, {
394
+ "content-type": "application/json; charset=utf-8",
395
+ "www-authenticate": "Bearer",
396
+ "x-request-id": correlationId,
397
+ });
398
+ res.end(body);
399
+ return;
400
+ }
401
+
402
+ if (req.method === "POST" && pathname === "/mcp") {
403
+ await this.handleMcpRequest(req, res);
404
+ return;
405
+ }
406
+
407
+ if (req.method === "GET" && pathname === "/engram/v1/health") {
408
+ this.respondJson(res, 200, await this.service.health());
409
+ return;
410
+ }
411
+
412
+ if (req.method === "GET" && pathname === "/engram/v1/adapters") {
413
+ const identity = this.resolveAdapterIdentity(req);
414
+ this.respondJson(res, 200, {
415
+ adaptersEnabled: this.adapterRegistry !== null,
416
+ registered: this.adapterRegistry?.list() ?? [],
417
+ resolved: identity,
418
+ });
419
+ return;
420
+ }
421
+
422
+ if (req.method === "POST" && pathname === "/engram/v1/recall") {
423
+ const body = await this.readValidatedBody(req, "recall");
424
+ // Preserve the distinction between `codingContext: null` (explicit
425
+ // clear) and `codingContext` missing from the JSON payload
426
+ // (untouched). The previous `?? undefined` collapsed both into
427
+ // undefined, so callers lost the ability to clear the session's
428
+ // attached context through the recall endpoint.
429
+ const codingContext =
430
+ "codingContext" in body ? body.codingContext : undefined;
431
+ // Disclosure resolution (issue #677 PR 2/4): accept the value from
432
+ // the validated body OR the `?disclosure=` query parameter, with
433
+ // the body taking precedence so an explicit JSON payload is never
434
+ // silently overridden by a stale URL. CLAUDE.md rule 51: invalid
435
+ // query-param values throw, never fall back silently.
436
+ const disclosure = this.resolveRecallDisclosure(body.disclosure, parsed);
437
+ // Issue #680 — historical recall pin (`asOf`). Body field wins
438
+ // over `?as_of=` query param. Empty query is rejected only when
439
+ // the body didn't supply a valid pin (codex P2 + cursor Medium).
440
+ const asOfQueryRaw = parsed.searchParams.get("as_of");
441
+ const bodyHasAsOf =
442
+ typeof body.asOf === "string" && body.asOf.length > 0;
443
+ if (
444
+ !bodyHasAsOf &&
445
+ asOfQueryRaw !== null &&
446
+ asOfQueryRaw.length === 0
447
+ ) {
448
+ throw new EngramAccessInputError(
449
+ "as_of must be a non-empty timestamp (got empty value)",
450
+ );
451
+ }
452
+ const asOf =
453
+ body.asOf ??
454
+ (asOfQueryRaw !== null && asOfQueryRaw.length > 0
455
+ ? asOfQueryRaw
456
+ : undefined);
457
+ // Tag filter (issue #689). Body presence wins over query params
458
+ // — explicit `tags: []` in body clears the filter even with
459
+ // stale `?tag=` URLs.
460
+ const bodyHasTagsField =
461
+ body !== null &&
462
+ typeof body === "object" &&
463
+ "tags" in (body as Record<string, unknown>);
464
+ const bodyTagsValue = bodyHasTagsField
465
+ ? (body as { tags?: unknown }).tags
466
+ : undefined;
467
+ const bodyTags = Array.isArray(bodyTagsValue)
468
+ ? (bodyTagsValue as string[])
469
+ : undefined;
470
+ const queryTags = parsed.searchParams.getAll("tag");
471
+ const tags = bodyHasTagsField
472
+ ? bodyTags
473
+ : queryTags.length > 0
474
+ ? queryTags
475
+ : undefined;
476
+ const bodyTagMatch = (body as { tagMatch?: unknown }).tagMatch;
477
+ let tagMatch: "any" | "all" | undefined;
478
+ if (bodyTagMatch !== undefined) {
479
+ if (bodyTagMatch === "any" || bodyTagMatch === "all") {
480
+ tagMatch = bodyTagMatch;
481
+ }
482
+ } else {
483
+ const queryTagMatch = parsed.searchParams.get("tag_match");
484
+ if (queryTagMatch !== null) {
485
+ if (queryTagMatch !== "any" && queryTagMatch !== "all") {
486
+ throw new EngramAccessInputError(
487
+ `tag_match must be one of: any, all (got: ${queryTagMatch})`,
488
+ );
489
+ }
490
+ tagMatch = queryTagMatch;
491
+ }
492
+ }
493
+ // Issue #681 — `?include_low_confidence=true|false` mirrors the CLI
494
+ // `--include-low-confidence` flag. Body field wins so a JSON payload can
495
+ // explicitly clear a stale query parameter.
496
+ const bodyIncludeLowConfidence =
497
+ (body as { includeLowConfidence?: unknown }).includeLowConfidence;
498
+ const queryIncludeLowConfidence = parsed.searchParams.get("include_low_confidence");
499
+ if (
500
+ bodyIncludeLowConfidence === undefined &&
501
+ queryIncludeLowConfidence !== null &&
502
+ queryIncludeLowConfidence !== "true" &&
503
+ queryIncludeLowConfidence !== "false"
504
+ ) {
505
+ throw new EngramAccessInputError(
506
+ `include_low_confidence must be one of: true, false (got: ${queryIncludeLowConfidence})`,
507
+ );
508
+ }
509
+ const includeLowConfidence =
510
+ bodyIncludeLowConfidence === true ||
511
+ (bodyIncludeLowConfidence === undefined &&
512
+ queryIncludeLowConfidence === "true");
513
+ const response = await this.service.recall({
514
+ query: body.query ?? "",
515
+ sessionKey: body.sessionKey,
516
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
517
+ idempotencyKey: body.idempotencyKey,
518
+ namespace: this.resolveNamespace(req, body.namespace),
519
+ topK: body.topK,
520
+ mode: body.mode as RecallPlanMode | "auto" | undefined,
521
+ includeDebug: body.includeDebug === true,
522
+ // Forward the validated disclosure depth to the service layer
523
+ // (issue #677). The zod schema accepts/rejects body values;
524
+ // `resolveRecallDisclosure()` validates the query-param fallback.
525
+ disclosure,
526
+ codingContext,
527
+ // Forward cwd/projectTag for auto git-context resolution (issue #569).
528
+ cwd: body.cwd,
529
+ projectTag: body.projectTag,
530
+ ...(asOf !== undefined ? { asOf } : {}),
531
+ ...(tags !== undefined ? { tags } : {}),
532
+ ...(tagMatch !== undefined ? { tagMatch } : {}),
533
+ ...(includeLowConfidence ? { includeLowConfidence: true } : {}),
534
+ });
535
+ this.respondJson(res, 200, response);
536
+ return;
537
+ }
538
+
539
+ // Attach / clear coding-agent context for a session (issue #569 PR 5).
540
+ // Mirrors `setCodingContext` on the access service. Connectors call this
541
+ // at session start after resolving a git context for the cwd; `remnic
542
+ // doctor` (PR 8) surfaces the attached context.
543
+ if (req.method === "POST" && pathname === "/engram/v1/coding-context") {
544
+ const body = await this.readValidatedBody(req, "setCodingContext");
545
+ this.service.setCodingContext({
546
+ sessionKey: body.sessionKey,
547
+ codingContext: body.codingContext,
548
+ });
549
+ this.respondJson(res, 200, { ok: true });
550
+ return;
551
+ }
552
+
553
+ if (
554
+ req.method === "POST" &&
555
+ (pathname === "/engram/v1/capsules/export" || pathname === "/remnic/v1/capsules/export")
556
+ ) {
557
+ const body = await this.readValidatedBody(req, "capsuleExport");
558
+ this.ensureWriteRateLimitAvailable();
559
+ const result = await this.service.capsuleExport({
560
+ name: body.name,
561
+ namespace: this.resolveNamespace(req, body.namespace),
562
+ principal: this.resolveRequestPrincipal(req),
563
+ since: body.since,
564
+ includeKinds: body.includeKinds,
565
+ peerIds: body.peerIds,
566
+ includeTranscripts: body.includeTranscripts,
567
+ encrypt: body.encrypt,
568
+ });
569
+ this.recordWriteRateLimitHit();
570
+ this.respondJson(res, 200, result);
571
+ return;
572
+ }
573
+
574
+ if (
575
+ req.method === "POST" &&
576
+ (pathname === "/engram/v1/capsules/import" || pathname === "/remnic/v1/capsules/import")
577
+ ) {
578
+ const body = await this.readValidatedBody(req, "capsuleImport");
579
+ this.ensureWriteRateLimitAvailable();
580
+ const result = await this.service.capsuleImport({
581
+ archivePath: expandTildePath(body.archivePath),
582
+ namespace: this.resolveNamespace(req, body.namespace),
583
+ principal: this.resolveRequestPrincipal(req),
584
+ mode: body.mode,
585
+ });
586
+ this.recordWriteRateLimitHit();
587
+ this.respondJson(res, 200, result);
588
+ return;
589
+ }
590
+
591
+ if (req.method === "POST" && pathname === "/engram/v1/recall/explain") {
592
+ const body = await this.readValidatedBody(req, "recallExplain");
593
+ const response = await this.service.recallExplain({
594
+ sessionKey: body.sessionKey,
595
+ namespace: this.resolveNamespace(req, body.namespace),
596
+ });
597
+ this.respondJson(res, 200, response);
598
+ return;
599
+ }
600
+
601
+ if (
602
+ req.method === "POST" &&
603
+ (pathname === "/engram/v1/action-confidence" || pathname === "/remnic/v1/action-confidence")
604
+ ) {
605
+ const body = await this.readValidatedBody(req, "actionConfidence");
606
+ this.respondJson(res, 200, await this.service.actionConfidence(body));
607
+ return;
608
+ }
609
+
610
+ // Tier-explain (issue #518): structured per-result annotation from
611
+ // the direct-answer retrieval tier. Orthogonal to /recall/explain
612
+ // above, which returns a graph-path explanation document.
613
+ if (req.method === "GET" && pathname === "/engram/v1/recall/tier-explain") {
614
+ const sessionParam = parsed.searchParams.get("session");
615
+ const sessionKey = sessionParam && sessionParam.length > 0 ? sessionParam : undefined;
616
+ const namespaceParam = parsed.searchParams.get("namespace");
617
+ const namespace = this.resolveNamespace(
618
+ req,
619
+ namespaceParam && namespaceParam.length > 0 ? namespaceParam : undefined,
620
+ );
621
+ const payload = await this.service.recallTierExplain(
622
+ sessionKey,
623
+ namespace,
624
+ this.resolveRequestPrincipal(req),
625
+ );
626
+ this.respondJson(res, 200, payload);
627
+ return;
628
+ }
629
+
630
+ // Recall X-ray (issue #570 PR 4): unified per-result attribution
631
+ // snapshot. Requires bearer auth (same as every other endpoint
632
+ // here) and enforces namespace scope before the recall fires
633
+ // (CLAUDE.md rule 42). Query comes from the `q` search param so
634
+ // GET stays cacheable; `namespace` / `session` / `budget` are
635
+ // optional.
636
+ if (req.method === "GET" && pathname === "/engram/v1/recall/xray") {
637
+ const queryParam = parsed.searchParams.get("q");
638
+ if (!queryParam || queryParam.trim().length === 0) {
639
+ this.respondJson(res, 400, {
640
+ error: "missing_query",
641
+ code: "missing_query",
642
+ message: "q search parameter is required and must be non-empty",
643
+ });
644
+ return;
645
+ }
646
+ const sessionParam = parsed.searchParams.get("session");
647
+ const sessionKey = sessionParam && sessionParam.length > 0
648
+ ? sessionParam
649
+ : undefined;
650
+ const namespaceParam = parsed.searchParams.get("namespace");
651
+ const namespace = this.resolveNamespace(
652
+ req,
653
+ namespaceParam && namespaceParam.length > 0
654
+ ? namespaceParam
655
+ : undefined,
656
+ );
657
+ const budgetParam = parsed.searchParams.get("budget");
658
+ // Reject invalid `budget` with 400 rather than silently
659
+ // defaulting (CLAUDE.md rules 14 + 51).
660
+ let budget: number | undefined;
661
+ if (budgetParam !== null && budgetParam !== "") {
662
+ const parsedBudget = Number(budgetParam);
663
+ if (
664
+ !Number.isFinite(parsedBudget)
665
+ || parsedBudget <= 0
666
+ || !Number.isInteger(parsedBudget)
667
+ ) {
668
+ this.respondJson(res, 400, {
669
+ error: "invalid_budget",
670
+ code: "invalid_budget",
671
+ message:
672
+ "budget expects a positive integer",
673
+ });
674
+ return;
675
+ }
676
+ budget = parsedBudget;
677
+ }
678
+ // Disclosure depth (issue #677 PR 3/4 telemetry plumbing). When
679
+ // present, must match the chunk|section|raw allow-list; invalid
680
+ // values surface as a 400 (CLAUDE.md rule 51 — no silent
681
+ // fallback) rather than silently disabling the per-disclosure
682
+ // summary table.
683
+ const disclosureParam = parsed.searchParams.get("disclosure");
684
+ let disclosure: RecallDisclosure | undefined;
685
+ if (disclosureParam !== null && disclosureParam.length > 0) {
686
+ if (!isRecallDisclosure(disclosureParam)) {
687
+ this.respondJson(res, 400, {
688
+ error: "invalid_disclosure",
689
+ code: "invalid_disclosure",
690
+ message:
691
+ "disclosure must be one of: chunk, section, raw",
692
+ });
693
+ return;
694
+ }
695
+ disclosure = disclosureParam;
696
+ }
697
+ // Only translate validation errors (empty query, bad budget)
698
+ // into 400s. Backend faults (timeouts, storage errors,
699
+ // unexpected orchestrator failures) must bubble to the global
700
+ // `handle()` error handler so they return 500 and get logged
701
+ // properly. `service.recallXray` prefixes its validation
702
+ // errors with "recallXray:" so we key off that prefix rather
703
+ // than catching everything.
704
+ let payload: Awaited<ReturnType<typeof this.service.recallXray>>;
705
+ try {
706
+ payload = await this.service.recallXray({
707
+ query: queryParam,
708
+ sessionKey,
709
+ namespace,
710
+ budget,
711
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
712
+ ...(disclosure !== undefined ? { disclosure } : {}),
713
+ });
714
+ } catch (err) {
715
+ const message = err instanceof Error ? err.message : String(err);
716
+ if (message.startsWith("recallXray:")) {
717
+ this.respondJson(res, 400, {
718
+ error: "invalid_request",
719
+ code: "invalid_request",
720
+ message,
721
+ });
722
+ return;
723
+ }
724
+ // Anything else is a server-side fault; rethrow so the
725
+ // outer `handle()` catch returns 500 + logs the error.
726
+ throw err;
727
+ }
728
+ this.respondJson(res, 200, payload);
729
+ return;
730
+ }
731
+
732
+ if (req.method === "POST" && pathname === "/engram/v1/observe") {
733
+ const body = await this.readValidatedBody(req, "observe");
734
+ this.ensureWriteRateLimitAvailable();
735
+ const response = await this.service.observe({
736
+ sessionKey: body.sessionKey,
737
+ messages: body.messages.map((message) => ({
738
+ role: message.role,
739
+ content: message.content,
740
+ sourceFormat: message.sourceFormat ?? undefined,
741
+ rawContent: message.rawContent ?? undefined,
742
+ parts: message.parts ?? undefined,
743
+ })),
744
+ namespace: this.resolveNamespace(req, body.namespace),
745
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
746
+ skipExtraction: body.skipExtraction === true,
747
+ // Forward cwd/projectTag for auto git-context resolution (issue #569).
748
+ cwd: body.cwd,
749
+ projectTag: body.projectTag,
750
+ });
751
+ this.recordWriteRateLimitHit();
752
+ this.respondJson(res, 202, response);
753
+ return;
754
+ }
755
+
756
+ if (req.method === "POST" && pathname === "/engram/v1/lcm/search") {
757
+ const body = await this.readValidatedBody(req, "lcmSearch");
758
+ const response = await this.service.lcmSearch({
759
+ query: body.query,
760
+ sessionKey: body.sessionKey,
761
+ namespace: this.resolveNamespace(req, body.namespace),
762
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
763
+ limit: body.limit,
764
+ });
765
+ this.respondJson(res, 200, response);
766
+ return;
767
+ }
768
+
769
+ if (
770
+ req.method === "POST" &&
771
+ (pathname === "/engram/v1/lcm/compaction/flush" || pathname === "/remnic/v1/lcm/compaction/flush")
772
+ ) {
773
+ const body = await this.readValidatedBody(req, "lcmCompactionFlush");
774
+ this.ensureWriteRateLimitAvailable();
775
+ const response = await this.service.lcmCompactionFlush({
776
+ sessionKey: body.sessionKey,
777
+ namespace: this.resolveNamespace(req, body.namespace),
778
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
779
+ });
780
+ this.recordWriteRateLimitHit();
781
+ this.respondJson(res, 200, response);
782
+ return;
783
+ }
784
+
785
+ if (
786
+ req.method === "POST" &&
787
+ (pathname === "/engram/v1/lcm/compaction/record" || pathname === "/remnic/v1/lcm/compaction/record")
788
+ ) {
789
+ const body = await this.readValidatedBody(req, "lcmCompactionRecord");
790
+ this.ensureWriteRateLimitAvailable();
791
+ const response = await this.service.lcmCompactionRecord({
792
+ sessionKey: body.sessionKey,
793
+ namespace: this.resolveNamespace(req, body.namespace),
794
+ tokensBefore: body.tokensBefore,
795
+ tokensAfter: body.tokensAfter,
796
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
797
+ });
798
+ this.recordWriteRateLimitHit();
799
+ this.respondJson(res, 200, response);
800
+ return;
801
+ }
802
+
803
+ if (req.method === "GET" && pathname === "/engram/v1/lcm/status") {
804
+ this.respondJson(res, 200, await this.service.lcmStatus());
805
+ return;
806
+ }
807
+
808
+ if (req.method === "POST" && pathname === "/engram/v1/memories") {
809
+ const body = await this.readValidatedBody(req, "memoryStore");
810
+ const request = {
811
+ schemaVersion: body.schemaVersion,
812
+ idempotencyKey: body.idempotencyKey,
813
+ dryRun: body.dryRun === true,
814
+ sessionKey: body.sessionKey,
815
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
816
+ content: body.content,
817
+ category: body.category,
818
+ confidence: body.confidence,
819
+ namespace: this.resolveNamespace(req, body.namespace),
820
+ tags: body.tags,
821
+ entityRef: body.entityRef,
822
+ ttl: body.ttl,
823
+ sourceReason: body.sourceReason,
824
+ };
825
+ const idempotencyStatus = await this.service.peekMemoryStoreIdempotency(request);
826
+ if (idempotencyStatus === "miss" && request.dryRun !== true) {
827
+ this.ensureWriteRateLimitAvailable();
828
+ }
829
+ const response = await this.service.memoryStore(request);
830
+ if (this.shouldCountWriteRateLimit(response as { dryRun?: boolean; idempotencyReplay?: boolean })) {
831
+ this.recordWriteRateLimitHit();
832
+ }
833
+ this.respondJson(res, this.writeResponseStatus(response), response);
834
+ return;
835
+ }
836
+
837
+ if (req.method === "POST" && pathname === "/engram/v1/suggestions") {
838
+ const body = await this.readValidatedBody(req, "suggestionSubmit");
839
+ const request = {
840
+ schemaVersion: body.schemaVersion,
841
+ idempotencyKey: body.idempotencyKey,
842
+ dryRun: body.dryRun === true,
843
+ sessionKey: body.sessionKey,
844
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
845
+ content: body.content,
846
+ category: body.category,
847
+ confidence: body.confidence,
848
+ namespace: this.resolveNamespace(req, body.namespace),
849
+ tags: body.tags,
850
+ entityRef: body.entityRef,
851
+ ttl: body.ttl,
852
+ sourceReason: body.sourceReason,
853
+ };
854
+ const idempotencyStatus = await this.service.peekSuggestionSubmitIdempotency(request);
855
+ if (idempotencyStatus === "miss" && request.dryRun !== true) {
856
+ this.ensureWriteRateLimitAvailable();
857
+ }
858
+ const response = await this.service.suggestionSubmit(request);
859
+ if (this.shouldCountWriteRateLimit(response as { dryRun?: boolean; idempotencyReplay?: boolean })) {
860
+ this.recordWriteRateLimitHit();
861
+ }
862
+ this.respondJson(res, this.writeResponseStatus(response), response);
863
+ return;
864
+ }
865
+
866
+ if (req.method === "GET" && pathname === "/engram/v1/memories") {
867
+ const limitRaw = parseInt(parsed.searchParams.get("limit") ?? "50", 10);
868
+ const offsetRaw = parseInt(parsed.searchParams.get("offset") ?? "0", 10);
869
+ const sortParam = parsed.searchParams.get("sort") ?? undefined;
870
+ const sort = sortParam === "updated_desc"
871
+ || sortParam === "updated_asc"
872
+ || sortParam === "created_desc"
873
+ || sortParam === "created_asc"
874
+ ? sortParam
875
+ : undefined;
876
+ const response = await this.service.memoryBrowse({
877
+ query: parsed.searchParams.get("q") ?? undefined,
878
+ status: parsed.searchParams.get("status") ?? undefined,
879
+ category: parsed.searchParams.get("category") ?? undefined,
880
+ namespace: parsed.searchParams.get("namespace") ?? undefined,
881
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
882
+ sort,
883
+ limit: Number.isFinite(limitRaw) ? limitRaw : 50,
884
+ offset: Number.isFinite(offsetRaw) ? offsetRaw : 0,
885
+ });
886
+ this.respondJson(res, 200, response);
887
+ return;
888
+ }
889
+
890
+ const memoryMatch = pathname.match(/^\/engram\/v1\/memories\/([^/]+)$/);
891
+ if (req.method === "GET" && memoryMatch) {
892
+ const memoryId = decodeURIComponent(memoryMatch[1] ?? "");
893
+ const namespace = parsed.searchParams.get("namespace") ?? undefined;
894
+ const response = await this.service.memoryGet(memoryId, namespace, this.resolveRequestPrincipal(req));
895
+ this.respondJson(res, response.found ? 200 : 404, response);
896
+ return;
897
+ }
898
+
899
+ const timelineMatch = pathname.match(/^\/engram\/v1\/memories\/([^/]+)\/timeline$/);
900
+ if (req.method === "GET" && timelineMatch) {
901
+ const memoryId = decodeURIComponent(timelineMatch[1] ?? "");
902
+ const namespace = parsed.searchParams.get("namespace") ?? undefined;
903
+ const limitRaw = parseInt(parsed.searchParams.get("limit") ?? "200", 10);
904
+ const limit = Number.isFinite(limitRaw) ? limitRaw : 200;
905
+ const response = await this.service.memoryTimeline(memoryId, namespace, limit, this.resolveRequestPrincipal(req));
906
+ this.respondJson(res, response.found ? 200 : 404, response);
907
+ return;
908
+ }
909
+
910
+ if (req.method === "GET" && pathname === "/engram/v1/entities") {
911
+ const limitRaw = parseInt(parsed.searchParams.get("limit") ?? "50", 10);
912
+ const offsetRaw = parseInt(parsed.searchParams.get("offset") ?? "0", 10);
913
+ const response = await this.service.entityList({
914
+ namespace: parsed.searchParams.get("namespace") ?? undefined,
915
+ query: parsed.searchParams.get("q") ?? undefined,
916
+ limit: Number.isFinite(limitRaw) ? limitRaw : 50,
917
+ offset: Number.isFinite(offsetRaw) ? offsetRaw : 0,
918
+ });
919
+ this.respondJson(res, 200, response);
920
+ return;
921
+ }
922
+
923
+ const entityMatch = pathname.match(/^\/engram\/v1\/entities\/([^/]+)$/);
924
+ if (req.method === "GET" && entityMatch) {
925
+ const entityName = decodeURIComponent(entityMatch[1] ?? "");
926
+ const namespace = parsed.searchParams.get("namespace") ?? undefined;
927
+ const response = await this.service.entityGet(entityName, namespace);
928
+ this.respondJson(res, response.found ? 200 : 404, response);
929
+ return;
930
+ }
931
+
932
+ if (req.method === "GET" && pathname === "/engram/v1/review-queue") {
933
+ const response = await this.service.reviewQueue(
934
+ parsed.searchParams.get("runId") ?? undefined,
935
+ parsed.searchParams.get("namespace") ?? undefined,
936
+ this.resolveRequestPrincipal(req),
937
+ );
938
+ this.respondJson(res, 200, response);
939
+ return;
940
+ }
941
+
942
+ if (req.method === "GET" && pathname === "/engram/v1/maintenance") {
943
+ this.respondJson(res, 200, await this.service.maintenance(parsed.searchParams.get("namespace") ?? undefined, this.resolveRequestPrincipal(req)));
944
+ return;
945
+ }
946
+
947
+ if (req.method === "GET" && pathname === "/engram/v1/quality") {
948
+ this.respondJson(res, 200, await this.service.quality(parsed.searchParams.get("namespace") ?? undefined, this.resolveRequestPrincipal(req)));
949
+ return;
950
+ }
951
+
952
+ if (req.method === "GET" && pathname === "/engram/v1/trust-zones/status") {
953
+ this.respondJson(
954
+ res,
955
+ 200,
956
+ await this.service.trustZoneStatus(parsed.searchParams.get("namespace") ?? undefined, this.resolveRequestPrincipal(req)),
957
+ );
958
+ return;
959
+ }
960
+
961
+ // Procedural memory stats (issue #567 PR 5/5). Read-only; namespace is
962
+ // scoped via the same resolver used by recall/trust-zones so cross-
963
+ // tenant reads aren't possible (CLAUDE.md rule 42).
964
+ if (req.method === "GET" && pathname === "/engram/v1/procedural/stats") {
965
+ const namespaceParam = parsed.searchParams.get("namespace");
966
+ this.respondJson(
967
+ res,
968
+ 200,
969
+ await this.service.procedureStats(
970
+ {
971
+ namespace: this.resolveNamespace(
972
+ req,
973
+ namespaceParam && namespaceParam.length > 0
974
+ ? namespaceParam
975
+ : undefined,
976
+ ),
977
+ },
978
+ this.resolveRequestPrincipal(req),
979
+ ),
980
+ );
981
+ return;
982
+ }
983
+
984
+ if (req.method === "GET" && pathname === "/engram/v1/trust-zones/records") {
985
+ const limitRaw = parseInt(parsed.searchParams.get("limit") ?? "25", 10);
986
+ const offsetRaw = parseInt(parsed.searchParams.get("offset") ?? "0", 10);
987
+ const response = await this.service.trustZoneBrowse({
988
+ query: parsed.searchParams.get("q") ?? undefined,
989
+ zone: parseTrustZoneFilter(parsed.searchParams.get("zone")),
990
+ kind: parseTrustZoneKindFilter(parsed.searchParams.get("kind")),
991
+ sourceClass: parseTrustZoneSourceClassFilter(parsed.searchParams.get("sourceClass")),
992
+ namespace: parsed.searchParams.get("namespace") ?? undefined,
993
+ limit: Number.isFinite(limitRaw) ? limitRaw : 25,
994
+ offset: Number.isFinite(offsetRaw) ? offsetRaw : 0,
995
+ }, this.resolveRequestPrincipal(req));
996
+ this.respondJson(res, 200, response);
997
+ return;
998
+ }
999
+
1000
+ if (req.method === "POST" && pathname === "/engram/v1/review-disposition") {
1001
+ const body = await this.readValidatedBody(req, "reviewDisposition");
1002
+ this.ensureWriteRateLimitAvailable();
1003
+ const response = await this.service.reviewDisposition({
1004
+ memoryId: body.memoryId,
1005
+ status: body.status,
1006
+ reasonCode: body.reasonCode,
1007
+ namespace: this.resolveNamespace(req, body.namespace),
1008
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
1009
+ });
1010
+ if (this.shouldCountWriteRateLimit(response as unknown as { dryRun?: boolean; idempotencyReplay?: boolean })) {
1011
+ this.recordWriteRateLimitHit();
1012
+ }
1013
+ this.respondJson(res, 200, response);
1014
+ return;
1015
+ }
1016
+
1017
+ if (req.method === "POST" && pathname === "/engram/v1/trust-zones/promote") {
1018
+ const body = await this.readValidatedBody(req, "trustZonePromote");
1019
+ const dryRun = body.dryRun === true;
1020
+ if (!dryRun) {
1021
+ this.ensureWriteRateLimitAvailable();
1022
+ }
1023
+ const response = await this.service.trustZonePromote({
1024
+ recordId: body.recordId,
1025
+ targetZone: body.targetZone,
1026
+ promotionReason: body.promotionReason,
1027
+ recordedAt: body.recordedAt,
1028
+ summary: body.summary,
1029
+ dryRun,
1030
+ namespace: this.resolveNamespace(req, body.namespace),
1031
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
1032
+ });
1033
+ if (this.shouldCountWriteRateLimit(response as unknown as { dryRun?: boolean; idempotencyReplay?: boolean })) {
1034
+ this.recordWriteRateLimitHit();
1035
+ }
1036
+ this.respondJson(res, response.dryRun ? 200 : 201, response);
1037
+ return;
1038
+ }
1039
+
1040
+ if (req.method === "POST" && pathname === "/engram/v1/trust-zones/demo-seed") {
1041
+ const body = await this.readValidatedBody(req, "trustZoneDemoSeed");
1042
+ const dryRun = body.dryRun === true;
1043
+ if (!dryRun) {
1044
+ this.ensureWriteRateLimitAvailable();
1045
+ }
1046
+ const response = await this.service.trustZoneDemoSeed({
1047
+ scenario: body.scenario,
1048
+ recordedAt: body.recordedAt,
1049
+ dryRun,
1050
+ namespace: this.resolveNamespace(req, body.namespace),
1051
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
1052
+ });
1053
+ if (this.shouldCountWriteRateLimit(response as unknown as { dryRun?: boolean; idempotencyReplay?: boolean })) {
1054
+ this.recordWriteRateLimitHit();
1055
+ }
1056
+ this.respondJson(res, response.dryRun ? 200 : 201, response);
1057
+ return;
1058
+ }
1059
+
1060
+ // Citation usage tracking (issue #379)
1061
+ if (req.method === "POST" && pathname === "/v1/citations/observed") {
1062
+ const body = await this.readJsonBody(req);
1063
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
1064
+ throw new HttpError(400, "request body must be a JSON object", "invalid_body");
1065
+ }
1066
+ const payload = body as Record<string, unknown>;
1067
+ const sessionId = typeof payload.sessionId === "string" ? payload.sessionId : undefined;
1068
+ const namespace = typeof payload.namespace === "string" ? payload.namespace : undefined;
1069
+ const citationsRaw = payload.citations;
1070
+ if (!citationsRaw || typeof citationsRaw !== "object" || Array.isArray(citationsRaw)) {
1071
+ throw new HttpError(400, "citations must be a JSON object with entries and rolloutIds", "invalid_citations");
1072
+ }
1073
+ const citObj = citationsRaw as Record<string, unknown>;
1074
+ const entries: CitationEntry[] = [];
1075
+ if (Array.isArray(citObj.entries)) {
1076
+ for (const raw of citObj.entries) {
1077
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
1078
+ const e = raw as Record<string, unknown>;
1079
+ if (
1080
+ typeof e.path === "string" &&
1081
+ typeof e.lineStart === "number" &&
1082
+ typeof e.lineEnd === "number"
1083
+ ) {
1084
+ entries.push({
1085
+ path: e.path,
1086
+ lineStart: e.lineStart,
1087
+ lineEnd: e.lineEnd,
1088
+ note: typeof e.note === "string" ? e.note : "",
1089
+ });
1090
+ }
1091
+ }
1092
+ }
1093
+ }
1094
+ const rolloutIds: string[] = [];
1095
+ if (Array.isArray(citObj.rolloutIds)) {
1096
+ for (const id of citObj.rolloutIds) {
1097
+ if (typeof id === "string" && id.length > 0) {
1098
+ rolloutIds.push(id);
1099
+ }
1100
+ }
1101
+ }
1102
+
1103
+ // Record usage: for each citation entry, try to increment usage on the
1104
+ // matching memory. The service exposes recordAccess for this purpose.
1105
+ // Pass authenticatedPrincipal so namespace ACL checks use the same
1106
+ // identity resolution as other write endpoints (Finding #1, issue #379).
1107
+ let matched = 0;
1108
+ let submitted = 0;
1109
+ if (typeof this.service.recordCitationUsage === "function") {
1110
+ const result = await this.service.recordCitationUsage({
1111
+ sessionId,
1112
+ namespace: this.resolveNamespace(req, namespace),
1113
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
1114
+ entries,
1115
+ rolloutIds,
1116
+ });
1117
+ submitted = result.submitted;
1118
+ matched = result.matched;
1119
+ }
1120
+
1121
+ this.respondJson(res, 200, {
1122
+ ok: true,
1123
+ submitted,
1124
+ matched,
1125
+ entriesReceived: entries.length,
1126
+ rolloutIdsReceived: rolloutIds.length,
1127
+ });
1128
+ return;
1129
+ }
1130
+
1131
+ // ── Contradiction Review (issue #520) ─────────────────────────────────────
1132
+ if (req.method === "GET" && pathname === "/engram/v1/review/contradictions") {
1133
+ const VALID_FILTERS = new Set(["all", "unresolved", "contradicts", "independent", "duplicates", "needs-user"]);
1134
+ const rawFilter = parsed.searchParams.get("filter") ?? "unresolved";
1135
+ if (!VALID_FILTERS.has(rawFilter)) {
1136
+ this.respondJson(res, 400, { error: `Invalid filter '${rawFilter}'. Valid: ${[...VALID_FILTERS].join(", ")}` });
1137
+ return;
1138
+ }
1139
+ const namespace = parsed.searchParams.get("namespace") ?? undefined;
1140
+ const limitRaw = parseInt(parsed.searchParams.get("limit") ?? "50", 10);
1141
+ const {
1142
+ isDefaultReviewNamespace,
1143
+ listPairs,
1144
+ } = await import("./contradiction/contradiction-review.js");
1145
+ const principal = this.resolveRequestPrincipal(req);
1146
+ const resolved = await this.service.getReadableStorageForNamespace(namespace, principal);
1147
+ const reviewNamespace = this.service.configRef.namespacesEnabled ? resolved.namespace : undefined;
1148
+ const includeUnscopedForNamespace = Boolean(
1149
+ reviewNamespace && isDefaultReviewNamespace(this.service.configRef.defaultNamespace, namespace, reviewNamespace),
1150
+ );
1151
+ const result = listPairs(this.service.memoryDir, {
1152
+ filter: rawFilter as "all" | "unresolved" | "contradicts" | "independent" | "duplicates" | "needs-user",
1153
+ namespace: reviewNamespace,
1154
+ includeUnscopedForNamespace,
1155
+ limit: Number.isFinite(limitRaw) ? limitRaw : 50,
1156
+ });
1157
+ this.respondJson(res, 200, result);
1158
+ return;
1159
+ }
1160
+
1161
+ if (req.method === "GET" && pathname.startsWith("/engram/v1/review/contradictions/")) {
1162
+ const pairId = pathname.split("/").pop() ?? "";
1163
+ const { readPair } = await import("./contradiction/contradiction-review.js");
1164
+ const pair = readPair(this.service.memoryDir, pairId);
1165
+ if (!pair) {
1166
+ this.respondJson(res, 404, { error: "pair_not_found" });
1167
+ return;
1168
+ }
1169
+ try {
1170
+ await this.service.getReadableStorageForNamespace(pair.namespace, this.resolveRequestPrincipal(req));
1171
+ } catch {
1172
+ this.respondJson(res, 404, { error: "pair_not_found" });
1173
+ return;
1174
+ }
1175
+ this.respondJson(res, 200, pair);
1176
+ return;
1177
+ }
1178
+
1179
+ if (req.method === "POST" && pathname === "/engram/v1/review/resolve") {
1180
+ const body = await this.readJsonBody(req) as Record<string, unknown>;
1181
+ const pairId = typeof body.pairId === "string" ? body.pairId : "";
1182
+ const verb = typeof body.verb === "string" ? body.verb : "";
1183
+ if (!pairId || !verb) {
1184
+ this.respondJson(res, 400, { error: "pairId and verb are required" });
1185
+ return;
1186
+ }
1187
+ const { isValidResolutionVerb, executeResolution } = await import("./contradiction/resolution.js");
1188
+ if (!isValidResolutionVerb(verb)) {
1189
+ this.respondJson(res, 400, { error: `Invalid verb: ${verb}. Must be one of: keep-a, keep-b, merge, both-valid, needs-more-context` });
1190
+ return;
1191
+ }
1192
+ const principal = this.resolveRequestPrincipal(req);
1193
+ const result = await executeResolution(this.service.memoryDir, this.service.storageRef, pairId, verb, {
1194
+ mergedMemoryId: typeof body.mergedMemoryId === "string" ? body.mergedMemoryId : undefined,
1195
+ mergedContent: typeof body.mergedContent === "string" ? body.mergedContent : undefined,
1196
+ storageForNamespace: async (namespace) => {
1197
+ const resolved = await this.service.getWritableStorageForNamespace(namespace, principal);
1198
+ return resolved.storage;
1199
+ },
1200
+ });
1201
+ this.respondJson(res, 200, result);
1202
+ return;
1203
+ }
1204
+
1205
+ // Graph snapshot (issue #691 PR 2/5) — read-only adjacency view used by
1206
+ // the admin-pane scaffold shipped in PR 1/5. All filters are query
1207
+ // params so the surface stays cacheable; invalid values yield 400 with
1208
+ // a descriptive body (CLAUDE.md rule 51 — never silently default).
1209
+ if (req.method === "GET" && pathname === "/engram/v1/graph/snapshot") {
1210
+ const limitRaw = parsed.searchParams.get("limit");
1211
+ let limit: number | undefined;
1212
+ if (limitRaw !== null && limitRaw.length > 0) {
1213
+ const parsedLimit = Number(limitRaw);
1214
+ if (
1215
+ !Number.isFinite(parsedLimit)
1216
+ || !Number.isInteger(parsedLimit)
1217
+ || parsedLimit <= 0
1218
+ ) {
1219
+ this.respondJson(res, 400, {
1220
+ error: "invalid_limit",
1221
+ code: "invalid_limit",
1222
+ message: "limit must be a positive integer",
1223
+ });
1224
+ return;
1225
+ }
1226
+ limit = parsedLimit;
1227
+ }
1228
+ const sinceRaw = parsed.searchParams.get("since");
1229
+ let since: string | undefined;
1230
+ if (sinceRaw !== null && sinceRaw.length > 0) {
1231
+ // Validate up-front so the access service can stay focused on the
1232
+ // pure snapshot logic (parser also runs there as a defense in
1233
+ // depth, but rejecting at the boundary preserves the
1234
+ // "invalid_since" error code instead of leaking a generic 500).
1235
+ if (!Number.isFinite(Date.parse(sinceRaw))) {
1236
+ this.respondJson(res, 400, {
1237
+ error: "invalid_since",
1238
+ code: "invalid_since",
1239
+ message: "since must be a parseable ISO timestamp",
1240
+ });
1241
+ return;
1242
+ }
1243
+ since = sinceRaw;
1244
+ }
1245
+ const focusNodeIdRaw = parsed.searchParams.get("focusNodeId");
1246
+ const focusNodeId = focusNodeIdRaw && focusNodeIdRaw.length > 0
1247
+ ? focusNodeIdRaw
1248
+ : undefined;
1249
+ const categoriesRaw = parsed.searchParams.get("categories");
1250
+ let categories: string[] | undefined;
1251
+ if (categoriesRaw !== null && categoriesRaw.length > 0) {
1252
+ categories = categoriesRaw
1253
+ .split(",")
1254
+ .map((value) => value.trim())
1255
+ .filter((value) => value.length > 0);
1256
+ if (categories.length === 0) {
1257
+ this.respondJson(res, 400, {
1258
+ error: "invalid_categories",
1259
+ code: "invalid_categories",
1260
+ message:
1261
+ "categories must be a comma-separated list with at least one non-empty value",
1262
+ });
1263
+ return;
1264
+ }
1265
+ }
1266
+ const namespaceParam = parsed.searchParams.get("namespace");
1267
+ const namespace = this.resolveNamespace(
1268
+ req,
1269
+ namespaceParam && namespaceParam.length > 0 ? namespaceParam : undefined,
1270
+ );
1271
+ try {
1272
+ const snapshot = await this.service.graphSnapshot(
1273
+ {
1274
+ namespace,
1275
+ ...(limit !== undefined ? { limit } : {}),
1276
+ ...(since !== undefined ? { since } : {}),
1277
+ ...(focusNodeId !== undefined ? { focusNodeId } : {}),
1278
+ ...(categories !== undefined ? { categories } : {}),
1279
+ },
1280
+ this.resolveRequestPrincipal(req),
1281
+ );
1282
+ this.respondJson(res, 200, snapshot);
1283
+ } catch (err) {
1284
+ const message = err instanceof Error ? err.message : String(err);
1285
+ if (message.startsWith("graphSnapshot:")) {
1286
+ this.respondJson(res, 400, {
1287
+ error: "invalid_request",
1288
+ code: "invalid_request",
1289
+ message,
1290
+ });
1291
+ return;
1292
+ }
1293
+ throw err;
1294
+ }
1295
+ return;
1296
+ }
1297
+
1298
+ if (req.method === "POST" && pathname === "/engram/v1/contradiction-scan") {
1299
+ const body = await this.readJsonBody(req) as Record<string, unknown>;
1300
+ const { runContradictionScan } = await import("./contradiction/contradiction-scan.js");
1301
+ const principal = this.resolveRequestPrincipal(req);
1302
+ const result = await runContradictionScan({
1303
+ storage: this.service.storageRef,
1304
+ config: this.service.configRef,
1305
+ memoryDir: this.service.memoryDir,
1306
+ embeddingLookupFactory: this.service.embeddingLookupFactoryRef,
1307
+ storageForNamespace: (namespace) =>
1308
+ this.service.getWritableStorageForNamespace(namespace, principal),
1309
+ localLlm: this.service.localLlmRef,
1310
+ fallbackLlm: this.service.fallbackLlmRef,
1311
+ namespace: typeof body.namespace === "string" ? body.namespace : undefined,
1312
+ });
1313
+ this.respondJson(res, 200, result);
1314
+ return;
1315
+ }
1316
+
1317
+ // ── Graph mutation event stream (issue #691 PR 5/5) ──────────────────────
1318
+ //
1319
+ // GET /engram/v1/graph/events
1320
+ //
1321
+ // Server-Sent Events stream that emits graph mutation events in real time.
1322
+ // Event types: node-added, node-updated, edge-added, edge-updated, edge-removed.
1323
+ //
1324
+ // Auth: same Bearer token scheme as every other endpoint (checked above).
1325
+ //
1326
+ // The SSE handler subscribes to the in-process graph event bus for the
1327
+ // resolved memory dir. Events are batched within a 200 ms window so a
1328
+ // burst of writes (e.g. extraction of a large turn) doesn't overwhelm
1329
+ // the admin UI canvas with individual re-renders.
1330
+ //
1331
+ // The client receives a `data: <json>\n\n` line per batch. Each batch
1332
+ // payload is { events: GraphEvent[] }.
1333
+ //
1334
+ // The stream sends a heartbeat `data: {"type":"heartbeat"}\n\n` every
1335
+ // 25 s so load balancers and proxies don't time out idle connections.
1336
+ if (req.method === "GET" && pathname === "/engram/v1/graph/events") {
1337
+ await this.handleGraphEventsSSE(req, res);
1338
+ return;
1339
+ }
1340
+
1341
+ // ── Peer Registry endpoints (issue #679) ─────────────────────────────────
1342
+ // GET /engram/v1/console/state — operator console engine-state snapshot (issue #688 PR 2/3).
1343
+ // Read-only; namespace-aware via resolveRequestPrincipal so cross-tenant
1344
+ // reads are not possible (CLAUDE.md rule 42).
1345
+ if (req.method === "GET" && pathname === "/engram/v1/console/state") {
1346
+ const namespace = parsed.searchParams.get("namespace") ?? undefined;
1347
+ const snapshot = await this.service.consoleState(namespace, this.resolveRequestPrincipal(req));
1348
+ this.respondJson(res, 200, snapshot);
1349
+ return;
1350
+ }
1351
+
1352
+ // GET /engram/v1/peers — list all peers
1353
+ // GET /engram/v1/peers/:id — get one peer
1354
+ // PUT /engram/v1/peers/:id — upsert (create/update)
1355
+ // DELETE /engram/v1/peers/:id — delete identity only (idempotent)
1356
+ // DELETE /engram/v1/peers/:id?forget=true — destructive full purge (issue #679 completion)
1357
+ // GET /engram/v1/peers/:id/profile — get peer profile
1358
+ if (req.method === "GET" && pathname === "/engram/v1/peers") {
1359
+ const result = await this.service.peerList();
1360
+ this.respondJson(res, 200, result);
1361
+ return;
1362
+ }
1363
+
1364
+ const peerProfileMatch = /^\/engram\/v1\/peers\/([^/]+)\/profile$/.exec(pathname);
1365
+ if (peerProfileMatch) {
1366
+ if (req.method !== "GET") {
1367
+ this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
1368
+ return;
1369
+ }
1370
+ const peerId = decodePeerIdSegment(peerProfileMatch[1] ?? "");
1371
+ const result = await this.service.peerProfileGet(peerId);
1372
+ if (!result.found) {
1373
+ this.respondJson(res, 404, { error: "peer_profile_not_found", code: "peer_profile_not_found" });
1374
+ return;
1375
+ }
1376
+ this.respondJson(res, 200, result);
1377
+ return;
1378
+ }
1379
+
1380
+ const peerIdMatch = /^\/engram\/v1\/peers\/([^/]+)$/.exec(pathname);
1381
+ if (peerIdMatch) {
1382
+ const peerId = decodePeerIdSegment(peerIdMatch[1] ?? "");
1383
+
1384
+ if (req.method === "GET") {
1385
+ const result = await this.service.peerGet(peerId);
1386
+ if (!result.found) {
1387
+ this.respondJson(res, 404, { error: "peer_not_found", code: "peer_not_found" });
1388
+ return;
1389
+ }
1390
+ this.respondJson(res, 200, result);
1391
+ return;
1392
+ }
1393
+
1394
+ if (req.method === "PUT") {
1395
+ const body = await this.readJsonBody(req) as Record<string, unknown>;
1396
+ // Reject malformed types up front rather than silently dropping them
1397
+ // to undefined and letting peerSet fall back to defaults
1398
+ // (CLAUDE.md rule 51: no silent defaults on bad input).
1399
+ if ("kind" in body && body.kind !== undefined && typeof body.kind !== "string") {
1400
+ throw new EngramAccessInputError("kind must be a string when provided");
1401
+ }
1402
+ if (
1403
+ "displayName" in body &&
1404
+ body.displayName !== undefined &&
1405
+ typeof body.displayName !== "string"
1406
+ ) {
1407
+ throw new EngramAccessInputError("displayName must be a string when provided");
1408
+ }
1409
+ if ("notes" in body && body.notes !== undefined && typeof body.notes !== "string") {
1410
+ throw new EngramAccessInputError("notes must be a string when provided");
1411
+ }
1412
+ const result = await this.service.peerSet({
1413
+ id: peerId,
1414
+ kind: typeof body.kind === "string" ? body.kind : undefined,
1415
+ displayName: typeof body.displayName === "string" ? body.displayName : undefined,
1416
+ notes: typeof body.notes === "string" ? body.notes : undefined,
1417
+ });
1418
+ this.respondJson(res, result.created ? 201 : 200, result);
1419
+ return;
1420
+ }
1421
+
1422
+ if (req.method === "DELETE") {
1423
+ // `?forget=true` triggers the destructive full-purge path (issue #679
1424
+ // completion). The caller must also pass `confirm=yes` in the request
1425
+ // body; absent confirmation yields 400. Plain DELETE (no ?forget) keeps
1426
+ // the existing soft-delete behaviour (identity.md only).
1427
+ const forgetParam = parsed.searchParams.get("forget");
1428
+ if (forgetParam === "true") {
1429
+ const body = await this.readJsonBody(req) as Record<string, unknown>;
1430
+ const confirm = typeof body.confirm === "string" ? body.confirm : "";
1431
+ if (confirm !== "yes") {
1432
+ this.respondJson(res, 400, {
1433
+ error: "confirm_required",
1434
+ code: "confirm_required",
1435
+ message: "DELETE ?forget=true requires { confirm: 'yes' } in the request body",
1436
+ });
1437
+ return;
1438
+ }
1439
+ const result = await this.service.peerForget(peerId, { confirm: "yes" });
1440
+ this.respondJson(res, 200, result);
1441
+ return;
1442
+ }
1443
+ const result = await this.service.peerDelete(peerId);
1444
+ this.respondJson(res, 200, result);
1445
+ return;
1446
+ }
1447
+
1448
+ this.respondJson(res, 405, { error: "method_not_allowed", code: "method_not_allowed" });
1449
+ return;
1450
+ }
1451
+
1452
+ // ── Dreams telemetry (issue #678 PR 3+4) ──────────────────────────────────
1453
+
1454
+ if (req.method === "GET" && pathname === "/engram/v1/dreams/status") {
1455
+ const { normalizeDreamsStatusWindowHours } = await import("./maintenance/dreams-ledger.js");
1456
+ const windowHoursRaw = parsed.searchParams.get("windowHours");
1457
+ let windowHours: number;
1458
+ try {
1459
+ windowHours = normalizeDreamsStatusWindowHours(
1460
+ windowHoursRaw !== null ? Number(windowHoursRaw) : undefined,
1461
+ );
1462
+ } catch {
1463
+ this.respondJson(res, 400, { error: "windowHours must be a positive integer" });
1464
+ return;
1465
+ }
1466
+ const namespaceParam = parsed.searchParams.get("namespace");
1467
+ const namespace = namespaceParam && namespaceParam.length > 0 ? namespaceParam : undefined;
1468
+ const result = await this.service.dreamsStatus({
1469
+ windowHours,
1470
+ namespace,
1471
+ principal: this.resolveRequestPrincipal(req),
1472
+ });
1473
+ this.respondJson(res, 200, result);
1474
+ return;
1475
+ }
1476
+
1477
+ if (req.method === "POST" && pathname === "/engram/v1/dreams/run") {
1478
+ const body = await this.readJsonBody(req) as Record<string, unknown>;
1479
+ const VALID_PHASES = ["lightSleep", "rem", "deepSleep"] as const;
1480
+ const phase = typeof body.phase === "string" ? body.phase : undefined;
1481
+ if (!phase || !(VALID_PHASES as readonly string[]).includes(phase)) {
1482
+ this.respondJson(res, 400, {
1483
+ error: `phase is required and must be one of: ${VALID_PHASES.join(", ")}`,
1484
+ });
1485
+ return;
1486
+ }
1487
+ if (
1488
+ "dryRun" in body &&
1489
+ body.dryRun !== undefined &&
1490
+ typeof body.dryRun !== "boolean"
1491
+ ) {
1492
+ this.respondJson(res, 400, {
1493
+ error: "dryRun must be a boolean when provided",
1494
+ });
1495
+ return;
1496
+ }
1497
+ if (
1498
+ "namespace" in body &&
1499
+ body.namespace !== undefined &&
1500
+ typeof body.namespace !== "string"
1501
+ ) {
1502
+ this.respondJson(res, 400, {
1503
+ error: "namespace must be a string when provided",
1504
+ });
1505
+ return;
1506
+ }
1507
+ const dryRun = body.dryRun === true;
1508
+ const namespace =
1509
+ typeof body.namespace === "string" ? body.namespace : undefined;
1510
+ if (!dryRun) {
1511
+ this.ensureWriteRateLimitAvailable();
1512
+ }
1513
+ const result = await this.service.dreamsRun({
1514
+ phase: phase as import("./types.js").DreamsPhase,
1515
+ dryRun,
1516
+ namespace,
1517
+ authenticatedPrincipal: this.resolveRequestPrincipal(req),
1518
+ });
1519
+ if (this.shouldCountWriteRateLimit(result as { dryRun?: boolean; idempotencyReplay?: boolean })) {
1520
+ this.recordWriteRateLimitHit();
1521
+ }
1522
+ this.respondJson(res, 200, result);
1523
+ return;
1524
+ }
1525
+
1526
+ this.respondJson(res, 404, { error: "not_found", code: "not_found" });
1527
+ }
1528
+
1529
+ /**
1530
+ * SSE handler for /engram/v1/graph/events.
1531
+ *
1532
+ * Lifecycle:
1533
+ * 1. Write SSE headers (Content-Type: text/event-stream).
1534
+ * 2. Register this response in `sseClients`.
1535
+ * 3. Resolve the namespace from the request and subscribe to THAT
1536
+ * namespace's graph event bus (Codex P1: in multi-namespace
1537
+ * deployments each namespace has its own bus keyed by its storage
1538
+ * dir — subscribing to the global root leaks events across tenants).
1539
+ * 4. On each event, add to a 200 ms batch; flush batch as a single SSE frame.
1540
+ * 5. Send heartbeat every 25 s.
1541
+ * 6. On client disconnect (req "close"), clean up timers and unsubscribe.
1542
+ * 7. Register the cleanup callback in `sseCleanupFns` so `stop()` can
1543
+ * release the heartbeat interval and bus subscription even when the
1544
+ * client never disconnects (Cursor review thread `access-http.ts:232`).
1545
+ */
1546
+ private async handleGraphEventsSSE(req: IncomingMessage, res: ServerResponse): Promise<void> {
1547
+ // Resolve namespace from the ?namespace= query parameter (same pattern
1548
+ // as graphSnapshot and other read endpoints). Falls back to the
1549
+ // default namespace when absent.
1550
+ const parsed = new URL(req.url ?? "/", `http://${hostToUrlAuthority(this.host)}`);
1551
+ const namespaceParam = parsed.searchParams.get("namespace");
1552
+ const namespace = namespaceParam && namespaceParam.length > 0 ? namespaceParam : undefined;
1553
+ // Resolve to the per-namespace storage directory so the bus subscription
1554
+ // is scoped to the correct tenant (CLAUDE.md rule 42).
1555
+ // Pass the request principal so namespace ACL is enforced — without it,
1556
+ // resolveReadableNamespace throws when namespacesEnabled=true (Cursor
1557
+ // thread PRRT_kwDORJXyws59snoR / Codex thread PRRT_kwDORJXyws59soGJ).
1558
+ const principal = this.resolveRequestPrincipal(req);
1559
+ const memoryDir = await this.service.getMemoryDirForNamespace(namespace, principal);
1560
+
1561
+ res.writeHead(200, {
1562
+ "content-type": "text/event-stream; charset=utf-8",
1563
+ "cache-control": "no-cache, no-store, must-revalidate",
1564
+ "connection": "keep-alive",
1565
+ "x-accel-buffering": "no", // prevent nginx buffering
1566
+ "transfer-encoding": "chunked",
1567
+ });
1568
+
1569
+ // Send initial "connected" frame so the client knows the stream is live.
1570
+ const writeSSE = (payload: unknown): void => {
1571
+ try {
1572
+ res.write(`data: ${JSON.stringify(payload)}\n\n`);
1573
+ } catch {
1574
+ // client already gone — cleanup will fire via "close"
1575
+ }
1576
+ };
1577
+
1578
+ writeSSE({ type: "connected" });
1579
+
1580
+ this.sseClients.add(res);
1581
+
1582
+ // --- 200 ms batch throttle -----------------------------------------------
1583
+ const flushBatch = (): void => {
1584
+ const batch = this.ssePendingBatches.get(res);
1585
+ if (!batch || batch.length === 0) return;
1586
+ this.ssePendingBatches.delete(res);
1587
+ this.sseBatchTimers.delete(res);
1588
+ writeSSE({ type: "batch", events: batch });
1589
+ };
1590
+
1591
+ const unsubscribe = subscribeGraphEvents(memoryDir, (event: GraphEvent) => {
1592
+ let batch = this.ssePendingBatches.get(res);
1593
+ if (!batch) {
1594
+ batch = [];
1595
+ this.ssePendingBatches.set(res, batch);
1596
+ }
1597
+ batch.push(event);
1598
+ if (!this.sseBatchTimers.has(res)) {
1599
+ this.sseBatchTimers.set(res, setTimeout(flushBatch, 200));
1600
+ }
1601
+ });
1602
+
1603
+ // --- 25 s heartbeat -------------------------------------------------------
1604
+ const heartbeatInterval = setInterval(() => {
1605
+ writeSSE({ type: "heartbeat" });
1606
+ }, 25_000);
1607
+
1608
+ // --- Cleanup on client disconnect -----------------------------------------
1609
+ const cleanup = (): void => {
1610
+ clearInterval(heartbeatInterval);
1611
+ const timer = this.sseBatchTimers.get(res);
1612
+ if (timer !== undefined) {
1613
+ clearTimeout(timer);
1614
+ this.sseBatchTimers.delete(res);
1615
+ }
1616
+ this.ssePendingBatches.delete(res);
1617
+ unsubscribe();
1618
+ this.sseClients.delete(res);
1619
+ this.sseCleanupFns.delete(cleanup);
1620
+ try { res.end(); } catch { /* ignore */ }
1621
+ };
1622
+
1623
+ // Register so stop() can invoke cleanup even when the client is still
1624
+ // connected (releases the heartbeat interval and bus subscription
1625
+ // before the HTTP server is torn down).
1626
+ this.sseCleanupFns.add(cleanup);
1627
+
1628
+ req.once("close", cleanup);
1629
+ req.once("error", cleanup);
1630
+ }
1631
+
1632
+ private async handleMcpRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
1633
+ const body = await this.readJsonBody(req);
1634
+ const request = body as {
1635
+ jsonrpc?: string;
1636
+ id?: string | number | null;
1637
+ method?: string;
1638
+ params?: Record<string, unknown>;
1639
+ };
1640
+
1641
+ // Enforce write rate limiting for MCP tool calls that mutate state,
1642
+ // matching the same protection applied to the REST write endpoints.
1643
+ // Pre-check ensures capacity; post-check skips counting dry runs and
1644
+ // idempotency replays, consistent with the REST handlers.
1645
+ const toolName = typeof request.params?.name === "string" ? request.params.name : "";
1646
+ const toolArgs = request.params?.arguments;
1647
+ const dreamsRunDryRun =
1648
+ (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run") &&
1649
+ toolArgs !== null &&
1650
+ typeof toolArgs === "object" &&
1651
+ !Array.isArray(toolArgs) &&
1652
+ (toolArgs as { dryRun?: unknown }).dryRun === true;
1653
+ const memoryActionApplyDryRun =
1654
+ (toolName === "engram.memory_action_apply" || toolName === "remnic.memory_action_apply") &&
1655
+ toolArgs !== null &&
1656
+ typeof toolArgs === "object" &&
1657
+ !Array.isArray(toolArgs) &&
1658
+ (toolArgs as { dryRun?: unknown }).dryRun === true;
1659
+ const isMcpWrite =
1660
+ request.method === "tools/call" &&
1661
+ (
1662
+ toolName === "engram.memory_store" ||
1663
+ toolName === "remnic.memory_store" ||
1664
+ toolName === "engram.suggestion_submit" ||
1665
+ toolName === "remnic.suggestion_submit" ||
1666
+ toolName === "engram.observe" ||
1667
+ toolName === "remnic.observe" ||
1668
+ toolName === "engram.lcm_compaction_flush" ||
1669
+ toolName === "remnic.lcm_compaction_flush" ||
1670
+ toolName === "engram.lcm_compaction_record" ||
1671
+ toolName === "remnic.lcm_compaction_record" ||
1672
+ toolName === "engram.capsule_export" ||
1673
+ toolName === "remnic.capsule_export" ||
1674
+ toolName === "engram.capsule_import" ||
1675
+ toolName === "remnic.capsule_import" ||
1676
+ (
1677
+ !dreamsRunDryRun &&
1678
+ (toolName === "engram.dreams_run" || toolName === "remnic.dreams_run")
1679
+ ) ||
1680
+ (
1681
+ !memoryActionApplyDryRun &&
1682
+ (
1683
+ toolName === "engram.memory_action_apply" ||
1684
+ toolName === "remnic.memory_action_apply"
1685
+ )
1686
+ )
1687
+ );
1688
+ if (isMcpWrite) {
1689
+ this.ensureWriteRateLimitAvailable();
1690
+ }
1691
+
1692
+ const sessionId = (() => {
1693
+ const raw = req.headers["mcp-session-id"];
1694
+ return typeof raw === "string" ? raw.trim() : undefined;
1695
+ })();
1696
+ const mcpCorrelationId = correlationIdStore.getStore() ?? randomUUID();
1697
+ const response = await this.mcpServer.handleRequest(request, {
1698
+ principalOverride: this.resolveRequestPrincipal(req),
1699
+ sessionId,
1700
+ correlationId: mcpCorrelationId,
1701
+ });
1702
+
1703
+ if (isMcpWrite && response !== null) {
1704
+ const result = (response as Record<string, unknown>).result as Record<string, unknown> | undefined;
1705
+ const isError = result?.isError === true;
1706
+ const structured = result?.structuredContent as { dryRun?: boolean; idempotencyReplay?: boolean } | undefined;
1707
+ if (!isError && structured && this.shouldCountWriteRateLimit(structured)) {
1708
+ this.recordWriteRateLimitHit();
1709
+ }
1710
+ }
1711
+ if (response === null) {
1712
+ res.statusCode = 202;
1713
+ res.end();
1714
+ return;
1715
+ }
1716
+ // If this was an initialize response, pop the session ID keyed by
1717
+ // correlation ID (unique per HTTP request, not client-chosen JSON-RPC id).
1718
+ const assignedSessionId = this.mcpServer.popInitSessionId(mcpCorrelationId);
1719
+ if (assignedSessionId) {
1720
+ res.setHeader("mcp-session-id", assignedSessionId);
1721
+ }
1722
+ this.respondJson(res, 200, response);
1723
+ }
1724
+
1725
+ private respondJson(res: ServerResponse, status: number, payload: unknown): void {
1726
+ const body = JSON.stringify(payload, null, 2);
1727
+ res.statusCode = status;
1728
+ res.setHeader("content-type", "application/json; charset=utf-8");
1729
+ res.setHeader("content-length", String(Buffer.byteLength(body)));
1730
+ const cid = correlationIdStore.getStore();
1731
+ if (cid) {
1732
+ res.setHeader("x-request-id", cid);
1733
+ }
1734
+ res.end(body);
1735
+ }
1736
+
1737
+ private async handleAdminConsole(
1738
+ req: IncomingMessage,
1739
+ res: ServerResponse,
1740
+ pathname: string,
1741
+ ): Promise<boolean> {
1742
+ if (req.method !== "GET") return false;
1743
+ if (pathname === "/remnic/ui" || pathname === "/engram/ui") {
1744
+ res.statusCode = 301;
1745
+ res.setHeader("location", pathname + "/");
1746
+ res.end();
1747
+ return true;
1748
+ }
1749
+ if (pathname === "/remnic/ui/" || pathname === "/engram/ui/") {
1750
+ await this.respondStatic(res, path.join(this.adminConsolePublicDir, "index.html"), "text/html; charset=utf-8");
1751
+ return true;
1752
+ }
1753
+ if (pathname === "/remnic/ui/app.js" || pathname === "/engram/ui/app.js") {
1754
+ await this.respondStatic(res, path.join(this.adminConsolePublicDir, "app.js"), "application/javascript; charset=utf-8");
1755
+ return true;
1756
+ }
1757
+ return false;
1758
+ }
1759
+
1760
+ private async respondStatic(res: ServerResponse, filePath: string, contentType: string): Promise<void> {
1761
+ try {
1762
+ const body = await readFile(filePath, "utf-8");
1763
+ res.statusCode = 200;
1764
+ res.setHeader("content-type", contentType);
1765
+ res.setHeader("content-length", String(Buffer.byteLength(body)));
1766
+ res.end(body);
1767
+ } catch {
1768
+ this.respondJson(res, 404, { error: "not_found" });
1769
+ }
1770
+ }
1771
+
1772
+ private async readJsonBody(req: IncomingMessage): Promise<Record<string, unknown>> {
1773
+ const chunks: Buffer[] = [];
1774
+ let total = 0;
1775
+ for await (const chunk of req) {
1776
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1777
+ total += buffer.length;
1778
+ if (total > this.maxBodyBytes) {
1779
+ throw new HttpError(413, "request_body_too_large", "request_body_too_large");
1780
+ }
1781
+ chunks.push(buffer);
1782
+ }
1783
+ if (chunks.length === 0) return {};
1784
+ const raw = Buffer.concat(chunks).toString("utf-8").trim();
1785
+ if (raw.length === 0) return {};
1786
+ let parsed: unknown;
1787
+ try {
1788
+ parsed = JSON.parse(raw);
1789
+ } catch {
1790
+ throw new HttpError(400, "invalid_json", "invalid_json");
1791
+ }
1792
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1793
+ throw new HttpError(400, "invalid_json_object", "invalid_json_object");
1794
+ }
1795
+ return parsed as Record<string, unknown>;
1796
+ }
1797
+
1798
+ private async readValidatedBody<S extends SchemaName>(req: IncomingMessage, schemaName: S): Promise<SchemaTypeFor<S>> {
1799
+ const raw = await this.readJsonBody(req);
1800
+ const result = validateRequest(schemaName, raw);
1801
+ if (!result.success) {
1802
+ throw new HttpError(400, result.error.error, "validation_error", result.error.details);
1803
+ }
1804
+ return result.data as SchemaTypeFor<S>;
1805
+ }
1806
+
1807
+ private isAuthorized(req: IncomingMessage, pathname?: string): boolean {
1808
+ if (!this.authToken && this.authTokens.length === 0 && !this.authTokensGetter) return false;
1809
+ // Primary path: Authorization: Bearer <token> header.
1810
+ const raw = req.headers.authorization;
1811
+ let candidate: string | null = null;
1812
+ if (raw) {
1813
+ const separator = raw.indexOf(" ");
1814
+ if (separator > 0) {
1815
+ const scheme = raw.slice(0, separator).toLowerCase();
1816
+ if (scheme === "bearer") {
1817
+ candidate = raw.slice(separator + 1).trim();
1818
+ }
1819
+ }
1820
+ }
1821
+ // Fallback: ?token= query parameter — ONLY accepted for the SSE
1822
+ // endpoint (/engram/v1/graph/events). EventSource cannot set request
1823
+ // headers, so SSE clients must pass the token via the query string.
1824
+ // Allowing this fallback on every endpoint would let a CSRF attacker
1825
+ // embed a credentialed URL anywhere — restricting it to SSE limits the
1826
+ // attack surface (Codex P2 review thread `access-http.ts:1406`; Cursor
1827
+ // review thread `access-http.ts:1412`). Authorization header always
1828
+ // wins; timing-safe compare used below.
1829
+ if (!candidate && pathname === "/engram/v1/graph/events") {
1830
+ try {
1831
+ const parsed = new URL(req.url ?? "/", `http://${hostToUrlAuthority(this.host)}`);
1832
+ const queryToken = parsed.searchParams.get("token");
1833
+ if (queryToken && queryToken.length > 0) {
1834
+ candidate = queryToken;
1835
+ }
1836
+ } catch {
1837
+ // Malformed URL — don't authenticate
1838
+ }
1839
+ }
1840
+ if (!candidate) return false;
1841
+ const token = candidate;
1842
+ // Check primary token
1843
+ if (this.authToken && this.timingSafeStringEqual(token, this.authToken)) return true;
1844
+ // Check static multi-connector tokens
1845
+ for (const valid of this.authTokens) {
1846
+ if (this.timingSafeStringEqual(token, valid)) return true;
1847
+ }
1848
+ // Check dynamic tokens (reloaded per request for generate/revoke without restart)
1849
+ if (this.authTokensGetter) {
1850
+ for (const valid of this.authTokensGetter()) {
1851
+ if (this.timingSafeStringEqual(token, valid)) return true;
1852
+ }
1853
+ }
1854
+ return false;
1855
+ }
1856
+
1857
+ private timingSafeStringEqual(a: string, b: string): boolean {
1858
+ const left = this.encodeSecret(a);
1859
+ const right = this.encodeSecret(b);
1860
+ if (!left || !right) return false;
1861
+ return timingSafeEqual(left, right);
1862
+ }
1863
+
1864
+ private encodeSecret(value: string): Buffer | null {
1865
+ const encoded = Buffer.from(value, "utf-8");
1866
+ if (encoded.length > 1024) return null;
1867
+ const out = Buffer.alloc(2 + 1024);
1868
+ out.writeUInt16BE(encoded.length, 0);
1869
+ encoded.copy(out, 2);
1870
+ return out;
1871
+ }
1872
+
1873
+ private writeResponseStatus(response: { dryRun: boolean; status: string }): number {
1874
+ if (response.dryRun === true) return 200;
1875
+ if (response.status === "stored" || response.status === "queued_for_review") return 201;
1876
+ return 200;
1877
+ }
1878
+
1879
+ private ensureWriteRateLimitAvailable(): void {
1880
+ const now = Date.now();
1881
+ while (
1882
+ this.writeRequestTimestamps.length > 0 &&
1883
+ now - (this.writeRequestTimestamps[0] ?? 0) > WRITE_RATE_LIMIT_WINDOW_MS
1884
+ ) {
1885
+ this.writeRequestTimestamps.shift();
1886
+ }
1887
+ if (this.writeRequestTimestamps.length >= WRITE_RATE_LIMIT_MAX_REQUESTS) {
1888
+ throw new HttpError(429, "write_rate_limited", "write_rate_limited");
1889
+ }
1890
+ }
1891
+
1892
+ private recordWriteRateLimitHit(): void {
1893
+ this.writeRequestTimestamps.push(Date.now());
1894
+ }
1895
+
1896
+ private shouldCountWriteRateLimit(response: { dryRun?: boolean; idempotencyReplay?: boolean }): boolean {
1897
+ return response.dryRun !== true && response.idempotencyReplay !== true;
1898
+ }
1899
+ }