@remnic/core 9.7.15 → 9.9.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-DbZ1G3zV.d.ts} +29 -36
  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-L2B3EE7B.js → chunk-2QMKMVHH.js} +2 -2
  88. package/dist/{chunk-VJVNLNDQ.js → chunk-2UFO7X4Q.js} +7 -7
  89. package/dist/{chunk-2MR3MFQB.js → chunk-2VEOKUPP.js} +3 -3
  90. package/dist/chunk-2Z24E5W2.js +22 -0
  91. package/dist/chunk-2Z24E5W2.js.map +1 -0
  92. package/dist/{chunk-RHOZ5LLO.js → chunk-347WD2LE.js} +2 -2
  93. package/dist/{chunk-HDTQVNWD.js → chunk-3QAH2S33.js} +3 -3
  94. package/dist/{chunk-37CTGERM.js → chunk-3WCASNQZ.js} +12 -1
  95. package/dist/chunk-3WCASNQZ.js.map +1 -0
  96. package/dist/{chunk-LRNMRXJO.js → chunk-3Y224NYC.js} +3 -3
  97. package/dist/{chunk-3IY64MV6.js → chunk-44XZ75OQ.js} +44 -10
  98. package/dist/chunk-44XZ75OQ.js.map +1 -0
  99. package/dist/{chunk-VFSP73LP.js → chunk-45GF7Y3V.js} +2 -2
  100. package/dist/{chunk-K7LOWSLU.js → chunk-4BJQODZF.js} +2479 -1520
  101. package/dist/chunk-4BJQODZF.js.map +1 -0
  102. package/dist/{chunk-JREB6UFH.js → chunk-57OWKFUQ.js} +17 -6
  103. package/dist/chunk-57OWKFUQ.js.map +1 -0
  104. package/dist/{chunk-OEHCMALU.js → chunk-5I4M26Q7.js} +5 -5
  105. package/dist/{chunk-2AZXIZEN.js → chunk-5Q7EVV4X.js} +6 -6
  106. package/dist/{chunk-LC7OODWZ.js → chunk-5SB7SC2G.js} +80 -49
  107. package/dist/chunk-5SB7SC2G.js.map +1 -0
  108. package/dist/{chunk-PUBTHW5O.js → chunk-6DRPPQEJ.js} +2 -2
  109. package/dist/{chunk-NWTGD4AN.js → chunk-6KIQT7RG.js} +10 -9
  110. package/dist/chunk-6KIQT7RG.js.map +1 -0
  111. package/dist/{chunk-FDCTQWLW.js → chunk-6O22TZSB.js} +3 -3
  112. package/dist/{chunk-ZQ7UBUZ6.js → chunk-6QYST5XN.js} +6 -6
  113. package/dist/{chunk-W4PGTAS2.js → chunk-6WAFYNVT.js} +3 -3
  114. package/dist/chunk-6ZMAMSHS.js +20 -0
  115. package/dist/chunk-6ZMAMSHS.js.map +1 -0
  116. package/dist/{chunk-R5VAVVHP.js → chunk-72A4L5JJ.js} +2 -2
  117. package/dist/{chunk-X64YLC24.js → chunk-72VAXGEI.js} +5 -13
  118. package/dist/chunk-72VAXGEI.js.map +1 -0
  119. package/dist/{chunk-IKQTFQMI.js → chunk-755Q3DGF.js} +22 -3
  120. package/dist/chunk-755Q3DGF.js.map +1 -0
  121. package/dist/{chunk-D7FOKIY5.js → chunk-7AMFUATK.js} +12 -9
  122. package/dist/{chunk-D7FOKIY5.js.map → chunk-7AMFUATK.js.map} +1 -1
  123. package/dist/{chunk-BFVEEB7W.js → chunk-7ON2JW73.js} +57 -22
  124. package/dist/chunk-7ON2JW73.js.map +1 -0
  125. package/dist/chunk-7RNBVZZO.js +270 -0
  126. package/dist/chunk-7RNBVZZO.js.map +1 -0
  127. package/dist/{chunk-3FAMU5TX.js → chunk-A6USBZTO.js} +2 -2
  128. package/dist/{chunk-ZHIVCODT.js → chunk-A72SWTWW.js} +7 -7
  129. package/dist/{chunk-FC77VUX5.js → chunk-A7Q43EUZ.js} +2 -2
  130. package/dist/{chunk-UJBESW6X.js → chunk-AE7WMV4V.js} +7 -7
  131. package/dist/{chunk-J2HSAU72.js → chunk-AIK5W3IW.js} +29 -1
  132. package/dist/chunk-AIK5W3IW.js.map +1 -0
  133. package/dist/{chunk-A5UXZRRM.js → chunk-AJ7QCSIS.js} +2 -2
  134. package/dist/{chunk-FNMFJWBY.js → chunk-ANEUPF6O.js} +5 -5
  135. package/dist/chunk-AREGMX2J.js +50 -0
  136. package/dist/chunk-AREGMX2J.js.map +1 -0
  137. package/dist/chunk-AVPXXV6J.js +448 -0
  138. package/dist/chunk-AVPXXV6J.js.map +1 -0
  139. package/dist/{chunk-2A3OXAP2.js → chunk-B7RWBZXW.js} +2 -2
  140. package/dist/{chunk-QKQYXZVS.js → chunk-BI2OJF6J.js} +1819 -1481
  141. package/dist/chunk-BI2OJF6J.js.map +1 -0
  142. package/dist/{chunk-GWTFLF2P.js → chunk-BJMBJZ2Y.js} +59 -59
  143. package/dist/chunk-BJMBJZ2Y.js.map +1 -0
  144. package/dist/{chunk-74F5STS4.js → chunk-BLAKS3CP.js} +4 -4
  145. package/dist/{chunk-M7VGG3ID.js → chunk-BV5WMSIJ.js} +9 -9
  146. package/dist/{chunk-2BFLDH5F.js → chunk-BXLOS5AJ.js} +2 -2
  147. package/dist/{chunk-UXKMMNJD.js → chunk-C23BYTJV.js} +6 -6
  148. package/dist/{chunk-JZHHREBL.js → chunk-C3AKM55W.js} +2 -2
  149. package/dist/{chunk-RP7YXA67.js → chunk-C5JF6EZ7.js} +2 -2
  150. package/dist/{chunk-T7Y5KPLN.js → chunk-C6XBRYVD.js} +16 -15
  151. package/dist/chunk-C6XBRYVD.js.map +1 -0
  152. package/dist/{chunk-FTYDWKO6.js → chunk-CEN6DW6X.js} +12 -2
  153. package/dist/chunk-CEN6DW6X.js.map +1 -0
  154. package/dist/chunk-CGASUJVP.js +421 -0
  155. package/dist/chunk-CGASUJVP.js.map +1 -0
  156. package/dist/{chunk-EHCONI6E.js → chunk-CQMEM5CC.js} +2 -2
  157. package/dist/{chunk-PBNZVPFS.js → chunk-CXZQOVIE.js} +12 -12
  158. package/dist/chunk-D3V3UMET.js +44 -0
  159. package/dist/chunk-D3V3UMET.js.map +1 -0
  160. package/dist/{chunk-7N2RUBAP.js → chunk-DDA47TES.js} +2 -2
  161. package/dist/{chunk-64SE4X5G.js → chunk-DEBHXHJR.js} +3 -3
  162. package/dist/{chunk-EHK6KZHK.js → chunk-DNHVPZOD.js} +2 -2
  163. package/dist/{chunk-437S3G37.js → chunk-DQEMWVMT.js} +2 -2
  164. package/dist/{chunk-LZZNTPLR.js → chunk-DX3XRGLH.js} +2 -2
  165. package/dist/chunk-EA2H5LML.js +72 -0
  166. package/dist/chunk-EA2H5LML.js.map +1 -0
  167. package/dist/{chunk-INELTPRG.js → chunk-EEC7UJPX.js} +103 -106
  168. package/dist/chunk-EEC7UJPX.js.map +1 -0
  169. package/dist/{chunk-KSNVDPDF.js → chunk-EKBNDKDY.js} +21 -20
  170. package/dist/chunk-EKBNDKDY.js.map +1 -0
  171. package/dist/{chunk-YF6FMLBT.js → chunk-EQWEIIKQ.js} +64 -34
  172. package/dist/chunk-EQWEIIKQ.js.map +1 -0
  173. package/dist/{chunk-DZKHSDOM.js → chunk-ES36HTSC.js} +4 -4
  174. package/dist/{chunk-TN7THW5U.js → chunk-ET356KSB.js} +2 -2
  175. package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
  176. package/dist/{chunk-DMSDUGDM.js → chunk-F7GAIWFA.js} +20 -20
  177. package/dist/{chunk-G6XXLKC5.js → chunk-FFPJINJE.js} +6 -6
  178. package/dist/chunk-FMLKOL2Z.js +636 -0
  179. package/dist/chunk-FMLKOL2Z.js.map +1 -0
  180. package/dist/chunk-FQC3DHH2.js +20 -0
  181. package/dist/chunk-FQC3DHH2.js.map +1 -0
  182. package/dist/{chunk-G4CN3ZXL.js → chunk-FUTF72MJ.js} +15 -15
  183. package/dist/chunk-GCASGYIO.js +71 -0
  184. package/dist/chunk-GCASGYIO.js.map +1 -0
  185. package/dist/{chunk-HF77M34J.js → chunk-GFPZS3F5.js} +8 -8
  186. package/dist/{chunk-BCDKBDSY.js → chunk-GNVZQ5JN.js} +99 -41
  187. package/dist/chunk-GNVZQ5JN.js.map +1 -0
  188. package/dist/{chunk-G6NKALRN.js → chunk-H4WG3UQZ.js} +2 -2
  189. package/dist/{chunk-UDQUSU27.js → chunk-HNZKVLAN.js} +2 -2
  190. package/dist/{chunk-5EZXHU36.js → chunk-HYQJQGD3.js} +2 -2
  191. package/dist/{chunk-PLQE6MJA.js → chunk-IFH6LWTO.js} +50 -8
  192. package/dist/chunk-IFH6LWTO.js.map +1 -0
  193. package/dist/{chunk-RULE4VG5.js → chunk-ILGC6JH6.js} +21 -6
  194. package/dist/chunk-ILGC6JH6.js.map +1 -0
  195. package/dist/chunk-IUM3VSHI.js +113 -0
  196. package/dist/chunk-IUM3VSHI.js.map +1 -0
  197. package/dist/{chunk-TRWPCR6P.js → chunk-JECBQBEX.js} +3 -3
  198. package/dist/{chunk-E6CBU7XP.js → chunk-JYIFGGEM.js} +2 -2
  199. package/dist/{chunk-VA4WSW54.js → chunk-JYIJNNN3.js} +3 -3
  200. package/dist/{chunk-L5HJQC36.js → chunk-K2TDK7GG.js} +3 -3
  201. package/dist/{chunk-KDXVOFYY.js → chunk-KAVBJSBQ.js} +4 -4
  202. package/dist/{chunk-BS4SGLWV.js → chunk-KF5EUVKQ.js} +16 -6
  203. package/dist/chunk-KF5EUVKQ.js.map +1 -0
  204. package/dist/chunk-KFZY2OFH.js +42 -0
  205. package/dist/chunk-KFZY2OFH.js.map +1 -0
  206. package/dist/{chunk-4OKLXWDZ.js → chunk-KNB7IBBU.js} +2 -2
  207. package/dist/{chunk-QLULYKWZ.js → chunk-KSXJOC4F.js} +2 -2
  208. package/dist/{chunk-2S6QHZGJ.js → chunk-L2DPF4DF.js} +1 -1
  209. package/dist/{chunk-JI5AVXYO.js → chunk-LEPOP46W.js} +6 -6
  210. package/dist/{chunk-O7BBFJLP.js → chunk-LTAVSOZW.js} +2 -2
  211. package/dist/{chunk-E3VODCC3.js → chunk-LUDXM7MD.js} +14 -2
  212. package/dist/{chunk-E3VODCC3.js.map → chunk-LUDXM7MD.js.map} +1 -1
  213. package/dist/{chunk-2IZAGAJR.js → chunk-LULR4IUW.js} +3 -3
  214. package/dist/{chunk-QQGPUKMT.js → chunk-MC5Q3J7S.js} +398 -7
  215. package/dist/chunk-MC5Q3J7S.js.map +1 -0
  216. package/dist/{chunk-WMWVO45V.js → chunk-MC6XO7T4.js} +2 -2
  217. package/dist/chunk-MKQBXQKU.js +107 -0
  218. package/dist/chunk-MKQBXQKU.js.map +1 -0
  219. package/dist/{chunk-7PND4PUC.js → chunk-MMGGVZ4Q.js} +29 -9
  220. package/dist/{chunk-7PND4PUC.js.map → chunk-MMGGVZ4Q.js.map} +1 -1
  221. package/dist/{chunk-D6RIFG4O.js → chunk-MNB5RMAY.js} +2 -2
  222. package/dist/{chunk-Q5WG43OP.js → chunk-MYFQ3ED4.js} +7 -7
  223. package/dist/{chunk-V3UE5I5F.js → chunk-MYVPLJOO.js} +2 -2
  224. package/dist/chunk-NINSR6LX.js +83 -0
  225. package/dist/chunk-NINSR6LX.js.map +1 -0
  226. package/dist/{chunk-MTWKDOVO.js → chunk-NLE6TSLG.js} +2 -2
  227. package/dist/{chunk-OAZVR2BV.js → chunk-OGCAYLNA.js} +8 -6
  228. package/dist/{chunk-OAZVR2BV.js.map → chunk-OGCAYLNA.js.map} +1 -1
  229. package/dist/{chunk-DBRYEIGY.js → chunk-OJP3N7UK.js} +2 -2
  230. package/dist/{chunk-VDW4VU7B.js → chunk-OQJIB32H.js} +28 -23
  231. package/dist/chunk-OQJIB32H.js.map +1 -0
  232. package/dist/{chunk-SMX5267P.js → chunk-OZKOPQCM.js} +2 -2
  233. package/dist/{chunk-JCK7V6SN.js → chunk-P65YJEEL.js} +2 -2
  234. package/dist/{chunk-MTR7BZQN.js → chunk-PBGWYM3H.js} +3 -3
  235. package/dist/chunk-PIG4WJFQ.js +87 -0
  236. package/dist/chunk-PIG4WJFQ.js.map +1 -0
  237. package/dist/chunk-PJSK6OJM.js +201 -0
  238. package/dist/chunk-PJSK6OJM.js.map +1 -0
  239. package/dist/{chunk-OVJ2HIPE.js → chunk-PMUD7ERF.js} +158 -125
  240. package/dist/chunk-PMUD7ERF.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-SK7AMF2J.js +168 -0
  263. package/dist/chunk-SK7AMF2J.js.map +1 -0
  264. package/dist/{chunk-KSBEEMQF.js → chunk-TG6IHVE2.js} +151 -6
  265. package/dist/chunk-TG6IHVE2.js.map +1 -0
  266. package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
  267. package/dist/{chunk-DPAENM7O.js → chunk-U3ZAVRP3.js} +2 -2
  268. package/dist/{chunk-ODPLEWB6.js → chunk-UFGFMWAD.js} +47 -17
  269. package/dist/chunk-UFGFMWAD.js.map +1 -0
  270. package/dist/{chunk-4KJLYTLS.js → chunk-V57NLO5P.js} +2 -2
  271. package/dist/{chunk-FLVL4L3L.js → chunk-VDX2J7OX.js} +2 -2
  272. package/dist/{chunk-AJLLIIML.js → chunk-W4FBGYGK.js} +1 -1
  273. package/dist/{chunk-TXLGHDHS.js → chunk-W4XJFLAL.js} +53 -32
  274. package/dist/chunk-W4XJFLAL.js.map +1 -0
  275. package/dist/{chunk-6SXVCD7W.js → chunk-W7IGLX4G.js} +2 -2
  276. package/dist/{chunk-KQDNAXOM.js → chunk-WG5HSQ4P.js} +19 -6
  277. package/dist/chunk-WG5HSQ4P.js.map +1 -0
  278. package/dist/{chunk-LTJAMRGI.js → chunk-WL5EBRDH.js} +17 -3
  279. package/dist/chunk-WL5EBRDH.js.map +1 -0
  280. package/dist/{chunk-KAWTOYAW.js → chunk-WLZYGLJ4.js} +2 -2
  281. package/dist/{chunk-RO4LQGT3.js → chunk-WON4APQY.js} +4 -4
  282. package/dist/{chunk-PQVK2S3J.js → chunk-WZFAPHIK.js} +6 -6
  283. package/dist/{chunk-KQPJLK3X.js → chunk-XL24QQ63.js} +2 -2
  284. package/dist/{chunk-LZOV7MX7.js → chunk-XQDRKZ7V.js} +6476 -5747
  285. package/dist/chunk-XQDRKZ7V.js.map +1 -0
  286. package/dist/{chunk-RVZRYUQH.js → chunk-YROFNEG5.js} +7 -7
  287. package/dist/{chunk-RAR2BSEM.js → chunk-YV32N67O.js} +1 -1
  288. package/dist/chunk-YV32N67O.js.map +1 -0
  289. package/dist/{chunk-PBSAPHHA.js → chunk-YVKF3HEW.js} +1 -1
  290. package/dist/chunk-YVKF3HEW.js.map +1 -0
  291. package/dist/{chunk-VRAD2FGO.js → chunk-Z54EM5YZ.js} +12 -12
  292. package/dist/{chunk-2VN4JAX7.js → chunk-ZOKZIR5K.js} +95 -33
  293. package/dist/chunk-ZOKZIR5K.js.map +1 -0
  294. package/dist/{chunk-DCFV7A7H.js → chunk-ZVAYK7GU.js} +101 -91
  295. package/dist/chunk-ZVAYK7GU.js.map +1 -0
  296. package/dist/{cli-Cd5fMmhq.d.ts → cli-5tNLVTvJ.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 +94 -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 +172 -0
  617. package/src/access-observe-write-surface.ts +137 -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 +186 -179
  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 +245 -0
  781. package/src/write-quarantine.ts +290 -0
  782. package/src/write-rate-limiter.test.ts +105 -0
  783. package/src/write-rate-limiter.ts +135 -0
  784. package/src/write-sealed-parity.test.ts +204 -0
  785. package/dist/chunk-2VN4JAX7.js.map +0 -1
  786. package/dist/chunk-37CTGERM.js.map +0 -1
  787. package/dist/chunk-3IY64MV6.js.map +0 -1
  788. package/dist/chunk-BCDKBDSY.js.map +0 -1
  789. package/dist/chunk-BFPETA3S.js.map +0 -1
  790. package/dist/chunk-BFVEEB7W.js.map +0 -1
  791. package/dist/chunk-BS4SGLWV.js.map +0 -1
  792. package/dist/chunk-DCFV7A7H.js.map +0 -1
  793. package/dist/chunk-DYP3YYJJ.js.map +0 -1
  794. package/dist/chunk-E5BK6CGV.js +0 -335
  795. package/dist/chunk-E5BK6CGV.js.map +0 -1
  796. package/dist/chunk-FTYDWKO6.js.map +0 -1
  797. package/dist/chunk-GWTFLF2P.js.map +0 -1
  798. package/dist/chunk-IKQTFQMI.js.map +0 -1
  799. package/dist/chunk-INELTPRG.js.map +0 -1
  800. package/dist/chunk-J2HSAU72.js.map +0 -1
  801. package/dist/chunk-JREB6UFH.js.map +0 -1
  802. package/dist/chunk-K7LOWSLU.js.map +0 -1
  803. package/dist/chunk-KQDNAXOM.js.map +0 -1
  804. package/dist/chunk-KSBEEMQF.js.map +0 -1
  805. package/dist/chunk-KSNVDPDF.js.map +0 -1
  806. package/dist/chunk-LC7OODWZ.js.map +0 -1
  807. package/dist/chunk-LTJAMRGI.js.map +0 -1
  808. package/dist/chunk-LZOV7MX7.js.map +0 -1
  809. package/dist/chunk-NWTGD4AN.js.map +0 -1
  810. package/dist/chunk-ODPLEWB6.js.map +0 -1
  811. package/dist/chunk-OVJ2HIPE.js.map +0 -1
  812. package/dist/chunk-PBSAPHHA.js.map +0 -1
  813. package/dist/chunk-PHK3HARR.js +0 -32
  814. package/dist/chunk-PHK3HARR.js.map +0 -1
  815. package/dist/chunk-PLQE6MJA.js.map +0 -1
  816. package/dist/chunk-QKQYXZVS.js.map +0 -1
  817. package/dist/chunk-QQGPUKMT.js.map +0 -1
  818. package/dist/chunk-RANHFI3P.js +0 -8
  819. package/dist/chunk-RANHFI3P.js.map +0 -1
  820. package/dist/chunk-RAR2BSEM.js.map +0 -1
  821. package/dist/chunk-RULE4VG5.js.map +0 -1
  822. package/dist/chunk-SVOZFLIQ.js +0 -16
  823. package/dist/chunk-SVOZFLIQ.js.map +0 -1
  824. package/dist/chunk-T7Y5KPLN.js.map +0 -1
  825. package/dist/chunk-TXLGHDHS.js.map +0 -1
  826. package/dist/chunk-VDW4VU7B.js.map +0 -1
  827. package/dist/chunk-VSMRDBFT.js +0 -17
  828. package/dist/chunk-VSMRDBFT.js.map +0 -1
  829. package/dist/chunk-X64YLC24.js.map +0 -1
  830. package/dist/chunk-XDSXEKYK.js +0 -118
  831. package/dist/chunk-XDSXEKYK.js.map +0 -1
  832. package/dist/chunk-YF6FMLBT.js.map +0 -1
  833. package/dist/resolution-VH4NGENZ.js +0 -12
  834. /package/dist/{capsule-crypto-CZJSLEFG.js.map → access-citation.js.map} +0 -0
  835. /package/dist/{contradiction-scan-J57T5QV7.js.map → access-memory-search-fanout.js.map} +0 -0
  836. /package/dist/{dreams-ledger-6UODQOX6.js.map → access-recall-concurrency.js.map} +0 -0
  837. /package/dist/{graph-edge-decay-IOGBBAGC.js.map → access-recall-response.js.map} +0 -0
  838. /package/dist/{resolution-VH4NGENZ.js.map → bounded-jsonl-state.js.map} +0 -0
  839. /package/dist/{chunk-A3GOMRJG.js.map → chunk-23L3IXFD.js.map} +0 -0
  840. /package/dist/{chunk-MRHFJV44.js.map → chunk-2DQ2XREU.js.map} +0 -0
  841. /package/dist/{chunk-L2B3EE7B.js.map → chunk-2QMKMVHH.js.map} +0 -0
  842. /package/dist/{chunk-VJVNLNDQ.js.map → chunk-2UFO7X4Q.js.map} +0 -0
  843. /package/dist/{chunk-2MR3MFQB.js.map → chunk-2VEOKUPP.js.map} +0 -0
  844. /package/dist/{chunk-RHOZ5LLO.js.map → chunk-347WD2LE.js.map} +0 -0
  845. /package/dist/{chunk-HDTQVNWD.js.map → chunk-3QAH2S33.js.map} +0 -0
  846. /package/dist/{chunk-LRNMRXJO.js.map → chunk-3Y224NYC.js.map} +0 -0
  847. /package/dist/{chunk-VFSP73LP.js.map → chunk-45GF7Y3V.js.map} +0 -0
  848. /package/dist/{chunk-OEHCMALU.js.map → chunk-5I4M26Q7.js.map} +0 -0
  849. /package/dist/{chunk-2AZXIZEN.js.map → chunk-5Q7EVV4X.js.map} +0 -0
  850. /package/dist/{chunk-PUBTHW5O.js.map → chunk-6DRPPQEJ.js.map} +0 -0
  851. /package/dist/{chunk-FDCTQWLW.js.map → chunk-6O22TZSB.js.map} +0 -0
  852. /package/dist/{chunk-ZQ7UBUZ6.js.map → chunk-6QYST5XN.js.map} +0 -0
  853. /package/dist/{chunk-W4PGTAS2.js.map → chunk-6WAFYNVT.js.map} +0 -0
  854. /package/dist/{chunk-R5VAVVHP.js.map → chunk-72A4L5JJ.js.map} +0 -0
  855. /package/dist/{chunk-3FAMU5TX.js.map → chunk-A6USBZTO.js.map} +0 -0
  856. /package/dist/{chunk-ZHIVCODT.js.map → chunk-A72SWTWW.js.map} +0 -0
  857. /package/dist/{chunk-FC77VUX5.js.map → chunk-A7Q43EUZ.js.map} +0 -0
  858. /package/dist/{chunk-UJBESW6X.js.map → chunk-AE7WMV4V.js.map} +0 -0
  859. /package/dist/{chunk-A5UXZRRM.js.map → chunk-AJ7QCSIS.js.map} +0 -0
  860. /package/dist/{chunk-FNMFJWBY.js.map → chunk-ANEUPF6O.js.map} +0 -0
  861. /package/dist/{chunk-2A3OXAP2.js.map → chunk-B7RWBZXW.js.map} +0 -0
  862. /package/dist/{chunk-74F5STS4.js.map → chunk-BLAKS3CP.js.map} +0 -0
  863. /package/dist/{chunk-M7VGG3ID.js.map → chunk-BV5WMSIJ.js.map} +0 -0
  864. /package/dist/{chunk-2BFLDH5F.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  865. /package/dist/{chunk-UXKMMNJD.js.map → chunk-C23BYTJV.js.map} +0 -0
  866. /package/dist/{chunk-JZHHREBL.js.map → chunk-C3AKM55W.js.map} +0 -0
  867. /package/dist/{chunk-RP7YXA67.js.map → chunk-C5JF6EZ7.js.map} +0 -0
  868. /package/dist/{chunk-EHCONI6E.js.map → chunk-CQMEM5CC.js.map} +0 -0
  869. /package/dist/{chunk-PBNZVPFS.js.map → chunk-CXZQOVIE.js.map} +0 -0
  870. /package/dist/{chunk-7N2RUBAP.js.map → chunk-DDA47TES.js.map} +0 -0
  871. /package/dist/{chunk-64SE4X5G.js.map → chunk-DEBHXHJR.js.map} +0 -0
  872. /package/dist/{chunk-EHK6KZHK.js.map → chunk-DNHVPZOD.js.map} +0 -0
  873. /package/dist/{chunk-437S3G37.js.map → chunk-DQEMWVMT.js.map} +0 -0
  874. /package/dist/{chunk-LZZNTPLR.js.map → chunk-DX3XRGLH.js.map} +0 -0
  875. /package/dist/{chunk-DZKHSDOM.js.map → chunk-ES36HTSC.js.map} +0 -0
  876. /package/dist/{chunk-TN7THW5U.js.map → chunk-ET356KSB.js.map} +0 -0
  877. /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
  878. /package/dist/{chunk-DMSDUGDM.js.map → chunk-F7GAIWFA.js.map} +0 -0
  879. /package/dist/{chunk-G6XXLKC5.js.map → chunk-FFPJINJE.js.map} +0 -0
  880. /package/dist/{chunk-G4CN3ZXL.js.map → chunk-FUTF72MJ.js.map} +0 -0
  881. /package/dist/{chunk-HF77M34J.js.map → chunk-GFPZS3F5.js.map} +0 -0
  882. /package/dist/{chunk-G6NKALRN.js.map → chunk-H4WG3UQZ.js.map} +0 -0
  883. /package/dist/{chunk-UDQUSU27.js.map → chunk-HNZKVLAN.js.map} +0 -0
  884. /package/dist/{chunk-5EZXHU36.js.map → chunk-HYQJQGD3.js.map} +0 -0
  885. /package/dist/{chunk-TRWPCR6P.js.map → chunk-JECBQBEX.js.map} +0 -0
  886. /package/dist/{chunk-E6CBU7XP.js.map → chunk-JYIFGGEM.js.map} +0 -0
  887. /package/dist/{chunk-VA4WSW54.js.map → chunk-JYIJNNN3.js.map} +0 -0
  888. /package/dist/{chunk-L5HJQC36.js.map → chunk-K2TDK7GG.js.map} +0 -0
  889. /package/dist/{chunk-KDXVOFYY.js.map → chunk-KAVBJSBQ.js.map} +0 -0
  890. /package/dist/{chunk-4OKLXWDZ.js.map → chunk-KNB7IBBU.js.map} +0 -0
  891. /package/dist/{chunk-QLULYKWZ.js.map → chunk-KSXJOC4F.js.map} +0 -0
  892. /package/dist/{chunk-2S6QHZGJ.js.map → chunk-L2DPF4DF.js.map} +0 -0
  893. /package/dist/{chunk-JI5AVXYO.js.map → chunk-LEPOP46W.js.map} +0 -0
  894. /package/dist/{chunk-O7BBFJLP.js.map → chunk-LTAVSOZW.js.map} +0 -0
  895. /package/dist/{chunk-2IZAGAJR.js.map → chunk-LULR4IUW.js.map} +0 -0
  896. /package/dist/{chunk-WMWVO45V.js.map → chunk-MC6XO7T4.js.map} +0 -0
  897. /package/dist/{chunk-D6RIFG4O.js.map → chunk-MNB5RMAY.js.map} +0 -0
  898. /package/dist/{chunk-Q5WG43OP.js.map → chunk-MYFQ3ED4.js.map} +0 -0
  899. /package/dist/{chunk-V3UE5I5F.js.map → chunk-MYVPLJOO.js.map} +0 -0
  900. /package/dist/{chunk-MTWKDOVO.js.map → chunk-NLE6TSLG.js.map} +0 -0
  901. /package/dist/{chunk-DBRYEIGY.js.map → chunk-OJP3N7UK.js.map} +0 -0
  902. /package/dist/{chunk-SMX5267P.js.map → chunk-OZKOPQCM.js.map} +0 -0
  903. /package/dist/{chunk-JCK7V6SN.js.map → chunk-P65YJEEL.js.map} +0 -0
  904. /package/dist/{chunk-MTR7BZQN.js.map → chunk-PBGWYM3H.js.map} +0 -0
  905. /package/dist/{chunk-AXTXN4T6.js.map → chunk-PWFCHOB3.js.map} +0 -0
  906. /package/dist/{chunk-FDPZVY6D.js.map → chunk-QFN4U7AG.js.map} +0 -0
  907. /package/dist/{chunk-K2ZLHESQ.js.map → chunk-QIEUU4UK.js.map} +0 -0
  908. /package/dist/{chunk-3D6AFGQR.js.map → chunk-QVT3E4CE.js.map} +0 -0
  909. /package/dist/{chunk-SFQ6QNL7.js.map → chunk-R6YJI3HE.js.map} +0 -0
  910. /package/dist/{chunk-5CJVLG3H.js.map → chunk-RFDD3B7W.js.map} +0 -0
  911. /package/dist/{chunk-JBPKEARU.js.map → chunk-RKNJBZ55.js.map} +0 -0
  912. /package/dist/{chunk-FNZQGN76.js.map → chunk-S2BBISHU.js.map} +0 -0
  913. /package/dist/{chunk-IALLX7EE.js.map → chunk-S2FSBU4H.js.map} +0 -0
  914. /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
  915. /package/dist/{chunk-DPAENM7O.js.map → chunk-U3ZAVRP3.js.map} +0 -0
  916. /package/dist/{chunk-4KJLYTLS.js.map → chunk-V57NLO5P.js.map} +0 -0
  917. /package/dist/{chunk-FLVL4L3L.js.map → chunk-VDX2J7OX.js.map} +0 -0
  918. /package/dist/{chunk-AJLLIIML.js.map → chunk-W4FBGYGK.js.map} +0 -0
  919. /package/dist/{chunk-6SXVCD7W.js.map → chunk-W7IGLX4G.js.map} +0 -0
  920. /package/dist/{chunk-KAWTOYAW.js.map → chunk-WLZYGLJ4.js.map} +0 -0
  921. /package/dist/{chunk-RO4LQGT3.js.map → chunk-WON4APQY.js.map} +0 -0
  922. /package/dist/{chunk-PQVK2S3J.js.map → chunk-WZFAPHIK.js.map} +0 -0
  923. /package/dist/{chunk-KQPJLK3X.js.map → chunk-XL24QQ63.js.map} +0 -0
  924. /package/dist/{chunk-RVZRYUQH.js.map → chunk-YROFNEG5.js.map} +0 -0
  925. /package/dist/{chunk-VRAD2FGO.js.map → chunk-Z54EM5YZ.js.map} +0 -0
  926. /package/dist/{first-start-migration-TI67YMLG.js.map → first-start-migration-CSRHCJIQ.js.map} +0 -0
  927. /package/dist/{forget-CQCLB5LB.js.map → forget-HRPLVB3W.js.map} +0 -0
  928. /package/dist/{purge-NDWV6WYD.js.map → purge-IRF47ZTX.js.map} +0 -0
  929. /package/dist/{tier-stats-KV7IWCJR.js.map → tier-stats-QEJSPFV2.js.map} +0 -0
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  isTemporalQuery,
3
3
  recencyWindowBoundsFromPrompt
4
- } from "./chunk-2VN4JAX7.js";
5
- import {
6
- log
7
- } from "./chunk-37CTGERM.js";
4
+ } from "./chunk-ZOKZIR5K.js";
8
5
  import {
9
6
  isAbortError
10
7
  } from "./chunk-PVGDJXVK.js";
