@remnic/core 9.69.102 → 9.69.103

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 (339) hide show
  1. package/dist/access-admin-ops-surface.js +52 -51
  2. package/dist/access-authorization-probe.js +53 -52
  3. package/dist/access-boundary.js +52 -51
  4. package/dist/access-cli.js +151 -148
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +52 -51
  7. package/dist/access-http-briefing.js +53 -52
  8. package/dist/access-http-query.js +54 -53
  9. package/dist/access-http.js +76 -75
  10. package/dist/access-identity-continuity-surface.js +52 -51
  11. package/dist/access-lcm-surface.js +52 -51
  12. package/dist/access-mcp.js +65 -64
  13. package/dist/access-namespace-preflight.js +52 -51
  14. package/dist/access-observe-write-category.js +8 -8
  15. package/dist/access-observe-write-surface.js +52 -51
  16. package/dist/access-offline-manifest.js +5 -5
  17. package/dist/access-operations-batch.js +56 -55
  18. package/dist/access-operations.js +63 -62
  19. package/dist/access-recall-concurrency.js +52 -51
  20. package/dist/access-recall-response.js +52 -51
  21. package/dist/access-recall-surface.js +52 -51
  22. package/dist/access-schema.js +7 -7
  23. package/dist/access-service.js +52 -51
  24. package/dist/access-surface-catalog.js +54 -53
  25. package/dist/access-surface-catalog.js.map +1 -1
  26. package/dist/active-recall.js +20 -19
  27. package/dist/active-recall.js.map +1 -1
  28. package/dist/adapters/index.js +4 -4
  29. package/dist/adapters/registry.js +2 -2
  30. package/dist/briefing.js +14 -13
  31. package/dist/buffer.js +5 -5
  32. package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
  33. package/dist/{capsule-merge-ZCXUPLNJ.js → capsule-merge-42GJGDUC.js} +4 -3
  34. package/dist/{capsule-merge-ZCXUPLNJ.js.map → capsule-merge-42GJGDUC.js.map} +1 -1
  35. package/dist/causal-behavior.js +2 -2
  36. package/dist/causal-chain.js +2 -2
  37. package/dist/causal-consolidation.js +17 -16
  38. package/dist/causal-consolidation.js.map +1 -1
  39. package/dist/causal-retrieval.js +2 -2
  40. package/dist/causal-trajectory-graph.js +3 -3
  41. package/dist/causal-trajectory.js +1 -1
  42. package/dist/{chunk-UL7KDVLC.js → chunk-26DJ7FG6.js} +11 -11
  43. package/dist/{chunk-QCYUIZK2.js → chunk-2GSO4FEC.js} +7 -7
  44. package/dist/{chunk-533RHNHB.js → chunk-2JJD7GIW.js} +2 -2
  45. package/dist/{chunk-IOYY6ROU.js → chunk-2MMBJE53.js} +7 -7
  46. package/dist/{chunk-QSXEQPC4.js → chunk-2OFDC3AK.js} +2 -2
  47. package/dist/{chunk-5FAGCOZD.js → chunk-2U3R3DDH.js} +9 -9
  48. package/dist/{chunk-SCXURCVT.js → chunk-2UPTRDGM.js} +3 -3
  49. package/dist/{chunk-XVZA6VXC.js → chunk-3SL52JKD.js} +4 -4
  50. package/dist/{chunk-XEATJBNB.js → chunk-4QHWRWCF.js} +70 -70
  51. package/dist/{chunk-SBOCJ47I.js → chunk-5FHH4NJR.js} +6 -6
  52. package/dist/{chunk-V3I4NLZP.js → chunk-5GKRQBPQ.js} +2 -2
  53. package/dist/{chunk-PYYQCHJC.js → chunk-5PEBJUYD.js} +2 -2
  54. package/dist/{chunk-PEZAHDDU.js → chunk-6RWPKWCZ.js} +4 -4
  55. package/dist/{chunk-DSPWVQZ6.js → chunk-73R4HOOK.js} +3 -3
  56. package/dist/chunk-7CT3AI52.js +84 -0
  57. package/dist/chunk-7CT3AI52.js.map +1 -0
  58. package/dist/{chunk-S533KGX6.js → chunk-7X7A7KVN.js} +2 -2
  59. package/dist/{chunk-F5MINS3R.js → chunk-A6WNTVA7.js} +3 -3
  60. package/dist/{chunk-F5MINS3R.js.map → chunk-A6WNTVA7.js.map} +1 -1
  61. package/dist/{chunk-JZ66PN72.js → chunk-AWSP3WNM.js} +2 -2
  62. package/dist/{chunk-IY47KPML.js → chunk-B3OFQP5Y.js} +6 -6
  63. package/dist/{chunk-GNBZB43J.js → chunk-B44TVSEI.js} +2 -2
  64. package/dist/{chunk-4DXNOVNW.js → chunk-B454SHPU.js} +6 -6
  65. package/dist/{chunk-3LMOJXZR.js → chunk-BKVCTTEN.js} +2 -2
  66. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  67. package/dist/{chunk-HWLIMQE2.js → chunk-CHDGJJV6.js} +2 -2
  68. package/dist/{chunk-UYPQOASU.js → chunk-CNBGAFDY.js} +2 -2
  69. package/dist/{chunk-CYG3K2T2.js → chunk-D42DLA4D.js} +6 -6
  70. package/dist/{chunk-X5TAWDRO.js → chunk-D4MXH276.js} +2 -2
  71. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  72. package/dist/{chunk-UFIZRMQZ.js → chunk-E4XLFYHV.js} +61 -29
  73. package/dist/chunk-E4XLFYHV.js.map +1 -0
  74. package/dist/{chunk-4OXXDL3X.js → chunk-EWWK65CI.js} +2 -2
  75. package/dist/{chunk-DFPG47ET.js → chunk-EZ3F4YIN.js} +11 -11
  76. package/dist/{chunk-PNVOKGAX.js → chunk-G5I5ZT6O.js} +2 -2
  77. package/dist/{chunk-7Z2UKRNJ.js → chunk-GQ6ZF4LD.js} +2 -2
  78. package/dist/{chunk-7OA4JWP5.js → chunk-GWWHV7F3.js} +2 -2
  79. package/dist/chunk-H6Z3RNW3.js +330 -0
  80. package/dist/chunk-H6Z3RNW3.js.map +1 -0
  81. package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
  82. package/dist/{chunk-PHBH74Q6.js → chunk-HTXTVY64.js} +2 -2
  83. package/dist/{chunk-AXHU56C3.js → chunk-HWGFTTPS.js} +2 -2
  84. package/dist/{chunk-ARY6O3JN.js → chunk-HYFVZJLX.js} +2 -2
  85. package/dist/{chunk-2SGLMP2E.js → chunk-IDSZNHIP.js} +2 -2
  86. package/dist/{chunk-LO4UKPKX.js → chunk-IFCKHPY4.js} +2 -2
  87. package/dist/{chunk-ZHXSOMYH.js → chunk-IOMLGJHG.js} +4 -4
  88. package/dist/{chunk-YRULQW5J.js → chunk-ISX7GVZE.js} +4 -4
  89. package/dist/{chunk-5PHZSD75.js → chunk-J3ZJBX7I.js} +2 -2
  90. package/dist/{chunk-DINOVGA5.js → chunk-JHZOY4CJ.js} +48 -48
  91. package/dist/chunk-JHZOY4CJ.js.map +1 -0
  92. package/dist/{chunk-RFQT7IZO.js → chunk-JRDL3FGM.js} +3 -3
  93. package/dist/{chunk-OGZC2WUX.js → chunk-JWFTPRYD.js} +2 -2
  94. package/dist/{chunk-VPOAMPDD.js → chunk-K5ABKRXK.js} +59 -59
  95. package/dist/{chunk-P3WRFSYF.js → chunk-KMN2JE4D.js} +2 -2
  96. package/dist/{chunk-4MP2Q2EI.js → chunk-KN4GNQH7.js} +3 -3
  97. package/dist/{chunk-L6YCGS7I.js → chunk-KYUVFCT2.js} +2 -2
  98. package/dist/{chunk-5BVL2XUD.js → chunk-L6BUCPGI.js} +17 -17
  99. package/dist/{chunk-55NZVUNI.js → chunk-LP4UW5WI.js} +2 -2
  100. package/dist/{chunk-SF7CWSR6.js → chunk-LXJZUNLX.js} +2 -2
  101. package/dist/{chunk-XCB6QDL6.js → chunk-M5GVCJSJ.js} +8 -2
  102. package/dist/chunk-M5GVCJSJ.js.map +1 -0
  103. package/dist/{chunk-DGS34QSY.js → chunk-MFZACYZ6.js} +2 -2
  104. package/dist/{chunk-G3BW6SDE.js → chunk-NBYSCXOY.js} +3 -3
  105. package/dist/{chunk-KYLYXGKD.js → chunk-NIGZ3LE7.js} +2 -2
  106. package/dist/{chunk-JTC3PBNH.js → chunk-NN62MUZA.js} +2 -2
  107. package/dist/{chunk-R2TVWQLR.js → chunk-NT2Q6AHC.js} +2 -2
  108. package/dist/{chunk-ABGM2BN4.js → chunk-O43AAJFQ.js} +2 -2
  109. package/dist/{chunk-CZN53OT5.js → chunk-OC6ATPIP.js} +2 -2
  110. package/dist/{chunk-6Z263Z3E.js → chunk-OWV6LKNZ.js} +2 -2
  111. package/dist/{chunk-NM3BT2OH.js → chunk-PNZAFABF.js} +2 -2
  112. package/dist/{chunk-HIHN4XHL.js → chunk-PQ7EB5AD.js} +3 -3
  113. package/dist/{chunk-VVYLVE6R.js → chunk-PQKZLBCJ.js} +1 -1
  114. package/dist/{chunk-OFFW73OZ.js → chunk-PRNR47DI.js} +2 -2
  115. package/dist/{chunk-RZXFXKWY.js → chunk-Q3LWW7WS.js} +2 -2
  116. package/dist/{chunk-M7T2NKET.js → chunk-QKCGOECM.js} +2 -2
  117. package/dist/{chunk-Z3O53WZR.js → chunk-QWUJWBZ5.js} +2 -2
  118. package/dist/{chunk-HFOY2SBG.js → chunk-RYN4AIRM.js} +7 -5
  119. package/dist/chunk-RYN4AIRM.js.map +1 -0
  120. package/dist/{chunk-4LNCLZNK.js → chunk-S2GR3T7W.js} +2 -2
  121. package/dist/{chunk-2IWBHZ3H.js → chunk-SIGDETMB.js} +2 -2
  122. package/dist/{chunk-7AUIGOH4.js → chunk-SOVRTUD3.js} +2 -2
  123. package/dist/chunk-SP4DB6SX.js +19 -0
  124. package/dist/chunk-SP4DB6SX.js.map +1 -0
  125. package/dist/{chunk-S5R77WHV.js → chunk-SWXPR5SE.js} +2 -2
  126. package/dist/{chunk-3DNTFZAT.js → chunk-T32YYUT2.js} +2 -2
  127. package/dist/{chunk-W5JQSVPZ.js → chunk-T6RQNV7E.js} +2 -2
  128. package/dist/{chunk-O23QMYN4.js → chunk-TRN56SPQ.js} +2 -2
  129. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  130. package/dist/{chunk-ZGYLPCWN.js → chunk-VO2BNAA2.js} +3 -3
  131. package/dist/{chunk-WVEECBCG.js → chunk-VV2MVHCJ.js} +2 -2
  132. package/dist/{chunk-HVDEKJJR.js → chunk-VYIBRCVW.js} +70 -61
  133. package/dist/chunk-VYIBRCVW.js.map +1 -0
  134. package/dist/{chunk-B3O3IRZN.js → chunk-VZBW6CDG.js} +1 -1
  135. package/dist/{chunk-JPKK466T.js → chunk-W5XWHTDO.js} +2 -2
  136. package/dist/{chunk-JNAOTN7B.js → chunk-XLWMNDQR.js} +4 -4
  137. package/dist/chunk-XLWMNDQR.js.map +1 -0
  138. package/dist/{chunk-DBWNTLLI.js → chunk-XM32D6YZ.js} +6 -6
  139. package/dist/{chunk-RP7RSENW.js → chunk-XQWKB6PY.js} +2 -2
  140. package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
  141. package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
  142. package/dist/{chunk-Y7427SL7.js → chunk-Z6EZCN6Z.js} +10 -10
  143. package/dist/{chunk-5K2ALMSZ.js → chunk-ZRUC7FD3.js} +2 -2
  144. package/dist/cli.js +102 -101
  145. package/dist/coding/export-okf-codegraph.js +13 -12
  146. package/dist/coding/export-okf-codegraph.js.map +1 -1
  147. package/dist/coding/pre-action-gate.js +2 -2
  148. package/dist/compounding/engine.js +14 -13
  149. package/dist/config.js +20 -19
  150. package/dist/connectors/codex-materialize-runner.js +14 -13
  151. package/dist/connectors/index.js +14 -13
  152. package/dist/consolidation-provenance-check.js +4 -3
  153. package/dist/consolidation-undo.js +3 -2
  154. package/dist/consolidation-undo.js.map +1 -1
  155. package/dist/contradiction/index.js +3 -2
  156. package/dist/convergence-refresh.js +16 -15
  157. package/dist/corpus-watermark.js +16 -15
  158. package/dist/dashboard-runtime.js +2 -2
  159. package/dist/deep-recall-http-glue.js +8 -8
  160. package/dist/deep-recall.js +2 -2
  161. package/dist/entity-mention-carry.d.ts +16 -0
  162. package/dist/entity-mention-carry.d.ts.map +1 -0
  163. package/dist/entity-mention-carry.js +8 -0
  164. package/dist/entity-mention-epoch.d.ts +22 -0
  165. package/dist/entity-mention-epoch.d.ts.map +1 -0
  166. package/dist/entity-mention-epoch.js +8 -0
  167. package/dist/entity-mention-index-cache.d.ts +28 -0
  168. package/dist/entity-mention-index-cache.d.ts.map +1 -0
  169. package/dist/entity-mention-index-cache.js +29 -0
  170. package/dist/entity-mention-index-cache.js.map +1 -0
  171. package/dist/entity-retrieval.d.ts.map +1 -1
  172. package/dist/entity-retrieval.js +18 -15
  173. package/dist/event-order-recall.js +2 -2
  174. package/dist/external-wiki-access.js +53 -52
  175. package/dist/extraction-span-materialize.js +2 -2
  176. package/dist/extraction.js +21 -20
  177. package/dist/{graph-edge-decay-MKIRZ762.js → graph-edge-decay-PVKDKLQK.js} +6 -6
  178. package/dist/graph-edge-decay-PVKDKLQK.js.map +1 -0
  179. package/dist/graph-jsonl.js +4 -4
  180. package/dist/graph-snapshot.js +4 -4
  181. package/dist/graph.js +5 -5
  182. package/dist/harmonic-retrieval.js +1 -1
  183. package/dist/index.js +186 -183
  184. package/dist/lcm/engine.js +4 -4
  185. package/dist/lcm/index.js +13 -13
  186. package/dist/location/index.js +16 -15
  187. package/dist/maintenance/memory-governance.js +14 -13
  188. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -13
  189. package/dist/maintenance/rebuild-memory-projection.js +15 -14
  190. package/dist/memory-browse-operations.js +53 -52
  191. package/dist/memory-browse.js +5 -5
  192. package/dist/memory-corpus-version.d.ts +8 -3
  193. package/dist/memory-corpus-version.d.ts.map +1 -1
  194. package/dist/memory-corpus-version.js +2 -1
  195. package/dist/namespaces/migrate.js +15 -14
  196. package/dist/namespaces/search.js +7 -7
  197. package/dist/namespaces/storage.js +14 -13
  198. package/dist/offline-sync-changeset-generations.js +3 -3
  199. package/dist/offline-sync-embedding-generation.js +2 -2
  200. package/dist/offline-sync-snapshot-stream.js +5 -5
  201. package/dist/offline-sync.js +7 -7
  202. package/dist/operator-doctor-corpus.js +17 -16
  203. package/dist/operator-toolkit.js +33 -32
  204. package/dist/orchestration/maintenance.js +22 -21
  205. package/dist/orchestrator.js +151 -148
  206. package/dist/page-versioning.js +3 -2
  207. package/dist/preferences/preference-drift.d.ts.map +1 -1
  208. package/dist/procedural/library-maintenance.d.ts.map +1 -1
  209. package/dist/qmd.js +4 -4
  210. package/dist/recall-navigation-operations.js +53 -52
  211. package/dist/recall-navigation.js +52 -51
  212. package/dist/recall-state-view-admission.js +5 -5
  213. package/dist/recall-why-service.js +5 -5
  214. package/dist/{resolution-BROE3TRO.js → resolution-RSG4HZTL.js} +4 -3
  215. package/dist/resolution-RSG4HZTL.js.map +1 -0
  216. package/dist/resume-bundles.js +21 -20
  217. package/dist/search/factory.js +6 -6
  218. package/dist/search/index.js +12 -12
  219. package/dist/semantic-consolidation.js +15 -14
  220. package/dist/semantic-rule-promotion.js +14 -13
  221. package/dist/semantic-rule-verifier.js +14 -13
  222. package/dist/source-agent-qualifier.js +17 -16
  223. package/dist/standup/build.js +14 -13
  224. package/dist/storage/entity-mention-frontmatter-hold.d.ts +14 -0
  225. package/dist/storage/entity-mention-frontmatter-hold.d.ts.map +1 -0
  226. package/dist/storage.d.ts +13 -9
  227. package/dist/storage.d.ts.map +1 -1
  228. package/dist/storage.js +13 -12
  229. package/dist/support-passport/index.js +55 -54
  230. package/dist/transfer/backup.js +2 -2
  231. package/dist/transfer/capsule-export.js +2 -2
  232. package/dist/transfer/capsule-import.js +5 -4
  233. package/dist/transfer/export-okf.js +14 -13
  234. package/dist/verified-recall.js +14 -13
  235. package/dist/who-knows-cli.js +15 -14
  236. package/dist/who-knows.js +14 -13
  237. package/package.json +2 -2
  238. package/src/entity-mention-carry.ts +31 -0
  239. package/src/entity-mention-epoch.ts +120 -0
  240. package/src/entity-mention-index-cache.ts +443 -0
  241. package/src/entity-retrieval.ts +72 -71
  242. package/src/maintenance/rebuild-memory-projection.ts +1 -1
  243. package/src/memory-corpus-version.ts +13 -3
  244. package/src/preferences/preference-drift.ts +5 -3
  245. package/src/procedural/library-maintenance.ts +4 -3
  246. package/src/semantic-rule-promotion.ts +1 -1
  247. package/src/storage/entity-mention-frontmatter-hold.ts +44 -0
  248. package/src/storage.ts +40 -38
  249. package/dist/chunk-DINOVGA5.js.map +0 -1
  250. package/dist/chunk-HFOY2SBG.js.map +0 -1
  251. package/dist/chunk-HVDEKJJR.js.map +0 -1
  252. package/dist/chunk-JNAOTN7B.js.map +0 -1
  253. package/dist/chunk-UFIZRMQZ.js.map +0 -1
  254. package/dist/chunk-XCB6QDL6.js.map +0 -1
  255. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  256. /package/dist/{chunk-UL7KDVLC.js.map → chunk-26DJ7FG6.js.map} +0 -0
  257. /package/dist/{chunk-QCYUIZK2.js.map → chunk-2GSO4FEC.js.map} +0 -0
  258. /package/dist/{chunk-533RHNHB.js.map → chunk-2JJD7GIW.js.map} +0 -0
  259. /package/dist/{chunk-IOYY6ROU.js.map → chunk-2MMBJE53.js.map} +0 -0
  260. /package/dist/{chunk-QSXEQPC4.js.map → chunk-2OFDC3AK.js.map} +0 -0
  261. /package/dist/{chunk-5FAGCOZD.js.map → chunk-2U3R3DDH.js.map} +0 -0
  262. /package/dist/{chunk-SCXURCVT.js.map → chunk-2UPTRDGM.js.map} +0 -0
  263. /package/dist/{chunk-XVZA6VXC.js.map → chunk-3SL52JKD.js.map} +0 -0
  264. /package/dist/{chunk-XEATJBNB.js.map → chunk-4QHWRWCF.js.map} +0 -0
  265. /package/dist/{chunk-SBOCJ47I.js.map → chunk-5FHH4NJR.js.map} +0 -0
  266. /package/dist/{chunk-V3I4NLZP.js.map → chunk-5GKRQBPQ.js.map} +0 -0
  267. /package/dist/{chunk-PYYQCHJC.js.map → chunk-5PEBJUYD.js.map} +0 -0
  268. /package/dist/{chunk-PEZAHDDU.js.map → chunk-6RWPKWCZ.js.map} +0 -0
  269. /package/dist/{chunk-DSPWVQZ6.js.map → chunk-73R4HOOK.js.map} +0 -0
  270. /package/dist/{chunk-S533KGX6.js.map → chunk-7X7A7KVN.js.map} +0 -0
  271. /package/dist/{chunk-JZ66PN72.js.map → chunk-AWSP3WNM.js.map} +0 -0
  272. /package/dist/{chunk-IY47KPML.js.map → chunk-B3OFQP5Y.js.map} +0 -0
  273. /package/dist/{chunk-GNBZB43J.js.map → chunk-B44TVSEI.js.map} +0 -0
  274. /package/dist/{chunk-4DXNOVNW.js.map → chunk-B454SHPU.js.map} +0 -0
  275. /package/dist/{chunk-3LMOJXZR.js.map → chunk-BKVCTTEN.js.map} +0 -0
  276. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  277. /package/dist/{chunk-HWLIMQE2.js.map → chunk-CHDGJJV6.js.map} +0 -0
  278. /package/dist/{chunk-UYPQOASU.js.map → chunk-CNBGAFDY.js.map} +0 -0
  279. /package/dist/{chunk-CYG3K2T2.js.map → chunk-D42DLA4D.js.map} +0 -0
  280. /package/dist/{chunk-X5TAWDRO.js.map → chunk-D4MXH276.js.map} +0 -0
  281. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  282. /package/dist/{chunk-4OXXDL3X.js.map → chunk-EWWK65CI.js.map} +0 -0
  283. /package/dist/{chunk-DFPG47ET.js.map → chunk-EZ3F4YIN.js.map} +0 -0
  284. /package/dist/{chunk-PNVOKGAX.js.map → chunk-G5I5ZT6O.js.map} +0 -0
  285. /package/dist/{chunk-7Z2UKRNJ.js.map → chunk-GQ6ZF4LD.js.map} +0 -0
  286. /package/dist/{chunk-7OA4JWP5.js.map → chunk-GWWHV7F3.js.map} +0 -0
  287. /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
  288. /package/dist/{chunk-PHBH74Q6.js.map → chunk-HTXTVY64.js.map} +0 -0
  289. /package/dist/{chunk-AXHU56C3.js.map → chunk-HWGFTTPS.js.map} +0 -0
  290. /package/dist/{chunk-ARY6O3JN.js.map → chunk-HYFVZJLX.js.map} +0 -0
  291. /package/dist/{chunk-2SGLMP2E.js.map → chunk-IDSZNHIP.js.map} +0 -0
  292. /package/dist/{chunk-LO4UKPKX.js.map → chunk-IFCKHPY4.js.map} +0 -0
  293. /package/dist/{chunk-ZHXSOMYH.js.map → chunk-IOMLGJHG.js.map} +0 -0
  294. /package/dist/{chunk-YRULQW5J.js.map → chunk-ISX7GVZE.js.map} +0 -0
  295. /package/dist/{chunk-5PHZSD75.js.map → chunk-J3ZJBX7I.js.map} +0 -0
  296. /package/dist/{chunk-RFQT7IZO.js.map → chunk-JRDL3FGM.js.map} +0 -0
  297. /package/dist/{chunk-OGZC2WUX.js.map → chunk-JWFTPRYD.js.map} +0 -0
  298. /package/dist/{chunk-VPOAMPDD.js.map → chunk-K5ABKRXK.js.map} +0 -0
  299. /package/dist/{chunk-P3WRFSYF.js.map → chunk-KMN2JE4D.js.map} +0 -0
  300. /package/dist/{chunk-4MP2Q2EI.js.map → chunk-KN4GNQH7.js.map} +0 -0
  301. /package/dist/{chunk-L6YCGS7I.js.map → chunk-KYUVFCT2.js.map} +0 -0
  302. /package/dist/{chunk-5BVL2XUD.js.map → chunk-L6BUCPGI.js.map} +0 -0
  303. /package/dist/{chunk-55NZVUNI.js.map → chunk-LP4UW5WI.js.map} +0 -0
  304. /package/dist/{chunk-SF7CWSR6.js.map → chunk-LXJZUNLX.js.map} +0 -0
  305. /package/dist/{chunk-DGS34QSY.js.map → chunk-MFZACYZ6.js.map} +0 -0
  306. /package/dist/{chunk-G3BW6SDE.js.map → chunk-NBYSCXOY.js.map} +0 -0
  307. /package/dist/{chunk-KYLYXGKD.js.map → chunk-NIGZ3LE7.js.map} +0 -0
  308. /package/dist/{chunk-JTC3PBNH.js.map → chunk-NN62MUZA.js.map} +0 -0
  309. /package/dist/{chunk-R2TVWQLR.js.map → chunk-NT2Q6AHC.js.map} +0 -0
  310. /package/dist/{chunk-ABGM2BN4.js.map → chunk-O43AAJFQ.js.map} +0 -0
  311. /package/dist/{chunk-CZN53OT5.js.map → chunk-OC6ATPIP.js.map} +0 -0
  312. /package/dist/{chunk-6Z263Z3E.js.map → chunk-OWV6LKNZ.js.map} +0 -0
  313. /package/dist/{chunk-NM3BT2OH.js.map → chunk-PNZAFABF.js.map} +0 -0
  314. /package/dist/{chunk-HIHN4XHL.js.map → chunk-PQ7EB5AD.js.map} +0 -0
  315. /package/dist/{chunk-VVYLVE6R.js.map → chunk-PQKZLBCJ.js.map} +0 -0
  316. /package/dist/{chunk-OFFW73OZ.js.map → chunk-PRNR47DI.js.map} +0 -0
  317. /package/dist/{chunk-RZXFXKWY.js.map → chunk-Q3LWW7WS.js.map} +0 -0
  318. /package/dist/{chunk-M7T2NKET.js.map → chunk-QKCGOECM.js.map} +0 -0
  319. /package/dist/{chunk-Z3O53WZR.js.map → chunk-QWUJWBZ5.js.map} +0 -0
  320. /package/dist/{chunk-4LNCLZNK.js.map → chunk-S2GR3T7W.js.map} +0 -0
  321. /package/dist/{chunk-2IWBHZ3H.js.map → chunk-SIGDETMB.js.map} +0 -0
  322. /package/dist/{chunk-7AUIGOH4.js.map → chunk-SOVRTUD3.js.map} +0 -0
  323. /package/dist/{chunk-S5R77WHV.js.map → chunk-SWXPR5SE.js.map} +0 -0
  324. /package/dist/{chunk-3DNTFZAT.js.map → chunk-T32YYUT2.js.map} +0 -0
  325. /package/dist/{chunk-W5JQSVPZ.js.map → chunk-T6RQNV7E.js.map} +0 -0
  326. /package/dist/{chunk-O23QMYN4.js.map → chunk-TRN56SPQ.js.map} +0 -0
  327. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  328. /package/dist/{chunk-ZGYLPCWN.js.map → chunk-VO2BNAA2.js.map} +0 -0
  329. /package/dist/{chunk-WVEECBCG.js.map → chunk-VV2MVHCJ.js.map} +0 -0
  330. /package/dist/{chunk-B3O3IRZN.js.map → chunk-VZBW6CDG.js.map} +0 -0
  331. /package/dist/{chunk-JPKK466T.js.map → chunk-W5XWHTDO.js.map} +0 -0
  332. /package/dist/{chunk-DBWNTLLI.js.map → chunk-XM32D6YZ.js.map} +0 -0
  333. /package/dist/{chunk-RP7RSENW.js.map → chunk-XQWKB6PY.js.map} +0 -0
  334. /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
  335. /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
  336. /package/dist/{chunk-Y7427SL7.js.map → chunk-Z6EZCN6Z.js.map} +0 -0
  337. /package/dist/{chunk-5K2ALMSZ.js.map → chunk-ZRUC7FD3.js.map} +0 -0
  338. /package/dist/{graph-edge-decay-MKIRZ762.js.map → entity-mention-carry.js.map} +0 -0
  339. /package/dist/{resolution-BROE3TRO.js.map → entity-mention-epoch.js.map} +0 -0
