@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
package/src/storage.ts
CHANGED
|
@@ -87,6 +87,8 @@ import {
|
|
|
87
87
|
deleteInFlightReadsForDir,
|
|
88
88
|
clearInFlightReads,
|
|
89
89
|
} from "./in-flight-reads.js";
|
|
90
|
+
import { entityMentionEpoch } from "./entity-mention-epoch.js";
|
|
91
|
+
import { holdMentionNeutralFrontmatter } from "./storage/entity-mention-frontmatter-hold.js";
|
|
90
92
|
import * as archive from "./archive-mutation-version.js";
|
|
91
93
|
import { rotateMarkdownFileToArchive } from "./hygiene.js";
|
|
92
94
|
import { sanitizeMemoryContent } from "./sanitize.js";
|
|
@@ -2419,11 +2421,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
2419
2421
|
* Bump only — NOT invalidateAllForDir (that would drop the very hot
|
|
2420
2422
|
* entry the write path just patched).
|
|
2421
2423
|
*/
|
|
2422
|
-
protected bumpMemoryCorpusVersion(): void {
|
|
2424
|
+
protected bumpMemoryCorpusVersion(opts?: { indexedText?: boolean }): void {
|
|
2423
2425
|
this.bumpSharedVersion("memory-corpus", StorageManager.memoryCorpusVersionByDir);
|
|
2424
|
-
|
|
2425
|
-
// must not attach to a scan that began BEFORE this mutation. (The patch
|
|
2426
|
-
// path uses bumpMemoryCorpusVersionExclusive and clears the slot itself.)
|
|
2426
|
+
if (opts?.indexedText !== false && !entityMentionEpoch.suppressed()) entityMentionEpoch.bump(this.baseDir);
|
|
2427
2427
|
deleteInFlightReadsForDir(this.baseDir);
|
|
2428
2428
|
}
|
|
2429
2429
|
getArchiveMutationVersion(): number { return archive.getArchiveMutationVersionForDir(this.baseDir); }
|
|
@@ -2434,7 +2434,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
2434
2434
|
* patchHotMemoriesCache refuse to re-key its locally patched corpus at a
|
|
2435
2435
|
* version already reflecting a peer's still-unread concurrent append.
|
|
2436
2436
|
*/
|
|
2437
|
-
private bumpMemoryCorpusVersionExclusive(): { produced: number; exclusive: boolean } {
|
|
2437
|
+
private bumpMemoryCorpusVersionExclusive(opts?: { indexedText?: boolean }): { produced: number; exclusive: boolean } {
|
|
2438
2438
|
const filePath = this.versionFilePath("memory-corpus");
|
|
2439
2439
|
try {
|
|
2440
2440
|
mkdirSync(this.stateDir, { recursive: true });
|
|
@@ -2447,11 +2447,13 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
2447
2447
|
appendFileSync(filePath, "x");
|
|
2448
2448
|
const produced = statSync(filePath).size;
|
|
2449
2449
|
StorageManager.memoryCorpusVersionByDir.set(this.baseDir, produced);
|
|
2450
|
+
if (opts?.indexedText !== false && !entityMentionEpoch.suppressed()) entityMentionEpoch.bump(this.baseDir);
|
|
2450
2451
|
// Exclusive iff exactly our single byte landed between the two stats.
|
|
2451
2452
|
return { produced, exclusive: produced === before + 1 };
|
|
2452
2453
|
} catch {
|
|
2453
2454
|
const next = (StorageManager.memoryCorpusVersionByDir.get(this.baseDir) ?? 0) + 1;
|
|
2454
2455
|
StorageManager.memoryCorpusVersionByDir.set(this.baseDir, next);
|
|
2456
|
+
if (opts?.indexedText !== false && !entityMentionEpoch.suppressed()) entityMentionEpoch.bump(this.baseDir);
|
|
2455
2457
|
return { produced: next, exclusive: true };
|
|
2456
2458
|
}
|
|
2457
2459
|
}
|
|
@@ -4417,8 +4419,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4417
4419
|
/** Invalidate the readAllMemories() cache after writes that add/remove memories. */
|
|
4418
4420
|
/** Public cache invalidation for callers that need authoritative disk reads
|
|
4419
4421
|
* (e.g. projection verify/rebuild). */
|
|
4420
|
-
invalidateAllMemoriesCacheForDir(): void {
|
|
4421
|
-
this.invalidateAllMemoriesCache();
|
|
4422
|
+
invalidateAllMemoriesCacheForDir(opts?: { indexedText?: boolean }): void {
|
|
4423
|
+
this.invalidateAllMemoriesCache(opts);
|
|
4422
4424
|
}
|
|
4423
4425
|
|
|
4424
4426
|
/** Invalidate only the cache layers affected by direct tier file deletes. */
|
|
@@ -4443,9 +4445,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4443
4445
|
}
|
|
4444
4446
|
}
|
|
4445
4447
|
|
|
4446
|
-
/** Clear
|
|
4447
|
-
* (bypassing StorageManager.writeMemory) to avoid stale reads. */
|
|
4448
|
+
/** Clear every static cache. Direct file writes call this so the next read is not stale. */
|
|
4448
4449
|
static clearAllStaticCaches(): void {
|
|
4450
|
+
entityMentionEpoch.reset();
|
|
4449
4451
|
clearInFlightReads();
|
|
4450
4452
|
StorageManager.questionsCache.clear();
|
|
4451
4453
|
StorageManager.coldMemoriesCache.clear(); // also wipe the cold-scan TTL cache
|
|
@@ -4475,12 +4477,12 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
4475
4477
|
* exclusively by invalidateColdMemoriesCache(), which is called only when
|
|
4476
4478
|
* cold content actually changes (hot→cold demotions, writeMemoryFileAtomic
|
|
4477
4479
|
* inside cold/, archiveMemory, etc.). */
|
|
4478
|
-
protected invalidateAllMemoriesCache(): void {
|
|
4480
|
+
protected invalidateAllMemoriesCache(opts?: { indexedText?: boolean }): void {
|
|
4479
4481
|
deleteInFlightReadsForDir(this.baseDir);
|
|
4480
4482
|
// Bulk/ambiguous mutations drop the hot layer wholesale (below); bump the
|
|
4481
4483
|
// corpus sentinel too so PEER processes rescan instead of serving a warm
|
|
4482
4484
|
// pre-mutation corpus entry (issue #1902 cross-process coherence).
|
|
4483
|
-
this.bumpMemoryCorpusVersion();
|
|
4485
|
+
this.bumpMemoryCorpusVersion(opts);
|
|
4484
4486
|
// Invalidation chokepoint (issue #1535 / #1904): evict the layers a
|
|
4485
4487
|
// memory-mutate can affect — hot, archive, derived episode/rule views, and
|
|
4486
4488
|
// both QMD result caches (qmdSearchCache and qmdRecallCache). Before the QMD
|
|
@@ -5356,18 +5358,16 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
5356
5358
|
[buildCapturePathLockIdentity(expected.path), oldIdentity, newIdentity]
|
|
5357
5359
|
);
|
|
5358
5360
|
}
|
|
5359
|
-
/**
|
|
5360
|
-
|
|
5361
|
-
*/
|
|
5361
|
+
/** Frontmatter patch. A replaced body still moves the mention epoch. */
|
|
5362
|
+
private static readonly frontmatterBody = StorageManager.prototype.writeMemoryFrontmatter;
|
|
5362
5363
|
async writeMemoryFrontmatter(
|
|
5363
5364
|
memory: MemoryFile,
|
|
5364
5365
|
patch: Partial<MemoryFrontmatter>,
|
|
5365
|
-
lifecycle?: MemoryLifecycleEventWriteOptions
|
|
5366
|
+
lifecycle?: MemoryLifecycleEventWriteOptions,
|
|
5367
|
+
mentionReentry?: boolean
|
|
5366
5368
|
): Promise<boolean> {
|
|
5367
5369
|
const beforeStatus = memory.frontmatter.status ?? "active";
|
|
5368
|
-
// Canonicalize
|
|
5369
|
-
// unrelated patch must not rewrite an inherited legacy ref back out.
|
|
5370
|
-
// #2807: `patch.updated` is business time, persisted VERBATIM.
|
|
5370
|
+
// Canonicalize merged entityRef (#2213). patch.updated is business time (#2807).
|
|
5371
5371
|
const resolveIds = this.currentHistoricalIds();
|
|
5372
5372
|
const updated: MemoryFrontmatter = entityRefs.canonicalizeEntityRefOption(
|
|
5373
5373
|
{ ...memory.frontmatter, ...patch },
|
|
@@ -5375,6 +5375,11 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
5375
5375
|
);
|
|
5376
5376
|
const refIds = typeof updated.entityRef === "string" ? resolveIds : null;
|
|
5377
5377
|
const afterStatus = updated.status ?? "active";
|
|
5378
|
+
if (!mentionReentry && entityMentionEpoch.neutral(memory.frontmatter, updated)) {
|
|
5379
|
+
const held = await holdMentionNeutralFrontmatter(this, memory, updated, () =>
|
|
5380
|
+
entityMentionEpoch.hold(() => StorageManager.frontmatterBody.call(this, memory, patch, lifecycle, true)));
|
|
5381
|
+
if (held !== undefined) return held;
|
|
5382
|
+
}
|
|
5378
5383
|
|
|
5379
5384
|
const fileContent = `${serializeFrontmatter(this.withOkfType(updated))}\n\n${memory.content}\n`;
|
|
5380
5385
|
await this.writeTombstoneBlockedFrontmatter(memory, fileContent, updated, async () => {
|
|
@@ -5390,6 +5395,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
5390
5395
|
onFailRestore: memory,
|
|
5391
5396
|
});
|
|
5392
5397
|
}
|
|
5398
|
+
if (mentionReentry && !entityMentionEpoch.neutral(memory.frontmatter, updated)) entityMentionEpoch.bump(this.baseDir);
|
|
5393
5399
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
5394
5400
|
if (memory.path.includes(`${path.sep}cold${path.sep}`)) {
|
|
5395
5401
|
this.invalidateColdMemoriesCache();
|
|
@@ -5422,9 +5428,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
5422
5428
|
lifecycle?.ruleVersion
|
|
5423
5429
|
);
|
|
5424
5430
|
if (beforeStatus !== afterStatus) {
|
|
5425
|
-
// Status
|
|
5426
|
-
// entity/derived caches and peer processes observe it (issue #1902:
|
|
5427
|
-
// restored — corpus bump alone doesn't cover status-derived views).
|
|
5431
|
+
// Status change bumps memory-status; a corpus bump does not cover it (#1902).
|
|
5428
5432
|
this.bumpMemoryStatusVersion();
|
|
5429
5433
|
}
|
|
5430
5434
|
return true;
|
|
@@ -6496,11 +6500,9 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
6496
6500
|
const memoryMap = new Map(memories.map((m) => [m.frontmatter.id, m]));
|
|
6497
6501
|
const memoryPathMap = new Map(memories.map((m) => [path.resolve(m.path), m]));
|
|
6498
6502
|
let updated = 0;
|
|
6499
|
-
//
|
|
6500
|
-
// entries in place and then re-key them to the version this flush produces
|
|
6501
|
-
// (issue #1902). Access-tracking flush is batched (consolidation / buffer
|
|
6502
|
-
// full), not per-recall, so the single corpus bump below is cheap.
|
|
6503
|
+
// Corpus version and warmth are snapshotted before the writes (#1902).
|
|
6503
6504
|
const prevVersion = this.getMemoryCorpusVersion();
|
|
6505
|
+
let entityRefMoved = false;
|
|
6504
6506
|
// Snapshot the secure-store key identity once (Cursor Medium #1902), mirroring
|
|
6505
6507
|
// readAllMemories/patchHotMemoriesCache: a mid-flush setSecureStoreKey change
|
|
6506
6508
|
// would otherwise let loop patches or the re-keyed entry be stored under a
|
|
@@ -6540,6 +6542,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
6540
6542
|
{ onFailRestore: current }
|
|
6541
6543
|
);
|
|
6542
6544
|
}
|
|
6545
|
+
if (!entityMentionEpoch.neutral(current.frontmatter, newFm)) entityRefMoved = true;
|
|
6543
6546
|
return { current, newFm };
|
|
6544
6547
|
});
|
|
6545
6548
|
if (applied === null) continue;
|
|
@@ -6557,14 +6560,11 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
6557
6560
|
}
|
|
6558
6561
|
|
|
6559
6562
|
if (updated > 0) {
|
|
6560
|
-
// Advance the corpus sentinel so
|
|
6561
|
-
// this
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
// exclusive and still the current sentinel; otherwise a peer also wrote
|
|
6566
|
-
// and we must let the next read rescan.
|
|
6567
|
-
const { produced, exclusive } = this.bumpMemoryCorpusVersionExclusive();
|
|
6563
|
+
// Advance the corpus sentinel so peers rescan (#1902). Re-key local patches
|
|
6564
|
+
// only when this bump was exclusive; a peer write must rescan instead.
|
|
6565
|
+
const { produced, exclusive } = this.bumpMemoryCorpusVersionExclusive({
|
|
6566
|
+
indexedText: entityRefMoved ? undefined : false,
|
|
6567
|
+
});
|
|
6568
6568
|
// Drop the in-flight read slot after the bump (parity with
|
|
6569
6569
|
// patchHotMemoriesCache, Cursor Medium #1902): a readAllMemories scan that
|
|
6570
6570
|
// started before the flush would otherwise keep awaiting a pre-flush scan
|
|
@@ -6574,6 +6574,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
6574
6574
|
if (
|
|
6575
6575
|
warm &&
|
|
6576
6576
|
exclusive &&
|
|
6577
|
+
!entityRefMoved &&
|
|
6577
6578
|
produced === prevVersion + 1 &&
|
|
6578
6579
|
this.getMemoryCorpusVersion() === produced &&
|
|
6579
6580
|
this.hotCacheKeyId() === keyId
|
|
@@ -7010,6 +7011,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
7010
7011
|
});
|
|
7011
7012
|
if (!written) return false;
|
|
7012
7013
|
exactReplay = written === "exact-replay";
|
|
7014
|
+
const mentionBefore = { entityRef: currentBefore.frontmatter.entityRef, origin: currentBefore.frontmatter.origin };
|
|
7013
7015
|
if (exactReplay && typeof updatedFm.entityRef === "string") {
|
|
7014
7016
|
await this.entityRefRepair.repair(
|
|
7015
7017
|
currentBefore.path,
|
|
@@ -7020,6 +7022,7 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
7020
7022
|
{ onFailRestore: currentBefore },
|
|
7021
7023
|
);
|
|
7022
7024
|
}
|
|
7025
|
+
const mentionUnchanged = entityMentionEpoch.neutral(mentionBefore, updatedFm);
|
|
7023
7026
|
const supersededAt = updatedFm.supersededAt ?? initialNow;
|
|
7024
7027
|
const beforeState = this.summarizeLifecycleState(
|
|
7025
7028
|
currentBefore.frontmatter,
|
|
@@ -7041,8 +7044,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
7041
7044
|
readMemoryByPath: (filePath) => this.readMemoryByPath(filePath),
|
|
7042
7045
|
isColdOrArchiveTierPath: (memoryPath) => this.isColdOrArchiveTierPath(memoryPath),
|
|
7043
7046
|
invalidateColdMemoriesCache: () => this.invalidateColdMemoriesCache(),
|
|
7044
|
-
invalidateAllMemoriesCache: () => this.invalidateAllMemoriesCache(),
|
|
7045
|
-
bumpMemoryCorpusVersion: () => this.bumpMemoryCorpusVersion(),
|
|
7047
|
+
invalidateAllMemoriesCache: () => this.invalidateAllMemoriesCache({ indexedText: mentionUnchanged ? false : undefined }),
|
|
7048
|
+
bumpMemoryCorpusVersion: () => this.bumpMemoryCorpusVersion({ indexedText: mentionUnchanged ? false : undefined }),
|
|
7046
7049
|
appendLifecycleEvent: async () => {
|
|
7047
7050
|
let lifecycleEvents: MemoryLifecycleEvent[] = [];
|
|
7048
7051
|
try {
|
|
@@ -7156,9 +7159,8 @@ export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
|
7156
7159
|
return true;
|
|
7157
7160
|
});
|
|
7158
7161
|
if (!written) continue;
|
|
7159
|
-
//
|
|
7160
|
-
|
|
7161
|
-
this.bumpMemoryCorpusVersion();
|
|
7162
|
+
// Corpus bump before the lifecycle append (#1902). Epoch moves only if repair changed entityRef or origin.
|
|
7163
|
+
this.bumpMemoryCorpusVersion({ indexedText: entityMentionEpoch.neutral(currentBefore.frontmatter, updatedFm) ? false : undefined });
|
|
7162
7164
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.archiveMemories", {
|
|
7163
7165
|
memoryId: id,
|
|
7164
7166
|
eventType: "archived",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/embedding-fallback.ts"],"sourcesContent":["import path from \"node:path\";\nimport { log } from \"./logger.js\";\nimport {\n resolveMemoryLifecycleCapabilities,\n resolveLocalLlmCapabilities,\n} from \"./capabilities.js\";\nimport { readEnvVar } from \"./runtime/env.js\";\nimport type { PluginConfig } from \"./types.js\";\nimport { resolvePipelineProcessingCapabilities } from \"./capabilities.js\";\nimport {\n getHostEmbeddingProvider,\n type HostEmbeddingProvider,\n normalizeHostEmbeddingVector,\n} from \"./host-embedding-provider.js\";\nimport {\n EmbeddingIndexFileStore,\n EmbeddingIndexStorageError,\n type EmbeddingIndexEntry,\n type EmbeddingIndexFile,\n type EmbeddingIndexIdentity,\n type EmbeddingProviderType,\n EmbeddingIndexStoreIo,\n} from \"./embedding-index-storage.js\";\nexport {\n EmbeddingIndexCapacityError,\n EmbeddingIndexStorageError,\n} from \"./embedding-index-storage.js\";\nexport type {\n EmbeddingIndexStatusFile,\n EmbeddingIndexWriteFailure,\n} from \"./embedding-index-storage.js\";\ntype ProviderConfig = {\n type: EmbeddingProviderType;\n model: string;\n endpoint?: string;\n headers?: Record<string, string>;\n hostProvider?: HostEmbeddingProvider;\n};\n\ntype EmbeddingResult = {\n provider: ProviderConfig;\n vector: number[];\n};\n\ntype EmbeddingIndexComparable =\n | EmbeddingIndexIdentity\n | Pick<ProviderConfig, \"type\" | \"model\">;\n\nconst DEFAULT_OPENAI_MODEL = \"text-embedding-3-small\";\n\n/**\n * Thrown by `EmbeddingFallback.search()` (via `embed()`) when the embedding\n * backend is effectively unavailable on the lookup path — either because the\n * HTTP fetch exceeded its deadline OR because the endpoint returned a non-2xx\n * status code. Callers that need to distinguish a backend outage from \"no\n * candidates\" can `instanceof`-check against this class.\n *\n * Round 9 fix (Finding UZqB): previously a timeout returned null from embed(),\n * which caused search() to return [] silently. decideSemanticDedup then\n * classified the result as no_candidates instead of backend_unavailable, so\n * the per-batch batchBackendUnavailable short-circuit never activated and\n * batches of N facts each paid a full timeout roundtrip.\n *\n * Round 10 fix (Findings Ui1J + Ui1L): search() now only re-throws this error\n * when the caller explicitly passes `{ throwOnTimeout: true }`. Without that\n * flag search() catches it and returns [] instead, preserving fail-open\n * semantics for recall-path callers (searchEmbeddingFallback) that have no\n * try/catch. Only the semantic-dedup path (semanticDedupLookup) passes the\n * flag so it can still reach decideSemanticDedup's backend_unavailable branch.\n *\n * Round 11 fix (Finding Ur_J): `embed()` now also throws this error from the\n * lookup path when the HTTP response is non-2xx (e.g. 429, 500, 503). Without\n * this, repeated 5xx outages would each return null → [] → no_candidates and\n * subsequent facts in the same batch would all pay full roundtrips instead of\n * tripping the per-batch backend_unavailable short-circuit.\n *\n * The class name is kept for backward compatibility — `EmbeddingTimeoutError`\n * now signals \"lookup backend unavailable\" rather than strictly \"timed out\".\n */\nimport {\n assertEmbeddingGenerationLockHeld,\n embeddingGenerationLockPath,\n EmbeddingGenerationLockUnavailableError,\n withEmbeddingGenerationLock,\n type EmbeddingGenerationLockSection,\n} from \"./embedding-generation-lock.js\";\n\nexport class EmbeddingTimeoutError extends Error {\n override readonly name = \"EmbeddingTimeoutError\" as const;\n constructor(message: string) {\n super(message);\n }\n}\n\nexport class EmbeddingProviderUnavailableError extends Error {\n override readonly name = \"EmbeddingProviderUnavailableError\" as const;\n constructor(message: string) {\n super(message);\n }\n}\n\nfunction isLookupBackendUnavailableError(err: unknown): boolean {\n return (\n err instanceof EmbeddingTimeoutError ||\n err instanceof EmbeddingProviderUnavailableError\n );\n}\n\n/**\n * Maximum time to wait for an embedding HTTP request on the LOOKUP/query\n * path before giving up.\n *\n * The write-time semantic dedup guard in orchestrator.persistExtraction()\n * blocks each candidate fact on an embedding lookup. If the embedding\n * endpoint hangs (degraded OpenAI, stalled local gateway, DNS timeout),\n * extraction would otherwise stall indefinitely — a single bad backend\n * could freeze the entire persist loop. Bounding the fetch here ensures\n * the decision path fails open (returns null) within a predictable window\n * and writes proceed as non-duplicates.\n *\n * Tests can override via REMNIC_EMBEDDING_FETCH_TIMEOUT_MS so they don't\n * have to wait the full default on hung-fetch assertions.\n *\n * Related: joshuaswarren/remnic#373, PR #399 P1/P2 review.\n */\nconst DEFAULT_EMBEDDING_LOOKUP_TIMEOUT_MS = 5000;\n\n/**\n * Maximum time to wait for an embedding HTTP request on the INDEX path.\n *\n * Indexing runs asynchronously after a memory has already been persisted\n * to disk. It does not block extraction or writes — it only updates the\n * embedding index used by later semantic dedup lookups. A slow local\n * CPU-backed embedding model can legitimately take tens of seconds per\n * call, so applying the short lookup timeout here silently dropped index\n * updates and caused later dedup lookups to miss recently persisted\n * memories. Use a much larger budget on this path.\n *\n * Tests can override via REMNIC_EMBEDDING_INDEX_TIMEOUT_MS.\n */\nconst DEFAULT_EMBEDDING_INDEX_TIMEOUT_MS = 120_000;\n\nfunction resolveEmbeddingLookupTimeoutMs(): number {\n const raw = readEnvVar(\"REMNIC_EMBEDDING_FETCH_TIMEOUT_MS\");\n if (raw) {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) {\n return Math.floor(parsed);\n }\n }\n return DEFAULT_EMBEDDING_LOOKUP_TIMEOUT_MS;\n}\n\nfunction resolveEmbeddingIndexTimeoutMs(): number {\n const raw = readEnvVar(\"REMNIC_EMBEDDING_INDEX_TIMEOUT_MS\");\n if (raw) {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) {\n return Math.floor(parsed);\n }\n }\n return DEFAULT_EMBEDDING_INDEX_TIMEOUT_MS;\n}\n\n/**\n * Options for the low-level embed() call.\n *\n * `mode` selects the timeout profile:\n * - \"lookup\" (default): bounded by the short lookup budget; fails open fast.\n * - \"index\": bounded by a much longer budget so slow backends can still\n * index newly persisted memories.\n */\nexport type EmbedMode = \"lookup\" | \"index\";\n\nexport class EmbeddingFallback {\n private readonly store: EmbeddingIndexFileStore;\n private loaded: EmbeddingIndexFile | null = null;\n /** True when `loaded` was read from disk rather than started fresh, so identity probes can trust it. */\n private loadedFromDisk = false;\n private mutationQueue: Promise<void> = Promise.resolve();\n\n /** The canonical state dir scoping this fallback's generation mutation lock. */\n private readonly generationStateDir: string;\n private readonly generationLockPath: string;\n private lockSection: EmbeddingGenerationLockSection | null = null;\n\n constructor(private readonly config: PluginConfig, storeIo?: EmbeddingIndexStoreIo) {\n const stateDir = path.join(config.memoryDir, \"state\");\n this.generationStateDir = stateDir;\n this.generationLockPath = embeddingGenerationLockPath(stateDir);\n this.store = new EmbeddingIndexFileStore(\n path.join(stateDir, \"embeddings.json\"),\n path.join(stateDir, \"embeddings\"),\n path.join(stateDir, \"embedding-fallback-status.json\"),\n storeIo,\n );\n }\n\n async isAvailable(): Promise<boolean> {\n return (await this.resolveProvider()) !== null;\n }\n\n /**\n * Embed an array of texts and return their embedding vectors.\n *\n * This is the public batch-embed interface used by semantic chunking\n * (Finding 1, PR #420 post-merge). Texts are grouped into batches of\n * `embeddingBatchSize` (from `semanticChunkingConfig`, default 32) and\n * each batch is dispatched concurrently via `Promise.all()`. This\n * preserves the semantic intent of `embeddingBatchSize` — without batching,\n * every text incurred a sequential HTTP round-trip, making the batch size\n * config ineffective. (PR #439 post-merge Finding 2.)\n *\n * If the provider is unavailable or any single embedding fails, the method\n * throws so the caller can fall back to recursive chunking.\n */\n async embedTexts(texts: string[]): Promise<number[][]> {\n const provider = await this.resolveProvider();\n if (!provider) {\n throw new Error(\"Embedding provider is not available\");\n }\n\n const batchSize = Math.max(\n 1,\n this.config.semanticChunkingConfig?.embeddingBatchSize ?? 32,\n );\n\n const vectors: number[][] = [];\n for (let i = 0; i < texts.length; i += batchSize) {\n const batch = texts.slice(i, i + batchSize);\n const batchResults = await Promise.all(\n batch.map((text) => this.embed(text, provider, { mode: \"lookup\" })),\n );\n for (const vec of batchResults) {\n if (!vec) {\n throw new Error(\"Embedding returned null for input text\");\n }\n vectors.push(vec);\n }\n }\n return vectors;\n }\n\n /**\n * Nearest-neighbor search against the embedding index.\n *\n * @param query The query string to embed and search for.\n * @param limit Max number of hits to return.\n * @param options Optional filters.\n * - `pathPrefix` Restrict candidates to entries whose indexed `path`\n * starts with this prefix (relative to `memoryDir`).\n * Used by the semantic dedup guard to scope lookups\n * to the target namespace so a high-similarity hit\n * from a different namespace can't suppress a write\n * in the target namespace. Default: no filter.\n * - `pathExcludePrefixes`\n * Exclude any entry whose indexed `path` starts with\n * any of these prefixes. Used for the default\n * namespace case: when the default namespace lives at\n * `memoryDir` root (legacy layout) we still want to\n * exclude `namespaces/<other>/…` entries.\n */\n async search(\n query: string,\n limit: number,\n options: {\n pathPrefix?: string;\n pathExcludePrefixes?: readonly string[];\n /**\n * When true, an `EmbeddingTimeoutError` from the embedding backend is\n * re-thrown to the caller. Use this on the semantic-dedup path so\n * `decideSemanticDedup`'s catch block can classify the result as\n * `reason=\"backend_unavailable\"` and activate the per-batch\n * short-circuit.\n *\n * When false (the default), a timeout is caught here and search()\n * returns [] instead — preserving fail-open semantics for the recall\n * path (`searchEmbeddingFallback`) which has no surrounding try/catch.\n * Without this gate a timed-out embedding request on the recall path\n * would propagate as an unhandled rejection and abort recall entirely.\n * (Round 10 fix, Findings Ui1J + Ui1L.)\n */\n throwOnTimeout?: boolean;\n } = {},\n ): Promise<Array<{ id: string; score: number; path: string }>> {\n const provider = await this.resolveProvider();\n if (!provider) return [];\n\n let queryResult = await this.embedForSearch(query, provider, options);\n if (!queryResult) return [];\n // Effective query embedding: swapped to the disk index's provider\n // below when the on-disk generation has a different identity. `let` so\n // the swap reassigns; TS narrowing bound for control flow.\n let active = queryResult;\n\n // Recall fails OPEN on index read failures (corrupt/oversized/mixed\n // generations, unreadable shard dirs): return [] without caching so a\n // later mutation still revalidates and fails closed (issue #3146).\n try {\n const diskIdentity = await this.readIndexIdentityFromDisk();\n if (diskIdentity && !sameIndexIdentity(diskIdentity, queryResult.provider)) {\n // The provider swap needs a NETWORK embed: run it OUTSIDE the\n // generation lock, then re-verify the identity under the lock below.\n const diskProvider = await this.resolveFallbackProviderForIndexIdentity(diskIdentity);\n if (diskProvider) {\n const diskQueryResult = await this.embedForSearch(query, diskProvider, options);\n if (diskQueryResult && sameIndexIdentity(diskIdentity, diskQueryResult.provider)) {\n active = diskQueryResult;\n } else {\n log.debug(\n `embedding fallback search skipped: preserved ${diskIdentity.provider}/${diskIdentity.model} index is unavailable for lookup`,\n );\n return [];\n }\n } else {\n log.debug(\n `embedding fallback search skipped: query provider ${queryResult.provider.type}/${queryResult.provider.model} does not match existing ${diskIdentity.provider}/${diskIdentity.model} index`,\n );\n return [];\n }\n }\n\n // Cold load only (cache miss): the complete shard enumeration + read\n // happens under the generation mutation lock so a concurrent swap can\n // never leave a cached SUBSET of the generation (codex\n // PRRT_kwDORJXyws6mZ72p). Warm cached searches stay lock-free.\n let index: EmbeddingIndexFile;\n try {\n index = await this.loadIndexUnderGenerationLock(active.provider);\n } catch (err) {\n if (err instanceof EmbeddingGenerationLockUnavailableError) {\n // Existing read diagnostic, recorded once per failed load; recall\n // itself fails open.\n await this.store.recordIndexStatusForLoad(\n `generation mutation lock unavailable: ${err instanceof Error ? err.message : String(err)}`,\n );\n return [];\n }\n throw err;\n }\n const ids = Object.keys(index.entries);\n if (ids.length === 0) return [];\n\n const includePrefix = normalizePathPrefix(options.pathPrefix);\n const excludePrefixes = (options.pathExcludePrefixes ?? [])\n .map((p) => normalizePathPrefix(p))\n .filter((p): p is string => typeof p === \"string\");\n\n const scored = ids\n .map((id) => {\n const entry = index.entries[id];\n return {\n id,\n path: entry.path,\n score: cosineSimilarity(active.vector, entry.vector),\n };\n })\n .filter((r) => {\n if (!Number.isFinite(r.score)) return false;\n const normalized = normalizeEntryPath(r.path);\n if (includePrefix !== undefined && !normalized.startsWith(includePrefix)) {\n return false;\n }\n for (const excl of excludePrefixes) {\n if (normalized.startsWith(excl)) return false;\n }\n return true;\n })\n .sort((a, b) => b.score - a.score)\n .slice(0, Math.max(1, limit));\n\n return scored;\n } catch (err) {\n if (err instanceof EmbeddingIndexStorageError) {\n log.debug(\n `embedding fallback index unreadable on recall path, failing open: ${err instanceof Error ? err.message : String(err)}`,\n );\n return [];\n }\n throw err;\n }\n }\n\n async indexFile(memoryId: string, content: string, filePath: string): Promise<void> {\n const provider = await this.resolveProvider();\n if (!provider) return;\n // Indexing is not on the write-critical path: a newly persisted memory\n // has already been written to disk by the time we reach this call. Use\n // the long \"index\" timeout so slow local embedding backends can still\n // add the entry to the index. Previously this used the short lookup\n // budget and silently dropped updates, leaving later dedup lookups\n // blind to the memory. Related: PR #399 P2.\n const result = await this.embedWithEffectiveProvider(content, provider, {\n mode: \"index\",\n });\n if (!result) return;\n\n await this.enqueueIndexMutation(memoryId, async () => {\n try {\n const existing = await this.readIndexIdentityFromDisk();\n if (\n existing &&\n !sameIndexIdentity(existing, result.provider) &&\n !canReplaceIndexIdentity(existing, result.provider)\n ) {\n log.debug(\n `embedding fallback index update skipped: ${result.provider.type}/${result.provider.model} would replace existing ${existing.provider}/${existing.model} index`,\n );\n return;\n }\n const index = await this.loadIndex(result.provider);\n const relPath = toMemoryRelativePath(this.config.memoryDir, filePath);\n index.entries[memoryId] = {\n vector: result.vector,\n path: relPath,\n };\n await this.saveIndex(index, { touchedIds: [memoryId], memoryId });\n } catch (err) {\n await this.store.recordIndexWriteOutcome(err, memoryId);\n throw err;\n }\n await this.store.recordIndexWriteOutcome(null, memoryId);\n });\n }\n\n async removeFromIndex(memoryId: string): Promise<void> {\n const provider = await this.resolveProvider();\n if (!provider) return;\n\n await this.enqueueIndexMutation(memoryId, async () => {\n const providers = [provider];\n const diskIdentity = await this.readIndexIdentityFromDisk();\n if (\n diskIdentity &&\n !providers.some((entry) => sameIndexIdentity(entry, diskIdentity))\n ) {\n providers.push(providerFromIndexIdentity(diskIdentity));\n }\n if (provider.type === \"host\") {\n const fallbackProvider = await this.resolveProvider({ includeHost: false });\n if (\n fallbackProvider &&\n !providers.some((entry) => sameIndexIdentity(entry, fallbackProvider))\n ) {\n providers.push(fallbackProvider);\n }\n }\n\n let saved = false;\n for (const indexProvider of providers) {\n try {\n const index = await this.loadIndex(indexProvider);\n if (!index.entries[memoryId]) continue;\n delete index.entries[memoryId];\n await this.saveIndex(index, { touchedIds: [memoryId], memoryId });\n saved = true;\n } catch (err) {\n await this.store.recordIndexWriteOutcome(err, memoryId);\n throw err;\n }\n }\n // A no-op removal (id absent from every candidate index) performs no\n // save: it must NOT clear a recorded persistence failure, or the\n // operator console would report an unresolved failure as fixed\n // without any demonstration that storage is writable (round 6).\n if (saved) await this.store.recordIndexWriteOutcome(null, memoryId);\n });\n }\n\n private enqueueIndexMutation<T>(memoryId: string | undefined, mutation: () => Promise<T>): Promise<T> {\n const run = this.mutationQueue\n .catch(() => undefined)\n .then(() =>\n withEmbeddingGenerationLock(this.generationStateDir, async (section) => {\n this.lockSection = section;\n try {\n // A peer process may have persisted while this process did not\n // hold the generation lock: drop any cached index so the\n // mutation loads the real disk state (codex P1).\n this.loaded = null;\n this.loadedFromDisk = false;\n // Recover an interrupted replacement BEFORE the mutation's identity\n // probes and existence checks: a deletion no-op must not skip\n // persistence while the published generation sits in the rename-gap\n // backup, and insertions must load the restored vectors rather than\n // a fresh empty index (issue #3148, codex round 3).\n let recovered = false;\n try {\n recovered = await this.store.recoverIfInterrupted(this.generationFence());\n } catch (err) {\n // Record the recovery failure durably BEFORE rejecting the\n // mutation: the write-outcome try below is never reached.\n await this.store.recordIndexWriteOutcome(err).catch(() => undefined);\n throw err;\n }\n if (recovered) {\n this.loaded = null;\n this.loadedFromDisk = false;\n }\n return await mutation();\n } finally {\n this.lockSection = null;\n }\n }).catch(async (err) => {\n // A lock ACQUISITION failure never reaches the mutation closure's\n // own write-outcome recording. Route it through the existing\n // durable diagnostic exactly once (codex PRRT_kwDORJXyws6mZ8qS);\n // in-lock failures were already recorded by the closure.\n if (err instanceof EmbeddingGenerationLockUnavailableError) {\n await this.store.recordIndexWriteOutcome(err, memoryId).catch(() => undefined);\n }\n throw err;\n }));\n this.mutationQueue = run.then(\n () => undefined,\n () => undefined,\n );\n return run;\n }\n\n private async resolveProvider(\n options: { includeHost?: boolean } = {},\n ): Promise<ProviderConfig | null> {\n if (!resolveMemoryLifecycleCapabilities(this.config).embeddingFallback) return null;\n\n if (\n options.includeHost !== false &&\n resolvePipelineProcessingCapabilities(this.config).hostEmbeddingProvider !== false\n ) {\n const hostProvider = getHostEmbeddingProvider(this.config.memoryDir);\n if (hostProvider) {\n return {\n type: \"host\",\n model: hostProvider.model || hostProvider.id,\n hostProvider,\n };\n }\n }\n\n const preferred = this.config.embeddingFallbackProvider;\n const providers = preferred === \"auto\" ? [\"openai\", \"local\"] : [preferred];\n\n for (const p of providers) {\n if (p === \"openai\") {\n const provider = this.createOpenAiProvider();\n if (provider) return provider;\n }\n\n if (p === \"local\") {\n const provider = this.createLocalProvider();\n if (provider) return provider;\n }\n }\n\n return null;\n }\n\n private async resolveFallbackProviderForIndexIdentity(\n identity: EmbeddingIndexIdentity,\n ): Promise<ProviderConfig | null> {\n if (identity.provider === \"openai\") {\n const provider = this.createOpenAiProvider();\n return provider && sameIndexIdentity(provider, identity) ? provider : null;\n }\n if (identity.provider === \"local\") {\n const provider = this.createLocalProvider();\n return provider && sameIndexIdentity(provider, identity) ? provider : null;\n }\n return null;\n }\n\n private createOpenAiProvider(): ProviderConfig | null {\n if (!this.config.openaiApiKey) return null;\n const baseUrl = this.config.openaiBaseUrl ?? \"https://api.openai.com/v1\";\n return {\n type: \"openai\",\n model: DEFAULT_OPENAI_MODEL,\n endpoint: `${baseUrl.replace(/\\/$/, \"\")}/embeddings`,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.config.openaiApiKey}`,\n },\n };\n }\n\n private createLocalProvider(): ProviderConfig | null {\n if (!resolveLocalLlmCapabilities(this.config).localLlm || !this.config.localLlmUrl) return null;\n const base = this.config.localLlmUrl.replace(/\\/$/, \"\");\n const endpoint = /\\/v1$/i.test(base) ? `${base}/embeddings` : `${base}/v1/embeddings`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...(this.config.localLlmHeaders ?? {}),\n };\n if (this.config.localLlmApiKey && this.config.localLlmAuthHeader !== false) {\n headers.Authorization = `Bearer ${this.config.localLlmApiKey}`;\n }\n return {\n type: \"local\",\n model:\n this.config.embeddingFallbackModel ||\n this.config.localLlmModel ||\n DEFAULT_OPENAI_MODEL,\n endpoint,\n headers,\n };\n }\n\n private async embedForSearch(\n query: string,\n provider: ProviderConfig,\n options: { throwOnTimeout?: boolean } = {},\n ): Promise<EmbeddingResult | null> {\n try {\n return await this.embedWithEffectiveProvider(query, provider, {\n mode: \"lookup\",\n });\n } catch (err) {\n if (isLookupBackendUnavailableError(err)) {\n if (options.throwOnTimeout) {\n throw err;\n }\n // Fail-open: recall-path callers get an empty result rather than an\n // unhandled rejection that would abort recall entirely.\n log.debug(\"embedding fallback search: backend unavailable on lookup, returning [] (throwOnTimeout=false)\");\n return null;\n }\n throw err;\n }\n }\n\n private async embed(\n input: string,\n provider: ProviderConfig,\n options: { mode?: EmbedMode } = {},\n ): Promise<number[] | null> {\n const result = await this.embedWithEffectiveProvider(input, provider, options);\n return result?.vector ?? null;\n }\n\n private async embedWithEffectiveProvider(\n input: string,\n provider: ProviderConfig,\n options: { mode?: EmbedMode } = {},\n ): Promise<EmbeddingResult | null> {\n // Bound the fetch so a hung embedding endpoint cannot stall callers.\n // The lookup path uses a short budget (see DEFAULT_EMBEDDING_LOOKUP_TIMEOUT_MS\n // docblock) so semantic dedup fails open fast. The index path uses a\n // much longer budget because slow local backends (CPU embedding models)\n // otherwise drop index updates and blind later dedup lookups. See\n // DEFAULT_EMBEDDING_INDEX_TIMEOUT_MS docblock and PR #399 P2 review.\n const mode: EmbedMode = options.mode ?? \"lookup\";\n const timeoutMs =\n mode === \"index\"\n ? resolveEmbeddingIndexTimeoutMs()\n : resolveEmbeddingLookupTimeoutMs();\n if (provider.type === \"host\") {\n const vector = await this.embedWithHostProvider(input, provider, mode, timeoutMs);\n if (vector) return { provider, vector };\n const fallbackProvider = await this.resolveProvider({ includeHost: false });\n if (!fallbackProvider) {\n if (mode === \"lookup\") {\n throw new EmbeddingProviderUnavailableError(\n `host embedding provider unavailable (${provider.hostProvider?.id ?? provider.model})`,\n );\n }\n return null;\n }\n return this.embedWithEffectiveProvider(input, fallbackProvider, options);\n }\n if (!provider.endpoint || !provider.headers) return null;\n try {\n const res = await fetch(provider.endpoint, {\n method: \"POST\",\n headers: provider.headers,\n body: JSON.stringify({\n model: provider.model,\n input: input.slice(0, 8000),\n encoding_format: \"float\",\n }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!res.ok) {\n log.debug(`embedding fallback request failed: ${provider.type} ${res.status}`);\n // Round 11 fix (Finding Ur_J): on the LOOKUP path, a non-2xx response\n // means the embedding backend is effectively unavailable. Throw the\n // tagged error so search() (when called with throwOnTimeout) propagates\n // to decideSemanticDedup's backend_unavailable branch, activating the\n // per-batch short-circuit. Without this, repeated 429/5xx responses\n // would silently return [] for every fact in the batch.\n //\n // On the INDEX path a non-2xx is non-fatal (the memory is already\n // persisted; index update can be skipped) — return null there.\n if (mode === \"lookup\") {\n throw new EmbeddingTimeoutError(\n `embedding backend returned ${res.status} (${provider.type})`,\n );\n }\n return null;\n }\n const payload = (await res.json()) as any;\n const vector = payload?.data?.[0]?.embedding;\n if (!Array.isArray(vector)) return null;\n const normalized = vector\n .map((n: unknown) => Number(n))\n .filter((n: number) => Number.isFinite(n));\n return normalized.length > 0 ? { provider, vector: normalized } : null;\n } catch (err) {\n // Round 11 (Finding Ur_J): the !res.ok branch above throws\n // EmbeddingTimeoutError directly. Re-throw it here so the catch does\n // not swallow our own intentional signal back into a null return.\n if (isLookupBackendUnavailableError(err)) {\n throw err;\n }\n // AbortSignal.timeout throws a DOMException with name \"TimeoutError\";\n // surface at warn level so operators can distinguish slow backends from\n // generic errors.\n const isTimeout =\n err instanceof Error &&\n (err.name === \"TimeoutError\" || err.name === \"AbortError\");\n if (isTimeout) {\n log.warn(\n `embedding fallback fetch timed out after ${timeoutMs}ms (${provider.type}, mode=${mode})`,\n );\n // Round 9 fix (Finding UZqB): on the LOOKUP path a timeout means the\n // embedding backend is effectively unavailable — re-throw so that\n // search() propagates the error to semanticDedupLookup, which lets it\n // reach decideSemanticDedup's catch block and return\n // reason=\"backend_unavailable\". Without this, search() would silently\n // return [] and the per-batch batchBackendUnavailable flag would never\n // flip, causing subsequent facts in the same batch to each pay a full\n // timeout roundtrip (N × timeout instead of 1 × timeout).\n //\n // On the INDEX path a timeout is not fatal (the memory is already\n // persisted; index update can be skipped) — return null there so\n // indexFile() stays non-blocking.\n if (mode === \"lookup\") {\n throw new EmbeddingTimeoutError(\n `embedding backend timed out after ${timeoutMs}ms (${provider.type})`,\n );\n }\n } else {\n // Round 12 fix (PR #399 thread PRRT_kwDORJXyws56U6Gi): non-timeout\n // transport failures (ECONNREFUSED, DNS errors, TLS failures) are just\n // as fatal as timeouts on the LOOKUP path — the embedding backend is\n // effectively unreachable. Throw EmbeddingTimeoutError so that\n // search() (when called with throwOnTimeout:true) propagates the error\n // to decideSemanticDedup's backend_unavailable branch, activating the\n // per-batch short-circuit. Without this, each fact in the batch would\n // pay a full ECONNREFUSED roundtrip and return null → [] → no_candidates,\n // preventing batchBackendUnavailable from ever being set.\n //\n // On the INDEX path a transport failure is non-fatal — the memory is\n // already persisted; index update can be safely skipped.\n if (mode === \"lookup\") {\n log.warn(\n `embedding fallback transport error on lookup path (${provider.type}): ${err}`,\n );\n throw new EmbeddingTimeoutError(\n `embedding backend transport failure (${provider.type}): ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n log.debug(`embedding fallback error: ${err}`);\n }\n return null;\n }\n }\n\n private async embedWithHostProvider(\n input: string,\n provider: ProviderConfig,\n mode: EmbedMode,\n timeoutMs: number,\n ): Promise<number[] | null> {\n const hostProvider = provider.hostProvider;\n if (!hostProvider) return null;\n try {\n const vector = await hostProvider.embed(input.slice(0, 8000), {\n signal: AbortSignal.timeout(timeoutMs),\n inputType: mode === \"lookup\" ? \"query\" : \"document\",\n });\n return normalizeHostEmbeddingVector(vector);\n } catch (err) {\n log.debug(`host embedding provider error: ${hostProvider.id}: ${err}`);\n return null;\n }\n }\n\n /**\n * Load the authoritative generation for `provider`. STRICT: an unreadable,\n * malformed, foreign-shaped, or mixed-identity index throws\n * EmbeddingIndexStorageError instead of ever returning a partial view —\n * recall catches it at its boundary and fails open uncached; mutations\n * propagate it and fail closed. This.loaded is only ever assigned a fully\n * validated generation.\n */\n /**\n * A fence the store calls immediately before destructive writes: reasserts\n * THIS section's lock ownership after potentially long serialization.\n * Fail closed when no section is held — persisting unsynchronized would\n * reintroduce the lost-update race.\n */\n private generationFence(): () => Promise<void> {\n const section = this.lockSection;\n if (!section) {\n return async () => {\n throw new EmbeddingIndexStorageError(\n \"embedding index mutation attempted outside the generation lock; refusing to publish\",\n );\n };\n }\n return () => assertEmbeddingGenerationLockHeld(this.generationLockPath, section);\n }\n\n /**\n * Cache-warm path: a normal search serves the in-memory index WITHOUT the\n * lock (pre-existing staleness semantics: a peer swap between searches is\n * refreshed by the next cold load). On a cache miss the COMPLETE load runs\n * under the generation mutation lock so the enumeration + shard reads see\n * exactly one generation. The identity is re-checked under the lock on the\n * cold path; the caller has already performed any provider-swap embed\n * outside the lock.\n */\n private async loadIndexUnderGenerationLock(provider: ProviderConfig): Promise<EmbeddingIndexFile> {\n if (this.loaded && this.loadedFromDisk) return this.loaded;\n return await withEmbeddingGenerationLock(this.generationStateDir, async () => {\n // Another in-process consumer may have warmed the cache while this\n // call waited on the lock.\n if (this.loaded && this.loadedFromDisk) return this.loaded;\n // Re-check the on-disk identity UNDER the lock: a peer swap during the\n // caller's embed means the cold load must fail open, never serve a\n // mixed generation (codex PRRT_kwDORJXyws6mZ72p).\n const diskIdentity = await this.store.identityFromDisk();\n if (diskIdentity && !sameIndexIdentity(diskIdentity, provider)) {\n throw new EmbeddingIndexStorageError(\n `embedding index identity swapped to ${diskIdentity.provider}/${diskIdentity.model} during the cold load; failing open`,\n );\n }\n return await this.loadIndex(provider);\n });\n }\n\n private async loadIndex(provider: ProviderConfig): Promise<EmbeddingIndexFile> {\n if (this.loaded && this.loaded.provider === provider.type && this.loaded.model === provider.model) {\n return this.loaded;\n }\n\n const merged: Record<string, EmbeddingIndexEntry> = {};\n let diskIdentity: EmbeddingIndexIdentity | null = null;\n\n const layout = await this.store.detectLayout();\n if (layout === \"sharded\") {\n // Published generation: shards are the ONLY authoritative state. A\n // legacy embeddings.json left over by a crash between the atomic\n // publish and its recovery rename is never merged (single-generation\n // rule, issue #3146).\n diskIdentity = await this.store.readShardGenerationInto(merged);\n } else if (layout === \"legacy\") {\n const read = await this.store.readLegacy();\n if (read.outcome === \"ok\") {\n diskIdentity = { provider: read.file.provider, model: read.file.model };\n Object.assign(merged, read.file.entries);\n } else if (read.outcome === \"unreadable\") {\n await this.store.recordIndexStatusForLoad(read.reason);\n throw new EmbeddingIndexStorageError(\n `refusing to continue from an unreadable embedding index at ${this.store.legacyPath}: ${read.reason}`,\n );\n } else if (read.outcome === \"foreign\") {\n // Valid JSON but not an index we recognize (wrong version/shape).\n // Overwriting it would destroy unknown data, so mutation fails\n // closed; the file stays in place for recovery.\n await this.store.recordIndexStatusForLoad(\"unrecognized index format\");\n throw new EmbeddingIndexStorageError(\n `refusing to continue from a malformed embedding index at ${this.store.legacyPath} (unrecognized format); file preserved in place`,\n );\n }\n // absent falls through to a fresh index; a readable legacy index with\n // a different provider identity keeps the existing replace semantics\n // (gated by canReplaceIndexIdentity at the mutation sites).\n }\n\n if (diskIdentity && diskIdentity.provider === provider.type && diskIdentity.model === provider.model) {\n this.loaded = {\n version: 1,\n provider: provider.type,\n model: provider.model,\n entries: merged,\n };\n this.loadedFromDisk = true;\n return this.loaded;\n }\n\n this.loaded = {\n version: 1,\n provider: provider.type,\n model: provider.model,\n entries: {},\n };\n this.loadedFromDisk = false;\n return this.loaded;\n }\n\n private async readIndexIdentityFromDisk(): Promise<EmbeddingIndexIdentity | null> {\n // After a load, `loaded` mirrors the disk state exactly (every mutation\n // saves through saveIndex and a failed save invalidates the cache), so\n // skip re-reading on every mutation.\n if (this.loaded && this.loadedFromDisk) {\n return { provider: this.loaded.provider, model: this.loaded.model };\n }\n return this.store.identityFromDisk();\n }\n\n /**\n * Persist the index through the file store. On any failure the in-memory\n * cache is invalidated: entries mutated in the cached map must never be\n * mistaken for durable state by a retry or a later search.\n */\n private async saveIndex(\n index: EmbeddingIndexFile,\n opts: { touchedIds?: readonly string[]; memoryId?: string } = {},\n ): Promise<void> {\n try {\n await this.store.persist(index, { ...opts, fence: this.generationFence() });\n } catch (err) {\n this.loaded = null;\n this.loadedFromDisk = false;\n throw err;\n }\n this.loaded = index;\n this.loadedFromDisk = true;\n }\n}\n\nfunction toMemoryRelativePath(memoryDir: string, filePath: string): string {\n if (!path.isAbsolute(filePath)) return filePath;\n const rel = path.relative(memoryDir, filePath);\n return rel.startsWith(\"..\") ? filePath : rel;\n}\n\n/**\n * Normalize an index entry path to forward-slashes for stable prefix\n * comparison. Entries are stored as `path.relative(memoryDir, …)` output,\n * which on Windows uses back-slashes. Normalize both sides so prefix\n * matching is OS-independent.\n *\n * Also strip a leading `./` so this helper's output is symmetric with\n * `normalizePathPrefix` below. `toMemoryRelativePath` is a pass-through for\n * non-absolute filePath inputs, so an index entry could legitimately carry a\n * stored path like `\"./namespaces/alpha/facts/f.md\"`. Without this strip, a\n * caller-supplied prefix `\"./namespaces/alpha\"` (which `normalizePathPrefix`\n * rewrites to `\"namespaces/alpha/\"`) would silently miss that entry and\n * namespace-scoped dedup would either let a near-duplicate through or fail\n * to exclude a cross-namespace hit.\n */\nfunction normalizeEntryPath(p: string): string {\n let out = p.replace(/\\\\/g, \"/\");\n if (out.startsWith(\"./\")) out = out.slice(2);\n return out;\n}\n\n/**\n * Normalize a caller-supplied path prefix:\n * - Return `undefined` for nullish/empty input (no filter).\n * - Replace back-slashes with forward-slashes.\n * - Strip a leading `./`.\n * - Ensure a trailing `/` so `\"namespaces/a\"` doesn't accidentally match\n * `\"namespaces/another/…\"`.\n */\nfunction normalizePathPrefix(prefix: string | undefined): string | undefined {\n if (prefix === undefined || prefix === null) return undefined;\n let p = String(prefix).replace(/\\\\/g, \"/\");\n if (p.startsWith(\"./\")) p = p.slice(2);\n if (p.length === 0) return undefined;\n if (!p.endsWith(\"/\")) p = `${p}/`;\n return p;\n}\n\nfunction sameIndexIdentity(\n left: EmbeddingIndexComparable,\n right: EmbeddingIndexComparable,\n): boolean {\n return indexIdentityProvider(left) === indexIdentityProvider(right) && left.model === right.model;\n}\n\nfunction indexIdentityProvider(identity: EmbeddingIndexComparable): EmbeddingProviderType {\n return \"provider\" in identity ? identity.provider : identity.type;\n}\n\nfunction canReplaceIndexIdentity(\n existing: EmbeddingIndexIdentity,\n replacement: Pick<ProviderConfig, \"type\" | \"model\">,\n): boolean {\n return existing.provider === \"host\" && !sameIndexIdentity(existing, replacement);\n}\n\nfunction providerFromIndexIdentity(identity: EmbeddingIndexIdentity): ProviderConfig {\n return {\n type: identity.provider,\n model: identity.model,\n };\n}\n\nfunction cosineSimilarity(a: number[], b: number[]): number {\n const n = Math.min(a.length, b.length);\n if (n === 0) return 0;\n let dot = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < n; i++) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dot += av * bv;\n normA += av * av;\n normB += bv * bv;\n }\n const denom = Math.sqrt(normA) * Math.sqrt(normB);\n if (denom === 0) return 0;\n return dot / denom;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,OAAO,UAAU;AAgDjB,IAAM,uBAAuB;AAuCtB,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC7B,OAAO;AAAA,EACzB,YAAY,SAAiB;AAC3B,UAAM,OAAO;AAAA,EACf;AACF;AAEO,IAAM,oCAAN,cAAgD,MAAM;AAAA,EACzC,OAAO;AAAA,EACzB,YAAY,SAAiB;AAC3B,UAAM,OAAO;AAAA,EACf;AACF;AAEA,SAAS,gCAAgC,KAAuB;AAC9D,SACE,eAAe,yBACf,eAAe;AAEnB;AAmBA,IAAM,sCAAsC;AAe5C,IAAM,qCAAqC;AAE3C,SAAS,kCAA0C;AACjD,QAAM,MAAM,WAAW,mCAAmC;AAC1D,MAAI,KAAK;AACP,UAAM,SAAS,OAAO,GAAG;AACzB,QAAI,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACzC,aAAO,KAAK,MAAM,MAAM;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,iCAAyC;AAChD,QAAM,MAAM,WAAW,mCAAmC;AAC1D,MAAI,KAAK;AACP,UAAM,SAAS,OAAO,GAAG;AACzB,QAAI,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACzC,aAAO,KAAK,MAAM,MAAM;AAAA,IAC1B;AAAA,EACF;AACA,SAAO;AACT;AAYO,IAAM,oBAAN,MAAwB;AAAA,EAY7B,YAA6B,QAAsB,SAAiC;AAAvD;AAC3B,UAAM,WAAW,KAAK,KAAK,OAAO,WAAW,OAAO;AACpD,SAAK,qBAAqB;AAC1B,SAAK,qBAAqB,4BAA4B,QAAQ;AAC9D,SAAK,QAAQ,IAAI;AAAA,MACf,KAAK,KAAK,UAAU,iBAAiB;AAAA,MACrC,KAAK,KAAK,UAAU,YAAY;AAAA,MAChC,KAAK,KAAK,UAAU,gCAAgC;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAAA,EAV6B;AAAA,EAXZ;AAAA,EACT,SAAoC;AAAA;AAAA,EAEpC,iBAAiB;AAAA,EACjB,gBAA+B,QAAQ,QAAQ;AAAA;AAAA,EAGtC;AAAA,EACA;AAAA,EACT,cAAqD;AAAA,EAc7D,MAAM,cAAgC;AACpC,WAAQ,MAAM,KAAK,gBAAgB,MAAO;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,WAAW,OAAsC;AACrD,UAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AAEA,UAAM,YAAY,KAAK;AAAA,MACrB;AAAA,MACA,KAAK,OAAO,wBAAwB,sBAAsB;AAAA,IAC5D;AAEA,UAAM,UAAsB,CAAC;AAC7B,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,YAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,SAAS;AAC1C,YAAM,eAAe,MAAM,QAAQ;AAAA,QACjC,MAAM,IAAI,CAAC,SAAS,KAAK,MAAM,MAAM,UAAU,EAAE,MAAM,SAAS,CAAC,CAAC;AAAA,MACpE;AACA,iBAAW,OAAO,cAAc;AAC9B,YAAI,CAAC,KAAK;AACR,gBAAM,IAAI,MAAM,wCAAwC;AAAA,QAC1D;AACA,gBAAQ,KAAK,GAAG;AAAA,MAClB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,MAAM,OACJ,OACA,OACA,UAkBI,CAAC,GACwD;AAC7D,UAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,QAAI,CAAC,SAAU,QAAO,CAAC;AAEvB,QAAI,cAAc,MAAM,KAAK,eAAe,OAAO,UAAU,OAAO;AACpE,QAAI,CAAC,YAAa,QAAO,CAAC;AAI1B,QAAI,SAAS;AAKb,QAAI;AACF,YAAM,eAAe,MAAM,KAAK,0BAA0B;AAC1D,UAAI,gBAAgB,CAAC,kBAAkB,cAAc,YAAY,QAAQ,GAAG;AAG1E,cAAM,eAAe,MAAM,KAAK,wCAAwC,YAAY;AACpF,YAAI,cAAc;AAChB,gBAAM,kBAAkB,MAAM,KAAK,eAAe,OAAO,cAAc,OAAO;AAC9E,cAAI,mBAAmB,kBAAkB,cAAc,gBAAgB,QAAQ,GAAG;AAChF,qBAAS;AAAA,UACX,OAAO;AACL,gBAAI;AAAA,cACF,gDAAgD,aAAa,QAAQ,IAAI,aAAa,KAAK;AAAA,YAC7F;AACA,mBAAO,CAAC;AAAA,UACV;AAAA,QACF,OAAO;AACL,cAAI;AAAA,YACF,qDAAqD,YAAY,SAAS,IAAI,IAAI,YAAY,SAAS,KAAK,4BAA4B,aAAa,QAAQ,IAAI,aAAa,KAAK;AAAA,UACrL;AACA,iBAAO,CAAC;AAAA,QACV;AAAA,MACF;AAMA,UAAI;AACJ,UAAI;AACF,gBAAQ,MAAM,KAAK,6BAA6B,OAAO,QAAQ;AAAA,MACjE,SAAS,KAAK;AACZ,YAAI,eAAe,yCAAyC;AAG1D,gBAAM,KAAK,MAAM;AAAA,YACf,yCAAyC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,UAC3F;AACA,iBAAO,CAAC;AAAA,QACV;AACA,cAAM;AAAA,MACR;AACA,YAAM,MAAM,OAAO,KAAK,MAAM,OAAO;AACrC,UAAI,IAAI,WAAW,EAAG,QAAO,CAAC;AAE9B,YAAM,gBAAgB,oBAAoB,QAAQ,UAAU;AAC5D,YAAM,mBAAmB,QAAQ,uBAAuB,CAAC,GACtD,IAAI,CAAC,MAAM,oBAAoB,CAAC,CAAC,EACjC,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAEnD,YAAM,SAAS,IACZ,IAAI,CAAC,OAAO;AACX,cAAM,QAAQ,MAAM,QAAQ,EAAE;AAC9B,eAAO;AAAA,UACL;AAAA,UACA,MAAM,MAAM;AAAA,UACZ,OAAO,iBAAiB,OAAO,QAAQ,MAAM,MAAM;AAAA,QACrD;AAAA,MACF,CAAC,EACA,OAAO,CAAC,MAAM;AACb,YAAI,CAAC,OAAO,SAAS,EAAE,KAAK,EAAG,QAAO;AACtC,cAAM,aAAa,mBAAmB,EAAE,IAAI;AAC5C,YAAI,kBAAkB,UAAa,CAAC,WAAW,WAAW,aAAa,GAAG;AACxE,iBAAO;AAAA,QACT;AACA,mBAAW,QAAQ,iBAAiB;AAClC,cAAI,WAAW,WAAW,IAAI,EAAG,QAAO;AAAA,QAC1C;AACA,eAAO;AAAA,MACT,CAAC,EACA,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,KAAK,IAAI,GAAG,KAAK,CAAC;AAE9B,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAI,eAAe,4BAA4B;AAC7C,YAAI;AAAA,UACF,qEAAqE,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACvH;AACA,eAAO,CAAC;AAAA,MACV;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,UAAU,UAAkB,SAAiB,UAAiC;AAClF,UAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,QAAI,CAAC,SAAU;AAOf,UAAM,SAAS,MAAM,KAAK,2BAA2B,SAAS,UAAU;AAAA,MACtE,MAAM;AAAA,IACR,CAAC;AACD,QAAI,CAAC,OAAQ;AAEb,UAAM,KAAK,qBAAqB,UAAU,YAAY;AACpD,UAAI;AACF,cAAM,WAAW,MAAM,KAAK,0BAA0B;AACtD,YACE,YACA,CAAC,kBAAkB,UAAU,OAAO,QAAQ,KAC5C,CAAC,wBAAwB,UAAU,OAAO,QAAQ,GAClD;AACA,cAAI;AAAA,YACF,4CAA4C,OAAO,SAAS,IAAI,IAAI,OAAO,SAAS,KAAK,2BAA2B,SAAS,QAAQ,IAAI,SAAS,KAAK;AAAA,UACzJ;AACA;AAAA,QACF;AACA,cAAM,QAAQ,MAAM,KAAK,UAAU,OAAO,QAAQ;AAClD,cAAM,UAAU,qBAAqB,KAAK,OAAO,WAAW,QAAQ;AACpE,cAAM,QAAQ,QAAQ,IAAI;AAAA,UACxB,QAAQ,OAAO;AAAA,UACf,MAAM;AAAA,QACR;AACA,cAAM,KAAK,UAAU,OAAO,EAAE,YAAY,CAAC,QAAQ,GAAG,SAAS,CAAC;AAAA,MAClE,SAAS,KAAK;AACZ,cAAM,KAAK,MAAM,wBAAwB,KAAK,QAAQ;AACtD,cAAM;AAAA,MACR;AACA,YAAM,KAAK,MAAM,wBAAwB,MAAM,QAAQ;AAAA,IACzD,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,gBAAgB,UAAiC;AACrD,UAAM,WAAW,MAAM,KAAK,gBAAgB;AAC5C,QAAI,CAAC,SAAU;AAEf,UAAM,KAAK,qBAAqB,UAAU,YAAY;AACpD,YAAM,YAAY,CAAC,QAAQ;AAC3B,YAAM,eAAe,MAAM,KAAK,0BAA0B;AAC1D,UACE,gBACA,CAAC,UAAU,KAAK,CAAC,UAAU,kBAAkB,OAAO,YAAY,CAAC,GACjE;AACA,kBAAU,KAAK,0BAA0B,YAAY,CAAC;AAAA,MACxD;AACA,UAAI,SAAS,SAAS,QAAQ;AAC5B,cAAM,mBAAmB,MAAM,KAAK,gBAAgB,EAAE,aAAa,MAAM,CAAC;AAC1E,YACE,oBACA,CAAC,UAAU,KAAK,CAAC,UAAU,kBAAkB,OAAO,gBAAgB,CAAC,GACrE;AACA,oBAAU,KAAK,gBAAgB;AAAA,QACjC;AAAA,MACF;AAEA,UAAI,QAAQ;AACZ,iBAAW,iBAAiB,WAAW;AACrC,YAAI;AACF,gBAAM,QAAQ,MAAM,KAAK,UAAU,aAAa;AAChD,cAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,iBAAO,MAAM,QAAQ,QAAQ;AAC7B,gBAAM,KAAK,UAAU,OAAO,EAAE,YAAY,CAAC,QAAQ,GAAG,SAAS,CAAC;AAChE,kBAAQ;AAAA,QACV,SAAS,KAAK;AACZ,gBAAM,KAAK,MAAM,wBAAwB,KAAK,QAAQ;AACtD,gBAAM;AAAA,QACR;AAAA,MACF;AAKA,UAAI,MAAO,OAAM,KAAK,MAAM,wBAAwB,MAAM,QAAQ;AAAA,IACpE,CAAC;AAAA,EACH;AAAA,EAEQ,qBAAwB,UAA8B,UAAwC;AACpG,UAAM,MAAM,KAAK,cACd,MAAM,MAAM,MAAS,EACrB,KAAK,MACJ,4BAA4B,KAAK,oBAAoB,OAAO,YAAY;AACtE,WAAK,cAAc;AACnB,UAAI;AAIF,aAAK,SAAS;AACd,aAAK,iBAAiB;AAMtB,YAAI,YAAY;AAChB,YAAI;AACF,sBAAY,MAAM,KAAK,MAAM,qBAAqB,KAAK,gBAAgB,CAAC;AAAA,QAC1E,SAAS,KAAK;AAGZ,gBAAM,KAAK,MAAM,wBAAwB,GAAG,EAAE,MAAM,MAAM,MAAS;AACnE,gBAAM;AAAA,QACR;AACA,YAAI,WAAW;AACb,eAAK,SAAS;AACd,eAAK,iBAAiB;AAAA,QACxB;AACA,eAAO,MAAM,SAAS;AAAA,MACxB,UAAE;AACA,aAAK,cAAc;AAAA,MACrB;AAAA,IACF,CAAC,EAAE,MAAM,OAAO,QAAQ;AAKtB,UAAI,eAAe,yCAAyC;AAC1D,cAAM,KAAK,MAAM,wBAAwB,KAAK,QAAQ,EAAE,MAAM,MAAM,MAAS;AAAA,MAC/E;AACA,YAAM;AAAA,IACR,CAAC,CAAC;AACN,SAAK,gBAAgB,IAAI;AAAA,MACvB,MAAM;AAAA,MACN,MAAM;AAAA,IACR;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,gBACZ,UAAqC,CAAC,GACN;AAChC,QAAI,CAAC,mCAAmC,KAAK,MAAM,EAAE,kBAAmB,QAAO;AAE/E,QACE,QAAQ,gBAAgB,SACxB,sCAAsC,KAAK,MAAM,EAAE,0BAA0B,OAC7E;AACA,YAAM,eAAe,yBAAyB,KAAK,OAAO,SAAS;AACnE,UAAI,cAAc;AAChB,eAAO;AAAA,UACL,MAAM;AAAA,UACN,OAAO,aAAa,SAAS,aAAa;AAAA,UAC1C;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,KAAK,OAAO;AAC9B,UAAM,YAAY,cAAc,SAAS,CAAC,UAAU,OAAO,IAAI,CAAC,SAAS;AAEzE,eAAW,KAAK,WAAW;AACzB,UAAI,MAAM,UAAU;AAClB,cAAM,WAAW,KAAK,qBAAqB;AAC3C,YAAI,SAAU,QAAO;AAAA,MACvB;AAEA,UAAI,MAAM,SAAS;AACjB,cAAM,WAAW,KAAK,oBAAoB;AAC1C,YAAI,SAAU,QAAO;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,wCACZ,UACgC;AAChC,QAAI,SAAS,aAAa,UAAU;AAClC,YAAM,WAAW,KAAK,qBAAqB;AAC3C,aAAO,YAAY,kBAAkB,UAAU,QAAQ,IAAI,WAAW;AAAA,IACxE;AACA,QAAI,SAAS,aAAa,SAAS;AACjC,YAAM,WAAW,KAAK,oBAAoB;AAC1C,aAAO,YAAY,kBAAkB,UAAU,QAAQ,IAAI,WAAW;AAAA,IACxE;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,uBAA8C;AACpD,QAAI,CAAC,KAAK,OAAO,aAAc,QAAO;AACtC,UAAM,UAAU,KAAK,OAAO,iBAAiB;AAC7C,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,UAAU,GAAG,QAAQ,QAAQ,OAAO,EAAE,CAAC;AAAA,MACvC,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,UAAU,KAAK,OAAO,YAAY;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,sBAA6C;AACnD,QAAI,CAAC,4BAA4B,KAAK,MAAM,EAAE,YAAY,CAAC,KAAK,OAAO,YAAa,QAAO;AAC3F,UAAM,OAAO,KAAK,OAAO,YAAY,QAAQ,OAAO,EAAE;AACtD,UAAM,WAAW,SAAS,KAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,GAAG,IAAI;AACrE,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,GAAI,KAAK,OAAO,mBAAmB,CAAC;AAAA,IACtC;AACA,QAAI,KAAK,OAAO,kBAAkB,KAAK,OAAO,uBAAuB,OAAO;AAC1E,cAAQ,gBAAgB,UAAU,KAAK,OAAO,cAAc;AAAA,IAC9D;AACA,WAAO;AAAA,MACL,MAAM;AAAA,MACN,OACE,KAAK,OAAO,0BACZ,KAAK,OAAO,iBACZ;AAAA,MACF;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,MAAc,eACZ,OACA,UACA,UAAwC,CAAC,GACR;AACjC,QAAI;AACF,aAAO,MAAM,KAAK,2BAA2B,OAAO,UAAU;AAAA,QAC5D,MAAM;AAAA,MACR,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,UAAI,gCAAgC,GAAG,GAAG;AACxC,YAAI,QAAQ,gBAAgB;AAC1B,gBAAM;AAAA,QACR;AAGA,YAAI,MAAM,+FAA+F;AACzG,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAc,MACZ,OACA,UACA,UAAgC,CAAC,GACP;AAC1B,UAAM,SAAS,MAAM,KAAK,2BAA2B,OAAO,UAAU,OAAO;AAC7E,WAAO,QAAQ,UAAU;AAAA,EAC3B;AAAA,EAEA,MAAc,2BACZ,OACA,UACA,UAAgC,CAAC,GACA;AAOjC,UAAM,OAAkB,QAAQ,QAAQ;AACxC,UAAM,YACJ,SAAS,UACL,+BAA+B,IAC/B,gCAAgC;AACtC,QAAI,SAAS,SAAS,QAAQ;AAC5B,YAAM,SAAS,MAAM,KAAK,sBAAsB,OAAO,UAAU,MAAM,SAAS;AAChF,UAAI,OAAQ,QAAO,EAAE,UAAU,OAAO;AACtC,YAAM,mBAAmB,MAAM,KAAK,gBAAgB,EAAE,aAAa,MAAM,CAAC;AAC1E,UAAI,CAAC,kBAAkB;AACrB,YAAI,SAAS,UAAU;AACrB,gBAAM,IAAI;AAAA,YACR,wCAAwC,SAAS,cAAc,MAAM,SAAS,KAAK;AAAA,UACrF;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,aAAO,KAAK,2BAA2B,OAAO,kBAAkB,OAAO;AAAA,IACzE;AACA,QAAI,CAAC,SAAS,YAAY,CAAC,SAAS,QAAS,QAAO;AACpD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,SAAS,UAAU;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS,SAAS;AAAA,QAClB,MAAM,KAAK,UAAU;AAAA,UACnB,OAAO,SAAS;AAAA,UAChB,OAAO,MAAM,MAAM,GAAG,GAAI;AAAA,UAC1B,iBAAiB;AAAA,QACnB,CAAC;AAAA,QACD,QAAQ,YAAY,QAAQ,SAAS;AAAA,MACvC,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,YAAI,MAAM,sCAAsC,SAAS,IAAI,IAAI,IAAI,MAAM,EAAE;AAU7E,YAAI,SAAS,UAAU;AACrB,gBAAM,IAAI;AAAA,YACR,8BAA8B,IAAI,MAAM,KAAK,SAAS,IAAI;AAAA,UAC5D;AAAA,QACF;AACA,eAAO;AAAA,MACT;AACA,YAAM,UAAW,MAAM,IAAI,KAAK;AAChC,YAAM,SAAS,SAAS,OAAO,CAAC,GAAG;AACnC,UAAI,CAAC,MAAM,QAAQ,MAAM,EAAG,QAAO;AACnC,YAAM,aAAa,OAChB,IAAI,CAAC,MAAe,OAAO,CAAC,CAAC,EAC7B,OAAO,CAAC,MAAc,OAAO,SAAS,CAAC,CAAC;AAC3C,aAAO,WAAW,SAAS,IAAI,EAAE,UAAU,QAAQ,WAAW,IAAI;AAAA,IACpE,SAAS,KAAK;AAIZ,UAAI,gCAAgC,GAAG,GAAG;AACxC,cAAM;AAAA,MACR;AAIA,YAAM,YACJ,eAAe,UACd,IAAI,SAAS,kBAAkB,IAAI,SAAS;AAC/C,UAAI,WAAW;AACb,YAAI;AAAA,UACF,4CAA4C,SAAS,OAAO,SAAS,IAAI,UAAU,IAAI;AAAA,QACzF;AAaA,YAAI,SAAS,UAAU;AACrB,gBAAM,IAAI;AAAA,YACR,qCAAqC,SAAS,OAAO,SAAS,IAAI;AAAA,UACpE;AAAA,QACF;AAAA,MACF,OAAO;AAaL,YAAI,SAAS,UAAU;AACrB,cAAI;AAAA,YACF,sDAAsD,SAAS,IAAI,MAAM,GAAG;AAAA,UAC9E;AACA,gBAAM,IAAI;AAAA,YACR,wCAAwC,SAAS,IAAI,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,UAC7G;AAAA,QACF;AACA,YAAI,MAAM,6BAA6B,GAAG,EAAE;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,sBACZ,OACA,UACA,MACA,WAC0B;AAC1B,UAAM,eAAe,SAAS;AAC9B,QAAI,CAAC,aAAc,QAAO;AAC1B,QAAI;AACF,YAAM,SAAS,MAAM,aAAa,MAAM,MAAM,MAAM,GAAG,GAAI,GAAG;AAAA,QAC5D,QAAQ,YAAY,QAAQ,SAAS;AAAA,QACrC,WAAW,SAAS,WAAW,UAAU;AAAA,MAC3C,CAAC;AACD,aAAO,6BAA6B,MAAM;AAAA,IAC5C,SAAS,KAAK;AACZ,UAAI,MAAM,kCAAkC,aAAa,EAAE,KAAK,GAAG,EAAE;AACrE,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBQ,kBAAuC;AAC7C,UAAM,UAAU,KAAK;AACrB,QAAI,CAAC,SAAS;AACZ,aAAO,YAAY;AACjB,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,MAAM,kCAAkC,KAAK,oBAAoB,OAAO;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAc,6BAA6B,UAAuD;AAChG,QAAI,KAAK,UAAU,KAAK,eAAgB,QAAO,KAAK;AACpD,WAAO,MAAM,4BAA4B,KAAK,oBAAoB,YAAY;AAG5E,UAAI,KAAK,UAAU,KAAK,eAAgB,QAAO,KAAK;AAIpD,YAAM,eAAe,MAAM,KAAK,MAAM,iBAAiB;AACvD,UAAI,gBAAgB,CAAC,kBAAkB,cAAc,QAAQ,GAAG;AAC9D,cAAM,IAAI;AAAA,UACR,uCAAuC,aAAa,QAAQ,IAAI,aAAa,KAAK;AAAA,QACpF;AAAA,MACF;AACA,aAAO,MAAM,KAAK,UAAU,QAAQ;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,UAAU,UAAuD;AAC7E,QAAI,KAAK,UAAU,KAAK,OAAO,aAAa,SAAS,QAAQ,KAAK,OAAO,UAAU,SAAS,OAAO;AACjG,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,SAA8C,CAAC;AACrD,QAAI,eAA8C;AAElD,UAAM,SAAS,MAAM,KAAK,MAAM,aAAa;AAC7C,QAAI,WAAW,WAAW;AAKxB,qBAAe,MAAM,KAAK,MAAM,wBAAwB,MAAM;AAAA,IAChE,WAAW,WAAW,UAAU;AAC9B,YAAM,OAAO,MAAM,KAAK,MAAM,WAAW;AACzC,UAAI,KAAK,YAAY,MAAM;AACzB,uBAAe,EAAE,UAAU,KAAK,KAAK,UAAU,OAAO,KAAK,KAAK,MAAM;AACtE,eAAO,OAAO,QAAQ,KAAK,KAAK,OAAO;AAAA,MACzC,WAAW,KAAK,YAAY,cAAc;AACxC,cAAM,KAAK,MAAM,yBAAyB,KAAK,MAAM;AACrD,cAAM,IAAI;AAAA,UACR,8DAA8D,KAAK,MAAM,UAAU,KAAK,KAAK,MAAM;AAAA,QACrG;AAAA,MACF,WAAW,KAAK,YAAY,WAAW;AAIrC,cAAM,KAAK,MAAM,yBAAyB,2BAA2B;AACrE,cAAM,IAAI;AAAA,UACR,4DAA4D,KAAK,MAAM,UAAU;AAAA,QACnF;AAAA,MACF;AAAA,IAIF;AAEA,QAAI,gBAAgB,aAAa,aAAa,SAAS,QAAQ,aAAa,UAAU,SAAS,OAAO;AACpG,WAAK,SAAS;AAAA,QACZ,SAAS;AAAA,QACT,UAAU,SAAS;AAAA,QACnB,OAAO,SAAS;AAAA,QAChB,SAAS;AAAA,MACX;AACA,WAAK,iBAAiB;AACtB,aAAO,KAAK;AAAA,IACd;AAEA,SAAK,SAAS;AAAA,MACZ,SAAS;AAAA,MACT,UAAU,SAAS;AAAA,MACnB,OAAO,SAAS;AAAA,MAChB,SAAS,CAAC;AAAA,IACZ;AACA,SAAK,iBAAiB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAc,4BAAoE;AAIhF,QAAI,KAAK,UAAU,KAAK,gBAAgB;AACtC,aAAO,EAAE,UAAU,KAAK,OAAO,UAAU,OAAO,KAAK,OAAO,MAAM;AAAA,IACpE;AACA,WAAO,KAAK,MAAM,iBAAiB;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,UACZ,OACA,OAA8D,CAAC,GAChD;AACf,QAAI;AACF,YAAM,KAAK,MAAM,QAAQ,OAAO,EAAE,GAAG,MAAM,OAAO,KAAK,gBAAgB,EAAE,CAAC;AAAA,IAC5E,SAAS,KAAK;AACZ,WAAK,SAAS;AACd,WAAK,iBAAiB;AACtB,YAAM;AAAA,IACR;AACA,SAAK,SAAS;AACd,SAAK,iBAAiB;AAAA,EACxB;AACF;AAEA,SAAS,qBAAqB,WAAmB,UAA0B;AACzE,MAAI,CAAC,KAAK,WAAW,QAAQ,EAAG,QAAO;AACvC,QAAM,MAAM,KAAK,SAAS,WAAW,QAAQ;AAC7C,SAAO,IAAI,WAAW,IAAI,IAAI,WAAW;AAC3C;AAiBA,SAAS,mBAAmB,GAAmB;AAC7C,MAAI,MAAM,EAAE,QAAQ,OAAO,GAAG;AAC9B,MAAI,IAAI,WAAW,IAAI,EAAG,OAAM,IAAI,MAAM,CAAC;AAC3C,SAAO;AACT;AAUA,SAAS,oBAAoB,QAAgD;AAC3E,MAAI,WAAW,UAAa,WAAW,KAAM,QAAO;AACpD,MAAI,IAAI,OAAO,MAAM,EAAE,QAAQ,OAAO,GAAG;AACzC,MAAI,EAAE,WAAW,IAAI,EAAG,KAAI,EAAE,MAAM,CAAC;AACrC,MAAI,EAAE,WAAW,EAAG,QAAO;AAC3B,MAAI,CAAC,EAAE,SAAS,GAAG,EAAG,KAAI,GAAG,CAAC;AAC9B,SAAO;AACT;AAEA,SAAS,kBACP,MACA,OACS;AACT,SAAO,sBAAsB,IAAI,MAAM,sBAAsB,KAAK,KAAK,KAAK,UAAU,MAAM;AAC9F;AAEA,SAAS,sBAAsB,UAA2D;AACxF,SAAO,cAAc,WAAW,SAAS,WAAW,SAAS;AAC/D;AAEA,SAAS,wBACP,UACA,aACS;AACT,SAAO,SAAS,aAAa,UAAU,CAAC,kBAAkB,UAAU,WAAW;AACjF;AAEA,SAAS,0BAA0B,UAAkD;AACnF,SAAO;AAAA,IACL,MAAM,SAAS;AAAA,IACf,OAAO,SAAS;AAAA,EAClB;AACF;AAEA,SAAS,iBAAiB,GAAa,GAAqB;AAC1D,QAAM,IAAI,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM;AACrC,MAAI,MAAM,EAAG,QAAO;AACpB,MAAI,MAAM;AACV,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,EAAE,CAAC,KAAK;AACnB,UAAM,KAAK,EAAE,CAAC,KAAK;AACnB,WAAO,KAAK;AACZ,aAAS,KAAK;AACd,aAAS,KAAK;AAAA,EAChB;AACA,QAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK;AAChD,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,MAAM;AACf;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/offline-sync-exclude-globs.ts"],"sourcesContent":["// Issue #1786: node-local runtime state that each node rebuilds from synced\n// records. Pushing these wastes bandwidth, corrupts the remote's live state\n// dir, and trips the large-file push retry loop on a live SQLite database.\n// Extracted from offline-sync.ts (issue #1995) so the god-file line-count\n// ratchet does not grow when the list gains entries; behavior is unchanged.\n/**\n * Directory-name prefix for the transient plaintext staging dir the CLI offline\n * decrypt path (`offline-storage-io.ts`) creates under the memory root when it\n * must materialize a decrypted secure-store file before hashing/pushing (#2033\n * P1). Shared here so the hard-exclude glob below and the CLI's `mkdtemp` prefix\n * can never drift apart: a crash-orphaned staging dir must NEVER be enumerated\n * into an offline snapshot, or decrypted secure-store plaintext would leak into\n * the remote push.\n */\nexport const OFFLINE_DECRYPT_STAGING_DIR_PREFIX = \".remnic-offline-decrypt-\";\n\nexport const DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS: readonly string[] = [\n // Leading `**/` matches zero or more segments, so each pattern covers both\n // the root `state/` dir AND per-namespace `namespaces/<ns>/state/` dirs\n // (Cursor review on PR #1793: multi-namespace deployments previously kept\n // pushing their namespaced live sqlite files).\n \"**/state/*.sqlite\",\n \"**/state/*.sqlite-*\",\n \"**/state/index_tags.json\",\n \"**/state/entity-mention-index.json\",\n \"**/state/memory-governance/runs/**\",\n // Rotated recall-impression archives (issue #1910). The active\n // recall_impressions.jsonl stays remote-authoritative; only the .1..N\n // archives and the .lock are node-local and never pushed/hashed.\n \"**/state/recall_impressions.jsonl.*\",\n // Durable recall-impression pending spill directory (issue #2033). Node-local:\n // an impression spills a per-event file here when its rotation lock cannot be\n // acquired, and the next lock holder folds them back into the synced active\n // recall_impressions.jsonl. The `.*` glob above matches the directory name but\n // not its children, so exclude the contents explicitly.\n \"**/state/recall_impressions.jsonl.pending.d/**\",\n // Durable lifecycle-append pending spill directory (issue #2033). Node-local:\n // an append spills a per-event file here when it cannot get the ledger lock,\n // and the next lock holder folds them back into the synced\n // memory-lifecycle-ledger.jsonl. Pushing them would duplicate rows remotely.\n // The offline-sync snapshot entrypoints drain this queue first (access-service\n // `drainPendingLifecycleForSync` -> `StorageManager.drainPendingMemoryLifecycleEventsForSync`),\n // aborting the snapshot when durable rows cannot be folded, so excluding the\n // dir never silently drops append-only lifecycle history (promotions/imports).\n \"**/state/memory-lifecycle-ledger.jsonl.pending.d/**\",\n // The active lifecycle ledger lock is node-local and must never be\n // transferred to another node during an offline snapshot.\n \"**/state/memory-lifecycle-ledger.jsonl.lock\",\n // Transient plaintext decrypt staging (issue #2033 P1). The CLI offline\n // decrypt path materializes a decrypted secure-store file under an owner-only\n // `<OFFLINE_DECRYPT_STAGING_DIR_PREFIX>*` dir before hashing/pushing, removing\n // it in a `finally`. A hard crash between create and cleanup would otherwise\n // let a later snapshot walk `<prefix>*/content` and push decrypted secure-store\n // plaintext to the remote. Exclude the dir and its contents unconditionally so\n // a crash-orphan can never be enumerated into a snapshot; the leading `**/`\n // matches the root-level dir (zero segments) and any nested placement.\n \"**/.remnic-offline-decrypt-*/**\",\n // Embedding-index transaction leftovers (issue #3146, PR #3148). The\n // identity-replacement backup (`embeddings.pre-replace.tmp`) can hold a\n // full obsolete generation and `embeddings.staging.tmp-*` holds a\n // partially staged one; both are node-local recovery state that must\n // never be enumerated into a snapshot. The demoted legacy recovery file\n // (`embeddings.json.pre-migration.tmp-*`) is likewise node-local.\n \"**/state/embeddings.pre-replace.tmp/**\",\n \"**/state/embeddings.staging.tmp-*/**\",\n \"**/state/embeddings.json.pre-migration.tmp-*\",\n \"**/namespaces/*/state/embeddings.pre-replace.tmp/**\",\n \"**/namespaces/*/state/embeddings.staging.tmp-*/**\",\n \"**/namespaces/*/state/embeddings.json.pre-migration.tmp-*\",\n];\n\n\nconst DEFAULT_OFFLINE_SYNC_EXCLUDE_REGEXPS: readonly RegExp[] =\n DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS.map((glob) => globToRegExp(glob));\n\n/**\n * Precompiled-once default-exclude check for the snapshot enumeration hot\n * path (Kilo review, PR #1793). Moved here from offline-sync.ts with the\n * glob list so the two can never drift (issue #1995).\n */\nexport function matchesOfflineSyncDefaultExclude(relPosix: string): boolean {\n for (const regexp of DEFAULT_OFFLINE_SYNC_EXCLUDE_REGEXPS) {\n if (regexp.test(relPosix)) return true;\n }\n return false;\n}\n\nexport function globToRegExp(glob: string): RegExp {\n if (typeof glob !== \"string\" || glob.length === 0) {\n throw new Error(\"offlineSyncExcludes entry must be a non-empty string\");\n }\n if (glob.includes(\"\\0\")) {\n throw new Error(\"offlineSyncExcludes entry must not contain NUL bytes\");\n }\n let source = \"\";\n for (let i = 0; i < glob.length; i += 1) {\n const ch = glob[i];\n if (ch === \"*\") {\n if (glob[i + 1] === \"*\") {\n // `**` is cross-segment wherever it appears:\n // leading `**/` -> zero or more whole segments\n // `/**` at end -> everything under the directory\n // `a/**/b` -> any depth between segments\n // (Cursor review on PR #1793: trailing `scratch/**` must match\n // nested `scratch/a/b.md`, matching the offline-mode guide.)\n if (glob[i + 2] === \"/\") {\n source += \"(?:[\\\\s\\\\S]*/)?\";\n i += 2;\n continue;\n }\n source += \"[\\\\s\\\\S]*\";\n i += 1;\n continue;\n }\n source += \"[^/]*\";\n continue;\n }\n if (ch === \"?\") {\n source += \"[^/]\";\n continue;\n }\n if (ch === \"/\") {\n source += \"/\";\n continue;\n }\n source += ch.replace(/[\\\\^$.+()|{}\\[\\]]/g, \"\\\\$&\");\n }\n return new RegExp(`^${source}$`);\n}\n\n/** Match relative POSIX paths: `*` stays within a segment; `**` crosses separators and matches every path alone. */\nexport function compileOfflineSyncExcludeGlobs(\n globs: readonly unknown[],\n): RegExp[] {\n const out: RegExp[] = [];\n for (const entry of globs) {\n if (typeof entry !== \"string\" || entry.length === 0) {\n throw new Error(\"offlineSyncExcludes must contain only non-empty strings\");\n }\n out.push(globToRegExp(entry));\n }\n return out;\n}\n\n/**\n * Validate the operator-supplied offline-sync exclude list (#1786).\n * Rejects loudly instead of silently defaulting (CLAUDE.md rule 39):\n * a misspelled key value must fail config parse, not be ignored.\n * Lives next to the glob compiler so config.ts only carries the call.\n */\nexport function parseOfflineSyncExcludes(raw: unknown): string[] {\n if (raw === undefined || raw === null) return [];\n if (!Array.isArray(raw)) {\n throw new Error(\n `offlineSyncExcludes must be an array of non-empty glob strings; got ${typeof raw}`,\n );\n }\n for (const entry of raw) {\n if (typeof entry !== \"string\" || entry.trim().length === 0) {\n throw new Error(\n \"offlineSyncExcludes must contain only non-empty glob strings\",\n );\n }\n }\n const globs = raw.map((entry) => (entry as string).trim());\n // Compile-check every glob now so a bad pattern fails at parse time\n // rather than mid-sync. compileOfflineSyncExcludeGlobs throws with a\n // per-entry message.\n compileOfflineSyncExcludeGlobs(globs);\n return globs;\n}\n"],"mappings":";AAcO,IAAM,qCAAqC;AAE3C,IAAM,qCAAwD;AAAA;AAAA;AAAA;AAAA;AAAA,EAKnE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA,EAIA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA;AAAA;AAAA;AAAA,EAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGA,IAAM,uCACJ,mCAAmC,IAAI,CAAC,SAAS,aAAa,IAAI,CAAC;AAO9D,SAAS,iCAAiC,UAA2B;AAC1E,aAAW,UAAU,sCAAsC;AACzD,QAAI,OAAO,KAAK,QAAQ,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAEO,SAAS,aAAa,MAAsB;AACjD,MAAI,OAAO,SAAS,YAAY,KAAK,WAAW,GAAG;AACjD,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AACA,MAAI,KAAK,SAAS,IAAI,GAAG;AACvB,UAAM,IAAI,MAAM,sDAAsD;AAAA,EACxE;AACA,MAAI,SAAS;AACb,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,UAAM,KAAK,KAAK,CAAC;AACjB,QAAI,OAAO,KAAK;AACd,UAAI,KAAK,IAAI,CAAC,MAAM,KAAK;AAOvB,YAAI,KAAK,IAAI,CAAC,MAAM,KAAK;AACvB,oBAAU;AACV,eAAK;AACL;AAAA,QACF;AACA,kBAAU;AACV,aAAK;AACL;AAAA,MACF;AACA,gBAAU;AACV;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,gBAAU;AACV;AAAA,IACF;AACA,QAAI,OAAO,KAAK;AACd,gBAAU;AACV;AAAA,IACF;AACA,cAAU,GAAG,QAAQ,sBAAsB,MAAM;AAAA,EACnD;AACA,SAAO,IAAI,OAAO,IAAI,MAAM,GAAG;AACjC;AAGO,SAAS,+BACd,OACU;AACV,QAAM,MAAgB,CAAC;AACvB,aAAW,SAAS,OAAO;AACzB,QAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,YAAM,IAAI,MAAM,yDAAyD;AAAA,IAC3E;AACA,QAAI,KAAK,aAAa,KAAK,CAAC;AAAA,EAC9B;AACA,SAAO;AACT;AAQO,SAAS,yBAAyB,KAAwB;AAC/D,MAAI,QAAQ,UAAa,QAAQ,KAAM,QAAO,CAAC;AAC/C,MAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,uEAAuE,OAAO,GAAG;AAAA,IACnF;AAAA,EACF;AACA,aAAW,SAAS,KAAK;AACvB,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,WAAW,GAAG;AAC1D,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,IAAI,CAAC,UAAW,MAAiB,KAAK,CAAC;AAIzD,iCAA+B,KAAK;AACpC,SAAO;AACT;","names":[]}
|