@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.
- package/dist/access-admin-ops-surface.js +53 -52
- package/dist/access-authorization-probe.js +54 -53
- package/dist/access-boundary.js +53 -52
- package/dist/access-cli.js +153 -150
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +53 -52
- package/dist/access-http-briefing.js +54 -53
- package/dist/access-http-query.js +55 -54
- package/dist/access-http.js +77 -76
- package/dist/access-identity-continuity-surface.js +53 -52
- package/dist/access-lcm-surface.js +53 -52
- package/dist/access-mcp.js +66 -65
- package/dist/access-namespace-preflight.js +53 -52
- package/dist/access-observe-write-category.js +9 -9
- package/dist/access-observe-write-surface.js +53 -52
- package/dist/access-offline-manifest.js +6 -6
- package/dist/access-operations-batch.js +57 -56
- package/dist/access-operations.js +64 -63
- package/dist/access-recall-concurrency.js +53 -52
- package/dist/access-recall-response.js +53 -52
- package/dist/access-recall-surface.js +53 -52
- package/dist/access-schema.js +8 -8
- package/dist/access-service.js +53 -52
- package/dist/access-surface-catalog.js +55 -54
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +21 -20
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/briefing.js +14 -13
- package/dist/buffer.js +5 -5
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
- package/dist/{capsule-merge-ZCXUPLNJ.js → capsule-merge-42GJGDUC.js} +4 -3
- package/dist/{capsule-merge-ZCXUPLNJ.js.map → capsule-merge-42GJGDUC.js.map} +1 -1
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +17 -16
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +3 -3
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-47LTKAGL.js → chunk-2COXDQ6C.js} +21 -6
- package/dist/chunk-2COXDQ6C.js.map +1 -0
- package/dist/{chunk-IOYY6ROU.js → chunk-2MMBJE53.js} +7 -7
- package/dist/{chunk-QSXEQPC4.js → chunk-2OFDC3AK.js} +2 -2
- package/dist/{chunk-4MP2Q2EI.js → chunk-2TCNEJ36.js} +4 -4
- package/dist/{chunk-5FAGCOZD.js → chunk-2U3R3DDH.js} +9 -9
- package/dist/{chunk-SCXURCVT.js → chunk-2UPTRDGM.js} +3 -3
- package/dist/{chunk-XVZA6VXC.js → chunk-3SL52JKD.js} +4 -4
- package/dist/{chunk-SBOCJ47I.js → chunk-5R4YPRAR.js} +19 -6
- package/dist/chunk-5R4YPRAR.js.map +1 -0
- package/dist/{chunk-ANPRNTPT.js → chunk-63LFM77Z.js} +15 -2
- package/dist/chunk-63LFM77Z.js.map +1 -0
- package/dist/{chunk-DINOVGA5.js → chunk-6WRHNA4I.js} +48 -48
- package/dist/chunk-6WRHNA4I.js.map +1 -0
- package/dist/{chunk-DSPWVQZ6.js → chunk-73R4HOOK.js} +3 -3
- package/dist/chunk-7CT3AI52.js +84 -0
- package/dist/chunk-7CT3AI52.js.map +1 -0
- package/dist/{chunk-PHBH74Q6.js → chunk-7NBUE5VY.js} +2 -2
- package/dist/{chunk-S533KGX6.js → chunk-7X7A7KVN.js} +2 -2
- package/dist/{chunk-XEATJBNB.js → chunk-7YQR6XHY.js} +71 -71
- package/dist/{chunk-F5MINS3R.js → chunk-A6WNTVA7.js} +3 -3
- package/dist/{chunk-F5MINS3R.js.map → chunk-A6WNTVA7.js.map} +1 -1
- package/dist/{chunk-533RHNHB.js → chunk-ATFFWIKL.js} +2 -2
- package/dist/{chunk-QCYUIZK2.js → chunk-AU43D3IT.js} +7 -7
- package/dist/{chunk-JZ66PN72.js → chunk-AWSP3WNM.js} +2 -2
- package/dist/{chunk-IY47KPML.js → chunk-B3OFQP5Y.js} +6 -6
- package/dist/{chunk-4DXNOVNW.js → chunk-B454SHPU.js} +6 -6
- package/dist/{chunk-3LMOJXZR.js → chunk-BKVCTTEN.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-CZN53OT5.js → chunk-CGBE3N7T.js} +2 -2
- package/dist/{chunk-HWLIMQE2.js → chunk-CHDGJJV6.js} +2 -2
- package/dist/{chunk-UYPQOASU.js → chunk-CNBGAFDY.js} +2 -2
- package/dist/{chunk-JTC3PBNH.js → chunk-DB6XFMOF.js} +3 -3
- package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
- package/dist/{chunk-UFIZRMQZ.js → chunk-E4XLFYHV.js} +61 -29
- package/dist/chunk-E4XLFYHV.js.map +1 -0
- package/dist/{chunk-4OXXDL3X.js → chunk-EWWK65CI.js} +2 -2
- package/dist/{chunk-DFPG47ET.js → chunk-EZ3F4YIN.js} +11 -11
- package/dist/{chunk-V3I4NLZP.js → chunk-FYX2LXIX.js} +2 -2
- package/dist/{chunk-PNVOKGAX.js → chunk-G5I5ZT6O.js} +2 -2
- package/dist/{chunk-7Z2UKRNJ.js → chunk-GQ6ZF4LD.js} +2 -2
- package/dist/{chunk-PEZAHDDU.js → chunk-GZ2KV2RO.js} +5 -5
- package/dist/chunk-H6Z3RNW3.js +330 -0
- package/dist/chunk-H6Z3RNW3.js.map +1 -0
- package/dist/{chunk-645WOU5I.js → chunk-H7BZBSP6.js} +4 -4
- package/dist/{chunk-AXHU56C3.js → chunk-HWGFTTPS.js} +2 -2
- package/dist/{chunk-2SGLMP2E.js → chunk-IDSZNHIP.js} +2 -2
- package/dist/{chunk-LO4UKPKX.js → chunk-IFCKHPY4.js} +2 -2
- package/dist/{chunk-ZHXSOMYH.js → chunk-IOMLGJHG.js} +4 -4
- package/dist/{chunk-YRULQW5J.js → chunk-ISX7GVZE.js} +4 -4
- package/dist/{chunk-5PHZSD75.js → chunk-J3ZJBX7I.js} +2 -2
- package/dist/{chunk-X5TAWDRO.js → chunk-JC7QQ2DJ.js} +2 -2
- package/dist/{chunk-RFQT7IZO.js → chunk-JRDL3FGM.js} +3 -3
- package/dist/{chunk-OGZC2WUX.js → chunk-JWFTPRYD.js} +2 -2
- package/dist/{chunk-7OA4JWP5.js → chunk-KFHEZJQZ.js} +2 -2
- package/dist/{chunk-P3WRFSYF.js → chunk-KMN2JE4D.js} +2 -2
- package/dist/{chunk-GNBZB43J.js → chunk-KV7LFOPF.js} +2 -2
- package/dist/{chunk-L6YCGS7I.js → chunk-KYUVFCT2.js} +2 -2
- package/dist/{chunk-5BVL2XUD.js → chunk-LHDSCWDK.js} +17 -17
- package/dist/{chunk-SF7CWSR6.js → chunk-LXJZUNLX.js} +2 -2
- package/dist/{chunk-XCB6QDL6.js → chunk-M5GVCJSJ.js} +8 -2
- package/dist/chunk-M5GVCJSJ.js.map +1 -0
- package/dist/{chunk-DGS34QSY.js → chunk-MFZACYZ6.js} +2 -2
- package/dist/{chunk-PYYQCHJC.js → chunk-MZOQ26LH.js} +2 -2
- package/dist/{chunk-R2TVWQLR.js → chunk-NT2Q6AHC.js} +2 -2
- package/dist/{chunk-ABGM2BN4.js → chunk-O43AAJFQ.js} +2 -2
- package/dist/{chunk-G3BW6SDE.js → chunk-OFGTRDWX.js} +3 -3
- package/dist/{chunk-6Z263Z3E.js → chunk-OWV6LKNZ.js} +2 -2
- package/dist/{chunk-HIHN4XHL.js → chunk-PQ7EB5AD.js} +3 -3
- package/dist/{chunk-VVYLVE6R.js → chunk-PQKZLBCJ.js} +1 -1
- package/dist/{chunk-OFFW73OZ.js → chunk-PRNR47DI.js} +2 -2
- package/dist/{chunk-UL7KDVLC.js → chunk-PZ2QQULY.js} +11 -11
- package/dist/{chunk-55NZVUNI.js → chunk-QAIVTLL2.js} +3 -3
- package/dist/{chunk-M7T2NKET.js → chunk-QKCGOECM.js} +2 -2
- package/dist/{chunk-ARY6O3JN.js → chunk-QUNCLKHM.js} +2 -2
- package/dist/{chunk-Z3O53WZR.js → chunk-QWUJWBZ5.js} +2 -2
- package/dist/{chunk-RZXFXKWY.js → chunk-QX3K3INR.js} +2 -2
- package/dist/{chunk-HFOY2SBG.js → chunk-RYN4AIRM.js} +7 -5
- package/dist/chunk-RYN4AIRM.js.map +1 -0
- package/dist/{chunk-4LNCLZNK.js → chunk-S2GR3T7W.js} +2 -2
- package/dist/{chunk-VPOAMPDD.js → chunk-SFDBLCZV.js} +59 -59
- package/dist/{chunk-2IWBHZ3H.js → chunk-SIGDETMB.js} +2 -2
- package/dist/chunk-SP4DB6SX.js +19 -0
- package/dist/chunk-SP4DB6SX.js.map +1 -0
- package/dist/{chunk-DBWNTLLI.js → chunk-STQC2FYQ.js} +6 -6
- package/dist/{chunk-S5R77WHV.js → chunk-SWXPR5SE.js} +2 -2
- package/dist/{chunk-3DNTFZAT.js → chunk-T32YYUT2.js} +2 -2
- package/dist/{chunk-W5JQSVPZ.js → chunk-T6RQNV7E.js} +2 -2
- package/dist/{chunk-O23QMYN4.js → chunk-TRN56SPQ.js} +2 -2
- package/dist/{chunk-NM3BT2OH.js → chunk-TXTRHF3B.js} +2 -2
- package/dist/{chunk-7AUIGOH4.js → chunk-VDVHIRCK.js} +2 -2
- package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-ZGYLPCWN.js → chunk-VO2BNAA2.js} +3 -3
- package/dist/{chunk-WVEECBCG.js → chunk-VV2MVHCJ.js} +2 -2
- package/dist/{chunk-HVDEKJJR.js → chunk-VYIBRCVW.js} +70 -61
- package/dist/chunk-VYIBRCVW.js.map +1 -0
- package/dist/{chunk-B3O3IRZN.js → chunk-VZBW6CDG.js} +1 -1
- package/dist/{chunk-JPKK466T.js → chunk-W5XWHTDO.js} +2 -2
- package/dist/{chunk-CYG3K2T2.js → chunk-WR66CTVU.js} +6 -6
- package/dist/{chunk-JNAOTN7B.js → chunk-XLWMNDQR.js} +4 -4
- package/dist/chunk-XLWMNDQR.js.map +1 -0
- package/dist/{chunk-RP7RSENW.js → chunk-XQWKB6PY.js} +2 -2
- package/dist/{chunk-64S6X4IU.js → chunk-XTN54NML.js} +4 -4
- package/dist/{chunk-UCPGOUDZ.js → chunk-Y57GJDG5.js} +108 -3
- package/dist/chunk-Y57GJDG5.js.map +1 -0
- package/dist/{chunk-KYLYXGKD.js → chunk-YI6XIFQV.js} +2 -2
- package/dist/{chunk-EZ25VE3G.js → chunk-YNDLCWXS.js} +4 -4
- package/dist/{chunk-Y7427SL7.js → chunk-Z6EZCN6Z.js} +10 -10
- package/dist/{chunk-5K2ALMSZ.js → chunk-ZRUC7FD3.js} +2 -2
- package/dist/cli.js +103 -102
- package/dist/coding/export-okf-codegraph.js +13 -12
- package/dist/coding/export-okf-codegraph.js.map +1 -1
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +14 -13
- package/dist/config.js +21 -20
- package/dist/connectors/codex-materialize-runner.js +14 -13
- package/dist/connectors/index.js +14 -13
- package/dist/consolidation-provenance-check.js +4 -3
- package/dist/consolidation-undo.js +3 -2
- package/dist/consolidation-undo.js.map +1 -1
- package/dist/contradiction/index.js +3 -2
- package/dist/convergence-refresh.js +16 -15
- package/dist/corpus-watermark.js +16 -15
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-http-glue.js +9 -9
- package/dist/deep-recall.js +2 -2
- package/dist/embedding-fallback.d.ts +2 -1
- package/dist/embedding-fallback.d.ts.map +1 -1
- package/dist/embedding-fallback.js +2 -2
- package/dist/embedding-index-storage.d.ts +28 -1
- package/dist/embedding-index-storage.d.ts.map +1 -1
- package/dist/embedding-index-storage.js +1 -1
- package/dist/entity-mention-carry.d.ts +16 -0
- package/dist/entity-mention-carry.d.ts.map +1 -0
- package/dist/entity-mention-carry.js +8 -0
- package/dist/entity-mention-epoch.d.ts +22 -0
- package/dist/entity-mention-epoch.d.ts.map +1 -0
- package/dist/entity-mention-epoch.js +8 -0
- package/dist/entity-mention-index-cache.d.ts +28 -0
- package/dist/entity-mention-index-cache.d.ts.map +1 -0
- package/dist/entity-mention-index-cache.js +29 -0
- package/dist/entity-mention-index-cache.js.map +1 -0
- package/dist/entity-retrieval.d.ts.map +1 -1
- package/dist/entity-retrieval.js +18 -15
- package/dist/event-order-recall.js +2 -2
- package/dist/external-wiki-access.js +54 -53
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.js +21 -20
- package/dist/{graph-edge-decay-MKIRZ762.js → graph-edge-decay-PVKDKLQK.js} +6 -6
- package/dist/graph-edge-decay-PVKDKLQK.js.map +1 -0
- package/dist/graph-jsonl.js +4 -4
- package/dist/graph-snapshot.js +4 -4
- package/dist/graph.js +5 -5
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.js +187 -184
- package/dist/lcm/engine.js +4 -4
- package/dist/lcm/index.js +13 -13
- package/dist/location/index.js +16 -15
- package/dist/maintenance/memory-governance.js +14 -13
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -13
- package/dist/maintenance/rebuild-memory-projection.js +15 -14
- package/dist/memory-browse-operations.js +54 -53
- package/dist/memory-browse.js +5 -5
- package/dist/memory-corpus-version.d.ts +8 -3
- package/dist/memory-corpus-version.d.ts.map +1 -1
- package/dist/memory-corpus-version.js +2 -1
- package/dist/namespaces/migrate.js +15 -14
- package/dist/namespaces/search.js +7 -7
- package/dist/namespaces/storage.js +14 -13
- package/dist/offline-sync-changeset-generations.js +4 -4
- package/dist/offline-sync-embedding-generation.js +3 -3
- package/dist/offline-sync-exclude-globs.d.ts +8 -0
- package/dist/offline-sync-exclude-globs.d.ts.map +1 -1
- package/dist/offline-sync-exclude-globs.js +3 -1
- package/dist/offline-sync-snapshot-stream.js +6 -6
- package/dist/offline-sync.d.ts.map +1 -1
- package/dist/offline-sync.js +9 -9
- package/dist/operator-doctor-corpus.js +17 -16
- package/dist/operator-toolkit.js +34 -33
- package/dist/orchestration/maintenance.js +22 -21
- package/dist/orchestrator.js +153 -150
- package/dist/page-versioning.js +3 -2
- package/dist/preferences/preference-drift.d.ts.map +1 -1
- package/dist/procedural/library-maintenance.d.ts.map +1 -1
- package/dist/qmd.js +4 -4
- package/dist/recall-navigation-operations.js +54 -53
- package/dist/recall-navigation.js +53 -52
- package/dist/recall-state-view-admission.js +5 -5
- package/dist/recall-why-service.js +5 -5
- package/dist/{resolution-BROE3TRO.js → resolution-RSG4HZTL.js} +4 -3
- package/dist/resolution-RSG4HZTL.js.map +1 -0
- package/dist/resume-bundles.js +22 -21
- package/dist/search/factory.js +6 -6
- package/dist/search/index.js +12 -12
- package/dist/semantic-consolidation.js +15 -14
- package/dist/semantic-rule-promotion.js +14 -13
- package/dist/semantic-rule-verifier.js +14 -13
- package/dist/source-agent-qualifier.js +17 -16
- package/dist/standup/build.js +14 -13
- package/dist/storage/entity-mention-frontmatter-hold.d.ts +14 -0
- package/dist/storage/entity-mention-frontmatter-hold.d.ts.map +1 -0
- package/dist/storage.d.ts +13 -9
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +13 -12
- package/dist/support-passport/index.js +56 -55
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +5 -4
- package/dist/transfer/export-okf.js +14 -13
- package/dist/verified-recall.js +14 -13
- package/dist/who-knows-cli.js +15 -14
- package/dist/who-knows.js +14 -13
- package/package.json +2 -2
- package/src/embedding-fallback-cross-process.test.ts +533 -0
- package/src/embedding-fallback.ts +19 -7
- package/src/embedding-index-storage.test.ts +482 -2
- package/src/embedding-index-storage.ts +131 -2
- package/src/entity-mention-carry.ts +31 -0
- package/src/entity-mention-epoch.ts +120 -0
- package/src/entity-mention-index-cache.ts +443 -0
- package/src/entity-retrieval.ts +72 -71
- package/src/maintenance/rebuild-memory-projection.ts +1 -1
- package/src/memory-corpus-version.ts +13 -3
- package/src/offline-sync-exclude-globs.ts +23 -0
- package/src/offline-sync.test.ts +325 -0
- package/src/offline-sync.ts +17 -1
- package/src/preferences/preference-drift.ts +5 -3
- package/src/procedural/library-maintenance.ts +4 -3
- package/src/semantic-rule-promotion.ts +1 -1
- package/src/storage/entity-mention-frontmatter-hold.ts +44 -0
- package/src/storage.ts +40 -38
- package/dist/chunk-47LTKAGL.js.map +0 -1
- package/dist/chunk-ANPRNTPT.js.map +0 -1
- package/dist/chunk-DINOVGA5.js.map +0 -1
- package/dist/chunk-HFOY2SBG.js.map +0 -1
- package/dist/chunk-HVDEKJJR.js.map +0 -1
- package/dist/chunk-JNAOTN7B.js.map +0 -1
- package/dist/chunk-SBOCJ47I.js.map +0 -1
- package/dist/chunk-UCPGOUDZ.js.map +0 -1
- package/dist/chunk-UFIZRMQZ.js.map +0 -1
- package/dist/chunk-XCB6QDL6.js.map +0 -1
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
- /package/dist/{chunk-IOYY6ROU.js.map → chunk-2MMBJE53.js.map} +0 -0
- /package/dist/{chunk-QSXEQPC4.js.map → chunk-2OFDC3AK.js.map} +0 -0
- /package/dist/{chunk-4MP2Q2EI.js.map → chunk-2TCNEJ36.js.map} +0 -0
- /package/dist/{chunk-5FAGCOZD.js.map → chunk-2U3R3DDH.js.map} +0 -0
- /package/dist/{chunk-SCXURCVT.js.map → chunk-2UPTRDGM.js.map} +0 -0
- /package/dist/{chunk-XVZA6VXC.js.map → chunk-3SL52JKD.js.map} +0 -0
- /package/dist/{chunk-DSPWVQZ6.js.map → chunk-73R4HOOK.js.map} +0 -0
- /package/dist/{chunk-PHBH74Q6.js.map → chunk-7NBUE5VY.js.map} +0 -0
- /package/dist/{chunk-S533KGX6.js.map → chunk-7X7A7KVN.js.map} +0 -0
- /package/dist/{chunk-XEATJBNB.js.map → chunk-7YQR6XHY.js.map} +0 -0
- /package/dist/{chunk-533RHNHB.js.map → chunk-ATFFWIKL.js.map} +0 -0
- /package/dist/{chunk-QCYUIZK2.js.map → chunk-AU43D3IT.js.map} +0 -0
- /package/dist/{chunk-JZ66PN72.js.map → chunk-AWSP3WNM.js.map} +0 -0
- /package/dist/{chunk-IY47KPML.js.map → chunk-B3OFQP5Y.js.map} +0 -0
- /package/dist/{chunk-4DXNOVNW.js.map → chunk-B454SHPU.js.map} +0 -0
- /package/dist/{chunk-3LMOJXZR.js.map → chunk-BKVCTTEN.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-CZN53OT5.js.map → chunk-CGBE3N7T.js.map} +0 -0
- /package/dist/{chunk-HWLIMQE2.js.map → chunk-CHDGJJV6.js.map} +0 -0
- /package/dist/{chunk-UYPQOASU.js.map → chunk-CNBGAFDY.js.map} +0 -0
- /package/dist/{chunk-JTC3PBNH.js.map → chunk-DB6XFMOF.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-4OXXDL3X.js.map → chunk-EWWK65CI.js.map} +0 -0
- /package/dist/{chunk-DFPG47ET.js.map → chunk-EZ3F4YIN.js.map} +0 -0
- /package/dist/{chunk-V3I4NLZP.js.map → chunk-FYX2LXIX.js.map} +0 -0
- /package/dist/{chunk-PNVOKGAX.js.map → chunk-G5I5ZT6O.js.map} +0 -0
- /package/dist/{chunk-7Z2UKRNJ.js.map → chunk-GQ6ZF4LD.js.map} +0 -0
- /package/dist/{chunk-PEZAHDDU.js.map → chunk-GZ2KV2RO.js.map} +0 -0
- /package/dist/{chunk-645WOU5I.js.map → chunk-H7BZBSP6.js.map} +0 -0
- /package/dist/{chunk-AXHU56C3.js.map → chunk-HWGFTTPS.js.map} +0 -0
- /package/dist/{chunk-2SGLMP2E.js.map → chunk-IDSZNHIP.js.map} +0 -0
- /package/dist/{chunk-LO4UKPKX.js.map → chunk-IFCKHPY4.js.map} +0 -0
- /package/dist/{chunk-ZHXSOMYH.js.map → chunk-IOMLGJHG.js.map} +0 -0
- /package/dist/{chunk-YRULQW5J.js.map → chunk-ISX7GVZE.js.map} +0 -0
- /package/dist/{chunk-5PHZSD75.js.map → chunk-J3ZJBX7I.js.map} +0 -0
- /package/dist/{chunk-X5TAWDRO.js.map → chunk-JC7QQ2DJ.js.map} +0 -0
- /package/dist/{chunk-RFQT7IZO.js.map → chunk-JRDL3FGM.js.map} +0 -0
- /package/dist/{chunk-OGZC2WUX.js.map → chunk-JWFTPRYD.js.map} +0 -0
- /package/dist/{chunk-7OA4JWP5.js.map → chunk-KFHEZJQZ.js.map} +0 -0
- /package/dist/{chunk-P3WRFSYF.js.map → chunk-KMN2JE4D.js.map} +0 -0
- /package/dist/{chunk-GNBZB43J.js.map → chunk-KV7LFOPF.js.map} +0 -0
- /package/dist/{chunk-L6YCGS7I.js.map → chunk-KYUVFCT2.js.map} +0 -0
- /package/dist/{chunk-5BVL2XUD.js.map → chunk-LHDSCWDK.js.map} +0 -0
- /package/dist/{chunk-SF7CWSR6.js.map → chunk-LXJZUNLX.js.map} +0 -0
- /package/dist/{chunk-DGS34QSY.js.map → chunk-MFZACYZ6.js.map} +0 -0
- /package/dist/{chunk-PYYQCHJC.js.map → chunk-MZOQ26LH.js.map} +0 -0
- /package/dist/{chunk-R2TVWQLR.js.map → chunk-NT2Q6AHC.js.map} +0 -0
- /package/dist/{chunk-ABGM2BN4.js.map → chunk-O43AAJFQ.js.map} +0 -0
- /package/dist/{chunk-G3BW6SDE.js.map → chunk-OFGTRDWX.js.map} +0 -0
- /package/dist/{chunk-6Z263Z3E.js.map → chunk-OWV6LKNZ.js.map} +0 -0
- /package/dist/{chunk-HIHN4XHL.js.map → chunk-PQ7EB5AD.js.map} +0 -0
- /package/dist/{chunk-VVYLVE6R.js.map → chunk-PQKZLBCJ.js.map} +0 -0
- /package/dist/{chunk-OFFW73OZ.js.map → chunk-PRNR47DI.js.map} +0 -0
- /package/dist/{chunk-UL7KDVLC.js.map → chunk-PZ2QQULY.js.map} +0 -0
- /package/dist/{chunk-55NZVUNI.js.map → chunk-QAIVTLL2.js.map} +0 -0
- /package/dist/{chunk-M7T2NKET.js.map → chunk-QKCGOECM.js.map} +0 -0
- /package/dist/{chunk-ARY6O3JN.js.map → chunk-QUNCLKHM.js.map} +0 -0
- /package/dist/{chunk-Z3O53WZR.js.map → chunk-QWUJWBZ5.js.map} +0 -0
- /package/dist/{chunk-RZXFXKWY.js.map → chunk-QX3K3INR.js.map} +0 -0
- /package/dist/{chunk-4LNCLZNK.js.map → chunk-S2GR3T7W.js.map} +0 -0
- /package/dist/{chunk-VPOAMPDD.js.map → chunk-SFDBLCZV.js.map} +0 -0
- /package/dist/{chunk-2IWBHZ3H.js.map → chunk-SIGDETMB.js.map} +0 -0
- /package/dist/{chunk-DBWNTLLI.js.map → chunk-STQC2FYQ.js.map} +0 -0
- /package/dist/{chunk-S5R77WHV.js.map → chunk-SWXPR5SE.js.map} +0 -0
- /package/dist/{chunk-3DNTFZAT.js.map → chunk-T32YYUT2.js.map} +0 -0
- /package/dist/{chunk-W5JQSVPZ.js.map → chunk-T6RQNV7E.js.map} +0 -0
- /package/dist/{chunk-O23QMYN4.js.map → chunk-TRN56SPQ.js.map} +0 -0
- /package/dist/{chunk-NM3BT2OH.js.map → chunk-TXTRHF3B.js.map} +0 -0
- /package/dist/{chunk-7AUIGOH4.js.map → chunk-VDVHIRCK.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-ZGYLPCWN.js.map → chunk-VO2BNAA2.js.map} +0 -0
- /package/dist/{chunk-WVEECBCG.js.map → chunk-VV2MVHCJ.js.map} +0 -0
- /package/dist/{chunk-B3O3IRZN.js.map → chunk-VZBW6CDG.js.map} +0 -0
- /package/dist/{chunk-JPKK466T.js.map → chunk-W5XWHTDO.js.map} +0 -0
- /package/dist/{chunk-CYG3K2T2.js.map → chunk-WR66CTVU.js.map} +0 -0
- /package/dist/{chunk-RP7RSENW.js.map → chunk-XQWKB6PY.js.map} +0 -0
- /package/dist/{chunk-64S6X4IU.js.map → chunk-XTN54NML.js.map} +0 -0
- /package/dist/{chunk-KYLYXGKD.js.map → chunk-YI6XIFQV.js.map} +0 -0
- /package/dist/{chunk-EZ25VE3G.js.map → chunk-YNDLCWXS.js.map} +0 -0
- /package/dist/{chunk-Y7427SL7.js.map → chunk-Z6EZCN6Z.js.map} +0 -0
- /package/dist/{chunk-5K2ALMSZ.js.map → chunk-ZRUC7FD3.js.map} +0 -0
- /package/dist/{graph-edge-decay-MKIRZ762.js.map → entity-mention-carry.js.map} +0 -0
- /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
|
+
});
|
package/src/entity-retrieval.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
365
|
-
|
|
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(
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
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
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
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
|
-
|
|
1104
|
-
|
|
1105
|
-
)
|
|
1106
|
-
|
|
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(),
|