@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { appendFileSync, mkdirSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { entityMentionEpoch } from "./entity-mention-epoch.js";
|
|
3
4
|
import { deleteInFlightReadsForDir } from "./in-flight-reads.js";
|
|
4
5
|
import { invalidateDerivedAndGlobalForDir } from "./memory-cache.js";
|
|
5
6
|
|
|
@@ -17,10 +18,16 @@ export const MEMORY_CORPUS_VERSION_SENTINEL = ".memory-corpus-version.log";
|
|
|
17
18
|
* — review approval, governance restore, migration. Appends one byte to the
|
|
18
19
|
* on-disk sentinel so any process's version-keyed hot-memories cache rescans
|
|
19
20
|
* on its next read (StorageManager.readSharedVersion reads the file size, so
|
|
20
|
-
* the growth is observed immediately).
|
|
21
|
-
*
|
|
21
|
+
* the growth is observed immediately). The same call moves the entity-mention
|
|
22
|
+
* epoch, unless the caller passes `{ indexedText: false }` for a metadata
|
|
23
|
+
* stamp that cannot change snippet text, entityRef, or origin. Fail-open: a
|
|
24
|
+
* bump failure must never crash the caller — a stale-cache window self-heals
|
|
25
|
+
* on the next real write.
|
|
22
26
|
*/
|
|
23
|
-
export function bumpMemoryCorpusVersionForDir(
|
|
27
|
+
export function bumpMemoryCorpusVersionForDir(
|
|
28
|
+
memoryDir: string,
|
|
29
|
+
options?: { indexedText?: boolean },
|
|
30
|
+
): void {
|
|
24
31
|
try {
|
|
25
32
|
const stateDir = path.join(memoryDir, "state");
|
|
26
33
|
mkdirSync(stateDir, { recursive: true });
|
|
@@ -28,6 +35,9 @@ export function bumpMemoryCorpusVersionForDir(memoryDir: string): void {
|
|
|
28
35
|
} catch {
|
|
29
36
|
/* fail-open: never let a cache-coherence bump crash the primary operation */
|
|
30
37
|
}
|
|
38
|
+
if (options?.indexedText !== false && !entityMentionEpoch.suppressed()) {
|
|
39
|
+
entityMentionEpoch.bump(memoryDir);
|
|
40
|
+
}
|
|
31
41
|
// Drop any in-flight readAllMemories scan for this dir (Cursor Medium, #1902):
|
|
32
42
|
// after the sentinel advances, a concurrent read that missed the version-keyed
|
|
33
43
|
// hot entry must not attach to a scan that began BEFORE this out-of-band write
|
|
@@ -67,12 +67,35 @@ export const DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS: readonly string[] = [
|
|
|
67
67
|
"**/namespaces/*/state/embeddings.pre-replace.tmp/**",
|
|
68
68
|
"**/namespaces/*/state/embeddings.staging.tmp-*/**",
|
|
69
69
|
"**/namespaces/*/state/embeddings.json.pre-migration.tmp-*",
|
|
70
|
+
// The warm-cache generation marker (PR #3176) is node-local coherence
|
|
71
|
+
// state: a synced copy — worse, a captured `in-flight` value — would make
|
|
72
|
+
// the receiving node's stamp probes unique forever and force a full index
|
|
73
|
+
// reload on every search. Exclude on both sides: never snapshotted, and
|
|
74
|
+
// an incoming (old or hostile) marker never overwrites or deletes the
|
|
75
|
+
// local one.
|
|
76
|
+
"**/state/embeddings.generation",
|
|
77
|
+
"**/namespaces/*/state/embeddings.generation",
|
|
70
78
|
];
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
const DEFAULT_OFFLINE_SYNC_EXCLUDE_REGEXPS: readonly RegExp[] =
|
|
74
82
|
DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS.map((glob) => globToRegExp(glob));
|
|
75
83
|
|
|
84
|
+
/**
|
|
85
|
+
* The warm-cache generation marker is the one default-excluded path that
|
|
86
|
+
* must ALSO be refused on apply (push-side default excludes like the live
|
|
87
|
+
* LCM sqlite are deliberately apply-accepted for first-sync bootstrapping).
|
|
88
|
+
* Node-local on both sides: never snapshotted, never overwritten by an
|
|
89
|
+
* incoming old or hostile copy.
|
|
90
|
+
*/
|
|
91
|
+
export function isEmbeddingGenerationMarkerPath(relPosix: string): boolean {
|
|
92
|
+
const normalized = relPosix.includes("\\") ? relPosix.replaceAll("\\", "/") : relPosix;
|
|
93
|
+
// Case-insensitive: on APFS/NTFS a differently cased path resolves to the
|
|
94
|
+
// same file, so `state/Embeddings.Generation` must be refused like the
|
|
95
|
+
// canonical spelling.
|
|
96
|
+
return /(?:^|\/)state\/embeddings\.generation$/i.test(normalized);
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
/**
|
|
77
100
|
* Precompiled-once default-exclude check for the snapshot enumeration hot
|
|
78
101
|
* path (Kilo review, PR #1793). Moved here from offline-sync.ts with the
|
package/src/offline-sync.test.ts
CHANGED
|
@@ -26,7 +26,15 @@ import {
|
|
|
26
26
|
summarizeOfflineSyncPendingFiles,
|
|
27
27
|
} from "./offline-sync.js";
|
|
28
28
|
import { isEncryptedFile } from "./secure-store/secure-fs.js";
|
|
29
|
+
import { isEmbeddingGenerationMarkerPath } from "./offline-sync-exclude-globs.js";
|
|
29
30
|
import { StorageManager } from "./storage.js";
|
|
31
|
+
import { parseConfig } from "./config.js";
|
|
32
|
+
import { EmbeddingFallback } from "./embedding-fallback.js";
|
|
33
|
+
import {
|
|
34
|
+
clearHostEmbeddingProvidersForTest,
|
|
35
|
+
registerHostEmbeddingProvider,
|
|
36
|
+
} from "./host-embedding-provider.js";
|
|
37
|
+
import type { EmbeddingIndexStoreIo } from "./embedding-index-storage.js";
|
|
30
38
|
|
|
31
39
|
async function tempDir(name: string): Promise<string> {
|
|
32
40
|
return mkdtemp(path.join(os.tmpdir(), `${name}-`));
|
|
@@ -551,6 +559,21 @@ test("offline sync push-side default excludes live LCM sqlite but apply-side sti
|
|
|
551
559
|
assert.equal(await readUtf8(root, "state/lcm.sqlite-shm"), "live shm");
|
|
552
560
|
assert.equal(await readUtf8(root, "state/lcm.sqlite-wal"), "live wal");
|
|
553
561
|
|
|
562
|
+
// The apply-side acceptance is real, not vacuous: a node with NO local
|
|
563
|
+
// sqlite must bootstrap it from the incoming snapshot.
|
|
564
|
+
const freshRoot = await tempDir("remnic-offline-lcm-sqlite-bootstrap");
|
|
565
|
+
try {
|
|
566
|
+
const bootstrap = await applyOfflineSyncSnapshot({
|
|
567
|
+
root: freshRoot,
|
|
568
|
+
snapshot: remoteSnapshot,
|
|
569
|
+
baseFiles: [],
|
|
570
|
+
});
|
|
571
|
+
assert.equal(await readUtf8(freshRoot, "state/lcm.sqlite"), "live db", "first sync bootstraps the LCM DB from the incoming snapshot");
|
|
572
|
+
assert.equal(bootstrap.upserted, 1);
|
|
573
|
+
} finally {
|
|
574
|
+
await rm(freshRoot, { recursive: true, force: true });
|
|
575
|
+
}
|
|
576
|
+
|
|
554
577
|
// The push-side changeset excludes lcm.sqlite and friends so the
|
|
555
578
|
// local node never pushes the live sqlite state up.
|
|
556
579
|
const changeset = await buildOfflineSyncChangeset({
|
|
@@ -569,6 +592,308 @@ test("offline sync push-side default excludes live LCM sqlite but apply-side sti
|
|
|
569
592
|
}
|
|
570
593
|
});
|
|
571
594
|
|
|
595
|
+
test("offline sync excludes the node-local embedding generation marker", async () => {
|
|
596
|
+
const root = await tempDir("remnic-offline-emb3176-marker");
|
|
597
|
+
try {
|
|
598
|
+
await write(root, "facts/a.md", "alpha");
|
|
599
|
+
await write(root, "state/embeddings.generation", "in-flight");
|
|
600
|
+
await write(root, "namespaces/team/state/embeddings.generation", "gen-team-uuid");
|
|
601
|
+
|
|
602
|
+
const snapshot = await buildOfflineSyncSnapshot({
|
|
603
|
+
root,
|
|
604
|
+
sourceId: "remote",
|
|
605
|
+
includeContent: true,
|
|
606
|
+
});
|
|
607
|
+
assert.deepEqual(snapshot.files.map((file) => file.path), ["facts/a.md"]);
|
|
608
|
+
|
|
609
|
+
const changeset = await buildOfflineSyncChangeset({
|
|
610
|
+
root,
|
|
611
|
+
sourceId: "laptop",
|
|
612
|
+
baseFiles: [{
|
|
613
|
+
path: "facts/a.md",
|
|
614
|
+
sha256: createHash("sha256").update("alpha").digest("hex"),
|
|
615
|
+
bytes: Buffer.byteLength("alpha"),
|
|
616
|
+
mtimeMs: 0,
|
|
617
|
+
}],
|
|
618
|
+
});
|
|
619
|
+
assert.deepEqual(changeset.changes.map((change) => change.path), []);
|
|
620
|
+
} finally {
|
|
621
|
+
await rm(root, { recursive: true, force: true });
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test("offline sync apply never lets an incoming marker overwrite the local one", async () => {
|
|
626
|
+
const root = await tempDir("remnic-offline-emb3176-marker-apply");
|
|
627
|
+
try {
|
|
628
|
+
const localMarker = "0d9d2c11-5f6e-4a0e-8f1a-2b3c4d5e6f70";
|
|
629
|
+
await write(root, "facts/a.md", "alpha");
|
|
630
|
+
await write(root, "state/embeddings.generation", localMarker);
|
|
631
|
+
|
|
632
|
+
// A hostile or pre-exclusion remote snapshot carries the marker as an
|
|
633
|
+
// ordinary file; the pinned-`in-flight` payload would make every local
|
|
634
|
+
// stamp probe unique and force a full reload on every search.
|
|
635
|
+
const hostile = "in-flight";
|
|
636
|
+
const snapshot = {
|
|
637
|
+
format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
638
|
+
schemaVersion: 1 as const,
|
|
639
|
+
createdAt: new Date().toISOString(),
|
|
640
|
+
sourceId: "remote",
|
|
641
|
+
includeTranscripts: true,
|
|
642
|
+
files: [
|
|
643
|
+
{
|
|
644
|
+
path: "facts/b.md",
|
|
645
|
+
sha256: createHash("sha256").update("beta").digest("hex"),
|
|
646
|
+
bytes: Buffer.byteLength("beta"),
|
|
647
|
+
mtimeMs: 0,
|
|
648
|
+
contentBase64: Buffer.from("beta").toString("base64"),
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
path: "state/embeddings.generation",
|
|
652
|
+
sha256: createHash("sha256").update(hostile).digest("hex"),
|
|
653
|
+
bytes: Buffer.byteLength(hostile),
|
|
654
|
+
mtimeMs: 0,
|
|
655
|
+
contentBase64: Buffer.from(hostile).toString("base64"),
|
|
656
|
+
},
|
|
657
|
+
],
|
|
658
|
+
};
|
|
659
|
+
const result = await applyOfflineSyncSnapshot({ root, snapshot, baseFiles: [] });
|
|
660
|
+
|
|
661
|
+
assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker, "the local marker is node-local: an incoming marker must never overwrite it");
|
|
662
|
+
assert.equal(await readUtf8(root, "facts/b.md"), "beta", "unrelated incoming files still apply");
|
|
663
|
+
assert.equal(result.deleted, 0);
|
|
664
|
+
assert.deepEqual(result.conflicts, [], "an incoming marker must be skipped node-local, not turned into a conflict");
|
|
665
|
+
|
|
666
|
+
// A stale base entry for the marker plus its absence from the incoming
|
|
667
|
+
// snapshot is NOT a delete instruction either.
|
|
668
|
+
const staleBase = [{
|
|
669
|
+
path: "state/embeddings.generation",
|
|
670
|
+
sha256: createHash("sha256").update("stale").digest("hex"),
|
|
671
|
+
bytes: Buffer.byteLength("stale"),
|
|
672
|
+
mtimeMs: 0,
|
|
673
|
+
}];
|
|
674
|
+
const deletionProbe = await applyOfflineSyncSnapshot({
|
|
675
|
+
root,
|
|
676
|
+
snapshot: {
|
|
677
|
+
...snapshot,
|
|
678
|
+
files: snapshot.files.filter((file) => file.path !== "state/embeddings.generation"),
|
|
679
|
+
},
|
|
680
|
+
baseFiles: staleBase,
|
|
681
|
+
});
|
|
682
|
+
assert.equal(await exists(root, "state/embeddings.generation"), true);
|
|
683
|
+
assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker);
|
|
684
|
+
assert.equal(deletionProbe.deleted, 0);
|
|
685
|
+
assert.deepEqual(deletionProbe.conflicts, [], "a marker absence is node-local, never a delete instruction or conflict");
|
|
686
|
+
} finally {
|
|
687
|
+
await rm(root, { recursive: true, force: true });
|
|
688
|
+
}
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
test("offline sync apply refuses differently cased marker paths on case-insensitive filesystems", async () => {
|
|
692
|
+
const root = await tempDir("remnic-offline-emb3176-marker-case");
|
|
693
|
+
try {
|
|
694
|
+
const localMarker = "0d9d2c11-5f6e-4a0e-8f1a-2b3c4d5e6f70";
|
|
695
|
+
await write(root, "facts/a.md", "alpha");
|
|
696
|
+
await write(root, "state/embeddings.generation", localMarker);
|
|
697
|
+
|
|
698
|
+
assert.equal(isEmbeddingGenerationMarkerPath("state/embeddings.generation"), true);
|
|
699
|
+
assert.equal(isEmbeddingGenerationMarkerPath("state/Embeddings.Generation"), true, "APFS/NTFS resolve this to the same file");
|
|
700
|
+
assert.equal(isEmbeddingGenerationMarkerPath("State/Embeddings.Generation"), true);
|
|
701
|
+
assert.equal(isEmbeddingGenerationMarkerPath("state/embeddings.json"), false);
|
|
702
|
+
|
|
703
|
+
const hostile = "in-flight";
|
|
704
|
+
const markerEntry = (relPath: string) => ({
|
|
705
|
+
path: relPath,
|
|
706
|
+
sha256: createHash("sha256").update(hostile).digest("hex"),
|
|
707
|
+
bytes: Buffer.byteLength(hostile),
|
|
708
|
+
mtimeMs: 0,
|
|
709
|
+
contentBase64: Buffer.from(hostile).toString("base64"),
|
|
710
|
+
});
|
|
711
|
+
const result = await applyOfflineSyncSnapshot({
|
|
712
|
+
root,
|
|
713
|
+
snapshot: {
|
|
714
|
+
format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
715
|
+
schemaVersion: 1 as const,
|
|
716
|
+
createdAt: new Date().toISOString(),
|
|
717
|
+
sourceId: "remote",
|
|
718
|
+
includeTranscripts: true,
|
|
719
|
+
files: [
|
|
720
|
+
{
|
|
721
|
+
path: "facts/b.md",
|
|
722
|
+
sha256: createHash("sha256").update("beta").digest("hex"),
|
|
723
|
+
bytes: Buffer.byteLength("beta"),
|
|
724
|
+
mtimeMs: 0,
|
|
725
|
+
contentBase64: Buffer.from("beta").toString("base64"),
|
|
726
|
+
},
|
|
727
|
+
markerEntry("state/Embeddings.Generation"),
|
|
728
|
+
],
|
|
729
|
+
},
|
|
730
|
+
baseFiles: [],
|
|
731
|
+
});
|
|
732
|
+
assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker, "the canonical local marker survives a cased incoming copy");
|
|
733
|
+
assert.equal(await exists(root, "state/Embeddings.Generation"), false, "a cased marker path is refused node-local, never written");
|
|
734
|
+
assert.equal(await exists(root, "facts/b.md"), true, "unrelated incoming files still apply");
|
|
735
|
+
assert.deepEqual(result.conflicts, []);
|
|
736
|
+
} finally {
|
|
737
|
+
await rm(root, { recursive: true, force: true });
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
test("a hostile incoming marker leaves the local warm embedding cache untouched", async () => {
|
|
742
|
+
const root = await tempDir("remnic-offline-emb3176-marker-warm");
|
|
743
|
+
try {
|
|
744
|
+
const vectorFor = (text: string): number[] => {
|
|
745
|
+
const vec = [0, 0, 0, 0];
|
|
746
|
+
for (let i = 0; i < text.length; i++) vec[i % 4] += text.charCodeAt(i) % 13;
|
|
747
|
+
return vec;
|
|
748
|
+
};
|
|
749
|
+
const unregister = registerHostEmbeddingProvider(root, {
|
|
750
|
+
id: "host-model-a",
|
|
751
|
+
model: "host-model-a",
|
|
752
|
+
embed: async (text: string) => vectorFor(text),
|
|
753
|
+
});
|
|
754
|
+
let reads = 0;
|
|
755
|
+
const countingIo: EmbeddingIndexStoreIo = {
|
|
756
|
+
readUtf8: async (filePath) => {
|
|
757
|
+
reads += 1;
|
|
758
|
+
return readFile(filePath, "utf-8");
|
|
759
|
+
},
|
|
760
|
+
writeUtf8: async (filePath, contents) => {
|
|
761
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
762
|
+
await writeFile(filePath, contents, "utf-8");
|
|
763
|
+
},
|
|
764
|
+
};
|
|
765
|
+
const fallback = new EmbeddingFallback(
|
|
766
|
+
parseConfig({
|
|
767
|
+
memoryDir: root,
|
|
768
|
+
embeddingFallbackEnabled: true,
|
|
769
|
+
embeddingFallbackProvider: "openai",
|
|
770
|
+
openaiApiKey: false,
|
|
771
|
+
}),
|
|
772
|
+
countingIo,
|
|
773
|
+
);
|
|
774
|
+
try {
|
|
775
|
+
await fallback.indexFile("mem-a", "parent fact about database indexes", "facts/a.md");
|
|
776
|
+
assert.equal((await fallback.search("parent fact about database indexes", 5))[0]?.id, "mem-a");
|
|
777
|
+
const readsBeforeApply = reads;
|
|
778
|
+
|
|
779
|
+
const hostile = "in-flight";
|
|
780
|
+
await applyOfflineSyncSnapshot({
|
|
781
|
+
root,
|
|
782
|
+
snapshot: {
|
|
783
|
+
format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
784
|
+
schemaVersion: 1 as const,
|
|
785
|
+
createdAt: new Date().toISOString(),
|
|
786
|
+
sourceId: "remote",
|
|
787
|
+
includeTranscripts: true,
|
|
788
|
+
files: [{
|
|
789
|
+
path: "state/embeddings.generation",
|
|
790
|
+
sha256: createHash("sha256").update(hostile).digest("hex"),
|
|
791
|
+
bytes: Buffer.byteLength(hostile),
|
|
792
|
+
mtimeMs: 0,
|
|
793
|
+
contentBase64: Buffer.from(hostile).toString("base64"),
|
|
794
|
+
}],
|
|
795
|
+
},
|
|
796
|
+
baseFiles: [],
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
const warm = await fallback.search("parent fact about database indexes", 5);
|
|
800
|
+
assert.equal(warm[0]?.id, "mem-a", "the local generation is still served");
|
|
801
|
+
assert.equal(
|
|
802
|
+
reads,
|
|
803
|
+
readsBeforeApply,
|
|
804
|
+
"the local marker survived the apply: the warm cache keeps its stable stamp and performs no index reads",
|
|
805
|
+
);
|
|
806
|
+
} finally {
|
|
807
|
+
unregister();
|
|
808
|
+
}
|
|
809
|
+
} finally {
|
|
810
|
+
await rm(root, { recursive: true, force: true });
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
test("offline sync changeset apply refuses marker upserts and deletes, continues ordinary changes", async () => {
|
|
815
|
+
const root = await tempDir("remnic-offline-emb3176-marker-changeset");
|
|
816
|
+
try {
|
|
817
|
+
const localMarker = "7f6c1e64-9b2a-4c7d-8e0f-1a2b3c4d5e6f";
|
|
818
|
+
const hostile = "in-flight";
|
|
819
|
+
const sha = (value: string) => createHash("sha256").update(value).digest("hex");
|
|
820
|
+
await write(root, "facts/a.md", "alpha");
|
|
821
|
+
await write(root, "state/embeddings.generation", localMarker);
|
|
822
|
+
|
|
823
|
+
const result = await applyOfflineSyncChangeset({
|
|
824
|
+
root,
|
|
825
|
+
currentFiles: [
|
|
826
|
+
{ path: "facts/a.md", sha256: sha("alpha"), bytes: 5, mtimeMs: 0 },
|
|
827
|
+
{ path: "state/embeddings.generation", sha256: sha(localMarker), bytes: localMarker.length, mtimeMs: 0 },
|
|
828
|
+
],
|
|
829
|
+
changeset: {
|
|
830
|
+
format: "remnic.offline-sync.changeset.v1",
|
|
831
|
+
schemaVersion: 1,
|
|
832
|
+
createdAt: new Date().toISOString(),
|
|
833
|
+
sourceId: "peer",
|
|
834
|
+
includeTranscripts: false,
|
|
835
|
+
changes: [
|
|
836
|
+
{ type: "upsert", path: "facts/b.md", file: { path: "facts/b.md", sha256: sha("beta"), bytes: 4, mtimeMs: 0, contentBase64: Buffer.from("beta").toString("base64") } },
|
|
837
|
+
{ type: "upsert", path: "state/embeddings.generation", file: { path: "state/embeddings.generation", sha256: sha(hostile), bytes: hostile.length, mtimeMs: 0, contentBase64: Buffer.from(hostile).toString("base64") } },
|
|
838
|
+
],
|
|
839
|
+
} as never,
|
|
840
|
+
});
|
|
841
|
+
const deletionResult = await applyOfflineSyncChangeset({
|
|
842
|
+
root,
|
|
843
|
+
currentFiles: [
|
|
844
|
+
{ path: "facts/a.md", sha256: sha("alpha"), bytes: 5, mtimeMs: 0 },
|
|
845
|
+
{ path: "facts/b.md", sha256: sha("beta"), bytes: 4, mtimeMs: 0 },
|
|
846
|
+
{ path: "state/embeddings.generation", sha256: sha(localMarker), bytes: localMarker.length, mtimeMs: 0 },
|
|
847
|
+
],
|
|
848
|
+
changeset: {
|
|
849
|
+
format: "remnic.offline-sync.changeset.v1",
|
|
850
|
+
schemaVersion: 1,
|
|
851
|
+
createdAt: new Date().toISOString(),
|
|
852
|
+
sourceId: "peer",
|
|
853
|
+
includeTranscripts: false,
|
|
854
|
+
changes: [
|
|
855
|
+
{ type: "delete", path: "facts/a.md", baseSha256: sha("alpha"), mtimeMs: 0 },
|
|
856
|
+
{ type: "delete", path: "state/embeddings.generation", baseSha256: sha(localMarker), mtimeMs: 0 },
|
|
857
|
+
],
|
|
858
|
+
} as never,
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
assert.equal(await readUtf8(root, "state/embeddings.generation"), localMarker, "node-local marker: neither an incoming upsert nor a delete may touch it");
|
|
862
|
+
assert.equal(await exists(root, "state/embeddings.generation"), true);
|
|
863
|
+
assert.equal(await readUtf8(root, "facts/b.md"), "beta", "ordinary upserts continue");
|
|
864
|
+
assert.equal(await exists(root, "facts/a.md"), false, "ordinary deletes continue");
|
|
865
|
+
assert.equal(deletionResult.appliedDeletes, 1);
|
|
866
|
+
assert.deepEqual(result.conflicts, [], "a marker upsert must be skipped node-local, not turned into a conflict");
|
|
867
|
+
assert.deepEqual(deletionResult.conflicts, [], "a marker delete must be skipped node-local, not turned into a conflict");
|
|
868
|
+
} finally {
|
|
869
|
+
await rm(root, { recursive: true, force: true });
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
test("offline sync file content chunks refuse the generation marker", async () => {
|
|
874
|
+
const root = await tempDir("remnic-offline-emb3176-marker-chunk");
|
|
875
|
+
try {
|
|
876
|
+
await write(root, "state/embeddings.generation", "local-uuid");
|
|
877
|
+
const content = Buffer.from("in-flight");
|
|
878
|
+
await assert.rejects(
|
|
879
|
+
() =>
|
|
880
|
+
applyOfflineSyncFileContentChunk({
|
|
881
|
+
root,
|
|
882
|
+
sourceId: "peer",
|
|
883
|
+
path: "state/embeddings.generation",
|
|
884
|
+
sha256: createHash("sha256").update(content).digest("hex"),
|
|
885
|
+
bytes: content.length,
|
|
886
|
+
mtimeMs: 0,
|
|
887
|
+
content,
|
|
888
|
+
}),
|
|
889
|
+
/node-local/,
|
|
890
|
+
);
|
|
891
|
+
assert.equal(await readUtf8(root, "state/embeddings.generation"), "local-uuid", "the chunked hostile payload never lands");
|
|
892
|
+
} finally {
|
|
893
|
+
await rm(root, { recursive: true, force: true });
|
|
894
|
+
}
|
|
895
|
+
});
|
|
896
|
+
|
|
572
897
|
test("offline sync includes durable runtime state and excludes only transient sync temp files", async () => {
|
|
573
898
|
const root = await tempDir("remnic-offline-runtime-state");
|
|
574
899
|
try {
|
package/src/offline-sync.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "./transfer/fs-utils.js";
|
|
26
26
|
import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
|
|
27
27
|
import { EmbeddingIndexStorageError } from "./embedding-index-storage.js";
|
|
28
|
-
import { matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
|
|
28
|
+
import { isEmbeddingGenerationMarkerPath, matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
|
|
29
29
|
import {
|
|
30
30
|
applyIncomingEmbeddingGenerations,
|
|
31
31
|
assertEmbeddingGenerationStillIncluded,
|
|
@@ -1696,6 +1696,15 @@ export async function applyOfflineSyncSnapshot(options: {
|
|
|
1696
1696
|
}
|
|
1697
1697
|
|
|
1698
1698
|
if (incoming) {
|
|
1699
|
+
// The generation marker (PR #3176) is node-local on BOTH sides: an
|
|
1700
|
+
// incoming copy — old or hostile — never overwrites the local file
|
|
1701
|
+
// and never enters the base set. Other default excludes stay
|
|
1702
|
+
// apply-accepted (#1786: first-sync LCM sqlite bootstrapping).
|
|
1703
|
+
if (isEmbeddingGenerationMarkerPath(relPath)) {
|
|
1704
|
+
nextBase.delete(relPath);
|
|
1705
|
+
skipped += 1;
|
|
1706
|
+
continue;
|
|
1707
|
+
}
|
|
1699
1708
|
if (currentEntry?.sha256 === incoming.sha256) {
|
|
1700
1709
|
if (await setSafeFileMtime(root, relPath, incoming.mtimeMs)) {
|
|
1701
1710
|
nextBase.set(relPath, toFileState(incoming));
|
|
@@ -1933,6 +1942,10 @@ export async function applyOfflineSyncChangeset(options: {
|
|
|
1933
1942
|
for (const change of changeset.changes) {
|
|
1934
1943
|
if (generationRouting.conflictedPaths.has(change.path)) continue;
|
|
1935
1944
|
if (generationRouting.transactionHandled.has(change.path)) continue;
|
|
1945
|
+
if (isEmbeddingGenerationMarkerPath(change.path)) {
|
|
1946
|
+
skipped += 1;
|
|
1947
|
+
continue;
|
|
1948
|
+
}
|
|
1936
1949
|
const currentEntry = currentMap.get(change.path);
|
|
1937
1950
|
if (change.type === "upsert") {
|
|
1938
1951
|
if (currentEntry?.sha256 === change.file.sha256) {
|
|
@@ -2225,6 +2238,9 @@ export async function applyOfflineSyncFileContentChunk(options: {
|
|
|
2225
2238
|
if (shouldExcludeRelPath(relPath, includeTranscripts)) {
|
|
2226
2239
|
throw new Error(`offline sync file content path is excluded: ${relPath}`);
|
|
2227
2240
|
}
|
|
2241
|
+
if (isEmbeddingGenerationMarkerPath(relPath)) {
|
|
2242
|
+
throw new Error(`offline sync file content path is node-local: ${relPath}`);
|
|
2243
|
+
}
|
|
2228
2244
|
const sha256 = assertSha256(options.sha256, "sha256");
|
|
2229
2245
|
const bytes = assertNonNegativeInteger(options.bytes, "bytes");
|
|
2230
2246
|
const mtimeMs = assertOfflineSyncMtimeMs(options.mtimeMs, "mtimeMs");
|
|
@@ -558,8 +558,10 @@ export async function runPreferenceDriftScan(
|
|
|
558
558
|
// shared corpus version out of band — otherwise hot recall caches keep
|
|
559
559
|
// serving pre-stamp frontmatter and the damping stage never sees the new
|
|
560
560
|
// `driftState` (§25/§31).
|
|
561
|
-
bumpMemoryCorpusVersionForDir(storage.dir);
|
|
562
|
-
if (storage.dir !== deps.memoryDir)
|
|
561
|
+
bumpMemoryCorpusVersionForDir(storage.dir, { indexedText: false });
|
|
562
|
+
if (storage.dir !== deps.memoryDir) {
|
|
563
|
+
bumpMemoryCorpusVersionForDir(deps.memoryDir, { indexedText: false });
|
|
564
|
+
}
|
|
563
565
|
}
|
|
564
566
|
try {
|
|
565
567
|
await writeJsonFileAtomic(driftMarkerPath(deps.memoryDir), {
|
|
@@ -640,7 +642,7 @@ export async function resolvePreferenceDrift(
|
|
|
640
642
|
{ lastCorroborated: nowIso, driftState: undefined, updated: nowIso },
|
|
641
643
|
{ at: now, actor: DRIFT_ACTOR, reasonCode: "drift_kept", ruleVersion: DRIFT_RULE_VERSION },
|
|
642
644
|
);
|
|
643
|
-
bumpMemoryCorpusVersionForDir(storage.dir);
|
|
645
|
+
bumpMemoryCorpusVersionForDir(storage.dir, { indexedText: false });
|
|
644
646
|
touched();
|
|
645
647
|
return { affectedIds: [preferenceId], message: `Preference ${preferenceId} confirmed still current` };
|
|
646
648
|
}
|
|
@@ -526,9 +526,10 @@ export async function runProcedureLibraryMaintenance(options: {
|
|
|
526
526
|
report.appliedCount = report.applied.length;
|
|
527
527
|
|
|
528
528
|
if (writes > 0) {
|
|
529
|
-
//
|
|
530
|
-
//
|
|
531
|
-
|
|
529
|
+
// Corpus bump so hot caches observe the pass. Archive already moved the
|
|
530
|
+
// mention epoch when it removed the live file. Merge-supersede and repair
|
|
531
|
+
// stamps are metadata-only, so this bump must not move the epoch.
|
|
532
|
+
bumpMemoryCorpusVersionForDir(options.memoryDir, { indexedText: false });
|
|
532
533
|
}
|
|
533
534
|
try {
|
|
534
535
|
await writeJsonFileAtomic(maintenanceMarkerPath(options.memoryDir), {
|
|
@@ -610,7 +610,7 @@ export async function promoteSemanticRuleFromMemory(options: {
|
|
|
610
610
|
};
|
|
611
611
|
|
|
612
612
|
return withPromotionLock(options.memoryDir, ruleKey, async (lock) => {
|
|
613
|
-
storage.invalidateAllMemoriesCacheForDir();
|
|
613
|
+
storage.invalidateAllMemoriesCacheForDir({ indexedText: false });
|
|
614
614
|
const existingRule = (await storage.readAllMemories()).find(
|
|
615
615
|
(memory) =>
|
|
616
616
|
memory.frontmatter.category === "rule" &&
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { entityMentionEpoch } from "../entity-mention-epoch.js";
|
|
2
|
+
import type { MemoryFile, MemoryFrontmatter } from "../types.js";
|
|
3
|
+
import {
|
|
4
|
+
buildCapturePathLockIdentity,
|
|
5
|
+
buildExplicitCaptureDedupKey,
|
|
6
|
+
} from "./tombstone-blocked-capture-mutation.js";
|
|
7
|
+
|
|
8
|
+
type MentionHoldStorage = {
|
|
9
|
+
withTombstoneBlockedCaptureWriteLock<T>(
|
|
10
|
+
task: () => Promise<T>,
|
|
11
|
+
identity?: readonly string[],
|
|
12
|
+
): Promise<T>;
|
|
13
|
+
readMemoryByPath(pathname: string): Promise<MemoryFile | null>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Metadata frontmatter writes may suppress the mention epoch only when the
|
|
18
|
+
* file still has the same body, entityRef, and origin while the path lock is
|
|
19
|
+
* held. The write runs in that same lock so a concurrent body or entityRef
|
|
20
|
+
* change cannot be overwritten under suppression.
|
|
21
|
+
*/
|
|
22
|
+
export async function holdMentionNeutralFrontmatter<T>(
|
|
23
|
+
storage: MentionHoldStorage,
|
|
24
|
+
memory: MemoryFile,
|
|
25
|
+
updated: MemoryFrontmatter,
|
|
26
|
+
write: () => Promise<T>,
|
|
27
|
+
): Promise<T | undefined> {
|
|
28
|
+
const identities = [
|
|
29
|
+
buildCapturePathLockIdentity(memory.path),
|
|
30
|
+
buildExplicitCaptureDedupKey(memory.content, memory.frontmatter.category, memory.frontmatter.sourceConnector),
|
|
31
|
+
buildExplicitCaptureDedupKey(memory.content, updated.category, updated.sourceConnector),
|
|
32
|
+
];
|
|
33
|
+
return storage.withTombstoneBlockedCaptureWriteLock(async () => {
|
|
34
|
+
const persisted = await storage.readMemoryByPath(memory.path);
|
|
35
|
+
if (
|
|
36
|
+
!persisted ||
|
|
37
|
+
persisted.content !== memory.content ||
|
|
38
|
+
!entityMentionEpoch.neutral(persisted.frontmatter, updated)
|
|
39
|
+
) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return write();
|
|
43
|
+
}, identities);
|
|
44
|
+
}
|