@remnic/core 9.69.102 → 9.69.104

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 (366) hide show
  1. package/dist/access-admin-ops-surface.js +53 -52
  2. package/dist/access-authorization-probe.js +54 -53
  3. package/dist/access-boundary.js +53 -52
  4. package/dist/access-cli.js +153 -150
  5. package/dist/access-cli.js.map +1 -1
  6. package/dist/access-extraction-force-flush.js +53 -52
  7. package/dist/access-http-briefing.js +54 -53
  8. package/dist/access-http-query.js +55 -54
  9. package/dist/access-http.js +77 -76
  10. package/dist/access-identity-continuity-surface.js +53 -52
  11. package/dist/access-lcm-surface.js +53 -52
  12. package/dist/access-mcp.js +66 -65
  13. package/dist/access-namespace-preflight.js +53 -52
  14. package/dist/access-observe-write-category.js +9 -9
  15. package/dist/access-observe-write-surface.js +53 -52
  16. package/dist/access-offline-manifest.js +6 -6
  17. package/dist/access-operations-batch.js +57 -56
  18. package/dist/access-operations.js +64 -63
  19. package/dist/access-recall-concurrency.js +53 -52
  20. package/dist/access-recall-response.js +53 -52
  21. package/dist/access-recall-surface.js +53 -52
  22. package/dist/access-schema.js +8 -8
  23. package/dist/access-service.js +53 -52
  24. package/dist/access-surface-catalog.js +55 -54
  25. package/dist/access-surface-catalog.js.map +1 -1
  26. package/dist/active-recall.js +21 -20
  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-47LTKAGL.js → chunk-2COXDQ6C.js} +21 -6
  43. package/dist/chunk-2COXDQ6C.js.map +1 -0
  44. package/dist/{chunk-IOYY6ROU.js → chunk-2MMBJE53.js} +7 -7
  45. package/dist/{chunk-QSXEQPC4.js → chunk-2OFDC3AK.js} +2 -2
  46. package/dist/{chunk-4MP2Q2EI.js → chunk-2TCNEJ36.js} +4 -4
  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-SBOCJ47I.js → chunk-5R4YPRAR.js} +19 -6
  51. package/dist/chunk-5R4YPRAR.js.map +1 -0
  52. package/dist/{chunk-ANPRNTPT.js → chunk-63LFM77Z.js} +15 -2
  53. package/dist/chunk-63LFM77Z.js.map +1 -0
  54. package/dist/{chunk-DINOVGA5.js → chunk-6WRHNA4I.js} +48 -48
  55. package/dist/chunk-6WRHNA4I.js.map +1 -0
  56. package/dist/{chunk-DSPWVQZ6.js → chunk-73R4HOOK.js} +3 -3
  57. package/dist/chunk-7CT3AI52.js +84 -0
  58. package/dist/chunk-7CT3AI52.js.map +1 -0
  59. package/dist/{chunk-PHBH74Q6.js → chunk-7NBUE5VY.js} +2 -2
  60. package/dist/{chunk-S533KGX6.js → chunk-7X7A7KVN.js} +2 -2
  61. package/dist/{chunk-XEATJBNB.js → chunk-7YQR6XHY.js} +71 -71
  62. package/dist/{chunk-F5MINS3R.js → chunk-A6WNTVA7.js} +3 -3
  63. package/dist/{chunk-F5MINS3R.js.map → chunk-A6WNTVA7.js.map} +1 -1
  64. package/dist/{chunk-533RHNHB.js → chunk-ATFFWIKL.js} +2 -2
  65. package/dist/{chunk-QCYUIZK2.js → chunk-AU43D3IT.js} +7 -7
  66. package/dist/{chunk-JZ66PN72.js → chunk-AWSP3WNM.js} +2 -2
  67. package/dist/{chunk-IY47KPML.js → chunk-B3OFQP5Y.js} +6 -6
  68. package/dist/{chunk-4DXNOVNW.js → chunk-B454SHPU.js} +6 -6
  69. package/dist/{chunk-3LMOJXZR.js → chunk-BKVCTTEN.js} +2 -2
  70. package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
  71. package/dist/{chunk-CZN53OT5.js → chunk-CGBE3N7T.js} +2 -2
  72. package/dist/{chunk-HWLIMQE2.js → chunk-CHDGJJV6.js} +2 -2
  73. package/dist/{chunk-UYPQOASU.js → chunk-CNBGAFDY.js} +2 -2
  74. package/dist/{chunk-JTC3PBNH.js → chunk-DB6XFMOF.js} +3 -3
  75. package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
  76. package/dist/{chunk-UFIZRMQZ.js → chunk-E4XLFYHV.js} +61 -29
  77. package/dist/chunk-E4XLFYHV.js.map +1 -0
  78. package/dist/{chunk-4OXXDL3X.js → chunk-EWWK65CI.js} +2 -2
  79. package/dist/{chunk-DFPG47ET.js → chunk-EZ3F4YIN.js} +11 -11
  80. package/dist/{chunk-V3I4NLZP.js → chunk-FYX2LXIX.js} +2 -2
  81. package/dist/{chunk-PNVOKGAX.js → chunk-G5I5ZT6O.js} +2 -2
  82. package/dist/{chunk-7Z2UKRNJ.js → chunk-GQ6ZF4LD.js} +2 -2
  83. package/dist/{chunk-PEZAHDDU.js → chunk-GZ2KV2RO.js} +5 -5
  84. package/dist/chunk-H6Z3RNW3.js +330 -0
  85. package/dist/chunk-H6Z3RNW3.js.map +1 -0
  86. package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
  87. package/dist/{chunk-AXHU56C3.js → chunk-HWGFTTPS.js} +2 -2
  88. package/dist/{chunk-2SGLMP2E.js → chunk-IDSZNHIP.js} +2 -2
  89. package/dist/{chunk-LO4UKPKX.js → chunk-IFCKHPY4.js} +2 -2
  90. package/dist/{chunk-ZHXSOMYH.js → chunk-IOMLGJHG.js} +4 -4
  91. package/dist/{chunk-YRULQW5J.js → chunk-ISX7GVZE.js} +4 -4
  92. package/dist/{chunk-5PHZSD75.js → chunk-J3ZJBX7I.js} +2 -2
  93. package/dist/{chunk-X5TAWDRO.js → chunk-JC7QQ2DJ.js} +2 -2
  94. package/dist/{chunk-RFQT7IZO.js → chunk-JRDL3FGM.js} +3 -3
  95. package/dist/{chunk-OGZC2WUX.js → chunk-JWFTPRYD.js} +2 -2
  96. package/dist/{chunk-7OA4JWP5.js → chunk-KFHEZJQZ.js} +2 -2
  97. package/dist/{chunk-P3WRFSYF.js → chunk-KMN2JE4D.js} +2 -2
  98. package/dist/{chunk-GNBZB43J.js → chunk-KV7LFOPF.js} +2 -2
  99. package/dist/{chunk-L6YCGS7I.js → chunk-KYUVFCT2.js} +2 -2
  100. package/dist/{chunk-5BVL2XUD.js → chunk-LHDSCWDK.js} +17 -17
  101. package/dist/{chunk-SF7CWSR6.js → chunk-LXJZUNLX.js} +2 -2
  102. package/dist/{chunk-XCB6QDL6.js → chunk-M5GVCJSJ.js} +8 -2
  103. package/dist/chunk-M5GVCJSJ.js.map +1 -0
  104. package/dist/{chunk-DGS34QSY.js → chunk-MFZACYZ6.js} +2 -2
  105. package/dist/{chunk-PYYQCHJC.js → chunk-MZOQ26LH.js} +2 -2
  106. package/dist/{chunk-R2TVWQLR.js → chunk-NT2Q6AHC.js} +2 -2
  107. package/dist/{chunk-ABGM2BN4.js → chunk-O43AAJFQ.js} +2 -2
  108. package/dist/{chunk-G3BW6SDE.js → chunk-OFGTRDWX.js} +3 -3
  109. package/dist/{chunk-6Z263Z3E.js → chunk-OWV6LKNZ.js} +2 -2
  110. package/dist/{chunk-HIHN4XHL.js → chunk-PQ7EB5AD.js} +3 -3
  111. package/dist/{chunk-VVYLVE6R.js → chunk-PQKZLBCJ.js} +1 -1
  112. package/dist/{chunk-OFFW73OZ.js → chunk-PRNR47DI.js} +2 -2
  113. package/dist/{chunk-UL7KDVLC.js → chunk-PZ2QQULY.js} +11 -11
  114. package/dist/{chunk-55NZVUNI.js → chunk-QAIVTLL2.js} +3 -3
  115. package/dist/{chunk-M7T2NKET.js → chunk-QKCGOECM.js} +2 -2
  116. package/dist/{chunk-ARY6O3JN.js → chunk-QUNCLKHM.js} +2 -2
  117. package/dist/{chunk-Z3O53WZR.js → chunk-QWUJWBZ5.js} +2 -2
  118. package/dist/{chunk-RZXFXKWY.js → chunk-QX3K3INR.js} +2 -2
  119. package/dist/{chunk-HFOY2SBG.js → chunk-RYN4AIRM.js} +7 -5
  120. package/dist/chunk-RYN4AIRM.js.map +1 -0
  121. package/dist/{chunk-4LNCLZNK.js → chunk-S2GR3T7W.js} +2 -2
  122. package/dist/{chunk-VPOAMPDD.js → chunk-SFDBLCZV.js} +59 -59
  123. package/dist/{chunk-2IWBHZ3H.js → chunk-SIGDETMB.js} +2 -2
  124. package/dist/chunk-SP4DB6SX.js +19 -0
  125. package/dist/chunk-SP4DB6SX.js.map +1 -0
  126. package/dist/{chunk-DBWNTLLI.js → chunk-STQC2FYQ.js} +6 -6
  127. package/dist/{chunk-S5R77WHV.js → chunk-SWXPR5SE.js} +2 -2
  128. package/dist/{chunk-3DNTFZAT.js → chunk-T32YYUT2.js} +2 -2
  129. package/dist/{chunk-W5JQSVPZ.js → chunk-T6RQNV7E.js} +2 -2
  130. package/dist/{chunk-O23QMYN4.js → chunk-TRN56SPQ.js} +2 -2
  131. package/dist/{chunk-NM3BT2OH.js → chunk-TXTRHF3B.js} +2 -2
  132. package/dist/{chunk-7AUIGOH4.js → chunk-VDVHIRCK.js} +2 -2
  133. package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
  134. package/dist/{chunk-ZGYLPCWN.js → chunk-VO2BNAA2.js} +3 -3
  135. package/dist/{chunk-WVEECBCG.js → chunk-VV2MVHCJ.js} +2 -2
  136. package/dist/{chunk-HVDEKJJR.js → chunk-VYIBRCVW.js} +70 -61
  137. package/dist/chunk-VYIBRCVW.js.map +1 -0
  138. package/dist/{chunk-B3O3IRZN.js → chunk-VZBW6CDG.js} +1 -1
  139. package/dist/{chunk-JPKK466T.js → chunk-W5XWHTDO.js} +2 -2
  140. package/dist/{chunk-CYG3K2T2.js → chunk-WR66CTVU.js} +6 -6
  141. package/dist/{chunk-JNAOTN7B.js → chunk-XLWMNDQR.js} +4 -4
  142. package/dist/chunk-XLWMNDQR.js.map +1 -0
  143. package/dist/{chunk-RP7RSENW.js → chunk-XQWKB6PY.js} +2 -2
  144. package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
  145. package/dist/{chunk-UCPGOUDZ.js → chunk-Y57GJDG5.js} +108 -3
  146. package/dist/chunk-Y57GJDG5.js.map +1 -0
  147. package/dist/{chunk-KYLYXGKD.js → chunk-YI6XIFQV.js} +2 -2
  148. package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
  149. package/dist/{chunk-Y7427SL7.js → chunk-Z6EZCN6Z.js} +10 -10
  150. package/dist/{chunk-5K2ALMSZ.js → chunk-ZRUC7FD3.js} +2 -2
  151. package/dist/cli.js +103 -102
  152. package/dist/coding/export-okf-codegraph.js +13 -12
  153. package/dist/coding/export-okf-codegraph.js.map +1 -1
  154. package/dist/coding/pre-action-gate.js +2 -2
  155. package/dist/compounding/engine.js +14 -13
  156. package/dist/config.js +21 -20
  157. package/dist/connectors/codex-materialize-runner.js +14 -13
  158. package/dist/connectors/index.js +14 -13
  159. package/dist/consolidation-provenance-check.js +4 -3
  160. package/dist/consolidation-undo.js +3 -2
  161. package/dist/consolidation-undo.js.map +1 -1
  162. package/dist/contradiction/index.js +3 -2
  163. package/dist/convergence-refresh.js +16 -15
  164. package/dist/corpus-watermark.js +16 -15
  165. package/dist/dashboard-runtime.js +2 -2
  166. package/dist/deep-recall-http-glue.js +9 -9
  167. package/dist/deep-recall.js +2 -2
  168. package/dist/embedding-fallback.d.ts +2 -1
  169. package/dist/embedding-fallback.d.ts.map +1 -1
  170. package/dist/embedding-fallback.js +2 -2
  171. package/dist/embedding-index-storage.d.ts +28 -1
  172. package/dist/embedding-index-storage.d.ts.map +1 -1
  173. package/dist/embedding-index-storage.js +1 -1
  174. package/dist/entity-mention-carry.d.ts +16 -0
  175. package/dist/entity-mention-carry.d.ts.map +1 -0
  176. package/dist/entity-mention-carry.js +8 -0
  177. package/dist/entity-mention-epoch.d.ts +22 -0
  178. package/dist/entity-mention-epoch.d.ts.map +1 -0
  179. package/dist/entity-mention-epoch.js +8 -0
  180. package/dist/entity-mention-index-cache.d.ts +28 -0
  181. package/dist/entity-mention-index-cache.d.ts.map +1 -0
  182. package/dist/entity-mention-index-cache.js +29 -0
  183. package/dist/entity-mention-index-cache.js.map +1 -0
  184. package/dist/entity-retrieval.d.ts.map +1 -1
  185. package/dist/entity-retrieval.js +18 -15
  186. package/dist/event-order-recall.js +2 -2
  187. package/dist/external-wiki-access.js +54 -53
  188. package/dist/extraction-span-materialize.js +2 -2
  189. package/dist/extraction.js +21 -20
  190. package/dist/{graph-edge-decay-MKIRZ762.js → graph-edge-decay-PVKDKLQK.js} +6 -6
  191. package/dist/graph-edge-decay-PVKDKLQK.js.map +1 -0
  192. package/dist/graph-jsonl.js +4 -4
  193. package/dist/graph-snapshot.js +4 -4
  194. package/dist/graph.js +5 -5
  195. package/dist/harmonic-retrieval.js +1 -1
  196. package/dist/index.js +187 -184
  197. package/dist/lcm/engine.js +4 -4
  198. package/dist/lcm/index.js +13 -13
  199. package/dist/location/index.js +16 -15
  200. package/dist/maintenance/memory-governance.js +14 -13
  201. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -13
  202. package/dist/maintenance/rebuild-memory-projection.js +15 -14
  203. package/dist/memory-browse-operations.js +54 -53
  204. package/dist/memory-browse.js +5 -5
  205. package/dist/memory-corpus-version.d.ts +8 -3
  206. package/dist/memory-corpus-version.d.ts.map +1 -1
  207. package/dist/memory-corpus-version.js +2 -1
  208. package/dist/namespaces/migrate.js +15 -14
  209. package/dist/namespaces/search.js +7 -7
  210. package/dist/namespaces/storage.js +14 -13
  211. package/dist/offline-sync-changeset-generations.js +4 -4
  212. package/dist/offline-sync-embedding-generation.js +3 -3
  213. package/dist/offline-sync-exclude-globs.d.ts +8 -0
  214. package/dist/offline-sync-exclude-globs.d.ts.map +1 -1
  215. package/dist/offline-sync-exclude-globs.js +3 -1
  216. package/dist/offline-sync-snapshot-stream.js +6 -6
  217. package/dist/offline-sync.d.ts.map +1 -1
  218. package/dist/offline-sync.js +9 -9
  219. package/dist/operator-doctor-corpus.js +17 -16
  220. package/dist/operator-toolkit.js +34 -33
  221. package/dist/orchestration/maintenance.js +22 -21
  222. package/dist/orchestrator.js +153 -150
  223. package/dist/page-versioning.js +3 -2
  224. package/dist/preferences/preference-drift.d.ts.map +1 -1
  225. package/dist/procedural/library-maintenance.d.ts.map +1 -1
  226. package/dist/qmd.js +4 -4
  227. package/dist/recall-navigation-operations.js +54 -53
  228. package/dist/recall-navigation.js +53 -52
  229. package/dist/recall-state-view-admission.js +5 -5
  230. package/dist/recall-why-service.js +5 -5
  231. package/dist/{resolution-BROE3TRO.js → resolution-RSG4HZTL.js} +4 -3
  232. package/dist/resolution-RSG4HZTL.js.map +1 -0
  233. package/dist/resume-bundles.js +22 -21
  234. package/dist/search/factory.js +6 -6
  235. package/dist/search/index.js +12 -12
  236. package/dist/semantic-consolidation.js +15 -14
  237. package/dist/semantic-rule-promotion.js +14 -13
  238. package/dist/semantic-rule-verifier.js +14 -13
  239. package/dist/source-agent-qualifier.js +17 -16
  240. package/dist/standup/build.js +14 -13
  241. package/dist/storage/entity-mention-frontmatter-hold.d.ts +14 -0
  242. package/dist/storage/entity-mention-frontmatter-hold.d.ts.map +1 -0
  243. package/dist/storage.d.ts +13 -9
  244. package/dist/storage.d.ts.map +1 -1
  245. package/dist/storage.js +13 -12
  246. package/dist/support-passport/index.js +56 -55
  247. package/dist/transfer/backup.js +2 -2
  248. package/dist/transfer/capsule-export.js +2 -2
  249. package/dist/transfer/capsule-import.js +5 -4
  250. package/dist/transfer/export-okf.js +14 -13
  251. package/dist/verified-recall.js +14 -13
  252. package/dist/who-knows-cli.js +15 -14
  253. package/dist/who-knows.js +14 -13
  254. package/package.json +2 -2
  255. package/src/embedding-fallback-cross-process.test.ts +533 -0
  256. package/src/embedding-fallback.ts +19 -7
  257. package/src/embedding-index-storage.test.ts +482 -2
  258. package/src/embedding-index-storage.ts +131 -2
  259. package/src/entity-mention-carry.ts +31 -0
  260. package/src/entity-mention-epoch.ts +120 -0
  261. package/src/entity-mention-index-cache.ts +443 -0
  262. package/src/entity-retrieval.ts +72 -71
  263. package/src/maintenance/rebuild-memory-projection.ts +1 -1
  264. package/src/memory-corpus-version.ts +13 -3
  265. package/src/offline-sync-exclude-globs.ts +23 -0
  266. package/src/offline-sync.test.ts +325 -0
  267. package/src/offline-sync.ts +17 -1
  268. package/src/preferences/preference-drift.ts +5 -3
  269. package/src/procedural/library-maintenance.ts +4 -3
  270. package/src/semantic-rule-promotion.ts +1 -1
  271. package/src/storage/entity-mention-frontmatter-hold.ts +44 -0
  272. package/src/storage.ts +40 -38
  273. package/dist/chunk-47LTKAGL.js.map +0 -1
  274. package/dist/chunk-ANPRNTPT.js.map +0 -1
  275. package/dist/chunk-DINOVGA5.js.map +0 -1
  276. package/dist/chunk-HFOY2SBG.js.map +0 -1
  277. package/dist/chunk-HVDEKJJR.js.map +0 -1
  278. package/dist/chunk-JNAOTN7B.js.map +0 -1
  279. package/dist/chunk-SBOCJ47I.js.map +0 -1
  280. package/dist/chunk-UCPGOUDZ.js.map +0 -1
  281. package/dist/chunk-UFIZRMQZ.js.map +0 -1
  282. package/dist/chunk-XCB6QDL6.js.map +0 -1
  283. /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
  284. /package/dist/{chunk-IOYY6ROU.js.map → chunk-2MMBJE53.js.map} +0 -0
  285. /package/dist/{chunk-QSXEQPC4.js.map → chunk-2OFDC3AK.js.map} +0 -0
  286. /package/dist/{chunk-4MP2Q2EI.js.map → chunk-2TCNEJ36.js.map} +0 -0
  287. /package/dist/{chunk-5FAGCOZD.js.map → chunk-2U3R3DDH.js.map} +0 -0
  288. /package/dist/{chunk-SCXURCVT.js.map → chunk-2UPTRDGM.js.map} +0 -0
  289. /package/dist/{chunk-XVZA6VXC.js.map → chunk-3SL52JKD.js.map} +0 -0
  290. /package/dist/{chunk-DSPWVQZ6.js.map → chunk-73R4HOOK.js.map} +0 -0
  291. /package/dist/{chunk-PHBH74Q6.js.map → chunk-7NBUE5VY.js.map} +0 -0
  292. /package/dist/{chunk-S533KGX6.js.map → chunk-7X7A7KVN.js.map} +0 -0
  293. /package/dist/{chunk-XEATJBNB.js.map → chunk-7YQR6XHY.js.map} +0 -0
  294. /package/dist/{chunk-533RHNHB.js.map → chunk-ATFFWIKL.js.map} +0 -0
  295. /package/dist/{chunk-QCYUIZK2.js.map → chunk-AU43D3IT.js.map} +0 -0
  296. /package/dist/{chunk-JZ66PN72.js.map → chunk-AWSP3WNM.js.map} +0 -0
  297. /package/dist/{chunk-IY47KPML.js.map → chunk-B3OFQP5Y.js.map} +0 -0
  298. /package/dist/{chunk-4DXNOVNW.js.map → chunk-B454SHPU.js.map} +0 -0
  299. /package/dist/{chunk-3LMOJXZR.js.map → chunk-BKVCTTEN.js.map} +0 -0
  300. /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
  301. /package/dist/{chunk-CZN53OT5.js.map → chunk-CGBE3N7T.js.map} +0 -0
  302. /package/dist/{chunk-HWLIMQE2.js.map → chunk-CHDGJJV6.js.map} +0 -0
  303. /package/dist/{chunk-UYPQOASU.js.map → chunk-CNBGAFDY.js.map} +0 -0
  304. /package/dist/{chunk-JTC3PBNH.js.map → chunk-DB6XFMOF.js.map} +0 -0
  305. /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
  306. /package/dist/{chunk-4OXXDL3X.js.map → chunk-EWWK65CI.js.map} +0 -0
  307. /package/dist/{chunk-DFPG47ET.js.map → chunk-EZ3F4YIN.js.map} +0 -0
  308. /package/dist/{chunk-V3I4NLZP.js.map → chunk-FYX2LXIX.js.map} +0 -0
  309. /package/dist/{chunk-PNVOKGAX.js.map → chunk-G5I5ZT6O.js.map} +0 -0
  310. /package/dist/{chunk-7Z2UKRNJ.js.map → chunk-GQ6ZF4LD.js.map} +0 -0
  311. /package/dist/{chunk-PEZAHDDU.js.map → chunk-GZ2KV2RO.js.map} +0 -0
  312. /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
  313. /package/dist/{chunk-AXHU56C3.js.map → chunk-HWGFTTPS.js.map} +0 -0
  314. /package/dist/{chunk-2SGLMP2E.js.map → chunk-IDSZNHIP.js.map} +0 -0
  315. /package/dist/{chunk-LO4UKPKX.js.map → chunk-IFCKHPY4.js.map} +0 -0
  316. /package/dist/{chunk-ZHXSOMYH.js.map → chunk-IOMLGJHG.js.map} +0 -0
  317. /package/dist/{chunk-YRULQW5J.js.map → chunk-ISX7GVZE.js.map} +0 -0
  318. /package/dist/{chunk-5PHZSD75.js.map → chunk-J3ZJBX7I.js.map} +0 -0
  319. /package/dist/{chunk-X5TAWDRO.js.map → chunk-JC7QQ2DJ.js.map} +0 -0
  320. /package/dist/{chunk-RFQT7IZO.js.map → chunk-JRDL3FGM.js.map} +0 -0
  321. /package/dist/{chunk-OGZC2WUX.js.map → chunk-JWFTPRYD.js.map} +0 -0
  322. /package/dist/{chunk-7OA4JWP5.js.map → chunk-KFHEZJQZ.js.map} +0 -0
  323. /package/dist/{chunk-P3WRFSYF.js.map → chunk-KMN2JE4D.js.map} +0 -0
  324. /package/dist/{chunk-GNBZB43J.js.map → chunk-KV7LFOPF.js.map} +0 -0
  325. /package/dist/{chunk-L6YCGS7I.js.map → chunk-KYUVFCT2.js.map} +0 -0
  326. /package/dist/{chunk-5BVL2XUD.js.map → chunk-LHDSCWDK.js.map} +0 -0
  327. /package/dist/{chunk-SF7CWSR6.js.map → chunk-LXJZUNLX.js.map} +0 -0
  328. /package/dist/{chunk-DGS34QSY.js.map → chunk-MFZACYZ6.js.map} +0 -0
  329. /package/dist/{chunk-PYYQCHJC.js.map → chunk-MZOQ26LH.js.map} +0 -0
  330. /package/dist/{chunk-R2TVWQLR.js.map → chunk-NT2Q6AHC.js.map} +0 -0
  331. /package/dist/{chunk-ABGM2BN4.js.map → chunk-O43AAJFQ.js.map} +0 -0
  332. /package/dist/{chunk-G3BW6SDE.js.map → chunk-OFGTRDWX.js.map} +0 -0
  333. /package/dist/{chunk-6Z263Z3E.js.map → chunk-OWV6LKNZ.js.map} +0 -0
  334. /package/dist/{chunk-HIHN4XHL.js.map → chunk-PQ7EB5AD.js.map} +0 -0
  335. /package/dist/{chunk-VVYLVE6R.js.map → chunk-PQKZLBCJ.js.map} +0 -0
  336. /package/dist/{chunk-OFFW73OZ.js.map → chunk-PRNR47DI.js.map} +0 -0
  337. /package/dist/{chunk-UL7KDVLC.js.map → chunk-PZ2QQULY.js.map} +0 -0
  338. /package/dist/{chunk-55NZVUNI.js.map → chunk-QAIVTLL2.js.map} +0 -0
  339. /package/dist/{chunk-M7T2NKET.js.map → chunk-QKCGOECM.js.map} +0 -0
  340. /package/dist/{chunk-ARY6O3JN.js.map → chunk-QUNCLKHM.js.map} +0 -0
  341. /package/dist/{chunk-Z3O53WZR.js.map → chunk-QWUJWBZ5.js.map} +0 -0
  342. /package/dist/{chunk-RZXFXKWY.js.map → chunk-QX3K3INR.js.map} +0 -0
  343. /package/dist/{chunk-4LNCLZNK.js.map → chunk-S2GR3T7W.js.map} +0 -0
  344. /package/dist/{chunk-VPOAMPDD.js.map → chunk-SFDBLCZV.js.map} +0 -0
  345. /package/dist/{chunk-2IWBHZ3H.js.map → chunk-SIGDETMB.js.map} +0 -0
  346. /package/dist/{chunk-DBWNTLLI.js.map → chunk-STQC2FYQ.js.map} +0 -0
  347. /package/dist/{chunk-S5R77WHV.js.map → chunk-SWXPR5SE.js.map} +0 -0
  348. /package/dist/{chunk-3DNTFZAT.js.map → chunk-T32YYUT2.js.map} +0 -0
  349. /package/dist/{chunk-W5JQSVPZ.js.map → chunk-T6RQNV7E.js.map} +0 -0
  350. /package/dist/{chunk-O23QMYN4.js.map → chunk-TRN56SPQ.js.map} +0 -0
  351. /package/dist/{chunk-NM3BT2OH.js.map → chunk-TXTRHF3B.js.map} +0 -0
  352. /package/dist/{chunk-7AUIGOH4.js.map → chunk-VDVHIRCK.js.map} +0 -0
  353. /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
  354. /package/dist/{chunk-ZGYLPCWN.js.map → chunk-VO2BNAA2.js.map} +0 -0
  355. /package/dist/{chunk-WVEECBCG.js.map → chunk-VV2MVHCJ.js.map} +0 -0
  356. /package/dist/{chunk-B3O3IRZN.js.map → chunk-VZBW6CDG.js.map} +0 -0
  357. /package/dist/{chunk-JPKK466T.js.map → chunk-W5XWHTDO.js.map} +0 -0
  358. /package/dist/{chunk-CYG3K2T2.js.map → chunk-WR66CTVU.js.map} +0 -0
  359. /package/dist/{chunk-RP7RSENW.js.map → chunk-XQWKB6PY.js.map} +0 -0
  360. /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
  361. /package/dist/{chunk-KYLYXGKD.js.map → chunk-YI6XIFQV.js.map} +0 -0
  362. /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
  363. /package/dist/{chunk-Y7427SL7.js.map → chunk-Z6EZCN6Z.js.map} +0 -0
  364. /package/dist/{chunk-5K2ALMSZ.js.map → chunk-ZRUC7FD3.js.map} +0 -0
  365. /package/dist/{graph-edge-decay-MKIRZ762.js.map → entity-mention-carry.js.map} +0 -0
  366. /package/dist/{resolution-BROE3TRO.js.map → entity-mention-epoch.js.map} +0 -0