@@ -2,7 +2,7 @@ import { resolveNamespaceCapabilities } from "./capabilities.js";
2
2
  import { renderAuthorityBoundContent } from "./recall-context-composition.js";
3
3
  import { createHash } from "node:crypto";
4
4
  import { sanitizeMemoryContent } from "./sanitize.js";
5
- import { mkdir, readFile, writeFile } from "node:fs/promises";
5
+ import { readFile } from "node:fs/promises";
6
6
  import path from "node:path";
7
7
  import { collectNativeKnowledgeChunks, type NativeKnowledgeChunk } from "./native-knowledge.js";
8
8
  import { compareEntityTimestamps, normalizeEntityName, type StorageManager } from "./storage.js";
@@ -22,6 +22,15 @@ import {
22
22
  yieldEntityRecallScan,
23
23
  yieldEntityRecallScanEvery,
24
24
  } from "./entity-recall-cancellation.js";
25
+ import { applyCarriedMemorySnippets } from "./entity-mention-carry.js";
26
+ import {
27
+ entityMentionCacheGeneration,
28
+ entityMentionIdentity,
29
+ entityMentionPersistedIndexReadable,
30
+ entityMentionScopeKey,
31
+ resolveEntityMentionIndex,
32
+ writePersistedEntityMentionIndex,
33
+ } from "./entity-mention-index-cache.js";
25
34
  const ENTITY_INDEX_VERSION = 3;
