@remnic/core 9.69.101 → 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
@@ -0,0 +1,120 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { appendFileSync, mkdirSync, statSync } from "node:fs";
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * Entity-mention freshness, separate from the hot-memories corpus sentinel.
7
+ *
8
+ * Every memory write bumps the corpus version so the hot cache stays coherent.
9
+ * The entity mention index does not depend on heat, decay, lifecycle, or
10
+ * lastValidatedAt, so those rewrites must not force a fact-file rescan.
11
+ * Content, entityRef, and origin changes bump this epoch. Metadata-only
12
+ * frontmatter rewrites run inside `hold()` and leave it alone.
13
+ *
14
+ * The on-disk sentinel is the byte size of `state/.entity-mention-epoch.log`
15
+ * (same pattern as the other version logs). A failed append adds one to an
16
+ * in-process extra, and `current` is the file size plus that extra. A later
17
+ * successful append, including one from another process, grows the file and
18
+ * moves `current` past those failed bumps. `reset` drops caches that
19
+ * registered at load (the mention index).
20
+ */
21
+ const fallbackByDir = new Map<string, { extra: number; disk: number }>();
22
+ const suppression = new AsyncLocalStorage<true>();
23
+ let onReset: (() => void) | null = null;
24
+
25
+ type MentionFrontmatter = {
26
+ entityRef?: unknown;
27
+ origin?: unknown;
28
+ };
29
+
30
+ function epochFile(dir: string): string {
31
+ return path.join(path.resolve(dir), "state", ".entity-mention-epoch.log");
32
+ }
33
+
34
+ function mentionField(value: unknown): string | undefined {
35
+ return typeof value === "string" ? value : undefined;
36
+ }
37
+
38
+ function diskSize(filePath: string): number {
39
+ try {
40
+ return statSync(filePath).size;
41
+ } catch {
42
+ return 0;
43
+ }
44
+ }
45
+
46
+ function localEpoch(key: string, disk: number): { extra: number; disk: number } {
47
+ const existing = fallbackByDir.get(key);
48
+ if (existing) return existing;
49
+ const created = { extra: 0, disk };
50
+ fallbackByDir.set(key, created);
51
+ return created;
52
+ }
53
+
54
+ function bump(dir: string): void {
55
+ const key = path.resolve(dir);
56
+ const filePath = epochFile(dir);
57
+ const before = diskSize(filePath);
58
+ let appended = false;
59
+ try {
60
+ mkdirSync(path.dirname(filePath), { recursive: true });
61
+ appendFileSync(filePath, "x");
62
+ appended = true;
63
+ } catch {
64
+ // The file can still be stat-able (read-only, or the disk is full). The
65
+ // in-process counter has to move or this process keeps serving a stale index.
66
+ }
67
+ const after = diskSize(filePath);
68
+ const local = localEpoch(key, before);
69
+ if (!appended || after <= before) local.extra += 1;
70
+ noteDisk(local, after);
71
+ }
72
+
73
+ function noteDisk(local: { extra: number; disk: number }, disk: number): void {
74
+ if (disk < local.disk) local.extra += local.disk - disk;
75
+ local.disk = disk;
76
+ }
77
+
78
+ function current(dir: string): number {
79
+ const key = path.resolve(dir);
80
+ const disk = diskSize(epochFile(dir));
81
+ const local = fallbackByDir.get(key);
82
+ if (!local) return disk;
83
+ noteDisk(local, disk);
84
+ // Failed appends stay ahead of the file. A peer's later successful append
85
+ // grows `disk` and must not land on the same number as those local failures.
86
+ return disk + local.extra;
87
+ }
88
+
89
+ function suppressed(): boolean {
90
+ return suppression.getStore() === true;
91
+ }
92
+
93
+ function hold<T>(fn: () => T): T {
94
+ return suppression.run(true, fn);
95
+ }
96
+
97
+ function registerReset(fn: () => void): void {
98
+ onReset = fn;
99
+ }
100
+
101
+ function reset(): void {
102
+ onReset?.();
103
+ }
104
+
105
+ function neutral(before: MentionFrontmatter, after: MentionFrontmatter): boolean {
106
+ return (
107
+ mentionField(before.entityRef) === mentionField(after.entityRef) &&
108
+ mentionField(before.origin) === mentionField(after.origin)
109
+ );
110
+ }
111
+
112
+ export const entityMentionEpoch = {
113
+ bump,
114
+ current,
115
+ suppressed,
116
+ hold,
117
+ neutral,
118
+ registerReset,
119
+ reset,
120
+ };
@@ -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
+ });