@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
@@ -17,37 +17,37 @@ import test from "node:test";
17
17
  import assert from "node:assert/strict";
18
18
  import os from "node:os";
19
19
  import path from "node:path";
20
- import { mkdtemp, mkdir } from "node:fs/promises";
20
+ import { mkdtemp, mkdir, rm } from "node:fs/promises";
21
+ import { existsSync } from "node:fs";
21
22
 
22
23
  import { parseConfig } from "./config.js";
23
24
  import { Orchestrator } from "./orchestrator.js";
24
- import { ContentHashIndex, type StorageManager } from "./storage.js";
25
- import type { ExtractionResult, ExtractedFact, MemoryFile } from "./types.js";
25
+ import { clearMemoryCache } from "./memory-cache.js";
26
+ import { ContentHashIndex, StorageManager } from "./storage.js";
27
+ import type { ExtractionResult, ExtractedFact, MemoryFile, MemoryCategory } from "./types.js";
26
28
  import type { ResolvedScopeProfilePlan } from "./namespaces/scope-profiles.js";
29
+ import { buildProcedurePersistBody } from "./procedural/procedure-types.js";
30
+ import type { PersistExtractionFn } from "./testing/orchestrator-lite.js";
27
31
 
28
32
  // ---------------------------------------------------------------------------
29
33
  // Types — minimal surface of Orchestrator needed by these tests.
30
34
  // persistExtraction is private; cast through unknown to reach it without `any`.
31
35
  // ---------------------------------------------------------------------------
32
36
 
33
- /** Source-context shape accepted by persistExtraction (connector subset). */
34
- interface TestSourceContext {
35
- sourceConnector?: string;
36
- validAt?: string;
37
- }
38
-
39
37
  /** Orchestrator fields the tests touch (all exist on the real instance). */
40
38
  interface OrchestratorTestSurface {
41
- persistExtraction: (
42
- result: ExtractionResult,
43
- storage: StorageManager,
44
- threadId: string | null,
45
- sourceContext?: TestSourceContext,
46
- baseNamespace?: string,
47
- scopeProfileWritePlan?: ResolvedScopeProfilePlan | null,
48
- ) => Promise<string[]>;
39
+ persistExtraction: PersistExtractionFn;
49
40
  getStorage: (namespace: string) => Promise<StorageManager>;
50
41
  contentHashIndex: ContentHashIndex | null;
42
+ // Private on the real instance; reached through the unknown-cast surface for
43
+ // the #1909 defer-durability tests (registration failure + concurrent runs).
44
+ addContentHashDedup: (targetStorage: StorageManager, content: string) => Promise<void>;
45
+ hasContentHashDedup: (targetStorage: StorageManager, content: string) => Promise<boolean>;
46
+ removeContentHashForMemory: (
47
+ targetStorage: StorageManager,
48
+ memory: MemoryFile,
49
+ context: string,
50
+ ) => Promise<void>;
51
51
  }
52
52
 
53
53
  // ---------------------------------------------------------------------------
