@remnic/core 9.7.15 → 9.8.0

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 (929) hide show
  1. package/dist/access-admin-ops-surface.d.ts +13 -6
  2. package/dist/access-admin-ops-surface.js +85 -71
  3. package/dist/access-audit.js +2 -2
  4. package/dist/access-boundary.d.ts +35 -17
  5. package/dist/access-boundary.js +86 -74
  6. package/dist/access-citation.d.ts +28 -0
  7. package/dist/access-citation.js +9 -0
  8. package/dist/access-cli.js +194 -178
  9. package/dist/access-cli.js.map +1 -1
  10. package/dist/access-errors.d.ts +16 -1
  11. package/dist/access-errors.js +7 -3
  12. package/dist/access-http.d.ts +25 -19
  13. package/dist/access-http.js +100 -87
  14. package/dist/access-identity-continuity-surface.d.ts +13 -8
  15. package/dist/access-identity-continuity-surface.js +85 -71
  16. package/dist/access-lcm-surface.d.ts +16 -9
  17. package/dist/access-lcm-surface.js +85 -71
  18. package/dist/access-mcp.d.ts +23 -16
  19. package/dist/access-mcp.js +96 -84
  20. package/dist/access-memory-search-fanout.d.ts +93 -0
  21. package/dist/access-memory-search-fanout.js +18 -0
  22. package/dist/access-observe-write-surface.d.ts +26 -8
  23. package/dist/access-observe-write-surface.js +85 -71
  24. package/dist/access-operations-batch.js +92 -80
  25. package/dist/access-operations.d.ts +29 -22
  26. package/dist/access-operations.js +94 -82
  27. package/dist/access-recall-concurrency.d.ts +250 -0
  28. package/dist/access-recall-concurrency.js +157 -0
  29. package/dist/access-recall-response.d.ts +130 -0
  30. package/dist/access-recall-response.js +147 -0
  31. package/dist/access-recall-surface.d.ts +15 -7
  32. package/dist/access-recall-surface.js +85 -71
  33. package/dist/access-schema.d.ts +98 -98
  34. package/dist/access-schema.js +8 -6
  35. package/dist/{access-service-DpcYZ9Nl.d.ts → access-service-DEL4qCrc.d.ts} +25 -32
  36. package/dist/access-service.d.ts +13 -6
  37. package/dist/access-service.js +89 -72
  38. package/dist/access-surface-catalog.d.ts +23 -16
  39. package/dist/access-surface-catalog.js +1 -0
  40. package/dist/access-surface-catalog.js.map +1 -1
  41. package/dist/access-token-capabilities.d.ts +2 -1
  42. package/dist/access-token-capabilities.js +4 -2
  43. package/dist/action-confidence.d.ts +3 -1
  44. package/dist/active-memory-bridge.d.ts +3 -1
  45. package/dist/active-memory-bridge.js +4 -4
  46. package/dist/active-recall.d.ts +4 -1
  47. package/dist/active-recall.js +23 -13
  48. package/dist/active-recall.js.map +1 -1
  49. package/dist/adapters/index.js +4 -4
  50. package/dist/adapters/registry.js +2 -2
  51. package/dist/{auto-sync-AAMTVZMK.js → auto-sync-UZG3XQZ5.js} +14 -8
  52. package/dist/{auto-sync-AAMTVZMK.js.map → auto-sync-UZG3XQZ5.js.map} +1 -1
  53. package/dist/behavior-learner.d.ts +3 -1
  54. package/dist/behavior-signals.d.ts +3 -1
  55. package/dist/bootstrap.d.ts +12 -7
  56. package/dist/bootstrap.js +2 -2
  57. package/dist/bounded-jsonl-state.d.ts +54 -0
  58. package/dist/bounded-jsonl-state.js +10 -0
  59. package/dist/boxes.js +2 -2
  60. package/dist/briefing.d.ts +5 -2
  61. package/dist/briefing.js +34 -24
  62. package/dist/buffer-surprise-report.d.ts +3 -1
  63. package/dist/buffer.d.ts +60 -2
  64. package/dist/buffer.js +3 -3
  65. package/dist/bulk-import/index.d.ts +5 -3
  66. package/dist/calibration.d.ts +3 -1
  67. package/dist/calibration.js +6 -6
  68. package/dist/capabilities.d.ts +7 -7
  69. package/dist/capabilities.js +1 -1
  70. package/dist/{capsule-crypto-CZJSLEFG.js → capsule-crypto-YO5QJ6L3.js} +3 -3
  71. package/dist/capsule-crypto-YO5QJ6L3.js.map +1 -0
  72. package/dist/{catalog-C9444P10.d.ts → catalog-BWqHb5HT.d.ts} +1 -1
  73. package/dist/causal-behavior.d.ts +3 -1
  74. package/dist/causal-behavior.js +6 -6
  75. package/dist/causal-chain.js +4 -4
  76. package/dist/causal-consolidation.d.ts +3 -1
  77. package/dist/causal-consolidation.js +43 -34
  78. package/dist/causal-consolidation.js.map +1 -1
  79. package/dist/causal-retrieval.js +6 -6
  80. package/dist/causal-trajectory-graph.d.ts +3 -1
  81. package/dist/causal-trajectory-graph.js +1 -1
  82. package/dist/causal-trajectory.js +1 -1
  83. package/dist/{chunk-A3GOMRJG.js → chunk-23L3IXFD.js} +2 -2
  84. package/dist/chunk-2CGLBUW3.js +15 -0
  85. package/dist/chunk-2CGLBUW3.js.map +1 -0
  86. package/dist/{chunk-MRHFJV44.js → chunk-2DQ2XREU.js} +2 -2
  87. package/dist/{chunk-QKQYXZVS.js → chunk-2EJOEICD.js} +1819 -1481
  88. package/dist/chunk-2EJOEICD.js.map +1 -0
  89. package/dist/{chunk-L2B3EE7B.js → chunk-2QMKMVHH.js} +2 -2
  90. package/dist/{chunk-VJVNLNDQ.js → chunk-2UFO7X4Q.js} +7 -7
  91. package/dist/{chunk-2MR3MFQB.js → chunk-2VEOKUPP.js} +3 -3
  92. package/dist/chunk-2Z24E5W2.js +22 -0
  93. package/dist/chunk-2Z24E5W2.js.map +1 -0
  94. package/dist/{chunk-RHOZ5LLO.js → chunk-347WD2LE.js} +2 -2
  95. package/dist/{chunk-HDTQVNWD.js → chunk-3QAH2S33.js} +3 -3
  96. package/dist/{chunk-37CTGERM.js → chunk-3WCASNQZ.js} +12 -1
  97. package/dist/chunk-3WCASNQZ.js.map +1 -0
  98. package/dist/{chunk-LRNMRXJO.js → chunk-3Y224NYC.js} +3 -3
  99. package/dist/{chunk-3IY64MV6.js → chunk-44XZ75OQ.js} +44 -10
  100. package/dist/chunk-44XZ75OQ.js.map +1 -0
  101. package/dist/{chunk-VFSP73LP.js → chunk-45GF7Y3V.js} +2 -2
  102. package/dist/{chunk-K7LOWSLU.js → chunk-4BJQODZF.js} +2479 -1520
  103. package/dist/chunk-4BJQODZF.js.map +1 -0
  104. package/dist/{chunk-JREB6UFH.js → chunk-57OWKFUQ.js} +17 -6
  105. package/dist/chunk-57OWKFUQ.js.map +1 -0
  106. package/dist/{chunk-OEHCMALU.js → chunk-5I4M26Q7.js} +5 -5
  107. package/dist/{chunk-2AZXIZEN.js → chunk-5Q7EVV4X.js} +6 -6
  108. package/dist/{chunk-LC7OODWZ.js → chunk-5SB7SC2G.js} +80 -49
  109. package/dist/chunk-5SB7SC2G.js.map +1 -0
  110. package/dist/{chunk-PUBTHW5O.js → chunk-6DRPPQEJ.js} +2 -2
  111. package/dist/{chunk-NWTGD4AN.js → chunk-6KIQT7RG.js} +10 -9
  112. package/dist/chunk-6KIQT7RG.js.map +1 -0
  113. package/dist/{chunk-FDCTQWLW.js → chunk-6O22TZSB.js} +3 -3
  114. package/dist/{chunk-ZQ7UBUZ6.js → chunk-6QYST5XN.js} +6 -6
  115. package/dist/{chunk-W4PGTAS2.js → chunk-6WAFYNVT.js} +3 -3
  116. package/dist/chunk-6ZMAMSHS.js +20 -0
  117. package/dist/chunk-6ZMAMSHS.js.map +1 -0
  118. package/dist/{chunk-R5VAVVHP.js → chunk-72A4L5JJ.js} +2 -2
  119. package/dist/{chunk-X64YLC24.js → chunk-72VAXGEI.js} +5 -13
  120. package/dist/chunk-72VAXGEI.js.map +1 -0
  121. package/dist/{chunk-IKQTFQMI.js → chunk-755Q3DGF.js} +22 -3
  122. package/dist/chunk-755Q3DGF.js.map +1 -0
  123. package/dist/{chunk-OVJ2HIPE.js → chunk-76GLCM57.js} +158 -125
  124. package/dist/chunk-76GLCM57.js.map +1 -0
  125. package/dist/{chunk-D7FOKIY5.js → chunk-7AMFUATK.js} +12 -9
  126. package/dist/{chunk-D7FOKIY5.js.map → chunk-7AMFUATK.js.map} +1 -1
  127. package/dist/{chunk-BFVEEB7W.js → chunk-7ON2JW73.js} +57 -22
  128. package/dist/chunk-7ON2JW73.js.map +1 -0
  129. package/dist/chunk-7RNBVZZO.js +270 -0
  130. package/dist/chunk-7RNBVZZO.js.map +1 -0
  131. package/dist/{chunk-3FAMU5TX.js → chunk-A6USBZTO.js} +2 -2
  132. package/dist/{chunk-ZHIVCODT.js → chunk-A72SWTWW.js} +7 -7
  133. package/dist/{chunk-FC77VUX5.js → chunk-A7Q43EUZ.js} +2 -2
  134. package/dist/{chunk-UJBESW6X.js → chunk-AE7WMV4V.js} +7 -7
  135. package/dist/{chunk-J2HSAU72.js → chunk-AIK5W3IW.js} +29 -1
  136. package/dist/chunk-AIK5W3IW.js.map +1 -0
  137. package/dist/{chunk-A5UXZRRM.js → chunk-AJ7QCSIS.js} +2 -2
  138. package/dist/{chunk-FNMFJWBY.js → chunk-ANEUPF6O.js} +5 -5
  139. package/dist/chunk-AREGMX2J.js +50 -0
  140. package/dist/chunk-AREGMX2J.js.map +1 -0
  141. package/dist/chunk-AVPXXV6J.js +448 -0
  142. package/dist/chunk-AVPXXV6J.js.map +1 -0
  143. package/dist/{chunk-2A3OXAP2.js → chunk-B7RWBZXW.js} +2 -2
  144. package/dist/{chunk-GWTFLF2P.js → chunk-BJMBJZ2Y.js} +59 -59
  145. package/dist/chunk-BJMBJZ2Y.js.map +1 -0
  146. package/dist/{chunk-74F5STS4.js → chunk-BLAKS3CP.js} +4 -4
  147. package/dist/{chunk-M7VGG3ID.js → chunk-BV5WMSIJ.js} +9 -9
  148. package/dist/{chunk-2BFLDH5F.js → chunk-BXLOS5AJ.js} +2 -2
  149. package/dist/{chunk-UXKMMNJD.js → chunk-C23BYTJV.js} +6 -6
  150. package/dist/{chunk-JZHHREBL.js → chunk-C3AKM55W.js} +2 -2
  151. package/dist/{chunk-RP7YXA67.js → chunk-C5JF6EZ7.js} +2 -2
  152. package/dist/{chunk-T7Y5KPLN.js → chunk-C6XBRYVD.js} +16 -15
  153. package/dist/chunk-C6XBRYVD.js.map +1 -0
  154. package/dist/{chunk-FTYDWKO6.js → chunk-CEN6DW6X.js} +12 -2
  155. package/dist/chunk-CEN6DW6X.js.map +1 -0
  156. package/dist/chunk-CGASUJVP.js +421 -0
  157. package/dist/chunk-CGASUJVP.js.map +1 -0
  158. package/dist/{chunk-EHCONI6E.js → chunk-CQMEM5CC.js} +2 -2
  159. package/dist/{chunk-PBNZVPFS.js → chunk-CXZQOVIE.js} +12 -12
  160. package/dist/chunk-D3V3UMET.js +44 -0
  161. package/dist/chunk-D3V3UMET.js.map +1 -0
  162. package/dist/{chunk-7N2RUBAP.js → chunk-DDA47TES.js} +2 -2
  163. package/dist/{chunk-64SE4X5G.js → chunk-DEBHXHJR.js} +3 -3
  164. package/dist/{chunk-EHK6KZHK.js → chunk-DNHVPZOD.js} +2 -2
  165. package/dist/{chunk-437S3G37.js → chunk-DQEMWVMT.js} +2 -2
  166. package/dist/{chunk-LZZNTPLR.js → chunk-DX3XRGLH.js} +2 -2
  167. package/dist/chunk-EA2H5LML.js +72 -0
  168. package/dist/chunk-EA2H5LML.js.map +1 -0
  169. package/dist/{chunk-INELTPRG.js → chunk-EEC7UJPX.js} +103 -106
  170. package/dist/chunk-EEC7UJPX.js.map +1 -0
  171. package/dist/{chunk-KSNVDPDF.js → chunk-EKBNDKDY.js} +21 -20
  172. package/dist/chunk-EKBNDKDY.js.map +1 -0
  173. package/dist/{chunk-YF6FMLBT.js → chunk-EQWEIIKQ.js} +64 -34
  174. package/dist/chunk-EQWEIIKQ.js.map +1 -0
  175. package/dist/{chunk-DZKHSDOM.js → chunk-ES36HTSC.js} +4 -4
  176. package/dist/{chunk-TN7THW5U.js → chunk-ET356KSB.js} +2 -2
  177. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  178. package/dist/{chunk-DMSDUGDM.js → chunk-F7GAIWFA.js} +20 -20
  179. package/dist/{chunk-G6XXLKC5.js → chunk-FFPJINJE.js} +6 -6
  180. package/dist/chunk-FMLKOL2Z.js +636 -0
  181. package/dist/chunk-FMLKOL2Z.js.map +1 -0
  182. package/dist/chunk-FQC3DHH2.js +20 -0
  183. package/dist/chunk-FQC3DHH2.js.map +1 -0
  184. package/dist/{chunk-G4CN3ZXL.js → chunk-FUTF72MJ.js} +15 -15
  185. package/dist/chunk-GCASGYIO.js +71 -0
  186. package/dist/chunk-GCASGYIO.js.map +1 -0
  187. package/dist/{chunk-HF77M34J.js → chunk-GFPZS3F5.js} +8 -8
  188. package/dist/{chunk-BCDKBDSY.js → chunk-GNVZQ5JN.js} +99 -41
  189. package/dist/chunk-GNVZQ5JN.js.map +1 -0
  190. package/dist/{chunk-G6NKALRN.js → chunk-H4WG3UQZ.js} +2 -2
  191. package/dist/{chunk-UDQUSU27.js → chunk-HNZKVLAN.js} +2 -2
  192. package/dist/{chunk-5EZXHU36.js → chunk-HYQJQGD3.js} +2 -2
  193. package/dist/{chunk-OAZVR2BV.js → chunk-IDZFBSHN.js} +8 -6
  194. package/dist/{chunk-OAZVR2BV.js.map → chunk-IDZFBSHN.js.map} +1 -1
  195. package/dist/{chunk-PLQE6MJA.js → chunk-IFH6LWTO.js} +50 -8
  196. package/dist/chunk-IFH6LWTO.js.map +1 -0
  197. package/dist/{chunk-RULE4VG5.js → chunk-ILGC6JH6.js} +21 -6
  198. package/dist/chunk-ILGC6JH6.js.map +1 -0
  199. package/dist/chunk-IUM3VSHI.js +113 -0
  200. package/dist/chunk-IUM3VSHI.js.map +1 -0
  201. package/dist/{chunk-DCFV7A7H.js → chunk-IYNGIMVG.js} +101 -91
  202. package/dist/chunk-IYNGIMVG.js.map +1 -0
  203. package/dist/{chunk-TRWPCR6P.js → chunk-JECBQBEX.js} +3 -3
  204. package/dist/{chunk-E6CBU7XP.js → chunk-JYIFGGEM.js} +2 -2
  205. package/dist/{chunk-VA4WSW54.js → chunk-JYIJNNN3.js} +3 -3
  206. package/dist/{chunk-L5HJQC36.js → chunk-K2TDK7GG.js} +3 -3
  207. package/dist/{chunk-KDXVOFYY.js → chunk-KAVBJSBQ.js} +4 -4
  208. package/dist/{chunk-BS4SGLWV.js → chunk-KF5EUVKQ.js} +16 -6
  209. package/dist/chunk-KF5EUVKQ.js.map +1 -0
  210. package/dist/chunk-KFZY2OFH.js +42 -0
  211. package/dist/chunk-KFZY2OFH.js.map +1 -0
  212. package/dist/{chunk-4OKLXWDZ.js → chunk-KNB7IBBU.js} +2 -2
  213. package/dist/{chunk-QLULYKWZ.js → chunk-KSXJOC4F.js} +2 -2
  214. package/dist/{chunk-2S6QHZGJ.js → chunk-L2DPF4DF.js} +1 -1
  215. package/dist/{chunk-JI5AVXYO.js → chunk-LEPOP46W.js} +6 -6
  216. package/dist/{chunk-O7BBFJLP.js → chunk-LTAVSOZW.js} +2 -2
  217. package/dist/{chunk-E3VODCC3.js → chunk-LUDXM7MD.js} +14 -2
  218. package/dist/{chunk-E3VODCC3.js.map → chunk-LUDXM7MD.js.map} +1 -1
  219. package/dist/{chunk-2IZAGAJR.js → chunk-LULR4IUW.js} +3 -3
  220. package/dist/{chunk-QQGPUKMT.js → chunk-MC5Q3J7S.js} +398 -7
  221. package/dist/chunk-MC5Q3J7S.js.map +1 -0
  222. package/dist/{chunk-WMWVO45V.js → chunk-MC6XO7T4.js} +2 -2
  223. package/dist/chunk-MKQBXQKU.js +107 -0
  224. package/dist/chunk-MKQBXQKU.js.map +1 -0
  225. package/dist/{chunk-7PND4PUC.js → chunk-MMGGVZ4Q.js} +29 -9
  226. package/dist/{chunk-7PND4PUC.js.map → chunk-MMGGVZ4Q.js.map} +1 -1
  227. package/dist/{chunk-D6RIFG4O.js → chunk-MNB5RMAY.js} +2 -2
  228. package/dist/{chunk-Q5WG43OP.js → chunk-MYFQ3ED4.js} +7 -7
  229. package/dist/{chunk-V3UE5I5F.js → chunk-MYVPLJOO.js} +2 -2
  230. package/dist/chunk-NINSR6LX.js +83 -0
  231. package/dist/chunk-NINSR6LX.js.map +1 -0
  232. package/dist/{chunk-MTWKDOVO.js → chunk-NLE6TSLG.js} +2 -2
  233. package/dist/{chunk-DBRYEIGY.js → chunk-OJP3N7UK.js} +2 -2
  234. package/dist/{chunk-VDW4VU7B.js → chunk-OQJIB32H.js} +28 -23
  235. package/dist/chunk-OQJIB32H.js.map +1 -0
  236. package/dist/{chunk-SMX5267P.js → chunk-OZKOPQCM.js} +2 -2
  237. package/dist/{chunk-JCK7V6SN.js → chunk-P65YJEEL.js} +2 -2
  238. package/dist/{chunk-MTR7BZQN.js → chunk-PBGWYM3H.js} +3 -3
  239. package/dist/chunk-PIG4WJFQ.js +87 -0
  240. package/dist/chunk-PIG4WJFQ.js.map +1 -0
  241. package/dist/chunk-PQVCH2AE.js +76 -0
  242. package/dist/chunk-PQVCH2AE.js.map +1 -0
  243. package/dist/{chunk-AXTXN4T6.js → chunk-PWFCHOB3.js} +2 -2
  244. package/dist/{chunk-FDPZVY6D.js → chunk-QFN4U7AG.js} +2 -2
  245. package/dist/{chunk-K2ZLHESQ.js → chunk-QIEUU4UK.js} +2 -2
  246. package/dist/{chunk-DYP3YYJJ.js → chunk-QJAQNXWI.js} +39 -5
  247. package/dist/chunk-QJAQNXWI.js.map +1 -0
  248. package/dist/{chunk-3D6AFGQR.js → chunk-QVT3E4CE.js} +7 -7
  249. package/dist/{chunk-FIJUY7GG.js → chunk-R26ZMXR2.js} +23 -23
  250. package/dist/{chunk-FIJUY7GG.js.map → chunk-R26ZMXR2.js.map} +1 -1
  251. package/dist/chunk-R3DJVOSM.js +34 -0
  252. package/dist/chunk-R3DJVOSM.js.map +1 -0
  253. package/dist/{chunk-SFQ6QNL7.js → chunk-R6YJI3HE.js} +2 -1
  254. package/dist/{chunk-5CJVLG3H.js → chunk-RFDD3B7W.js} +5 -5
  255. package/dist/{chunk-JBPKEARU.js → chunk-RKNJBZ55.js} +4 -4
  256. package/dist/chunk-ROI7BK7H.js +88 -0
  257. package/dist/chunk-ROI7BK7H.js.map +1 -0
  258. package/dist/{chunk-FNZQGN76.js → chunk-S2BBISHU.js} +4 -4
  259. package/dist/{chunk-IALLX7EE.js → chunk-S2FSBU4H.js} +3 -3
  260. package/dist/{chunk-BFPETA3S.js → chunk-SCCO46KZ.js} +3 -2
  261. package/dist/chunk-SCCO46KZ.js.map +1 -0
  262. package/dist/chunk-SICXL4MK.js +165 -0
  263. package/dist/chunk-SICXL4MK.js.map +1 -0
  264. package/dist/chunk-SK7AMF2J.js +168 -0
  265. package/dist/chunk-SK7AMF2J.js.map +1 -0
  266. package/dist/{chunk-LZOV7MX7.js → chunk-TDTXKOAJ.js} +6474 -5747
  267. package/dist/chunk-TDTXKOAJ.js.map +1 -0
  268. package/dist/{chunk-KSBEEMQF.js → chunk-TG6IHVE2.js} +151 -6
  269. package/dist/chunk-TG6IHVE2.js.map +1 -0
  270. package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
  271. package/dist/{chunk-DPAENM7O.js → chunk-U3ZAVRP3.js} +2 -2
  272. package/dist/{chunk-ODPLEWB6.js → chunk-UFGFMWAD.js} +47 -17
  273. package/dist/chunk-UFGFMWAD.js.map +1 -0
  274. package/dist/{chunk-4KJLYTLS.js → chunk-V57NLO5P.js} +2 -2
  275. package/dist/{chunk-FLVL4L3L.js → chunk-VDX2J7OX.js} +2 -2
  276. package/dist/{chunk-AJLLIIML.js → chunk-W4FBGYGK.js} +1 -1
  277. package/dist/{chunk-TXLGHDHS.js → chunk-W4XJFLAL.js} +53 -32
  278. package/dist/chunk-W4XJFLAL.js.map +1 -0
  279. package/dist/{chunk-6SXVCD7W.js → chunk-W7IGLX4G.js} +2 -2
  280. package/dist/{chunk-KQDNAXOM.js → chunk-WG5HSQ4P.js} +19 -6
  281. package/dist/chunk-WG5HSQ4P.js.map +1 -0
  282. package/dist/{chunk-LTJAMRGI.js → chunk-WL5EBRDH.js} +17 -3
  283. package/dist/chunk-WL5EBRDH.js.map +1 -0
  284. package/dist/{chunk-KAWTOYAW.js → chunk-WLZYGLJ4.js} +2 -2
  285. package/dist/{chunk-RO4LQGT3.js → chunk-WON4APQY.js} +4 -4
  286. package/dist/{chunk-PQVK2S3J.js → chunk-WZFAPHIK.js} +6 -6
  287. package/dist/{chunk-KQPJLK3X.js → chunk-XL24QQ63.js} +2 -2
  288. package/dist/{chunk-RVZRYUQH.js → chunk-YROFNEG5.js} +7 -7
  289. package/dist/{chunk-RAR2BSEM.js → chunk-YV32N67O.js} +1 -1
  290. package/dist/chunk-YV32N67O.js.map +1 -0
  291. package/dist/{chunk-PBSAPHHA.js → chunk-YVKF3HEW.js} +1 -1
  292. package/dist/chunk-YVKF3HEW.js.map +1 -0
  293. package/dist/{chunk-VRAD2FGO.js → chunk-Z54EM5YZ.js} +12 -12
  294. package/dist/{chunk-2VN4JAX7.js → chunk-ZOKZIR5K.js} +95 -33
  295. package/dist/chunk-ZOKZIR5K.js.map +1 -0
  296. package/dist/{cli-Cd5fMmhq.d.ts → cli-C_pqqTCw.d.ts} +28 -12
  297. package/dist/cli.d.ts +16 -9
  298. package/dist/cli.js +142 -127
  299. package/dist/compounding/engine.d.ts +5 -2
  300. package/dist/compounding/engine.js +36 -26
  301. package/dist/compounding/preference-consolidator.d.ts +3 -1
  302. package/dist/compounding/preference-consolidator.js +1 -1
  303. package/dist/compression-optimizer.d.ts +3 -1
  304. package/dist/config-qmd.d.ts +8 -0
  305. package/dist/config-qmd.js +20 -0
  306. package/dist/config-qmd.js.map +1 -0
  307. package/dist/config.d.ts +3 -1
  308. package/dist/config.js +15 -10
  309. package/dist/connectors/codex-materialize-runner.d.ts +3 -1
  310. package/dist/connectors/codex-materialize-runner.js +36 -26
  311. package/dist/connectors/codex-materialize.d.ts +3 -1
  312. package/dist/connectors/codex-materialize.js +2 -2
  313. package/dist/connectors/index.d.ts +3 -1
  314. package/dist/connectors/index.js +41 -32
  315. package/dist/consolidation-provenance-check.d.ts +5 -2
  316. package/dist/consolidation-undo.d.ts +5 -2
  317. package/dist/contradiction/index.d.ts +5 -2
  318. package/dist/contradiction/index.js +10 -4
  319. package/dist/{contradiction-scan-J57T5QV7.js → contradiction-scan-RJBABNRH.js} +4 -4
  320. package/dist/contradiction-scan-RJBABNRH.js.map +1 -0
  321. package/dist/conversation-index/backend.d.ts +3 -1
  322. package/dist/conversation-index/backend.js +5 -5
  323. package/dist/conversation-index/chunker.d.ts +3 -1
  324. package/dist/conversation-index/cleanup.js +2 -2
  325. package/dist/conversation-index/faiss-adapter.d.ts +3 -1
  326. package/dist/conversation-index/faiss-adapter.js +2 -2
  327. package/dist/conversation-index/indexer.d.ts +3 -1
  328. package/dist/conversation-index/indexer.js +2 -2
  329. package/dist/conversation-index/search.d.ts +3 -1
  330. package/dist/conversation-index/search.js +2 -2
  331. package/dist/cross-namespace-budget.d.ts +45 -1
  332. package/dist/cross-namespace-budget.js +5 -3
  333. package/dist/dashboard-runtime.js +2 -2
  334. package/dist/day-summary.d.ts +3 -1
  335. package/dist/day-summary.js +3 -3
  336. package/dist/delinearize.d.ts +3 -1
  337. package/dist/direct-answer-wiring.d.ts +3 -1
  338. package/dist/direct-answer.d.ts +3 -1
  339. package/dist/{dreams-ledger-6UODQOX6.js → dreams-ledger-567VKVOU.js} +4 -2
  340. package/dist/dreams-ledger-567VKVOU.js.map +1 -0
  341. package/dist/embedding-fallback.d.ts +3 -1
  342. package/dist/embedding-fallback.js +3 -3
  343. package/dist/enrichment/index.d.ts +3 -1
  344. package/dist/entity-retrieval.d.ts +5 -2
  345. package/dist/entity-retrieval.js +35 -26
  346. package/dist/entity-schema.d.ts +3 -1
  347. package/dist/explicit-capture.d.ts +13 -8
  348. package/dist/explicit-capture.js +7 -3
  349. package/dist/extraction-faithfulness.d.ts +3 -1
  350. package/dist/extraction-faithfulness.js +5 -5
  351. package/dist/extraction-judge-telemetry.d.ts +3 -1
  352. package/dist/extraction-judge-telemetry.js +2 -2
  353. package/dist/extraction-judge-training.d.ts +3 -1
  354. package/dist/extraction-judge-training.js +2 -2
  355. package/dist/extraction-judge.d.ts +3 -1
  356. package/dist/extraction-judge.js +5 -5
  357. package/dist/extraction.d.ts +3 -1
  358. package/dist/extraction.js +13 -12
  359. package/dist/faithfulness-config.js +3 -2
  360. package/dist/fallback-llm.d.ts +3 -1
  361. package/dist/fallback-llm.js +4 -4
  362. package/dist/{first-start-migration-TI67YMLG.js → first-start-migration-CSRHCJIQ.js} +2 -2
  363. package/dist/{forget-CQCLB5LB.js → forget-HRPLVB3W.js} +5 -5
  364. package/dist/graph-dashboard-diff.d.ts +3 -1
  365. package/dist/graph-dashboard-key.d.ts +3 -1
  366. package/dist/graph-dashboard-parser.d.ts +3 -1
  367. package/dist/{graph-edge-decay-IOGBBAGC.js → graph-edge-decay-Z6ENALU2.js} +3 -3
  368. package/dist/graph-edge-decay-Z6ENALU2.js.map +1 -0
  369. package/dist/graph-edge-reinforcement.d.ts +3 -1
  370. package/dist/graph-snapshot.d.ts +3 -1
  371. package/dist/graph-snapshot.js +2 -2
  372. package/dist/graph.d.ts +3 -1
  373. package/dist/graph.js +1 -1
  374. package/dist/identity-continuity.d.ts +1 -2
  375. package/dist/importance.d.ts +3 -1
  376. package/dist/importers/index.d.ts +3 -1
  377. package/dist/in-flight-reads.d.ts +3 -1
  378. package/dist/index.d.ts +473 -464
  379. package/dist/index.js +287 -239
  380. package/dist/intent.d.ts +3 -1
  381. package/dist/lcm/archive.js +2 -2
  382. package/dist/lcm/engine.d.ts +3 -1
  383. package/dist/lcm/engine.js +6 -6
  384. package/dist/lcm/index.d.ts +3 -1
  385. package/dist/lcm/index.js +13 -13
  386. package/dist/lcm/schema.js +2 -2
  387. package/dist/lcm/summarizer.js +3 -3
  388. package/dist/lcm/tools.d.ts +3 -1
  389. package/dist/lifecycle.d.ts +3 -1
  390. package/dist/live-connectors-runner.d.ts +3 -1
  391. package/dist/local-llm.d.ts +5 -2
  392. package/dist/local-llm.js +3 -3
  393. package/dist/local-model-endpoint.d.ts +3 -1
  394. package/dist/logger.d.ts +3 -1
  395. package/dist/logger.js +7 -3
  396. package/dist/maintenance/archive-observations.js +2 -2
  397. package/dist/maintenance/memory-governance.d.ts +3 -1
  398. package/dist/maintenance/memory-governance.js +35 -25
  399. package/dist/maintenance/migrate-observations.js +3 -3
  400. package/dist/maintenance/observation-ledger-utils.js +2 -2
  401. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +102 -1
  402. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +37 -25
  403. package/dist/maintenance/rebuild-memory-projection.js +38 -28
  404. package/dist/maintenance/rebuild-observations.js +3 -3
  405. package/dist/mcp-memory-inspector-app.d.ts +22 -15
  406. package/dist/memory-action-policy.d.ts +3 -1
  407. package/dist/memory-cache.d.ts +3 -1
  408. package/dist/memory-lifecycle-ledger-utils.d.ts +25 -2
  409. package/dist/memory-lifecycle-ledger-utils.js +11 -1
  410. package/dist/memory-projection-store.d.ts +3 -1
  411. package/dist/memory-projection-store.js +2 -2
  412. package/dist/memory-provenance.d.ts +3 -1
  413. package/dist/memory-worth-outcomes.d.ts +5 -2
  414. package/dist/model-registry.js +2 -2
  415. package/dist/models-json.d.ts +3 -1
  416. package/dist/models-json.js +2 -2
  417. package/dist/namespaces/migrate.d.ts +6 -3
  418. package/dist/namespaces/migrate.js +51 -42
  419. package/dist/namespaces/principal.d.ts +27 -2
  420. package/dist/namespaces/principal.js +6 -2
  421. package/dist/namespaces/search.d.ts +16 -2
  422. package/dist/namespaces/search.js +24 -20
  423. package/dist/namespaces/storage.d.ts +24 -3
  424. package/dist/namespaces/storage.js +35 -27
  425. package/dist/native-knowledge.d.ts +3 -1
  426. package/dist/native-knowledge.js +2 -2
  427. package/dist/negative.js +2 -2
  428. package/dist/offline-sync-exclude-globs.d.ts +13 -0
  429. package/dist/offline-sync-exclude-globs.js +10 -0
  430. package/dist/offline-sync-exclude-globs.js.map +1 -0
  431. package/dist/offline-sync-impression-drain.d.ts +78 -0
  432. package/dist/offline-sync-impression-drain.js +37 -0
  433. package/dist/offline-sync-impression-drain.js.map +1 -0
  434. package/dist/offline-sync.js +4 -2
  435. package/dist/operator-toolkit.d.ts +5 -2
  436. package/dist/operator-toolkit.js +69 -54
  437. package/dist/orchestration/compression-guideline-coordinator.d.ts +5 -2
  438. package/dist/orchestration/compression-guideline-coordinator.js +3 -3
  439. package/dist/orchestration/maintenance.d.ts +121 -2
  440. package/dist/orchestration/maintenance.js +44 -31
  441. package/dist/{orchestrator-Bp_jeBHK.d.ts → orchestrator-CQJ8U7En.d.ts} +85 -79
  442. package/dist/orchestrator.d.ts +13 -8
  443. package/dist/orchestrator.js +194 -177
  444. package/dist/patterns-cli.d.ts +3 -1
  445. package/dist/{pipeline-BAj5bx7g.d.ts → pipeline-Cn-kvYeV.d.ts} +1 -1
  446. package/dist/policy-runtime.d.ts +3 -1
  447. package/dist/prioritized-embed.d.ts +19 -0
  448. package/dist/prioritized-embed.js +8 -0
  449. package/dist/prioritized-embed.js.map +1 -0
  450. package/dist/proactive-contention.d.ts +28 -0
  451. package/dist/proactive-contention.js +8 -0
  452. package/dist/proactive-contention.js.map +1 -0
  453. package/dist/profiling.js +2 -2
  454. package/dist/provenance.d.ts +3 -1
  455. package/dist/provenance.js +3 -2
  456. package/dist/{purge-NDWV6WYD.js → purge-IRF47ZTX.js} +3 -3
  457. package/dist/{qmd-_MDSlj1m.d.ts → qmd-oh9VXz1d.d.ts} +11 -1
  458. package/dist/qmd-preflight.d.ts +5 -2
  459. package/dist/qmd-recall-cache.d.ts +3 -1
  460. package/dist/qmd-status.d.ts +57 -0
  461. package/dist/qmd-status.js +18 -0
  462. package/dist/qmd-status.js.map +1 -0
  463. package/dist/qmd.d.ts +5 -2
  464. package/dist/qmd.js +7 -3
  465. package/dist/recall-concurrency-config.d.ts +16 -0
  466. package/dist/recall-concurrency-config.js +10 -0
  467. package/dist/recall-concurrency-config.js.map +1 -0
  468. package/dist/recall-disclosure-escalation.d.ts +3 -1
  469. package/dist/recall-explain-renderer.d.ts +4 -1
  470. package/dist/recall-explain-renderer.js +3 -3
  471. package/dist/recall-memory-map.d.ts +26 -0
  472. package/dist/recall-memory-map.js +18 -0
  473. package/dist/recall-memory-map.js.map +1 -0
  474. package/dist/recall-planner-llm.d.ts +3 -1
  475. package/dist/recall-planner-llm.js +4 -4
  476. package/dist/recall-qos.js +2 -2
  477. package/dist/recall-state.d.ts +163 -2
  478. package/dist/recall-state.js +6 -3
  479. package/dist/recall-tag-filter.d.ts +3 -1
  480. package/dist/recall-timings.d.ts +11 -2
  481. package/dist/recall-timings.js +3 -1
  482. package/dist/recall-xray-cli.d.ts +3 -1
  483. package/dist/recall-xray-cli.js +4 -4
  484. package/dist/recall-xray-renderer.d.ts +3 -1
  485. package/dist/recall-xray-renderer.js +3 -3
  486. package/dist/recall-xray.d.ts +3 -1
  487. package/dist/recall-xray.js +2 -2
  488. package/dist/relevance.js +3 -3
  489. package/dist/replay/normalizers/chatgpt.d.ts +3 -1
  490. package/dist/replay/normalizers/claude.d.ts +3 -1
  491. package/dist/replay/normalizers/openclaw.d.ts +3 -1
  492. package/dist/replay/normalizers/shared.d.ts +3 -1
  493. package/dist/replay/runner.d.ts +3 -1
  494. package/dist/replay/types.d.ts +3 -1
  495. package/dist/rerank.d.ts +8 -1
  496. package/dist/rerank.js +3 -1
  497. package/dist/resolution-3K27T4VO.js +18 -0
  498. package/dist/resolution-3K27T4VO.js.map +1 -0
  499. package/dist/resolve-auth-token.d.ts +3 -1
  500. package/dist/resolve-provider-secret.js +2 -2
  501. package/dist/resume-bundles.js +19 -14
  502. package/dist/retrieval-agents.d.ts +19 -16
  503. package/dist/retrieval-agents.js +3 -3
  504. package/dist/retrieval-tiers.d.ts +3 -1
  505. package/dist/routing/engine.d.ts +3 -1
  506. package/dist/routing/store.d.ts +3 -1
  507. package/dist/routing/store.js +2 -2
  508. package/dist/salvage-envelope.d.ts +18 -0
  509. package/dist/salvage-envelope.js +15 -0
  510. package/dist/salvage-envelope.js.map +1 -0
  511. package/dist/schemas.d.ts +98 -98
  512. package/dist/scope-profiles-EpJG7yKP.d.ts +30 -0
  513. package/dist/search/document-scanner.js +3 -2
  514. package/dist/search/embed-helper.d.ts +3 -1
  515. package/dist/search/embed-helper.js +3 -3
  516. package/dist/search/factory.d.ts +3 -1
  517. package/dist/search/factory.js +18 -16
  518. package/dist/search/index.d.ts +3 -1
  519. package/dist/search/index.js +22 -20
  520. package/dist/search/lancedb-backend.d.ts +3 -1
  521. package/dist/search/lancedb-backend.js +6 -5
  522. package/dist/search/meilisearch-backend.d.ts +3 -1
  523. package/dist/search/meilisearch-backend.js +6 -5
  524. package/dist/search/noop-backend.d.ts +3 -1
  525. package/dist/search/orama-backend.d.ts +3 -1
  526. package/dist/search/orama-backend.js +6 -5
  527. package/dist/search/port.d.ts +16 -2
  528. package/dist/search/port.js +1 -1
  529. package/dist/search/remote-backend.d.ts +3 -1
  530. package/dist/search/remote-backend.js +3 -3
  531. package/dist/secure-store/index.d.ts +12 -1
  532. package/dist/secure-store/index.js +6 -3
  533. package/dist/{semantic-consolidation-C2m5eHHE.d.ts → semantic-consolidation-DILZEQq3.d.ts} +1 -1
  534. package/dist/semantic-consolidation.d.ts +4 -2
  535. package/dist/semantic-consolidation.js +38 -29
  536. package/dist/semantic-rule-promotion.js +34 -24
  537. package/dist/semantic-rule-verifier.d.ts +3 -1
  538. package/dist/semantic-rule-verifier.js +35 -25
  539. package/dist/session-observer-bands.d.ts +3 -1
  540. package/dist/session-observer-state.d.ts +3 -1
  541. package/dist/session-observer-state.js +2 -2
  542. package/dist/session-transcript-migration.js +2 -2
  543. package/dist/shared-context/manager.d.ts +11 -9
  544. package/dist/shared-context/manager.js +3 -3
  545. package/dist/signal.d.ts +3 -1
  546. package/dist/{storage-hr_Y2z5R.d.ts → storage-NIyKYD8g.d.ts} +489 -110
  547. package/dist/storage-guards.js +2 -2
  548. package/dist/storage.d.ts +6 -3
  549. package/dist/storage.js +38 -26
  550. package/dist/structured-attributes.d.ts +23 -1
  551. package/dist/structured-attributes.js +7 -1
  552. package/dist/summarizer.d.ts +3 -1
  553. package/dist/summarizer.js +11 -11
  554. package/dist/summary-snapshot.d.ts +3 -1
  555. package/dist/summary-snapshot.js +2 -2
  556. package/dist/temporal-index-batch.d.ts +1 -0
  557. package/dist/temporal-index-batch.js +10 -0
  558. package/dist/temporal-index-batch.js.map +1 -0
  559. package/dist/temporal-index-compat.d.ts +279 -1
  560. package/dist/temporal-index-compat.js +1 -1
  561. package/dist/temporal-index.d.ts +1 -225
  562. package/dist/temporal-index.js +35 -3
  563. package/dist/temporal-supersession.d.ts +5 -2
  564. package/dist/temporal-supersession.js +2 -2
  565. package/dist/temporal-timeline-recall.d.ts +3 -1
  566. package/dist/temporal-validity.d.ts +3 -1
  567. package/dist/threading.d.ts +3 -1
  568. package/dist/threading.js +2 -2
  569. package/dist/tier-migration.d.ts +5 -2
  570. package/dist/tier-routing.d.ts +3 -1
  571. package/dist/{tier-stats-KV7IWCJR.js → tier-stats-QEJSPFV2.js} +2 -2
  572. package/dist/tokens.js +3 -3
  573. package/dist/topics.d.ts +3 -1
  574. package/dist/transcript.d.ts +3 -1
  575. package/dist/transcript.js +2 -2
  576. package/dist/transfer/backup.js +3 -3
  577. package/dist/transfer/capsule-export.js +3 -3
  578. package/dist/transfer/capsule-import.js +3 -3
  579. package/dist/transfer/types.d.ts +90 -90
  580. package/dist/trust-score-stage.d.ts +75 -38
  581. package/dist/trust-score-stage.js +11 -3
  582. package/dist/trust-score.d.ts +3 -1
  583. package/dist/{types-Cg8q7W7_.d.ts → types-D_fG2ix8.d.ts} +39 -63
  584. package/dist/types-continuity.d.ts +52 -0
  585. package/dist/types-continuity.js +1 -0
  586. package/dist/types-continuity.js.map +1 -0
  587. package/dist/types.d.ts +3 -1
  588. package/dist/types.js +1 -1
  589. package/dist/utility-runtime.d.ts +3 -1
  590. package/dist/utils/serialize-mutations.d.ts +31 -2
  591. package/dist/utils/serialize-mutations.js +1 -1
  592. package/dist/verified-recall.js +36 -26
  593. package/dist/write-envelope.d.ts +282 -0
  594. package/dist/write-envelope.js +35 -0
  595. package/dist/write-envelope.js.map +1 -0
  596. package/dist/write-quarantine.d.ts +79 -0
  597. package/dist/write-quarantine.js +11 -0
  598. package/dist/write-quarantine.js.map +1 -0
  599. package/dist/write-rate-limiter.d.ts +45 -0
  600. package/dist/write-rate-limiter.js +9 -0
  601. package/dist/write-rate-limiter.js.map +1 -0
  602. package/package.json +37 -2
  603. package/src/access-admin-ops-surface.ts +17 -7
  604. package/src/access-boundary.test.ts +28 -0
  605. package/src/access-boundary.ts +31 -2
  606. package/src/access-citation.test.ts +72 -0
  607. package/src/access-citation.ts +139 -0
  608. package/src/access-errors.ts +21 -0
  609. package/src/access-fingerprint-parity.test.ts +211 -0
  610. package/src/access-http-cancellation.test.ts +59 -21
  611. package/src/access-http.test.ts +90 -0
  612. package/src/access-http.ts +79 -79
  613. package/src/access-mcp-cancellation.test.ts +16 -7
  614. package/src/access-memory-search-fanout.test.ts +220 -0
  615. package/src/access-memory-search-fanout.ts +168 -0
  616. package/src/access-observe-quarantine.test.ts +120 -0
  617. package/src/access-observe-write-surface.ts +130 -11
  618. package/src/access-operations-batch.ts +2 -0
  619. package/src/access-recall-concurrency.ts +773 -0
  620. package/src/access-recall-response.ts +307 -0
  621. package/src/access-recall-surface.ts +100 -252
  622. package/src/access-service-coding-write.test.ts +22 -4
  623. package/src/access-service-health.test.ts +49 -0
  624. package/src/access-service-namespace.test.ts +486 -8
  625. package/src/access-service-observe-scope.test.ts +4 -0
  626. package/src/access-service-recall-concurrency.test.ts +1990 -0
  627. package/src/access-service.ts +182 -175
  628. package/src/access-surface-catalog.ts +1 -0
  629. package/src/access-token-capabilities.ts +16 -9
  630. package/src/active-recall.test.ts +35 -0
  631. package/src/active-recall.ts +7 -0
  632. package/src/bounded-jsonl-state.ts +93 -0
  633. package/src/buffer-session.test.ts +422 -5
  634. package/src/buffer-surprise-trigger.test.ts +41 -0
  635. package/src/buffer.ts +218 -3
  636. package/src/capabilities-cluster-b.test.ts +1 -0
  637. package/src/capabilities.ts +5 -6
  638. package/src/cli.ts +21 -22
  639. package/src/coding/architecture-surfaces.test.ts +6 -3
  640. package/src/coding/architecture-surfaces.ts +36 -25
  641. package/src/coding/decision-surfaces.ts +42 -34
  642. package/src/compounding/engine.test.ts +5 -1
  643. package/src/compounding/engine.ts +7 -7
  644. package/src/config-qmd.ts +89 -0
  645. package/src/config.test.ts +136 -0
  646. package/src/config.ts +113 -118
  647. package/src/connector-provenance-lifecycle.test.ts +4 -4
  648. package/src/connectors/coerce.test.ts +131 -0
  649. package/src/connectors/coerce.ts +80 -7
  650. package/src/contradiction/contradiction.test.ts +10 -6
  651. package/src/contradiction/resolution.ts +17 -4
  652. package/src/correction/correction-access-wiring.ts +62 -18
  653. package/src/cross-namespace-budget.test.ts +35 -0
  654. package/src/cross-namespace-budget.ts +86 -19
  655. package/src/explicit-capture.ts +54 -17
  656. package/src/extraction.ts +2 -3
  657. package/src/faithfulness-graph-guard.test.ts +16 -69
  658. package/src/graph-recall.test.ts +31 -0
  659. package/src/index.ts +22 -2
  660. package/src/local-llm.ts +4 -6
  661. package/src/logger.ts +16 -0
  662. package/src/maintenance/dreams-ledger.ts +100 -49
  663. package/src/maintenance/rebuild-memory-lifecycle-ledger-cli.ts +77 -0
  664. package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +373 -9
  665. package/src/memory-lifecycle-ledger-utils.ts +51 -5
  666. package/src/namespaces/principal.test.ts +37 -1
  667. package/src/namespaces/principal.ts +48 -0
  668. package/src/namespaces/search.test.ts +79 -5
  669. package/src/namespaces/search.ts +59 -6
  670. package/src/namespaces/storage.ts +43 -1
  671. package/src/offline-sync-exclude-globs.ts +58 -0
  672. package/src/offline-sync-impression-drain.test.ts +316 -0
  673. package/src/offline-sync-impression-drain.ts +260 -0
  674. package/src/offline-sync.test.ts +32 -5
  675. package/src/offline-sync.ts +1 -14
  676. package/src/operator-toolkit.ts +1 -1
  677. package/src/orchestration/batch-deindex-flush.test.ts +146 -0
  678. package/src/orchestration/consolidation-run.ts +96 -85
  679. package/src/orchestration/extraction-envelope.ts +94 -0
  680. package/src/orchestration/extraction-persist-promotion.ts +134 -0
  681. package/src/orchestration/extraction-persist.ts +325 -260
  682. package/src/orchestration/extraction-run.test.ts +1 -1
  683. package/src/orchestration/extraction-run.ts +2 -2
  684. package/src/orchestration/graph-recall-coordinator.ts +18 -7
  685. package/src/orchestration/lifecycle-policy-coordinator.ts +63 -48
  686. package/src/orchestration/maintenance.test.ts +2103 -3
  687. package/src/orchestration/maintenance.ts +574 -2
  688. package/src/orchestration/namespace-read-fanout.ts +14 -8
  689. package/src/orchestration/orchestrator-helpers.ts +8 -1
  690. package/src/orchestration/orchestrator-init.ts +22 -6
  691. package/src/orchestration/orchestrator-namespace-scope.ts +71 -0
  692. package/src/orchestration/persistence-index.ts +64 -9
  693. package/src/orchestration/qmd-result-resolver.ts +27 -0
  694. package/src/orchestration/recall-entry.ts +34 -19
  695. package/src/orchestration/recall-internal.ts +207 -79
  696. package/src/orchestration/recall-rerank-coordinator.ts +229 -82
  697. package/src/orchestration/recall-result-formatter.ts +76 -20
  698. package/src/orchestration/recall-search-pipeline.test.ts +70 -0
  699. package/src/orchestration/recall-search-pipeline.ts +102 -129
  700. package/src/orchestration/recall-section-coordinator.ts +315 -85
  701. package/src/orchestration/semantic-consolidation-coordinator.ts +69 -37
  702. package/src/orchestration/session-context.ts +21 -0
  703. package/src/orchestration/workspace-ops.test.ts +348 -0
  704. package/src/orchestration/workspace-ops.ts +168 -34
  705. package/src/orchestrator-chunked-hash-durability.test.ts +126 -0
  706. package/src/orchestrator-connector-dedup.test.ts +1061 -40
  707. package/src/orchestrator-flush.test.ts +155 -39
  708. package/src/orchestrator-nonchunked-hash-durability.test.ts +121 -0
  709. package/src/orchestrator-source-attribution.test.ts +9 -9
  710. package/src/orchestrator.ts +160 -149
  711. package/src/prioritized-embed.ts +123 -0
  712. package/src/proactive-contention.test.ts +80 -0
  713. package/src/proactive-contention.ts +39 -0
  714. package/src/procedural/procedure-miner.test.ts +19 -14
  715. package/src/procedural/procedure-miner.ts +44 -13
  716. package/src/qmd-client.test.ts +111 -1
  717. package/src/qmd-status.ts +186 -0
  718. package/src/qmd.ts +47 -47
  719. package/src/recall-budget-config.test.ts +30 -0
  720. package/src/recall-cold-deadline.test.ts +149 -0
  721. package/src/recall-concurrency-config.ts +28 -0
  722. package/src/recall-memory-map.ts +74 -0
  723. package/src/recall-rerank-coordinator.test.ts +508 -0
  724. package/src/recall-state.test.ts +825 -1
  725. package/src/recall-state.ts +406 -7
  726. package/src/recall-timings-access.test.ts +41 -0
  727. package/src/recall-timings.ts +16 -0
  728. package/src/relay/mission-access.test.ts +5 -5
  729. package/src/rerank.ts +22 -0
  730. package/src/retrieval-agents.test.ts +79 -0
  731. package/src/retrieval-agents.ts +71 -19
  732. package/src/salvage-envelope.ts +28 -0
  733. package/src/scopes/scope-plan.test.ts +42 -0
  734. package/src/scopes/scope-plan.ts +45 -0
  735. package/src/search/port.ts +13 -0
  736. package/src/secure-store/index.ts +1 -0
  737. package/src/secure-store/secure-fs.ts +48 -0
  738. package/src/secure-store/secure-store.test.ts +40 -0
  739. package/src/semantic-rule-promotion.ts +13 -4
  740. package/src/storage/content-hash-index.ts +655 -0
  741. package/src/storage/entity-timeline.ts +473 -0
  742. package/src/storage/memory-lifecycle-ledger-access.ts +603 -0
  743. package/src/storage/secure-line-reader.test.ts +197 -0
  744. package/src/storage/secure-line-reader.ts +181 -3
  745. package/src/storage-append-header-sniff.test.ts +195 -0
  746. package/src/storage-behavior-signals-cache.test.ts +237 -0
  747. package/src/storage-contract/cache-coherence.test.ts +96 -1
  748. package/src/storage-contract/content-hash.test.ts +47 -2
  749. package/src/storage-fact-hash-index-defer.test.ts +1200 -0
  750. package/src/storage-memory-lifecycle-ledger.test.ts +1146 -3
  751. package/src/storage.ts +882 -890
  752. package/src/structured-attributes.ts +52 -0
  753. package/src/temporal-index-batch.ts +112 -0
  754. package/src/temporal-index.test.ts +193 -0
  755. package/src/temporal-index.ts +49 -53
  756. package/src/testing/lifecycle-matrix.test.ts +212 -0
  757. package/src/testing/lifecycle-matrix.ts +271 -0
  758. package/src/testing/orchestrator-lite.ts +236 -0
  759. package/src/testing/subjects/dreams-ledger.test.ts +67 -0
  760. package/src/testing/subjects/extraction-lifecycle.test.ts +442 -0
  761. package/src/testing/subjects/lifecycle-ledger.test.ts +93 -0
  762. package/src/testing/subjects/recall-budget.test.ts +461 -0
  763. package/src/testing/subjects/serialized-write-chain.test.ts +224 -0
  764. package/src/trust-score-stage.test.ts +154 -0
  765. package/src/trust-score-stage.ts +216 -53
  766. package/src/types-continuity.ts +57 -0
  767. package/src/types.ts +51 -77
  768. package/src/utils/path-containment.test.ts +75 -0
  769. package/src/utils/path-containment.ts +76 -0
  770. package/src/utils/serialize-mutations.test.ts +122 -0
  771. package/src/utils/serialize-mutations.ts +68 -3
  772. package/src/wearables/memory-gen.test.ts +31 -9
  773. package/src/wearables/memory-gen.ts +95 -55
  774. package/src/wearables/pipeline.test.ts +10 -7
  775. package/src/wearables/service.test.ts +6 -8
  776. package/src/wearables/service.ts +2 -2
  777. package/src/write-envelope.extension.test.ts +111 -0
  778. package/src/write-envelope.test.ts +683 -0
  779. package/src/write-envelope.ts +902 -0
  780. package/src/write-quarantine.test.ts +193 -0
  781. package/src/write-quarantine.ts +241 -0
  782. package/src/write-rate-limiter.test.ts +105 -0
  783. package/src/write-rate-limiter.ts +135 -0
  784. package/src/write-sealed-parity.test.ts +204 -0
  785. package/dist/chunk-2VN4JAX7.js.map +0 -1
  786. package/dist/chunk-37CTGERM.js.map +0 -1
  787. package/dist/chunk-3IY64MV6.js.map +0 -1
  788. package/dist/chunk-BCDKBDSY.js.map +0 -1
  789. package/dist/chunk-BFPETA3S.js.map +0 -1
  790. package/dist/chunk-BFVEEB7W.js.map +0 -1
  791. package/dist/chunk-BS4SGLWV.js.map +0 -1
  792. package/dist/chunk-DCFV7A7H.js.map +0 -1
  793. package/dist/chunk-DYP3YYJJ.js.map +0 -1
  794. package/dist/chunk-E5BK6CGV.js +0 -335
  795. package/dist/chunk-E5BK6CGV.js.map +0 -1
  796. package/dist/chunk-FTYDWKO6.js.map +0 -1
  797. package/dist/chunk-GWTFLF2P.js.map +0 -1
  798. package/dist/chunk-IKQTFQMI.js.map +0 -1
  799. package/dist/chunk-INELTPRG.js.map +0 -1
  800. package/dist/chunk-J2HSAU72.js.map +0 -1
  801. package/dist/chunk-JREB6UFH.js.map +0 -1
  802. package/dist/chunk-K7LOWSLU.js.map +0 -1
  803. package/dist/chunk-KQDNAXOM.js.map +0 -1
  804. package/dist/chunk-KSBEEMQF.js.map +0 -1
  805. package/dist/chunk-KSNVDPDF.js.map +0 -1
  806. package/dist/chunk-LC7OODWZ.js.map +0 -1
  807. package/dist/chunk-LTJAMRGI.js.map +0 -1
  808. package/dist/chunk-LZOV7MX7.js.map +0 -1
  809. package/dist/chunk-NWTGD4AN.js.map +0 -1
  810. package/dist/chunk-ODPLEWB6.js.map +0 -1
  811. package/dist/chunk-OVJ2HIPE.js.map +0 -1
  812. package/dist/chunk-PBSAPHHA.js.map +0 -1
  813. package/dist/chunk-PHK3HARR.js +0 -32
  814. package/dist/chunk-PHK3HARR.js.map +0 -1
  815. package/dist/chunk-PLQE6MJA.js.map +0 -1
  816. package/dist/chunk-QKQYXZVS.js.map +0 -1
  817. package/dist/chunk-QQGPUKMT.js.map +0 -1
  818. package/dist/chunk-RANHFI3P.js +0 -8
  819. package/dist/chunk-RANHFI3P.js.map +0 -1
  820. package/dist/chunk-RAR2BSEM.js.map +0 -1
  821. package/dist/chunk-RULE4VG5.js.map +0 -1
  822. package/dist/chunk-SVOZFLIQ.js +0 -16
  823. package/dist/chunk-SVOZFLIQ.js.map +0 -1
  824. package/dist/chunk-T7Y5KPLN.js.map +0 -1
  825. package/dist/chunk-TXLGHDHS.js.map +0 -1
  826. package/dist/chunk-VDW4VU7B.js.map +0 -1
  827. package/dist/chunk-VSMRDBFT.js +0 -17
  828. package/dist/chunk-VSMRDBFT.js.map +0 -1
  829. package/dist/chunk-X64YLC24.js.map +0 -1
  830. package/dist/chunk-XDSXEKYK.js +0 -118
  831. package/dist/chunk-XDSXEKYK.js.map +0 -1
  832. package/dist/chunk-YF6FMLBT.js.map +0 -1
  833. package/dist/resolution-VH4NGENZ.js +0 -12
  834. /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-citation.js.map} +0 -0
  835. /package/dist/{contradiction-scan-J57T5QV7.js.map → access-memory-search-fanout.js.map} +0 -0
  836. /package/dist/{dreams-ledger-6UODQOX6.js.map → access-recall-concurrency.js.map} +0 -0
  837. /package/dist/{graph-edge-decay-IOGBBAGC.js.map → access-recall-response.js.map} +0 -0
  838. /package/dist/{resolution-VH4NGENZ.js.map → bounded-jsonl-state.js.map} +0 -0
  839. /package/dist/{chunk-A3GOMRJG.js.map → chunk-23L3IXFD.js.map} +0 -0
  840. /package/dist/{chunk-MRHFJV44.js.map → chunk-2DQ2XREU.js.map} +0 -0
  841. /package/dist/{chunk-L2B3EE7B.js.map → chunk-2QMKMVHH.js.map} +0 -0
  842. /package/dist/{chunk-VJVNLNDQ.js.map → chunk-2UFO7X4Q.js.map} +0 -0
  843. /package/dist/{chunk-2MR3MFQB.js.map → chunk-2VEOKUPP.js.map} +0 -0
  844. /package/dist/{chunk-RHOZ5LLO.js.map → chunk-347WD2LE.js.map} +0 -0
  845. /package/dist/{chunk-HDTQVNWD.js.map → chunk-3QAH2S33.js.map} +0 -0
  846. /package/dist/{chunk-LRNMRXJO.js.map → chunk-3Y224NYC.js.map} +0 -0
  847. /package/dist/{chunk-VFSP73LP.js.map → chunk-45GF7Y3V.js.map} +0 -0
  848. /package/dist/{chunk-OEHCMALU.js.map → chunk-5I4M26Q7.js.map} +0 -0
  849. /package/dist/{chunk-2AZXIZEN.js.map → chunk-5Q7EVV4X.js.map} +0 -0
  850. /package/dist/{chunk-PUBTHW5O.js.map → chunk-6DRPPQEJ.js.map} +0 -0
  851. /package/dist/{chunk-FDCTQWLW.js.map → chunk-6O22TZSB.js.map} +0 -0
  852. /package/dist/{chunk-ZQ7UBUZ6.js.map → chunk-6QYST5XN.js.map} +0 -0
  853. /package/dist/{chunk-W4PGTAS2.js.map → chunk-6WAFYNVT.js.map} +0 -0
  854. /package/dist/{chunk-R5VAVVHP.js.map → chunk-72A4L5JJ.js.map} +0 -0
  855. /package/dist/{chunk-3FAMU5TX.js.map → chunk-A6USBZTO.js.map} +0 -0
  856. /package/dist/{chunk-ZHIVCODT.js.map → chunk-A72SWTWW.js.map} +0 -0
  857. /package/dist/{chunk-FC77VUX5.js.map → chunk-A7Q43EUZ.js.map} +0 -0
  858. /package/dist/{chunk-UJBESW6X.js.map → chunk-AE7WMV4V.js.map} +0 -0
  859. /package/dist/{chunk-A5UXZRRM.js.map → chunk-AJ7QCSIS.js.map} +0 -0
  860. /package/dist/{chunk-FNMFJWBY.js.map → chunk-ANEUPF6O.js.map} +0 -0
  861. /package/dist/{chunk-2A3OXAP2.js.map → chunk-B7RWBZXW.js.map} +0 -0
  862. /package/dist/{chunk-74F5STS4.js.map → chunk-BLAKS3CP.js.map} +0 -0
  863. /package/dist/{chunk-M7VGG3ID.js.map → chunk-BV5WMSIJ.js.map} +0 -0
  864. /package/dist/{chunk-2BFLDH5F.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  865. /package/dist/{chunk-UXKMMNJD.js.map → chunk-C23BYTJV.js.map} +0 -0
  866. /package/dist/{chunk-JZHHREBL.js.map → chunk-C3AKM55W.js.map} +0 -0
  867. /package/dist/{chunk-RP7YXA67.js.map → chunk-C5JF6EZ7.js.map} +0 -0
  868. /package/dist/{chunk-EHCONI6E.js.map → chunk-CQMEM5CC.js.map} +0 -0
  869. /package/dist/{chunk-PBNZVPFS.js.map → chunk-CXZQOVIE.js.map} +0 -0
  870. /package/dist/{chunk-7N2RUBAP.js.map → chunk-DDA47TES.js.map} +0 -0
  871. /package/dist/{chunk-64SE4X5G.js.map → chunk-DEBHXHJR.js.map} +0 -0
  872. /package/dist/{chunk-EHK6KZHK.js.map → chunk-DNHVPZOD.js.map} +0 -0
  873. /package/dist/{chunk-437S3G37.js.map → chunk-DQEMWVMT.js.map} +0 -0
  874. /package/dist/{chunk-LZZNTPLR.js.map → chunk-DX3XRGLH.js.map} +0 -0
  875. /package/dist/{chunk-DZKHSDOM.js.map → chunk-ES36HTSC.js.map} +0 -0
  876. /package/dist/{chunk-TN7THW5U.js.map → chunk-ET356KSB.js.map} +0 -0
  877. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  878. /package/dist/{chunk-DMSDUGDM.js.map → chunk-F7GAIWFA.js.map} +0 -0
  879. /package/dist/{chunk-G6XXLKC5.js.map → chunk-FFPJINJE.js.map} +0 -0
  880. /package/dist/{chunk-G4CN3ZXL.js.map → chunk-FUTF72MJ.js.map} +0 -0
  881. /package/dist/{chunk-HF77M34J.js.map → chunk-GFPZS3F5.js.map} +0 -0
  882. /package/dist/{chunk-G6NKALRN.js.map → chunk-H4WG3UQZ.js.map} +0 -0
  883. /package/dist/{chunk-UDQUSU27.js.map → chunk-HNZKVLAN.js.map} +0 -0
  884. /package/dist/{chunk-5EZXHU36.js.map → chunk-HYQJQGD3.js.map} +0 -0
  885. /package/dist/{chunk-TRWPCR6P.js.map → chunk-JECBQBEX.js.map} +0 -0
  886. /package/dist/{chunk-E6CBU7XP.js.map → chunk-JYIFGGEM.js.map} +0 -0
  887. /package/dist/{chunk-VA4WSW54.js.map → chunk-JYIJNNN3.js.map} +0 -0
  888. /package/dist/{chunk-L5HJQC36.js.map → chunk-K2TDK7GG.js.map} +0 -0
  889. /package/dist/{chunk-KDXVOFYY.js.map → chunk-KAVBJSBQ.js.map} +0 -0
  890. /package/dist/{chunk-4OKLXWDZ.js.map → chunk-KNB7IBBU.js.map} +0 -0
  891. /package/dist/{chunk-QLULYKWZ.js.map → chunk-KSXJOC4F.js.map} +0 -0
  892. /package/dist/{chunk-2S6QHZGJ.js.map → chunk-L2DPF4DF.js.map} +0 -0
  893. /package/dist/{chunk-JI5AVXYO.js.map → chunk-LEPOP46W.js.map} +0 -0
  894. /package/dist/{chunk-O7BBFJLP.js.map → chunk-LTAVSOZW.js.map} +0 -0
  895. /package/dist/{chunk-2IZAGAJR.js.map → chunk-LULR4IUW.js.map} +0 -0
  896. /package/dist/{chunk-WMWVO45V.js.map → chunk-MC6XO7T4.js.map} +0 -0
  897. /package/dist/{chunk-D6RIFG4O.js.map → chunk-MNB5RMAY.js.map} +0 -0
  898. /package/dist/{chunk-Q5WG43OP.js.map → chunk-MYFQ3ED4.js.map} +0 -0
  899. /package/dist/{chunk-V3UE5I5F.js.map → chunk-MYVPLJOO.js.map} +0 -0
  900. /package/dist/{chunk-MTWKDOVO.js.map → chunk-NLE6TSLG.js.map} +0 -0
  901. /package/dist/{chunk-DBRYEIGY.js.map → chunk-OJP3N7UK.js.map} +0 -0
  902. /package/dist/{chunk-SMX5267P.js.map → chunk-OZKOPQCM.js.map} +0 -0
  903. /package/dist/{chunk-JCK7V6SN.js.map → chunk-P65YJEEL.js.map} +0 -0
  904. /package/dist/{chunk-MTR7BZQN.js.map → chunk-PBGWYM3H.js.map} +0 -0
  905. /package/dist/{chunk-AXTXN4T6.js.map → chunk-PWFCHOB3.js.map} +0 -0
  906. /package/dist/{chunk-FDPZVY6D.js.map → chunk-QFN4U7AG.js.map} +0 -0
  907. /package/dist/{chunk-K2ZLHESQ.js.map → chunk-QIEUU4UK.js.map} +0 -0
  908. /package/dist/{chunk-3D6AFGQR.js.map → chunk-QVT3E4CE.js.map} +0 -0
  909. /package/dist/{chunk-SFQ6QNL7.js.map → chunk-R6YJI3HE.js.map} +0 -0
  910. /package/dist/{chunk-5CJVLG3H.js.map → chunk-RFDD3B7W.js.map} +0 -0
  911. /package/dist/{chunk-JBPKEARU.js.map → chunk-RKNJBZ55.js.map} +0 -0
  912. /package/dist/{chunk-FNZQGN76.js.map → chunk-S2BBISHU.js.map} +0 -0
  913. /package/dist/{chunk-IALLX7EE.js.map → chunk-S2FSBU4H.js.map} +0 -0
  914. /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
  915. /package/dist/{chunk-DPAENM7O.js.map → chunk-U3ZAVRP3.js.map} +0 -0
  916. /package/dist/{chunk-4KJLYTLS.js.map → chunk-V57NLO5P.js.map} +0 -0
  917. /package/dist/{chunk-FLVL4L3L.js.map → chunk-VDX2J7OX.js.map} +0 -0
  918. /package/dist/{chunk-AJLLIIML.js.map → chunk-W4FBGYGK.js.map} +0 -0
  919. /package/dist/{chunk-6SXVCD7W.js.map → chunk-W7IGLX4G.js.map} +0 -0
  920. /package/dist/{chunk-KAWTOYAW.js.map → chunk-WLZYGLJ4.js.map} +0 -0
  921. /package/dist/{chunk-RO4LQGT3.js.map → chunk-WON4APQY.js.map} +0 -0
  922. /package/dist/{chunk-PQVK2S3J.js.map → chunk-WZFAPHIK.js.map} +0 -0
  923. /package/dist/{chunk-KQPJLK3X.js.map → chunk-XL24QQ63.js.map} +0 -0
  924. /package/dist/{chunk-RVZRYUQH.js.map → chunk-YROFNEG5.js.map} +0 -0
  925. /package/dist/{chunk-VRAD2FGO.js.map → chunk-Z54EM5YZ.js.map} +0 -0
  926. /package/dist/{first-start-migration-TI67YMLG.js.map → first-start-migration-CSRHCJIQ.js.map} +0 -0
  927. /package/dist/{forget-CQCLB5LB.js.map → forget-HRPLVB3W.js.map} +0 -0
  928. /package/dist/{purge-NDWV6WYD.js.map → purge-IRF47ZTX.js.map} +0 -0
  929. /package/dist/{tier-stats-KV7IWCJR.js.map → tier-stats-QEJSPFV2.js.map} +0 -0
@@ -1,11 +1,12 @@
1
1
  import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
- import { mkdtemp, readFile } from "node:fs/promises";
3
+ import { chmod, lstat, mkdir, mkdtemp, readFile, readdir, rm, stat, symlink, writeFile } from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
 
7
7
  import { LastRecallStore } from "./recall-state.js";
8
8
  import type { RecallTierExplain } from "./types.js";
9
+ import { listContainedSpillFiles } from "./utils/path-containment.js";
9
10
 
10
11
  async function freshStore() {
11
12
  const dir = await mkdtemp(path.join(os.tmpdir(), "engram-recall-state-"));
@@ -54,6 +55,21 @@ test("LastRecallStore.record persists tierExplain and round-trips to JSON on dis
54
55
  assert.deepEqual(parsed["s1"]?.tierExplain, tierExplain);
55
56
  });
56
57
 
58
+ test("LastRecallStore.record persists per-result namespaces", async () => {
59
+ const { store } = await freshStore();
60
+ const resultNamespaces = ["main", "shared"] as Array<string | undefined>;
61
+ await store.record({
62
+ sessionKey: "s1",
63
+ query: "namespaced recall",
64
+ memoryIds: ["same-id", "same-id"],
65
+ resultPaths: ["facts/same-id.md", "facts/same-id.md"],
66
+ resultNamespaces,
67
+ });
68
+ const snap = store.get("s1");
69
+ assert.ok(snap);
70
+ assert.deepEqual(snap.resultNamespaces, resultNamespaces);
71
+ });
72
+
57
73
  // ── Defensive copies isolate the stored snapshot from caller mutation ──────
58
74
 
59
75
  test("LastRecallStore.record copies filteredBy so caller mutation does not tear the snapshot", async () => {
@@ -231,3 +247,811 @@ test("LastRecallStore.record copies sourceAnchors array and lineRange tuple", as
231
247
  assert.equal(snap?.tierExplain?.sourceAnchors?.[0]?.path, "/a.md");
232
248
  assert.deepEqual(snap?.tierExplain?.sourceAnchors?.[0]?.lineRange, [1, 2]);
233
249
  });
250
+
251
+ // ── Recall-impressions rotation (issue #1910) ──────────────────────────────
252
+ async function fileExists(p: string): Promise<boolean> {
253
+ try {
254
+ await stat(p);
255
+ return true;
256
+ } catch {
257
+ return false;
258
+ }
259
+ }
260
+
261
+ test("LastRecallStore rotates recall_impressions.jsonl once it exceeds the byte threshold", async () => {
262
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-rotate-"));
263
+ try {
264
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
265
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 64, impressionsRotateKeep: 3 });
266
+ await store.load();
267
+
268
+ // First append creates the active file (below threshold, no rotation).
269
+ await store.record({ sessionKey: "s1", query: "q1", memoryIds: [] });
270
+ assert.equal(await fileExists(`${impressionsPath}.1`), false);
271
+
272
+ // Grow the active file past the threshold, then append: rotation moves the
273
+ // oversized active file to .1 and starts a fresh active file.
274
+ await writeFile(impressionsPath, "x".repeat(128), "utf8");
275
+ await store.record({ sessionKey: "s2", query: "q2", memoryIds: [] });
276
+
277
+ assert.equal(await fileExists(impressionsPath), true, "active file recreated");
278
+ assert.equal(await fileExists(`${impressionsPath}.1`), true, "archive .1 created");
279
+ const active = await readFile(impressionsPath, "utf8");
280
+ assert.ok(active.trim().length > 0, "active file holds the new impression");
281
+ assert.equal(active.includes("x".repeat(128)), false, "old rows moved out of active file");
282
+ } finally {
283
+ await rm(dir, { recursive: true, force: true });
284
+ }
285
+ });
286
+
287
+ test("LastRecallStore keeps at most impressionsRotateKeep archives", async () => {
288
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-keep-"));
289
+ try {
290
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
291
+ const keep = 2;
292
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 32, impressionsRotateKeep: keep });
293
+ await store.load();
294
+
295
+ // Bootstrap the state dir + active file (record() creates them).
296
+ await store.record({ sessionKey: "seed", query: "seed", memoryIds: [] });
297
+
298
+ // Force several rotations by growing the active file before each append.
299
+ for (let i = 0; i < 5; i += 1) {
300
+ await writeFile(impressionsPath, "y".repeat(64), "utf8");
301
+ await store.record({ sessionKey: `s${i}`, query: `q${i}`, memoryIds: [] });
302
+ }
303
+
304
+ assert.equal(await fileExists(`${impressionsPath}.1`), true);
305
+ assert.equal(await fileExists(`${impressionsPath}.2`), true);
306
+ assert.equal(await fileExists(`${impressionsPath}.3`), false, "archives beyond keep are dropped");
307
+ } finally {
308
+ await rm(dir, { recursive: true, force: true });
309
+ }
310
+ });
311
+
312
+ test("LastRecallStore never rotates when impressionsRotateBytes is 0 (disabled)", async () => {
313
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-off-"));
314
+ try {
315
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
316
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 0, impressionsRotateKeep: 5 });
317
+ await store.load();
318
+
319
+ await store.record({ sessionKey: "s1", query: "q1", memoryIds: [] });
320
+ await writeFile(impressionsPath, "z".repeat(1024), "utf8");
321
+ await store.record({ sessionKey: "s2", query: "q2", memoryIds: [] });
322
+
323
+ assert.equal(await fileExists(`${impressionsPath}.1`), false, "rotation disabled leaves no archive");
324
+ } finally {
325
+ await rm(dir, { recursive: true, force: true });
326
+ }
327
+ });
328
+
329
+ test("LastRecallStore serializes concurrent impression writes without losing rows (#1910)", async () => {
330
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-concurrent-"));
331
+ try {
332
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
333
+ // Small threshold forces rotations mid-flight; a large keep means no archive
334
+ // falls off the end, so every appended row survives somewhere.
335
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 200, impressionsRotateKeep: 100 });
336
+ await store.load();
337
+
338
+ const total = 24;
339
+ await Promise.all(
340
+ Array.from({ length: total }, (_, i) =>
341
+ store.record({ sessionKey: `s${i}`, query: `q${i}`, memoryIds: [`m${i}`] }),
342
+ ),
343
+ );
344
+
345
+ // Gather every impression line across the active file and all archives.
346
+ const stateDir = path.dirname(impressionsPath);
347
+ const files = (await readdir(stateDir))
348
+ .filter((f) => f.startsWith("recall_impressions.jsonl"))
349
+ .sort();
350
+ const lines: string[] = [];
351
+ for (const f of files) {
352
+ const content = await readFile(path.join(stateDir, f), "utf8");
353
+ for (const line of content.split("\n")) {
354
+ if (line.trim()) lines.push(line);
355
+ }
356
+ }
357
+ // Serialization guarantees no interleaved/torn writes: every line parses and
358
+ // every session key appears exactly once.
359
+ const keys = new Set<string>();
360
+ for (const line of lines) {
361
+ const parsed = JSON.parse(line) as { sessionKey: string };
362
+ keys.add(parsed.sessionKey);
363
+ }
364
+ assert.equal(lines.length, total, "no impression rows lost or duplicated under concurrency");
365
+ assert.equal(keys.size, total, "every session key persisted exactly once");
366
+ } finally {
367
+ await rm(dir, { recursive: true, force: true });
368
+ }
369
+ });
370
+
371
+ test("LastRecallStore serializes rotation across processes sharing memoryDir (#1910)", async () => {
372
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-crossproc-"));
373
+ try {
374
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
375
+ // Two independent stores over the SAME memoryDir stand in for two processes.
376
+ // Each has its own in-process write chain, so only the on-disk rotation lock
377
+ // keeps their archive shifts from interleaving and stomping each other's
378
+ // `.1`. A large keep means nothing falls off the end, so every appended row
379
+ // must survive somewhere.
380
+ const makeStore = async () => {
381
+ const s = new LastRecallStore(dir, { impressionsRotateBytes: 200, impressionsRotateKeep: 100 });
382
+ await s.load();
383
+ return s;
384
+ };
385
+ const storeA = await makeStore();
386
+ const storeB = await makeStore();
387
+
388
+ const total = 40;
389
+ await Promise.all(
390
+ Array.from({ length: total }, (_, i) =>
391
+ (i % 2 === 0 ? storeA : storeB).record({
392
+ sessionKey: `s${i}`,
393
+ query: `q${i}`,
394
+ memoryIds: [`m${i}`],
395
+ }),
396
+ ),
397
+ );
398
+
399
+ const stateDir = path.dirname(impressionsPath);
400
+ const files = (await readdir(stateDir)).filter(
401
+ (f) => f.startsWith("recall_impressions.jsonl") && !f.endsWith(".lock"),
402
+ );
403
+ const keys = new Set<string>();
404
+ let lineCount = 0;
405
+ for (const f of files) {
406
+ const content = await readFile(path.join(stateDir, f), "utf8");
407
+ for (const line of content.split("\n")) {
408
+ if (!line.trim()) continue;
409
+ lineCount += 1;
410
+ const parsed = JSON.parse(line) as { sessionKey: string };
411
+ keys.add(parsed.sessionKey);
412
+ }
413
+ }
414
+ assert.equal(lineCount, total, "no impression rows lost to a cross-process rotation race");
415
+ assert.equal(keys.size, total, "every session key persisted exactly once across both processes");
416
+ } finally {
417
+ await rm(dir, { recursive: true, force: true });
418
+ }
419
+ });
420
+
421
+ test("LastRecallStore preserves the current impression when rotation fails (#1910)", async () => {
422
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-rotate-fail-"));
423
+ try {
424
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
425
+ await mkdir(path.dirname(impressionsPath), { recursive: true });
426
+ // Seed an active file over the (tiny) threshold so rotation is attempted.
427
+ await writeFile(impressionsPath, "seed-row-that-exceeds-threshold\n", "utf8");
428
+ // Make the .1 archive target a non-empty directory so the active→.1 rename
429
+ // fails with a non-ENOENT error, forcing rotateImpressionsIfNeeded to throw.
430
+ await mkdir(`${impressionsPath}.1`, { recursive: true });
431
+ await writeFile(path.join(`${impressionsPath}.1`, "blocker"), "x", "utf8");
432
+
433
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 1, impressionsRotateKeep: 1 });
434
+ await store.load();
435
+ await store.record({ sessionKey: "preserved", query: "q", memoryIds: [] });
436
+
437
+ // Rotation threw, but the current impression must still be appended.
438
+ const active = await readFile(impressionsPath, "utf8");
439
+ assert.ok(active.includes('"sessionKey":"preserved"'), "impression preserved despite rotation failure");
440
+ // The blocking directory remains (rotation could not move over it).
441
+ assert.ok((await stat(`${impressionsPath}.1`)).isDirectory(), "rotation did not clobber the blocked target");
442
+ } finally {
443
+ await rm(dir, { recursive: true, force: true });
444
+ }
445
+ });
446
+
447
+ test("LastRecallStore spills an impression to the durable pending queue on rotation-lock timeout, never writing the active file unlocked (#2033)", async () => {
448
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-lock-timeout-"));
449
+ try {
450
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
451
+ await mkdir(path.dirname(impressionsPath), { recursive: true });
452
+ // Seed the active file over the threshold so rotation WOULD be attempted.
453
+ const seeded = `${"x".repeat(256)}\n`;
454
+ await writeFile(impressionsPath, seeded, "utf8");
455
+ // Hold the cross-process rotation lock from a foreign owner with a fresh
456
+ // mtime so it is not broken as stale within the acquisition budget. Our
457
+ // record() must then observe acquired=false.
458
+ const lockPath = `${impressionsPath}.lock`;
459
+ await writeFile(lockPath, "999999 foreign-owner 2999-01-01T00:00:00.000Z\n", "utf8");
460
+
461
+ // Tiny maxWaitMs so the acquisition times out deterministically instead of
462
+ // blocking for the default multi-second budget.
463
+ const store = new LastRecallStore(dir, {
464
+ impressionsRotateBytes: 128,
465
+ impressionsRotateKeep: 5,
466
+ impressionsLockOptions: { maxWaitMs: 40, pollMs: 10 },
467
+ });
468
+ await store.load();
469
+ await store.record({ sessionKey: "spilled-append", query: "q", memoryIds: [] });
470
+
471
+ // Rotation was refused because the lock could not be acquired: no archive
472
+ // shift ran, so an unlocked rename never raced a peer's rotation.
473
+ assert.equal(
474
+ await fileExists(`${impressionsPath}.1`),
475
+ false,
476
+ "rotation must be skipped when the cross-process lock is not acquired",
477
+ );
478
+ // The impression is NEVER written to the active file unlocked — that could
479
+ // land it in the inode a peer renames to `.1`. The active file is untouched.
480
+ assert.equal(
481
+ await readFile(impressionsPath, "utf8"),
482
+ seeded,
483
+ "active file must not be written while the rotation lock is held elsewhere",
484
+ );
485
+ // It is durably queued in the pending spill instead, not dropped.
486
+ const pendingDir = `${impressionsPath}.pending.d`;
487
+ const spillNames = await readdir(pendingDir);
488
+ assert.equal(spillNames.length, 1, "exactly one impression spilled to the pending queue");
489
+ assert.ok(
490
+ (await readFile(path.join(pendingDir, spillNames[0]!), "utf8")).includes('"sessionKey":"spilled-append"'),
491
+ "spilled impression durably queued",
492
+ );
493
+
494
+ // Release the lock; the next record() DOES acquire it and folds the spill
495
+ // back into the active file (after rotation) before appending its own row.
496
+ await rm(lockPath, { force: true });
497
+ await store.record({ sessionKey: "drained-append", query: "q2", memoryIds: [] });
498
+ const active = await readFile(`${impressionsPath}.1`, "utf8").catch(() => "")
499
+ + await readFile(impressionsPath, "utf8");
500
+ assert.ok(active.includes('"sessionKey":"spilled-append"'), "spilled impression folded back in");
501
+ assert.ok(active.includes('"sessionKey":"drained-append"'), "current impression appended");
502
+ assert.equal(
503
+ (await readdir(pendingDir)).length,
504
+ 0,
505
+ "pending spill drained empty after a successful locked append",
506
+ );
507
+ } finally {
508
+ await rm(dir, { recursive: true, force: true });
509
+ }
510
+ });
511
+
512
+ test("LastRecallStore takes the shared rotation lock for active-file appends even when local rotation is disabled, spilling on a peer lock/rename race (#2033)", async () => {
513
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-off-lock-"));
514
+ try {
515
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
516
+ await mkdir(path.dirname(impressionsPath), { recursive: true });
517
+ // This writer disables its OWN rotation (bytes=0), but a peer writer with
518
+ // rotation enabled shares the same on-disk lock and may rename the active
519
+ // inode to `.1` at any moment. An unlocked append here could land in that
520
+ // offline-sync-excluded archive and be silently dropped (#2033).
521
+ const seeded = "seed-row\n";
522
+ await writeFile(impressionsPath, seeded, "utf8");
523
+ // A peer holds the cross-process rotation lock (fresh mtime, not stale
524
+ // within the budget), standing in for a peer mid-rotation.
525
+ const lockPath = `${impressionsPath}.lock`;
526
+ await writeFile(lockPath, "999999 foreign-owner 2999-01-01T00:00:00.000Z\n", "utf8");
527
+
528
+ const store = new LastRecallStore(dir, {
529
+ impressionsRotateBytes: 0, // local rotation disabled
530
+ impressionsRotateKeep: 5,
531
+ impressionsLockOptions: { maxWaitMs: 40, pollMs: 10 },
532
+ });
533
+ await store.load();
534
+ await store.record({ sessionKey: "off-spilled", query: "q", memoryIds: [] });
535
+
536
+ // Even with local rotation disabled, the append took the shared lock, could
537
+ // not acquire it, and spilled rather than writing the active file the peer
538
+ // may rename to `.1`.
539
+ assert.equal(
540
+ await readFile(impressionsPath, "utf8"),
541
+ seeded,
542
+ "active file untouched while a peer holds the rotation lock, even with local rotation disabled",
543
+ );
544
+ const pendingDir = `${impressionsPath}.pending.d`;
545
+ const spillNames = await readdir(pendingDir);
546
+ assert.equal(spillNames.length, 1, "impression spilled to the durable pending queue");
547
+ assert.ok(
548
+ (await readFile(path.join(pendingDir, spillNames[0]!), "utf8")).includes('"sessionKey":"off-spilled"'),
549
+ "spilled impression durably queued",
550
+ );
551
+
552
+ // Release the lock; the next append acquires it and folds the spill back into
553
+ // the active file. Local rotation stays disabled, so no archive is created.
554
+ await rm(lockPath, { force: true });
555
+ await store.record({ sessionKey: "off-drained", query: "q2", memoryIds: [] });
556
+ const active = await readFile(impressionsPath, "utf8");
557
+ assert.ok(active.includes('"sessionKey":"off-spilled"'), "spilled impression folded back in");
558
+ assert.ok(active.includes('"sessionKey":"off-drained"'), "current impression appended");
559
+ assert.equal(await fileExists(`${impressionsPath}.1`), false, "local rotation stays disabled (no archive)");
560
+ assert.equal((await readdir(pendingDir)).length, 0, "pending spill drained empty");
561
+ } finally {
562
+ await rm(dir, { recursive: true, force: true });
563
+ }
564
+ });
565
+
566
+ test("LastRecallStore never re-appends a drained impression when its spill unlink fails (#2033)", async () => {
567
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-dup-"));
568
+ const pendingDir = path.join(dir, "state", "recall_impressions.jsonl.pending.d");
569
+ try {
570
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
571
+ await mkdir(pendingDir, { recursive: true });
572
+ // A durable spill left by a prior lock-timed-out append.
573
+ await writeFile(
574
+ path.join(pendingDir, "spill.jsonl"),
575
+ `${JSON.stringify({ sessionKey: "evt-spill" })}\n`,
576
+ "utf8",
577
+ );
578
+ // Rotation stays out of the way: threshold far above anything written here.
579
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 1_000_000, impressionsRotateKeep: 5 });
580
+ await store.load();
581
+
582
+ // Make the pending dir non-writable so the drain can READ the spill but its
583
+ // CLAIM (a rename to `<uuid>.jsonl.claimed`, which needs directory write
584
+ // permission) FAILS. Because the crash-safe fix claims each spill by rename
585
+ // BEFORE committing its rows, a spill that cannot be claimed is skipped this
586
+ // pass rather than appended-then-left-behind — so it can never be re-read and
587
+ // duplicated on the next drain.
588
+ await chmod(pendingDir, 0o555);
589
+ await store.record({ sessionKey: "cur1", query: "q1", memoryIds: [] });
590
+
591
+ let active = await readFile(impressionsPath, "utf8");
592
+ assert.ok(active.includes("cur1"), "current impression appended");
593
+ assert.ok(
594
+ !active.includes("evt-spill"),
595
+ "unclaimable spill must NOT be appended (would otherwise duplicate on the next drain)",
596
+ );
597
+ assert.equal((await readdir(pendingDir)).length, 1, "unclaimed spill remains for a later pass");
598
+
599
+ // Restore write permission; the next drain claims the spill and folds it in
600
+ // exactly once.
601
+ await chmod(pendingDir, 0o755);
602
+ await store.record({ sessionKey: "cur2", query: "q2", memoryIds: [] });
603
+ active = await readFile(impressionsPath, "utf8");
604
+ const spillOccurrences = active.split("evt-spill").length - 1;
605
+ assert.equal(spillOccurrences, 1, "drained impression folded in exactly once — no duplicate");
606
+ assert.ok(active.includes("cur2"), "later impression appended");
607
+ assert.equal((await readdir(pendingDir)).length, 0, "pending spill drained empty");
608
+ } finally {
609
+ await chmod(pendingDir, 0o755).catch(() => undefined);
610
+ await rm(dir, { recursive: true, force: true });
611
+ }
612
+ });
613
+
614
+ test("LastRecallStore.drainPendingImpressions folds only what fits the synced active file under the cap and defers the rest instead of rotating drained rows into sync-excluded archives (#2033)", async () => {
615
+ // Regression for the pre-sync-rotation thread: a pre-snapshot drain must NEVER
616
+ // rotate a just-drained row into recall_impressions.jsonl.1/.2, because those
617
+ // archives are offline-sync-EXCLUDED — rotating a drained row there and then
618
+ // deleting its spill would leave the row only in a path sync omits, lost when
619
+ // the node is discarded after a "successful" sync. Instead the drain folds
620
+ // only the rows that fit the synced active file under the cap and leaves the
621
+ // rest durable in the pending queue, reporting the drain deferred.
622
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-bound-"));
623
+ try {
624
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
625
+ const pendingDir = `${impressionsPath}.pending.d`;
626
+ await mkdir(pendingDir, { recursive: true });
627
+ const cap = 200;
628
+ // Six ~97-byte spill rows (~582 bytes total) — nearly 3x the cap. Two fit the
629
+ // active file under the cap (97 + 97 = 194 <= 200); the third would need a
630
+ // rotation into an excluded archive, so the drain stops there.
631
+ const sessionKeys: string[] = [];
632
+ for (let i = 0; i < 6; i += 1) {
633
+ const key = `oversized-${i}`;
634
+ sessionKeys.push(key);
635
+ await writeFile(
636
+ path.join(pendingDir, `spill-${i}.jsonl`),
637
+ `${JSON.stringify({ sessionKey: key, writeNonce: `n-${i}`, pad: "p".repeat(40) })}\n`,
638
+ "utf-8",
639
+ );
640
+ }
641
+
642
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: cap, impressionsRotateKeep: 10 });
643
+ await store.load();
644
+ assert.deepEqual(
645
+ await store.drainPendingImpressions(),
646
+ { folded: true, pendingDeferred: true },
647
+ "folded what fits, deferred the overflow that would rotate into an excluded archive",
648
+ );
649
+
650
+ // The synced active file holds the folded rows and stays within the cap.
651
+ const activeSize = (await stat(impressionsPath)).size;
652
+ assert.ok(activeSize <= cap, `active file (${activeSize}B) stays within the ${cap}B cap`);
653
+ // No archive was created: no drained row was moved into a sync-excluded path.
654
+ assert.equal(await fileExists(`${impressionsPath}.1`), false, "no drained row rotated into a sync-excluded archive");
655
+
656
+ const active = await readFile(impressionsPath, "utf-8");
657
+ const foldedKeys = sessionKeys.filter((k) => active.includes(k));
658
+ const remaining = await readdir(pendingDir);
659
+ assert.equal(foldedKeys.length, 2, "exactly the two rows that fit the cap were folded");
660
+ assert.equal(remaining.length, 4, "the deferred overflow stays durable in the pending queue");
661
+ // No row is dropped or duplicated: folded + still-pending accounts for all six.
662
+ assert.equal(foldedKeys.length + remaining.length, sessionKeys.length, "every row is either folded or still pending — none lost");
663
+ for (const key of foldedKeys) {
664
+ assert.equal(active.split(key).length - 1, 1, `${key} folded exactly once`);
665
+ }
666
+
667
+ // A second drain makes no further progress — the active file is already at
668
+ // the cap, so the next row would still need an excluded-archive rotation.
669
+ // The rows stay durable and the drain stays deferred rather than losing them.
670
+ assert.deepEqual(
671
+ await store.drainPendingImpressions(),
672
+ { folded: false, pendingDeferred: true },
673
+ "a full active file defers the remaining spills rather than rotating them into excluded archives",
674
+ );
675
+ assert.equal((await readdir(pendingDir)).length, 4, "deferred spills remain durable across retries");
676
+ } finally {
677
+ await rm(dir, { recursive: true, force: true });
678
+ }
679
+ });
680
+
681
+ test("LastRecallStore.drainPendingImpressions folds a large pending batch one spill at a time, preserving every row without materializing the whole queue (#2033)", async () => {
682
+ // Regression for the huge-drain-under-lock thread: the fold must not join and
683
+ // split the entire pending queue as one synchronous payload while holding the
684
+ // rotation lock — that blocks the event loop long enough for the lock-refresh
685
+ // timer to miss and a peer to stale-break the lock. Each spill is folded as an
686
+ // independent claim -> append -> finalize unit with an await between files.
687
+ // With rotation disabled the whole batch lands in the synced active file; the
688
+ // drain completes cleanly with nothing deferred.
689
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-batch-"));
690
+ try {
691
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
692
+ const pendingDir = `${impressionsPath}.pending.d`;
693
+ await mkdir(pendingDir, { recursive: true });
694
+ const count = 40;
695
+ const keys: string[] = [];
696
+ for (let i = 0; i < count; i += 1) {
697
+ // Zero-padded names so the sorted spill order is deterministic.
698
+ const key = `batch-${String(i).padStart(3, "0")}`;
699
+ keys.push(key);
700
+ await writeFile(
701
+ path.join(pendingDir, `spill-${String(i).padStart(3, "0")}.jsonl`),
702
+ `${JSON.stringify({ sessionKey: key, writeNonce: `n-${i}` })}\n`,
703
+ "utf-8",
704
+ );
705
+ }
706
+
707
+ // Rotation disabled: the whole batch folds into the active file, no archives.
708
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 0, impressionsRotateKeep: 5 });
709
+ await store.load();
710
+ assert.deepEqual(
711
+ await store.drainPendingImpressions(),
712
+ { folded: true, pendingDeferred: false },
713
+ "entire batch folded, nothing deferred",
714
+ );
715
+
716
+ const active = await readFile(impressionsPath, "utf-8");
717
+ const foldedOrder = active
718
+ .split("\n")
719
+ .filter((l) => l.trim())
720
+ .map((l) => {
721
+ const parsed = JSON.parse(l) as { sessionKey: string };
722
+ return parsed.sessionKey;
723
+ });
724
+ assert.deepEqual(foldedOrder, keys, "every row folded exactly once, in spill-name order");
725
+ assert.equal(await fileExists(`${impressionsPath}.1`), false, "rotation disabled: no archive created");
726
+ assert.deepEqual(await readdir(pendingDir), [], "pending queue drained empty");
727
+ } finally {
728
+ await rm(dir, { recursive: true, force: true });
729
+ }
730
+ });
731
+
732
+ test("LastRecallStore recovers an impression spill claim orphaned by a crash before commit — the row is never lost (#2033)", async () => {
733
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-crash-claim-"));
734
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
735
+ const pendingDir = `${impressionsPath}.pending.d`;
736
+ try {
737
+ await mkdir(pendingDir, { recursive: true });
738
+ // Simulate a drain that CLAIMED a spill (renamed q.jsonl -> q.jsonl.claimed)
739
+ // and then CRASHED before appending the rows to the active file. The
740
+ // `.claimed` file is the ONLY durable copy of the impression — the old
741
+ // read-then-unlink ordering would already have deleted it and lost the row.
742
+ await writeFile(
743
+ path.join(pendingDir, "orphan.jsonl.claimed"),
744
+ `${JSON.stringify({ sessionKey: "evt-orphan" })}\n`,
745
+ "utf8",
746
+ );
747
+ // Rotation stays out of the way so both rows land in the active file.
748
+ const store = new LastRecallStore(dir, { impressionsRotateBytes: 1_000_000, impressionsRotateKeep: 5 });
749
+ await store.load();
750
+ await store.record({ sessionKey: "cur", query: "q", memoryIds: [] });
751
+
752
+ const active = await readFile(impressionsPath, "utf8");
753
+ assert.ok(active.includes("evt-orphan"), "crash-orphaned claim recovered into the active file — not lost");
754
+ assert.equal(active.split("evt-orphan").length - 1, 1, "orphaned impression committed exactly once");
755
+ assert.ok(active.includes('"sessionKey":"cur"'), "current impression appended");
756
+ assert.equal((await readdir(pendingDir)).length, 0, "recovered claim cleaned up after commit");
757
+ } finally {
758
+ await rm(dir, { recursive: true, force: true });
759
+ }
760
+ });
761
+
762
+ test("LastRecallStore refuses to write an impression spill into a symlinked pending directory (#2033)", async () => {
763
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-symlink-spill-"));
764
+ const outside = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-symlink-outside-"));
765
+ try {
766
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
767
+ await mkdir(path.dirname(impressionsPath), { recursive: true });
768
+ // Plant a symlink AT the spill-directory path pointing at a real directory
769
+ // outside the memory store. The write path must refuse it before any file
770
+ // lands in the target.
771
+ const pendingDir = `${impressionsPath}.pending.d`;
772
+ await symlink(outside, pendingDir);
773
+
774
+ // Force the spill branch: hold the rotation lock from a foreign owner so
775
+ // record()'s append cannot acquire it and falls back to spilling.
776
+ const lockPath = `${impressionsPath}.lock`;
777
+ await writeFile(lockPath, "999999 foreign-owner 2999-01-01T00:00:00.000Z\n", "utf8");
778
+ const store = new LastRecallStore(dir, {
779
+ impressionsRotateBytes: 0,
780
+ impressionsRotateKeep: 5,
781
+ impressionsLockOptions: { maxWaitMs: 40, pollMs: 10 },
782
+ });
783
+ await store.load();
784
+ // The spill refusal is swallowed as a logged append failure (record never
785
+ // throws), but nothing may be written through the poisoned link.
786
+ await store.record({ sessionKey: "symlink-refused", query: "q", memoryIds: [] });
787
+
788
+ assert.equal(
789
+ (await lstat(pendingDir)).isSymbolicLink(),
790
+ true,
791
+ "spill directory symlink left intact (not replaced by a real dir)",
792
+ );
793
+ assert.deepEqual(
794
+ await readdir(outside),
795
+ [],
796
+ "no spill file leaked through the symlink into the outside directory",
797
+ );
798
+ } finally {
799
+ await rm(dir, { recursive: true, force: true });
800
+ await rm(outside, { recursive: true, force: true });
801
+ }
802
+ });
803
+
804
+ test("LastRecallStore writes an impression spill via a temp name then renames to *.jsonl so a drain never sees a partial spill (#2033)", async () => {
805
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-atomic-spill-"));
806
+ try {
807
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
808
+ await mkdir(path.dirname(impressionsPath), { recursive: true });
809
+ // Force the spill branch: a foreign owner holds the rotation lock so
810
+ // record()'s append cannot acquire it and falls back to spilling.
811
+ const lockPath = `${impressionsPath}.lock`;
812
+ await writeFile(lockPath, "999999 foreign-owner 2999-01-01T00:00:00.000Z\n", "utf8");
813
+ const store = new LastRecallStore(dir, {
814
+ impressionsRotateBytes: 0,
815
+ impressionsRotateKeep: 5,
816
+ impressionsLockOptions: { maxWaitMs: 40, pollMs: 10 },
817
+ });
818
+ await store.load();
819
+ await store.record({ sessionKey: "atomic-spill", query: "q", memoryIds: [] });
820
+
821
+ const pendingDir = `${impressionsPath}.pending.d`;
822
+ const entries = await readdir(pendingDir);
823
+ // Exactly one spill, named with the final `.jsonl` suffix — no temp artifact
824
+ // left behind, and nothing ending in the temp `.tmp` suffix.
825
+ assert.equal(entries.length, 1, "exactly one spill file after the rename");
826
+ assert.ok(entries[0].endsWith(".jsonl"), "spill has the final .jsonl suffix");
827
+ assert.ok(!entries[0].endsWith(".jsonl.tmp"), "no temp artifact left behind");
828
+
829
+ // The drain's own lister (the exact one a concurrent lock holder runs) sees a
830
+ // single COMPLETE spill — the temp name is invisible to it, so a drain can
831
+ // never read/rename a partial `.jsonl`.
832
+ const listed = await listContainedSpillFiles(pendingDir);
833
+ assert.equal(listed.length, 1, "drain lister sees exactly the one committed spill");
834
+ const spilled = JSON.parse(await readFile(listed[0], "utf8"));
835
+ assert.equal(spilled.sessionKey, "atomic-spill", "spilled row is complete, parseable JSON");
836
+ } finally {
837
+ await rm(dir, { recursive: true, force: true });
838
+ }
839
+ });
840
+
841
+ test("LastRecallStore.drainPendingImpressions folds pending spills into the synced active file (#2033)", async () => {
842
+ // Regression for the offline-sync impression-drain thread: a record() that
843
+ // times out on the rotation lock spills to the offline-sync-EXCLUDED
844
+ // recall_impressions.jsonl.pending.d/. drainPendingImpressions() folds those
845
+ // rows back into the synced active file so a snapshot can capture them.
846
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-"));
847
+ try {
848
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
849
+ const pendingDir = `${impressionsPath}.pending.d`;
850
+ await mkdir(pendingDir, { recursive: true });
851
+ const row = `${JSON.stringify({ sessionKey: "s1", writeNonce: "n-1", memoryIds: ["m-1"] })}\n`;
852
+ await writeFile(path.join(pendingDir, "spill-1.jsonl"), row, "utf-8");
853
+
854
+ const store = new LastRecallStore(dir);
855
+ await store.load();
856
+
857
+ assert.deepEqual(
858
+ await store.drainPendingImpressions(),
859
+ { folded: true, pendingDeferred: false },
860
+ "drain reports rows folded and nothing deferred",
861
+ );
862
+ assert.equal(await readFile(impressionsPath, "utf-8"), row, "spill folded verbatim into active file");
863
+ assert.deepEqual(await readdir(pendingDir), [], "committed spill deleted from the pending queue");
864
+
865
+ // Nothing pending now → fast no-op that folds nothing and defers nothing.
866
+ assert.deepEqual(
867
+ await store.drainPendingImpressions(),
868
+ { folded: false, pendingDeferred: false },
869
+ "second drain is a no-op",
870
+ );
871
+ } finally {
872
+ await rm(dir, { recursive: true, force: true });
873
+ }
874
+ });
875
+
876
+ test("LastRecallStore.drainPendingImpressions is a side-effect-free no-op when nothing is pending (#2033)", async () => {
877
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-noop-"));
878
+ try {
879
+ const store = new LastRecallStore(dir);
880
+ await store.load();
881
+ assert.deepEqual(
882
+ await store.drainPendingImpressions(),
883
+ { folded: false, pendingDeferred: false },
884
+ "no pending dir → no drain, nothing deferred",
885
+ );
886
+ // A no-op drain must not create the state dir or a transient lock file.
887
+ await assert.rejects(() => readdir(path.join(dir, "state")), /ENOENT/);
888
+ } finally {
889
+ await rm(dir, { recursive: true, force: true });
890
+ }
891
+ });
892
+
893
+ test("LastRecallStore.drainPendingImpressions recovers a crash-orphaned .claimed spill (#2033)", async () => {
894
+ // A crash between claim and commit leaves the rows as a `<uuid>.jsonl.claimed`
895
+ // orphan — the only durable copy. The drain must recover it (rename back to
896
+ // `.jsonl`) and fold it into the active file, never lose it.
897
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-orphan-"));
898
+ try {
899
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
900
+ const pendingDir = `${impressionsPath}.pending.d`;
901
+ await mkdir(pendingDir, { recursive: true });
902
+ const row = `${JSON.stringify({ sessionKey: "s1", writeNonce: "n-1", memoryIds: ["m-1"] })}\n`;
903
+ await writeFile(path.join(pendingDir, "spill-1.jsonl.claimed"), row, "utf-8");
904
+
905
+ const store = new LastRecallStore(dir);
906
+ await store.load();
907
+
908
+ assert.deepEqual(
909
+ await store.drainPendingImpressions(),
910
+ { folded: true, pendingDeferred: false },
911
+ "orphaned claim recovered and folded",
912
+ );
913
+ assert.equal(await readFile(impressionsPath, "utf-8"), row, "recovered row appended to active file");
914
+ assert.deepEqual(await readdir(pendingDir), [], "recovered spill finalized");
915
+ } finally {
916
+ await rm(dir, { recursive: true, force: true });
917
+ }
918
+ });
919
+
920
+ test("LastRecallStore.drainPendingImpressions recovers a completed spill temp after a crash (#2033)", async () => {
921
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-temp-"));
922
+ try {
923
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
924
+ const pendingDir = `${impressionsPath}.pending.d`;
925
+ await mkdir(pendingDir, { recursive: true });
926
+ const row = `${JSON.stringify({ sessionKey: "s-temp", writeNonce: "n-temp", memoryIds: ["m-temp"] })}\n`;
927
+ await writeFile(path.join(pendingDir, "spill-1.jsonl.tmp"), row, "utf-8");
928
+
929
+ const store = new LastRecallStore(dir);
930
+ await store.load();
931
+
932
+ assert.deepEqual(
933
+ await store.drainPendingImpressions(),
934
+ { folded: true, pendingDeferred: false },
935
+ "completed temp is promoted and folded",
936
+ );
937
+ assert.equal(await readFile(impressionsPath, "utf-8"), row, "recovered temp appended verbatim");
938
+ assert.deepEqual(await readdir(pendingDir), [], "recovered temp finalized");
939
+ } finally {
940
+ await rm(dir, { recursive: true, force: true });
941
+ }
942
+ });
943
+
944
+ test("LastRecallStore.drainPendingImpressions defers an incomplete spill temp (#2033)", async () => {
945
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-temp-incomplete-"));
946
+ try {
947
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
948
+ const pendingDir = `${impressionsPath}.pending.d`;
949
+ await mkdir(pendingDir, { recursive: true });
950
+ await writeFile(path.join(pendingDir, "spill-1.jsonl.tmp"), '{"sessionKey":"incomplete"', "utf-8");
951
+
952
+ const store = new LastRecallStore(dir);
953
+ await store.load();
954
+
955
+ assert.deepEqual(
956
+ await store.drainPendingImpressions(),
957
+ { folded: false, pendingDeferred: true },
958
+ "incomplete temp remains deferred rather than being discarded",
959
+ );
960
+ assert.deepEqual(await readdir(pendingDir), ["spill-1.jsonl.tmp"], "incomplete temp is preserved");
961
+ assert.equal(await fileExists(impressionsPath), false, "incomplete temp is never appended");
962
+ } finally {
963
+ await rm(dir, { recursive: true, force: true });
964
+ }
965
+ });
966
+
967
+ test("LastRecallStore.drainPendingImpressions reports pendingDeferred and never claims success when the rotation lock is held (#2033)", async () => {
968
+ // Regression for the offline-sync lock-timeout thread (PRRT_kwDORJXyws6SE4Ac):
969
+ // a pending spill exists but a PEER holds the rotation lock, so the drain
970
+ // cannot fold it into the synced active file. The drain MUST report
971
+ // pendingDeferred=true (a distinct deferred signal) and MUST NOT touch the
972
+ // offline-sync-EXCLUDED spill — otherwise a snapshot taken now would silently
973
+ // omit the recorded impression while the caller believes the drain succeeded.
974
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-locked-"));
975
+ try {
976
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
977
+ const pendingDir = `${impressionsPath}.pending.d`;
978
+ await mkdir(pendingDir, { recursive: true });
979
+ const row = `${JSON.stringify({ sessionKey: "s1", writeNonce: "n-1", memoryIds: ["m-1"] })}\n`;
980
+ await writeFile(path.join(pendingDir, "spill-1.jsonl"), row, "utf-8");
981
+
982
+ // Foreign-owner lock with a far-future timestamp so it is NOT broken as
983
+ // stale within the acquisition budget; the drain must observe acquired=false.
984
+ const lockPath = `${impressionsPath}.lock`;
985
+ await writeFile(lockPath, "999999 foreign-owner 2999-01-01T00:00:00.000Z\n", "utf8");
986
+
987
+ // Tiny maxWaitMs so acquisition times out deterministically.
988
+ const store = new LastRecallStore(dir, {
989
+ impressionsLockOptions: { maxWaitMs: 40, pollMs: 10 },
990
+ });
991
+ await store.load();
992
+
993
+ assert.deepEqual(
994
+ await store.drainPendingImpressions(),
995
+ { folded: false, pendingDeferred: true },
996
+ "lock held → drain is deferred, never reports a fold",
997
+ );
998
+ // The active file was NEVER written unlocked: a snapshot now would miss the
999
+ // spilled impression, which is exactly why the caller must not treat a
1000
+ // deferred drain as a clean snapshot.
1001
+ assert.equal(
1002
+ await fileExists(impressionsPath),
1003
+ false,
1004
+ "pending spill must NOT be folded into the active file while the lock is held",
1005
+ );
1006
+ // The spill is preserved in the offline-sync-EXCLUDED queue for a later
1007
+ // lock holder (or caller retry) to fold — deferred, never dropped.
1008
+ assert.deepEqual(
1009
+ await readdir(pendingDir),
1010
+ ["spill-1.jsonl"],
1011
+ "deferred spill stays in the pending queue",
1012
+ );
1013
+ } finally {
1014
+ await rm(dir, { recursive: true, force: true });
1015
+ }
1016
+ });
1017
+
1018
+ test("LastRecallStore.drainPendingImpressions reports pendingDeferred when a spill stays unclaimable even though the lock was acquired (#2033)", async () => {
1019
+ // Regression for finding 1: acquiring the rotation lock is NOT sufficient to
1020
+ // report a clean drain. If the fold cannot claim a spill (rename race), its
1021
+ // durable rows stay in the offline-sync-EXCLUDED queue. The drain must report
1022
+ // the fold INCOMPLETE (pendingDeferred=true) — even when it DID fold other
1023
+ // spills — so the caller retries/aborts instead of snapshotting an active file
1024
+ // that omits the leftover row.
1025
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-impressions-drain-leftover-"));
1026
+ try {
1027
+ const impressionsPath = path.join(dir, "state", "recall_impressions.jsonl");
1028
+ const pendingDir = `${impressionsPath}.pending.d`;
1029
+ await mkdir(pendingDir, { recursive: true });
1030
+ const foldable = `${JSON.stringify({ sessionKey: "foldable", writeNonce: "n-ok" })}\n`;
1031
+ const stuck = `${JSON.stringify({ sessionKey: "stuck", writeNonce: "n-stuck" })}\n`;
1032
+ await writeFile(path.join(pendingDir, "a-foldable.jsonl"), foldable, "utf-8");
1033
+ await writeFile(path.join(pendingDir, "b-stuck.jsonl"), stuck, "utf-8");
1034
+ // Block ONLY b-stuck's claim: a directory sits at its `.claimed` target, so
1035
+ // the claim rename (file -> directory) fails while a-foldable claims cleanly.
1036
+ await mkdir(path.join(pendingDir, "b-stuck.jsonl.claimed"), { recursive: true });
1037
+
1038
+ const store = new LastRecallStore(dir);
1039
+ await store.load();
1040
+ assert.deepEqual(
1041
+ await store.drainPendingImpressions(),
1042
+ { folded: true, pendingDeferred: true },
1043
+ "folded the claimable spill but a leftover remains → deferred, never a clean drain",
1044
+ );
1045
+ // The claimable row reached the active file; the stuck row did not.
1046
+ const active = await readFile(impressionsPath, "utf-8");
1047
+ assert.ok(active.includes("foldable"), "claimable spill folded into the active file");
1048
+ assert.ok(!active.includes('"sessionKey":"stuck"'), "unclaimable spill NOT folded — still excluded");
1049
+ // The leftover live spill is preserved for a later pass.
1050
+ assert.ok(
1051
+ (await readdir(pendingDir)).includes("b-stuck.jsonl"),
1052
+ "leftover spill preserved in the pending queue for a retry",
1053
+ );
1054
+ } finally {
1055
+ await rm(dir, { recursive: true, force: true });
1056
+ }
1057
+ });