@@ -0,0 +1,443 @@
1
+ import path from "node:path";
2
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { raceAbort } from "./abort-error.js";
4
+ import { entityMentionEpoch } from "./entity-mention-epoch.js";
5
+ import { log } from "./logger.js";
6
+
7
+ /**
8
+ * In-memory entity mention index.
9
+ *
10
+ * Scope (namespaces, dirs, secure-store key, alias map, native revision) is
11
+ * the cache key. Mention epoch and entity-file mutation are identity inside
12
+ * the slot, not part of the key:
13
+ *
14
+ * - metadata-only corpus bumps do not move the epoch, so the next recall hits
15
+ * - entity-file changes rebuild from entity files before the recall returns,
16
+ * reusing memory snippets (no fact scan). A new entity is visible on that
17
+ * recall; its memory snippets arrive with the next full reconcile. A new
18
+ * canonical id schedules that reconcile. Callers share one scan. One
19
+ * caller's abort stops that caller waiting and leaves the scan running
20
+ * for the others
21
+ * - memory create/edit/delete moves the epoch. The recall serves the last
22
+ * index immediately and one background reconcile runs, plus at most one
23
+ * follow-up if that scan overlaps a later write. A recall during either
24
+ * scan does not grant another follow-up. Further rescans wait for the
25
+ * next recall. After writes quiesce and `settleEntityMentionIndex`
26
+ * finishes, the index matches a full rebuild
27
+ * - secure-store key, alias map, and native revision stay in the key, so a
28
+ * change is a miss and awaits a full rebuild. A locked store never reads
29
+ * another key's plaintext. Evicting that older scope also stops its scan
30
+ * from publishing the persisted index
31
+ * - a persisted index is readable per storage root. A process-wide cache
32
+ * clear blocks every root until that root's own post-clear build publishes.
33
+ * One root publishing does not reopen another root's file
34
+ */
35
+
36
+ const MAX_SLOTS = 32;
37
+ const SETTLE_ROUNDS = 8;
38
+
39
+ export type EntityMentionIdentity = {
40
+ mentionEpoch: string;
41
+ entityMutation: string;
42
+ };
43
+
44
+ type ScopedStorage = {
45
+ dir: string;
46
+ hotCacheKeyId?(): string;
47
+ entityAliases?: Readonly<Record<string, string>> | null;
48
+ getEntityMutationVersion?(): number;
49
+ };
50
+
51
+ type Slot = {
52
+ scopeKey: string;
53
+ index: unknown;
54
+ identity: EntityMentionIdentity;
55
+ token: number;
56
+ rebuild: Promise<void> | null;
57
+ entityJob: Promise<void> | null;
58
+ again: boolean;
59
+ followUps: number;
60
+ currentIdentity: () => EntityMentionIdentity;
61
+ buildFull: (abortSignal?: AbortSignal) => Promise<unknown>;
62
+ };
63
+
64
+ const slots = new Map<string, Slot>();
65
+ const rebuildsByScope = new Map<string, number>();
66
+ const GENERATION_STRIDE = 1_000_000_000;
67
+ let clearEpoch = 0;
68
+ const localGenerationByDir = new Map<string, number>();
69
+ const publishedGenerationByDir = new Map<string, number>();
70
+ const persistTailByDir = new Map<string, Promise<void>>();
71
+
72
+ function resolveMentionDir(dir: string): string {
73
+ return path.resolve(dir);
74
+ }
75
+
76
+ export function entityMentionCacheGeneration(dir: string): number {
77
+ const resolved = resolveMentionDir(dir);
78
+ return clearEpoch * GENERATION_STRIDE + (localGenerationByDir.get(resolved) ?? 0);
79
+ }
80
+
81
+ export function entityMentionPersistedIndexReadable(dir: string): boolean {
82
+ const resolved = resolveMentionDir(dir);
83
+ const local = localGenerationByDir.get(resolved) ?? 0;
84
+ if (clearEpoch === 0 && local === 0) return true;
85
+ return publishedGenerationByDir.get(resolved) === entityMentionCacheGeneration(resolved);
86
+ }
87
+
88
+ function dirsInScope(scopeKey: string): string[] {
89
+ const nsEnd = scopeKey.indexOf("\u001e");
90
+ const storageEnd = nsEnd === -1 ? -1 : scopeKey.indexOf("\u001e", nsEnd + 1);
91
+ if (nsEnd === -1 || storageEnd === -1) return [];
92
+ return scopeKey
93
+ .slice(nsEnd + 1, storageEnd)
94
+ .split("\u001f")
95
+ .map((part) => part.split("\u001d")[0] ?? "")
96
+ .filter((dir) => dir.length > 0)
97
+ .map((dir) => resolveMentionDir(dir));
98
+ }
99
+
100
+ function persistedMentionIndexFiles(scopeKey: string): string[] {
101
+ return dirsInScope(scopeKey).map((dir) => path.join(dir, "state", "entity-mention-index.json"));
102
+ }
103
+
104
+ function bumpScopeDirs(scopeKey: string): void {
105
+ for (const dir of dirsInScope(scopeKey)) {
106
+ localGenerationByDir.set(dir, (localGenerationByDir.get(dir) ?? 0) + 1);
107
+ }
108
+ }
109
+
110
+ function captureDirGenerations(scopeKey: string): Map<string, number> {
111
+ const captured = new Map<string, number>();
112
+ for (const dir of dirsInScope(scopeKey)) captured.set(dir, entityMentionCacheGeneration(dir));
113
+ return captured;
114
+ }
115
+
116
+ function generationsCurrent(captured: Map<string, number>): boolean {
117
+ for (const [dir, generation] of captured) {
118
+ if (entityMentionCacheGeneration(dir) !== generation) return false;
119
+ }
120
+ return true;
121
+ }
122
+
123
+ function publishDirGenerations(captured: Map<string, number>): void {
124
+ for (const [dir, generation] of captured) {
125
+ if (entityMentionCacheGeneration(dir) === generation) publishedGenerationByDir.set(dir, generation);
126
+ }
127
+ }
128
+
129
+ export async function writePersistedEntityMentionIndex(
130
+ dir: string,
131
+ generation: number,
132
+ content: string,
133
+ ): Promise<void> {
134
+ const resolved = resolveMentionDir(dir);
135
+ const previous = persistTailByDir.get(resolved) ?? Promise.resolve();
136
+ const run = previous.catch(() => undefined).then(async () => {
137
+ if (entityMentionCacheGeneration(resolved) !== generation) return;
138
+ const file = path.join(resolved, "state", "entity-mention-index.json");
139
+ await mkdir(path.dirname(file), { recursive: true });
140
+ const current = await readFile(file, "utf-8").catch(() => "");
141
+ if (current === content) return;
142
+ if (entityMentionCacheGeneration(resolved) !== generation) return;
143
+ await writeFile(file, content, "utf-8");
144
+ });
145
+ persistTailByDir.set(resolved, run.then(() => undefined, () => undefined));
146
+ await run;
147
+ }
148
+
149
+ function discardPersistedMentionIndex(scopeKey: string): void {
150
+ for (const file of persistedMentionIndexFiles(scopeKey)) {
151
+ void rm(file, { force: true }).catch(() => undefined);
152
+ }
153
+ }
154
+
155
+ function throwIfMentionAborted(signal?: AbortSignal): void {
156
+ if (!signal?.aborted) return;
157
+ const reason = signal.reason;
158
+ throw reason instanceof Error ? reason : new Error("entity mention rebuild aborted");
159
+ }
160
+
161
+ function compareStrings(left: string, right: string): number {
162
+ if (left < right) return -1;
163
+ if (left > right) return 1;
164
+ return 0;
165
+ }
166
+
167
+ function deduped(values: readonly string[]): string[] {
168
+ const seen = new Set<string>();
169
+ const out: string[] = [];
170
+ for (const value of values) {
171
+ const trimmed = value.trim();
172
+ if (trimmed.length === 0 || seen.has(trimmed)) continue;
173
+ seen.add(trimmed);
174
+ out.push(trimmed);
175
+ }
176
+ return out;
177
+ }
178
+
179
+ function sameIdentity(left: EntityMentionIdentity, right: EntityMentionIdentity): boolean {
180
+ return left.mentionEpoch === right.mentionEpoch && left.entityMutation === right.entityMutation;
181
+ }
182
+
183
+ function noteRebuild(scopeKey: string): void {
184
+ rebuildsByScope.set(scopeKey, (rebuildsByScope.get(scopeKey) ?? 0) + 1);
185
+ }
186
+
187
+ function scopeFamily(scopeKey: string): string {
188
+ const nsEnd = scopeKey.indexOf("\u001e");
189
+ const storageEnd = nsEnd === -1 ? -1 : scopeKey.indexOf("\u001e", nsEnd + 1);
190
+ const namespaceKey = nsEnd === -1 ? scopeKey : scopeKey.slice(0, nsEnd);
191
+ const storageKey = nsEnd === -1 || storageEnd === -1 ? "" : scopeKey.slice(nsEnd + 1, storageEnd);
192
+ const dirs = storageKey
193
+ .split("\u001f")
194
+ .map((part) => part.split("\u001d")[0] ?? "")
195
+ .join("\u001f");
196
+ return `${namespaceKey}\u001e${dirs}`;
197
+ }
198
+
199
+ function forgetScope(scopeKey: string, bumpPersisted = false): void {
200
+ const slot = slots.get(scopeKey);
201
+ if (slot) {
202
+ slot.token += 1;
203
+ slot.again = false;
204
+ }
205
+ if (bumpPersisted) bumpScopeDirs(scopeKey);
206
+ slots.delete(scopeKey);
207
+ rebuildsByScope.delete(scopeKey);
208
+ }
209
+
210
+ function evictOldest(scopeKey: string): void {
211
+ if (slots.size < MAX_SLOTS || slots.has(scopeKey)) return;
212
+ const oldest = slots.keys().next().value;
213
+ if (typeof oldest === "string") forgetScope(oldest);
214
+ }
215
+
216
+ function evictSameFamily(scopeKey: string): void {
217
+ const family = scopeFamily(scopeKey);
218
+ for (const key of [...slots.keys()]) {
219
+ if (key !== scopeKey && scopeFamily(key) === family) forgetScope(key, true);
220
+ }
221
+ }
222
+
223
+ function wantsSnippetReconcile(index: unknown): boolean {
224
+ return (
225
+ !!index &&
226
+ typeof index === "object" &&
227
+ (index as { pendingSnippetReconcile?: boolean }).pendingSnippetReconcile === true
228
+ );
229
+ }
230
+
231
+ function armRebuild(slot: Slot): void {
232
+ // A recall that arrives while a scan is already running must not grant
233
+ // another follow-up. The chain stops after the one already armed.
234
+ if (!slot.rebuild) slot.followUps = 1;
235
+ startRebuild(slot);
236
+ }
237
+
238
+ function startRebuild(slot: Slot): void {
239
+ // A recall that arrives during a scan is not a write. The follow-up is
240
+ // armed only when the scan's end identity differs from the identity it
241
+ // started with. Setting `again` here made every concurrent recall pay a
242
+ // second full corpus scan after a stable rebuild.
243
+ if (slot.rebuild) return;
244
+ const token = slot.token;
245
+ const captured = captureDirGenerations(slot.scopeKey);
246
+ noteRebuild(slot.scopeKey);
247
+ const started = slot.currentIdentity();
248
+ slot.again = false;
249
+ const handle: { pending: Promise<void> | null } = { pending: null };
250
+ handle.pending = slot
251
+ .buildFull(undefined)
252
+ .then((index) => {
253
+ if (slot.token !== token || !generationsCurrent(captured)) return;
254
+ publishDirGenerations(captured);
255
+ const ended = slot.currentIdentity();
256
+ slot.index = index;
257
+ if (sameIdentity(started, ended)) {
258
+ slot.identity = ended;
259
+ return;
260
+ }
261
+ // Scan overlapped later writes. Keep the start identity so the next
262
+ // recall stays stale and exactly one follow-up reconcile runs.
263
+ slot.identity = started;
264
+ slot.again = true;
265
+ })
266
+ .catch((err: unknown) => {
267
+ const message = err instanceof Error ? err.message : String(err);
268
+ log.warn(`entity mention index rebuild failed: ${message}`);
269
+ })
270
+ .finally(() => {
271
+ if (slot.rebuild === handle.pending) slot.rebuild = null;
272
+ // One automatic follow-up per caller-started rebuild. A store that keeps
273
+ // changing during every scan waits for the next recall (or settle) instead
274
+ // of chaining full scans with nobody waiting.
275
+ if (!slot.again || slot.followUps <= 0) return;
276
+ slot.again = false;
277
+ slot.followUps -= 1;
278
+ startRebuild(slot);
279
+ });
280
+ slot.rebuild = handle.pending;
281
+ }
282
+
283
+ export function entityMentionScopeKey(
284
+ namespaces: readonly string[] | undefined,
285
+ storages: readonly ScopedStorage[],
286
+ nativeRevision: string,
287
+ ): string {
288
+ const namespaceKey = deduped(namespaces ?? []).join("\u001f");
289
+ const storageKey = storages
290
+ .map((storage) => {
291
+ const aliases = Object.entries(storage.entityAliases ?? {}).sort(([left], [right]) =>
292
+ compareStrings(left, right),
293
+ );
294
+ return `${path.resolve(storage.dir)}\u001d${storage.hotCacheKeyId?.() ?? ""}\u001d${JSON.stringify(aliases)}`;
295
+ })
296
+ .join("\u001f");
297
+ return `${namespaceKey}\u001e${storageKey}\u001e${nativeRevision}`;
298
+ }
299
+
300
+ export function entityMentionIdentity(storages: readonly ScopedStorage[]): EntityMentionIdentity {
301
+ return {
302
+ mentionEpoch: storages.map((storage) => String(entityMentionEpoch.current(storage.dir))).join("\u001f"),
303
+ entityMutation: storages.map((storage) => String(storage.getEntityMutationVersion?.() ?? 0)).join("\u001f"),
304
+ };
305
+ }
306
+
307
+ export function entityMentionFullRebuildsStarted(scopeKey: string): number {
308
+ return rebuildsByScope.get(scopeKey) ?? 0;
309
+ }
310
+
311
+ export function entityMentionIndexScopeKeys(): string[] {
312
+ return [...slots.keys()];
313
+ }
314
+
315
+ export function dropEntityMentionIndexCache(scopeKey?: string): void {
316
+ if (scopeKey === undefined) {
317
+ clearEpoch += 1;
318
+ for (const key of [...slots.keys()]) {
319
+ discardPersistedMentionIndex(key);
320
+ forgetScope(key);
321
+ }
322
+ return;
323
+ }
324
+ discardPersistedMentionIndex(scopeKey);
325
+ forgetScope(scopeKey, true);
326
+ }
327
+
328
+ export async function settleEntityMentionIndex(scopeKey?: string): Promise<void> {
329
+ const matches = (slot: Slot) => scopeKey === undefined || slot.scopeKey === scopeKey;
330
+ for (let round = 0; round < SETTLE_ROUNDS; round += 1) {
331
+ for (const slot of slots.values()) {
332
+ if (!matches(slot)) continue;
333
+ if (!slot.rebuild && slot.again) {
334
+ slot.again = false;
335
+ startRebuild(slot);
336
+ }
337
+ }
338
+ const pending = [...slots.values()].filter((slot) => matches(slot) && slot.rebuild).map((slot) => slot.rebuild!);
339
+ if (pending.length === 0) return;
340
+ await Promise.all(pending);
341
+ }
342
+ }
343
+
344
+ export async function resolveEntityMentionIndex<T>(options: {
345
+ scopeKey: string;
346
+ currentIdentity: () => EntityMentionIdentity;
347
+ buildFull: (abortSignal?: AbortSignal) => Promise<T>;
348
+ rebuildEntities: (previous: T, abortSignal?: AbortSignal) => Promise<T>;
349
+ abortSignal?: AbortSignal;
350
+ }): Promise<T> {
351
+ const live = options.currentIdentity();
352
+ const existing = slots.get(options.scopeKey);
353
+ if (!existing) {
354
+ evictSameFamily(options.scopeKey);
355
+ evictOldest(options.scopeKey);
356
+ noteRebuild(options.scopeKey);
357
+ const captured = captureDirGenerations(options.scopeKey);
358
+ const started = options.currentIdentity();
359
+ const index = await options.buildFull(options.abortSignal);
360
+ // A clear or family eviction during this first scan must not publish it.
361
+ if (!generationsCurrent(captured)) return index;
362
+ publishDirGenerations(captured);
363
+ const ended = options.currentIdentity();
364
+ evictSameFamily(options.scopeKey);
365
+ evictOldest(options.scopeKey);
366
+ const slot: Slot = {
367
+ scopeKey: options.scopeKey,
368
+ index,
369
+ identity: sameIdentity(started, ended) ? ended : started,
370
+ token: 0,
371
+ rebuild: null,
372
+ entityJob: null,
373
+ again: false,
374
+ followUps: 0,
375
+ currentIdentity: options.currentIdentity,
376
+ buildFull: options.buildFull,
377
+ };
378
+ slots.set(options.scopeKey, slot);
379
+ if (!sameIdentity(started, ended)) armRebuild(slot);
380
+ return index;
381
+ }
382
+ existing.currentIdentity = options.currentIdentity;
383
+ existing.buildFull = options.buildFull;
384
+ while (existing.identity.entityMutation !== options.currentIdentity().entityMutation) {
385
+ throwIfMentionAborted(options.abortSignal);
386
+ const pending = existing.entityJob;
387
+ if (pending) {
388
+ await raceAbort(pending, options.abortSignal, "entity mention rebuild aborted");
389
+ continue;
390
+ }
391
+ const liveNow = options.currentIdentity();
392
+ const epochBefore = existing.identity.mentionEpoch;
393
+ const mutationBefore = liveNow.entityMutation;
394
+ let release = (): void => {};
395
+ const gate = new Promise<void>((resolve) => {
396
+ release = resolve;
397
+ });
398
+ existing.entityJob = gate;
399
+ existing.token += 1;
400
+ existing.again = false;
401
+ const token = existing.token;
402
+ const captured = captureDirGenerations(existing.scopeKey);
403
+ // Shared scan. This caller's abort stops its wait and leaves the scan
404
+ // running. Publish when the scan settles, even if this caller has left.
405
+ // A family eviction bumps the token and the root generation; this scan
406
+ // must not install its result or start a follow-up that would republish.
407
+ const scan = options.rebuildEntities(existing.index as T, undefined);
408
+ void scan
409
+ .then(
410
+ (rebuilt) => {
411
+ if (existing.token !== token || !generationsCurrent(captured)) return;
412
+ const now = options.currentIdentity();
413
+ existing.index = rebuilt;
414
+ const epochStable = epochBefore === liveNow.mentionEpoch && epochBefore === now.mentionEpoch;
415
+ const mutationStable = mutationBefore === now.entityMutation;
416
+ existing.identity = {
417
+ entityMutation: mutationStable ? now.entityMutation : mutationBefore,
418
+ mentionEpoch: epochStable ? now.mentionEpoch : epochBefore,
419
+ };
420
+ if (existing.identity.mentionEpoch !== now.mentionEpoch || wantsSnippetReconcile(rebuilt)) {
421
+ armRebuild(existing);
422
+ }
423
+ },
424
+ () => {},
425
+ )
426
+ .finally(() => {
427
+ if (existing.entityJob === gate) existing.entityJob = null;
428
+ release();
429
+ });
430
+ const rebuilt = await raceAbort(scan, options.abortSignal, "entity mention rebuild aborted");
431
+ throwIfMentionAborted(options.abortSignal);
432
+ return rebuilt;
433
+ }
434
+ if (existing.identity.mentionEpoch !== live.mentionEpoch) {
435
+ armRebuild(existing);
436
+ return existing.index as T;
437
+ }
438
+ return existing.index as T;
439
+ }
440
+
441
+ entityMentionEpoch.registerReset(() => {
442
+ dropEntityMentionIndexCache();
443
+ });
@@ -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(),