@@ -121,7 +121,7 @@ test("connector dedup (QOjlB): same content + same connector dedupes", async ()
121
121
  "The deployment pipeline runs canary checks every fifteen minutes before promoting to production.";
122
122
 
123
123
  // First write with connector "chatgpt" — must succeed.
124
- const ids1 = await orchestrator.persistExtraction(
124
+ const { persistedIds: ids1 } = await orchestrator.persistExtraction(
125
125
  factResult(body),
126
126
  storage,
127
127
  null,
@@ -130,7 +130,7 @@ test("connector dedup (QOjlB): same content + same connector dedupes", async ()
130
130
  assert.equal(ids1.length, 1, "first write must succeed");
131
131
 
132
132
  // Second write: same content, same connector → must dedupe.
133
- const ids2 = await orchestrator.persistExtraction(
133
+ const { persistedIds: ids2 } = await orchestrator.persistExtraction(
134
134
  factResult(body),
135
135
  storage,
136
136
  null,
@@ -150,7 +150,7 @@ test("connector dedup (QOjlB): same content + different connector does NOT dedup
150
150
  "The staging environment uses isolated feature flags per tenant for safer rollouts.";
151
151
 
152
152
  // First write with connector "chatgpt".
153
- const ids1 = await orchestrator.persistExtraction(
153
+ const { persistedIds: ids1 } = await orchestrator.persistExtraction(
154
154
  factResult(body),
155
155
  storage,
156
156
  null,
@@ -159,7 +159,7 @@ test("connector dedup (QOjlB): same content + different connector does NOT dedup
159
159
  assert.equal(ids1.length, 1, "first write must succeed");
160
160
 
161
161
  // Second write: same content, DIFFERENT connector → must NOT dedupe.
162
- const ids2 = await orchestrator.persistExtraction(
162
+ const { persistedIds: ids2 } = await orchestrator.persistExtraction(
163
163
  factResult(body),
164
164
  storage,
165
165
  null,
@@ -190,7 +190,7 @@ test("connector dedup (QOjlB): stale hash with no matching memory writes through
190
190
 
191
191
  // persistExtraction must write through — the connector-aware scan finds
192
192
  // no matching memory, so exactDuplicate falls to false.
193
- const ids = await orchestrator.persistExtraction(
193
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
194
194
  factResult(body),
195
195
  storage,
196
196
  null,
@@ -224,7 +224,7 @@ test("connector dedup (QOjlB): readAllMemories throws → writes through (fail o
224
224
 
225
225
  // persistExtraction must write through — the scan failure is caught and
226
226
  // exactDuplicate falls to false (fail open).
227
- const ids = await orchestrator.persistExtraction(
227
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
228
228
  factResult(body),
229
229
  storage,
230
230
  null,
@@ -339,7 +339,7 @@ test("promotion dedup (QOjlD): same content + same connector skips promotion", a
339
339
  });
340
340
 
341
341
  // persistExtraction writes to source (default) and promotes to target.
342
- const ids = await orchestrator.persistExtraction(
342
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
343
343
  factResult(body),
344
344
  storage,
345
345
  null,
@@ -370,7 +370,7 @@ test("promotion dedup (QOjlD): same content + different connector promotes", asy
370
370
  });
371
371
 
372
372
  // Extract with a DIFFERENT connector — promotion must proceed.
373
- const ids = await orchestrator.persistExtraction(
373
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
374
374
  factResult(body),
375
375
  storage,
376
376
  null,
@@ -412,7 +412,7 @@ test("promotion dedup (QOjlD): connector vs operator promotes", async () => {
412
412
  });
413
413
 
414
414
  // Extract WITHOUT a sourceConnector (operator write).
415
- const ids = await orchestrator.persistExtraction(
415
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
416
416
  factResult(body),
417
417
  storage,
418
418
  null,
@@ -442,7 +442,7 @@ test("promotion dedup (QOjlD): operator vs operator skips promotion", async () =
442
442
  });
443
443
 
444
444
  // Extract WITHOUT a sourceConnector (operator write).
445
- const ids = await orchestrator.persistExtraction(
445
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
446
446
  factResult(body),
447
447
  storage,
448
448
  null,
@@ -491,7 +491,7 @@ test("promotion dedup (QOjlD): readAllMemories throws on target → promotion pr
491
491
  throw new Error("simulated target readAllMemories failure");
492
492
  };
493
493
 
494
- const ids = await orchestrator.persistExtraction(
494
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
495
495
  factResult(body),
496
496
  storage,
497
497
  null,
@@ -526,7 +526,7 @@ test("promotion dedup (QOjlD): cited fact canonicalized before connector-aware d
526
526
  "The deployment manifest pins container digests for reproducible builds.";
527
527
 
528
528
  // First persist: orchestrator appends an inline citation and writes the fact.
529
- const firstIds = await orchestrator.persistExtraction(
529
+ const { persistedIds: firstIds } = await orchestrator.persistExtraction(
530
530
  factResult(rawBody),
531
531
  storage,
532
532
  null,
@@ -550,7 +550,7 @@ test("promotion dedup (QOjlD): cited fact canonicalized before connector-aware d
550
550
  // normalizeStoredHashSource strips the citation before comparing, so the
551
551
  // connector-aware scan finds the matching fact and dedupes at the source
552
552
  // gate — promotion never fires.
553
- const secondIds = await orchestrator.persistExtraction(
553
+ const { persistedIds: secondIds } = await orchestrator.persistExtraction(
554
554
  factResult(firstMemory.content),
555
555
  storage,
556
556
  null,
@@ -614,7 +614,7 @@ test("connector dedup (QPDE5): same-connector procedure with steps dedupes", asy
614
614
  ];
615
615
 
616
616
  // First write with connector "chatgpt" — must succeed.
617
- const ids1 = await orchestrator.persistExtraction(
617
+ const { persistedIds: ids1 } = await orchestrator.persistExtraction(
618
618
  procedureResult(title, steps),
619
619
  storage,
620
620
  null,
@@ -623,7 +623,7 @@ test("connector dedup (QPDE5): same-connector procedure with steps dedupes", asy
623
623
  assert.equal(ids1.length, 1, "first procedure write must succeed");
624
624
 
625
625
  // Second write: same procedure, same connector → must dedupe.
626
- const ids2 = await orchestrator.persistExtraction(
626
+ const { persistedIds: ids2 } = await orchestrator.persistExtraction(
627
627
  procedureResult(title, steps),
628
628
  storage,
629
629
  null,
@@ -647,7 +647,7 @@ test("connector dedup (QPDE5): same procedure + different connector does NOT ded
647
647
  ];
648
648
 
649
649
  // First write with connector "chatgpt".
650
- const ids1 = await orchestrator.persistExtraction(
650
+ const { persistedIds: ids1 } = await orchestrator.persistExtraction(
651
651
  procedureResult(title, steps),
652
652
  storage,
653
653
  null,
@@ -656,7 +656,7 @@ test("connector dedup (QPDE5): same procedure + different connector does NOT ded
656
656
  assert.equal(ids1.length, 1, "first procedure write must succeed");
657
657
 
658
658
  // Second write: same procedure, DIFFERENT connector → must NOT dedupe.
659
- const ids2 = await orchestrator.persistExtraction(
659
+ const { persistedIds: ids2 } = await orchestrator.persistExtraction(
660
660
  procedureResult(title, steps),
661
661
  storage,
662
662
  null,
@@ -711,7 +711,7 @@ test("connector dedup (QO42V): same-connector fact with structuredAttributes ded
711
711
  const attrs = { dbEngine: "postgresql", version: "16" };
712
712
 
713
713
  // First write with connector "chatgpt" — must succeed.
714
- const ids1 = await orchestrator.persistExtraction(
714
+ const { persistedIds: ids1 } = await orchestrator.persistExtraction(
715
715
  enrichedResult(body, attrs),
716
716
  storage,
717
717
  null,
@@ -720,7 +720,7 @@ test("connector dedup (QO42V): same-connector fact with structuredAttributes ded
720
720
  assert.equal(ids1.length, 1, "first enriched fact write must succeed");
721
721
 
722
722
  // Second write: same content + attrs, same connector → must dedupe.
723
- const ids2 = await orchestrator.persistExtraction(
723
+ const { persistedIds: ids2 } = await orchestrator.persistExtraction(
724
724
  enrichedResult(body, attrs),
725
725
  storage,
726
726
  null,
@@ -809,7 +809,7 @@ test("shared promotion (QPAn-): different-connector fact promotes when supersess
809
809
 
810
810
  // Extract with a DIFFERENT connector — shared promotion else branch
811
811
  // must NOT short-circuit; the different-connector fact gets promoted.
812
- const ids = await orchestrator.persistExtraction(
812
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
813
813
  factResult(body),
814
814
  storage,
815
815
  null,
@@ -848,7 +848,7 @@ test("shared promotion (QPAn-): same-connector fact skips promotion when superse
848
848
  });
849
849
 
850
850
  // Extract with the SAME connector — shared promotion must be skipped.
851
- const ids = await orchestrator.persistExtraction(
851
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
852
852
  factResult(body),
853
853
  storage,
854
854
  null,
@@ -902,7 +902,7 @@ test("shared backfill: different-connector shared fact is NOT temporal-patched",
902
902
 
903
903
  // Extract the SAME content with a DIFFERENT connector and an eventTime
904
904
  // expression that resolves a validUntil bound.
905
- const ids = await orchestrator.persistExtraction(
905
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
906
906
  {
907
907
  facts: [{
908
908
  content: body,
@@ -1044,7 +1044,7 @@ test("shared backfill: operator (no-connector) extraction must NOT patch a conne
1044
1044
 
1045
1045
  // Extract the SAME content with NO sourceConnector (operator
1046
1046
  // re-extraction) and an eventTime that resolves a validUntil bound.
1047
- const ids = await orchestrator.persistExtraction(
1047
+ const { persistedIds: ids } = await orchestrator.persistExtraction(
1048
1048
  {
1049
1049
  facts: [{
1050
1050
  content: body,
@@ -1080,3 +1080,1024 @@ test("shared backfill: operator (no-connector) extraction must NOT patch a conne
1080
1080
  "tagged fact must NOT get invalid_at from connectorless/operator backfill",
1081
1081
  );
1082
1082
  });
1083
+ // ---------------------------------------------------------------------------
1084
+ // Issue #1909 review round 2 finding 2 — defer only covered by the batch save
1085
+ // ---------------------------------------------------------------------------
1086
+
1087
+ test("#1909: with factDeduplicationEnabled=false, extraction writes flush the fact-hash index immediately", async () => {
1088
+ // With dedup off, contentHashIndexForStorage() returns null so the
1089
+ // orchestrator's end-of-persist batch save is a no-op. The main-path fact
1090
+ // write must therefore NOT defer its per-fact index flush — otherwise the
1091
+ // storage-owned fact-hash index is never written, and a restart with
1092
+ // fact-hashes.ready present trusts a stale index missing the fact.
1093
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-defer-dedup-off-"));
1094
+ const config = parseConfig({
1095
+ openaiApiKey: "sk-test",
1096
+ memoryDir,
1097
+ workspaceDir: path.join(memoryDir, "workspace"),
1098
+ qmdEnabled: false,
1099
+ embeddingFallbackEnabled: false,
1100
+ chunkingEnabled: false,
1101
+ multiGraphMemoryEnabled: false,
1102
+ factDeduplicationEnabled: false,
1103
+ });
1104
+ const orchestrator = new Orchestrator(config) as unknown as OrchestratorTestSurface;
1105
+ const storage = await orchestrator.getStorage("default");
1106
+ await storage.ensureDirectories();
1107
+ // Warm the index authoritative and create the .ready marker over the current
1108
+ // (empty) corpus, so a later fresh session trusts the on-disk index.
1109
+ assert.equal(await storage.hasFactContentHash("warm"), false);
1110
+
1111
+ const body = "The billing service retries failed charges with exponential backoff.";
1112
+ const { persistedIds: ids } = await orchestrator.persistExtraction(factResult(body), storage, null);
1113
+ assert.equal(ids.length, 1, "the fact is written");
1114
+
1115
+ // Fresh StorageManager with .ready present trusts the on-disk fact-hash index
1116
+ // (no rebuild). The hash must be there via the immediate per-fact save.
1117
+ const restarted = new StorageManager(memoryDir);
1118
+ assert.equal(
1119
+ await restarted.hasFactContentHash(body),
1120
+ true,
1121
+ "the fact hash was flushed immediately despite the batch saver being a no-op",
1122
+ );
1123
+ });
1124
+ test("#2016 SD-nH: fact dedup disabled never suppresses a write via the authority/corpus-confirm path", async () => {
1125
+ // With dedup DISABLED the entire content-hash dedup path must be
1126
+ // short-circuited. The pre-fix code still ran hasContentHashDedup (returns
1127
+ // false with a null index), then consulted isFactContentHashAuthoritative();
1128
+ // when that reported NON-authoritative (e.g. a peer holds the rebuild lock)
1129
+ // it set needsCorpusConfirm and the connector-aware corpus scan could find a
1130
+ // same-content same-connector active fact and SUPPRESS the write — dedup
1131
+ // behavior while dedup is turned off.
1132
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-dedup-off-nosuppress-"));
1133
+ try {
1134
+ const config = parseConfig({
1135
+ openaiApiKey: "sk-test",
1136
+ memoryDir,
1137
+ workspaceDir: path.join(memoryDir, "workspace"),
1138
+ qmdEnabled: false,
1139
+ embeddingFallbackEnabled: false,
1140
+ chunkingEnabled: false,
1141
+ multiGraphMemoryEnabled: false,
1142
+ factDeduplicationEnabled: false,
1143
+ });
1144
+ const orchestrator = new Orchestrator(config) as unknown as OrchestratorTestSurface;
1145
+ const storage = await orchestrator.getStorage("default");
1146
+ await storage.ensureDirectories();
1147
+
1148
+ const body = "The billing service retries failed charges with exponential backoff.";
1149
+ const source = { sourceConnector: "slack" };
1150
+
1151
+ // Seed one active same-connector copy in the corpus.
1152
+ const first = await orchestrator.persistExtraction(factResult(body), storage, null, source);
1153
+ assert.equal(first.persistedIds.length, 1, "first write lands");
1154
+
1155
+ // Force the storage fact-hash index NON-authoritative so the pre-fix code
1156
+ // falls into needsCorpusConfirm and runs the corpus scan — the exact path
1157
+ // that (wrongly) suppressed a write while dedup is DISABLED.
1158
+ // isFactContentHashAuthoritative is a real method on StorageManager; the
1159
+ // cast only reaches it as an overridable slot for this test.
1160
+ const authorityStub = storage as unknown as {
1161
+ isFactContentHashAuthoritative: () => Promise<boolean>;
1162
+ };
1163
+ authorityStub.isFactContentHashAuthoritative = async () => false;
1164
+ // Read the corpus from disk so the scan sees the seeded copy.
1165
+ clearMemoryCache();
1166
+
1167
+ const second = await orchestrator.persistExtraction(factResult(body), storage, null, source);
1168
+ assert.equal(
1169
+ second.persistedIds.length,
1170
+ 1,
1171
+ "dedup disabled must not suppress the write via the authority/corpus-confirm path",
1172
+ );
1173
+ } finally {
1174
+ await rm(memoryDir, { recursive: true, force: true });
1175
+ }
1176
+ });
1177
+ test("#1909 round 11: deferred persist writes no ready marker and a restart rebuild still dedups", async () => {
1178
+ // With dedup ON the main-path fact write defers; there is NO fact-hashes.ready
1179
+ // marker anymore. A restart rebuilds the fact-hash index authoritatively from
1180
+ // the corpus and still dedups the fact.
1181
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-defer-window-"));
1182
+ const config = parseConfig({
1183
+ openaiApiKey: "sk-test",
1184
+ memoryDir,
1185
+ workspaceDir: path.join(memoryDir, "workspace"),
1186
+ qmdEnabled: false,
1187
+ embeddingFallbackEnabled: false,
1188
+ chunkingEnabled: false,
1189
+ multiGraphMemoryEnabled: false,
1190
+ factDeduplicationEnabled: true,
1191
+ });
1192
+ const orchestrator = new Orchestrator(config) as unknown as OrchestratorTestSurface;
1193
+ const storage = await orchestrator.getStorage("default");
1194
+ await storage.ensureDirectories();
1195
+ const readyPath = path.join(memoryDir, "state", "fact-hashes.ready");
1196
+
1197
+ const body = "The scheduler batches webhook deliveries into 250ms windows.";
1198
+ const { persistedIds: ids } = await orchestrator.persistExtraction(factResult(body), storage, null);
1199
+ assert.equal(ids.length, 1, "the fact is written");
1200
+ assert.equal(existsSync(readyPath), false, "no ready marker is ever written (round 11)");
1201
+
1202
+ // A fresh instance rebuilds from the corpus and still dedups.
1203
+ const restarted = new StorageManager(memoryDir);
1204
+ assert.equal(await restarted.hasFactContentHash(body), true);
1205
+ assert.equal(existsSync(readyPath), false, "still no marker after the restart rebuild");
1206
+ });
1207
+ test("#1909: a deferred fact stays durable when addContentHashDedup throws", async () => {
1208
+ // Review round 6 finding 1: the deferred write's durability must not depend on
1209
+ // the orchestrator dedup registration succeeding. writeMemory already added the
1210
+ // hash to the storage-owned index; the end-of-run storage-index flush must put
1211
+ // it on disk even though addContentHashDedup threw (caught+logged).
1212
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-defer-regfail-"));
1213
+ const config = parseConfig({
1214
+ openaiApiKey: "sk-test",
1215
+ memoryDir,
1216
+ workspaceDir: path.join(memoryDir, "workspace"),
1217
+ qmdEnabled: false,
1218
+ embeddingFallbackEnabled: false,
1219
+ chunkingEnabled: false,
1220
+ multiGraphMemoryEnabled: false,
1221
+ factDeduplicationEnabled: true,
1222
+ });
1223
+ const orchestrator = new Orchestrator(config) as unknown as OrchestratorTestSurface;
1224
+ const storage = await orchestrator.getStorage("default");
1225
+ await storage.ensureDirectories();
1226
+ assert.equal(await storage.hasFactContentHash("warm"), false); // warm + create marker
1227
+
1228
+ // Force the orchestrator dedup registration to throw for every fact this run.
1229
+ orchestrator.addContentHashDedup = async () => {
1230
+ throw new Error("simulated registration failure");
1231
+ };
1232
+
1233
+ const body = "The queue drains oldest-first under sustained backpressure.";
1234
+ const { persistedIds: ids } = await orchestrator.persistExtraction(factResult(body), storage, null);
1235
+ assert.equal(ids.length, 1, "the fact is written despite the registration failure");
1236
+
1237
+ // Restart trusts the marker (restored) and finds the hash on disk — no
1238
+ // duplicate re-creation — because the storage-owned index flush persisted it.
1239
+ const restarted = new StorageManager(memoryDir);
1240
+ assert.equal(
1241
+ await restarted.hasFactContentHash(body),
1242
+ true,
1243
+ "the deferred hash reached disk independent of addContentHashDedup",
1244
+ );
1245
+ });
1246
+
1247
+ test("#1909: two interleaved persist runs both land on disk (merge, no clobber)", async () => {
1248
+ // Review round 6 finding 2: two orchestrators that snapshot an empty index
1249
+ // before either saves must not clobber each other — the merge-save unions.
1250
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-defer-interleave-"));
1251
+ const makeConfig = () =>
1252
+ parseConfig({
1253
+ openaiApiKey: "sk-test",
1254
+ memoryDir,
1255
+ workspaceDir: path.join(memoryDir, "workspace"),
1256
+ qmdEnabled: false,
1257
+ embeddingFallbackEnabled: false,
1258
+ chunkingEnabled: false,
1259
+ multiGraphMemoryEnabled: false,
1260
+ factDeduplicationEnabled: true,
1261
+ });
1262
+ const orchA = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1263
+ const orchB = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1264
+ const storageA = await orchA.getStorage("default");
1265
+ const storageB = await orchB.getStorage("default");
1266
+ await storageA.ensureDirectories();
1267
+
1268
+ // Force BOTH orchestrator indexes to load an empty snapshot up front (the race:
1269
+ // each holds a pre-write view of the shared on-disk index).
1270
+ await orchA.hasContentHashDedup(storageA, "noop-a");
1271
+ await orchB.hasContentHashDedup(storageB, "noop-b");
1272
+
1273
+ await orchA.persistExtraction(factResult("alpha interleaved fact"), storageA, null);
1274
+ await orchB.persistExtraction(factResult("beta interleaved fact"), storageB, null);
1275
+
1276
+ // A blind overwrite by B (stale empty snapshot) would drop alpha; the merge
1277
+ // union preserves both.
1278
+ const fresh = new StorageManager(memoryDir);
1279
+ assert.equal(await fresh.hasFactContentHash("alpha interleaved fact"), true, "A's fact survived");
1280
+ assert.equal(await fresh.hasFactContentHash("beta interleaved fact"), true, "B's fact survived");
1281
+ });
1282
+ test("#1909 round 11: the restart rebuild reflects the current corpus (archived fact is not re-deduped)", async () => {
1283
+ // No ready marker exists — the fact-hash index is ALWAYS rebuilt from the
1284
+ // durable corpus on restart. So an archival/consolidation removal LANDS after
1285
+ // restart (the removed .md is excluded from the rebuild) even if that run's
1286
+ // reconciling save could not publish (lock timeout) — there is no stale
1287
+ // on-disk index to trust. This is the definitive replacement for the
1288
+ // marker-invalidate-on-removal machinery.
1289
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-corpus-reflect-"));
1290
+ const config = parseConfig({
1291
+ openaiApiKey: "sk-test",
1292
+ memoryDir,
1293
+ workspaceDir: path.join(memoryDir, "workspace"),
1294
+ qmdEnabled: false,
1295
+ embeddingFallbackEnabled: false,
1296
+ chunkingEnabled: false,
1297
+ multiGraphMemoryEnabled: false,
1298
+ factDeduplicationEnabled: true,
1299
+ });
1300
+ const orchestrator = new Orchestrator(config) as unknown as OrchestratorTestSurface;
1301
+ const storage = await orchestrator.getStorage("default");
1302
+ await storage.ensureDirectories();
1303
+
1304
+ const body = "The nightly job compacts cold storage at 02:00 UTC.";
1305
+ await orchestrator.persistExtraction(factResult(body), storage, null);
1306
+ const readyPath = path.join(memoryDir, "state", "fact-hashes.ready");
1307
+ assert.equal(existsSync(readyPath), false, "no ready marker is ever written (round 11)");
1308
+
1309
+ // A fresh instance rebuilds from the corpus → dedups the persisted fact.
1310
+ const before = new StorageManager(memoryDir);
1311
+ assert.equal(await before.hasFactContentHash(body), true);
1312
+
1313
+ // Archive the fact: remove its .md from the active corpus.
1314
+ const mem = (await storage.readAllMemories()).find((m: MemoryFile) => m.content.includes(body));
1315
+ assert.ok(mem, "persisted fact is readable");
1316
+ await rm(mem.path);
1317
+ // Simulate a fresh process (real restart): drop the process-wide memory cache
1318
+ // so the rebuild re-reads the now-smaller corpus from disk.
1319
+ clearMemoryCache(memoryDir);
1320
+
1321
+ // A fresh instance rebuilds from the now-smaller corpus → the archived fact is
1322
+ // no longer deduped (re-extraction is allowed). The removal landed with no
1323
+ // marker and no reliance on the reconciling save having published.
1324
+ const after = new StorageManager(memoryDir);
1325
+ assert.equal(
1326
+ await after.hasFactContentHash(body),
1327
+ false,
1328
+ "archived fact is not deduped after the restart rebuild",
1329
+ );
1330
+ });
1331
+ test("#1909 round 11 finding 2: destroy() flushes the debounced buffer BEFORE catalog touches", async () => {
1332
+ // The buffer save fires a coalesced namespace-catalog touch; if catalog touches
1333
+ // were flushed first, that shutdown-time touch would queue after and be lost.
1334
+ // destroy() must flush the buffer first so its touch folds into the catalog
1335
+ // flush and both settle before destroy() returns.
1336
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-destroy-order-"));
1337
+ const config = parseConfig({
1338
+ openaiApiKey: "sk-test",
1339
+ memoryDir,
1340
+ workspaceDir: path.join(memoryDir, "workspace"),
1341
+ qmdEnabled: false,
1342
+ embeddingFallbackEnabled: false,
1343
+ chunkingEnabled: false,
1344
+ multiGraphMemoryEnabled: false,
1345
+ });
1346
+ const orchestrator = new Orchestrator(config);
1347
+ const priv = orchestrator as unknown as {
1348
+ buffer: { flushPendingSave: () => Promise<void> };
1349
+ namespaceCatalog: { flushPendingTouches: () => Promise<void> };
1350
+ destroy: () => Promise<void>;
1351
+ };
1352
+ const order: string[] = [];
1353
+ const realFlushSave = priv.buffer.flushPendingSave.bind(priv.buffer);
1354
+ priv.buffer.flushPendingSave = async () => {
1355
+ order.push("buffer");
1356
+ return realFlushSave();
1357
+ };
1358
+ const realFlushTouch = priv.namespaceCatalog.flushPendingTouches.bind(priv.namespaceCatalog);
1359
+ priv.namespaceCatalog.flushPendingTouches = async () => {
1360
+ order.push("catalog");
1361
+ return realFlushTouch();
1362
+ };
1363
+
1364
+ await priv.destroy();
1365
+ assert.deepEqual(
1366
+ order,
1367
+ ["buffer", "catalog"],
1368
+ "buffer flush must run before the catalog-touch flush on shutdown",
1369
+ );
1370
+ });
1371
+ test("#1909 round 14: destroy() surfaces a buffer flush failure and still completes teardown", async () => {
1372
+ // Graceful-shutdown durability contract: a failed shutdown buffer flush must
1373
+ // NOT be silently swallowed — the pending turns are retained in memory but
1374
+ // lost on process exit, so the host has to learn about it. destroy() runs the
1375
+ // remaining teardown in a finally block, then rethrows the flush failure.
1376
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-destroy-fail-"));
1377
+ const config = parseConfig({
1378
+ openaiApiKey: "sk-test",
1379
+ memoryDir,
1380
+ workspaceDir: path.join(memoryDir, "workspace"),
1381
+ qmdEnabled: false,
1382
+ embeddingFallbackEnabled: false,
1383
+ chunkingEnabled: false,
1384
+ multiGraphMemoryEnabled: false,
1385
+ });
1386
+ const orchestrator = new Orchestrator(config);
1387
+ const priv = orchestrator as unknown as {
1388
+ buffer: { flushPendingSave: (opts?: { throwOnFailure?: boolean }) => Promise<void> };
1389
+ namespaceCatalog: { flushPendingTouches: () => Promise<void> };
1390
+ destroy: () => Promise<void>;
1391
+ };
1392
+ const flushErr = new Error("simulated shutdown buffer write failure");
1393
+ priv.buffer.flushPendingSave = async () => {
1394
+ throw flushErr;
1395
+ };
1396
+ let catalogFlushed = false;
1397
+ const realFlushTouch = priv.namespaceCatalog.flushPendingTouches.bind(priv.namespaceCatalog);
1398
+ priv.namespaceCatalog.flushPendingTouches = async () => {
1399
+ catalogFlushed = true;
1400
+ return realFlushTouch();
1401
+ };
1402
+
1403
+ await assert.rejects(
1404
+ priv.destroy(),
1405
+ (err: unknown) => err === flushErr,
1406
+ "destroy() must surface the buffer flush failure instead of swallowing it",
1407
+ );
1408
+ assert.equal(
1409
+ catalogFlushed,
1410
+ true,
1411
+ "teardown (catalog flush) must still run in the finally block despite the flush failure",
1412
+ );
1413
+ });
1414
+ test("#1909 round 12: after a crash before the batch save, the orchestrator dedup sees the fact (corpus rebuild)", async () => {
1415
+ // A deferred fact write persists the .md but not fact-hashes.txt; a crash
1416
+ // before saveContentHashIndexes leaves only the .md durable. On restart the
1417
+ // orchestrator's dedup index must be corpus-AUTHORITATIVE (round 12) — sharing
1418
+ // StorageManager's rebuild — so hasContentHashDedup sees the fact and
1419
+ // persistExtraction does NOT re-create it. Pre-round-12 it loaded a stale
1420
+ // fact-hashes.txt (missing the fact) and would duplicate.
1421
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-orch-rebuild-"));
1422
+ const body = "The deploy pipeline gates on a green smoke suite.";
1423
+
1424
+ // Phase 1 — crash window: deferred write, NO batch save / index flush.
1425
+ {
1426
+ const seed = new StorageManager(memoryDir);
1427
+ await seed.ensureDirectories();
1428
+ await seed.writeMemory("fact", body, { source: "extraction", deferHashIndexSave: true });
1429
+ // CRASH: no saveContentHashIndexes → fact-hashes.txt never got the hash.
1430
+ }
1431
+ clearMemoryCache(memoryDir); // model a fresh process
1432
+
1433
+ // Phase 2 — restart: the orchestrator dedup index rebuilds from the corpus.
1434
+ const config = parseConfig({
1435
+ openaiApiKey: "sk-test",
1436
+ memoryDir,
1437
+ workspaceDir: path.join(memoryDir, "workspace"),
1438
+ qmdEnabled: false,
1439
+ embeddingFallbackEnabled: false,
1440
+ chunkingEnabled: false,
1441
+ multiGraphMemoryEnabled: false,
1442
+ factDeduplicationEnabled: true,
1443
+ });
1444
+ const orchestrator = new Orchestrator(config) as unknown as OrchestratorTestSurface;
1445
+ const storage = await orchestrator.getStorage("default");
1446
+
1447
+ assert.equal(
1448
+ await orchestrator.hasContentHashDedup(storage, body),
1449
+ true,
1450
+ "orchestrator dedup sees the crashed-but-durable fact via the corpus rebuild",
1451
+ );
1452
+ // And re-persisting the same fact is deduped (no duplicate .md created).
1453
+ const { persistedIds: ids } = await orchestrator.persistExtraction(factResult(body), storage, null);
1454
+ assert.equal(ids.length, 0, "the fact is deduped on re-extraction — not re-created");
1455
+ });
1456
+
1457
+ test("#1909 round 13: startup rebuild preserves PROCEDURE hashes as well as fact hashes across restart", async () => {
1458
+ // The content-hash dedup index is SHARED by fact AND procedure dedup —
1459
+ // procedures register their persist-body hash into the same index. The
1460
+ // marker-less startup rebuild (ensureFactHashIndexAuthoritative) clears the
1461
+ // on-disk index and reconstructs it from the .md corpus on first use per
1462
+ // process. A fact-only rebuild dropped every persisted procedure hash, so a
1463
+ // restart would re-create identical procedures. Both categories must survive.
1464
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-proc-rebuild-"));
1465
+ const factBody = "The staging deploy gates on a green smoke suite.";
1466
+ const procTitle = "When you cut a hotfix release, follow the checklist";
1467
+ const procSteps = [
1468
+ { intent: "Branch from main and cherry-pick the fix" },
1469
+ { intent: "Run CI and tag the release" },
1470
+ ];
1471
+ // The dedup key for a procedure is its full persist body (title + steps).
1472
+ const procBody = buildProcedurePersistBody(
1473
+ procTitle,
1474
+ procSteps.map((s, i) => ({ order: i + 1, intent: s.intent })),
1475
+ );
1476
+
1477
+ const makeConfig = () =>
1478
+ parseConfig({
1479
+ openaiApiKey: "sk-test",
1480
+ memoryDir,
1481
+ workspaceDir: path.join(memoryDir, "workspace"),
1482
+ qmdEnabled: false,
1483
+ embeddingFallbackEnabled: false,
1484
+ chunkingEnabled: false,
1485
+ multiGraphMemoryEnabled: false,
1486
+ factDeduplicationEnabled: true,
1487
+ // procedural.enabled defaults to true.
1488
+ });
1489
+
1490
+ // Phase 1 — persist a fact and a procedure through the real persist path.
1491
+ {
1492
+ const orch = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1493
+ const storage = await orch.getStorage("default");
1494
+ const { persistedIds: factIds } = await orch.persistExtraction(factResult(factBody), storage, null);
1495
+ assert.equal(factIds.length, 1, "fact persisted in phase 1");
1496
+ const { persistedIds: procIds } = await orch.persistExtraction(
1497
+ procedureResult(procTitle, procSteps),
1498
+ storage,
1499
+ null,
1500
+ );
1501
+ assert.equal(procIds.length, 1, "procedure persisted in phase 1");
1502
+ }
1503
+ clearMemoryCache(memoryDir); // model a fresh process
1504
+
1505
+ // Phase 2 — restart: the dedup index rebuilds authoritatively from the corpus.
1506
+ const orch2 = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1507
+ const storage2 = await orch2.getStorage("default");
1508
+
1509
+ assert.equal(
1510
+ await orch2.hasContentHashDedup(storage2, factBody),
1511
+ true,
1512
+ "fact hash survives the corpus rebuild",
1513
+ );
1514
+ assert.equal(
1515
+ await orch2.hasContentHashDedup(storage2, procBody),
1516
+ true,
1517
+ "PROCEDURE hash survives the corpus rebuild (round 13 fix)",
1518
+ );
1519
+
1520
+ // Re-extraction of both is deduped — neither is re-created.
1521
+ const { persistedIds: factReIds } = await orch2.persistExtraction(factResult(factBody), storage2, null);
1522
+ assert.equal(factReIds.length, 0, "fact is deduped on restart");
1523
+ const { persistedIds: procReIds } = await orch2.persistExtraction(
1524
+ procedureResult(procTitle, procSteps),
1525
+ storage2,
1526
+ null,
1527
+ );
1528
+ assert.equal(
1529
+ procReIds.length,
1530
+ 0,
1531
+ "procedure is deduped on restart (would be re-created without the round 13 fix)",
1532
+ );
1533
+
1534
+ // Exactly one of each remains on disk.
1535
+ const all = await storage2.readAllMemories();
1536
+ assert.equal(
1537
+ all.filter((m: MemoryFile) => m.frontmatter.category === "fact").length,
1538
+ 1,
1539
+ "one fact on disk",
1540
+ );
1541
+ assert.equal(
1542
+ all.filter((m: MemoryFile) => m.frontmatter.category === "procedure").length,
1543
+ 1,
1544
+ "one procedure on disk",
1545
+ );
1546
+
1547
+ await rm(memoryDir, { recursive: true, force: true });
1548
+ });
1549
+
1550
+ test("#1909 round 15 (PR #2016): startup rebuild preserves EVERY registered category hash across restart", async () => {
1551
+ // The content-hash dedup index is shared by every registered write category:
1552
+ // persistExtraction calls addContentHashDedup for every writeCategory it
1553
+ // persists (preference, decision, commitment, …), not only fact/procedure.
1554
+ // A rebuild restricted to fact+procedure dropped those hashes on restart, so
1555
+ // the next extraction re-created identical active non-fact memories (the
1556
+ // retired fact-hashes.txt load used to preserve them). Every category's hash
1557
+ // must survive the corpus rebuild.
1558
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cat-rebuild-"));
1559
+ const prefContent = "The user prefers dark mode across every surface.";
1560
+ const decisionContent = "We standardized on pnpm for all workspace installs.";
1561
+ // A preference carrying structuredAttributes: writeMemory appends an
1562
+ // "[Attributes: …]" suffix to the stored body, but the registered dedup key
1563
+ // is the RAW content WITHOUT that suffix. The rebuild must strip the suffix
1564
+ // or the reconstructed hash never matches and the memory is re-created.
1565
+ const attrContent = "The user's working timezone is America/Chicago.";
1566
+ const attrs: Record<string, string> = { timezone: "America/Chicago", trust: "high" };
1567
+
1568
+ const categoryResult = (
1569
+ content: string,
1570
+ category: MemoryCategory,
1571
+ structuredAttributes?: Record<string, string>,
1572
+ ): ExtractionResult => ({
1573
+ facts: [
1574
+ {
1575
+ content,
1576
+ category,
1577
+ tags: [],
1578
+ confidence: 0.9,
1579
+ ...(structuredAttributes ? { structuredAttributes } : {}),
1580
+ } as ExtractedFact,
1581
+ ],
1582
+ entities: [],
1583
+ relationships: [],
1584
+ questions: [],
1585
+ profileUpdates: [],
1586
+ });
1587
+
1588
+ const makeConfig = () =>
1589
+ parseConfig({
1590
+ openaiApiKey: "sk-test",
1591
+ memoryDir,
1592
+ workspaceDir: path.join(memoryDir, "workspace"),
1593
+ qmdEnabled: false,
1594
+ embeddingFallbackEnabled: false,
1595
+ chunkingEnabled: false,
1596
+ multiGraphMemoryEnabled: false,
1597
+ factDeduplicationEnabled: true,
1598
+ });
1599
+
1600
+ // Phase 1 — persist non-fact/procedure categories through the real path.
1601
+ {
1602
+ const orch = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1603
+ const storage = await orch.getStorage("default");
1604
+ assert.equal(
1605
+ (await orch.persistExtraction(categoryResult(prefContent, "preference"), storage, null)).persistedIds.length,
1606
+ 1,
1607
+ "preference persisted in phase 1",
1608
+ );
1609
+ assert.equal(
1610
+ (await orch.persistExtraction(categoryResult(decisionContent, "decision"), storage, null)).persistedIds.length,
1611
+ 1,
1612
+ "decision persisted in phase 1",
1613
+ );
1614
+ assert.equal(
1615
+ (await orch.persistExtraction(categoryResult(attrContent, "preference", attrs), storage, null)).persistedIds.length,
1616
+ 1,
1617
+ "attributed preference persisted in phase 1",
1618
+ );
1619
+ }
1620
+ clearMemoryCache(memoryDir); // model a fresh process
1621
+
1622
+ // Phase 2 — restart: the dedup index rebuilds authoritatively from the corpus.
1623
+ const orch2 = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1624
+ const storage2 = await orch2.getStorage("default");
1625
+
1626
+ assert.equal(
1627
+ await orch2.hasContentHashDedup(storage2, prefContent),
1628
+ true,
1629
+ "preference hash survives the corpus rebuild (PR #2016 fix)",
1630
+ );
1631
+ assert.equal(
1632
+ await orch2.hasContentHashDedup(storage2, decisionContent),
1633
+ true,
1634
+ "decision hash survives the corpus rebuild (PR #2016 fix)",
1635
+ );
1636
+ assert.equal(
1637
+ await orch2.hasContentHashDedup(storage2, attrContent),
1638
+ true,
1639
+ "attributed preference hash survives the rebuild (attributes suffix stripped)",
1640
+ );
1641
+
1642
+ // Re-extraction of each is deduped — none is re-created.
1643
+ assert.equal(
1644
+ (await orch2.persistExtraction(categoryResult(prefContent, "preference"), storage2, null)).persistedIds.length,
1645
+ 0,
1646
+ "preference is deduped on restart (would be re-created without the fix)",
1647
+ );
1648
+ assert.equal(
1649
+ (await orch2.persistExtraction(categoryResult(decisionContent, "decision"), storage2, null)).persistedIds.length,
1650
+ 0,
1651
+ "decision is deduped on restart (would be re-created without the fix)",
1652
+ );
1653
+ assert.equal(
1654
+ (await orch2.persistExtraction(categoryResult(attrContent, "preference", attrs), storage2, null)).persistedIds.length,
1655
+ 0,
1656
+ "attributed preference is deduped on restart (would be re-created without the fix)",
1657
+ );
1658
+
1659
+ // The originals remain the only copies on disk.
1660
+ const all = await storage2.readAllMemories();
1661
+ assert.equal(
1662
+ all.filter((m: MemoryFile) => m.frontmatter.category === "preference").length,
1663
+ 2,
1664
+ "two preferences on disk (plain + attributed), none duplicated",
1665
+ );
1666
+ assert.equal(
1667
+ all.filter((m: MemoryFile) => m.frontmatter.category === "decision").length,
1668
+ 1,
1669
+ "one decision on disk, not duplicated",
1670
+ );
1671
+
1672
+ await rm(memoryDir, { recursive: true, force: true });
1673
+ });
1674
+
1675
+ test("#1909 (PR #2016): a fact demoted to cold is not re-created as a duplicate hot copy after restart", async () => {
1676
+ // The authoritative content-hash rebuild unions the HOT and COLD tiers, so a
1677
+ // restart's hash index reports a hit for a fact whose only active copy was
1678
+ // demoted to cold/. The connector-aware confirmation scan in
1679
+ // ExtractionPersistCoordinator previously scanned readAllMemories() (hot)
1680
+ // only, found no matching row, flipped exactDuplicate back to false, and
1681
+ // wrote a SECOND active hot copy. It must scan the cold tier too so the
1682
+ // cold-only active copy still suppresses the redundant hot write.
1683
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cold-dedup-"));
1684
+ const body =
1685
+ "The disaster-recovery drill runs on the first Sunday of every quarter.";
1686
+
1687
+ const makeConfig = () =>
1688
+ parseConfig({
1689
+ openaiApiKey: "sk-test",
1690
+ memoryDir,
1691
+ workspaceDir: path.join(memoryDir, "workspace"),
1692
+ qmdEnabled: false,
1693
+ embeddingFallbackEnabled: false,
1694
+ chunkingEnabled: false,
1695
+ multiGraphMemoryEnabled: false,
1696
+ factDeduplicationEnabled: true,
1697
+ });
1698
+
1699
+ // Phase 1 — persist a fact, then demote its only active copy to cold/.
1700
+ {
1701
+ const orch = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1702
+ const storage = await orch.getStorage("default");
1703
+ const { persistedIds: ids } = await orch.persistExtraction(factResult(body), storage, null, {
1704
+ sourceConnector: "chatgpt",
1705
+ });
1706
+ assert.equal(ids.length, 1, "fact persisted to hot in phase 1");
1707
+
1708
+ const [hotMemory] = await storage.readAllMemories();
1709
+ assert.ok(hotMemory, "the persisted fact is readable from the hot tier");
1710
+ const { changed } = await storage.migrateMemoryToTier(hotMemory, "cold");
1711
+ assert.equal(changed, true, "the fact was demoted to cold");
1712
+
1713
+ assert.equal(
1714
+ (await storage.readAllMemories()).length,
1715
+ 0,
1716
+ "no active copy remains in the hot tier after demotion",
1717
+ );
1718
+ const cold = await storage.readAllColdMemories();
1719
+ assert.equal(cold.length, 1, "the demoted fact is now in the cold tier");
1720
+ assert.equal(
1721
+ cold[0]?.frontmatter.status ?? "active",
1722
+ "active",
1723
+ "the cold copy is still active",
1724
+ );
1725
+ }
1726
+ clearMemoryCache(memoryDir); // model a fresh process
1727
+
1728
+ // Phase 2 — restart: the authoritative rebuild unions hot+cold, so the hash
1729
+ // index reports a hit for the cold-only copy.
1730
+ const orch2 = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1731
+ const storage2 = await orch2.getStorage("default");
1732
+ assert.equal(
1733
+ await orch2.hasContentHashDedup(storage2, body),
1734
+ true,
1735
+ "the demoted fact's hash survives the hot+cold rebuild",
1736
+ );
1737
+
1738
+ // Re-extraction of the same content with the same connector must be deduped
1739
+ // against the cold copy — no second hot copy is created.
1740
+ const { persistedIds: reIds } = await orch2.persistExtraction(factResult(body), storage2, null, {
1741
+ sourceConnector: "chatgpt",
1742
+ });
1743
+ assert.equal(
1744
+ reIds.length,
1745
+ 0,
1746
+ "cold-only active copy suppresses the duplicate hot write (would be 1 without the fix)",
1747
+ );
1748
+
1749
+ assert.equal(
1750
+ (await storage2.readAllMemories()).filter((m: MemoryFile) => m.content.includes(body))
1751
+ .length,
1752
+ 0,
1753
+ "no duplicate hot copy was written",
1754
+ );
1755
+ assert.equal(
1756
+ (await storage2.readAllColdMemories()).filter((m: MemoryFile) => m.content.includes(body))
1757
+ .length,
1758
+ 1,
1759
+ "the single cold copy remains the only copy of the fact",
1760
+ );
1761
+
1762
+ await rm(memoryDir, { recursive: true, force: true });
1763
+ });
1764
+
1765
+ test("#1909 (PR #2016) review: an authoritative instance still confirms a fact a peer flushed after its rebuild (no duplicate)", async () => {
1766
+ // Fresh finding beyond the crash/restart tests above: those model a NEW
1767
+ // process rebuilding from the corpus. Here BOTH instances stay live. Instance
1768
+ // A rebuilds its fact-hash index and marks it authoritative; instance B then
1769
+ // persists AND flushes a fact through the real persist path. A is still
1770
+ // flagged authoritative from its earlier rebuild, so without a freshness gate
1771
+ // its stale in-memory index answers a MISS and skips corpus confirmation —
1772
+ // persisting a duplicate active memory. The cheap per-operation freshness
1773
+ // check (durable index fingerprint advanced) must drop authority so A's
1774
+ // duplicate check finds B's fact.
1775
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-peer-fresh-"));
1776
+ const makeConfig = () =>
1777
+ parseConfig({
1778
+ openaiApiKey: "sk-test",
1779
+ memoryDir,
1780
+ workspaceDir: path.join(memoryDir, "workspace"),
1781
+ qmdEnabled: false,
1782
+ embeddingFallbackEnabled: false,
1783
+ chunkingEnabled: false,
1784
+ multiGraphMemoryEnabled: false,
1785
+ factDeduplicationEnabled: true,
1786
+ });
1787
+ const orchA = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1788
+ const orchB = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1789
+ const storageA = await orchA.getStorage("default");
1790
+ const storageB = await orchB.getStorage("default");
1791
+ await storageA.ensureDirectories();
1792
+
1793
+ const body = "The incident bridge auto-pages the on-call SRE after five minutes.";
1794
+
1795
+ // A rebuilds the index and becomes authoritative over a corpus without the
1796
+ // fact — its in-memory MISS for `body` is (correctly) trusted at this point.
1797
+ assert.equal(
1798
+ await storageA.isFactContentHashAuthoritative(),
1799
+ true,
1800
+ "A's fact-hash index rebuilt authoritative",
1801
+ );
1802
+ assert.equal(
1803
+ await orchA.hasContentHashDedup(storageA, body),
1804
+ false,
1805
+ "A has no such fact before B writes it",
1806
+ );
1807
+
1808
+ // B persists and flushes the fact through the real persist path (its
1809
+ // reconcile-save advances the durable fact-hashes.txt A rebuilt from).
1810
+ const { persistedIds: bIds } = await orchB.persistExtraction(factResult(body), storageB, null);
1811
+ assert.equal(bIds.length, 1, "B persisted the fact");
1812
+
1813
+ // A is still flagged authoritative, but the durable index advanced. The
1814
+ // freshness gate must catch it so A's duplicate check now finds B's fact
1815
+ // instead of trusting a stale miss.
1816
+ assert.equal(
1817
+ await orchA.hasContentHashDedup(storageA, body),
1818
+ true,
1819
+ "A sees B's flushed fact via the freshness-gated rebuild (stale-miss without the fix)",
1820
+ );
1821
+ assert.equal(
1822
+ await storageA.hasFactContentHash(body),
1823
+ true,
1824
+ "A's fact-only membership also reflects B's flushed fact",
1825
+ );
1826
+
1827
+ // Re-extracting the same fact on A is deduped — no duplicate .md is created.
1828
+ const { persistedIds: aIds } = await orchA.persistExtraction(factResult(body), storageA, null);
1829
+ assert.equal(aIds.length, 0, "A does not re-create B's fact (would be 1 without the fix)");
1830
+ const all = await new StorageManager(memoryDir).readAllMemories();
1831
+ assert.equal(
1832
+ all.filter((m: MemoryFile) => m.content.includes(body)).length,
1833
+ 1,
1834
+ "exactly one copy of the fact exists on disk",
1835
+ );
1836
+
1837
+ await rm(memoryDir, { recursive: true, force: true });
1838
+ });
1839
+
1840
+ test("#1909 (PR #2016): a re-extracted duplicate backfills temporal bounds onto a COLD-only active copy", async () => {
1841
+ // Finding: when a content-hash hit is confirmed only by the cold tier, the
1842
+ // dedup short-circuit fires but backfillTemporalBoundsOnDedupHit() scanned
1843
+ // readAllMemories() (hot) alone, so a cold active fact re-extracted with a
1844
+ // newly resolved invalid_at never had its cold copy patched and the corrected
1845
+ // temporal write was suppressed — leaving recall with stale bounds. The
1846
+ // helper must scan the cold tier too.
1847
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cold-backfill-"));
1848
+ const body =
1849
+ "The legacy billing pipeline drains all remaining invoices before shutdown.";
1850
+ const anchor = "2025-06-20T00:00:00.000Z";
1851
+
1852
+ const makeConfig = () =>
1853
+ parseConfig({
1854
+ openaiApiKey: "sk-test",
1855
+ memoryDir,
1856
+ workspaceDir: path.join(memoryDir, "workspace"),
1857
+ qmdEnabled: false,
1858
+ embeddingFallbackEnabled: false,
1859
+ chunkingEnabled: false,
1860
+ multiGraphMemoryEnabled: false,
1861
+ factDeduplicationEnabled: true,
1862
+ temporalBiTemporal: true,
1863
+ });
1864
+
1865
+ // Phase 1 — persist a fact with NO end bound, then demote it to cold.
1866
+ {
1867
+ const orch = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1868
+ const storage = await orch.getStorage("default");
1869
+ const { persistedIds: ids } = await orch.persistExtraction(factResult(body), storage, null, {
1870
+ sourceConnector: "chatgpt",
1871
+ validAt: anchor,
1872
+ });
1873
+ assert.equal(ids.length, 1, "fact persisted to hot in phase 1");
1874
+
1875
+ const [hotMemory] = await storage.readAllMemories();
1876
+ assert.ok(hotMemory, "the persisted fact is readable from the hot tier");
1877
+ assert.ok(
1878
+ !hotMemory.frontmatter.invalid_at,
1879
+ "the fact has no end bound before re-extraction",
1880
+ );
1881
+ const { changed } = await storage.migrateMemoryToTier(hotMemory, "cold");
1882
+ assert.equal(changed, true, "the fact was demoted to cold");
1883
+ assert.equal(
1884
+ (await storage.readAllMemories()).length,
1885
+ 0,
1886
+ "no active copy remains in the hot tier after demotion",
1887
+ );
1888
+ }
1889
+ clearMemoryCache(memoryDir); // model a fresh process
1890
+
1891
+ // Phase 2 — re-extract the SAME content, now carrying a resolved end bound
1892
+ // ("through 2026" → invalid_at 2027-01-01). The dedup hit is confirmed only
1893
+ // by the cold copy, so the backfill must patch the cold copy's invalid_at.
1894
+ const orch2 = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1895
+ const storage2 = await orch2.getStorage("default");
1896
+ const resultWithEndBound: ExtractionResult = {
1897
+ facts: [{ ...makeFact(body), eventTime: "through 2026" }],
1898
+ entities: [],
1899
+ relationships: [],
1900
+ questions: [],
1901
+ profileUpdates: [],
1902
+ };
1903
+ const { persistedIds: reIds } = await orch2.persistExtraction(resultWithEndBound, storage2, null, {
1904
+ sourceConnector: "chatgpt",
1905
+ validAt: anchor,
1906
+ });
1907
+ assert.equal(
1908
+ reIds.length,
1909
+ 0,
1910
+ "cold-only active copy suppresses the duplicate hot write",
1911
+ );
1912
+ assert.equal(
1913
+ (await storage2.readAllMemories()).length,
1914
+ 0,
1915
+ "no duplicate hot copy was written",
1916
+ );
1917
+
1918
+ const cold = await storage2.readAllColdMemories();
1919
+ const coldCopy = cold.find((m: MemoryFile) => m.content.includes(body));
1920
+ assert.ok(coldCopy, "the single cold copy remains");
1921
+ assert.equal(
1922
+ coldCopy!.frontmatter.invalid_at,
1923
+ "2027-01-01T00:00:00.000Z",
1924
+ "the corrected invalid_at was backfilled onto the COLD copy (empty without the fix)",
1925
+ );
1926
+
1927
+ await rm(memoryDir, { recursive: true, force: true });
1928
+ });
1929
+
1930
+ test("#1909 (PR #2016): a promoted fact demoted to cold is not re-promoted as a duplicate hot copy after restart", async () => {
1931
+ // Finding: adding cold memories to the authoritative hash rebuild means the
1932
+ // TARGET namespace's hasFactContentHash() can hit a promoted copy that was
1933
+ // demoted to cold/, but the promotion confirmation scan read readAllMemories()
1934
+ // (hot) alone, missed the cold copy, and fell through to writeSealedMemory —
1935
+ // creating a duplicate hot promotion. The confirmation must scan cold too.
1936
+ const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-cold-promo-"));
1937
+ const body = "The compliance archive is retained for exactly seven years.";
1938
+ const scopePlan = makePromotionScopePlan();
1939
+
1940
+ const makeConfig = () =>
1941
+ parseConfig({
1942
+ openaiApiKey: "sk-test",
1943
+ memoryDir,
1944
+ workspaceDir: path.join(memoryDir, "workspace"),
1945
+ qmdEnabled: false,
1946
+ embeddingFallbackEnabled: false,
1947
+ chunkingEnabled: false,
1948
+ multiGraphMemoryEnabled: false,
1949
+ factDeduplicationEnabled: true,
1950
+ namespacesEnabled: true,
1951
+ defaultNamespace: "default",
1952
+ sharedNamespace: "shared",
1953
+ });
1954
+
1955
+ // Phase 1 — pre-write the fact into the TARGET namespace, then demote its
1956
+ // only active copy to cold/.
1957
+ {
1958
+ const orch = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1959
+ const targetStorage = await orch.getStorage(PROMOTION_TARGET_NS);
1960
+ await targetStorage.ensureDirectories();
1961
+ await targetStorage.writeMemory("fact", body, {
1962
+ confidence: 0.9,
1963
+ sourceConnector: "chatgpt",
1964
+ });
1965
+ const [hotMemory] = await targetStorage.readAllMemories();
1966
+ assert.ok(hotMemory, "the pre-written promotion copy is in the hot tier");
1967
+ const { changed } = await targetStorage.migrateMemoryToTier(hotMemory, "cold");
1968
+ assert.equal(changed, true, "the promoted copy was demoted to cold");
1969
+ assert.equal(
1970
+ (await targetStorage.readAllMemories()).length,
1971
+ 0,
1972
+ "no active promotion copy remains in the hot tier",
1973
+ );
1974
+ assert.equal(
1975
+ (await targetStorage.readAllColdMemories()).length,
1976
+ 1,
1977
+ "the demoted promotion copy is now in the cold tier",
1978
+ );
1979
+ }
1980
+ clearMemoryCache(memoryDir); // model a fresh process
1981
+
1982
+ // Phase 2 — restart: the target rebuild unions hot+cold, so hasFactContentHash
1983
+ // reports a hit for the cold-only promoted copy.
1984
+ const orch2 = new Orchestrator(makeConfig()) as unknown as OrchestratorTestSurface;
1985
+ const storage2 = await orch2.getStorage("default");
1986
+ await storage2.ensureDirectories();
1987
+ const targetStorage2 = await orch2.getStorage(PROMOTION_TARGET_NS);
1988
+ assert.equal(
1989
+ await targetStorage2.hasFactContentHash(body),
1990
+ true,
1991
+ "the demoted promotion copy's hash survives the hot+cold rebuild",
1992
+ );
1993
+
1994
+ const { persistedIds: ids } = await orch2.persistExtraction(
1995
+ factResult(body),
1996
+ storage2,
1997
+ null,
1998
+ { sourceConnector: "chatgpt" },
1999
+ "default",
2000
+ scopePlan,
2001
+ );
2002
+ assert.equal(ids.length, 1, "source write must succeed");
2003
+
2004
+ // The promotion must be deduped against the cold copy — no duplicate hot
2005
+ // promotion is created (would be 1 without the fix).
2006
+ assert.equal(
2007
+ (await targetStorage2.readAllMemories()).filter((m: MemoryFile) =>
2008
+ m.content.includes(body),
2009
+ ).length,
2010
+ 0,
2011
+ "no duplicate hot promotion copy was written",
2012
+ );
2013
+ assert.equal(
2014
+ (await targetStorage2.readAllColdMemories()).filter((m: MemoryFile) =>
2015
+ m.content.includes(body),
2016
+ ).length,
2017
+ 1,
2018
+ "the single cold promotion copy remains the only copy",
2019
+ );
2020
+
2021
+ await rm(memoryDir, { recursive: true, force: true });
2022
+ });
2023
+
2024
+ test("#2016 thread SDyCj: a peer-advanced hash visible only after the post-miss authority rebuild is not written as a duplicate", async () => {
2025
+ // Distinct from the freshness-gate test in the defer suite: that pre-warms the
2026
+ // freshness so the FIRST hasContentHashDedup already returns a hit. Here we
2027
+ // isolate the exact race INSIDE a single persistExtraction — a peer advances
2028
+ // the durable index AFTER our MISS but BEFORE the authority check. The MISS was
2029
+ // read from the pre-rebuild snapshot; the authority check then rebuilds and
2030
+ // reports authoritative, but `exactDuplicate` stayed the stale `false`, so the
2031
+ // corpus-confirm block was skipped and a duplicate written. The fix re-runs the
2032
+ // lookup against the freshly authoritative set.
2033
+ const { orchestrator, storage, memoryDir } = await makeDedupOrchestrator();
2034
+ const body = "The release train departs every second Thursday at 1500 UTC.";
2035
+
2036
+ // Peer persisted the fact durably through the real path.
2037
+ const { persistedIds: first } = await orchestrator.persistExtraction(factResult(body), storage, null);
2038
+ assert.equal(first.length, 1, "peer's initial write persists");
2039
+
2040
+ // Model the race: the first dedup lookup in the NEXT persist reports a stale
2041
+ // MISS (captured before the peer flush was visible to this snapshot); the
2042
+ // authority check that follows rebuilds from the corpus (which contains the
2043
+ // fact) and reports authoritative, so the re-run lookup must catch it.
2044
+ const realHas = orchestrator.hasContentHashDedup.bind(orchestrator);
2045
+ let calls = 0;
2046
+ orchestrator.hasContentHashDedup = async (ts: StorageManager, content: string) => {
2047
+ calls += 1;
2048
+ if (calls === 1) return false; // stale-snapshot miss
2049
+ return realHas(ts, content); // now-authoritative result
2050
+ };
2051
+
2052
+ const { persistedIds: ids } = await orchestrator.persistExtraction(factResult(body), storage, null);
2053
+ assert.equal(
2054
+ ids.length,
2055
+ 0,
2056
+ "the re-run lookup after the authority rebuild catches the peer fact (a duplicate is written without the fix)",
2057
+ );
2058
+
2059
+ const all = await new StorageManager(memoryDir).readAllMemories();
2060
+ assert.equal(
2061
+ all.filter((m: MemoryFile) => m.content.includes(body)).length,
2062
+ 1,
2063
+ "exactly one copy of the fact exists on disk",
2064
+ );
2065
+
2066
+ await rm(memoryDir, { recursive: true, force: true });
2067
+ });
2068
+
2069
+ test("#2016 threads SDzOP/SDzOR: orchestrator archival removal clears fact-only membership (no stale hasFactContentHash)", async () => {
2070
+ // The orchestrator's category-agnostic removeContentHashForMemory (archival /
2071
+ // semantic consolidation) used to mutate only the shared index, leaving the
2072
+ // fact-ONLY membership set holding the removed fact's hash. hasFactContentHash
2073
+ // reads that set when authoritative, so it returned a stale `true` and
2074
+ // wearable / explicit-capture / promotion callers skipped a valid write until
2075
+ // the next corpus rebuild.
2076
+ const { orchestrator, storage } = await makeDedupOrchestrator();
2077
+ const body = "The nightly backup job rotates encryption keys every thirty days.";
2078
+
2079
+ // Register an active fact (shared index + fact-only set).
2080
+ await storage.writeMemory("fact", body, { source: "manual" });
2081
+ assert.equal(
2082
+ await storage.isFactContentHashAuthoritative(),
2083
+ true,
2084
+ "index is authoritative so hasFactContentHash reads the in-memory fact-only set",
2085
+ );
2086
+ assert.equal(await storage.hasFactContentHash(body), true, "fact is registered");
2087
+
2088
+ const [memory] = (await storage.readAllMemories()).filter((m: MemoryFile) =>
2089
+ m.content.includes(body),
2090
+ );
2091
+ assert.ok(memory, "the fact is on disk");
2092
+
2093
+ // Remove via the ORCHESTRATOR coordinator path (what archival uses). It does
2094
+ // NOT save, so the durable index fingerprint is unchanged and
2095
+ // hasFactContentHash still reads the in-memory fact-only set below.
2096
+ await orchestrator.removeContentHashForMemory(storage, memory, "fact-archival");
2097
+
2098
+ assert.equal(
2099
+ await storage.hasFactContentHash(body),
2100
+ false,
2101
+ "the removed fact must not linger in the fact-only membership (stale true without the fix)",
2102
+ );
2103
+ });