8
+ import {
9
+ log
10
+ } from "./chunk-3WCASNQZ.js";
11
11
 
12
12
  // src/retrieval-agents.ts
13
13
  import path from "path";
@@ -56,7 +56,11 @@ async function resolveContainedRegularFile(memoryDir, candidatePath, canonicalRo
56
56
  return null;
57
57
  }
58
58
  }
59
- async function runDirectAgent(query, memoryDir, maxResults = 10) {
59
+ function namespaceRelativeResult(result, _memoryDir, namespaceFromPath) {
60
+ if (!namespaceFromPath || !result.path || result.namespace) return result;
61
+ return { ...result, namespace: namespaceFromPath(result.path) };
62
+ }
63
+ async function runDirectAgent(query, memoryDir, maxResults = 10, namespaceFromPath) {
60
64
  try {
61
65
  const canonicalRoot = await realpath(memoryDir);
62
66
  const entitiesDir = path.join(memoryDir, "entities");
@@ -78,15 +82,21 @@ async function runDirectAgent(query, memoryDir, maxResults = 10) {
78
82
  const fullPath = path.join(entitiesDir, entry);
79
83
  const safePath = await resolveContainedRegularFile(memoryDir, fullPath, canonicalRoot);
80
84
  if (!safePath) continue;
81
- results.push({
82
- docid: nameWithoutExt,
83
- path: safePath,
84
- snippet: "",
85
- // populated by augmentWithDirectAndTemporal after merge
86
- score,
87
- transport: "scoped_prefilter",
88
- agentSource: "direct"
89
- });
85
+ results.push(
86
+ namespaceRelativeResult(
87
+ {
88
+ docid: nameWithoutExt,
89
+ path: safePath,
90
+ snippet: "",
91
+ // populated by augmentWithDirectAndTemporal after merge
92
+ score,
93
+ transport: "scoped_prefilter",
94
+ agentSource: "direct"
95
+ },
96
+ memoryDir,
97
+ namespaceFromPath
98
+ )
99
+ );
90
100
  }
91
101
  return results.sort((a, b) => b.score - a.score).slice(0, maxResults);
92
102
  } catch (err) {
@@ -94,7 +104,7 @@ async function runDirectAgent(query, memoryDir, maxResults = 10) {
94
104
  return [];
95
105
  }
96
106
  }
97
- async function runTemporalAgent(query, memoryDir, maxResults = 20, candidatePaths) {
107
+ async function runTemporalAgent(query, memoryDir, maxResults = 20, candidatePaths, namespaceFromPath) {
98
108
  try {
99
109
  const canonicalRoot = await realpath(memoryDir);
100
110
  let dateIndex = {};
@@ -231,15 +241,21 @@ async function runTemporalAgent(query, memoryDir, maxResults = 20, candidatePath
231
241
  }
232
242
  const score = recencyScore * (1 + topicBoost * 0.3);
233
243
  const baseName = path.basename(safePath, ".md");
234
- results.push({
235
- docid: baseName,
236
- path: safePath,
237
- snippet: "",
238
- // populated by augmentWithDirectAndTemporal after merge
239
- score,
240
- transport: "scoped_prefilter",
241
- agentSource: "temporal"
242
- });
244
+ results.push(
245
+ namespaceRelativeResult(
246
+ {
247
+ docid: baseName,
248
+ path: safePath,
249
+ snippet: "",
250
+ // populated by augmentWithDirectAndTemporal after merge
251
+ score,
252
+ transport: "scoped_prefilter",
253
+ agentSource: "temporal"
254
+ },
255
+ memoryDir,
256
+ namespaceFromPath
257
+ )
258
+ );
243
259
  }
244
260
  return results.sort((a, b) => b.score - a.score).slice(0, maxResults);
245
261
  } catch (err) {
@@ -262,16 +278,18 @@ async function runContextualAgent(query, qmd, collection, maxResults, signal) {
262
278
  return [];
263
279
  }
264
280
  }
265
- function mergeAgentResults(allResults, weights, maxResults) {
281
+ function mergeAgentResults(allResults, weights, maxResults, resolveNamespace) {
266
282
  const merged = /* @__PURE__ */ new Map();
267
283
  for (const result of allResults) {
268
- const key = result.path || result.docid;
284
+ const namespace = result.namespace ?? (resolveNamespace && result.path ? resolveNamespace(result.path) : void 0);
285
+ const key = `${namespace ?? ""}\0${result.path || result.docid}`;
269
286
  const weightedScore = result.score * weights[result.agentSource];
270
287
  const existing = merged.get(key);
271
288
  if (!existing || weightedScore > existing.score) {
272
289
  merged.set(key, {
273
290
  docid: result.docid,
274
291
  path: result.path,
292
+ namespace,
275
293
  // Preserve any snippet from the existing entry when the new one has none
276
294
  snippet: result.snippet || existing?.snippet || "",
277
295
  score: weightedScore,
@@ -311,7 +329,7 @@ async function populateEmptySnippets(results, memoryDir) {
311
329
  if (snippetMap.size === 0) return results;
312
330
  return results.map((r) => r.path && snippetMap.has(r.path) ? { ...r, snippet: snippetMap.get(r.path) } : r);
313
331
  }
314
- async function mergeWithAgentResults(contextualResults, directResults, temporalResults, weights, maxResults, memoryDir) {
332
+ async function mergeWithAgentResults(contextualResults, directResults, temporalResults, weights, maxResults, memoryDir, resolveNamespace) {
315
333
  if (directResults.length === 0 && temporalResults.length === 0) {
316
334
  return contextualResults.slice(0, maxResults);
317
335
  }
@@ -322,11 +340,12 @@ async function mergeWithAgentResults(contextualResults, directResults, temporalR
322
340
  const merged = mergeAgentResults(
323
341
  [...contextualTagged, ...directResults, ...temporalResults],
324
342
  weights,
325
- maxResults
343
+ maxResults,
344
+ resolveNamespace
326
345
  );
327
346
  return populateEmptySnippets(merged, memoryDir);
328
347
  }
329
- async function augmentWithDirectAndTemporal(query, memoryDir, contextualResults, weights, maxPerAgent, maxResults, candidatePaths) {
348
+ async function augmentWithDirectAndTemporal(query, memoryDir, contextualResults, weights, maxPerAgent, maxResults, candidatePaths, resolveNamespace) {
330
349
  if (maxPerAgent === 0) return contextualResults;
331
350
  const knownEntityCount = (query.match(/\b[A-Z][a-z]{1,}/g) ?? []).length;
332
351
  const runDirect = shouldRunAgent("direct", query, knownEntityCount);
@@ -357,7 +376,8 @@ async function augmentWithDirectAndTemporal(query, memoryDir, contextualResults,
357
376
  const merged = mergeAgentResults(
358
377
  [...contextualTagged, ...scopedDirect, ...temporalResults],
359
378
  weights,
360
- maxResults
379
+ maxResults,
380
+ resolveNamespace
361
381
  );
362
382
  return populateEmptySnippets(merged, memoryDir);
363
383
  }
@@ -391,7 +411,8 @@ async function parallelRetrieval(query, qmd, memoryDir, options = {}) {
391
411
  const merged = mergeAgentResults(
392
412
  [...contextualResults, ...directResults, ...temporalResults],
393
413
  options.agentWeights ?? PARALLEL_AGENT_WEIGHTS,
394
- maxResults
414
+ maxResults,
415
+ options.resolveNamespace
395
416
  );
396
417
  return populateEmptySnippets(merged, memoryDir);
397
418
  }
@@ -406,4 +427,4 @@ export {
406
427
  augmentWithDirectAndTemporal,
407
428
  parallelRetrieval
408
429
  };
409
- //# sourceMappingURL=chunk-TXLGHDHS.js.map
430
+ //# sourceMappingURL=chunk-W4XJFLAL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/retrieval-agents.ts"],"sourcesContent":["/**\n * Parallel Specialized Retrieval (v9.1 — ASMR-inspired)\n *\n * Replaces single-pass hybrid search with three parallel specialized agents:\n *\n * 1. DirectFactAgent — entity-first lookup via entity filename index (<5ms)\n * 2. ContextualAgent — existing hybridSearch (same cost as current single-pass)\n * 3. TemporalAgent — temporal index prefilter + recency scoring (<10ms)\n *\n * All agents run via Promise.all(), so total latency = max(agents), not sum.\n * No new LLM inference is introduced — agents reuse existing search primitives.\n *\n * Graceful degradation: any agent error/timeout does not block the others.\n *\n * References:\n * - Supermemory ASMR: https://blog.supermemory.ai/...\n * - Spec: docs/ideas/parallel-specialized-retrieval.md\n */\n\nimport path from \"node:path\";\nimport { lstat, readdir, readFile, realpath, stat } from \"node:fs/promises\";\nimport { log } from \"./logger.js\";\nimport type { QmdSearchResult } from \"./types.js\";\nimport type { QmdClient } from \"./qmd.js\";\nimport { isTemporalQuery, recencyWindowBoundsFromPrompt } from \"./temporal-index.js\";\nimport { isAbortError } from \"./abort-error.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport type SearchAgentSource = \"direct\" | \"contextual\" | \"temporal\";\n\nexport interface ParallelSearchResult extends QmdSearchResult {\n /** Which retrieval agent produced this result. */\n agentSource: SearchAgentSource;\n}\n\n/** Source-based weights applied during merge. Higher = higher confidence/precision. */\nexport const PARALLEL_AGENT_WEIGHTS: Record<SearchAgentSource, number> = {\n direct: 1.0, // entity-first: high precision\n temporal: 0.85, // recency-boosted: relevant but broader\n contextual: 0.7, // semantic: broadest, lower precision\n};\n\n// ─── Query classification ────────────────────────────────────────────────────\n\n/**\n * Decide whether a given agent should run for this query.\n * Agent 1 (direct) is the only one that may be skipped — when the query\n * contains no proper-noun-like tokens, entity lookups will return nothing.\n * Agents 2 (contextual) and 3 (temporal) always run.\n */\nexport function shouldRunAgent(\n agent: SearchAgentSource,\n query: string,\n knownEntityCount: number,\n): boolean {\n switch (agent) {\n case \"direct\":\n // Skip only if query has no word-like tokens at all. Both capitalized and\n // lowercase entity names are stored (normalizeEntityName lowercases them),\n // so gating on capitalization would silently skip most real entity lookups.\n return knownEntityCount > 0 || /\\b\\w{2,}/.test(query);\n case \"temporal\":\n // Only run temporal agent when the query actually asks about a time window.\n // Running it for every query injects recency bias into semantic searches.\n return isTemporalQuery(query);\n case \"contextual\":\n return true;\n default:\n return true;\n }\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction tokenize(value: string): string[] {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim()\n .split(/\\s+/)\n .filter((t) => t.length >= 2);\n}\n\nfunction overlapScore(queryTokens: string[], candidateTokens: string[]): number {\n if (queryTokens.length === 0 || candidateTokens.length === 0) return 0;\n const candidateSet = new Set(candidateTokens);\n const hits = queryTokens.filter((t) => candidateSet.has(t)).length;\n return hits / Math.max(queryTokens.length, candidateTokens.length);\n}\n\nasync function resolveContainedRegularFile(\n memoryDir: string,\n candidatePath: string,\n canonicalRoot?: string,\n): Promise<string | null> {\n try {\n const root = canonicalRoot ?? await realpath(memoryDir);\n const absolute = path.isAbsolute(candidatePath)\n ? path.resolve(candidatePath)\n : path.resolve(memoryDir, candidatePath);\n const entryStat = await lstat(absolute);\n if (entryStat.isSymbolicLink()) return null;\n const fileStat = await stat(absolute);\n if (!fileStat.isFile()) return null;\n const canonical = await realpath(absolute);\n if (canonical !== root && !canonical.startsWith(root + path.sep)) return null;\n return absolute;\n } catch {\n return null;\n }\n}\n\n// ─── Agent 1: Direct Facts ───────────────────────────────────────────────────\n\n/**\n * Direct Facts Agent — entity-first retrieval.\n *\n * Reads entity filenames from the entities directory and scores them by\n * keyword overlap with the query. Returns matching entity file paths as\n * QmdSearchResult[]. Zero LLM inference; pure file I/O.\n *\n * Cost: readdir on entities/ + filename scoring. Typically <5ms.\n *\n */\n// Agents scan one namespace storage dir and return absolute paths. Contextual\n// fanout hits are also absolute (globally-unique identity), so we keep the\n// agent path absolute and only stamp the owning namespace here — that carries\n// the namespace through merge/tracking without re-deriving it (#2020). No-op\n// without a resolver.\nfunction namespaceRelativeResult(\n result: ParallelSearchResult,\n _memoryDir: string,\n namespaceFromPath?: (p: string) => string,\n): ParallelSearchResult {\n if (!namespaceFromPath || !result.path || result.namespace) return result;\n return { ...result, namespace: namespaceFromPath(result.path) };\n}\n\nexport async function runDirectAgent(\n query: string,\n memoryDir: string,\n maxResults = 10,\n namespaceFromPath?: (p: string) => string,\n): Promise<ParallelSearchResult[]> {\n try {\n const canonicalRoot = await realpath(memoryDir);\n const entitiesDir = path.join(memoryDir, \"entities\");\n let entries: string[];\n try {\n entries = await readdir(entitiesDir);\n } catch {\n return []; // entities dir missing or unreadable — not an error\n }\n\n const queryTokens = tokenize(query);\n if (queryTokens.length === 0) return [];\n\n const results: ParallelSearchResult[] = [];\n for (const entry of entries) {\n if (!entry.endsWith(\".md\")) continue;\n\n // Filename tokens: split on hyphens (normalizeEntityName uses hyphens)\n const nameWithoutExt = entry.slice(0, -3);\n const entityTokens = nameWithoutExt.split(\"-\").filter((t) => t.length >= 2).map((t) => t.toLowerCase());\n\n const score = overlapScore(queryTokens, entityTokens);\n if (score <= 0) continue;\n\n const fullPath = path.join(entitiesDir, entry);\n const safePath = await resolveContainedRegularFile(memoryDir, fullPath, canonicalRoot);\n if (!safePath) continue;\n\n results.push(\n namespaceRelativeResult(\n {\n docid: nameWithoutExt,\n path: safePath,\n snippet: \"\", // populated by augmentWithDirectAndTemporal after merge\n score,\n transport: \"scoped_prefilter\",\n agentSource: \"direct\",\n },\n memoryDir,\n namespaceFromPath,\n ),\n );\n }\n\n return results\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n } catch (err) {\n log.debug(`DirectFactAgent error: ${err}`);\n return [];\n }\n}\n\n// ─── Agent 3: Temporal ───────────────────────────────────────────────────────\n\n/**\n * Temporal Agent — recency-focused retrieval.\n *\n * Uses the temporal index (index_time.json) to find memories from the\n * recent time window derived from the query (default: last 7 days).\n * Combines recency decay with query-filename token overlap so results stay\n * relevant to the query content, not just recency. Zero LLM inference; File I/O + math.\n *\n * Cost: read index_time.json + date range scan. Typically <10ms.\n */\nexport async function runTemporalAgent(\n query: string,\n memoryDir: string,\n maxResults = 20,\n /** Optional candidate set from query-aware prefilter. Applied BEFORE the top-K cap so\n * in-scope entries are not displaced by newer out-of-scope entries. */\n candidatePaths?: Set<string> | null,\n namespaceFromPath?: (p: string) => string,\n): Promise<ParallelSearchResult[]> {\n try {\n const canonicalRoot = await realpath(memoryDir);\n // Read index_time.json once — used for both date-range filtering and recency scoring.\n let dateIndex: Record<string, string[]> = {};\n try {\n const indexPath = path.join(memoryDir, \"state\", \"index_time.json\");\n const raw = await readFile(indexPath, \"utf-8\");\n const parsed = JSON.parse(raw) as { dates?: Record<string, string[]> };\n dateIndex = parsed.dates ?? {};\n } catch {\n return []; // Index missing or unreadable — nothing to return\n }\n\n // Derive both window edges from the same function so fromDate/toDate always use\n // consistent pattern-matching and priority ordering.\n const { fromDate, toDate } = recencyWindowBoundsFromPrompt(query);\n\n // Build path → date map, filtering to the recency window in one pass.\n // Apply candidatePaths here (before scoring + top-K) so in-scope entries are\n // not displaced by newer out-of-scope entries at the slice boundary.\n const pathToDate = new Map<string, string>();\n for (const [date, datePaths] of Object.entries(dateIndex)) {\n // toDate is an exclusive upper bound (the first day NOT included in the window).\n // e.g. \"3 days ago\" → fromDate=\"2026-03-12\", toDate=\"2026-03-13\" → only includes 2026-03-12.\n if (date >= fromDate && date < toDate) {\n for (const p of datePaths) {\n // Skip paths excluded by the query-aware prefilter scope\n if (candidatePaths && !candidatePaths.has(p)) continue;\n if (!pathToDate.has(p)) pathToDate.set(p, date);\n }\n }\n }\n\n if (pathToDate.size === 0) return [];\n\n const todayMs = Date.now();\n\n // Extract non-temporal topic tokens for tag-based relevance boost.\n // Purely temporal words (dates, relative time) and common stopwords are stripped\n // so the signal reflects the subject the user is asking about (e.g. \"auth\" in\n // \"what changed in auth last week\"), not the time window itself.\n const TEMPORAL_STOPWORDS = new Set([\n \"today\", \"yesterday\", \"this\", \"last\", \"week\", \"month\", \"year\",\n \"morning\", \"night\", \"now\", \"earlier\", \"since\", \"after\", \"before\",\n \"ago\", \"hours\", \"days\", \"weeks\", \"months\",\n \"january\", \"february\", \"march\", \"april\", \"may\", \"june\",\n \"july\", \"august\", \"september\", \"october\", \"november\", \"december\",\n \"monday\", \"tuesday\", \"wednesday\", \"thursday\", \"friday\", \"saturday\", \"sunday\",\n \"what\", \"when\", \"where\", \"who\", \"why\", \"how\", \"the\", \"a\", \"an\",\n \"and\", \"or\", \"in\", \"on\", \"at\", \"to\", \"of\", \"for\", \"with\", \"by\",\n \"from\", \"changed\", \"happened\", \"was\", \"were\", \"is\", \"are\", \"did\",\n \"about\", \"that\", \"have\", \"had\", \"has\", \"been\", \"any\", \"all\",\n ]);\n const topicTokens = tokenize(query).filter((t) => !TEMPORAL_STOPWORDS.has(t));\n\n // Load tag index for topical scoring, in parallel with existence checks.\n // Fail-open: if the tag index is unavailable, topicBoost = 0 and scores\n // degrade gracefully to recency-only ordering (existing behaviour).\n const entries = [...pathToDate.entries()];\n const tagIndexPromise: Promise<Map<string, string[]>> = topicTokens.length > 0\n ? readFile(path.join(memoryDir, \"state\", \"index_tags.json\"), \"utf-8\")\n .then((raw) => {\n const parsed = JSON.parse(raw) as { tags?: Record<string, { paths?: string[] } | string[]> };\n const result = new Map<string, string[]>();\n for (const [tag, node] of Object.entries(parsed.tags ?? {})) {\n const tagPaths = Array.isArray(node) ? node : (node.paths ?? []);\n for (const tp of tagPaths) {\n const existing = result.get(tp);\n if (existing) existing.push(tag);\n else result.set(tp, [tag]);\n }\n }\n return result;\n })\n .catch(() => new Map<string, string[]>())\n : Promise.resolve(new Map<string, string[]>());\n\n // Parallel: existence checks + tag index read.\n const [safePathResults, pathToTags] = await Promise.all([\n Promise.all(entries.map(([p]) => resolveContainedRegularFile(memoryDir, p, canonicalRoot))),\n tagIndexPromise,\n ]);\n\n const results: ParallelSearchResult[] = [];\n\n for (let i = 0; i < entries.length; i++) {\n const [p, dateStr] = entries[i];\n const safePath = safePathResults[i];\n if (!safePath) continue; // skip stale or out-of-root index entry\n const ageMs = todayMs - new Date(dateStr).getTime();\n const ageDays = ageMs / 86_400_000;\n // Exponential recency decay with half-life of ~30 days.\n // score ≈ 1.0 at day 0, ≈ 0.79 at 7 days, ≈ 0.36 at 30 days, ≈ 0.13 at 60 days.\n const recencyScore = Math.exp(-ageDays / 30);\n\n // Topical boost: fraction of non-temporal query tokens matched by this file's tags.\n // Adds up to +30% on recency score for a perfect tag match; zero when query is\n // purely temporal (no topic tokens) or the tag index doesn't cover the file.\n let topicBoost = 0;\n if (topicTokens.length > 0) {\n const tags = pathToTags.get(p) ?? pathToTags.get(safePath);\n if (tags && tags.length > 0) {\n const tagSet = new Set(tags.flatMap((t) => tokenize(t)));\n const hits = topicTokens.filter((t) => tagSet.has(t)).length;\n topicBoost = hits / topicTokens.length;\n }\n }\n const score = recencyScore * (1 + topicBoost * 0.3);\n\n const baseName = path.basename(safePath, \".md\");\n\n results.push(\n namespaceRelativeResult(\n {\n docid: baseName,\n path: safePath,\n snippet: \"\", // populated by augmentWithDirectAndTemporal after merge\n score,\n transport: \"scoped_prefilter\",\n agentSource: \"temporal\",\n },\n memoryDir,\n namespaceFromPath,\n ),\n );\n }\n\n return results\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n } catch (err) {\n log.debug(`TemporalAgent error: ${err}`);\n return [];\n }\n}\n\n// ─── Agent 2: Contextual ─────────────────────────────────────────────────────\n\n/**\n * Contextual Agent — broad semantic/hybrid search.\n *\n * Delegates to the existing hybridSearch (BM25 + vector). Same cost as the\n * current single-pass retrieval — this agent introduces no additional LLM calls.\n */\nexport async function runContextualAgent(\n query: string,\n qmd: QmdClient,\n collection: string | undefined,\n maxResults: number,\n signal?: AbortSignal,\n): Promise<ParallelSearchResult[]> {\n try {\n const results = await qmd.hybridSearch(\n query,\n collection,\n maxResults,\n signal ? { signal } : undefined,\n );\n return results.map((r: QmdSearchResult) => ({ ...r, agentSource: \"contextual\" as const }));\n } catch (err) {\n if (isAbortError(err)) throw err;\n log.debug(`ContextualAgent error: ${err}`);\n return [];\n }\n}\n\n// ─── Merge helper ─────────────────────────────────────────────────────────────\n\n/**\n * Merge results from multiple agents into a single deduplicated, weighted list.\n * Deduplicates by (namespace, path). Contextual results carry a `namespace`\n * (from the namespace-fanout search); direct/temporal results do not. When a\n * `resolveNamespace` resolver is supplied, the missing namespace is derived from\n * the result path the same canonical way the contextual side derives it\n * (`result.namespace ?? resolveNamespace(result.path)`), so a direct/temporal hit\n * and a contextual hit for the same (namespace, path) collapse to ONE merge key\n * instead of being injected and access-counted twice. Preserves snippets: if a\n * higher-scoring result lacks a snippet, the existing snippet from a lower-scoring\n * source is retained.\n */\nfunction mergeAgentResults(\n allResults: ParallelSearchResult[],\n weights: Record<SearchAgentSource, number>,\n maxResults: number,\n resolveNamespace?: (path: string) => string,\n): QmdSearchResult[] {\n const merged = new Map<string, QmdSearchResult>();\n for (const result of allResults) {\n const namespace =\n result.namespace ?? (resolveNamespace && result.path ? resolveNamespace(result.path) : undefined);\n const key = `${namespace ?? \"\"}\\0${result.path || result.docid}`;\n const weightedScore = result.score * weights[result.agentSource];\n const existing = merged.get(key);\n if (!existing || weightedScore > existing.score) {\n merged.set(key, {\n docid: result.docid,\n path: result.path,\n namespace,\n // Preserve any snippet from the existing entry when the new one has none\n snippet: result.snippet || existing?.snippet || \"\",\n score: weightedScore,\n transport: \"hybrid\",\n });\n }\n }\n return [...merged.values()]\n .sort((a, b) => b.score - a.score)\n .slice(0, maxResults);\n}\n\n/**\n * For merged results that have an empty snippet, attempt to read the first\n * 200 characters of the file as a fallback preview. Bounded to MAX_SNIPPET_READS\n * to cap I/O when many specialized-agent results are present.\n */\nconst SNIPPET_PREVIEW_CHARS = 200;\nconst MAX_SNIPPET_READS = 8;\n\n/** Strip YAML frontmatter (---...---) and return the document body. */\nfunction stripFrontmatter(raw: string): string {\n if (!raw.startsWith(\"---\")) return raw;\n const end = raw.indexOf(\"\\n---\", 3);\n if (end === -1) return raw;\n // Skip the closing \"---\" line and any leading blank lines\n return raw.slice(end + 4).replace(/^\\n+/, \"\");\n}\n\nasync function populateEmptySnippets(results: QmdSearchResult[], memoryDir?: string): Promise<QmdSearchResult[]> {\n const needsSnippet = results.filter((r) => !r.snippet && r.path);\n if (needsSnippet.length === 0) return results;\n const canonicalRoot = memoryDir ? await realpath(memoryDir).catch(() => null) : null;\n\n const toRead = needsSnippet.slice(0, MAX_SNIPPET_READS);\n const snippetMap = new Map<string, string>();\n\n await Promise.all(\n toRead.map(async (r) => {\n try {\n const safePath = canonicalRoot && r.path\n ? await resolveContainedRegularFile(memoryDir!, r.path, canonicalRoot)\n : null;\n if (!safePath) return;\n const raw = await readFile(safePath, \"utf-8\");\n // Memory files start with YAML frontmatter — skip it so the snippet\n // contains actual memory content, not metadata fields.\n const body = stripFrontmatter(raw);\n const preview = body.slice(0, SNIPPET_PREVIEW_CHARS).replace(/\\s+/g, \" \").trim();\n if (preview) snippetMap.set(r.path, preview);\n } catch {\n // File unreadable — leave snippet empty\n }\n }),\n );\n\n if (snippetMap.size === 0) return results;\n return results.map((r) => (r.path && snippetMap.has(r.path) ? { ...r, snippet: snippetMap.get(r.path)! } : r));\n}\n\n/**\n * Merge pre-computed direct and temporal agent results with contextual results.\n *\n * This is the merge half of augmentWithDirectAndTemporal. Expose it separately so\n * callers that started agents concurrently with the contextual search (for true\n * parallel latency) can merge after all three complete, without re-running agents.\n *\n * Handles the same cases as augmentWithDirectAndTemporal:\n * - Returns contextual unchanged (no reweighting) when both agents return nothing\n * - Applies maxResults cap in all paths\n * - Populates snippets for results discovered only by specialized agents\n */\nexport async function mergeWithAgentResults(\n contextualResults: QmdSearchResult[],\n directResults: ParallelSearchResult[],\n temporalResults: ParallelSearchResult[],\n weights: Record<SearchAgentSource, number>,\n maxResults: number,\n memoryDir?: string,\n /** Optional namespace resolver. Direct/temporal results lack a namespace under\n * namespace fanout; deriving it from the path lets them collapse onto the same\n * merge key as the contextual result for the same (namespace, path). */\n resolveNamespace?: (path: string) => string,\n): Promise<QmdSearchResult[]> {\n if (directResults.length === 0 && temporalResults.length === 0) {\n return contextualResults.slice(0, maxResults);\n }\n\n const contextualTagged: ParallelSearchResult[] = contextualResults.map((r) => ({\n ...r,\n agentSource: \"contextual\" as const,\n }));\n\n const merged = mergeAgentResults(\n [...contextualTagged, ...directResults, ...temporalResults],\n weights,\n maxResults,\n resolveNamespace,\n );\n return populateEmptySnippets(merged, memoryDir);\n}\n\n// ─── Augmentation helper (used by tests; orchestrator uses inline logic) ─────\n//\n// NOTE: This function passes the same `memoryDir` to both direct and temporal\n// agents. The orchestrator's inline implementation differs: it passes a\n// namespace-specific storage dir to runDirectAgent and the shared root to\n// runTemporalAgent. augmentWithDirectAndTemporal is intentionally simpler\n// (single-dir assumption) and is exercised by unit tests where both agents\n// operate on the same temporary directory.\n\n/**\n * Augment a set of contextual (hybridSearch) results with direct and temporal\n * agent results, returning a merged, deduplicated list.\n *\n * **Test helper** — production recall uses the inline orchestrator path which\n * passes namespace-specific directories to each agent. This function uses a\n * simplified single-directory contract suitable for unit tests.\n *\n * Direct and temporal agents run in parallel, then all three are merged with\n * configurable weights.\n *\n * Contextual weight is always applied to `contextualResults` so scoring is\n * consistent regardless of whether direct/temporal agents return anything.\n *\n * Snippets: snippets from contextual results are preserved during merge. For\n * results discovered only by direct/temporal agents (no contextual match),\n * the first ~200 chars of the file are read as a fallback preview so downstream\n * formatters and rerankers have content to work with, not just a path.\n */\nexport async function augmentWithDirectAndTemporal(\n query: string,\n memoryDir: string,\n contextualResults: QmdSearchResult[],\n weights: Record<SearchAgentSource, number>,\n maxPerAgent: number,\n maxResults: number,\n /** Optional candidate set from query-aware prefilter (time/tag scoped). Agents' results are\n * filtered to this set when provided so recall stays within the operator-specified scope. */\n candidatePaths?: Set<string> | null,\n /** Optional namespace resolver so direct/temporal results collapse onto the same\n * (namespace, path) merge key as their contextual counterpart. */\n resolveNamespace?: (path: string) => string,\n): Promise<QmdSearchResult[]> {\n // maxPerAgent=0 is a hard disable: skip agents entirely and return contextual unchanged\n if (maxPerAgent === 0) return contextualResults;\n\n const knownEntityCount = (query.match(/\\b[A-Z][a-z]{1,}/g) ?? []).length;\n const runDirect = shouldRunAgent(\"direct\", query, knownEntityCount);\n const runTemporal = shouldRunAgent(\"temporal\", query, knownEntityCount);\n\n const startMs = Date.now();\n const [directResults, temporalResults] = await Promise.all([\n runDirect\n ? runDirectAgent(query, memoryDir, maxPerAgent).catch((err) => {\n log.debug(`augmentWithDirectAndTemporal: DirectAgent failed — ${err}`);\n return [] as ParallelSearchResult[];\n })\n : Promise.resolve([] as ParallelSearchResult[]),\n runTemporal\n // Pass candidatePaths so scope-filtering happens before the top-K cap inside runTemporalAgent,\n // preventing out-of-scope newer entries from displacing in-scope ones at the slice boundary.\n ? runTemporalAgent(query, memoryDir, maxPerAgent, candidatePaths).catch((err) => {\n log.debug(`augmentWithDirectAndTemporal: TemporalAgent failed — ${err}`);\n return [] as ParallelSearchResult[];\n })\n : Promise.resolve([] as ParallelSearchResult[]),\n ]);\n const durationMs = Date.now() - startMs;\n\n // Direct agent results (entities/) intentionally bypass candidatePaths.\n //\n // Why: candidatePaths is built from temporal + tag indexes, which never include\n // entity files (entities/*.md). Applying the filter here would silently drop ALL\n // direct-agent results for any scoped query, making the direct agent a no-op.\n // Entity files are identified by filename matching (query tokens vs. entity name),\n // so they are already query-scoped independently of the time/tag prefilter.\n // Temporal results are scoped inside runTemporalAgent() before the top-K cap.\n const scopedDirect = directResults;\n\n // Tag contextual results with their source so they can participate in weighted merge.\n // We do NOT cap contextual here: contextualResults comes from fetchQmdMemoryResultsWithArtifactTopUp\n // which intentionally over-fetches so downstream phases (graph expansion, lifecycle filtering,\n // reranking) can drop candidates. Capping before the merge would remove that headroom.\n const contextualTagged: ParallelSearchResult[] = contextualResults.map((r) => ({\n ...r,\n agentSource: \"contextual\" as const,\n }));\n\n log.debug(\n `augmentWithDirectAndTemporal: direct=${scopedDirect.length} temporal=${temporalResults.length} contextual=${contextualTagged.length} agentMs=${durationMs}ms`,\n );\n\n // If no specialized agents contributed anything (e.g. fresh setup without entities/ or temporal\n // index), skip reweighting entirely — applying the contextual weight penalty with zero benefit\n // would silently reduce every result score by ~30% compared to the non-augmented path.\n if (scopedDirect.length === 0 && temporalResults.length === 0) {\n return (contextualResults as QmdSearchResult[]).slice(0, maxResults);\n }\n\n // Merge all three; contextual first so its snippets are preserved when a higher-scoring\n // direct/temporal result overrides the same path (see mergeAgentResults snippet logic).\n // Note: maxPerAgent=0 is handled by the early-return guard above; we never reach here with 0.\n const merged = mergeAgentResults(\n [...contextualTagged, ...scopedDirect, ...temporalResults],\n weights,\n maxResults,\n resolveNamespace,\n );\n\n // Populate snippets for results discovered only by direct/temporal (no contextual preview)\n return populateEmptySnippets(merged, memoryDir);\n}\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n\nexport interface ParallelRetrievalOptions {\n collection?: string;\n maxResults?: number;\n signal?: AbortSignal;\n /** If true, skip the contextual agent (hybridSearch is already running externally). */\n skipContextual?: boolean;\n /** Max results per agent (capped before merge). */\n maxResultsPerAgent?: number;\n /** Override default agent weights. Falls back to PARALLEL_AGENT_WEIGHTS when absent. */\n agentWeights?: Record<SearchAgentSource, number>;\n /** Optional namespace resolver so direct/temporal results collapse onto the same\n * (namespace, path) merge key as the contextual result for the same path. */\n resolveNamespace?: (path: string) => string;\n}\n\n/**\n * Run parallel specialized retrieval agents and merge results.\n *\n * When `skipContextual` is true, only the direct and temporal agents run.\n * This is the intended usage inside the orchestrator, where hybridSearch\n * already runs as the primary search — we augment with the cheap extra agents.\n *\n * Total latency = max(agent_latencies), not sum.\n * Agent errors are isolated — others still return results.\n */\nexport async function parallelRetrieval(\n query: string,\n qmd: QmdClient,\n memoryDir: string,\n options: ParallelRetrievalOptions = {},\n): Promise<QmdSearchResult[]> {\n const maxResults = options.maxResults ?? 20;\n const maxPerAgent = options.maxResultsPerAgent ?? maxResults;\n const startMs = Date.now();\n\n // Rough entity detection: count proper-noun-like tokens in query\n const knownEntityCount = (query.match(/\\b[A-Z][a-z]{1,}/g) ?? []).length;\n\n const runDirect = shouldRunAgent(\"direct\", query, knownEntityCount);\n const runTemporal = shouldRunAgent(\"temporal\", query, knownEntityCount);\n const runContextual = !options.skipContextual && shouldRunAgent(\"contextual\", query, knownEntityCount);\n\n const [directResults, temporalResults, contextualResults] = await Promise.all([\n runDirect\n ? runDirectAgent(query, memoryDir, maxPerAgent).catch((err) => {\n log.debug(`parallelRetrieval: DirectAgent failed — ${err}`);\n return [] as ParallelSearchResult[];\n })\n : Promise.resolve([] as ParallelSearchResult[]),\n runTemporal\n ? runTemporalAgent(query, memoryDir, maxPerAgent).catch((err) => {\n log.debug(`parallelRetrieval: TemporalAgent failed — ${err}`);\n return [] as ParallelSearchResult[];\n })\n : Promise.resolve([] as ParallelSearchResult[]),\n runContextual\n ? runContextualAgent(query, qmd, options.collection, maxPerAgent, options.signal).catch((err) => {\n if (isAbortError(err)) throw err;\n log.debug(`parallelRetrieval: ContextualAgent failed — ${err}`);\n return [] as ParallelSearchResult[];\n })\n : Promise.resolve([] as ParallelSearchResult[]),\n ]);\n\n const durationMs = Date.now() - startMs;\n log.debug(\n `parallelRetrieval: direct=${directResults.length} temporal=${temporalResults.length} contextual=${contextualResults.length} durationMs=${durationMs}ms`,\n );\n\n // Contextual results go first so their snippets are preserved when a higher-scoring\n // direct/temporal result overrides the same path in mergeAgentResults.\n const merged = mergeAgentResults(\n [...contextualResults, ...directResults, ...temporalResults],\n options.agentWeights ?? PARALLEL_AGENT_WEIGHTS,\n maxResults,\n options.resolveNamespace,\n );\n return populateEmptySnippets(merged, memoryDir);\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  assertPathInsideRoot
3
- } from "./chunk-VSMRDBFT.js";
3
+ } from "./chunk-GCASGYIO.js";
4
4
  import {
5
5
  RECALL_FALLBACK_DIRS
6
6
  } from "./chunk-VS2IYZRU.js";
@@ -92,4 +92,4 @@ async function scanMemoryDir(memoryDir) {
92
92
  export {
93
93
  scanMemoryDir
94
94
  };
95
- //# sourceMappingURL=chunk-6SXVCD7W.js.map
95
+ //# sourceMappingURL=chunk-W7IGLX4G.js.map
@@ -2,9 +2,12 @@ import {
2
2
  readPair,
3
3
  resolvePair
4
4
  } from "./chunk-35NN3ZFA.js";
5
+ import {
6
+ composeMemoryEnvelope
7
+ } from "./chunk-CGASUJVP.js";
5
8
  import {
6
9
  log
7
- } from "./chunk-37CTGERM.js";
10
+ } from "./chunk-3WCASNQZ.js";
8
11
 
9
12
  // src/contradiction/resolution.ts
10
13
  var VALID_VERBS = ["keep-a", "keep-b", "merge", "both-valid", "needs-more-context"];
@@ -235,11 +238,21 @@ async function prepareMergeReplacement(storage, pairId, idA, idB, options) {
235
238
  const category = options.mergedCategory ?? mergedMemoryCategory(sourceA, sourceB);
236
239
  let mergedId;
237
240
  try {
238
- const mergeResult = await storage.writeMemory(category, mergedContent, {
241
+ const mergeEnvelope = composeMemoryEnvelope(
242
+ {
243
+ content: mergedContent,
244
+ category,
245
+ confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),
246
+ tags: ["contradiction-resolution", "merge"]
247
+ },
248
+ { source: "contradiction-resolution" },
249
+ { salvage: true }
250
+ );
251
+ if (mergeEnvelope.salvageNotes.length > 0) {
252
+ log.warn(`contradiction-resolution write salvaged invalid fields: ${mergeEnvelope.salvageNotes.join("; ")}`);
253
+ }
254
+ const mergeResult = await storage.writeSealedMemory(mergeEnvelope, {
239
255
  actor: "contradiction-resolution",
240
- confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),
241
- tags: ["contradiction-resolution", "merge"],
242
- source: "contradiction-resolution",
243
256
  lineage: [idA, idB],
244
257
  derivedVia: "merge"
245
258
  });
@@ -386,4 +399,4 @@ export {
386
399
  isValidResolutionVerb,
387
400
  executeResolution
388
401
  };
389
- //# sourceMappingURL=chunk-KQDNAXOM.js.map
402
+ //# sourceMappingURL=chunk-WG5HSQ4P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/contradiction/resolution.ts"],"sourcesContent":["/**\n * Resolution Verbs — executes user-chosen resolution actions on contradiction pairs (issue #520).\n *\n * All resolution paths delegate to StorageManager.supersedeMemory. Do not\n * reimplement supersession logic here (rule 22: deduplicate resolution).\n */\n\nimport { log } from \"../logger.js\";\nimport { composeMemoryEnvelope } from \"../write-envelope.js\";\nimport type { StorageManager } from \"../storage.js\";\nimport type { MemoryCategory, MemoryFile } from \"../types.js\";\nimport type { ResolutionVerb } from \"./contradiction-review.js\";\nimport { readPair, resolvePair } from \"./contradiction-review.js\";\n\nexport interface ResolutionResult {\n pairId: string;\n verb: ResolutionVerb;\n /** Memory IDs affected by the resolution. */\n affectedIds: string[];\n /** Human-readable status. */\n message: string;\n}\n\nexport interface ExecuteResolutionOptions {\n /** Existing merged memory to supersede both source memories to. */\n mergedMemoryId?: string;\n /** Content for a new merged memory. Required for merge when mergedMemoryId is omitted. */\n mergedContent?: string;\n /** Category for a newly created merged memory. Defaults to the shared source category, or fact. */\n mergedCategory?: MemoryCategory;\n /** Resolve storage for the pair namespace, or the default namespace for legacy unscoped pairs. */\n storageForNamespace?: (namespace: string | undefined) => StorageManager | Promise<StorageManager>;\n /**\n * Best-effort hook invoked after a contradiction resolution leaves a durable\n * mutation in the namespace's memory files (issue #1499 sweep, NH1dX / NH3X3).\n * Every mutating verb — `merge` (creates a new memory and supersedes both\n * sources), `keep-a`, and `keep-b` (supersede the losing source + rewrite\n * frontmatter) — writes directly to the pair's (possibly DYNAMIC) namespace\n * storage, bypassing the extraction write path that records catalog writes. So\n * without this the namespace's `lastWriteAt` stays stale and QMD maintenance /\n * `writtenSince` can skip a namespace whose only post-write mutation is\n * resolving a contradiction. It fires after the resolution commits, or after a\n * failed resolution/rollback path when durable memory changes are still left on\n * disk. If a failure rolls back cleanly, this is never called, so the catalog\n * never records a write that did not survive (rule #25). Non-mutating verbs\n * (`both-valid`, `needs-more-context`) never trigger it. Callers wire this to\n * `Orchestrator.recordCatalogWrite(namespace, storageDir)`. Must be\n * failure-tolerant: it is fire-and-forget and must never affect resolution.\n */\n onMergedMemoryWritten?: (namespace: string | undefined, storageDir: string) => void;\n}\n\nconst VALID_VERBS: ResolutionVerb[] = [\"keep-a\", \"keep-b\", \"merge\", \"both-valid\", \"needs-more-context\"];\n\nexport function isValidResolutionVerb(value: string): value is ResolutionVerb {\n return VALID_VERBS.includes(value as ResolutionVerb);\n}\n\n/**\n * Execute a resolution verb on a contradiction pair.\n *\n * - `keep-a`: Supersede B, keep A active.\n * - `keep-b`: Supersede A, keep B active.\n * - `merge`: Create or verify a real merged memory, then supersede both inputs.\n * - `both-valid`: Mark pair as reviewed; no memories are superseded.\n * - `needs-more-context`: Defer; no action, short cooldown.\n */\nexport async function executeResolution(\n memoryDir: string,\n storage: StorageManager,\n pairId: string,\n verb: ResolutionVerb,\n options: ExecuteResolutionOptions = {},\n): Promise<ResolutionResult> {\n if (typeof verb !== \"string\" || !isValidResolutionVerb(verb)) {\n throw new Error(`Invalid contradiction resolution verb: ${String(verb)}`);\n }\n\n const pair = readPair(memoryDir, pairId);\n if (!pair) {\n return { pairId, verb, affectedIds: [], message: `Pair ${pairId} not found` };\n }\n\n if (pair.namespace && !options.storageForNamespace) {\n throw new Error(\n \"contradiction resolution requires storageForNamespace for namespaced pairs so callers resolve the correct namespace storage\",\n );\n }\n\n const resolutionStorage = options.storageForNamespace\n ? await options.storageForNamespace(pair.namespace)\n : storage;\n\n if (pair.resolution && pair.resolution !== \"needs-more-context\") {\n return { pairId, verb, affectedIds: [], message: `Pair already resolved with verb \"${pair.resolution}\"` };\n }\n\n const [idA, idB] = pair.memoryIds;\n const affectedIds: string[] = [];\n let message = \"\";\n let supersedeFailed = false;\n let rollbackAfterResolveFailure: (() => Promise<boolean>) | null = null;\n // Deferred catalog-write touch for any resolution that leaves durable namespace\n // memory mutations (issue #1499 sweep, NH1dX / NH3X3). Rule #25: never record a\n // catalog touch for a write that is fully rolled back. Successful mutating\n // resolutions invoke it after `resolvePair` persists. Failed paths invoke it\n // only when rollback inspection shows the namespace still differs from the\n // pre-mutation snapshot.\n let recordCatalogWriteTouch: (() => void) | null = null;\n // Returns the deferred touch fn for a mutating verb (or null when the caller\n // wired no catalog hook), so each branch assigns `recordCatalogWriteTouch`\n // directly in the function body — keeping TS control-flow narrowing intact at\n // the post-commit invocation below.\n const buildCatalogTouch = (): (() => void) | null => {\n if (!options.onMergedMemoryWritten) return null;\n const onMergedMemoryWritten = options.onMergedMemoryWritten;\n const namespace = pair.namespace;\n const storageDir = resolutionStorage.dir;\n return () => onMergedMemoryWritten(namespace, storageDir);\n };\n const catalogWriteTouch = buildCatalogTouch();\n const recordCatalogWriteTouchSafely = (context: string, touch = catalogWriteTouch): void => {\n if (!touch) return;\n try {\n touch();\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] catalog write touch failed for pair=%s context=%s: %s\",\n pairId,\n context,\n err instanceof Error ? err.message : err,\n );\n }\n };\n const touchCatalogIfRollbackLeftChange = async (\n context: string,\n snapshots: MemoryFile[],\n replacement?: Extract<MergeReplacement, { ok: true }>,\n ): Promise<void> => {\n if (!catalogWriteTouch) return;\n if (await rollbackLeftDurableMutation(resolutionStorage, snapshots, replacement)) {\n recordCatalogWriteTouchSafely(context);\n }\n };\n\n switch (verb) {\n case \"keep-a\": {\n const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idA);\n if (!keepTarget.ok) {\n supersedeFailed = true;\n message = keepTarget.message;\n break;\n }\n const sourceB = await loadSourceSnapshot(resolutionStorage, idB);\n const ok = sourceB\n ? await supersedeSafe(resolutionStorage, idB, idA, \"contradiction-resolution:keep-a\")\n : false;\n if (ok) {\n affectedIds.push(idB);\n rollbackAfterResolveFailure = async () =>\n restoreMemorySnapshot(resolutionStorage, sourceB!, \"contradiction-resolution:keep-a-rollback\");\n // keep-a superseded idB in this namespace — record a catalog touch once\n // the resolution durably commits (NH3X3).\n recordCatalogWriteTouch = catalogWriteTouch;\n message = `Kept ${idA}, superseded ${idB}`;\n }\n else {\n supersedeFailed = true;\n const rolledBack = sourceB\n ? await restoreMemorySnapshot(resolutionStorage, sourceB, \"contradiction-resolution:keep-a-rollback\")\n : false;\n if (sourceB && !rolledBack) {\n await touchCatalogIfRollbackLeftChange(\"keep-a-rollback-incomplete\", [sourceB]);\n }\n message = rolledBack\n ? `Supersede failed for ${idB}; restored ${idB} and did not resolve`\n : `Supersede failed for ${idB}; rollback incomplete for ${idB} and pair is not resolved`;\n }\n break;\n }\n case \"keep-b\": {\n const keepTarget = await validateKeepTarget(resolutionStorage, pairId, idB);\n if (!keepTarget.ok) {\n supersedeFailed = true;\n message = keepTarget.message;\n break;\n }\n const sourceA = await loadSourceSnapshot(resolutionStorage, idA);\n const ok = sourceA\n ? await supersedeSafe(resolutionStorage, idA, idB, \"contradiction-resolution:keep-b\")\n : false;\n if (ok) {\n affectedIds.push(idA);\n rollbackAfterResolveFailure = async () =>\n restoreMemorySnapshot(resolutionStorage, sourceA!, \"contradiction-resolution:keep-b-rollback\");\n // keep-b superseded idA in this namespace — record a catalog touch once\n // the resolution durably commits (NH3X3).\n recordCatalogWriteTouch = catalogWriteTouch;\n message = `Kept ${idB}, superseded ${idA}`;\n }\n else {\n supersedeFailed = true;\n const rolledBack = sourceA\n ? await restoreMemorySnapshot(resolutionStorage, sourceA, \"contradiction-resolution:keep-b-rollback\")\n : false;\n if (sourceA && !rolledBack) {\n await touchCatalogIfRollbackLeftChange(\"keep-b-rollback-incomplete\", [sourceA]);\n }\n message = rolledBack\n ? `Supersede failed for ${idA}; restored ${idA} and did not resolve`\n : `Supersede failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;\n }\n break;\n }\n case \"merge\": {\n const replacement = await prepareMergeReplacement(resolutionStorage, pairId, idA, idB, options);\n if (!replacement.ok) {\n supersedeFailed = true;\n message = replacement.message;\n break;\n }\n\n const okA = await supersedeSafe(resolutionStorage, idA, replacement.mergedId, \"contradiction-resolution:merge\");\n if (!okA) {\n supersedeFailed = true;\n const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);\n message = rolledBackA\n ? `Merge failed for ${idA}; restored ${idA} and did not resolve`\n : `Merge failed for ${idA}; rollback incomplete for ${idA} and pair is not resolved`;\n if (rolledBackA) {\n await cleanupCreatedReplacement(resolutionStorage, replacement);\n }\n else {\n await touchCatalogIfRollbackLeftChange(\"merge-first-rollback-incomplete\", [replacement.sourceA], replacement);\n }\n break;\n }\n\n const okB = await supersedeSafe(resolutionStorage, idB, replacement.mergedId, \"contradiction-resolution:merge\");\n if (!okB) {\n supersedeFailed = true;\n const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);\n const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);\n message = rolledBackA && rolledBackB\n ? `Merge failed for ${idB}; restored ${idA} and ${idB} and did not resolve`\n : `Merge failed for ${idB}; rollback incomplete for ${[\n rolledBackA ? undefined : idA,\n rolledBackB ? undefined : idB,\n ].filter(Boolean).join(\", \")} and pair is not resolved`;\n if (rolledBackA && rolledBackB) {\n await cleanupCreatedReplacement(resolutionStorage, replacement);\n }\n else {\n await touchCatalogIfRollbackLeftChange(\n \"merge-second-rollback-incomplete\",\n [replacement.sourceA, replacement.sourceB],\n replacement,\n );\n }\n break;\n }\n\n affectedIds.push(idA, idB);\n rollbackAfterResolveFailure = async () => {\n const rolledBackA = await restoreMemorySnapshot(resolutionStorage, replacement.sourceA);\n const rolledBackB = await restoreMemorySnapshot(resolutionStorage, replacement.sourceB);\n if (rolledBackA && rolledBackB) {\n await cleanupCreatedReplacement(resolutionStorage, replacement);\n }\n return rolledBackA && rolledBackB;\n };\n // Catalog write touch (issue #1499 sweep): the merge supersedes BOTH sources\n // (and, when created, writes a fresh merged memory) in the pair's (possibly\n // dynamic) namespace storage — but the resolution is not durable yet\n // (resolvePair persists below, and a failure rolls the merge back). Defer\n // the touch so it fires ONLY after the resolution commits past the rollback\n // point (NH1dX, rule #25). Arm it for EVERY successful merge — even reusing\n // an existing merged-id still supersedes both sources, a namespace mutation\n // that must refresh `lastWriteAt` (NH3X3). Otherwise a dynamic namespace\n // whose only durable mutation is a contradiction merge stays invisible to\n // QMD maintenance / `writtenSince`. Best-effort on the caller side.\n recordCatalogWriteTouch = catalogWriteTouch;\n message = `Both memories superseded by merged ${replacement.mergedId}`;\n break;\n }\n case \"both-valid\": {\n message = \"Pair marked as both-valid; cooldown applied\";\n break;\n }\n case \"needs-more-context\": {\n message = \"Deferred; no action taken, short cooldown applied\";\n break;\n }\n }\n\n if (!supersedeFailed) {\n let resolved = false;\n try {\n resolved = resolvePair(memoryDir, pairId, verb) !== null;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] failed to persist pair=%s verb=%s: %s\",\n pairId,\n verb,\n err instanceof Error ? err.message : err,\n );\n }\n if (!resolved) {\n if (rollbackAfterResolveFailure) {\n const rolledBack = await rollbackAfterResolveFailure();\n affectedIds.length = 0;\n message = rolledBack\n ? `Resolution persistence failed; rolled back memory changes and did not resolve ${pairId}`\n : \"Resolution persistence failed; rollback incomplete and pair is not resolved\";\n if (!rolledBack && recordCatalogWriteTouch) {\n recordCatalogWriteTouchSafely(\"resolve-persistence-rollback-incomplete\", recordCatalogWriteTouch);\n }\n } else {\n message = \"Resolution persistence failed; pair is not resolved\";\n }\n } else if (recordCatalogWriteTouch) {\n // The resolution durably committed (memory mutated AND the resolution\n // persisted past the rollback point). Only now is it safe to record the\n // catalog write for the namespace mutation (NH1dX / NH3X3, rule #25).\n // Best-effort: the caller's callback swallows errors; guard here so a\n // throwing callback never derails a successful resolution.\n recordCatalogWriteTouchSafely(\"resolved\", recordCatalogWriteTouch);\n }\n }\n log.info(\"[contradiction-resolution] pair=%s verb=%s affected=%d\", pairId, verb, affectedIds.length);\n return { pairId, verb, affectedIds, message };\n}\n\ntype MergeReplacement =\n | {\n ok: true;\n mergedId: string;\n sourceA: MemoryFile;\n sourceB: MemoryFile;\n created: boolean;\n }\n | {\n ok: false;\n message: string;\n };\n\nasync function prepareMergeReplacement(\n storage: StorageManager,\n pairId: string,\n idA: string,\n idB: string,\n options: ExecuteResolutionOptions,\n): Promise<MergeReplacement> {\n const sourceA = await storage.getMemoryById(idA);\n const sourceB = await storage.getMemoryById(idB);\n if (!sourceA || !sourceB) {\n return { ok: false, message: `Merge requires both source memories to exist; not resolving ${pairId}` };\n }\n\n const requestedMergedId = options.mergedMemoryId?.trim();\n if (requestedMergedId) {\n if (requestedMergedId === idA || requestedMergedId === idB) {\n return { ok: false, message: \"Merge replacement must be distinct from both source memories; not resolving\" };\n }\n const replacement = await storage.getMemoryById(requestedMergedId);\n if (!replacement) {\n return { ok: false, message: `Merged memory ${requestedMergedId} not found; not resolving` };\n }\n const replacementStatus = replacement.frontmatter.status ?? \"active\";\n if (replacementStatus !== \"active\") {\n return {\n ok: false,\n message: `Merged memory ${requestedMergedId} is ${replacementStatus}; not resolving`,\n };\n }\n return { ok: true, mergedId: requestedMergedId, sourceA, sourceB, created: false };\n }\n\n const mergedContent = options.mergedContent;\n if (typeof mergedContent !== \"string\" || mergedContent.trim().length === 0) {\n return {\n ok: false,\n message: \"Merge requires mergedMemoryId or mergedContent; no memories changed\",\n };\n }\n\n const category = options.mergedCategory ?? mergedMemoryCategory(sourceA, sourceB);\n let mergedId: string;\n try {\n // Sealed-envelope write (issue #1989 PR4): merged content is\n // LLM-produced — salvage; drops are warn-logged.\n const mergeEnvelope = composeMemoryEnvelope(\n {\n content: mergedContent,\n category,\n confidence: Math.min(sourceA.frontmatter.confidence ?? 0.8, sourceB.frontmatter.confidence ?? 0.8),\n tags: [\"contradiction-resolution\", \"merge\"],\n },\n { source: \"contradiction-resolution\" },\n { salvage: true },\n );\n if (mergeEnvelope.salvageNotes.length > 0) {\n log.warn(`contradiction-resolution write salvaged invalid fields: ${mergeEnvelope.salvageNotes.join(\"; \")}`);\n }\n const mergeResult = await storage.writeSealedMemory(mergeEnvelope, {\n actor: \"contradiction-resolution\",\n lineage: [idA, idB],\n derivedVia: \"merge\",\n });\n mergedId = mergeResult.id;\n if (mergeResult.tombstoneBlocked) {\n // #1645: merged content matched a tombstone (pending_review). Don't\n // supersede both sources to a non-active replacement — that retires\n // the only active copies. Clean up the pending merge and abort the pair.\n await cleanupMemoryId(storage, mergedId);\n return { ok: false, message: `Merged memory for ${pairId} was tombstone-blocked (pending_review); not resolving — sources kept active` };\n }\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] merged memory creation failed for %s: %s\",\n pairId,\n err instanceof Error ? err.message : err,\n );\n return { ok: false, message: `Merged memory could not be created; not resolving ${pairId}` };\n }\n const replacement = await storage.getMemoryById(mergedId);\n if (!replacement) {\n await cleanupMemoryId(storage, mergedId);\n return { ok: false, message: `Merged memory ${mergedId} could not be verified; not resolving` };\n }\n return { ok: true, mergedId, sourceA, sourceB, created: true };\n}\n\nfunction mergedMemoryCategory(sourceA: MemoryFile, sourceB: MemoryFile): MemoryCategory {\n return sourceA.frontmatter.category === sourceB.frontmatter.category\n ? sourceA.frontmatter.category\n : \"fact\";\n}\n\ntype KeepTargetValidation =\n | { ok: true }\n | { ok: false; message: string };\n\nasync function validateKeepTarget(\n storage: StorageManager,\n pairId: string,\n keepId: string,\n): Promise<KeepTargetValidation> {\n const target = await loadSourceSnapshot(storage, keepId);\n if (!target) {\n return { ok: false, message: `Kept memory ${keepId} not found; not resolving ${pairId}` };\n }\n\n const status = target.frontmatter.status ?? \"active\";\n if (status !== \"active\") {\n return { ok: false, message: `Kept memory ${keepId} is ${status}; not resolving ${pairId}` };\n }\n\n return { ok: true };\n}\n\nasync function loadSourceSnapshot(storage: StorageManager, memoryId: string): Promise<MemoryFile | null> {\n try {\n return await storage.getMemoryById(memoryId);\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] source snapshot failed for %s: %s\",\n memoryId,\n err instanceof Error ? err.message : err,\n );\n return null;\n }\n}\n\nasync function restoreMemorySnapshot(\n storage: StorageManager,\n memory: MemoryFile,\n reasonCode = \"contradiction-resolution:merge-rollback\",\n): Promise<boolean> {\n try {\n const current = await storage.getMemoryById(memory.frontmatter.id);\n if (!current) return false;\n const restoredFrontmatter: Partial<MemoryFile[\"frontmatter\"]> = {\n ...memory.frontmatter,\n status: memory.frontmatter.status,\n supersededBy: memory.frontmatter.supersededBy,\n supersededAt: memory.frontmatter.supersededAt,\n };\n return await storage.writeMemoryFrontmatter(current, restoredFrontmatter, {\n actor: \"contradiction-resolution\",\n reasonCode,\n });\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] rollback failed for %s: %s\",\n memory.frontmatter.id,\n err instanceof Error ? err.message : err,\n );\n return false;\n }\n}\n\nasync function rollbackLeftDurableMutation(\n storage: StorageManager,\n snapshots: MemoryFile[],\n replacement?: Extract<MergeReplacement, { ok: true }>,\n): Promise<boolean> {\n for (const snapshot of snapshots) {\n try {\n const current = await storage.getMemoryById(snapshot.frontmatter.id);\n if (!current) return true;\n if (supersessionStateChanged(current, snapshot)) return true;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] rollback inspection failed for %s: %s\",\n snapshot.frontmatter.id,\n err instanceof Error ? err.message : err,\n );\n return true;\n }\n }\n\n if (replacement?.created) {\n try {\n return (await storage.getMemoryById(replacement.mergedId)) !== null;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] rollback replacement inspection failed for %s: %s\",\n replacement.mergedId,\n err instanceof Error ? err.message : err,\n );\n return true;\n }\n }\n\n return false;\n}\n\nfunction supersessionStateChanged(current: MemoryFile, snapshot: MemoryFile): boolean {\n return (\n current.frontmatter.status !== snapshot.frontmatter.status ||\n current.frontmatter.supersededBy !== snapshot.frontmatter.supersededBy ||\n current.frontmatter.supersededAt !== snapshot.frontmatter.supersededAt\n );\n}\n\nasync function cleanupCreatedReplacement(storage: StorageManager, replacement: Extract<MergeReplacement, { ok: true }>): Promise<void> {\n if (!replacement.created) return;\n await cleanupMemoryId(storage, replacement.mergedId);\n}\n\nasync function cleanupMemoryId(storage: StorageManager, memoryId: string): Promise<void> {\n try {\n const memory = await storage.getMemoryById(memoryId);\n const invalidated = await storage.invalidateMemory(memoryId);\n if (invalidated && memory?.frontmatter.category === \"fact\") {\n await storage.removeFactContentHashesForMemories([memory]);\n }\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] cleanup failed for merged memory %s: %s\",\n memoryId,\n err instanceof Error ? err.message : err,\n );\n }\n}\n\nasync function supersedeSafe(\n storage: StorageManager,\n oldId: string,\n newId: string,\n reason: string,\n): Promise<boolean> {\n try {\n const result = await storage.supersedeMemory(oldId, newId, reason);\n if (result === false) {\n log.warn(\"[contradiction-resolution] supersede returned false for %s → %s\", oldId, newId);\n return false;\n }\n return true;\n } catch (err) {\n log.warn(\n \"[contradiction-resolution] supersede failed %s → %s: %s\",\n oldId,\n newId,\n err instanceof Error ? err.message : err,\n );\n return false;\n }\n}\n"],"mappings":"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","names":["replacement"]}
@@ -101,7 +101,7 @@ async function withHeldFileLock(lockPath, opts, task) {
101
101
  const lockDir = path.dirname(lockPath);
102
102
  const held = await acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs);
103
103
  if (!held) {
104
- return task(false);
104
+ return task(false, { refresh: async () => false });
105
105
  }
106
106
  const heartbeat = setInterval(() => {
107
107
  lockHeldBySelf(held).then((ours) => {
@@ -112,8 +112,11 @@ async function withHeldFileLock(lockPath, opts, task) {
112
112
  });
113
113
  }, heartbeatMs);
114
114
  heartbeat.unref?.();
115
+ const controller = {
116
+ refresh: () => refreshHeldLock(held, warn, opts.onBeforeRefreshUtimesForTest)
117
+ };
115
118
  try {
116
- return await task(true);
119
+ return await task(true, controller);
117
120
  } finally {
118
121
  clearInterval(heartbeat);
119
122
  await releaseLock(held, warn, opts.onAfterReleaseRenameForTest);
@@ -272,6 +275,17 @@ async function lockHeldBySelf(held) {
272
275
  return false;
273
276
  }
274
277
  }
278
+ async function refreshHeldLock(held, warn, onBeforeRefreshUtimesForTest) {
279
+ if (!await lockHeldBySelf(held)) return false;
280
+ if (onBeforeRefreshUtimesForTest) await onBeforeRefreshUtimesForTest();
281
+ try {
282
+ await utimes(held.path, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date());
283
+ } catch (err) {
284
+ warn("withHeldFileLock manual refresh failed", err);
285
+ return false;
286
+ }
287
+ return lockHeldBySelf(held);
288
+ }
275
289
  function sleep(ms) {
276
290
  return new Promise((resolve) => {
277
291
  setTimeout(resolve, ms);
@@ -283,4 +297,4 @@ export {
283
297
  serializeMutations,
284
298
  withHeldFileLock
285
299
  };
286
- //# sourceMappingURL=chunk-LTJAMRGI.js.map
300
+ //# sourceMappingURL=chunk-WL5EBRDH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/serialize-mutations.ts"],"sourcesContent":["// ---------------------------------------------------------------------------\n// Shared serialized-mutation utilities for TOCTOU hotspots (issue #1524).\n//\n// Two complementary primitives that the namespace catalog (`queueCritical` +\n// `withHeldCatalogLock`), the storage router's resolve-hook serialization, and\n// the summary-snapshot writer each re-implement today:\n//\n// 1. `serializeMutations(key, task)` — keyed IN-PROCESS async serialization\n// that recovers after a rejection (CLAUDE.md rule #40). One failed task\n// never poisons the tasks queued behind it; the failed task's error is\n// still surfaced to ITS caller.\n//\n// 2. `withHeldFileLock(lockPath, opts, task)` — a held CROSS-PROCESS file\n// lock with replacement-safe stale breaking (the NG7Bg invariant from\n// #1506 round 28) and ownership-checked release.\n//\n// This is the UTILITY module only. Per-issue PR split: one PR for the utility\n// + tests (this file), then one PR per adoption hotspot (catalog, router\n// provenance, summary snapshot). No adoptions live here.\n// ---------------------------------------------------------------------------\n\nimport { randomUUID } from \"node:crypto\";\nimport { link, mkdir, open, readFile, rename, stat, unlink, utimes } from \"node:fs/promises\";\nimport path from \"node:path\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// 1. serializeMutations — keyed async serialization with rejection recovery\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * One entry in the per-key serialization map. `tail` is the recovered promise\n * the next queued task chains off of; it never rejects (both settle handlers\n * swallow), so a prior task's failure can never break subsequent ones.\n */\ninterface MutationChainEntry {\n tail: Promise<void>;\n}\n\n/**\n * Instance-scoped keyed serializer. Holds the per-key chain map so that all\n * tasks queued under the same key on the SAME serializer run strictly in order.\n *\n * The map is instance-scoped (not module-level) so tests can construct a fresh\n * serializer per case and avoid cross-test contamination, and so adopters that\n * want isolation (e.g. one serializer per storage root) can have it. The free\n * {@link serializeMutations} export delegates to a single shared default\n * instance for callers that want process-wide serialization.\n */\nexport class MutationSerializer {\n private readonly chains = new Map<string, MutationChainEntry>();\n\n /**\n * Run `task` strictly after every other task already queued under `key` on\n * this serializer has settled.\n *\n * Rejection recovery (rule #40, mirroring the catalog's `queueCritical`):\n * if a prior task rejects, later tasks STILL RUN, while the rejecting task's\n * error is surfaced to ITS OWN caller. Concretely, the recovered tail is\n * `run.then(noop, noop)` — never a bare `.then(fn)`, which would let one\n * failure kill every queued task behind it.\n *\n * No unbounded growth: when a chain's last task settles and no newer task\n * chained onto it, its entry is deleted (the storage router's\n * `inFlightResolved` marker-then-clear discipline).\n */\n serialize<T>(key: string, task: () => Promise<T>): Promise<T> {\n if (typeof key !== \"string\" || key.length === 0) {\n throw new TypeError(\"MutationSerializer.serialize: key must be a non-empty string\");\n }\n if (typeof task !== \"function\") {\n throw new TypeError(\"MutationSerializer.serialize: task must be a function returning a promise\");\n }\n\n let entry = this.chains.get(key);\n if (!entry) {\n entry = { tail: Promise.resolve() };\n this.chains.set(key, entry);\n }\n\n // Chain this task off the prior tail. `tail.then(task)` runs task only once\n // the previous task has settled, preserving read-modify-write ordering.\n const run = entry.tail.then(task);\n\n // Recover the tail after a rejection so a failed task never poisons later\n // ones. Both handlers swallow; `run` still carries the original resolution\n // (or rejection) to THIS caller. This is the line a naive `.then(fn)`\n // implementation omits — see the \"naive poison chain\" prove-fail test.\n const recovered = run.then(settleNoop, settleNoop);\n entry.tail = recovered;\n\n // Self-cleaning: once our recovered tail settles, if no newer task chained\n // onto us the entry still points at `recovered` and is safe to delete. A\n // concurrent `serialize()` call enqueues synchronously and would have\n // replaced `entry.tail` BEFORE this microtask runs, so the identity check\n // is race-free (no newer task's entry can be wrongly removed).\n //\n // `recovered` cannot reject in correct operation (both handlers above\n // swallow) and the cleanup body cannot throw — but we attach a rejection\n // handler anyway so that IF the recovery invariant is ever broken, the\n // failure surfaces as a behavioral assertion (skipped tasks) rather than an\n // unhandled-rejection storm that masks which task failed. The handler is a\n // no-op: cleanup only runs on fulfillment.\n void recovered.then(\n () => {\n if (entry && entry.tail === recovered) {\n this.chains.delete(key);\n }\n },\n () => undefined,\n );\n\n return run;\n }\n\n /**\n * Test-only: the number of keys with a not-yet-cleaned chain. Used to assert\n * the no-unbounded-growth invariant. Not part of the public contract.\n */\n pendingKeysForTest(): number {\n return this.chains.size;\n }\n}\n\n/**\n * Recovery handler shared by both settle arms. Named (not inline\n * `() => undefined`) so the chain assignment stays self-documenting in stack\n * traces and the review-patterns poison-chain check can see the chain is\n * recovered, not bare `.then(fn)`.\n */\nfunction settleNoop(): void {\n /* swallow — the original resolution/rejection is carried by `run` */\n}\n\n/**\n * Process-wide default serializer backing the free {@link serializeMutations}\n * export. Lazy so it is only created when first used (tests that construct\n * their own `MutationSerializer` pay nothing).\n */\nlet defaultSerializer: MutationSerializer | undefined;\n\n/**\n * Free-function entry point (issue #1524 signature). Serializes `task` against\n * every other task queued under `key` across the whole process, via a shared\n * default {@link MutationSerializer}. For isolated/testable serialization,\n * construct a `MutationSerializer` directly.\n */\nexport function serializeMutations<T>(key: string, task: () => Promise<T>): Promise<T> {\n if (!defaultSerializer) defaultSerializer = new MutationSerializer();\n return defaultSerializer.serialize(key, task);\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// 2. withHeldFileLock — cross-process held file lock with stale breaking\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Options for {@link withHeldFileLock}. */\nexport interface HeldFileLockOptions {\n /**\n * A lock whose mtime is older than this (in ms) is treated as a crashed\n * holder and broken. Required — there is no safe default, since the right\n * value depends on how long the guarded critical section can legitimately\n * run.\n */\n readonly staleMs: number;\n /**\n * Bounded acquisition: give up trying to acquire a busy lock after this long\n * (ms) and invoke `task(false)` best-effort WITHOUT holding the lock, rather\n * than blocking forever or crashing the primary op. Default 5000ms (matches\n * the namespace catalog's `REBUILD_LOCK_MAX_WAIT_MS`).\n */\n readonly maxWaitMs?: number;\n /**\n * Poll interval (ms) while waiting for a busy lock to clear. Default 50ms.\n */\n readonly pollMs?: number;\n /**\n * While WE hold the lock, refresh its mtime on this cadence (ms) so a\n * legitimately long task is not mistaken for a crashed holder and broken out\n * from under. Default `floor(staleMs / 3)` (at least 100ms), mirroring the\n * catalog heartbeat ratio. Must be comfortably below `staleMs`.\n */\n readonly heartbeatMs?: number;\n /**\n * Test seam (NG7Bg, #1506 round 28): fires AFTER a lock is judged stale and\n * BEFORE the re-verify + unlink, simulating a replacement lock being created\n * in the race window. No-op in production.\n */\n readonly onBeforeBreakStaleUnlinkForTest?: () => Promise<void> | void;\n /**\n * Test seam (codex P2): fires AFTER the release rename moves the lock to a\n * trash path and BEFORE the ownership re-verify/restore — simulating a third\n * contender acquiring the (now-empty) lockPath in the race window. No-op in\n * production. Used to prove the pre-check prevents the rename entirely.\n */\n readonly onAfterReleaseRenameForTest?: () => Promise<void> | void;\n /**\n * Test seam (#2033): fires inside {@link HeldFileLockController.refresh} AFTER\n * the ownership pre-check passes and BEFORE the `utimes` mtime bump —\n * simulating a peer that deletes the lock (making the bump throw) or replaces\n * it (a different owner the post-bump re-check must catch). No-op in\n * production; proves refresh reports LOST rather than clobbering a peer.\n */\n readonly onBeforeRefreshUtimesForTest?: () => Promise<void> | void;\n /**\n * Best-effort hook for non-fatal lock warnings (heartbeat refresh failure,\n * release-time ownership check failure). Never throws into the caller. If\n * omitted, warnings are swallowed (the lock is advisory; release/heartbeat\n * failures must never crash the guarded op).\n */\n readonly onLockWarning?: (message: string, err: unknown) => void;\n}\n\n/**\n * Control surface handed to a held-lock task so a long, CPU-bound critical\n * section can re-assert ownership immediately before its destructive write.\n */\nexport interface HeldFileLockController {\n /**\n * Re-verify THIS acquirer still owns the lock and, when it does, refresh the\n * lock's mtime. Returns `true` only while we still hold it.\n *\n * The timer heartbeat cannot fire while a synchronous, CPU-bound section\n * (a large parse/merge/sort/serialize) blocks the event loop, so a peer can\n * judge the lock stale, break it, and start its own write within the stale\n * window. A caller about to perform a destructive rewrite MUST call this\n * first: `false` means the lock was stale-broken/replaced and the caller MUST\n * abort rather than clobber the peer that now holds it; `true` also re-stamps\n * the mtime so the bounded write that immediately follows cannot itself be\n * judged stale mid-write. On the best-effort unlocked path (`acquired` was\n * `false`) this always resolves `false` — there is no lock to hold.\n */\n refresh(): Promise<boolean>;\n}\n\n/** Default bounded acquisition wait, mirroring the catalog. */\nconst DEFAULT_MAX_WAIT_MS = 5_000;\n/** Default busy-lock poll interval, mirroring the catalog. */\nconst DEFAULT_POLL_MS = 50;\n/** Floor for the derived heartbeat cadence. */\nconst MIN_HEARTBEAT_MS = 100;\n/** Node's setTimeout/setInterval 32-bit signed-int ceiling (2^31 − 1 ms ≈ 24.8\n * days). Delays above this are silently clamped to 1ms by the Node timer, so\n * timer-backed options (pollMs, heartbeatMs) must be rejected at this boundary\n * (chatgpt-codex-connector P2). */\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/** Internal handle for a lock we successfully acquired. */\ninterface HeldLock {\n readonly path: string;\n readonly ownerId: string;\n}\n\n/**\n * Run `task` under an exclusive on-disk lock at `lockPath`.\n *\n * Cross-process mutex via `open(lockPath, \"wx\")` (atomic exclusive create).\n * While held, a heartbeat timer refreshes the lock's mtime so a legitimately\n * long task is not mistaken for a crashed holder and broken out from under. A\n * lock older than `opts.staleMs` is treated as stale and broken — but\n * REPLACEMENT-SAFE (NG7Bg): we capture the stale lock's identity (full content\n * line: `<pid> <owner-uuid> <iso>`) when judging it stale, then RE-READ and\n * RE-STAT immediately before `unlink`, deleting only if byte-identical AND\n * still stale. A replacement lock created in the window has a different owner\n * id / timestamp, so its content differs and is left untouched.\n *\n * `task` receives `acquired: boolean` — `true` when we hold the lock, `false`\n * when acquisition timed out (best-effort). The signature takes\n * `(acquired) => Promise<T>` rather than the issue's sketched `() => Promise<T>`\n * so this can be the SINGLE lock home (issue: \"do NOT leave two lock\n * implementations; pick one home\"): the catalog's touch path needs to DROP on\n * timeout, which requires knowing whether the lock was acquired. A caller that\n * ignores the flag is still assignable (`() => Promise<T>` ⊆\n * `(acquired: boolean) => Promise<T>` in TypeScript).\n *\n * Release is ownership-checked: we only `unlink` a lock whose content still\n * identifies THIS acquirer (same owner id), so a replacement created after we\n * stopped heartbeating is never destroyed — mirroring the catalog's\n * `rebuildLockHeldBySelf`.\n *\n * ADOPTION NOTE: lock only the brief final read-merge-write window, never a\n * long scan — a scan-length lock makes concurrent writers time out and\n * silently drop work (catalog round 5, codex/cursor P2).\n */\nexport async function withHeldFileLock<T>(\n lockPath: string,\n opts: HeldFileLockOptions,\n task: (acquired: boolean, lock: HeldFileLockController) => Promise<T>,\n): Promise<T> {\n if (typeof lockPath !== \"string\" || lockPath.length === 0) {\n throw new TypeError(\"withHeldFileLock: lockPath must be a non-empty string\");\n }\n if (typeof opts?.staleMs !== \"number\" || !Number.isFinite(opts.staleMs) || opts.staleMs <= 0) {\n throw new TypeError(\n `withHeldFileLock: opts.staleMs must be a positive finite number ` +\n `(valid range: > 0 ms, finite; got ${formatInvalidNumber(opts?.staleMs)}).`,\n );\n }\n\n // Validate optional timings: a NaN/Infinity here is a real hazard (e.g.\n // `Date.now() + NaN` === NaN, so `Date.now() >= deadline` is always false and\n // the bounded acquire loop would wait forever instead of falling back to\n // best-effort). Reject invalid input rather than silently defaulting it\n // (codex P2 review). Omitting an option still picks its default.\n const maxWaitMs = optionalPositiveMs(opts.maxWaitMs, \"maxWaitMs\", DEFAULT_MAX_WAIT_MS, MAX_TIMER_DELAY_MS);\n const pollMs = optionalPositiveMs(opts.pollMs, \"pollMs\", DEFAULT_POLL_MS, MAX_TIMER_DELAY_MS);\n const heartbeatMs = optionalPositiveMs(\n opts.heartbeatMs,\n \"heartbeatMs\",\n Math.max(MIN_HEARTBEAT_MS, Math.floor(opts.staleMs / 3)),\n MAX_TIMER_DELAY_MS,\n );\n if (heartbeatMs >= opts.staleMs) {\n throw new TypeError(\n `withHeldFileLock: heartbeatMs (${heartbeatMs}) must be below staleMs (${opts.staleMs}) ` +\n `(valid range: > 0 and < staleMs ms) so at least one heartbeat lands per stale window.`,\n );\n }\n if (heartbeatMs > MAX_TIMER_DELAY_MS) {\n throw new TypeError(\n `withHeldFileLock: derived heartbeatMs (${heartbeatMs} = floor(staleMs/3)) exceeds ` +\n `Node's setTimeout ceiling (${MAX_TIMER_DELAY_MS} ms). Use an explicit opts.heartbeatMs ` +\n `at or below ${MAX_TIMER_DELAY_MS} ms.`,\n );\n }\n // Wrap the consumer's warning hook so a throwing callback never turns a\n // non-fatal advisory lock warning into an unhandled rejection (heartbeat\n // catch handler) or overrides the task's result (release path). The option\n // is documented as never throwing into the caller; enforce that here\n // (codex P2 review).\n const rawWarn = opts.onLockWarning;\n const warn = (message: string, err: unknown): void => {\n if (!rawWarn) return;\n try {\n rawWarn(message, err);\n } catch {\n /* swallow — a throwing advisory hook must not crash the guarded op */\n }\n };\n\n // Per-call owner identity. Two withHeldFileLock calls in the SAME process\n // get different ids, so neither mistakes the other's lock for its own\n // (stronger than the catalog's per-instance id, which is what we want for a\n // stateless utility).\n const ownerId = randomUUID();\n const lockDir = path.dirname(lockPath);\n\n const held = await acquireLock(lockPath, lockDir, ownerId, opts, maxWaitMs, pollMs);\n if (!held) {\n // Best-effort: run the task WITHOUT the lock. The caller decides what to\n // do (the catalog touch path will drop its append); we never crash the\n // primary op on contention.\n return task(false, { refresh: async () => false });\n }\n\n // Heartbeat: while WE hold the lock, refresh its mtime so age-based stale\n // detection sees an active holder and does not break us out from under\n // (catalog round 5). Failures are swallowed (advisory lock); the timer is\n // always cleared in the finally.\n //\n // OWNERSHIP CHECK (codex P2): if our event loop was paused long enough that\n // another process judged us stale, broke our lock, and created a replacement,\n // we must NOT refresh the replacement's mtime — that would keep a (possibly\n // crashed) replacement looking fresh. Verify lockHeldBySelf before each\n // utimes; if ownership is lost, stop heartbeating (our lock is gone).\n const heartbeat = setInterval(() => {\n lockHeldBySelf(held)\n .then((ours) => {\n if (!ours) return; // broken/replaced — stop refreshing\n return utimes(held.path, new Date(), new Date());\n })\n .catch((err: unknown) => {\n warn(\"withHeldFileLock heartbeat refresh failed\", err);\n });\n }, heartbeatMs);\n // Don't keep the event loop alive solely for the heartbeat.\n heartbeat.unref?.();\n const controller: HeldFileLockController = {\n refresh: () => refreshHeldLock(held, warn, opts.onBeforeRefreshUtimesForTest),\n };\n try {\n return await task(true, controller);\n } finally {\n clearInterval(heartbeat);\n await releaseLock(held, warn, opts.onAfterReleaseRenameForTest);\n }\n}\n\n/**\n * Resolve an optional millisecond timing option, REJECTING invalid values\n * (NaN, Infinity, non-positive, or above `maxMs`) rather than silently defaulting\n * them. A NaN or Infinity maxWaitMs would make the bounded acquire loop wait\n * forever (`Date.now() + NaN` is NaN); a non-positive poll/heartbeat makes no\n * sense. Timer-backed options (pollMs, heartbeatMs) are bounded to Node's\n * setTimeout ceiling (`MAX_TIMER_DELAY_MS`): a value above 2^31−1 is silently\n * clamped to 1ms by the timer, turning a typo into tight polling (codex P2).\n * Omitting the option (`undefined`) picks `fallback`. Non-number types are also\n * rejected (defensive against config/env coercion).\n */\nfunction optionalPositiveMs(\n value: number | undefined,\n name: \"maxWaitMs\" | \"pollMs\" | \"heartbeatMs\",\n fallback: number,\n maxMs: number,\n): number {\n if (value === undefined) return fallback;\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new TypeError(\n `withHeldFileLock: opts.${name} must be a positive finite number ` +\n `(valid range: > 0 ms, finite; got ${formatInvalidNumber(value)}). ` +\n `Omit the option to use the default of ${fallback} ms.`,\n );\n }\n if (value > maxMs) {\n throw new TypeError(\n `withHeldFileLock: opts.${name} (${value} ms) exceeds the ${maxMs} ms ` +\n `ceiling (Node's setTimeout clamps larger delays to 1ms, turning a ` +\n `typo into tight polling). Omit the option to use the default of ${fallback} ms.`,\n );\n }\n return value;\n}\n\n/**\n * Human-readable label for a rejected numeric input. Makes the error message\n * immediately actionable for NaN/Infinity (which print as \"NaN\"/\"Infinity\" via\n * String() but are easier to triage with an explicit sign), and surfaces the\n * actual type for non-number values (defensive against config/env coercion).\n */\nfunction formatInvalidNumber(value: unknown): string {\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) return \"NaN\";\n if (value === Infinity) return \"+Infinity\";\n if (value === -Infinity) return \"-Infinity\";\n return String(value);\n }\n return `${typeof value} ${JSON.stringify(value)}`;\n}\n\n/**\n * Atomically create the lock file, looping until acquired/stale-broken/timeout.\n * Returns the held-lock handle on success, or `undefined` on bounded-timeout.\n * Unexpected FS errors proceed best-effort (return undefined) rather than\n * crashing the guarded op, matching the catalog.\n */\nasync function acquireLock(\n lockPath: string,\n lockDir: string,\n ownerId: string,\n opts: HeldFileLockOptions,\n maxWaitMs: number,\n pollMs: number,\n): Promise<HeldLock | undefined> {\n try {\n await mkdir(lockDir, { recursive: true });\n } catch {\n // Lock-directory setup failure (e.g. an intermediate path is a file, or\n // permissions deny mkdir) must NOT crash the guarded op — the advisory\n // lock contract is best-effort. Return undefined so task(false) runs\n // instead of rejecting (codex P2 review).\n return undefined;\n }\n const deadline = Date.now() + maxWaitMs;\n for (;;) {\n try {\n const handle = await open(lockPath, \"wx\");\n let wroteMeta = true;\n try {\n await handle.writeFile(`${process.pid} ${ownerId} ${new Date().toISOString()}\\n`, \"utf8\");\n } catch {\n // The metadata write failed; the lock file may be empty or partial.\n // Our ownership check on release would NOT find this ownerId, leaving\n // a malformed lock that lingers until stale and blocks other callers\n // out of the mutex (codex P2). Undo our exclusive create and report\n // acquisition failure so the caller runs best-effort instead.\n wroteMeta = false;\n } finally {\n try {\n await handle.close();\n } catch {\n // close() can report a deferred I/O error (e.g. write that appeared\n // to succeed but failed on flush). The lock file may be malformed —\n // treat it as a metadata-write failure so the cleanup path unlinks\n // the orphaned lock (codex P2 review).\n wroteMeta = false;\n }\n }\n if (!wroteMeta) {\n await unlink(lockPath).catch(() => undefined);\n return undefined;\n }\n return { path: lockPath, ownerId };\n } catch (err) {\n if ((err as NodeJS.ErrnoException | undefined)?.code !== \"EEXIST\") {\n // Unexpected FS error — proceed best-effort without the lock.\n return undefined;\n }\n // Lock exists: break it if stale, then poll. breakStaleLock is\n // replacement-safe (NG7Bg) and never throws.\n await breakStaleLock(lockPath, opts.staleMs, opts.onBeforeBreakStaleUnlinkForTest);\n if (Date.now() >= deadline) return undefined;\n // Cap the sleep to the remaining budget so a large pollMs cannot block\n // acquisition far past maxWaitMs (e.g. maxWaitMs=1000, pollMs=60000\n // would otherwise block ~60s instead of 1s — codex P2).\n await sleep(Math.min(pollMs, deadline - Date.now()));\n }\n }\n}\n\n/**\n * Replacement-safe stale-lock breaking (NG7Bg, #1506 round 28). Capture the\n * lock's identity when judging it stale, then ATOMICALLY rename it to a unique\n * trash path and verify the moved content matches. A replacement lock created\n * in the race window is either left untouched (different identity at\n * lockPath, so the rename moves the stale lock — not the replacement) or\n * restored (if the rename accidentally moves a replacement, the verify\n * detects the mismatch and renames it back).\n *\n * ATOMICITY (codex P2): `rename` is atomic on POSIX — only ONE contender can\n * successfully rename a given file. This eliminates the TOCTOU between the\n * identity/stat checks and the deletion that a bare `unlink` leaves open:\n * without rename, contender A could verify identity X, pause, then unlink\n * contender B's freshly acquired replacement Y. With rename, A moves whatever\n * is at lockPath, then checks: if it is X, A broke the stale lock; if it is\n * not X (a replacement appeared between A's last check and the rename), A\n * restores it.\n */\nasync function breakStaleLock(\n lockPath: string,\n staleMs: number,\n onBeforeBreakStaleUnlinkForTest: (() => Promise<void> | void) | undefined,\n): Promise<void> {\n let staleIdentity: string;\n try {\n const info = await stat(lockPath);\n if (Date.now() - info.mtimeMs <= staleMs) {\n // Not stale (a live holder's heartbeat keeps it fresh) — leave it.\n return;\n }\n staleIdentity = await readFile(lockPath, \"utf8\");\n } catch {\n // Lock vanished (released by holder) or stat/read failed — nothing to do.\n return;\n }\n // Test seam: simulate a replacement lock being created in the race window\n // between the staleness judgment and the atomic break. No-op in production.\n if (onBeforeBreakStaleUnlinkForTest) {\n await onBeforeBreakStaleUnlinkForTest();\n }\n try {\n // Re-validate immediately before breaking: the lock must still carry the\n // SAME identity AND still be stale.\n const current = await readFile(lockPath, \"utf8\");\n if (current !== staleIdentity) return; // replaced — leave the fresh lock\n const recheck = await stat(lockPath);\n if (Date.now() - recheck.mtimeMs <= staleMs) return; // heartbeat refreshed it\n\n // ATOMIC BREAK: rename is atomic on POSIX. Only one contender succeeds;\n // others get ENOENT (the file is already gone). After the rename, verify\n // the moved content: if it matches staleIdentity we broke the right lock;\n // if it does not, a replacement appeared in the window and we restore it.\n const trashPath = `${lockPath}.breaking.${process.pid}.${Date.now()}`;\n await rename(lockPath, trashPath);\n try {\n const moved = await readFile(trashPath, \"utf8\");\n if (moved !== staleIdentity) {\n // We accidentally moved a replacement lock (created between our last\n // check and the rename). Restore it so the replacement holder's lock\n // survives. Use link (not rename) to AVOID overwriting a fresh lock\n // that a third contender may have acquired at lockPath while the file\n // was in trash: link fails with EEXIST if lockPath exists, leaving\n // the third contender's lock intact (codex P2 review).\n try {\n await link(trashPath, lockPath);\n // link succeeded — remove the redundant trash hard link. The lock\n // now lives only at lockPath.\n await unlink(trashPath).catch(() => undefined);\n } catch {\n // lockPath already exists (a third contender acquired it). Do NOT\n // unlink the moved file — it may be a LIVE lock whose holder is\n // still in its critical section. Destroying it would leave the\n // holder running with no visible lock, breaking mutual exclusion\n // (codex P2). Leave it in trash as a breadcrumb; it is not at\n // lockPath so it does not block other contenders.\n }\n } else {\n // Content matches — but verify the moved file is STILL stale. The\n // original holder may have resumed and heartbeated between our\n // pre-rename stat() and the rename, refreshing the mtime. If so, the\n // holder is live: restore the lock instead of deleting it (codex P2).\n const movedStat = await stat(trashPath);\n if (Date.now() - movedStat.mtimeMs <= staleMs) {\n // Mtime was refreshed — the holder resumed. Restore the lock.\n try {\n await link(trashPath, lockPath);\n await unlink(trashPath).catch(() => undefined);\n } catch {\n // lockPath already exists — another contender acquired it. Do NOT\n // unlink the moved file (it may be a live lock). Leave it in trash.\n }\n } else {\n // Still stale — we broke the right lock. Clean up the trash.\n await unlink(trashPath).catch(() => undefined);\n }\n }\n } catch {\n // Could not read the trash file — clean it up best-effort.\n await unlink(trashPath).catch(() => undefined);\n }\n } catch {\n // The lock changed/vanished between checks — another process handled it.\n }\n}\n\n/**\n * Release the lock ONLY if its content still identifies THIS acquirer (same\n * owner id). Two-stage ownership check:\n *\n * 1. PRE-CHECK (chatgpt-codex-connector P2): read lockPath BEFORE renaming.\n * If the lock is already a replacement (a contender broke our stale lock),\n * return WITHOUT renaming — renaming a replacement out of lockPath leaves\n * it empty, letting a third contender acquire while the replacement holder\n * is still active. The replacement is safe at lockPath; leave it alone.\n *\n * 2. ATOMIC CLAIM: if the pre-check saw our ownerId, rename lockPath→trash\n * (POSIX-atomic) and re-verify on the moved file. A replacement could\n * appear between the pre-check and the rename; if the moved file is no\n * longer ours, restore it via link (non-overwriting). This ties the\n * ownership check to the deletion so a bare readFile-then-unlink TOCTOU\n * cannot delete a fresh replacement (codex P2).\n */\nasync function releaseLock(\n held: HeldLock,\n warn: (message: string, err: unknown) => void,\n onAfterReleaseRenameForTest: (() => Promise<void> | void) | undefined,\n): Promise<void> {\n try {\n // PRE-CHECK (chatgpt-codex-connector P2): read lockPath before renaming. If\n // the lock is no longer ours, a contender broke our stale lock and created a\n // replacement. Return WITHOUT renaming — renaming the replacement out of\n // lockPath leaves it empty, so a third contender could acquire while the\n // replacement holder is still active. The replacement is safe at lockPath.\n let precheck: string;\n try {\n precheck = await readFile(held.path, \"utf8\");\n } catch {\n return; // lock vanished — nothing to release.\n }\n if (!precheck.includes(held.ownerId)) {\n return; // replacement lock — leave it untouched for its holder.\n }\n // It was ours when we read it. Atomically claim via rename, then re-verify\n // on the moved file: a replacement could appear between the pre-check read\n // above and this rename.\n const trashPath = `${held.path}.releasing.${process.pid}.${Date.now()}`;\n await rename(held.path, trashPath);\n // Test seam: simulate a third contender acquiring the now-empty lockPath\n // in the rename-to-restore window. No-op in production.\n if (onAfterReleaseRenameForTest) {\n await onAfterReleaseRenameForTest();\n }\n try {\n const moved = await readFile(trashPath, \"utf8\");\n if (moved.includes(held.ownerId)) {\n // Still our lock — safe to delete.\n await unlink(trashPath).catch(() => undefined);\n } else {\n // Not ours: a replacement appeared between the pre-check and the rename.\n // Restore it via link (non-overwriting — if lockPath already has a newer\n // lock, leave it).\n try {\n await link(trashPath, held.path);\n } catch {\n // lockPath already exists — a newer holder is active. Leave the\n // moved file in trash rather than destroying a live lock (codex P2).\n return;\n }\n await unlink(trashPath).catch(() => undefined);\n }\n } catch {\n // Could not read the moved file — clean it up best-effort.\n await unlink(trashPath).catch(() => undefined);\n }\n } catch (err) {\n // Best-effort release; a stale lock will be broken on the next acquire.\n warn(\"withHeldFileLock release failed\", err);\n }\n}\n\n/**\n * Whether the lock file at `held.path` was written by THIS acquirer (same owner\n * id). Reads the content and matches the `<pid> <owner-uuid>` prefix; the iso\n * timestamp varies so it is not part of the identity check.\n */\nasync function lockHeldBySelf(held: HeldLock): Promise<boolean> {\n try {\n const body = await readFile(held.path, \"utf8\");\n const parts = body.trim().split(/\\s+/);\n const fileOwner = parts[1];\n return typeof fileOwner === \"string\" && fileOwner === held.ownerId;\n } catch {\n return false;\n }\n}\n\n/**\n * Re-assert ownership and refresh the lock mtime for {@link HeldFileLockController.refresh}.\n * Returns `true` only while THIS acquirer still owns the lock. When ownership\n * was lost (a peer stale-broke and replaced the lock while a CPU-bound section\n * blocked the event loop), returns `false` WITHOUT touching the replacement's\n * mtime — mirroring the heartbeat's ownership guard.\n *\n * FAIL-CLOSED on the mtime bump (#2033): a `utimes` failure means we cannot\n * prove the lock is still ours AND fresh — the file may have been stale-broken,\n * deleted, or replaced between the ownership check and the bump — so we report\n * lost ownership rather than let the caller rewrite the ledger on an unrefreshed\n * (soon-stale) or already-replaced lock. After a successful bump we RE-CHECK\n * ownership: a peer could have broken and replaced the lock in the window, in\n * which case `utimes` just refreshed the replacement's mtime and we must not\n * report held.\n */\nasync function refreshHeldLock(\n held: HeldLock,\n warn: (message: string, err: unknown) => void,\n onBeforeRefreshUtimesForTest?: () => Promise<void> | void,\n): Promise<boolean> {\n if (!(await lockHeldBySelf(held))) return false;\n if (onBeforeRefreshUtimesForTest) await onBeforeRefreshUtimesForTest();\n try {\n await utimes(held.path, new Date(), new Date());\n } catch (err) {\n warn(\"withHeldFileLock manual refresh failed\", err);\n return false;\n }\n return lockHeldBySelf(held);\n}\n\nfunction sleep(ms: number): Promise<void> {\n // Manual deferred instead of Promise.withResolvers (ES2024) — plugin-openclaw's\n // standalone tsconfig targets ES2022 lib and this module is reachable from its\n // type graph, so withResolvers would TS2550 there (same fix as\n // extraction-faithfulness.ts:467).\n return new Promise<void>((resolve) => {\n // NOT unref'd: this polls inside an awaited acquire loop, so the caller's\n // await chain keeps the loop alive; unref would let Node exit mid-poll when\n // nothing else is pending (the heartbeat interval IS unref'd separately).\n setTimeout(resolve, ms);\n });\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  log
3
- } from "./chunk-37CTGERM.js";
3
+ } from "./chunk-3WCASNQZ.js";
4
4
 
5
5
  // src/model-registry.ts
6
6
  import fs from "fs";
@@ -236,4 +236,4 @@ var ModelRegistry = class {
236
236
  export {
237
237
  ModelRegistry
238
238
  };
239
- //# sourceMappingURL=chunk-KAWTOYAW.js.map
239
+ //# sourceMappingURL=chunk-WLZYGLJ4.js.map
@@ -1,19 +1,19 @@
1
1
  import {
2
2
  hasCodexMaterializeSentinel,
3
3
  materializeForNamespace
4
- } from "./chunk-QLULYKWZ.js";
4
+ } from "./chunk-KSXJOC4F.js";
5
5
  import {
6
6
  resolveNamespaceChildRoot
7
7
  } from "./chunk-3UXOZBHV.js";
8
8
  import {
9
9
  StorageManager
10
- } from "./chunk-K7LOWSLU.js";
10
+ } from "./chunk-4BJQODZF.js";
11
11
  import {
12
12
  isSafeRouteNamespace
13
13
  } from "./chunk-U3PN77QT.js";
14
14
  import {
15
15
  log
16
- } from "./chunk-37CTGERM.js";
16
+ } from "./chunk-3WCASNQZ.js";
17
17
 
18
18
  // src/connectors/codex-materialize-runner.ts
19
19
  import { existsSync } from "fs";
@@ -118,4 +118,4 @@ export {
118
118
  runCodexMaterialize,
119
119
  runPostConsolidationMaterialize
120
120
  };
121
- //# sourceMappingURL=chunk-RO4LQGT3.js.map
121
+ //# sourceMappingURL=chunk-WON4APQY.js.map