@remnic/core 9.69.87 → 9.69.89
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 +56 -49
- package/dist/access-authorization-probe.js +57 -50
- package/dist/access-boundary.js +56 -49
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +129 -122
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +56 -49
- package/dist/access-http-briefing.js +57 -50
- package/dist/access-http-offline-stream.d.ts.map +1 -1
- package/dist/access-http-offline-stream.js +1 -1
- package/dist/access-http-query.js +58 -51
- package/dist/access-http.js +78 -71
- package/dist/access-identity-continuity-surface.js +56 -49
- package/dist/access-lcm-surface.js +56 -49
- package/dist/access-mcp-output-schemas.d.ts.map +1 -1
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp.js +69 -62
- package/dist/access-namespace-preflight.js +56 -49
- package/dist/access-observe-write-category.js +20 -13
- package/dist/access-observe-write-surface.js +56 -49
- package/dist/access-offline-manifest.js +16 -10
- package/dist/access-operations-batch.js +60 -53
- package/dist/access-operations.js +67 -60
- package/dist/access-recall-concurrency.js +56 -49
- package/dist/access-recall-response.js +56 -49
- package/dist/access-recall-surface.js +56 -49
- package/dist/access-schema.js +16 -7
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +56 -49
- package/dist/access-surface-catalog.js +58 -51
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +24 -18
- package/dist/active-recall.js.map +1 -1
- package/dist/{auto-sync-6IPX2BAC.js → auto-sync-U5N3CB5Y.js} +7 -7
- package/dist/briefing.js +11 -11
- package/dist/buffer.js +7 -7
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
- package/dist/{capsule-merge-3MNURAQ3.js → capsule-merge-ZCXUPLNJ.js} +2 -2
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +14 -14
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-JE334SIA.js → chunk-22DYJLFN.js} +2 -2
- package/dist/{chunk-YTQGGO3K.js → chunk-22LMCQ5B.js} +85 -82
- package/dist/{chunk-YTQGGO3K.js.map → chunk-22LMCQ5B.js.map} +1 -1
- package/dist/{chunk-JGSQ6P4D.js → chunk-2FX374UI.js} +2 -2
- package/dist/{chunk-SY5KDWGU.js → chunk-2WLMWP2T.js} +2 -2
- package/dist/{chunk-6TT6HRE4.js → chunk-3AGHSQPM.js} +2 -2
- package/dist/{chunk-P6WFK4ZJ.js → chunk-3O5BB45T.js} +4 -4
- package/dist/{chunk-3C3J243X.js → chunk-3QKO4QN7.js} +3 -3
- package/dist/{chunk-KFCMMDHH.js → chunk-3YD44CH2.js} +2 -2
- package/dist/{chunk-FRCESVV6.js → chunk-4HUCP4R4.js} +18 -18
- package/dist/chunk-4HUCP4R4.js.map +1 -0
- package/dist/chunk-4NSLQ7EU.js +137 -0
- package/dist/chunk-4NSLQ7EU.js.map +1 -0
- package/dist/{chunk-7DE2NOON.js → chunk-4O4XCBOS.js} +3 -3
- package/dist/{chunk-GDYU6VF4.js → chunk-5AYUHSAU.js} +2 -2
- package/dist/{chunk-ICZQG4XE.js → chunk-5FAGCOZD.js} +4 -4
- package/dist/{chunk-VKQI47DJ.js → chunk-5FNZ3G6X.js} +4 -4
- package/dist/{chunk-CK5KOVHW.js → chunk-5R627MYY.js} +3 -2
- package/dist/{chunk-CK5KOVHW.js.map → chunk-5R627MYY.js.map} +1 -1
- package/dist/{chunk-VQNONC4E.js → chunk-5STPRPE7.js} +2 -2
- package/dist/{chunk-52II7PB6.js → chunk-645WOU5I.js} +4 -4
- package/dist/{chunk-SRZW7FNU.js → chunk-64ZUALXK.js} +2 -2
- package/dist/{chunk-4BZP2B4U.js → chunk-6MNKDTDI.js} +2 -2
- package/dist/{chunk-DUJYRVUD.js → chunk-6O2VUWV6.js} +3 -3
- package/dist/chunk-75PT4N7Q.js +75 -0
- package/dist/chunk-75PT4N7Q.js.map +1 -0
- package/dist/{chunk-HESDNYRB.js → chunk-76M5OK7V.js} +2 -2
- package/dist/{chunk-FAN73COB.js → chunk-7D7OUO5W.js} +2 -2
- package/dist/{chunk-DZ525SNQ.js → chunk-7QE46QXI.js} +1 -1
- package/dist/{chunk-VX6GCKBS.js → chunk-7WBSMXGC.js} +4 -4
- package/dist/{chunk-VVDWZRZG.js → chunk-A6RVIHHK.js} +3 -3
- package/dist/{chunk-M536WNTQ.js → chunk-AEJ3TE5H.js} +2 -2
- package/dist/{chunk-72JNNJJJ.js → chunk-AXC4WSF3.js} +3 -3
- package/dist/chunk-AXGJMDBS.js +684 -0
- package/dist/chunk-AXGJMDBS.js.map +1 -0
- package/dist/{chunk-UA5DX5C3.js → chunk-AZIOEOK4.js} +2 -2
- package/dist/{chunk-N4S4NUX5.js → chunk-B3O3IRZN.js} +1 -1
- package/dist/{chunk-XTYX6V52.js → chunk-BJVO4H5C.js} +2 -2
- package/dist/{chunk-7LCGFUMB.js → chunk-BUKK5SWA.js} +4 -4
- package/dist/{chunk-TM3RXL4G.js → chunk-BUQKSQXL.js} +2 -2
- package/dist/chunk-C6HPNPNK.js +64 -0
- package/dist/chunk-C6HPNPNK.js.map +1 -0
- package/dist/{chunk-RB776UH3.js → chunk-C7V3KPKJ.js} +235 -93
- package/dist/chunk-C7V3KPKJ.js.map +1 -0
- package/dist/{chunk-FOH7AMMI.js → chunk-CALBQ2NG.js} +71 -72
- package/dist/chunk-CALBQ2NG.js.map +1 -0
- package/dist/{chunk-37S3IMYB.js → chunk-DGS34QSY.js} +2 -2
- package/dist/{chunk-4IEOGIPB.js → chunk-DO4C4AXD.js} +2 -2
- package/dist/{chunk-SPSRGEDQ.js → chunk-EAQQ2FD3.js} +9 -9
- package/dist/{chunk-3BTFBQLO.js → chunk-EFHNFTWB.js} +49 -3
- package/dist/chunk-EFHNFTWB.js.map +1 -0
- package/dist/{chunk-C3QXITIP.js → chunk-EQUD7FP4.js} +2 -2
- package/dist/chunk-ETUZFFPU.js +218 -0
- package/dist/chunk-ETUZFFPU.js.map +1 -0
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-YITN5WXI.js → chunk-FC52YWAO.js} +2 -2
- package/dist/{chunk-ZI7QA4MK.js → chunk-FEIXG5FA.js} +2 -2
- package/dist/{chunk-CLXTDOKV.js → chunk-FUNBJQYR.js} +2 -2
- package/dist/{chunk-CLSYE5OZ.js → chunk-G2STTRYB.js} +2 -2
- package/dist/{chunk-TDYAIXHK.js → chunk-H36X4NRH.js} +5 -5
- package/dist/{chunk-3GQC3NMB.js → chunk-H56VY4BY.js} +5 -5
- package/dist/{chunk-PUKTBULQ.js → chunk-HWLIMQE2.js} +2 -2
- package/dist/{chunk-V2ES4WCZ.js → chunk-IIARQTZ2.js} +2 -2
- package/dist/{chunk-QEV26L2F.js → chunk-IKDRGVVJ.js} +2 -2
- package/dist/{chunk-WQZZDN5M.js → chunk-IVEHY2VT.js} +254 -377
- package/dist/chunk-IVEHY2VT.js.map +1 -0
- package/dist/{chunk-LS3XH3UE.js → chunk-IYIBGSHJ.js} +2 -2
- package/dist/{chunk-YSJTCOHR.js → chunk-J27JML5O.js} +2 -2
- package/dist/{chunk-AQK54Q7O.js → chunk-JB56XYHP.js} +2 -2
- package/dist/{chunk-NE6PHPLG.js → chunk-JBH26P62.js} +10 -10
- package/dist/{chunk-CWHV574S.js → chunk-JUT3OR2L.js} +6 -6
- package/dist/{chunk-6ET2VII7.js → chunk-JYG2334B.js} +2 -2
- package/dist/{chunk-LF5IGOOD.js → chunk-JYM4Y6SO.js} +16 -16
- package/dist/{chunk-FB64XE3R.js → chunk-JZ4TJHR2.js} +2 -2
- package/dist/{chunk-EHS5MJEF.js → chunk-KTKHO3UI.js} +3 -3
- package/dist/{chunk-WIKO4SCR.js → chunk-LHRQBMJN.js} +2 -2
- package/dist/{chunk-K4J4E7WV.js → chunk-LO4UKPKX.js} +2 -2
- package/dist/{chunk-JN72EPOE.js → chunk-MAAL6OZV.js} +7 -7
- package/dist/chunk-MAAL6OZV.js.map +1 -0
- package/dist/{chunk-MY2PLXSM.js → chunk-MCTM4YPL.js} +2 -1
- package/dist/{chunk-MY2PLXSM.js.map → chunk-MCTM4YPL.js.map} +1 -1
- package/dist/{chunk-6NQ4LSMS.js → chunk-MGZMC6WN.js} +47 -47
- package/dist/chunk-MSAI3Q7D.js +372 -0
- package/dist/chunk-MSAI3Q7D.js.map +1 -0
- package/dist/{chunk-BMYZTZ5D.js → chunk-MY7B3HYC.js} +2 -2
- package/dist/{chunk-NB3LRY4T.js → chunk-N4PYB235.js} +2 -2
- package/dist/{chunk-S3YM4SZQ.js → chunk-N7QV3CMX.js} +2 -2
- package/dist/{chunk-L6UZEAID.js → chunk-NJVR724O.js} +4 -4
- package/dist/{chunk-VLLD4TOB.js → chunk-NMZVDRVP.js} +2 -2
- package/dist/{chunk-P2WZHLUF.js → chunk-NOL7SEWN.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-6L3PBSAR.js → chunk-PMNLL2TL.js} +2 -2
- package/dist/{chunk-VYCOJKSX.js → chunk-PO7NXNGC.js} +2 -2
- package/dist/{chunk-4IIMMZZK.js → chunk-PULDSMCJ.js} +10 -10
- package/dist/{chunk-G6SVZWPB.js → chunk-PZX3AQLF.js} +44 -1
- package/dist/chunk-PZX3AQLF.js.map +1 -0
- package/dist/{chunk-HBBHL4YF.js → chunk-QOXNTKXB.js} +2 -2
- package/dist/{chunk-CUCWX3MO.js → chunk-QZLCVO2V.js} +2 -2
- package/dist/{chunk-NE7I2RCT.js → chunk-R3X2T2LP.js} +2 -2
- package/dist/{chunk-S7GCT7NU.js → chunk-RBITPCMK.js} +8 -8
- package/dist/{chunk-EVBI7S4J.js → chunk-RMDGAAZF.js} +4 -4
- package/dist/{chunk-BJRAELDH.js → chunk-SCXURCVT.js} +3 -3
- package/dist/{chunk-EQ6YOIKO.js → chunk-ST2RQUXC.js} +3 -3
- package/dist/{chunk-CS52MFDV.js → chunk-SZG4WFAG.js} +2 -2
- package/dist/{chunk-BFO3IR56.js → chunk-TPEZ2SHV.js} +2 -2
- package/dist/chunk-TYT4TEQV.js +45 -0
- package/dist/chunk-TYT4TEQV.js.map +1 -0
- package/dist/{chunk-YRLHEEQT.js → chunk-U6VC6RP3.js} +2 -2
- package/dist/chunk-UCPGOUDZ.js +726 -0
- package/dist/chunk-UCPGOUDZ.js.map +1 -0
- package/dist/{chunk-MOQPEFLN.js → chunk-UP75KL3L.js} +2 -2
- package/dist/{chunk-W4AOR52H.js → chunk-UQDC3W77.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
- package/dist/{chunk-D5F7TKMH.js → chunk-V4JZCTMK.js} +2 -2
- package/dist/{chunk-MQJL6CDZ.js → chunk-VA4O3U7U.js} +4 -4
- package/dist/{chunk-4CDYONWO.js → chunk-VDO32NLK.js} +3 -3
- package/dist/{chunk-SOZUJCME.js → chunk-VHDKPGMG.js} +6 -6
- package/dist/{chunk-QWW2JDDX.js → chunk-WNRTXEWW.js} +6 -6
- package/dist/{chunk-NR2ASHBL.js → chunk-XOV7NRY5.js} +2 -2
- package/dist/{chunk-KP7SANCF.js → chunk-YPI4RQWV.js} +2 -2
- package/dist/{chunk-JKMHZF3Q.js → chunk-YSWJXOOL.js} +2 -2
- package/dist/{chunk-LQG5O5EP.js → chunk-Z3O53WZR.js} +2 -2
- package/dist/{chunk-QVS22WTO.js → chunk-ZG5DWWKK.js} +2 -2
- package/dist/cli.js +99 -92
- package/dist/coding/export-okf-codegraph.js +12 -12
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +12 -12
- package/dist/config.js +24 -18
- package/dist/connectors/codex-materialize-runner.js +11 -11
- package/dist/connectors/index.js +11 -11
- package/dist/console/state.d.ts +30 -0
- package/dist/console/state.d.ts.map +1 -1
- package/dist/contradiction/index.js +5 -5
- package/dist/convergence-refresh.js +13 -13
- package/dist/corpus-watermark.js +13 -13
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-http-glue.js +16 -8
- package/dist/embedding-fallback.d.ts +41 -31
- package/dist/embedding-fallback.d.ts.map +1 -1
- package/dist/embedding-fallback.js +9 -1
- package/dist/embedding-generation-lock.d.ts +49 -0
- package/dist/embedding-generation-lock.d.ts.map +1 -0
- package/dist/embedding-generation-lock.js +19 -0
- package/dist/embedding-index-storage.d.ts +276 -0
- package/dist/embedding-index-storage.d.ts.map +1 -0
- package/dist/embedding-index-storage.js +28 -0
- package/dist/entity-retrieval.js +12 -12
- package/dist/event-order-recall.js +3 -3
- package/dist/explicit-capture.js +6 -6
- package/dist/external-wiki-access.js +57 -50
- package/dist/extraction.js +17 -17
- package/dist/{first-start-migration-7OATHJKB.js → first-start-migration-GAT7G733.js} +4 -4
- package/dist/{graph-edge-decay-KQGS3L3B.js → graph-edge-decay-MKIRZ762.js} +5 -5
- package/dist/graph-jsonl.js +3 -3
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +2 -2
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +195 -164
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/local-llm.js +2 -2
- package/dist/location/index.js +14 -14
- package/dist/maintenance/memory-governance.js +11 -11
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +11 -11
- package/dist/maintenance/rebuild-memory-projection.js +12 -12
- package/dist/memory-browse-operations.js +57 -50
- package/dist/memory-subject.js +3 -3
- package/dist/namespaces/migrate.js +12 -12
- package/dist/namespaces/storage.js +11 -11
- package/dist/offline-sync-changeset-generations.d.ts +82 -0
- package/dist/offline-sync-changeset-generations.d.ts.map +1 -0
- package/dist/offline-sync-changeset-generations.js +21 -0
- package/dist/offline-sync-embedding-generation.d.ts +257 -0
- package/dist/offline-sync-embedding-generation.d.ts.map +1 -0
- package/dist/offline-sync-embedding-generation.js +44 -0
- package/dist/offline-sync-embedding-generation.js.map +1 -0
- package/dist/offline-sync-exclude-globs.d.ts +15 -0
- package/dist/offline-sync-exclude-globs.d.ts.map +1 -1
- package/dist/offline-sync-exclude-globs.js +11 -3
- package/dist/offline-sync-file-io.d.ts +7 -0
- package/dist/offline-sync-file-io.d.ts.map +1 -1
- package/dist/offline-sync-file-io.js +14 -2
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/offline-sync-runtime-state.d.ts +5 -0
- package/dist/offline-sync-runtime-state.d.ts.map +1 -0
- package/dist/offline-sync-runtime-state.js +14 -0
- package/dist/offline-sync-runtime-state.js.map +1 -0
- package/dist/offline-sync-snapshot-stream.d.ts +26 -0
- package/dist/offline-sync-snapshot-stream.d.ts.map +1 -0
- package/dist/offline-sync-snapshot-stream.js +29 -0
- package/dist/offline-sync-snapshot-stream.js.map +1 -0
- package/dist/offline-sync-upload-staging.d.ts +34 -0
- package/dist/offline-sync-upload-staging.d.ts.map +1 -0
- package/dist/offline-sync-upload-staging.js +26 -0
- package/dist/offline-sync-upload-staging.js.map +1 -0
- package/dist/offline-sync.d.ts +58 -28
- package/dist/offline-sync.d.ts.map +1 -1
- package/dist/offline-sync.js +22 -8
- package/dist/operator-doctor-corpus.js +14 -14
- package/dist/operator-toolkit.js +35 -29
- package/dist/orchestration/maintenance.js +22 -22
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +129 -122
- package/dist/recall-navigation-operations.js +57 -50
- package/dist/recall-navigation.js +56 -49
- package/dist/reconcile/manifest.js +4 -4
- package/dist/{resolution-DNX7FO7A.js → resolution-K4A3VPKZ.js} +6 -6
- package/dist/resolution-K4A3VPKZ.js.map +1 -0
- package/dist/resume-bundles.js +25 -19
- package/dist/salvage-envelope.js +5 -5
- package/dist/secure-store/index.js +2 -2
- package/dist/secure-store/secure-fs.d.ts +14 -1
- package/dist/secure-store/secure-fs.d.ts.map +1 -1
- package/dist/semantic-consolidation.js +12 -12
- package/dist/semantic-rule-promotion.js +11 -11
- package/dist/semantic-rule-verifier.js +11 -11
- package/dist/source-agent-qualifier.js +14 -14
- package/dist/standup/build.js +11 -11
- package/dist/{state-ITXJKXRE.js → state-JB47KHYV.js} +2 -2
- package/dist/state-JB47KHYV.js.map +1 -0
- package/dist/storage.d.ts +9 -3
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -10
- package/dist/subject-config.js +4 -4
- package/dist/summarizer.js +3 -3
- package/dist/support-passport/index.js +59 -52
- package/dist/support-passport/offline-sync-guard.d.ts +6 -2
- package/dist/support-passport/offline-sync-guard.d.ts.map +1 -1
- package/dist/{trace-ZFVRH4GO.js → trace-LXXCMZJA.js} +3 -3
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +3 -3
- package/dist/transfer/export-okf.js +12 -12
- package/dist/{tui-55UJMJX7.js → tui-2Z5WOV7L.js} +3 -3
- package/dist/tui-2Z5WOV7L.js.map +1 -0
- package/dist/verified-recall.js +11 -11
- package/dist/who-knows-cli.js +12 -12
- package/dist/who-knows.js +11 -11
- package/dist/write-envelope.js +4 -4
- package/package.json +2 -2
- package/src/access-http-offline-stream.ts +3 -0
- package/src/access-mcp-output-schemas.ts +1 -0
- package/src/access-service.ts +7 -18
- package/src/console/state.test.ts +56 -0
- package/src/console/state.ts +83 -0
- package/src/embedding-fallback.ts +325 -141
- package/src/embedding-generation-lock.ts +141 -0
- package/src/embedding-index-storage.test.ts +245 -0
- package/src/embedding-index-storage.ts +1068 -0
- package/src/index.ts +3 -3
- package/src/offline-sync-changeset-generations.test.ts +962 -0
- package/src/offline-sync-changeset-generations.ts +549 -0
- package/src/offline-sync-embedding-generation.test.ts +2173 -0
- package/src/offline-sync-embedding-generation.ts +1153 -0
- package/src/offline-sync-exclude-globs.ts +112 -0
- package/src/offline-sync-file-io.ts +48 -0
- package/src/offline-sync-runtime-state.ts +48 -0
- package/src/offline-sync-snapshot-stream.ts +99 -0
- package/src/offline-sync-upload-staging.ts +291 -0
- package/src/offline-sync.test.ts +164 -1
- package/src/offline-sync.ts +372 -454
- package/src/orchestrator.ts +2 -1
- package/src/secure-store/secure-fs.ts +26 -6
- package/src/storage.ts +11 -6
- package/src/support-passport/offline-sync-guard.ts +16 -2
- package/dist/chunk-3BTFBQLO.js.map +0 -1
- package/dist/chunk-AREGMX2J.js +0 -50
- package/dist/chunk-AREGMX2J.js.map +0 -1
- package/dist/chunk-FOH7AMMI.js.map +0 -1
- package/dist/chunk-FRCESVV6.js.map +0 -1
- package/dist/chunk-G6SVZWPB.js.map +0 -1
- package/dist/chunk-JN72EPOE.js.map +0 -1
- package/dist/chunk-RB776UH3.js.map +0 -1
- package/dist/chunk-WQZZDN5M.js.map +0 -1
- /package/dist/{auto-sync-6IPX2BAC.js.map → auto-sync-U5N3CB5Y.js.map} +0 -0
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
- /package/dist/{capsule-merge-3MNURAQ3.js.map → capsule-merge-ZCXUPLNJ.js.map} +0 -0
- /package/dist/{chunk-JE334SIA.js.map → chunk-22DYJLFN.js.map} +0 -0
- /package/dist/{chunk-JGSQ6P4D.js.map → chunk-2FX374UI.js.map} +0 -0
- /package/dist/{chunk-SY5KDWGU.js.map → chunk-2WLMWP2T.js.map} +0 -0
- /package/dist/{chunk-6TT6HRE4.js.map → chunk-3AGHSQPM.js.map} +0 -0
- /package/dist/{chunk-P6WFK4ZJ.js.map → chunk-3O5BB45T.js.map} +0 -0
- /package/dist/{chunk-3C3J243X.js.map → chunk-3QKO4QN7.js.map} +0 -0
- /package/dist/{chunk-KFCMMDHH.js.map → chunk-3YD44CH2.js.map} +0 -0
- /package/dist/{chunk-7DE2NOON.js.map → chunk-4O4XCBOS.js.map} +0 -0
- /package/dist/{chunk-GDYU6VF4.js.map → chunk-5AYUHSAU.js.map} +0 -0
- /package/dist/{chunk-ICZQG4XE.js.map → chunk-5FAGCOZD.js.map} +0 -0
- /package/dist/{chunk-VKQI47DJ.js.map → chunk-5FNZ3G6X.js.map} +0 -0
- /package/dist/{chunk-VQNONC4E.js.map → chunk-5STPRPE7.js.map} +0 -0
- /package/dist/{chunk-52II7PB6.js.map → chunk-645WOU5I.js.map} +0 -0
- /package/dist/{chunk-SRZW7FNU.js.map → chunk-64ZUALXK.js.map} +0 -0
- /package/dist/{chunk-4BZP2B4U.js.map → chunk-6MNKDTDI.js.map} +0 -0
- /package/dist/{chunk-DUJYRVUD.js.map → chunk-6O2VUWV6.js.map} +0 -0
- /package/dist/{chunk-HESDNYRB.js.map → chunk-76M5OK7V.js.map} +0 -0
- /package/dist/{chunk-FAN73COB.js.map → chunk-7D7OUO5W.js.map} +0 -0
- /package/dist/{chunk-DZ525SNQ.js.map → chunk-7QE46QXI.js.map} +0 -0
- /package/dist/{chunk-VX6GCKBS.js.map → chunk-7WBSMXGC.js.map} +0 -0
- /package/dist/{chunk-VVDWZRZG.js.map → chunk-A6RVIHHK.js.map} +0 -0
- /package/dist/{chunk-M536WNTQ.js.map → chunk-AEJ3TE5H.js.map} +0 -0
- /package/dist/{chunk-72JNNJJJ.js.map → chunk-AXC4WSF3.js.map} +0 -0
- /package/dist/{chunk-UA5DX5C3.js.map → chunk-AZIOEOK4.js.map} +0 -0
- /package/dist/{chunk-N4S4NUX5.js.map → chunk-B3O3IRZN.js.map} +0 -0
- /package/dist/{chunk-XTYX6V52.js.map → chunk-BJVO4H5C.js.map} +0 -0
- /package/dist/{chunk-7LCGFUMB.js.map → chunk-BUKK5SWA.js.map} +0 -0
- /package/dist/{chunk-TM3RXL4G.js.map → chunk-BUQKSQXL.js.map} +0 -0
- /package/dist/{chunk-37S3IMYB.js.map → chunk-DGS34QSY.js.map} +0 -0
- /package/dist/{chunk-4IEOGIPB.js.map → chunk-DO4C4AXD.js.map} +0 -0
- /package/dist/{chunk-SPSRGEDQ.js.map → chunk-EAQQ2FD3.js.map} +0 -0
- /package/dist/{chunk-C3QXITIP.js.map → chunk-EQUD7FP4.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-YITN5WXI.js.map → chunk-FC52YWAO.js.map} +0 -0
- /package/dist/{chunk-ZI7QA4MK.js.map → chunk-FEIXG5FA.js.map} +0 -0
- /package/dist/{chunk-CLXTDOKV.js.map → chunk-FUNBJQYR.js.map} +0 -0
- /package/dist/{chunk-CLSYE5OZ.js.map → chunk-G2STTRYB.js.map} +0 -0
- /package/dist/{chunk-TDYAIXHK.js.map → chunk-H36X4NRH.js.map} +0 -0
- /package/dist/{chunk-3GQC3NMB.js.map → chunk-H56VY4BY.js.map} +0 -0
- /package/dist/{chunk-PUKTBULQ.js.map → chunk-HWLIMQE2.js.map} +0 -0
- /package/dist/{chunk-V2ES4WCZ.js.map → chunk-IIARQTZ2.js.map} +0 -0
- /package/dist/{chunk-QEV26L2F.js.map → chunk-IKDRGVVJ.js.map} +0 -0
- /package/dist/{chunk-LS3XH3UE.js.map → chunk-IYIBGSHJ.js.map} +0 -0
- /package/dist/{chunk-YSJTCOHR.js.map → chunk-J27JML5O.js.map} +0 -0
- /package/dist/{chunk-AQK54Q7O.js.map → chunk-JB56XYHP.js.map} +0 -0
- /package/dist/{chunk-NE6PHPLG.js.map → chunk-JBH26P62.js.map} +0 -0
- /package/dist/{chunk-CWHV574S.js.map → chunk-JUT3OR2L.js.map} +0 -0
- /package/dist/{chunk-6ET2VII7.js.map → chunk-JYG2334B.js.map} +0 -0
- /package/dist/{chunk-LF5IGOOD.js.map → chunk-JYM4Y6SO.js.map} +0 -0
- /package/dist/{chunk-FB64XE3R.js.map → chunk-JZ4TJHR2.js.map} +0 -0
- /package/dist/{chunk-EHS5MJEF.js.map → chunk-KTKHO3UI.js.map} +0 -0
- /package/dist/{chunk-WIKO4SCR.js.map → chunk-LHRQBMJN.js.map} +0 -0
- /package/dist/{chunk-K4J4E7WV.js.map → chunk-LO4UKPKX.js.map} +0 -0
- /package/dist/{chunk-6NQ4LSMS.js.map → chunk-MGZMC6WN.js.map} +0 -0
- /package/dist/{chunk-BMYZTZ5D.js.map → chunk-MY7B3HYC.js.map} +0 -0
- /package/dist/{chunk-NB3LRY4T.js.map → chunk-N4PYB235.js.map} +0 -0
- /package/dist/{chunk-S3YM4SZQ.js.map → chunk-N7QV3CMX.js.map} +0 -0
- /package/dist/{chunk-L6UZEAID.js.map → chunk-NJVR724O.js.map} +0 -0
- /package/dist/{chunk-VLLD4TOB.js.map → chunk-NMZVDRVP.js.map} +0 -0
- /package/dist/{chunk-P2WZHLUF.js.map → chunk-NOL7SEWN.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
- /package/dist/{chunk-6L3PBSAR.js.map → chunk-PMNLL2TL.js.map} +0 -0
- /package/dist/{chunk-VYCOJKSX.js.map → chunk-PO7NXNGC.js.map} +0 -0
- /package/dist/{chunk-4IIMMZZK.js.map → chunk-PULDSMCJ.js.map} +0 -0
- /package/dist/{chunk-HBBHL4YF.js.map → chunk-QOXNTKXB.js.map} +0 -0
- /package/dist/{chunk-CUCWX3MO.js.map → chunk-QZLCVO2V.js.map} +0 -0
- /package/dist/{chunk-NE7I2RCT.js.map → chunk-R3X2T2LP.js.map} +0 -0
- /package/dist/{chunk-S7GCT7NU.js.map → chunk-RBITPCMK.js.map} +0 -0
- /package/dist/{chunk-EVBI7S4J.js.map → chunk-RMDGAAZF.js.map} +0 -0
- /package/dist/{chunk-BJRAELDH.js.map → chunk-SCXURCVT.js.map} +0 -0
- /package/dist/{chunk-EQ6YOIKO.js.map → chunk-ST2RQUXC.js.map} +0 -0
- /package/dist/{chunk-CS52MFDV.js.map → chunk-SZG4WFAG.js.map} +0 -0
- /package/dist/{chunk-BFO3IR56.js.map → chunk-TPEZ2SHV.js.map} +0 -0
- /package/dist/{chunk-YRLHEEQT.js.map → chunk-U6VC6RP3.js.map} +0 -0
- /package/dist/{chunk-MOQPEFLN.js.map → chunk-UP75KL3L.js.map} +0 -0
- /package/dist/{chunk-W4AOR52H.js.map → chunk-UQDC3W77.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
- /package/dist/{chunk-D5F7TKMH.js.map → chunk-V4JZCTMK.js.map} +0 -0
- /package/dist/{chunk-MQJL6CDZ.js.map → chunk-VA4O3U7U.js.map} +0 -0
- /package/dist/{chunk-4CDYONWO.js.map → chunk-VDO32NLK.js.map} +0 -0
- /package/dist/{chunk-SOZUJCME.js.map → chunk-VHDKPGMG.js.map} +0 -0
- /package/dist/{chunk-QWW2JDDX.js.map → chunk-WNRTXEWW.js.map} +0 -0
- /package/dist/{chunk-NR2ASHBL.js.map → chunk-XOV7NRY5.js.map} +0 -0
- /package/dist/{chunk-KP7SANCF.js.map → chunk-YPI4RQWV.js.map} +0 -0
- /package/dist/{chunk-JKMHZF3Q.js.map → chunk-YSWJXOOL.js.map} +0 -0
- /package/dist/{chunk-LQG5O5EP.js.map → chunk-Z3O53WZR.js.map} +0 -0
- /package/dist/{chunk-QVS22WTO.js.map → chunk-ZG5DWWKK.js.map} +0 -0
- /package/dist/{graph-edge-decay-KQGS3L3B.js.map → embedding-generation-lock.js.map} +0 -0
- /package/dist/{resolution-DNX7FO7A.js.map → embedding-index-storage.js.map} +0 -0
- /package/dist/{first-start-migration-7OATHJKB.js.map → first-start-migration-GAT7G733.js.map} +0 -0
- /package/dist/{state-ITXJKXRE.js.map → graph-edge-decay-MKIRZ762.js.map} +0 -0
- /package/dist/{tui-55UJMJX7.js.map → offline-sync-changeset-generations.js.map} +0 -0
- /package/dist/{trace-ZFVRH4GO.js.map → trace-LXXCMZJA.js.map} +0 -0
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
import {
|
|
2
|
+
readEnvVar
|
|
3
|
+
} from "./chunk-P2QRMMVQ.js";
|
|
4
|
+
import {
|
|
5
|
+
log
|
|
6
|
+
} from "./chunk-3WCASNQZ.js";
|
|
7
|
+
|
|
8
|
+
// src/embedding-index-storage.ts
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { constants as bufferConstants } from "buffer";
|
|
11
|
+
import { lstat, mkdir, mkdtemp, realpath, readFile, readdir, rename, rm, stat, writeFile } from "fs/promises";
|
|
12
|
+
var EmbeddingIndexCapacityError = class extends Error {
|
|
13
|
+
constructor(message, memoryId) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.memoryId = memoryId;
|
|
16
|
+
}
|
|
17
|
+
memoryId;
|
|
18
|
+
name = "EmbeddingIndexCapacityError";
|
|
19
|
+
};
|
|
20
|
+
var EmbeddingIndexStorageError = class extends Error {
|
|
21
|
+
name = "EmbeddingIndexStorageError";
|
|
22
|
+
};
|
|
23
|
+
var DEFAULT_INDEX_FILE_CHAR_LIMIT = bufferConstants.MAX_STRING_LENGTH - 16 * 1024 * 1024;
|
|
24
|
+
function resolveIndexHardReadCharLimit() {
|
|
25
|
+
const soft = resolveIndexFileCharLimit();
|
|
26
|
+
const raw = readEnvVar("REMNIC_EMBEDDING_INDEX_HARD_READ_LIMIT_CHARS");
|
|
27
|
+
if (raw) {
|
|
28
|
+
const parsed = Number(raw);
|
|
29
|
+
if (Number.isInteger(parsed) && parsed >= soft) return parsed;
|
|
30
|
+
}
|
|
31
|
+
return bufferConstants.MAX_STRING_LENGTH;
|
|
32
|
+
}
|
|
33
|
+
var SHARD_COUNT = 64;
|
|
34
|
+
var SHARD_FILE_PATTERN = /^shard-\d{4}\.json$/;
|
|
35
|
+
function resolveIndexFileCharLimit() {
|
|
36
|
+
const raw = readEnvVar("REMNIC_EMBEDDING_INDEX_FILE_CHAR_LIMIT");
|
|
37
|
+
if (raw) {
|
|
38
|
+
const parsed = Number(raw);
|
|
39
|
+
if (Number.isInteger(parsed) && parsed >= 1024) return parsed;
|
|
40
|
+
}
|
|
41
|
+
return DEFAULT_INDEX_FILE_CHAR_LIMIT;
|
|
42
|
+
}
|
|
43
|
+
function shardFileName(shardIndex) {
|
|
44
|
+
return `shard-${String(shardIndex).padStart(4, "0")}.json`;
|
|
45
|
+
}
|
|
46
|
+
function replacementBackupPath(shardDir) {
|
|
47
|
+
return path.join(path.dirname(shardDir), "embeddings.pre-replace.tmp");
|
|
48
|
+
}
|
|
49
|
+
function shardIndexOf(memoryId, shardCount) {
|
|
50
|
+
let hash = 2166136261;
|
|
51
|
+
for (let i = 0; i < memoryId.length; i++) {
|
|
52
|
+
hash ^= memoryId.charCodeAt(i);
|
|
53
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
54
|
+
}
|
|
55
|
+
return hash % shardCount;
|
|
56
|
+
}
|
|
57
|
+
function validateShardMembership(shardName, file) {
|
|
58
|
+
const match = /^shard-(\d{4})\.json$/.exec(shardName);
|
|
59
|
+
if (!match) return;
|
|
60
|
+
const shardIndex = Number(match[1]);
|
|
61
|
+
for (const id of Object.keys(file.entries)) {
|
|
62
|
+
if (shardIndexOf(id, SHARD_COUNT) !== shardIndex) {
|
|
63
|
+
throw new EmbeddingIndexStorageError(
|
|
64
|
+
`refusing embedding index shard ${shardName}: id ${id} hashes to shard ${shardIndexOf(id, SHARD_COUNT)}, not ${shardIndex}; file preserved in place`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function isInvalidStringLengthError(err) {
|
|
70
|
+
return err instanceof RangeError && /invalid string length/i.test(err.message);
|
|
71
|
+
}
|
|
72
|
+
function parseEmbeddingIndexDocument(raw) {
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(raw);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return {
|
|
78
|
+
outcome: "unreadable",
|
|
79
|
+
reason: `JSON parse failed: ${err instanceof Error ? err.message : String(err)}`
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 1 || typeof parsed.provider !== "string" || typeof parsed.model !== "string" || parsed.model.length === 0) {
|
|
83
|
+
return { outcome: "foreign" };
|
|
84
|
+
}
|
|
85
|
+
if (parsed.provider !== "openai" && parsed.provider !== "local" && parsed.provider !== "host") {
|
|
86
|
+
return { outcome: "foreign" };
|
|
87
|
+
}
|
|
88
|
+
const entries = parsed.entries;
|
|
89
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
|
|
90
|
+
return {
|
|
91
|
+
outcome: "unreadable",
|
|
92
|
+
reason: "entries must be a plain record of memory id -> entry"
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
for (const [id, entry] of Object.entries(entries)) {
|
|
96
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
|
97
|
+
return { outcome: "unreadable", reason: `entry ${id} must be an object` };
|
|
98
|
+
}
|
|
99
|
+
const candidate = entry;
|
|
100
|
+
if (typeof candidate.path !== "string") {
|
|
101
|
+
return { outcome: "unreadable", reason: `entry ${id}.path must be a string` };
|
|
102
|
+
}
|
|
103
|
+
if (!Array.isArray(candidate.vector)) {
|
|
104
|
+
return { outcome: "unreadable", reason: `entry ${id}.vector must be an array` };
|
|
105
|
+
}
|
|
106
|
+
for (const component of candidate.vector) {
|
|
107
|
+
if (!Number.isFinite(component)) {
|
|
108
|
+
return {
|
|
109
|
+
outcome: "unreadable",
|
|
110
|
+
reason: `entry ${id}.vector must contain only finite numbers`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { outcome: "ok", file: parsed };
|
|
116
|
+
}
|
|
117
|
+
function shardEntriesForIndex(index) {
|
|
118
|
+
const groups = /* @__PURE__ */ new Map();
|
|
119
|
+
for (const id of Object.keys(index.entries)) {
|
|
120
|
+
const shardIndex = shardIndexOf(id, SHARD_COUNT);
|
|
121
|
+
const group = groups.get(shardIndex);
|
|
122
|
+
if (group) group[id] = index.entries[id];
|
|
123
|
+
else groups.set(shardIndex, { [id]: index.entries[id] });
|
|
124
|
+
}
|
|
125
|
+
return groups;
|
|
126
|
+
}
|
|
127
|
+
function serializeEmbeddingShard(index, shardIndex, entries, limit = resolveIndexFileCharLimit()) {
|
|
128
|
+
let body;
|
|
129
|
+
try {
|
|
130
|
+
body = JSON.stringify({
|
|
131
|
+
version: 1,
|
|
132
|
+
provider: index.provider,
|
|
133
|
+
model: index.model,
|
|
134
|
+
entries
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
throw new EmbeddingIndexCapacityError(
|
|
138
|
+
`embedding index shard ${shardFileName(shardIndex)} exceeded the single-string ceiling while serializing: ${err instanceof Error ? err.message : String(err)}`,
|
|
139
|
+
largestEntryId(entries) || void 0
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (body.length > limit) {
|
|
143
|
+
const worstId = largestEntryId(entries);
|
|
144
|
+
const worstLength = worstId && entries[worstId] ? JSON.stringify(entries[worstId]).length : 0;
|
|
145
|
+
throw new EmbeddingIndexCapacityError(
|
|
146
|
+
`embedding index shard ${shardFileName(shardIndex)} needs ${body.length} chars, over the ${limit}-char single-file limit; largest entry ${worstId || "(unknown)"} serializes to ${worstLength} chars`,
|
|
147
|
+
worstId || void 0
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return body;
|
|
151
|
+
}
|
|
152
|
+
function largestEntryId(entries) {
|
|
153
|
+
let worstId = "";
|
|
154
|
+
let worstLength = 0;
|
|
155
|
+
for (const [id, entry] of Object.entries(entries)) {
|
|
156
|
+
let length;
|
|
157
|
+
try {
|
|
158
|
+
length = JSON.stringify(entry).length;
|
|
159
|
+
} catch {
|
|
160
|
+
return id;
|
|
161
|
+
}
|
|
162
|
+
if (length > worstLength) {
|
|
163
|
+
worstLength = length;
|
|
164
|
+
worstId = id;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return worstId;
|
|
168
|
+
}
|
|
169
|
+
var EmbeddingIndexFileStore = class _EmbeddingIndexFileStore {
|
|
170
|
+
constructor(indexPath, shardDir, statusPath, io) {
|
|
171
|
+
this.indexPath = indexPath;
|
|
172
|
+
this.shardDir = shardDir;
|
|
173
|
+
this.statusPath = statusPath;
|
|
174
|
+
this.io = io;
|
|
175
|
+
if (io && Boolean(io.readUtf8) !== Boolean(io.writeUtf8)) {
|
|
176
|
+
throw new EmbeddingIndexStorageError(
|
|
177
|
+
"embedding index store io requires both readUtf8 and writeUtf8; a half-wired io would mix encrypted and plaintext files"
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
indexPath;
|
|
182
|
+
shardDir;
|
|
183
|
+
statusPath;
|
|
184
|
+
io;
|
|
185
|
+
/** Legacy single-file layout path (public for error messages). */
|
|
186
|
+
get legacyPath() {
|
|
187
|
+
return this.indexPath;
|
|
188
|
+
}
|
|
189
|
+
/** Record an unreadable-authoritative-file reason (both read and mutation paths). */
|
|
190
|
+
async recordIndexStatusForLoad(reason) {
|
|
191
|
+
await this.recordIndexStatus({
|
|
192
|
+
lastReadRecovery: { ts: (/* @__PURE__ */ new Date()).toISOString(), message: reason }
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Which generation owns the index right now. The published
|
|
197
|
+
* `state/embeddings/` directory IS the layout marker: until it exists the
|
|
198
|
+
* legacy file is authoritative; from the atomic rename onward it never
|
|
199
|
+
* merges again.
|
|
200
|
+
*/
|
|
201
|
+
async detectLayout() {
|
|
202
|
+
try {
|
|
203
|
+
const info = await lstat(this.shardDir);
|
|
204
|
+
if (info.isSymbolicLink()) {
|
|
205
|
+
throw new EmbeddingIndexStorageError(
|
|
206
|
+
`embedding index shard directory is a symlink; refusing to read or write through it: ${this.shardDir}`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
return "sharded";
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (err instanceof EmbeddingIndexStorageError) throw err;
|
|
212
|
+
if (err.code !== "ENOENT") {
|
|
213
|
+
log.warn(`embedding index: shard dir stat failed (${err instanceof Error ? err.message : String(err)}); assuming sharded layout`);
|
|
214
|
+
return "sharded";
|
|
215
|
+
}
|
|
216
|
+
if (await this.replacementBackupExists()) return "sharded";
|
|
217
|
+
return this.legacyOrEmptyLayout();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Roll the fixed transaction backup back into the published position.
|
|
222
|
+
* Returns true when a rollback happened.
|
|
223
|
+
*/
|
|
224
|
+
async replacementBackupExists() {
|
|
225
|
+
const backupPath = replacementBackupPath(this.shardDir);
|
|
226
|
+
try {
|
|
227
|
+
await stat(backupPath);
|
|
228
|
+
return true;
|
|
229
|
+
} catch (err) {
|
|
230
|
+
if (err.code === "ENOENT") return false;
|
|
231
|
+
throw new EmbeddingIndexStorageError(
|
|
232
|
+
`cannot stat embedding index replacement backup ${backupPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The fixed transaction backup is a path with well-known semantics; a
|
|
238
|
+
* planted symlink must never be restored into the published position (it
|
|
239
|
+
* would point the generation outside the memory dir) nor removed through
|
|
240
|
+
* as if it were the former generation. Symlinks are rejected outright.
|
|
241
|
+
*/
|
|
242
|
+
async assertBackupNotSymlink(backupPath) {
|
|
243
|
+
const info = await lstat(backupPath).catch((err) => {
|
|
244
|
+
if (err.code === "ENOENT") return null;
|
|
245
|
+
throw err;
|
|
246
|
+
});
|
|
247
|
+
if (info?.isSymbolicLink()) {
|
|
248
|
+
throw new EmbeddingIndexStorageError(
|
|
249
|
+
`embedding index replacement backup is a symlink; refusing to touch it: ${backupPath}`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Mutation-queue entry point: roll the fixed transaction backup back into
|
|
255
|
+
* the published position after an interrupted replacement. Returns true
|
|
256
|
+
* when a rollback happened. Only fires in the actual rename gap
|
|
257
|
+
* (published directory absent); callers must invalidate any cached index
|
|
258
|
+
* view when this returns true.
|
|
259
|
+
*/
|
|
260
|
+
async recoverIfInterrupted(fence) {
|
|
261
|
+
try {
|
|
262
|
+
await stat(this.shardDir);
|
|
263
|
+
return false;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
if (err.code !== "ENOENT") {
|
|
266
|
+
log.warn(`embedding index: recovery skipped because shard dir stat failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const backupPath = replacementBackupPath(this.shardDir);
|
|
271
|
+
if (!await this.replacementBackupExists()) return false;
|
|
272
|
+
await this.assertBackupNotSymlink(backupPath);
|
|
273
|
+
await fence?.();
|
|
274
|
+
await rename(backupPath, this.shardDir);
|
|
275
|
+
log.warn(
|
|
276
|
+
`embedding index: recovered interrupted replacement; former generation restored from ${backupPath}`
|
|
277
|
+
);
|
|
278
|
+
await this.recordIndexStatus({
|
|
279
|
+
lastReadRecovery: {
|
|
280
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
281
|
+
message: "interrupted identity replacement rolled back to the former generation"
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
async legacyOrEmptyLayout() {
|
|
287
|
+
try {
|
|
288
|
+
await stat(this.indexPath);
|
|
289
|
+
return "legacy";
|
|
290
|
+
} catch (err) {
|
|
291
|
+
if (err.code === "ENOENT") return "empty";
|
|
292
|
+
throw err;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/** Header identity of the authoritative generation, read leniently. */
|
|
296
|
+
async identityFromDisk() {
|
|
297
|
+
const layout = await this.detectLayout();
|
|
298
|
+
if (layout === "sharded") {
|
|
299
|
+
for (const name of await this.readShardNames()) {
|
|
300
|
+
const read = await this.readFileAt(path.join(this.shardDir, name));
|
|
301
|
+
if (read.outcome === "ok") {
|
|
302
|
+
return { provider: read.file.provider, model: read.file.model };
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return null;
|
|
306
|
+
}
|
|
307
|
+
if (layout === "legacy") {
|
|
308
|
+
const read = await this.readFileAt(this.indexPath);
|
|
309
|
+
if (read.outcome === "ok") {
|
|
310
|
+
return { provider: read.file.provider, model: read.file.model };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Load every readable shard into `merged`. Returns the generation identity.
|
|
317
|
+
* Unreadable shards record durable diagnostics; `strict` additionally
|
|
318
|
+
* fails the mutation closed.
|
|
319
|
+
*/
|
|
320
|
+
async readShardGenerationInto(merged) {
|
|
321
|
+
let identity = null;
|
|
322
|
+
for (const name of await this.readShardNames()) {
|
|
323
|
+
const read = await this.readFileAt(path.join(this.shardDir, name));
|
|
324
|
+
if (read.outcome !== "ok") {
|
|
325
|
+
if (read.outcome === "unreadable") {
|
|
326
|
+
await this.recordIndexStatus({
|
|
327
|
+
lastReadRecovery: { ts: (/* @__PURE__ */ new Date()).toISOString(), message: `${name}: ${read.reason}` }
|
|
328
|
+
});
|
|
329
|
+
throw new EmbeddingIndexStorageError(
|
|
330
|
+
`refusing to continue from unreadable embedding index shard ${name}: ${read.reason}`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
await this.recordIndexStatus({
|
|
334
|
+
lastReadRecovery: { ts: (/* @__PURE__ */ new Date()).toISOString(), message: `${name}: unrecognized index format` }
|
|
335
|
+
});
|
|
336
|
+
throw new EmbeddingIndexStorageError(
|
|
337
|
+
`refusing to continue from malformed embedding index shard ${name} (unrecognized format); file preserved in place`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
validateShardMembership(name, read.file);
|
|
342
|
+
} catch (error) {
|
|
343
|
+
await this.recordIndexStatus({
|
|
344
|
+
lastReadRecovery: { ts: (/* @__PURE__ */ new Date()).toISOString(), message: String(error) }
|
|
345
|
+
});
|
|
346
|
+
throw error;
|
|
347
|
+
}
|
|
348
|
+
const shardIdentity = { provider: read.file.provider, model: read.file.model };
|
|
349
|
+
if (!identity) identity = shardIdentity;
|
|
350
|
+
if (!sameIndexIdentity(shardIdentity, identity)) {
|
|
351
|
+
throw new EmbeddingIndexStorageError(
|
|
352
|
+
`refusing to continue from mixed-identity embedding index generation: ${name} is ${shardIdentity.provider}/${shardIdentity.model} but the generation identity is ${identity.provider}/${identity.model}; files preserved in place`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
Object.assign(merged, read.file.entries);
|
|
356
|
+
}
|
|
357
|
+
return identity;
|
|
358
|
+
}
|
|
359
|
+
async readLegacy() {
|
|
360
|
+
return this.readFileAt(this.indexPath);
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Read one managed index file (legacy or shard). The file is never
|
|
364
|
+
* modified or moved here: an unreadable file stays in place for recovery
|
|
365
|
+
* and is reported through the returned outcome (mutation paths fail
|
|
366
|
+
* closed on it; read-only paths fail open and record the reason).
|
|
367
|
+
*/
|
|
368
|
+
/**
|
|
369
|
+
* Read/mutation boundary for any generation member or the legacy marker
|
|
370
|
+
* (codex PRRT_kwDORJXyws6mZ72r): a symlinked member is rejected outright
|
|
371
|
+
* and its real target must stay inside the canonical state directory —
|
|
372
|
+
* following a planted link would read or write outside the memory root.
|
|
373
|
+
* Runs BEFORE the secure IO so storage-backed reads are covered too.
|
|
374
|
+
*/
|
|
375
|
+
async assertGenerationFileNotSymlinked(filePath) {
|
|
376
|
+
const info = await lstat(filePath).catch((err) => {
|
|
377
|
+
if (err.code === "ENOENT") return null;
|
|
378
|
+
throw new EmbeddingIndexStorageError(
|
|
379
|
+
`cannot lstat embedding index file ${filePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
380
|
+
);
|
|
381
|
+
});
|
|
382
|
+
const stateDir = path.dirname(this.shardDir);
|
|
383
|
+
const real = await realpath(filePath).catch(() => null);
|
|
384
|
+
if (real) {
|
|
385
|
+
const scope = await realpath(stateDir).catch(() => stateDir);
|
|
386
|
+
const rel = path.relative(scope, real);
|
|
387
|
+
if (rel.startsWith("..") || path.isAbsolute(rel)) {
|
|
388
|
+
throw new EmbeddingIndexStorageError(
|
|
389
|
+
`embedding index file escapes the state directory via symlink; refusing: ${filePath} -> ${real}`
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (!info || !info.isSymbolicLink()) return;
|
|
394
|
+
throw new EmbeddingIndexStorageError(
|
|
395
|
+
`embedding index file is a symlink; refusing to read or write through it: ${filePath}`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
async readFileAt(filePath) {
|
|
399
|
+
await this.assertGenerationFileNotSymlinked(filePath);
|
|
400
|
+
const hardLimit = resolveIndexHardReadCharLimit();
|
|
401
|
+
let size = 0;
|
|
402
|
+
try {
|
|
403
|
+
size = (await stat(filePath)).size;
|
|
404
|
+
} catch (err) {
|
|
405
|
+
if (err.code === "ENOENT") return { outcome: "absent" };
|
|
406
|
+
return { outcome: "unreadable", reason: `stat failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
407
|
+
}
|
|
408
|
+
if (size > hardLimit) {
|
|
409
|
+
return {
|
|
410
|
+
outcome: "unreadable",
|
|
411
|
+
reason: `file is ${size} bytes, over the ${hardLimit}-char hard single-string read ceiling (V8 RangeError territory); bytes preserved in place \u2014 move or prune the file to recover`
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
let raw;
|
|
415
|
+
try {
|
|
416
|
+
raw = this.io ? await this.io.readUtf8(filePath) : await readFile(filePath, "utf-8");
|
|
417
|
+
} catch (err) {
|
|
418
|
+
return { outcome: "unreadable", reason: `read failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
419
|
+
}
|
|
420
|
+
return parseEmbeddingIndexDocument(raw);
|
|
421
|
+
}
|
|
422
|
+
async readShardNames() {
|
|
423
|
+
try {
|
|
424
|
+
return (await readdir(this.shardDir)).filter((name) => SHARD_FILE_PATTERN.test(name)).sort();
|
|
425
|
+
} catch (err) {
|
|
426
|
+
if (err.code === "ENOENT") return [];
|
|
427
|
+
throw new EmbeddingIndexStorageError(
|
|
428
|
+
`cannot enumerate embedding index shard directory ${this.shardDir}: ${err instanceof Error ? `${err.message} (${err.code ?? "unknown"})` : String(err)}`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Persist the index. Layout decisions, in order:
|
|
434
|
+
* - published generation: rewrite only the shards this mutation touched;
|
|
435
|
+
* - legacy/empty generation and the whole index fits one bounded JSON
|
|
436
|
+
* string: stay the single `state/embeddings.json` file;
|
|
437
|
+
* - over budget: ONE-WAY migration — stage a complete shard set, publish
|
|
438
|
+
* it with a single directory rename, then rename the legacy file aside
|
|
439
|
+
* as a non-authoritative recovery artifact (issue #3146).
|
|
440
|
+
*/
|
|
441
|
+
async persist(index, opts = {}) {
|
|
442
|
+
const stateDir = path.dirname(this.indexPath);
|
|
443
|
+
await mkdir(stateDir, { recursive: true });
|
|
444
|
+
await this.cleanForeignStagingDirs();
|
|
445
|
+
const groups = shardEntriesForIndex(index);
|
|
446
|
+
const layout = await this.detectLayout();
|
|
447
|
+
if (layout === "sharded") {
|
|
448
|
+
const limit2 = resolveIndexFileCharLimit();
|
|
449
|
+
const diskIdentity = await this.identityFromDisk();
|
|
450
|
+
if (groups.size === 0) {
|
|
451
|
+
groups.set(0, {});
|
|
452
|
+
await this.publishReplacementGeneration(index, groups, limit2, opts.fence);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const identityChanged = diskIdentity !== null && (diskIdentity.provider !== index.provider || diskIdentity.model !== index.model);
|
|
456
|
+
if (identityChanged) {
|
|
457
|
+
await this.publishReplacementGeneration(index, groups, limit2, opts.fence);
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const dirtyShards = opts.touchedIds?.length ? new Set(opts.touchedIds.map((id) => shardIndexOf(id, SHARD_COUNT))) : null;
|
|
461
|
+
const targets = dirtyShards ? [...dirtyShards] : [...groups.keys()];
|
|
462
|
+
const payloads = targets.filter((shardIndex) => groups.has(shardIndex)).map((shardIndex) => ({
|
|
463
|
+
shardIndex,
|
|
464
|
+
body: serializeEmbeddingShard(index, shardIndex, groups.get(shardIndex), limit2)
|
|
465
|
+
}));
|
|
466
|
+
await opts.fence?.();
|
|
467
|
+
for (const payload of payloads) {
|
|
468
|
+
await this.writeAtomicFile(
|
|
469
|
+
path.join(this.shardDir, shardFileName(payload.shardIndex)),
|
|
470
|
+
payload.body
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
for (const shardIndex of targets) {
|
|
474
|
+
if (!groups.has(shardIndex)) {
|
|
475
|
+
await rm(path.join(this.shardDir, shardFileName(shardIndex)), { force: true });
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
let whole;
|
|
481
|
+
try {
|
|
482
|
+
whole = JSON.stringify(index);
|
|
483
|
+
} catch (err) {
|
|
484
|
+
if (!isInvalidStringLengthError(err)) throw err;
|
|
485
|
+
whole = null;
|
|
486
|
+
}
|
|
487
|
+
const limit = resolveIndexFileCharLimit();
|
|
488
|
+
if (whole !== null && whole.length <= limit) {
|
|
489
|
+
await opts.fence?.();
|
|
490
|
+
await this.writeAtomicFile(this.indexPath, whole);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
const stagingDir = path.join(
|
|
494
|
+
stateDir,
|
|
495
|
+
`embeddings.staging.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
496
|
+
);
|
|
497
|
+
await mkdir(stagingDir, { recursive: true });
|
|
498
|
+
try {
|
|
499
|
+
await this.stageShardSet(stagingDir, index, groups, limit);
|
|
500
|
+
await this.publishSwappedGeneration(stagingDir, opts.fence);
|
|
501
|
+
} catch (err) {
|
|
502
|
+
await rm(stagingDir, { recursive: true, force: true }).catch(() => void 0);
|
|
503
|
+
throw err;
|
|
504
|
+
}
|
|
505
|
+
await rename(
|
|
506
|
+
this.indexPath,
|
|
507
|
+
`${this.indexPath}.pre-migration.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
508
|
+
).catch(() => void 0);
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Replace a published generation wholesale after a provider/model identity
|
|
512
|
+
* change (replacement semantics — the former entries are obsolete by
|
|
513
|
+
* definition). Crash-safe by the same construction as the migration: the
|
|
514
|
+
* COMPLETE new-identity set is staged and validated first (old bytes are
|
|
515
|
+
* not touched until staging succeeds), then published atomically through
|
|
516
|
+
* `publishSwappedGeneration`.
|
|
517
|
+
*/
|
|
518
|
+
async publishReplacementGeneration(index, groups, limit, fence) {
|
|
519
|
+
const stateDir = path.dirname(this.indexPath);
|
|
520
|
+
const stagingDir = path.join(
|
|
521
|
+
stateDir,
|
|
522
|
+
`embeddings.staging.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
|
|
523
|
+
);
|
|
524
|
+
await mkdir(stagingDir, { recursive: true });
|
|
525
|
+
try {
|
|
526
|
+
await this.stageShardSet(stagingDir, index, groups, limit);
|
|
527
|
+
await this.publishSwappedGeneration(stagingDir, fence);
|
|
528
|
+
} catch (err) {
|
|
529
|
+
await rm(stagingDir, { recursive: true, force: true }).catch(() => void 0);
|
|
530
|
+
throw err;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Write a complete shard set into a staging directory. Every shard is
|
|
535
|
+
* serialized before the first write, so a capacity failure cannot leave a
|
|
536
|
+
* partial stage behind.
|
|
537
|
+
*/
|
|
538
|
+
async stageShardSet(stagingDir, index, groups, limit) {
|
|
539
|
+
const payloads = [...groups].map(([shardIndex, entries]) => ({
|
|
540
|
+
shardIndex,
|
|
541
|
+
body: serializeEmbeddingShard(index, shardIndex, entries, limit)
|
|
542
|
+
}));
|
|
543
|
+
for (const payload of payloads) {
|
|
544
|
+
const finalAadFilePath = path.join(this.shardDir, shardFileName(payload.shardIndex));
|
|
545
|
+
await this.writeAtomicFile(
|
|
546
|
+
path.join(stagingDir, shardFileName(payload.shardIndex)),
|
|
547
|
+
payload.body,
|
|
548
|
+
{ finalAadFilePath }
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Atomically remove the published generation (offline-sync: the remote
|
|
554
|
+
* deleted the whole index) by publishing an EMPTY staged directory
|
|
555
|
+
* through the shared swap: the rename is atomic, so the former generation
|
|
556
|
+
* moves whole into the fixed backup and back — a failure before the
|
|
557
|
+
* commit rolls the former generation back intact, and a post-commit
|
|
558
|
+
* backup-cleanup failure is non-fatal (the empty published directory
|
|
559
|
+
* stays the authoritative one-way layout marker). The fixed backup is
|
|
560
|
+
* symlink-checked first (round 5).
|
|
561
|
+
*/
|
|
562
|
+
async removePublishedGeneration(fence) {
|
|
563
|
+
const backupPath = replacementBackupPath(this.shardDir);
|
|
564
|
+
await this.assertBackupNotSymlink(backupPath);
|
|
565
|
+
await rm(backupPath, { recursive: true, force: true });
|
|
566
|
+
await mkdir(path.dirname(this.shardDir), { recursive: true });
|
|
567
|
+
const emptyStaging = await mkdtemp(
|
|
568
|
+
path.join(path.dirname(this.shardDir), "embeddings.staging.tmp-empty-")
|
|
569
|
+
);
|
|
570
|
+
try {
|
|
571
|
+
await this.publishSwappedGeneration(emptyStaging, fence);
|
|
572
|
+
} catch (err) {
|
|
573
|
+
await rm(emptyStaging, { recursive: true, force: true }).catch(() => void 0);
|
|
574
|
+
throw err;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Atomically publish a fully staged generation directory as THE published
|
|
579
|
+
* generation. The former published directory is demoted to the FIXED
|
|
580
|
+
* transaction backup and the staging dir is renamed in with a single
|
|
581
|
+
* rename; if the publish rename fails the former generation is rolled
|
|
582
|
+
* back, and if the process dies in the rename gap `detectLayout()` /
|
|
583
|
+
* `recoverIfInterrupted()` perform the same rollback on next use.
|
|
584
|
+
* Shared by the mutation-side migration/replacement and the offline-sync
|
|
585
|
+
* generation transaction (issue #3148, round 4) so there is exactly one
|
|
586
|
+
* swap state machine. Staging-dir cleanup stays with the caller.
|
|
587
|
+
*/
|
|
588
|
+
async publishSwappedGeneration(stagingDir, fence) {
|
|
589
|
+
const backupPath = replacementBackupPath(this.shardDir);
|
|
590
|
+
await this.assertBackupNotSymlink(backupPath);
|
|
591
|
+
await fence?.();
|
|
592
|
+
await rm(backupPath, { recursive: true, force: true });
|
|
593
|
+
await fence?.();
|
|
594
|
+
let demoted = false;
|
|
595
|
+
try {
|
|
596
|
+
await stat(this.shardDir);
|
|
597
|
+
await rename(this.shardDir, backupPath);
|
|
598
|
+
demoted = true;
|
|
599
|
+
} catch (err) {
|
|
600
|
+
if (err.code !== "ENOENT") throw err;
|
|
601
|
+
}
|
|
602
|
+
try {
|
|
603
|
+
await rename(stagingDir, this.shardDir);
|
|
604
|
+
} catch (err) {
|
|
605
|
+
if (demoted) await rename(backupPath, this.shardDir).catch(() => void 0);
|
|
606
|
+
throw err;
|
|
607
|
+
}
|
|
608
|
+
await rm(backupPath, { recursive: true, force: true }).catch((err) => {
|
|
609
|
+
log.warn(`embedding index: could not remove replacement backup ${backupPath}: ${err}`);
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
/** Remove staging dirs orphaned by crashed migrations. A foreign dir younger than the grace window may belong to a live writer mid-migration, so only older dirs are removed (issue #3148 review, round 3). */
|
|
613
|
+
static STAGING_CLEANUP_GRACE_MS = 10 * 60 * 1e3;
|
|
614
|
+
async cleanForeignStagingDirs() {
|
|
615
|
+
const stateDir = path.dirname(this.indexPath);
|
|
616
|
+
let names = [];
|
|
617
|
+
try {
|
|
618
|
+
names = await readdir(stateDir);
|
|
619
|
+
} catch {
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const ownPrefix = `embeddings.staging.tmp-${process.pid}-`;
|
|
623
|
+
const cutoff = Date.now() - _EmbeddingIndexFileStore.STAGING_CLEANUP_GRACE_MS;
|
|
624
|
+
for (const name of names) {
|
|
625
|
+
if (!name.startsWith("embeddings.staging.tmp-") || name.startsWith(ownPrefix)) continue;
|
|
626
|
+
try {
|
|
627
|
+
const stats = await stat(path.join(stateDir, name));
|
|
628
|
+
if (stats.mtimeMs > cutoff) continue;
|
|
629
|
+
} catch {
|
|
630
|
+
continue;
|
|
631
|
+
}
|
|
632
|
+
await rm(path.join(stateDir, name), { recursive: true, force: true }).catch(() => void 0);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Temp-file + rename write. The temp name carries `.tmp-` so offline sync
|
|
637
|
+
* ignores in-flight files under `state/`. With a wired io, the write goes
|
|
638
|
+
* through the canonical secure-store contract instead, optionally binding
|
|
639
|
+
* the ciphertext to the FINAL published path (staged shards).
|
|
640
|
+
*/
|
|
641
|
+
async writeAtomicFile(filePath, contents, opts) {
|
|
642
|
+
if (this.io && opts?.bypassSecureIo !== true) {
|
|
643
|
+
await this.io.writeUtf8(filePath, contents, opts?.finalAadFilePath === void 0 ? void 0 : { finalAadFilePath: opts.finalAadFilePath });
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
647
|
+
try {
|
|
648
|
+
await writeFile(tempPath, contents, "utf-8");
|
|
649
|
+
await rename(tempPath, filePath);
|
|
650
|
+
} catch (err) {
|
|
651
|
+
await rm(tempPath, { force: true }).catch(() => void 0);
|
|
652
|
+
throw err;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Record the outcome of one index mutation durably. This is diagnostics,
|
|
657
|
+
* NOT recovery: write failures are recorded and then re-thrown by the
|
|
658
|
+
* caller so the extraction/consolidation task fails explicitly instead of
|
|
659
|
+
* declaring success over an index that could not be persisted (issue
|
|
660
|
+
* #3146).
|
|
661
|
+
*/
|
|
662
|
+
async recordIndexWriteOutcome(err, memoryId) {
|
|
663
|
+
if (!err) {
|
|
664
|
+
await this.recordIndexStatus({
|
|
665
|
+
lastWriteFailure: null,
|
|
666
|
+
lastSuccessAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
667
|
+
});
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
const kind = err instanceof EmbeddingIndexCapacityError || isInvalidStringLengthError(err) ? "capacity" : "io";
|
|
671
|
+
await this.recordIndexStatus({
|
|
672
|
+
failureCountIncrement: 1,
|
|
673
|
+
lastWriteFailure: {
|
|
674
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
675
|
+
kind,
|
|
676
|
+
message: err instanceof Error ? err.message : String(err),
|
|
677
|
+
...memoryId ? { memoryId } : {}
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Merge diagnostic state into `state/embedding-fallback-status.json`.
|
|
683
|
+
* Best-effort: a diagnostics failure never masks the real outcome.
|
|
684
|
+
*/
|
|
685
|
+
async recordIndexStatus(patch) {
|
|
686
|
+
try {
|
|
687
|
+
let current = { version: 1, failureCount: 0 };
|
|
688
|
+
try {
|
|
689
|
+
const parsed = JSON.parse(await readFile(this.statusPath, "utf-8"));
|
|
690
|
+
if (parsed && typeof parsed === "object" && typeof parsed.failureCount === "number") {
|
|
691
|
+
current = { ...parsed, version: 1, failureCount: parsed.failureCount };
|
|
692
|
+
}
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
const next = { ...current };
|
|
696
|
+
if (patch.failureCountIncrement) next.failureCount += patch.failureCountIncrement;
|
|
697
|
+
if (patch.lastWriteFailure !== void 0) {
|
|
698
|
+
if (patch.lastWriteFailure === null) delete next.lastWriteFailure;
|
|
699
|
+
else next.lastWriteFailure = patch.lastWriteFailure;
|
|
700
|
+
}
|
|
701
|
+
if (patch.lastReadRecovery !== void 0) next.lastReadRecovery = patch.lastReadRecovery;
|
|
702
|
+
if (patch.lastSuccessAt !== void 0) next.lastSuccessAt = patch.lastSuccessAt;
|
|
703
|
+
await mkdir(path.dirname(this.statusPath), { recursive: true });
|
|
704
|
+
await this.writeAtomicFile(this.statusPath, JSON.stringify(next), { bypassSecureIo: true });
|
|
705
|
+
} catch (err) {
|
|
706
|
+
log.debug(`embedding fallback status write failed: ${err}`);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
function sameIndexIdentity(left, right) {
|
|
711
|
+
return left.provider === right.provider && left.model === right.model;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
export {
|
|
715
|
+
EmbeddingIndexCapacityError,
|
|
716
|
+
EmbeddingIndexStorageError,
|
|
717
|
+
resolveIndexFileCharLimit,
|
|
718
|
+
shardIndexOf,
|
|
719
|
+
validateShardMembership,
|
|
720
|
+
isInvalidStringLengthError,
|
|
721
|
+
parseEmbeddingIndexDocument,
|
|
722
|
+
shardEntriesForIndex,
|
|
723
|
+
serializeEmbeddingShard,
|
|
724
|
+
EmbeddingIndexFileStore
|
|
725
|
+
};
|
|
726
|
+
//# sourceMappingURL=chunk-UCPGOUDZ.js.map
|