26
35
  const RECENT_TRANSCRIPT_LOOKBACK_HOURS = 24;
27
36
  const INSTRUCTION_LIKE_RE = /\b(always|never|must|should|remember to|do not|don't|process|workflow|template|checklist|instruction)\b/i;
@@ -64,6 +73,8 @@ type EntityMentionIndex = {
64
73
  updatedAt: string;
65
74
  entityStatusVersion?: number;
66
75
  entities: EntityMentionIndexEntry[];
76
+ /** In-memory only. A new canonical id had no snippets to carry. */
77
+ pendingSnippetReconcile?: boolean;
67
78
  };
68
79
  type EntityCandidate = {
69
80
  entry: EntityMentionIndexEntry;
@@ -356,13 +367,19 @@ async function readCurrentPersistedEntityIndex(
356
367
  return null;
357
368
  }
358
369
  const index = await readEntityIndexState(storage);
359
- if (!index || index.entityStatusVersion !== storage.getMemoryStatusVersion()) {
370
+ if (!entityMentionPersistedIndexReadable(storage.dir) || !index || index.entityStatusVersion !== storage.getMemoryStatusVersion()) {
360
371
  return null;
361
372
  }
362
373
  return index;
363
374
  }
364
- const namespaceEntityIndexCache = new Map<string, EntityMentionIndex>();
365
- const MAX_NAMESPACE_ENTITY_INDEX_CACHE_ENTRIES = 32;
375
+ type CarriedMemorySnippets = Map<string, { memorySnippets: string[]; memorySnippetOrigins?: (string | undefined)[] }>;
376
+
377
+ function carriedMemorySnippets(index: EntityMentionIndex): CarriedMemorySnippets {
378
+ return new Map(index.entities.map((entry) => [entry.canonicalId, {
379
+ memorySnippets: entry.memorySnippets.slice(),
380
+ memorySnippetOrigins: entry.memorySnippetOrigins?.slice(),
381
+ }]));
382
+ }
366
383
 
367
384
  function nativeEntityIndexRevision(chunks: NativeKnowledgeChunk[]): string {
368
385
  const projection = chunks.map((chunk) => [
@@ -376,40 +393,13 @@ function nativeEntityIndexRevision(chunks: NativeKnowledgeChunk[]): string {
376
393
  ]);
377
394
  return createHash("sha256").update(JSON.stringify(projection)).digest("hex");
378
395
  }
379
- function namespaceEntityIndexCacheKey(
380
- storages: StorageManager[],
381
- recallNamespaces: string[],
382
- nativeRevision: string,
383
- ): string {
384
- const namespaceKey = uniqueStrings(recallNamespaces).join("\u001f");
385
- const storageKey = storages
386
- .map((scopedStorage) => {
387
- const aliases = Object.entries(scopedStorage.entityAliases)
388
- .sort(([left], [right]) => left.localeCompare(right));
389
- return (
390
- `${path.resolve(scopedStorage.dir)}@${scopedStorage.getMemoryStatusVersion()}` +
391
- `:${scopedStorage.getMemoryCorpusVersion()}:${scopedStorage.getEntityMutationVersion()}` +
392
- `:${scopedStorage.hotCacheKeyId()}:${JSON.stringify(aliases)}`
393
- );
394
- })
395
- .join("\u001f");
396
- return `${namespaceKey}\u001e${storageKey}\u001e${nativeRevision}`;
397
- }
398
- function rememberNamespaceEntityIndex(key: string, index: EntityMentionIndex): void {
399
- if (namespaceEntityIndexCache.size >= MAX_NAMESPACE_ENTITY_INDEX_CACHE_ENTRIES) {
400
- const oldestKey = namespaceEntityIndexCache.keys().next().value;
401
- if (typeof oldestKey === "string") namespaceEntityIndexCache.delete(oldestKey);
402
- }
403
- namespaceEntityIndexCache.set(key, index);
404
- }
405
396
 
406
- async function writeEntityIndexState(storage: StorageManager, index: EntityMentionIndex): Promise<void> {
407
- const statePath = entityIndexStatePath(storage);
408
- await mkdir(path.dirname(statePath), { recursive: true });
409
- const nextContent = JSON.stringify(index, null, 2) + "\n";
410
- const currentContent = await readFile(statePath, "utf-8").catch(() => "");
411
- if (currentContent === nextContent) return;
412
- await writeFile(statePath, nextContent, "utf-8");
397
+ async function writeEntityIndexState(
398
+ storage: StorageManager,
399
+ index: EntityMentionIndex,
400
+ persistGeneration: number,
401
+ ): Promise<void> {
402
+ await writePersistedEntityMentionIndex(storage.dir, persistGeneration, JSON.stringify(index, null, 2) + "\n");
413
403
  }
414
404
 
415
405
  function nativePseudoCanonicalId(chunk: NativeKnowledgeChunk): string {
@@ -483,6 +473,7 @@ async function buildEntityMentionIndex(
483
473
  nativeChunksOverride?: NativeKnowledgeChunk[],
484
474
  resolvedStorages?: StorageManager[],
485
475
  abortSignal?: AbortSignal,
476
+ carrySnippets?: CarriedMemorySnippets,
486
477
  ): Promise<EntityMentionIndex> {
487
478
  checkEntityRecallAbort(abortSignal);
488
479
  const storages = resolvedStorages ?? await resolveEntityIndexStorages(
@@ -493,11 +484,14 @@ async function buildEntityMentionIndex(
493
484
  );
494
485
  const shouldPersistIndex =
495
486
  storages.length === 1 && path.resolve(storages[0]!.dir) === path.resolve(storage.dir);
487
+ const persistGeneration = entityMentionCacheGeneration(storage.dir);
496
488
  const entityStatusVersionBefore = shouldPersistIndex ? storage.getMemoryStatusVersion() : undefined;
497
489
  const [previousIndex, entityFileSets, memorySets, nativeChunks] = await Promise.all([
498
490
  shouldPersistIndex ? readEntityIndexState(storage) : Promise.resolve(null),
499
491
  Promise.all(storages.map((scopedStorage) => scopedStorage.readAllEntityFiles({ abortSignal }))),
500
- Promise.all(storages.map((scopedStorage) => scopedStorage.readAllMemories({ abortSignal }))),
492
+ carrySnippets
493
+ ? Promise.resolve([])
494
+ : Promise.all(storages.map((scopedStorage) => scopedStorage.readAllMemories({ abortSignal }))),
501
495
  nativeChunksOverride ? Promise.resolve(nativeChunksOverride) : readNativeChunks(config, recallNamespaces),
502
496
  ]);
503
497
  // The bulk reads now stop at their own scan boundaries (issue #2307); this
@@ -562,15 +556,20 @@ async function buildEntityMentionIndex(
562
556
  }
563
557
 
564
558
  scanned = 0;
565
- for (const memory of memories) {
566
- scanned += 1;
567
- await yieldEntityRecallScanEvery(scanned, abortSignal);
568
- const entry = typeof memory.frontmatter.entityRef === "string" ? entities.get(memory.frontmatter.entityRef) : undefined;
569
- if (!entry) continue;
570
- const snippet = await readMemorySnippet(memory);
571
- if (entry.memorySnippets.includes(snippet)) continue;
572
- entry.memorySnippets.push(snippet);
573
- entry.memorySnippetOrigins = [...(entry.memorySnippetOrigins ?? []), memory.frontmatter.origin];
559
+ const pendingSnippetReconcile = carrySnippets
560
+ ? applyCarriedMemorySnippets(entities.values(), carrySnippets)
561
+ : false;
562
+ if (!carrySnippets) {
563
+ for (const memory of memories) {
564
+ scanned += 1;
565
+ await yieldEntityRecallScanEvery(scanned, abortSignal);
566
+ const entry = typeof memory.frontmatter.entityRef === "string" ? entities.get(memory.frontmatter.entityRef) : undefined;
567
+ if (!entry) continue;
568
+ const snippet = await readMemorySnippet(memory);
569
+ if (entry.memorySnippets.includes(snippet)) continue;
570
+ entry.memorySnippets.push(snippet);
571
+ entry.memorySnippetOrigins = [...(entry.memorySnippetOrigins ?? []), memory.frontmatter.origin];
572
+ }
574
573
  }
575
574
  // Each remaining phase is another synchronous pass over the whole index
576
575
  // (alias map, native-chunk merge, sort, two full serializations). Yield between
@@ -604,7 +603,7 @@ async function buildEntityMentionIndex(
604
603
  await yieldEntityRecallScan(abortSignal);
605
604
  const nextEntities = JSON.stringify(sortedEntities);
606
605
  const entityStatusVersionAfter = shouldPersistIndex ? storage.getMemoryStatusVersion() : undefined;
607
- const canPersistIndex = shouldPersistIndex && entityStatusVersionBefore === entityStatusVersionAfter;
606
+ const canPersistIndex = shouldPersistIndex && entityStatusVersionBefore === entityStatusVersionAfter && persistGeneration === entityMentionCacheGeneration(storage.dir);
608
607
  const index: EntityMentionIndex = {
609
608
  version: ENTITY_INDEX_VERSION,
610
609
  updatedAt:
@@ -615,12 +614,10 @@ async function buildEntityMentionIndex(
615
614
  entities: sortedEntities,
616
615
  };
617
616
  if (canPersistIndex) {
618
- // A build the caller has abandoned must not leave a persisted index behind:
619
- // the write is I/O the timed-out recall no longer needs, and the next recall
620
- // is already competing for the same disk.
621
617
  checkEntityRecallAbort(abortSignal);
622
- await writeEntityIndexState(storage, index);
618
+ await writeEntityIndexState(storage, index, persistGeneration);
623
619
  }
620
+ if (pendingSnippetReconcile) index.pendingSnippetReconcile = true;
624
621
  return index;
625
622
  }
626
623
 
@@ -1081,30 +1078,34 @@ export async function buildEntityRecallSection(options: BuildEntityRecallSection
1081
1078
  options.namespaceStorage,
1082
1079
  )
1083
1080
  : undefined;
1084
- const namespaceCacheKey =
1085
- resolvedStorages && options.recallNamespaces && nativeChunks
1086
- ? namespaceEntityIndexCacheKey(
1087
- resolvedStorages,
1088
- options.recallNamespaces,
1089
- nativeEntityIndexRevision(nativeChunks),
1090
- )
1091
- : undefined;
1092
- let index: EntityMentionIndex | undefined = namespaceCacheKey
1093
- ? namespaceEntityIndexCache.get(namespaceCacheKey)
1094
- : undefined;
1095
- checkEntityRecallAbort(options.abortSignal);
1096
- if (!index) {
1097
- index = await buildEntityMentionIndex(
1081
+ if (!nativeChunks) {
1082
+ nativeChunks = await readNativeChunks(options.config, options.recallNamespaces);
1083
+ }
1084
+ const indexStorages = resolvedStorages ?? [options.storage];
1085
+ const index = await resolveEntityMentionIndex({
1086
+ scopeKey: entityMentionScopeKey(options.recallNamespaces, indexStorages, nativeEntityIndexRevision(nativeChunks)),
1087
+ currentIdentity: () => entityMentionIdentity(indexStorages),
1088
+ abortSignal: options.abortSignal,
1089
+ buildFull: (signal) => buildEntityMentionIndex(
1098
1090
  options.storage,
1099
1091
  options.config,
1100
1092
  options.recallNamespaces,
1101
1093
  options.namespaceStorage,
1102
1094
  nativeChunks,
1103
- resolvedStorages,
1104
- options.abortSignal,
1105
- );
1106
- if (namespaceCacheKey) rememberNamespaceEntityIndex(namespaceCacheKey, index);
1107
- }
1095
+ indexStorages,
1096
+ signal,
1097
+ ),
1098
+ rebuildEntities: (previous, signal) => buildEntityMentionIndex(
1099
+ options.storage,
1100
+ options.config,
1101
+ options.recallNamespaces,
1102
+ options.namespaceStorage,
1103
+ nativeChunks,
1104
+ indexStorages,
1105
+ signal,
1106
+ carriedMemorySnippets(previous),
1107
+ ),
1108
+ });
1108
1109
  // Candidate resolution is another synchronous pass over every entity and alias,
1109
1110
  // reached even on a cache hit that did no I/O at all — so yield here too, or a
1110
1111
  // cached corpus could still hold the loop past the section budget (issue #2291).
@@ -589,7 +589,7 @@ async function loadAuthoritativeProjectionSnapshot(options: {
589
589
  const storage = assertInjectedStorageRooted("rebuildMemoryProjection", options.memoryDir, options.storage) ?? new StorageManager(options.memoryDir);
590
590
  // Force a fresh disk read — projection verify/rebuild must see the true
591
591
  // on-disk state, not a potentially stale in-process cache.
592
- storage.invalidateAllMemoriesCacheForDir();
592
+ storage.invalidateAllMemoriesCacheForDir({ indexedText: false });
593
593
  const selected = selectProjectionMemories([
594
594
  await storage.readAllMemories(),
595
595
  await storage.readAllColdMemories(),
@@ -1,5 +1,6 @@
1
1
  import { appendFileSync, mkdirSync } from "node:fs";
2
2
  import path from "node:path";
3
+ import { entityMentionEpoch } from "./entity-mention-epoch.js";
3
4
  import { deleteInFlightReadsForDir } from "./in-flight-reads.js";
4
5
  import { invalidateDerivedAndGlobalForDir } from "./memory-cache.js";
5
6
 
@@ -17,10 +18,16 @@ export const MEMORY_CORPUS_VERSION_SENTINEL = ".memory-corpus-version.log";
17
18
  * — review approval, governance restore, migration. Appends one byte to the
18
19
  * on-disk sentinel so any process's version-keyed hot-memories cache rescans
19
20
  * on its next read (StorageManager.readSharedVersion reads the file size, so
20
- * the growth is observed immediately). Fail-open: a bump failure must never
21
- * crash the caller — a stale-cache window self-heals on the next real write.
21
+ * the growth is observed immediately). The same call moves the entity-mention
22
+ * epoch, unless the caller passes `{ indexedText: false }` for a metadata
23
+ * stamp that cannot change snippet text, entityRef, or origin. Fail-open: a
24
+ * bump failure must never crash the caller — a stale-cache window self-heals
25
+ * on the next real write.
22
26
  */
23
- export function bumpMemoryCorpusVersionForDir(memoryDir: string): void {
27
+ export function bumpMemoryCorpusVersionForDir(
28
+ memoryDir: string,
29
+ options?: { indexedText?: boolean },
30
+ ): void {
24
31
  try {
25
32
  const stateDir = path.join(memoryDir, "state");
26
33
  mkdirSync(stateDir, { recursive: true });
@@ -28,6 +35,9 @@ export function bumpMemoryCorpusVersionForDir(memoryDir: string): void {
28
35
  } catch {
29
36
  /* fail-open: never let a cache-coherence bump crash the primary operation */
30
37
  }
38
+ if (options?.indexedText !== false && !entityMentionEpoch.suppressed()) {
39
+ entityMentionEpoch.bump(memoryDir);
40
+ }
31
41
  // Drop any in-flight readAllMemories scan for this dir (Cursor Medium, #1902):
32
42
  // after the sentinel advances, a concurrent read that missed the version-keyed
33
43
  // hot entry must not attach to a scan that began BEFORE this out-of-band write
@@ -558,8 +558,10 @@ export async function runPreferenceDriftScan(
558
558
  // shared corpus version out of band — otherwise hot recall caches keep
559
559
  // serving pre-stamp frontmatter and the damping stage never sees the new
560
560
  // `driftState` (§25/§31).
561
- bumpMemoryCorpusVersionForDir(storage.dir);
562
- if (storage.dir !== deps.memoryDir) bumpMemoryCorpusVersionForDir(deps.memoryDir);
561
+ bumpMemoryCorpusVersionForDir(storage.dir, { indexedText: false });
562
+ if (storage.dir !== deps.memoryDir) {
563
+ bumpMemoryCorpusVersionForDir(deps.memoryDir, { indexedText: false });
564
+ }
563
565
  }
564
566
  try {
565
567
  await writeJsonFileAtomic(driftMarkerPath(deps.memoryDir), {
@@ -640,7 +642,7 @@ export async function resolvePreferenceDrift(
640
642
  { lastCorroborated: nowIso, driftState: undefined, updated: nowIso },
641
643
  { at: now, actor: DRIFT_ACTOR, reasonCode: "drift_kept", ruleVersion: DRIFT_RULE_VERSION },
642
644
  );
643
- bumpMemoryCorpusVersionForDir(storage.dir);
645
+ bumpMemoryCorpusVersionForDir(storage.dir, { indexedText: false });
644
646
  touched();
645
647
  return { affectedIds: [preferenceId], message: `Preference ${preferenceId} confirmed still current` };
646
648
  }
@@ -526,9 +526,10 @@ export async function runProcedureLibraryMaintenance(options: {
526
526
  report.appliedCount = report.applied.length;
527
527
 
528
528
  if (writes > 0) {
529
- // Out-of-band corpus bump + marker so hot caches and the stats surface
530
- // observe the pass (§25/§31 — direct writes must not leave stale views).
531
- bumpMemoryCorpusVersionForDir(options.memoryDir);
529
+ // Corpus bump so hot caches observe the pass. Archive already moved the
530
+ // mention epoch when it removed the live file. Merge-supersede and repair
531
+ // stamps are metadata-only, so this bump must not move the epoch.
532
+ bumpMemoryCorpusVersionForDir(options.memoryDir, { indexedText: false });
532
533
  }
533
534
  try {
534
535
  await writeJsonFileAtomic(maintenanceMarkerPath(options.memoryDir), {
@@ -610,7 +610,7 @@ export async function promoteSemanticRuleFromMemory(options: {
610
610
  };
611
611
 
612
612
  return withPromotionLock(options.memoryDir, ruleKey, async (lock) => {
613
- storage.invalidateAllMemoriesCacheForDir();
613
+ storage.invalidateAllMemoriesCacheForDir({ indexedText: false });
614
614
  const existingRule = (await storage.readAllMemories()).find(
615
615
  (memory) =>
616
616
  memory.frontmatter.category === "rule" &&
@@ -0,0 +1,44 @@
1
+ import { entityMentionEpoch } from "../entity-mention-epoch.js";
2
+ import type { MemoryFile, MemoryFrontmatter } from "../types.js";
3
+ import {
4
+ buildCapturePathLockIdentity,
5
+ buildExplicitCaptureDedupKey,
6
+ } from "./tombstone-blocked-capture-mutation.js";
7
+
8
+ type MentionHoldStorage = {
9
+ withTombstoneBlockedCaptureWriteLock<T>(
10
+ task: () => Promise<T>,
11
+ identity?: readonly string[],
12
+ ): Promise<T>;
13
+ readMemoryByPath(pathname: string): Promise<MemoryFile | null>;
14
+ };
15
+
16
+ /**
17
+ * Metadata frontmatter writes may suppress the mention epoch only when the
18
+ * file still has the same body, entityRef, and origin while the path lock is
19
+ * held. The write runs in that same lock so a concurrent body or entityRef
20
+ * change cannot be overwritten under suppression.
21
+ */
22
+ export async function holdMentionNeutralFrontmatter<T>(
23
+ storage: MentionHoldStorage,
24
+ memory: MemoryFile,
25
+ updated: MemoryFrontmatter,
26
+ write: () => Promise<T>,
27
+ ): Promise<T | undefined> {
28
+ const identities = [
29
+ buildCapturePathLockIdentity(memory.path),
30
+ buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector),
31
+ buildExplicitCaptureDedupKey(memory.content, updated.category, updated.sourceConnector),
32
+ ];
33
+ return storage.withTombstoneBlockedCaptureWriteLock(async () => {
34
+ const persisted = await storage.readMemoryByPath(memory.path);
35
+ if (
36
+ !persisted ||
37
+ persisted.content !== memory.content ||
38
+ !entityMentionEpoch.neutral(persisted.frontmatter, updated)
39
+ ) {
40
+ return undefined;
41
+ }
42
+ return write();
43
+ }, identities);
44
+ }
package/src/storage.ts CHANGED
@@ -87,6 +87,8 @@ import {
87
87
  deleteInFlightReadsForDir,
88
88
  clearInFlightReads,
89
89
  } from "./in-flight-reads.js";
90
+ import { entityMentionEpoch } from "./entity-mention-epoch.js";
91
+ import { holdMentionNeutralFrontmatter } from "./storage/entity-mention-frontmatter-hold.js";
90
92
  import * as archive from "./archive-mutation-version.js";
91
93
  import { rotateMarkdownFileToArchive } from "./hygiene.js";
92
94
  import { sanitizeMemoryContent } from "./sanitize.js";
@@ -2419,11 +2421,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
2419
2421
  * Bump only — NOT invalidateAllForDir (that would drop the very hot
2420
2422
  * entry the write path just patched).
2421
2423
  */
2422
- protected bumpMemoryCorpusVersion(): void {
2424
+ protected bumpMemoryCorpusVersion(opts?: { indexedText?: boolean }): void {
2423
2425
  this.bumpSharedVersion("memory-corpus", StorageManager.memoryCorpusVersionByDir);
2424
- // Drop the in-flight readAllMemories slot (#1902): a concurrent read
2425
- // must not attach to a scan that began BEFORE this mutation. (The patch
2426
- // path uses bumpMemoryCorpusVersionExclusive and clears the slot itself.)
2426
+ if (opts?.indexedText !== false && !entityMentionEpoch.suppressed()) entityMentionEpoch.bump(this.baseDir);
2427
2427
  deleteInFlightReadsForDir(this.baseDir);
2428
2428
  }
2429
2429
  getArchiveMutationVersion(): number { return archive.getArchiveMutationVersionForDir(this.baseDir); }
@@ -2434,7 +2434,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
2434
2434
  * patchHotMemoriesCache refuse to re-key its locally patched corpus at a
2435
2435
  * version already reflecting a peer's still-unread concurrent append.
2436
2436
  */
2437
- private bumpMemoryCorpusVersionExclusive(): { produced: number; exclusive: boolean } {
2437
+ private bumpMemoryCorpusVersionExclusive(opts?: { indexedText?: boolean }): { produced: number; exclusive: boolean } {
2438
2438
  const filePath = this.versionFilePath("memory-corpus");
2439
2439
  try {
2440
2440
  mkdirSync(this.stateDir, { recursive: true });
@@ -2447,11 +2447,13 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
2447
2447
  appendFileSync(filePath, "x");
2448
2448
  const produced = statSync(filePath).size;
2449
2449
  StorageManager.memoryCorpusVersionByDir.set(this.baseDir, produced);
2450
+ if (opts?.indexedText !== false && !entityMentionEpoch.suppressed()) entityMentionEpoch.bump(this.baseDir);
2450
2451
  // Exclusive iff exactly our single byte landed between the two stats.
2451
2452
  return { produced, exclusive: produced === before + 1 };
2452
2453
  } catch {
2453
2454
  const next = (StorageManager.memoryCorpusVersionByDir.get(this.baseDir) ?? 0) + 1;
2454
2455
  StorageManager.memoryCorpusVersionByDir.set(this.baseDir, next);
2456
+ if (opts?.indexedText !== false && !entityMentionEpoch.suppressed()) entityMentionEpoch.bump(this.baseDir);
2455
2457
  return { produced: next, exclusive: true };
2456
2458
  }
2457
2459
  }
@@ -4417,8 +4419,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4417
4419
  /** Invalidate the readAllMemories() cache after writes that add/remove memories. */
4418
4420
  /** Public cache invalidation for callers that need authoritative disk reads
4419
4421
  * (e.g. projection verify/rebuild). */
4420
- invalidateAllMemoriesCacheForDir(): void {
4421
- this.invalidateAllMemoriesCache();
4422
+ invalidateAllMemoriesCacheForDir(opts?: { indexedText?: boolean }): void {
4423
+ this.invalidateAllMemoriesCache(opts);
4422
4424
  }
4423
4425
 
4424
4426
  /** Invalidate only the cache layers affected by direct tier file deletes. */
@@ -4443,9 +4445,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4443
4445
  }
4444
4446
  }
4445
4447
 
4446
- /** Clear ALL static caches. Use in tests that write files directly
4447
- * (bypassing StorageManager.writeMemory) to avoid stale reads. */
4448
+ /** Clear every static cache. Direct file writes call this so the next read is not stale. */
4448
4449
  static clearAllStaticCaches(): void {
4450
+ entityMentionEpoch.reset();
4449
4451
  clearInFlightReads();
4450
4452
  StorageManager.questionsCache.clear();
4451
4453
  StorageManager.coldMemoriesCache.clear(); // also wipe the cold-scan TTL cache
@@ -4475,12 +4477,12 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
4475
4477
  * exclusively by invalidateColdMemoriesCache(), which is called only when
4476
4478
  * cold content actually changes (hot→cold demotions, writeMemoryFileAtomic
4477
4479
  * inside cold/, archiveMemory, etc.). */
4478
- protected invalidateAllMemoriesCache(): void {
4480
+ protected invalidateAllMemoriesCache(opts?: { indexedText?: boolean }): void {
4479
4481
  deleteInFlightReadsForDir(this.baseDir);
4480
4482
  // Bulk/ambiguous mutations drop the hot layer wholesale (below); bump the
4481
4483
  // corpus sentinel too so PEER processes rescan instead of serving a warm
4482
4484
  // pre-mutation corpus entry (issue #1902 cross-process coherence).
4483
- this.bumpMemoryCorpusVersion();
4485
+ this.bumpMemoryCorpusVersion(opts);
4484
4486
  // Invalidation chokepoint (issue #1535 / #1904): evict the layers a
4485
4487
  // memory-mutate can affect — hot, archive, derived episode/rule views, and
4486
4488
  // both QMD result caches (qmdSearchCache and qmdRecallCache). Before the QMD
@@ -5356,18 +5358,16 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
5356
5358
  [buildCapturePathLockIdentity(expected.path), oldIdentity, newIdentity]
5357
5359
  );
5358
5360
  }
5359
- /**
5360
- * Update frontmatter fields without changing memory content. Returns false when the memory is not found.
5361
- */
5361
+ /** Frontmatter patch. A replaced body still moves the mention epoch. */
5362
+ private static readonly frontmatterBody = StorageManager.prototype.writeMemoryFrontmatter;
5362
5363
  async writeMemoryFrontmatter(
5363
5364
  memory: MemoryFile,
5364
5365
  patch: Partial<MemoryFrontmatter>,
5365
- lifecycle?: MemoryLifecycleEventWriteOptions
5366
+ lifecycle?: MemoryLifecycleEventWriteOptions,
5367
+ mentionReentry?: boolean
5366
5368
  ): Promise<boolean> {
5367
5369
  const beforeStatus = memory.frontmatter.status ?? "active";
5368
- // Canonicalize the EFFECTIVE merged entityRef (issue #2213) — an
5369
- // unrelated patch must not rewrite an inherited legacy ref back out.
5370
- // #2807: `patch.updated` is business time, persisted VERBATIM.
5370
+ // Canonicalize merged entityRef (#2213). patch.updated is business time (#2807).
5371
5371
  const resolveIds = this.currentHistoricalIds();
5372
5372
  const updated: MemoryFrontmatter = entityRefs.canonicalizeEntityRefOption(
5373
5373
  { ...memory.frontmatter, ...patch },
@@ -5375,6 +5375,11 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
5375
5375
  );
5376
5376
  const refIds = typeof updated.entityRef === "string" ? resolveIds : null;
5377
5377
  const afterStatus = updated.status ?? "active";
5378
+ if (!mentionReentry && entityMentionEpoch.neutral(memory.frontmatter, updated)) {
5379
+ const held = await holdMentionNeutralFrontmatter(this, memory, updated, () =>
5380
+ entityMentionEpoch.hold(() => StorageManager.frontmatterBody.call(this, memory, patch, lifecycle, true)));
5381
+ if (held !== undefined) return held;
5382
+ }
5378
5383
 
5379
5384
  const fileContent = `${serializeFrontmatter(this.withOkfType(updated))}\n\n${memory.content}\n`;
5380
5385
  await this.writeTombstoneBlockedFrontmatter(memory, fileContent, updated, async () => {
@@ -5390,6 +5395,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
5390
5395
  onFailRestore: memory,
5391
5396
  });
5392
5397
  }
5398
+ if (mentionReentry && !entityMentionEpoch.neutral(memory.frontmatter, updated)) entityMentionEpoch.bump(this.baseDir);
5393
5399
  await this.patchHotMemoriesCache({ addedPath: memory.path });
5394
5400
  if (memory.path.includes(`${path.sep}cold${path.sep}`)) {
5395
5401
  this.invalidateColdMemoriesCache();
@@ -5422,9 +5428,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
5422
5428
  lifecycle?.ruleVersion
5423
5429
  );
5424
5430
  if (beforeStatus !== afterStatus) {
5425
- // Status/lifecycle change must bump memory-status so the version-keyed
5426
- // entity/derived caches and peer processes observe it (issue #1902:
5427
- // restored — corpus bump alone doesn't cover status-derived views).
5431
+ // Status change bumps memory-status; a corpus bump does not cover it (#1902).
5428
5432
  this.bumpMemoryStatusVersion();
5429
5433
  }
5430
5434
  return true;
@@ -6496,11 +6500,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
6496
6500
  const memoryMap = new Map(memories.map((m) => [m.frontmatter.id, m]));
6497
6501
  const memoryPathMap = new Map(memories.map((m) => [path.resolve(m.path), m]));
6498
6502
  let updated = 0;
6499
- // Capture the corpus version + warmth BEFORE writing so we can patch the hot
6500
- // entries in place and then re-key them to the version this flush produces
6501
- // (issue #1902). Access-tracking flush is batched (consolidation / buffer
6502
- // full), not per-recall, so the single corpus bump below is cheap.
6503
+ // Corpus version and warmth are snapshotted before the writes (#1902).
6503
6504
  const prevVersion = this.getMemoryCorpusVersion();
6505
+ let entityRefMoved = false;
6504
6506
  // Snapshot the secure-store key identity once (Cursor Medium #1902), mirroring
6505
6507
  // readAllMemories/patchHotMemoriesCache: a mid-flush setSecureStoreKey change
6506
6508
  // would otherwise let loop patches or the re-keyed entry be stored under a
@@ -6540,6 +6542,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
6540
6542
  { onFailRestore: current }
6541
6543
  );
6542
6544
  }
6545
+ if (!entityMentionEpoch.neutral(current.frontmatter, newFm)) entityRefMoved = true;
6543
6546
  return { current, newFm };
6544
6547
  });
6545
6548
  if (applied === null) continue;
@@ -6557,14 +6560,11 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
6557
6560
  }
6558
6561
 
6559
6562
  if (updated > 0) {
6560
- // Advance the corpus sentinel so PEER processes rescan and don't overwrite
6561
- // this process's increments (Codex P2): WorkspaceOpsCoordinator computes
6562
- // existingCount + update.count from the cached value, so a peer serving a
6563
- // stale count would undercount. Re-key the locally patched entries to the
6564
- // produced version so this process stays warm — only when our bump was
6565
- // exclusive and still the current sentinel; otherwise a peer also wrote
6566
- // and we must let the next read rescan.
6567
- const { produced, exclusive } = this.bumpMemoryCorpusVersionExclusive();
6563
+ // Advance the corpus sentinel so peers rescan (#1902). Re-key local patches
6564
+ // only when this bump was exclusive; a peer write must rescan instead.
6565
+ const { produced, exclusive } = this.bumpMemoryCorpusVersionExclusive({
6566
+ indexedText: entityRefMoved ? undefined : false,
6567
+ });
6568
6568
  // Drop the in-flight read slot after the bump (parity with
6569
6569
  // patchHotMemoriesCache, Cursor Medium #1902): a readAllMemories scan that
6570
6570
  // started before the flush would otherwise keep awaiting a pre-flush scan
@@ -6574,6 +6574,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
6574
6574
  if (
6575
6575
  warm &&
6576
6576
  exclusive &&
6577
+ !entityRefMoved &&
6577
6578
  produced === prevVersion + 1 &&
6578
6579
  this.getMemoryCorpusVersion() === produced &&
6579
6580
  this.hotCacheKeyId() === keyId
@@ -7010,6 +7011,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
7010
7011
  });
7011
7012
  if (!written) return false;
7012
7013
  exactReplay = written === "exact-replay";
7014
+ const mentionBefore = { entityRef: currentBefore.frontmatter.entityRef, origin: currentBefore.frontmatter.origin };
7013
7015
  if (exactReplay && typeof updatedFm.entityRef === "string") {
7014
7016
  await this.entityRefRepair.repair(
7015
7017
  currentBefore.path,
@@ -7020,6 +7022,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
7020
7022
  { onFailRestore: currentBefore },
7021
7023
  );
7022
7024
  }
7025
+ const mentionUnchanged = entityMentionEpoch.neutral(mentionBefore, updatedFm);
7023
7026
  const supersededAt = updatedFm.supersededAt ?? initialNow;
7024
7027
  const beforeState = this.summarizeLifecycleState(
7025
7028
  currentBefore.frontmatter,
@@ -7041,8 +7044,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
7041
7044
  readMemoryByPath: (filePath) => this.readMemoryByPath(filePath),
7042
7045
  isColdOrArchiveTierPath: (memoryPath) => this.isColdOrArchiveTierPath(memoryPath),
7043
7046
  invalidateColdMemoriesCache: () => this.invalidateColdMemoriesCache(),
7044
- invalidateAllMemoriesCache: () => this.invalidateAllMemoriesCache(),
7045
- bumpMemoryCorpusVersion: () => this.bumpMemoryCorpusVersion(),
7047
+ invalidateAllMemoriesCache: () => this.invalidateAllMemoriesCache({ indexedText: mentionUnchanged ? false : undefined }),
7048
+ bumpMemoryCorpusVersion: () => this.bumpMemoryCorpusVersion({ indexedText: mentionUnchanged ? false : undefined }),
7046
7049
  appendLifecycleEvent: async () => {
7047
7050
  let lifecycleEvents: MemoryLifecycleEvent[] = [];
7048
7051
  try {
@@ -7156,9 +7159,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
7156
7159
  return true;
7157
7160
  });
7158
7161
  if (!written) continue;
7159
- // Per-file corpus bump BEFORE the awaited lifecycle append (#1902):
7160
- // the end-of-loop status bump fires only after the whole batch.
7161
- this.bumpMemoryCorpusVersion();
7162
+ // Corpus bump before the lifecycle append (#1902). Epoch moves only if repair changed entityRef or origin.
7163
+ this.bumpMemoryCorpusVersion({ indexedText: entityMentionEpoch.neutral(currentBefore.frontmatter, updatedFm) ? false : undefined });
7162
7164
  await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.archiveMemories", {
7163
7165
  memoryId: id,
7164
7166
  eventType: "archived",