@remnic/core 10.0.47 → 10.0.49
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 +63 -61
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.js +64 -62
- package/dist/access-boundary.js +63 -61
- package/dist/access-cli.js +144 -142
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +63 -61
- package/dist/access-http-briefing.js +64 -62
- package/dist/access-http-query.js +65 -63
- package/dist/access-http.js +85 -83
- package/dist/access-identity-continuity-surface.js +63 -61
- package/dist/access-lcm-surface.js +63 -61
- package/dist/access-mcp.js +74 -72
- package/dist/access-namespace-preflight.js +63 -61
- package/dist/access-observe-write-category.js +11 -10
- package/dist/access-observe-write-surface.js +63 -61
- package/dist/access-offline-manifest.js +8 -7
- package/dist/access-operations-batch.js +66 -64
- package/dist/access-operations.js +73 -71
- package/dist/access-recall-concurrency.js +63 -61
- package/dist/access-recall-response.js +63 -61
- package/dist/access-recall-surface.js +63 -61
- package/dist/access-schema.js +10 -9
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +63 -61
- package/dist/access-surface-catalog.js +65 -63
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +23 -21
- 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 +13 -12
- package/dist/buffer.js +5 -5
- package/dist/calibration.js +3 -3
- package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/capture-write-lock-scope.d.ts +7 -0
- package/dist/capture-write-lock-scope.d.ts.map +1 -0
- package/dist/capture-write-lock-scope.js +10 -0
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +16 -15
- 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-J6B6FK2X.js → chunk-2BY56HE6.js} +43 -43
- package/dist/{chunk-ME5LVISI.js → chunk-2J6BIOVN.js} +2 -2
- package/dist/{chunk-EIYJK7OD.js → chunk-2M36PR7R.js} +2 -2
- package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-WOPZ2P5Z.js → chunk-3INLTKTW.js} +2 -2
- package/dist/{chunk-6P6MZDBL.js → chunk-3NL4ALUP.js} +2 -2
- package/dist/{chunk-YUE7J2HZ.js → chunk-3NYIXVCT.js} +2 -2
- package/dist/{chunk-B44NFHWI.js → chunk-3PVKV3XL.js} +2 -2
- package/dist/{chunk-H7BZBSP6.js → chunk-52II7PB6.js} +4 -4
- package/dist/{chunk-5KGYR5VN.js → chunk-5DVOBBTV.js} +2 -2
- package/dist/{chunk-WCGJK4NQ.js → chunk-5FNZ3G6X.js} +10 -10
- package/dist/{chunk-IJH6METJ.js → chunk-5IDQVE4U.js} +5 -5
- package/dist/{chunk-OXSTKCYK.js → chunk-6AEKSOWV.js} +33 -5
- package/dist/chunk-6AEKSOWV.js.map +1 -0
- package/dist/{chunk-2DPJFJFH.js → chunk-6TIVVOVU.js} +10 -10
- package/dist/{chunk-L46MG2GE.js → chunk-6W3QH3QW.js} +17 -14
- package/dist/chunk-6W3QH3QW.js.map +1 -0
- package/dist/{chunk-BUKK5SWA.js → chunk-7LCGFUMB.js} +4 -4
- package/dist/chunk-7VLBZKNB.js +26 -0
- package/dist/chunk-7VLBZKNB.js.map +1 -0
- package/dist/{chunk-CHSRA76M.js → chunk-7XFQSQYS.js} +2 -2
- package/dist/{chunk-A45QXZMU.js → chunk-A3WXV7YU.js} +2 -2
- package/dist/{chunk-LXN737FX.js → chunk-AAANTXCQ.js} +2 -2
- package/dist/{chunk-JNLYHJG7.js → chunk-AF4UIHON.js} +9 -9
- package/dist/{chunk-JGMJZBFV.js → chunk-AGCEJDG2.js} +2 -2
- package/dist/{chunk-I3IPXGFQ.js → chunk-AJJPXUE6.js} +4 -4
- package/dist/{chunk-KAUCVTAW.js → chunk-BJTFLP3J.js} +2 -2
- package/dist/{chunk-QR6W3FSW.js → chunk-BMG3SAXA.js} +3 -3
- package/dist/{chunk-QE2XQM3S.js → chunk-BWF3KNSQ.js} +2 -2
- package/dist/{chunk-BCDECPVO.js → chunk-C3HRYVGD.js} +2 -2
- package/dist/{chunk-RQLTCHUL.js → chunk-C7WDWD35.js} +10 -10
- package/dist/{chunk-YL564QS2.js → chunk-CMOYD5CA.js} +3 -3
- package/dist/{chunk-4J7BNL7B.js → chunk-CPBYKOTC.js} +2 -2
- package/dist/{chunk-PPDNG7WC.js → chunk-CRJX4OMN.js} +2 -2
- package/dist/{chunk-YFJHFEET.js → chunk-D76AHITY.js} +2 -2
- package/dist/{chunk-ZX5N5D6M.js → chunk-DAOMWGO2.js} +4 -4
- package/dist/{chunk-2RQR4TXV.js → chunk-DM6SOROF.js} +2 -2
- package/dist/{chunk-VWJ56T63.js → chunk-E3DL3CAL.js} +2 -2
- package/dist/{chunk-M7LA4QBY.js → chunk-E47UQBYW.js} +3 -3
- package/dist/{chunk-23ZMQONL.js → chunk-ERMT5WQM.js} +2 -2
- package/dist/{chunk-FAKRTS3Y.js → chunk-ESDOQEJM.js} +2 -2
- package/dist/{chunk-VTCNVGM6.js → chunk-FOKTW6GY.js} +2 -2
- package/dist/{chunk-TF5NZGPS.js → chunk-FYZXZUGU.js} +2 -2
- package/dist/{chunk-XTN54NML.js → chunk-G3T6YMBO.js} +4 -4
- package/dist/{chunk-2JNBPOZ2.js → chunk-GDYLZ6FR.js} +3 -3
- package/dist/{chunk-DZPTR2XQ.js → chunk-GFAEKSFR.js} +2 -2
- package/dist/{chunk-GLZWOIU3.js → chunk-HCPVL6SF.js} +3 -3
- package/dist/{chunk-NDMPRT3Z.js → chunk-HDT3DMUF.js} +2 -2
- package/dist/{chunk-BWRX322G.js → chunk-HI53EK3Y.js} +2 -2
- package/dist/{chunk-BFPF5N3H.js → chunk-HXFIILGF.js} +4 -4
- package/dist/chunk-JNA7LFMQ.js +41 -0
- package/dist/chunk-JNA7LFMQ.js.map +1 -0
- package/dist/{chunk-2V664H6O.js → chunk-JS44PBCM.js} +5 -5
- package/dist/{chunk-XKTHJHSQ.js → chunk-JZCBMKR5.js} +3 -3
- package/dist/{chunk-KRACAE73.js → chunk-KMTMMBEB.js} +6 -6
- package/dist/{chunk-FJ264QBP.js → chunk-KNCR7APE.js} +5 -5
- package/dist/{chunk-WUBGMV3G.js → chunk-KRAMCBIW.js} +9 -9
- package/dist/{chunk-DRXTMDAG.js → chunk-LCHFINL4.js} +2 -2
- package/dist/{chunk-PJBYVJKO.js → chunk-LXXIV7Y2.js} +2 -2
- package/dist/{chunk-6CC2YCKG.js → chunk-MCS4LIXP.js} +2 -2
- package/dist/{chunk-2NX7JTST.js → chunk-MPWOAIIH.js} +2 -2
- package/dist/{chunk-CMNL3QSL.js → chunk-N5PISU26.js} +2 -2
- package/dist/{chunk-PKPOO6X5.js → chunk-N6EM7ESC.js} +16 -16
- package/dist/{chunk-FWV2SXSL.js → chunk-NH37SP6V.js} +64 -64
- package/dist/{chunk-MZCTS3ZG.js → chunk-NMOEOQV4.js} +2 -2
- package/dist/{chunk-VECRSYQG.js → chunk-NRGQ5HJO.js} +1 -1
- package/dist/{chunk-3DKKZVXS.js → chunk-NUK5XU3P.js} +5 -5
- package/dist/{chunk-YBKQAVPZ.js → chunk-O4CA53UY.js} +77 -63
- package/dist/chunk-O4CA53UY.js.map +1 -0
- package/dist/{chunk-FPANDKMQ.js → chunk-O7GNSHFF.js} +2 -2
- package/dist/{chunk-5R2IF2KI.js → chunk-OD7ZXCUF.js} +4 -4
- package/dist/{chunk-RT2GN6KQ.js → chunk-PBWJVRKE.js} +2 -2
- package/dist/{chunk-EX27VKU6.js → chunk-PDTNQLPC.js} +2 -2
- package/dist/{chunk-LJCHDFLP.js → chunk-Q6PYCPHO.js} +2 -2
- package/dist/{chunk-XTVH2NAW.js → chunk-QFTNWDJ6.js} +3 -3
- package/dist/{chunk-6CWAY4R7.js → chunk-QZK6HG4J.js} +2 -2
- package/dist/{chunk-Q6UZREJK.js → chunk-RKRX4C4H.js} +2 -2
- package/dist/{chunk-UXAF4RQM.js → chunk-RZDDNH63.js} +2 -2
- package/dist/{chunk-CCTLRRV6.js → chunk-SA6P2KS3.js} +24 -19
- package/dist/chunk-SA6P2KS3.js.map +1 -0
- package/dist/{chunk-QFQ4UG3I.js → chunk-SVJ5JVZO.js} +4 -4
- package/dist/{chunk-CTHYR4QK.js → chunk-SW3WHVKN.js} +2 -2
- package/dist/{chunk-Y57GJDG5.js → chunk-SWRRZNHM.js} +14 -17
- package/dist/chunk-SWRRZNHM.js.map +1 -0
- package/dist/{chunk-B4XSXFKI.js → chunk-V4GPFCJ7.js} +2 -2
- package/dist/{chunk-7AIPWNM7.js → chunk-VCBS5GRQ.js} +2 -2
- package/dist/{chunk-LU57EEJW.js → chunk-VGRBKEBH.js} +3 -3
- package/dist/{chunk-M7L5A2AT.js → chunk-VKUMKVEU.js} +3 -3
- package/dist/{chunk-3EGL7XZO.js → chunk-WIBWONL4.js} +2 -2
- package/dist/{chunk-BGK3SFCC.js → chunk-WJCAAVZX.js} +2 -2
- package/dist/{chunk-BMOJFGVX.js → chunk-WQFHHJWK.js} +2 -2
- package/dist/{chunk-6HVLHIAB.js → chunk-X52SXKVC.js} +2 -2
- package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-XM747YJF.js → chunk-XFND63OB.js} +2 -2
- package/dist/{chunk-RUAIO5UA.js → chunk-XSJL62LE.js} +2 -2
- package/dist/{chunk-3SL52JKD.js → chunk-XVZA6VXC.js} +4 -4
- package/dist/{chunk-MXPVNDGO.js → chunk-YB3WN6XA.js} +8 -8
- package/dist/{chunk-JCYEDMOY.js → chunk-YY6PWFUC.js} +2 -2
- package/dist/{chunk-6Q56WDHG.js → chunk-ZLCBLDJO.js} +2 -2
- package/dist/cli.js +104 -102
- package/dist/coding/export-okf-codegraph.js +12 -11
- 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 +23 -21
- package/dist/connectors/codex-materialize-runner.js +13 -12
- package/dist/connectors/index.js +13 -12
- package/dist/convergence-refresh.js +15 -14
- package/dist/corpus-watermark.js +15 -14
- package/dist/deep-recall-http-glue.js +11 -10
- package/dist/deep-recall.js +2 -2
- package/dist/embedding-fallback.js +3 -2
- package/dist/embedding-generation-marker.d.ts +16 -0
- package/dist/embedding-generation-marker.d.ts.map +1 -0
- package/dist/embedding-generation-marker.js +19 -0
- package/dist/embedding-generation-marker.js.map +1 -0
- package/dist/embedding-index-storage.d.ts +2 -4
- package/dist/embedding-index-storage.d.ts.map +1 -1
- package/dist/embedding-index-storage.js +2 -1
- package/dist/entity-retrieval.js +14 -13
- package/dist/event-order-recall.js +2 -2
- package/dist/explicit-capture.js +1 -1
- package/dist/external-wiki-access.js +64 -62
- package/dist/extraction-span-materialize.js +2 -2
- package/dist/extraction.js +23 -22
- package/dist/{first-start-migration-A6PHIBUQ.js → first-start-migration-QVD22WWS.js} +4 -4
- package/dist/{graph-edge-decay-3HVLMT6U.js → graph-edge-decay-IU75NV42.js} +6 -6
- package/dist/graph-edge-decay-IU75NV42.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 +13 -12
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +192 -188
- package/dist/lcm/engine.js +4 -4
- package/dist/lcm/index.js +9 -9
- package/dist/local-llm.js +3 -3
- package/dist/location/index.js +15 -14
- package/dist/maintenance/memory-governance.js +13 -12
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +13 -12
- package/dist/maintenance/rebuild-memory-projection-runner.js +15 -14
- package/dist/maintenance/rebuild-memory-projection.js +14 -13
- package/dist/memory-browse-operations.js +64 -62
- package/dist/memory-browse.js +5 -5
- package/dist/namespaces/migrate.js +14 -13
- package/dist/namespaces/search.js +6 -6
- package/dist/namespaces/storage.js +13 -12
- package/dist/offline-sync-changeset-generations.js +5 -4
- package/dist/offline-sync-embedding-generation.d.ts +12 -2
- package/dist/offline-sync-embedding-generation.d.ts.map +1 -1
- package/dist/offline-sync-embedding-generation.js +6 -3
- package/dist/offline-sync-snapshot-stream.js +8 -7
- package/dist/offline-sync.d.ts.map +1 -1
- package/dist/offline-sync.js +9 -8
- package/dist/operator-doctor-corpus.js +16 -15
- package/dist/operator-toolkit.js +35 -33
- package/dist/orchestration/maintenance.js +26 -25
- package/dist/orchestrator.js +146 -144
- package/dist/qmd.js +2 -2
- package/dist/recall-navigation-operations.js +64 -62
- package/dist/recall-navigation.js +63 -61
- package/dist/recall-state-view-admission.js +5 -5
- package/dist/recall-why-service.js +5 -5
- package/dist/resume-bundles.js +24 -22
- package/dist/search/factory.js +5 -5
- package/dist/search/index.js +9 -9
- package/dist/semantic-consolidation.js +14 -13
- package/dist/semantic-rule-promotion.js +13 -12
- package/dist/semantic-rule-verifier.js +13 -12
- package/dist/source-agent-qualifier.js +16 -15
- package/dist/standup/build.js +13 -12
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +12 -11
- package/dist/summarizer.js +4 -4
- package/dist/support-passport/index.js +66 -64
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-okf.js +13 -12
- package/dist/verified-recall.js +13 -12
- package/dist/who-knows-cli.js +14 -13
- package/dist/who-knows.js +13 -12
- package/package.json +2 -2
- package/src/access-service-peer-capture-backoff.test.ts +236 -0
- package/src/access-service.ts +6 -2
- package/src/capture-write-lock-scope.test.ts +32 -0
- package/src/capture-write-lock-scope.ts +30 -0
- package/src/embedding-generation-marker.ts +51 -0
- package/src/embedding-index-storage.ts +13 -29
- package/src/index.ts +1 -1
- package/src/offline-sync-embedding-delete-digest.test.ts +58 -0
- package/src/offline-sync-embedding-generation-marker.test.ts +245 -0
- package/src/offline-sync-embedding-generation.ts +47 -2
- package/src/offline-sync-embedding-lock-loss.test.ts +42 -0
- package/src/offline-sync.ts +4 -1
- package/src/storage.ts +2 -1
- package/dist/chunk-CCTLRRV6.js.map +0 -1
- package/dist/chunk-L46MG2GE.js.map +0 -1
- package/dist/chunk-OXSTKCYK.js.map +0 -1
- package/dist/chunk-Y57GJDG5.js.map +0 -1
- package/dist/chunk-YBKQAVPZ.js.map +0 -1
- /package/dist/{capsule-crypto-GWVG7LGC.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{graph-edge-decay-3HVLMT6U.js.map → capture-write-lock-scope.js.map} +0 -0
- /package/dist/{chunk-J6B6FK2X.js.map → chunk-2BY56HE6.js.map} +0 -0
- /package/dist/{chunk-ME5LVISI.js.map → chunk-2J6BIOVN.js.map} +0 -0
- /package/dist/{chunk-EIYJK7OD.js.map → chunk-2M36PR7R.js.map} +0 -0
- /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-WOPZ2P5Z.js.map → chunk-3INLTKTW.js.map} +0 -0
- /package/dist/{chunk-6P6MZDBL.js.map → chunk-3NL4ALUP.js.map} +0 -0
- /package/dist/{chunk-YUE7J2HZ.js.map → chunk-3NYIXVCT.js.map} +0 -0
- /package/dist/{chunk-B44NFHWI.js.map → chunk-3PVKV3XL.js.map} +0 -0
- /package/dist/{chunk-H7BZBSP6.js.map → chunk-52II7PB6.js.map} +0 -0
- /package/dist/{chunk-5KGYR5VN.js.map → chunk-5DVOBBTV.js.map} +0 -0
- /package/dist/{chunk-WCGJK4NQ.js.map → chunk-5FNZ3G6X.js.map} +0 -0
- /package/dist/{chunk-IJH6METJ.js.map → chunk-5IDQVE4U.js.map} +0 -0
- /package/dist/{chunk-2DPJFJFH.js.map → chunk-6TIVVOVU.js.map} +0 -0
- /package/dist/{chunk-BUKK5SWA.js.map → chunk-7LCGFUMB.js.map} +0 -0
- /package/dist/{chunk-CHSRA76M.js.map → chunk-7XFQSQYS.js.map} +0 -0
- /package/dist/{chunk-A45QXZMU.js.map → chunk-A3WXV7YU.js.map} +0 -0
- /package/dist/{chunk-LXN737FX.js.map → chunk-AAANTXCQ.js.map} +0 -0
- /package/dist/{chunk-JNLYHJG7.js.map → chunk-AF4UIHON.js.map} +0 -0
- /package/dist/{chunk-JGMJZBFV.js.map → chunk-AGCEJDG2.js.map} +0 -0
- /package/dist/{chunk-I3IPXGFQ.js.map → chunk-AJJPXUE6.js.map} +0 -0
- /package/dist/{chunk-KAUCVTAW.js.map → chunk-BJTFLP3J.js.map} +0 -0
- /package/dist/{chunk-QR6W3FSW.js.map → chunk-BMG3SAXA.js.map} +0 -0
- /package/dist/{chunk-QE2XQM3S.js.map → chunk-BWF3KNSQ.js.map} +0 -0
- /package/dist/{chunk-BCDECPVO.js.map → chunk-C3HRYVGD.js.map} +0 -0
- /package/dist/{chunk-RQLTCHUL.js.map → chunk-C7WDWD35.js.map} +0 -0
- /package/dist/{chunk-YL564QS2.js.map → chunk-CMOYD5CA.js.map} +0 -0
- /package/dist/{chunk-4J7BNL7B.js.map → chunk-CPBYKOTC.js.map} +0 -0
- /package/dist/{chunk-PPDNG7WC.js.map → chunk-CRJX4OMN.js.map} +0 -0
- /package/dist/{chunk-YFJHFEET.js.map → chunk-D76AHITY.js.map} +0 -0
- /package/dist/{chunk-ZX5N5D6M.js.map → chunk-DAOMWGO2.js.map} +0 -0
- /package/dist/{chunk-2RQR4TXV.js.map → chunk-DM6SOROF.js.map} +0 -0
- /package/dist/{chunk-VWJ56T63.js.map → chunk-E3DL3CAL.js.map} +0 -0
- /package/dist/{chunk-M7LA4QBY.js.map → chunk-E47UQBYW.js.map} +0 -0
- /package/dist/{chunk-23ZMQONL.js.map → chunk-ERMT5WQM.js.map} +0 -0
- /package/dist/{chunk-FAKRTS3Y.js.map → chunk-ESDOQEJM.js.map} +0 -0
- /package/dist/{chunk-VTCNVGM6.js.map → chunk-FOKTW6GY.js.map} +0 -0
- /package/dist/{chunk-TF5NZGPS.js.map → chunk-FYZXZUGU.js.map} +0 -0
- /package/dist/{chunk-XTN54NML.js.map → chunk-G3T6YMBO.js.map} +0 -0
- /package/dist/{chunk-2JNBPOZ2.js.map → chunk-GDYLZ6FR.js.map} +0 -0
- /package/dist/{chunk-DZPTR2XQ.js.map → chunk-GFAEKSFR.js.map} +0 -0
- /package/dist/{chunk-GLZWOIU3.js.map → chunk-HCPVL6SF.js.map} +0 -0
- /package/dist/{chunk-NDMPRT3Z.js.map → chunk-HDT3DMUF.js.map} +0 -0
- /package/dist/{chunk-BWRX322G.js.map → chunk-HI53EK3Y.js.map} +0 -0
- /package/dist/{chunk-BFPF5N3H.js.map → chunk-HXFIILGF.js.map} +0 -0
- /package/dist/{chunk-2V664H6O.js.map → chunk-JS44PBCM.js.map} +0 -0
- /package/dist/{chunk-XKTHJHSQ.js.map → chunk-JZCBMKR5.js.map} +0 -0
- /package/dist/{chunk-KRACAE73.js.map → chunk-KMTMMBEB.js.map} +0 -0
- /package/dist/{chunk-FJ264QBP.js.map → chunk-KNCR7APE.js.map} +0 -0
- /package/dist/{chunk-WUBGMV3G.js.map → chunk-KRAMCBIW.js.map} +0 -0
- /package/dist/{chunk-DRXTMDAG.js.map → chunk-LCHFINL4.js.map} +0 -0
- /package/dist/{chunk-PJBYVJKO.js.map → chunk-LXXIV7Y2.js.map} +0 -0
- /package/dist/{chunk-6CC2YCKG.js.map → chunk-MCS4LIXP.js.map} +0 -0
- /package/dist/{chunk-2NX7JTST.js.map → chunk-MPWOAIIH.js.map} +0 -0
- /package/dist/{chunk-CMNL3QSL.js.map → chunk-N5PISU26.js.map} +0 -0
- /package/dist/{chunk-PKPOO6X5.js.map → chunk-N6EM7ESC.js.map} +0 -0
- /package/dist/{chunk-FWV2SXSL.js.map → chunk-NH37SP6V.js.map} +0 -0
- /package/dist/{chunk-MZCTS3ZG.js.map → chunk-NMOEOQV4.js.map} +0 -0
- /package/dist/{chunk-VECRSYQG.js.map → chunk-NRGQ5HJO.js.map} +0 -0
- /package/dist/{chunk-3DKKZVXS.js.map → chunk-NUK5XU3P.js.map} +0 -0
- /package/dist/{chunk-FPANDKMQ.js.map → chunk-O7GNSHFF.js.map} +0 -0
- /package/dist/{chunk-5R2IF2KI.js.map → chunk-OD7ZXCUF.js.map} +0 -0
- /package/dist/{chunk-RT2GN6KQ.js.map → chunk-PBWJVRKE.js.map} +0 -0
- /package/dist/{chunk-EX27VKU6.js.map → chunk-PDTNQLPC.js.map} +0 -0
- /package/dist/{chunk-LJCHDFLP.js.map → chunk-Q6PYCPHO.js.map} +0 -0
- /package/dist/{chunk-XTVH2NAW.js.map → chunk-QFTNWDJ6.js.map} +0 -0
- /package/dist/{chunk-6CWAY4R7.js.map → chunk-QZK6HG4J.js.map} +0 -0
- /package/dist/{chunk-Q6UZREJK.js.map → chunk-RKRX4C4H.js.map} +0 -0
- /package/dist/{chunk-UXAF4RQM.js.map → chunk-RZDDNH63.js.map} +0 -0
- /package/dist/{chunk-QFQ4UG3I.js.map → chunk-SVJ5JVZO.js.map} +0 -0
- /package/dist/{chunk-CTHYR4QK.js.map → chunk-SW3WHVKN.js.map} +0 -0
- /package/dist/{chunk-B4XSXFKI.js.map → chunk-V4GPFCJ7.js.map} +0 -0
- /package/dist/{chunk-7AIPWNM7.js.map → chunk-VCBS5GRQ.js.map} +0 -0
- /package/dist/{chunk-LU57EEJW.js.map → chunk-VGRBKEBH.js.map} +0 -0
- /package/dist/{chunk-M7L5A2AT.js.map → chunk-VKUMKVEU.js.map} +0 -0
- /package/dist/{chunk-3EGL7XZO.js.map → chunk-WIBWONL4.js.map} +0 -0
- /package/dist/{chunk-BGK3SFCC.js.map → chunk-WJCAAVZX.js.map} +0 -0
- /package/dist/{chunk-BMOJFGVX.js.map → chunk-WQFHHJWK.js.map} +0 -0
- /package/dist/{chunk-6HVLHIAB.js.map → chunk-X52SXKVC.js.map} +0 -0
- /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-XM747YJF.js.map → chunk-XFND63OB.js.map} +0 -0
- /package/dist/{chunk-RUAIO5UA.js.map → chunk-XSJL62LE.js.map} +0 -0
- /package/dist/{chunk-3SL52JKD.js.map → chunk-XVZA6VXC.js.map} +0 -0
- /package/dist/{chunk-MXPVNDGO.js.map → chunk-YB3WN6XA.js.map} +0 -0
- /package/dist/{chunk-JCYEDMOY.js.map → chunk-YY6PWFUC.js.map} +0 -0
- /package/dist/{chunk-6Q56WDHG.js.map → chunk-ZLCBLDJO.js.map} +0 -0
- /package/dist/{first-start-migration-A6PHIBUQ.js.map → first-start-migration-QVD22WWS.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/offline-sync-embedding-generation.ts"],"sourcesContent":["// Embedding-index generation semantics for offline sync (issue #3146, PR\n// #3148). Extracted from offline-sync.ts so the god-file line-count ratchet\n// does not grow when the rules gain behavior. The snapshot walker treats\n// every `state/embeddings/` shard set — or a legacy `state/embeddings.json`\n// marker — as ONE generation, in both directions:\n//\n// 1. Atomic replacement (apply side): an incoming generation is staged\n// complete, verified byte-for-byte, and published through the store's\n// single backup-swap state machine. A crash or write failure can never\n// leave a mix of the local and incoming generation on disk, and a\n// local-only shard of a replaced generation is removed by the swap, not\n// merged into the incoming set.\n// 2. Filter-to-omit, never filter-to-delete (push side): when an exclusion\n// filter removes ANY member of a local generation from a push, the\n// WHOLE generation is omitted — files and deletion metadata — and the\n// receiver keeps its copy. A filtered generation must never look\n// deleted, and a partially filtered snapshot must never look like a\n// complete generation.\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { lstat, mkdir, readFile, readdir, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n type EmbeddingIndexFile,\n EmbeddingIndexFileStore,\n type EmbeddingIndexIdentity,\n type EmbeddingIndexStoreIo,\n EmbeddingIndexStorageError,\n parseEmbeddingIndexDocument,\n serializeEmbeddingShard,\n shardEntriesForIndex,\n validateShardMembership,\n} from \"./embedding-index-storage.js\";\nimport {\n assertEmbeddingGenerationLockHeld,\n type EmbeddingGenerationFence,\n embeddingGenerationLockPath,\n withEmbeddingGenerationLock,\n} from \"./embedding-generation-lock.js\";\nimport type { OfflineSyncExcludeFile, OfflineSyncFileTarget } from \"./offline-sync-file-io.js\";\nimport { EMBEDDING_SHARD_FILE_PATTERN } from \"./offline-sync-runtime-state.js\";\nimport { log } from \"./logger.js\";\nimport type {\n OfflineSyncFileDeleteTarget,\n OfflineSyncFileStagingReadTarget,\n OfflineSyncFileStagingWriteTarget,\n OfflineSyncFileState,\n} from \"./offline-sync.js\";\nimport { type SafeArchiveRoot, resolveSafeArchiveTarget } from \"./transfer/fs-utils.js\";\n\nconst EMBEDDING_MARKER_BASENAME = \"embeddings.json\";\nexport const EMBEDDING_SHARD_DIR_BASENAME = \"embeddings\";\n\n/**\n * Canonical `state` dir prefix (\"state\" or \"namespaces/<ns>/state\") for a\n * path's segments, or null when the path is not canonical runtime state.\n */\nfunction canonicalStateDirPrefix(parts: string[]): string | null {\n if (parts[0] === \"state\") return \"state\";\n if (parts[0] === \"namespaces\" && parts.length >= 4 && parts[2] === \"state\") {\n return `namespaces/${parts[1]}/state`;\n }\n return null;\n}\n\n/** The sharded-generation directory when `relPath` is one of its shards. */\nexport function embeddingShardDirOf(relPath: string): string | null {\n const parts = relPath.split(\"/\");\n if (parts.length < 3) return null;\n if (!EMBEDDING_SHARD_FILE_PATTERN.test(parts[parts.length - 1] ?? \"\")) return null;\n if (parts[parts.length - 2] !== EMBEDDING_SHARD_DIR_BASENAME) return null;\n if (canonicalStateDirPrefix(parts) === null) return null;\n return parts.slice(0, -1).join(\"/\");\n}\n\n/**\n * True when `dirPath` is a canonical embeddings generation dir\n * (`state/embeddings` or `namespaces/<ns>/state/embeddings`). Encodes the\n * snapshot-field shape contract without a ReDoS-shaped literal (issue #2439).\n */\nexport function isEmbeddingGenerationDirPath(dirPath: string): boolean {\n const membership = embeddingGenerationMembership(`${dirPath}/shard-0000.json`);\n return membership !== null && membership.shardDir === dirPath;\n}\n\n/** The state dir when `relPath` is the legacy single-file marker. */\nexport function embeddingMarkerDirOf(relPath: string): string | null {\n const parts = relPath.split(\"/\");\n if ((parts[parts.length - 1] ?? \"\") !== EMBEDDING_MARKER_BASENAME) return null;\n if (canonicalStateDirPrefix(parts) === null) return null;\n return parts.slice(0, -1).join(\"/\");\n}\n\n/**\n * Membership of `relPath` in an embedding generation: shards belong to their\n * `.../embeddings` directory, the legacy marker belongs to the sibling\n * `.../embeddings` directory of its state dir.\n */\nexport function embeddingGenerationMembership(relPath: string): { kind: \"shard\" | \"marker\"; shardDir: string } | null {\n const shardDir = embeddingShardDirOf(relPath);\n if (shardDir) return { kind: \"shard\", shardDir };\n const markerDir = embeddingMarkerDirOf(relPath);\n if (markerDir) return { kind: \"marker\", shardDir: `${markerDir}/${EMBEDDING_SHARD_DIR_BASENAME}` };\n return null;\n}\n\nexport interface IncomingEmbeddingGenerations {\n /** Generation dirs with at least one incoming shard. */\n shardDirs: Set<string>;\n /** State dirs whose incoming snapshot carries the legacy marker. */\n legacyMarkerDirs: Set<string>;\n}\n\n/** Classify incoming snapshot paths into embedding generations. */\nexport function detectIncomingEmbeddingGenerations(incomingPaths: Iterable<string>): IncomingEmbeddingGenerations {\n const shardDirs = new Set<string>();\n const legacyMarkerDirs = new Set<string>();\n for (const relPath of incomingPaths) {\n const membership = embeddingGenerationMembership(relPath);\n if (!membership) continue;\n if (membership.kind === \"shard\") shardDirs.add(membership.shardDir);\n else legacyMarkerDirs.add(membership.shardDir);\n }\n return { shardDirs, legacyMarkerDirs };\n}\n\nfunction shardRelPath(shardDirRel: string, shardIndex: number): string {\n return `${shardDirRel}/shard-${String(shardIndex).padStart(4, \"0\")}.json`;\n}\n\nfunction describeIndexReadFailure(\n read: { outcome: \"absent\" } | { outcome: \"foreign\" } | { outcome: \"unreadable\"; reason: string }\n): string {\n if (read.outcome === \"unreadable\") return read.reason;\n if (read.outcome === \"foreign\") return \"unrecognized index format\";\n return \"generation document is absent\";\n}\n\nfunction sha256Hex(buffer: Buffer): string {\n return createHash(\"sha256\").update(buffer).digest(\"hex\");\n}\n\nasync function pathExists(absPath: string): Promise<boolean> {\n try {\n await lstat(absPath);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw err;\n }\n}\n\nexport interface EmbeddingGenerationTransactionIo {\n /** Storage-backed local read: hydrates unchanged shards (metadata-only\n * pulls skip their content) and decrypts secure-store files. */\n readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;\n writeStagingFile?: (target: OfflineSyncFileStagingWriteTarget) => Promise<void>;\n readStagingFile?: (target: OfflineSyncFileStagingReadTarget) => Promise<Buffer>;\n deleteFile?: (target: OfflineSyncFileDeleteTarget) => Promise<void>;\n}\n\nexport interface EmbeddingGenerationTransactionInput {\n root: SafeArchiveRoot;\n /** The generation dir, e.g. `state/embeddings`. */\n shardDirRel: string;\n /** Incoming shard rel paths — the complete incoming sharded generation. */\n incomingShardPaths: string[];\n /** Incoming legacy marker (rel path + verified content), when the\n * incoming generation is legacy-layout. Mutually exclusive with\n * `incomingShardPaths`. */\n incomingMarker: { path: string; sha256: string; buffer: Buffer | null } | null;\n /** True when the incoming snapshot ALSO carries the legacy marker while\n * shards form the generation (mixed remote): the per-file loop then writes\n * the marker as the inert remote artifact, so this transaction must NOT\n * remove a local marker. */\n incomingMarkerPresent: boolean;\n /** Incoming shard records (sha256 for local hydration of unchanged\n * metadata-only pulls). */\n incomingShardStates: ReadonlyMap<string, OfflineSyncFileState>;\n /** Verified incoming content by rel path (absent for shards the metadata\n * hydration skipped because the local hash already matches). */\n incomingBuffers: ReadonlyMap<string, Buffer>;\n /**\n * Shared-base states (pre-census): lets the transaction re-evaluate\n * divergence against ACTUAL disk contents while holding the mutation\n * lock, instead of trusting the census taken before the lock\n * (codex P1: a daemon mutation between census and lock was discarded).\n */\n baseStates?: ReadonlyMap<string, { sha256: string }>;\n /**\n * Paths this apply is pre-authorized to remove (changeset deletes / remote\n * tombstones). A base member absent from the under-lock census is\n * divergence UNLESS it is pre-authorized — the retry after a response loss\n * must re-apply idempotently instead of failing as diverged (#3150\n * review). A pre-authorized member still present with a changed digest\n * defers as before, so a daemon rewrite is never silently swept.\n */\n justifiedRemovals?: ReadonlySet<string>;\n /**\n * Deletion revisions (mtimeMs by path) to record through the delete hook\n * AFTER the swap publishes. Swept members bypass the per-file delete path,\n * so without this their tombstones are lost and a later peer without\n * shared-base evidence can reintroduce the removed shards (codex P1).\n */\n deletionMtimeByPath?: ReadonlyMap<string, number>;\n /**\n * True when the incoming set claims to be a FRESH creation but carries no\n * base evidence (manifested changeset members are all base-less upserts):\n * a non-empty local generation must match the incoming set exactly or the\n * apply defers as diverged, because the empty-base divergence skip would\n * otherwise silently replace it (#3150 review).\n */\n freshCreateClaim?: boolean;\n io: EmbeddingGenerationTransactionIo;\n now: number;\n}\n\nexport interface EmbeddingGenerationTransactionResult {\n shardDirRel: string;\n upserted: number;\n deleted: number;\n /** nextBase states for files the transaction published. */\n writtenStates: Map<string, OfflineSyncFileState>;\n /** nextBase paths the transaction removed (stale shards, demoted marker). */\n removedPaths: Set<string>;\n /** Paths the per-file apply loop must skip — all handled here. */\n handledPaths: Set<string>;\n /** True when the generation was deferred (not published) because the disk\n * diverged from the shared base while the lock was held. */\n deferredLocalDivergence?: boolean;\n}\n\n/**\n * Content for ONE incoming generation member: the verified incoming buffer,\n * or the LOCAL file hash-verified against the incoming record (metadata-only\n * pulls skip content whose local hash already matches — the local bytes ARE\n * the incoming content). Shared by the shard and legacy-marker branches.\n */\nasync function hydratedIncomingBuffer(\n io: EmbeddingGenerationTransactionIo,\n root: SafeArchiveRoot,\n relPath: string,\n expectedSha256: string,\n incoming: Buffer | null | undefined,\n): Promise<Buffer> {\n if (incoming) return incoming;\n const filePath = await resolveSafeArchiveTarget(root, relPath);\n const local = io.readFile\n ? await io.readFile({ root: root.abs, path: relPath, filePath })\n : await readFile(filePath);\n if (sha256Hex(local) !== expectedSha256) {\n throw new EmbeddingIndexStorageError(\n `cannot hydrate unchanged embedding index file ${relPath}: local digest does not match the incoming record; local generation preserved`,\n );\n }\n return local;\n}\n\n/**\n * Apply ONE incoming embedding generation atomically (issue #3148, round 4):\n * recover an interrupted replacement, validate the complete incoming\n * generation (legacy markers are converted to the sharded layout so the\n * one-way layout is preserved), stage it, verify the staged bytes, then\n * publish through the shared backup-swap. Any failure before the swap leaves\n * the local generation untouched; a crash in the swap gap is rolled back by\n * the store's recovery on next use.\n */\n/**\n * Enumerate and hash the ACTUAL local generation on disk (legacy marker +\n * every shard) through the configured secure IO — the fresh in-lock census\n * the stale pre-lock apply census cannot be trusted for (codex P1).\n */\n/** Scope the shared vault base to this physical embedding generation. */\nfunction baseStatesForGeneration(\n base: ReadonlyMap<string, { sha256: string }> | undefined,\n shardDirRel: string,\n): Map<string, { sha256: string }> {\n return new Map([...base ?? []].filter(([relPath]) => embeddingGenerationMembership(relPath)?.shardDir === shardDirRel));\n}\n\nasync function censusGenerationState(\n io: EmbeddingGenerationTransactionIo,\n root: SafeArchiveRoot,\n markerRel: string,\n markerAbs: string,\n shardDirAbs: string,\n shardDirRel: string,\n): Promise<Map<string, string>> {\n const census = new Map<string, string>();\n const readSha = async (relPath: string, filePath: string) =>\n sha256Hex(io.readFile ? await io.readFile({ root: root.abs, path: relPath, filePath }) : await readFile(filePath));\n if (await pathExists(markerAbs)) census.set(markerRel, await readSha(markerRel, markerAbs));\n const names = await readdir(shardDirAbs).catch((err: NodeJS.ErrnoException) => {\n if (err.code === \"ENOENT\") return [] as string[];\n throw err;\n });\n for (const name of names.filter((candidate) => EMBEDDING_SHARD_FILE_PATTERN.test(candidate)).sort()) {\n const relPath = `${shardDirRel}/${name}`;\n census.set(relPath, await readSha(relPath, path.join(shardDirAbs, name)));\n }\n return census;\n}\n\n/** Incoming member states for the divergence helper (digests as fallback). */\nfunction incomingGenerationStates(input: EmbeddingGenerationTransactionInput): { path: string; sha256: string }[] {\n const states = input.incomingShardPaths.map((relPath) => ({\n path: relPath,\n sha256: input.incomingShardStates.get(relPath)?.sha256 ?? sha256Hex(input.incomingBuffers.get(relPath) ?? Buffer.alloc(0)),\n }));\n if (input.incomingMarker) {\n states.push({ path: input.incomingMarker.path, sha256: input.incomingMarker.sha256 });\n }\n return states;\n}\n\nexport async function applyEmbeddingGenerationTransaction(\n input: EmbeddingGenerationTransactionInput\n): Promise<EmbeddingGenerationTransactionResult> {\n const { root, shardDirRel, io } = input;\n const stateDirRel = shardDirRel.slice(0, -`/${EMBEDDING_SHARD_DIR_BASENAME}`.length);\n const stateDirAbs = await resolveSafeArchiveTarget(root, stateDirRel);\n return await withEmbeddingGenerationLock(stateDirAbs, async (section) => {\n const lockPath = embeddingGenerationLockPath(stateDirAbs);\n const fence: EmbeddingGenerationFence = () => assertEmbeddingGenerationLockHeld(lockPath, section);\n const markerRel = `${stateDirRel}/${EMBEDDING_MARKER_BASENAME}`;\n const shardDirAbs = await resolveSafeArchiveTarget(root, shardDirRel);\n const markerAbs = await resolveSafeArchiveTarget(root, markerRel);\n const store = new EmbeddingIndexFileStore(\n markerAbs,\n shardDirAbs,\n await resolveSafeArchiveTarget(root, `${stateDirRel}/embedding-fallback-status.json`)\n );\n\n // 1. Close a pending rename gap (old generation restored) before staging:\n // the swap below must never build on a half-replaced generation. The\n // fixed backup path is validated first: a planted symlink (or any\n // escape) must fail closed instead of being renamed into the published\n // position or removed through. The restored target is re-resolved so a\n // symlinked restoration cannot slip past the subsequent IO.\n const backupRel = `${stateDirRel}/embeddings.pre-replace.tmp`;\n await resolveSafeArchiveTarget(root, backupRel);\n if (await store.recoverIfInterrupted(fence)) {\n await resolveSafeArchiveTarget(root, shardDirRel);\n }\n\n // Codex P1: the apply census was taken BEFORE this lock, so it may be\n // stale. Re-enumerate and hash the ACTUAL local generation through the\n // secure IO and defer the WHOLE generation when the disk no longer\n // matches the shared base — a concurrent daemon mutation must never be\n // silently discarded by the swap.\n // No shared base (fresh catch-up) = no divergence semantics to protect,\n // UNLESS the apply claims a fresh creation (manifested base-less\n // upserts): an existing local generation must then match the incoming\n // set exactly or defer, instead of being silently replaced.\n const justifiedRemovals = input.justifiedRemovals ?? new Set<string>();\n const baseStates = baseStatesForGeneration(input.baseStates, shardDirRel);\n if (baseStates.size > 0) {\n const localCensus = await censusGenerationState(io, root, markerRel, markerAbs, shardDirAbs, shardDirRel);\n const baseArr = [...baseStates].map(([relPath, state]) => ({ path: relPath, sha256: state.sha256 }));\n const censusArr = [...localCensus].map(([relPath, sha256]) => ({ path: relPath, sha256 }));\n const deferrals = divergedEmbeddingGenerationDeferrals({\n incomingFiles: incomingGenerationStates(input),\n // A pre-authorized removal keeps its base entry only while still on\n // disk (digest auth): present members are compared normally — a\n // daemon rewrite still defers — while an absent member is already\n // applied and must not diverge the retry (#3150 review).\n baseFiles: baseArr.filter((member) => !justifiedRemovals.has(member.path) || localCensus.has(member.path)),\n currentFiles: censusArr,\n });\n const incomingShardPaths = new Set(input.incomingShardPaths);\n const addedOrRemovedSinceBase =\n censusArr.some((member) =>\n !baseStates.has(member.path) &&\n !justifiedRemovals.has(member.path) &&\n // An incoming member found in the census is the apply's own\n // already-published work (response-loss retry), not a local\n // addition; its bytes were verified above.\n !incomingShardPaths.has(member.path)) ||\n baseArr.some((member) => !localCensus.has(member.path) && !justifiedRemovals.has(member.path));\n if (deferrals.length > 0 || addedOrRemovedSinceBase) {\n const handled = new Set<string>(input.incomingShardPaths);\n if (input.incomingMarker) handled.add(input.incomingMarker.path);\n if (input.incomingMarkerPresent) handled.add(markerRel);\n for (const relPath of localCensus.keys()) handled.add(relPath);\n log.warn(\n `embedding generation ${shardDirRel} deferred: disk diverged from the shared base under the mutation lock (${deferrals.length > 0 ? deferrals.join(\", \") : \"generation membership changed since base\"}); nothing published, nothing removed`,\n );\n return {\n shardDirRel,\n upserted: 0,\n deleted: 0,\n writtenStates: new Map<string, OfflineSyncFileState>(),\n removedPaths: new Set<string>(),\n handledPaths: handled,\n deferredLocalDivergence: true,\n };\n }\n } else if (input.freshCreateClaim) {\n const localCensus = await censusGenerationState(io, root, markerRel, markerAbs, shardDirAbs, shardDirRel);\n if (localCensus.size > 0) {\n const incomingArr = incomingGenerationStates(input).sort((left, right) => left.path.localeCompare(right.path));\n const censusArr = [...localCensus]\n .map(([relPath, sha256]) => ({ path: relPath, sha256 }))\n .sort((left, right) => left.path.localeCompare(right.path));\n const identical =\n censusArr.length === incomingArr.length &&\n censusArr.every((member, index) => member.path === incomingArr[index]?.path && member.sha256 === incomingArr[index]?.sha256);\n if (!identical) {\n const handled = new Set<string>(input.incomingShardPaths);\n if (input.incomingMarker) handled.add(input.incomingMarker.path);\n if (input.incomingMarkerPresent) handled.add(markerRel);\n for (const relPath of localCensus.keys()) handled.add(relPath);\n log.warn(\n `embedding generation ${shardDirRel} deferred: base-less creation does not match the existing local generation under the mutation lock; nothing published, nothing removed`,\n );\n return {\n shardDirRel,\n upserted: 0,\n deleted: 0,\n writtenStates: new Map<string, OfflineSyncFileState>(),\n removedPaths: new Set<string>(),\n handledPaths: handled,\n deferredLocalDivergence: true,\n };\n }\n }\n }\n\n const published = new Map<string, Buffer>();\n let identity: EmbeddingIndexIdentity | null = null;\n // Divergence re-evaluation happens HERE, under the lock, against real\n // disk bytes: the apply-side census may already be stale.\n // 2. Build the complete incoming generation (final rel path → plaintext\n // bytes) and validate every document BEFORE touching local state.\n if (input.incomingMarker) {\n const markerBuffer = await hydratedIncomingBuffer(\n io,\n root,\n input.incomingMarker.path,\n input.incomingMarker.sha256,\n input.incomingMarker.buffer,\n );\n const read = parseEmbeddingIndexDocument(markerBuffer.toString(\"utf-8\"));\n if (read.outcome !== \"ok\") {\n throw new EmbeddingIndexStorageError(\n `refusing incoming embedding index generation ${markerRel}: ${describeIndexReadFailure(read)}; local generation preserved`\n );\n }\n identity = { provider: read.file.provider, model: read.file.model };\n const groups = shardEntriesForIndex(read.file);\n for (const [shardIndex, entries] of groups) {\n if (Object.keys(entries).length === 0) continue;\n published.set(\n shardRelPath(shardDirRel, shardIndex),\n Buffer.from(serializeEmbeddingShard(read.file, shardIndex, entries), \"utf-8\")\n );\n }\n } else {\n for (const relPath of input.incomingShardPaths) {\n // Metadata-only pulls skip content whose local hash already matches\n // the incoming record: the LOCAL bytes ARE the incoming content, so\n // read and hash-verify them instead of failing (codex rounds 5-6).\n const state = input.incomingShardStates.get(relPath);\n const buffer = await hydratedIncomingBuffer(\n io,\n root,\n relPath,\n state?.sha256 ?? \"\",\n input.incomingBuffers.get(relPath),\n );\n const read = parseEmbeddingIndexDocument(buffer.toString(\"utf-8\"));\n if (read.outcome !== \"ok\") {\n throw new EmbeddingIndexStorageError(\n `refusing incoming embedding index shard ${relPath}: ${describeIndexReadFailure(read)}; local generation preserved`\n );\n }\n validateShardMembership(relPath.split(\"/\").pop() ?? \"\", read.file);\n const shardIdentity = { provider: read.file.provider, model: read.file.model };\n if (!identity) identity = shardIdentity;\n if (identity.provider !== shardIdentity.provider || identity.model !== shardIdentity.model) {\n throw new EmbeddingIndexStorageError(\n `refusing mixed-identity incoming embedding index generation in ${shardDirRel}: ${relPath} is ${shardIdentity.provider}/${shardIdentity.model} but the generation is ${identity.provider}/${identity.model}; local generation preserved`\n );\n }\n published.set(relPath, buffer);\n }\n if (published.size === 0) {\n throw new EmbeddingIndexStorageError(\n `incoming embedding index generation ${shardDirRel} is empty; local generation preserved`\n );\n }\n }\n\n const stagingRel = `${stateDirRel}/embeddings.staging.tmp-sync-${process.pid}-${input.now}-${randomBytes(8).toString(\"hex\")}`;\n const stagingAbs = await resolveSafeArchiveTarget(root, stagingRel);\n try {\n await mkdir(stagingAbs, { recursive: true });\n\n // 3. Stage the complete generation. With storage-backed IO the staged\n // ciphertext binds its FINAL canonical path (`aadRelPath`), so after\n // the directory swap it decrypts at the published path — a physical\n // staging-path AAD would make the published generation unreadable.\n for (const [relPath, buffer] of published) {\n const stagedRel = `${stagingRel}/${path.basename(relPath)}`;\n const stagedAbs = await resolveSafeArchiveTarget(root, stagedRel);\n const finalAbs = await resolveSafeArchiveTarget(root, relPath);\n if (io.writeStagingFile) {\n await io.writeStagingFile({\n root: root.abs,\n path: stagedRel,\n filePath: stagedAbs,\n content: buffer,\n finalAadFilePath: finalAbs,\n });\n } else {\n await writeFile(stagedAbs, buffer, { mode: 0o600 });\n }\n }\n\n // 4. Verify the staged bytes against the incoming content before the\n // published generation is touched at all.\n for (const [relPath, buffer] of published) {\n const stagedRel = `${stagingRel}/${path.basename(relPath)}`;\n const stagedAbs = await resolveSafeArchiveTarget(root, stagedRel);\n const staged = io.readStagingFile\n ? await io.readStagingFile({\n root: root.abs,\n path: stagedRel,\n filePath: stagedAbs,\n finalAadFilePath: await resolveSafeArchiveTarget(root, relPath),\n })\n : await readFile(stagedAbs);\n if (!staged.equals(buffer)) {\n throw new EmbeddingIndexStorageError(\n `staged embedding index file ${stagedRel} does not match the incoming content; local generation preserved`\n );\n }\n }\n\n // 5. Publish with the ONE swap state machine shared with persist(). The\n // old directory moves aside wholesale, so local shards absent from\n // the incoming generation are removed by the swap — never merged.\n const localShardRels = (\n await readdir(shardDirAbs).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [] as string[];\n throw err;\n })\n )\n .filter((name) => EMBEDDING_SHARD_FILE_PATTERN.test(name))\n .map((name) => `${shardDirRel}/${name}`)\n .sort();\n await store.publishSwappedGeneration(stagingAbs, fence);\n\n const writtenStates = new Map<string, OfflineSyncFileState>();\n for (const [relPath, buffer] of published) {\n writtenStates.set(relPath, {\n path: relPath,\n sha256: sha256Hex(buffer),\n bytes: buffer.length,\n mtimeMs: input.now,\n });\n }\n const removedPaths = new Set<string>(localShardRels.filter((rel) => !published.has(rel)));\n let deleted = removedPaths.size;\n let markerCleanupFailed = false;\n if ((await pathExists(markerAbs)) && !input.incomingMarker && !input.incomingMarkerPresent) {\n // Publication is already committed. Keep its base advancement even if\n // marker cleanup fails, retaining the marker in base for a retry.\n try {\n if (io.deleteFile) await io.deleteFile({ root: root.abs, path: markerRel, filePath: markerAbs });\n else await rm(markerAbs, { force: true });\n removedPaths.add(markerRel);\n deleted += 1;\n } catch (err) {\n markerCleanupFailed = true;\n log.warn(`embedding generation ${shardDirRel}: marker cleanup deferred after publish: ${err}`);\n }\n }\n // Swept members bypass the per-file delete path, so record their\n // deletion revisions here — mirroring the whole-generation removal\n // path — or a later peer without shared-base evidence can reintroduce\n // the removed shards (codex P1). An empty map still records (with\n // `now`) when the caller declared a deletion context, so tombstones\n // whose changeset deletes omit mtimeMs are not lost. Every member\n // gets an attempt before the failure propagates: the push side then\n // retries a fully-recorded apply or nothing, and the\n // justified-removals exemption keeps that retry idempotent.\n if (input.deletionMtimeByPath !== undefined && io.deleteFile) {\n let firstTombstoneError: unknown = null;\n for (const relPath of removedPaths) {\n try {\n await io.deleteFile({\n root: root.abs,\n path: relPath,\n filePath: await resolveSafeArchiveTarget(root, relPath),\n mtimeMs: input.deletionMtimeByPath.get(relPath) ?? input.now,\n });\n } catch (err) {\n firstTombstoneError ??= err;\n log.warn(`embedding generation ${shardDirRel}: tombstone recording failed for ${relPath}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n if (firstTombstoneError) throw firstTombstoneError;\n }\n return {\n shardDirRel,\n upserted: published.size,\n deleted,\n writtenStates,\n removedPaths,\n handledPaths: new Set([...published.keys(), ...removedPaths, ...(markerCleanupFailed ? [markerRel] : [])]),\n };\n } catch (err) {\n await rm(stagingAbs, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n });\n}\n\n// ---------------------------------------------------------------------------\n// Push-side whole-generation omission\n// ---------------------------------------------------------------------------\n\nexport interface EmbeddingGenerationOmissionOptions {\n rootAbs: string;\n userExcludeRegexps?: readonly RegExp[];\n excludeFile?: OfflineSyncExcludeFile;\n /** Recorded deletion-revision paths: a tombstoned member counts as a\n * generation member for omission detection even though it is absent on\n * disk (its exclusion must omit the WHOLE generation). */\n tombstonedPaths?: readonly string[];\n /**\n * Per-path push exclusion predicate (structural + node-local defaults +\n * user regexps). Defaults to the built-in default excludes + the supplied\n * user regexps, matching the walk that consumes the result.\n */\n isExcludedRelPath?: (relPath: string) => boolean;\n}\n\nexport interface EmbeddingGenerationOmission {\n /** Existing member paths omitted because their generation is partial. */\n omittedPaths: Set<string>;\n /** Generation dirs whose members are omitted (snapshot field value). */\n omittedDirs: string[];\n}\n\n/**\n * Compute the push-side whole-generation omission set: when ANY on-disk\n * member of an embedding generation is excluded by a push filter, EVERY\n * member of that generation is omitted (issue #3148, round 4).\n */\nexport async function computeOmittedEmbeddingGenerationPaths(\n options: EmbeddingGenerationOmissionOptions\n): Promise<EmbeddingGenerationOmission> {\n const omittedPaths = new Set<string>();\n const omittedDirs: string[] = [];\n const stateDirs = [\"state\"];\n let namespaceNames: string[] = [];\n try {\n namespaceNames = (await readdir(path.join(options.rootAbs, \"namespaces\"), { withFileTypes: true }))\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name);\n } catch {\n // no namespaces\n }\n for (const ns of namespaceNames) stateDirs.push(`namespaces/${ns}/state`);\n const isExcluded = options.isExcludedRelPath ??\n ((relPath: string) =>\n matchesOfflineSyncDefaultExclude(relPath) ||\n Boolean(options.userExcludeRegexps?.some((re) => re.test(relPath))));\n\n for (const stateDir of stateDirs) {\n const markerRel = `${stateDir}/${EMBEDDING_MARKER_BASENAME}`;\n const shardDirRel = `${stateDir}/${EMBEDDING_SHARD_DIR_BASENAME}`;\n const shardDirAbs = path.join(options.rootAbs, ...shardDirRel.split(\"/\"));\n const memberRels: string[] = [];\n if (await pathExists(path.join(options.rootAbs, ...markerRel.split(\"/\")))) {\n memberRels.push(markerRel);\n }\n let shardNames: string[] = [];\n try {\n shardNames = (await readdir(shardDirAbs, { withFileTypes: true }))\n .filter((entry) => entry.isFile() && EMBEDDING_SHARD_FILE_PATTERN.test(entry.name))\n .map((entry) => entry.name);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n for (const name of shardNames) memberRels.push(`${shardDirRel}/${name}`);\n // A tombstoned member is part of the generation even though it is\n // already absent on disk: its exclusion must omit the WHOLE generation,\n // not just drop its revision individually (codex round 6, XkFf).\n for (const tombstoned of options.tombstonedPaths ?? []) {\n const membership = embeddingGenerationMembership(tombstoned);\n if (membership?.shardDir === shardDirRel && !memberRels.includes(tombstoned)) {\n memberRels.push(tombstoned);\n }\n }\n if (memberRels.length === 0) continue;\n\n const excludedMembers: string[] = [];\n for (const relPath of memberRels) {\n let excluded = isExcluded(relPath);\n if (!excluded && options.excludeFile) {\n excluded = await options.excludeFile({\n root: options.rootAbs,\n path: relPath,\n filePath: path.join(options.rootAbs, ...relPath.split(\"/\")),\n });\n }\n if (excluded) excludedMembers.push(relPath);\n }\n if (excludedMembers.length === 0) continue;\n omittedDirs.push(shardDirRel);\n omittedPaths.add(shardDirRel); // skip new shards after the omission header\n for (const relPath of memberRels) omittedPaths.add(relPath);\n }\n return { omittedPaths, omittedDirs };\n}\n\n/** Reject an exclusion introduced after the snapshot header was computed. */\nexport async function assertEmbeddingGenerationStillIncluded(\n rootAbs: string, shardDirRel: string, excludeFile: OfflineSyncExcludeFile | undefined,\n isExcludedRelPath: (relPath: string) => boolean, tombstonedPaths: readonly string[],\n): Promise<void> {\n const current = await computeOmittedEmbeddingGenerationPaths({ rootAbs, excludeFile, isExcludedRelPath, tombstonedPaths });\n if (current.omittedDirs.includes(shardDirRel)) {\n throw new EmbeddingIndexStorageError(\n `embedding generation ${shardDirRel} became partially excluded during snapshot capture; retry with a fresh omission header`,\n );\n }\n}\n\n/** True when a path belongs to a generation the push omitted. */\nexport function isPathInOmittedEmbeddingGeneration(relPath: string, omittedDirs: ReadonlySet<string>): boolean {\n const membership = embeddingGenerationMembership(relPath);\n return membership !== null && omittedDirs.has(membership.shardDir);\n}\n\nexport type { EmbeddingIndexFile };\n\nimport { matchesOfflineSyncDefaultExclude } from \"./offline-sync-exclude-globs.js\";\nimport type { OfflineSyncDeletionRevision } from \"./offline-sync.js\";\n\nexport interface PushEmbeddingGenerationState {\n /** Generation dirs omitted by the push filters (snapshot header value). */\n omittedDirs: string[];\n /** All omitted member paths (walk skip set). */\n omittedPaths: Set<string>;\n /** Deletions with whole-generation suppression applied. */\n deletions: OfflineSyncDeletionRevision[];\n}\n\n/**\n * One-call push-side generation state for streaming snapshot builders: the\n * whole-generation omission set (from user regexps, excludeFile, and default\n * excludes) plus the deletion list with omitted-generation revisions\n * suppressed — including revisions for members that are already absent, so a\n * filtered generation can never be announced as deleted.\n */\nexport async function resolvePushEmbeddingGenerationState(options: {\n rootAbs: string;\n includeTranscripts: boolean;\n userExcludeRegexps?: readonly RegExp[];\n excludeFile?: OfflineSyncExcludeFile;\n deletions: readonly OfflineSyncDeletionRevision[];\n}): Promise<PushEmbeddingGenerationState> {\n const omission = await computeOmittedEmbeddingGenerationPaths({\n rootAbs: options.rootAbs,\n userExcludeRegexps: options.userExcludeRegexps,\n excludeFile: options.excludeFile,\n // RAW pre-filter tombstones: a deleted member's exclusion must omit the\n // whole generation even though the member is already absent on disk.\n tombstonedPaths: options.deletions.map((deletion) => deletion.path),\n isExcludedRelPath: (relPath) =>\n matchesOfflineSyncDefaultExclude(relPath) ||\n Boolean(options.userExcludeRegexps?.some((re) => re.test(relPath))),\n });\n const omittedDirs = new Set(omission.omittedDirs);\n const deletions = options.deletions.filter((deletion) => {\n if (matchesOfflineSyncDefaultExclude(deletion.path)) return false;\n if (options.userExcludeRegexps?.some((re) => re.test(deletion.path))) return false;\n const membership = embeddingGenerationMembership(deletion.path);\n if (membership && omittedDirs.has(membership.shardDir)) return false;\n return true;\n });\n return { omittedDirs: omission.omittedDirs, omittedPaths: omission.omittedPaths, deletions };\n}\n\n\ninterface StorageBackedIndexIoHost {\n readOfflineSyncFile(filePath: string, opts?: { aadFilePath?: string }): Promise<Buffer>;\n writeOfflineSyncStagingFile(\n filePath: string,\n content: Buffer,\n opts?: { aadFilePath?: string },\n ): Promise<void>;\n}\n\n/**\n * Daemon wiring for the index store IO backed by a StorageManager:\n * canonical-path secure reads/writes, so sync-published encrypted shards\n * decrypt in the daemon and daemon-written shards decrypt on sync. Shard\n * STAGING writes bind the FINAL published path.\n */\nexport function storageBackedIndexStoreIo(storage: StorageBackedIndexIoHost): EmbeddingIndexStoreIo {\n return {\n readUtf8: async (filePath) => (await storage.readOfflineSyncFile(filePath)).toString(\"utf-8\"),\n writeUtf8: (filePath, contents, opts) =>\n storage.writeOfflineSyncStagingFile(\n filePath,\n Buffer.from(contents, \"utf-8\"),\n opts?.finalAadFilePath === undefined ? undefined : { aadFilePath: opts.finalAadFilePath },\n ),\n };\n}\n\nexport interface EmbeddingGenerationRemovalInput {\n root: SafeArchiveRoot;\n /** The generation dir, e.g. `state/embeddings`. */\n shardDirRel: string;\n io: Pick<EmbeddingGenerationTransactionIo, \"deleteFile\" | \"readFile\">;\n /** Incoming deletion revisions by path, threaded so each removed member's\n * tombstone is recorded through the configured delete hook AFTER the\n * atomic empty publication (round 6: raw removal must not lose them). */\n deletionMtimeByPath?: ReadonlyMap<string, number>;\n now: number;\n /**\n * Shared-base states: when the live generation diverged from the base\n * (daemon indexed, added, or removed members after the census), the\n * removal DEFERS instead of erasing the local work (codex P1).\n */\n baseStates?: ReadonlyMap<string, { sha256: string }>;\n}\n\n/**\n * Remove a LOCAL embedding generation atomically because the remote deleted\n * it wholesale (every member has a deletion revision): recover first, then\n * demote the published directory through the store's single swap state\n * machine and delete the backup — a crash or delete failure can never leave\n * a partial same-identity generation serving silently missing memories.\n * The local legacy marker, when present, is removed through the same delete\n * hook the per-file path uses.\n */\nexport async function applyEmbeddingGenerationRemoval(\n input: EmbeddingGenerationRemovalInput,\n): Promise<EmbeddingGenerationTransactionResult> {\n const { root, shardDirRel, io } = input;\n const stateDirRel = shardDirRel.slice(0, -(`/${EMBEDDING_SHARD_DIR_BASENAME}`.length));\n const stateDirAbs = await resolveSafeArchiveTarget(root, stateDirRel);\n return await withEmbeddingGenerationLock(stateDirAbs, async (section) => {\n const lockPath = embeddingGenerationLockPath(stateDirAbs);\n const fence: EmbeddingGenerationFence = () => assertEmbeddingGenerationLockHeld(lockPath, section);\n const markerRel = `${stateDirRel}/${EMBEDDING_MARKER_BASENAME}`;\n const shardDirAbs = await resolveSafeArchiveTarget(root, shardDirRel);\n const markerAbs = await resolveSafeArchiveTarget(root, markerRel);\n const store = new EmbeddingIndexFileStore(\n markerAbs,\n shardDirAbs,\n await resolveSafeArchiveTarget(root, `${stateDirRel}/embedding-fallback-status.json`),\n );\n await store.recoverIfInterrupted(fence);\n\n // Codex P1: the deletion census is stale by the time the lock is held.\n // When the live generation no longer matches the shared base (daemon\n // indexed, added, or removed members), DEFER the removal — the local\n // work survives and a later sync re-evaluates with a fresh base.\n // No shared base (fresh catch-up) = no divergence semantics to protect.\n const baseStates = baseStatesForGeneration(input.baseStates, shardDirRel);\n if (baseStates.size > 0) {\n const localCensus = await censusGenerationState(io, root, markerRel, markerAbs, shardDirAbs, shardDirRel);\n const baseArr = [...baseStates].map(([relPath, state]) => ({ path: relPath, sha256: state.sha256 }));\n const censusArr = [...localCensus].map(([relPath, sha256]) => ({ path: relPath, sha256 }));\n const changedSinceBase =\n censusArr.some((member) => {\n // A MODIFIED member must also defer: removing the generation would\n // erase the daemon's rewrite, not just the remote's stale copy.\n const baseSha = baseStates.get(member.path)?.sha256;\n return baseSha === undefined || baseSha !== member.sha256;\n }) || baseArr.some((member) => !localCensus.has(member.path));\n if (changedSinceBase) {\n const removedPaths = new Set<string>([...localCensus.keys()]);\n log.warn(\n `embedding generation removal ${shardDirRel} deferred: disk diverged from the shared base under the mutation lock; local generation preserved`,\n );\n return {\n shardDirRel,\n upserted: 0,\n deleted: 0,\n writtenStates: new Map<string, OfflineSyncFileState>(),\n removedPaths,\n handledPaths: new Set(removedPaths),\n deferredLocalDivergence: true,\n };\n }\n }\n const localShardRels = (await readdir(shardDirAbs).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [] as string[];\n throw err;\n }))\n .filter((name) => EMBEDDING_SHARD_FILE_PATTERN.test(name))\n .map((name) => `${shardDirRel}/${name}`)\n .sort();\n await store.removePublishedGeneration(fence);\n const removedPaths = new Set(localShardRels);\n let deleted = removedPaths.size;\n const markerPresent = await pathExists(markerAbs);\n if (markerPresent) {\n removedPaths.add(markerRel);\n deleted += 1;\n }\n // Preserve the replicated tombstones through the configured hooks AFTER\n // the atomic publication: raw removal must not lose the deletion\n // revisions, or a downstream sync without base evidence retains stale\n // entries. EVERY member (shards and marker) is recorded exactly ONCE with\n // the remote revision when known, so a first hook call without a revision\n // is never overwritten by an older replicated one. The storage hook\n // tolerates already-absent paths.\n let firstError: unknown = null;\n for (const relPath of removedPaths) {\n const memberAbs = relPath === markerRel ? markerAbs : await resolveSafeArchiveTarget(root, relPath);\n const mtimeMs = input.deletionMtimeByPath?.get(relPath) ?? input.now;\n if (io.deleteFile) {\n try {\n await io.deleteFile({\n root: root.abs,\n path: relPath,\n filePath: await resolveSafeArchiveTarget(root, relPath),\n mtimeMs,\n });\n } catch (err) {\n firstError ??= err;\n log.warn(`embedding generation removal: tombstone recording failed for ${relPath}: ${err instanceof Error ? err.message : String(err)}`);\n }\n } else if (relPath === markerRel && markerPresent) {\n await rm(markerAbs, { force: true });\n }\n }\n if (firstError) throw firstError;\n return {\n shardDirRel,\n upserted: 0,\n deleted,\n writtenStates: new Map(),\n removedPaths,\n handledPaths: new Set(removedPaths),\n };\n });\n}\n\n/** Defer a whole generation when the shared remote base would erase a local change. */\nexport function divergedEmbeddingGenerationDeferrals(options: {\n incomingFiles: readonly { path: string; sha256: string }[];\n baseFiles: readonly { path: string; sha256: string }[];\n currentFiles: readonly { path: string; sha256: string }[];\n}): string[] {\n const base = new Map(options.baseFiles.map((f) => [f.path, f.sha256]));\n const current = new Map(options.currentFiles.map((f) => [f.path, f.sha256]));\n const incomingByPath = new Map(options.incomingFiles.map((f) => [f.path, f.sha256]));\n const sharedDirs = new Set(options.baseFiles.map((f) => embeddingGenerationMembership(f.path)?.shardDir));\n const conflictedDirs = new Set<string>();\n for (const relPath of new Set([...base.keys(), ...current.keys(), ...incomingByPath.keys()])) {\n const membership = embeddingGenerationMembership(relPath);\n // Without a shared generation base this is an initial remote-authoritative\n // import, not evidence of local indexing after a synchronized snapshot.\n if (!membership || !sharedDirs.has(membership.shardDir)) continue;\n const currentSha = current.get(relPath);\n if (currentSha !== base.get(relPath) && currentSha !== incomingByPath.get(relPath)) {\n conflictedDirs.add(membership.shardDir);\n }\n }\n if (conflictedDirs.size === 0) return [];\n const deferred: string[] = [];\n // Include local/base members too: an all-absent remote generation still\n // needs a concrete deferral so its removal cannot erase a local change.\n for (const relPath of new Set([...base.keys(), ...current.keys(), ...incomingByPath.keys()])) {\n const membership = embeddingGenerationMembership(relPath);\n if (membership && conflictedDirs.has(membership.shardDir)) deferred.push(relPath);\n }\n return deferred.sort((left, right) => left.localeCompare(right));\n}\n\n// ---------------------------------------------------------------------------\n// Snapshot-side generation routing, extracted from applyOfflineSyncSnapshot\n// for issue #3150 so the changeset path could share the machinery without\n// growing the offline-sync.ts ratchet ceiling. Behavior-preserving.\n// ---------------------------------------------------------------------------\n\nexport interface IncomingEmbeddingGenerationApply {\n transactionResults: EmbeddingGenerationTransactionResult[];\n deferredGenerationDirs: Set<string>;\n}\n\nexport interface IncomingEmbeddingGenerationApplyOptions {\n root: SafeArchiveRoot;\n incomingMap: ReadonlyMap<string, OfflineSyncFileState>;\n incomingBuffers: ReadonlyMap<string, Buffer>;\n baseMap: ReadonlyMap<string, OfflineSyncFileState>;\n currentMap: ReadonlyMap<string, OfflineSyncFileState>;\n deferredPaths: ReadonlySet<string>;\n omittedGenerationDirs: ReadonlySet<string>;\n deletionMtimeByPath?: ReadonlyMap<string, number>;\n io: EmbeddingGenerationTransactionIo;\n /** True when the caller supplied ANY custom storage IO. */\n customIoPresent: boolean;\n now: number;\n}\n\n/**\n * Snapshot-side generation routing, extracted from applyOfflineSyncSnapshot\n * for issue #3150 (the changeset path needed the same machinery and the\n * offline-sync.ts line ratchet had no headroom). Incoming generations replace\n * as ONE unit through the shared locked transaction. Behavior-preserving —\n * the #3148 regression suites own this contract.\n */\nexport async function applyIncomingEmbeddingGenerations(\n options: IncomingEmbeddingGenerationApplyOptions,\n): Promise<IncomingEmbeddingGenerationApply> {\n const incomingGenerations = detectIncomingEmbeddingGenerations(options.incomingMap.keys());\n const generationDirs = new Set<string>([\n ...incomingGenerations.shardDirs,\n ...incomingGenerations.legacyMarkerDirs,\n ]);\n const deferredGenerationDirs = new Set<string>();\n for (const deferredPath of options.deferredPaths) {\n const membership = embeddingGenerationMembership(deferredPath);\n if (membership) deferredGenerationDirs.add(membership.shardDir);\n }\n const transactionResults: EmbeddingGenerationTransactionResult[] = [];\n // Included and omitted cannot describe the same generation.\n for (const shardDir of generationDirs) {\n if (options.omittedGenerationDirs.has(shardDir)) {\n throw new EmbeddingIndexStorageError(\n `offline sync snapshot both includes and omits embedding generation ${shardDir}; refusing to apply`,\n );\n }\n }\n // Fail closed BEFORE any publication: custom storage IO (encrypted or\n // otherwise) without the staging pair would make the generation transaction\n // fall back to unencrypted raw writes — a plaintext downgrade of the staged\n // generation. Callers must wire both staging hooks (the CLI does).\n if (\n generationDirs.size > 0 &&\n options.customIoPresent &&\n !(options.io.writeStagingFile && options.io.readStagingFile)\n ) {\n throw new EmbeddingIndexStorageError(\n \"refusing to replace embedding generation: custom storage IO requires both writeStagingFile and readStagingFile hooks; unencrypted raw staging would plaintext-downgrade the index\",\n );\n }\n for (const shardDir of generationDirs) {\n if (deferredGenerationDirs.has(shardDir)) continue;\n if (\n incomingGenerations.legacyMarkerDirs.has(shardDir) &&\n !incomingGenerations.shardDirs.has(shardDir)\n ) {\n // Cross-layout: an incoming legacy marker replaces a local sharded\n // generation as a whole (codex round 4). When shards are ALSO\n // incoming, the sharded set is the generation and the marker is\n // written as an inert artifact by the per-file loop below.\n const markerRel = [...options.incomingMap.keys()].find(\n (relPath) => embeddingMarkerDirOf(relPath) === shardDir.slice(0, -`/${EMBEDDING_SHARD_DIR_BASENAME}`.length),\n );\n if (!markerRel) continue;\n transactionResults.push(await applyEmbeddingGenerationTransaction({\n root: options.root,\n shardDirRel: shardDir,\n incomingShardPaths: [],\n incomingMarker: {\n path: markerRel,\n sha256: options.incomingMap.get(markerRel)!.sha256,\n buffer: options.incomingBuffers.get(markerRel) ?? null,\n },\n incomingMarkerPresent: false,\n incomingShardStates: new Map(),\n incomingBuffers: options.incomingBuffers,\n baseStates: options.baseMap,\n io: options.io,\n now: options.now,\n }));\n continue;\n }\n transactionResults.push(await applyEmbeddingGenerationTransaction({\n root: options.root,\n shardDirRel: shardDir,\n incomingShardPaths: [...options.incomingMap.keys()]\n .filter((relPath) => embeddingShardDirOf(relPath) === shardDir)\n .sort(),\n incomingMarker: null,\n incomingMarkerPresent: Boolean(\n incomingGenerations.legacyMarkerDirs.has(shardDir),\n ),\n incomingShardStates: new Map(\n [...options.incomingMap.values()]\n .filter((state) => embeddingShardDirOf(state.path) === shardDir)\n .map((state) => [state.path, state]),\n ),\n incomingBuffers: options.incomingBuffers,\n baseStates: options.baseMap,\n io: options.io,\n now: options.now,\n }));\n }\n // Whole-generation removal (codex round 6 P1): when the remote deleted a\n // previously shared sharded index, the deletion revisions cover EVERY\n // local member. Remove that generation as ONE atomic swap instead of\n // deleting shards one-by-one, so a crash or delete failure can never\n // expose a partial same-identity generation serving missing memories.\n const localGenerationDirs = new Set<string>();\n for (const relPath of options.currentMap.keys()) {\n const membership = embeddingGenerationMembership(relPath);\n if (membership?.kind === \"shard\") localGenerationDirs.add(membership.shardDir);\n }\n for (const shardDir of localGenerationDirs) {\n if (\n generationDirs.has(shardDir) ||\n deferredGenerationDirs.has(shardDir) ||\n options.omittedGenerationDirs.has(shardDir)\n ) {\n continue;\n }\n const localMembers = [...options.currentMap.keys()].filter(\n (relPath) => embeddingGenerationMembership(relPath)?.shardDir === shardDir,\n );\n if (localMembers.length === 0) continue;\n // Tombstone or shared-base absence authorizes whole-generation removal.\n const baseMembers = localMembers.filter((relPath) => options.baseMap.has(relPath));\n // Tombstone evidence is satisfied by the members the REMOTE knew: the\n // remote cannot tombstone local-only extras or an inert legacy marker,\n // and the removal sweeps those once the remote deletion is established.\n // Non-empty same-generation tombstones establish remote absence on\n // their own: they may name shards already absent locally (removed in an\n // earlier round), so the set is NOT required to intersect localMembers\n // (codex round 6, XiOb).\n const tombstonedMembers =\n options.deletionMtimeByPath === undefined\n ? []\n : [...options.deletionMtimeByPath.keys()].filter(\n (relPath) => embeddingGenerationMembership(relPath)?.shardDir === shardDir,\n );\n const tombstoneCovered = tombstonedMembers.length > 0;\n const baseEvidenced =\n baseMembers.length > 0 && baseMembers.every((relPath) => !options.incomingMap.has(relPath));\n if (!tombstoneCovered && !baseEvidenced) continue;\n transactionResults.push(await applyEmbeddingGenerationRemoval({\n root: options.root,\n shardDirRel: shardDir,\n io: { deleteFile: options.io.deleteFile, readFile: options.io.readFile },\n deletionMtimeByPath: options.deletionMtimeByPath,\n baseStates: options.baseMap,\n now: options.now,\n }));\n }\n return { transactionResults, deferredGenerationDirs };\n}\n"],"mappings":"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","names":["removedPaths"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/embedding-index-storage.ts"],"sourcesContent":["/**\n * File-level storage for the embedding fallback index (issue #3146).\n *\n * Two single-generation layouts, exactly one authoritative at a time:\n * - `legacy`: the single `state/embeddings.json` file, used while the\n * whole index serializes within `resolveIndexFileCharLimit()` chars;\n * - `sharded`: the published `state/embeddings/` directory of hash-assigned\n * shard files, migrated to ONE-WAY and ATOMICALLY (stage a complete shard\n * set, publish with a single directory rename, demote the legacy file to\n * a non-authoritative recovery artifact). Generations never merge, so a\n * crash cannot resurrect deleted entries or drop published ones.\n *\n * This module owns files, budgets, and durable diagnostics only — no index\n * caching or provider logic (that lives in `embedding-fallback.ts`).\n */\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\nimport { constants as bufferConstants } from \"node:buffer\";\nimport { lstat, mkdir, mkdtemp, realpath, readFile, readdir, rename, rm, stat, writeFile } from \"node:fs/promises\";\nimport { log } from \"./logger.js\";\nimport { readEnvVar } from \"./runtime/env.js\";\nimport type { EmbeddingGenerationFence } from \"./embedding-generation-lock.js\";\n\nexport type EmbeddingProviderType = \"openai\" | \"local\" | \"host\";\n\nexport type EmbeddingIndexEntry = {\n vector: number[];\n path: string;\n};\n\nexport type EmbeddingIndexFile = {\n version: 1;\n provider: EmbeddingProviderType;\n model: string;\n entries: Record<string, EmbeddingIndexEntry>;\n};\n\nexport type EmbeddingIndexIdentity = Pick<EmbeddingIndexFile, \"provider\" | \"model\">;\n\n/** Outcome of reading one managed index file (legacy or shard). */\nexport type ManagedIndexRead =\n | { outcome: \"ok\"; file: EmbeddingIndexFile }\n | { outcome: \"absent\" }\n | { outcome: \"foreign\" }\n | { outcome: \"unreadable\"; reason: string };\n\n/** Which generation owns the index right now. */\nexport type IndexLayout = \"empty\" | \"legacy\" | \"sharded\";\n\n/**\n * Thrown when an embedding index (or one of its shards) cannot be persisted\n * within the per-file serialization budget. Issue #3146: index files are\n * materialized as single V8 strings, so a JSON document at or past\n * `bufferConstants.MAX_STRING_LENGTH` chars makes every read and write throw\n * `RangeError: Invalid string length`. The index is sharded before that\n * ceiling; this tagged error is reserved for the residual case where even\n * one shard cannot hold the payload (e.g. a single entry with an enormous\n * vector), so callers can classify it as recoverable-with-record instead of\n * an opaque background failure.\n */\nexport class EmbeddingIndexCapacityError extends Error {\n override readonly name = \"EmbeddingIndexCapacityError\" as const;\n constructor(\n message: string,\n readonly memoryId?: string,\n ) {\n super(message);\n }\n}\n\n/**\n * Thrown when an index file that should be authoritative cannot be read\n * (I/O error, over-budget size, unparseable JSON). Mutation paths fail\n * CLOSED on this: they must not continue with a fresh empty index and\n * pretend the previous vectors were loaded — the file is left untouched in\n * place for recovery. Read-only paths (recall) fail open to empty results\n * and record the reason durably.\n */\nexport class EmbeddingIndexStorageError extends Error {\n override readonly name = \"EmbeddingIndexStorageError\" as const;\n}\n\n/** Durable diagnostic row describing the last failed embedding-index write. */\nexport interface EmbeddingIndexWriteFailure {\n ts: string;\n kind: \"capacity\" | \"io\";\n message: string;\n memoryId?: string;\n}\n\n/**\n * Durable embedding-index diagnostics (issue #3146), persisted at\n * `<memoryDir>/state/embedding-fallback-status.json` and surfaced through\n * console_state, so background ingestion failures are visible to operators\n * instead of only ever appearing as log lines.\n */\nexport interface EmbeddingIndexStatusFile {\n version: 1;\n /** Cumulative count of failed index writes since the file was created. */\n failureCount: number;\n /** Active write failure; cleared by the next successful save. */\n lastWriteFailure?: EmbeddingIndexWriteFailure;\n /** Set when an unreadable index file was detected instead of overwritten. */\n lastReadRecovery?: { ts: string; message: string };\n lastSuccessAt?: string;\n}\n\n/**\n * Per-file serialization budget (JSON-string chars) for WRITES of the legacy\n * `state/embeddings.json` file and of every shard under `state/embeddings/`.\n * The 16MiB margin keeps the write round-trip clear of the V8 single-string\n * ceiling even with UTF-8 byte/count skew and small header growth. Override\n * per environment (tests, headroom tuning) via\n * `REMNIC_EMBEDDING_INDEX_FILE_CHAR_LIMIT`.\n *\n * READS are gated by the HARD ceiling (`resolveIndexHardReadCharLimit`),\n * never by this budget: a legacy index written by an older version may\n * legitimately sit between the soft budget and the hard ceiling (the\n * reported production file does), and it must still load and migrate.\n */\nconst DEFAULT_INDEX_FILE_CHAR_LIMIT =\n bufferConstants.MAX_STRING_LENGTH - 16 * 1024 * 1024;\n\n/**\n * Hard single-string ceiling for READS: files at or above this size are not\n * attempted (a readFile would throw `RangeError: Invalid string length`);\n * their bytes are preserved in place and mutations fail closed with an\n * actionable error. Defaults to the V8 maximum; tests inject a small value\n * via `REMNIC_EMBEDDING_INDEX_HARD_READ_LIMIT_CHARS`.\n */\nfunction resolveIndexHardReadCharLimit(): number {\n const soft = resolveIndexFileCharLimit();\n const raw = readEnvVar(\"REMNIC_EMBEDDING_INDEX_HARD_READ_LIMIT_CHARS\");\n if (raw) {\n const parsed = Number(raw);\n if (Number.isInteger(parsed) && parsed >= soft) return parsed;\n }\n return bufferConstants.MAX_STRING_LENGTH;\n}\n\n/** Hash shards under `state/embeddings/` used once the budget is exceeded. */\nconst SHARD_COUNT = 64;\n\nconst SHARD_FILE_PATTERN = /^shard-\\d{4}\\.json$/;\n\nexport function resolveIndexFileCharLimit(): number {\n const raw = readEnvVar(\"REMNIC_EMBEDDING_INDEX_FILE_CHAR_LIMIT\");\n if (raw) {\n const parsed = Number(raw);\n if (Number.isInteger(parsed) && parsed >= 1024) return parsed;\n }\n return DEFAULT_INDEX_FILE_CHAR_LIMIT;\n}\n\nfunction shardFileName(shardIndex: number): string {\n return `shard-${String(shardIndex).padStart(4, \"0\")}.json`;\n}\n\n/**\n * Fixed transaction backup path for identity replacements. A fixed (not\n * per-process) name is what makes the rename gap recoverable: whatever\n * process restarts after a crash finds the demoted former generation here\n * and rolls it back into place.\n */\nfunction replacementBackupPath(shardDir: string): string {\n return path.join(path.dirname(shardDir), \"embeddings.pre-replace.tmp\");\n}\n\n/**\n * Warm-cache generation marker (`<stateDir>/embeddings.generation`).\n * Two-phase publication barrier: in-flight before a publication's first\n * destructive write, fresh unique value after its last write. A stamp is\n * therefore stable only for a completed generation — readers can never\n * cache a stamp that stays stable while generation bytes change, and an\n * in-flight stamp never repeats, so probes cached during a failed\n * completion keep revalidating (PR #3176 codex P2). Plain fs like the\n * status file.\n */\nconst GENERATION_MARKER_BASENAME = \"embeddings.generation\";\n\n/** Reserved in-flight marker value; never equals a completion marker. */\nconst GENERATION_MARKER_IN_FLIGHT = \"in-flight\";\n\n/**\n * Stable FNV-1a shard assignment: an entry always hashes to the same shard,\n * so an update or removal never needs a manifest to find its file.\n */\nexport function shardIndexOf(memoryId: string, shardCount: number): number {\n let hash = 0x811c9dc5;\n for (let i = 0; i < memoryId.length; i++) {\n hash ^= memoryId.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return hash % shardCount;\n}\n\n/**\n * A shard file named `shard-NNNN.json` must only contain ids that hash to\n * that shard: a misplaced copy would be loaded today and silently resurrect\n * stale vectors after the computed shard is rewritten (issue #3148, round 6).\n * Throws the tagged storage error so mutations fail closed and recall fails\n * open at its boundary.\n */\nexport function validateShardMembership(shardName: string, file: EmbeddingIndexFile): void {\n const match = /^shard-(\\d{4})\\.json$/.exec(shardName);\n if (!match) return; // not a shard file (legacy documents have no constraint)\n const shardIndex = Number(match[1]);\n for (const id of Object.keys(file.entries)) {\n if (shardIndexOf(id, SHARD_COUNT) !== shardIndex) {\n throw new EmbeddingIndexStorageError(\n `refusing embedding index shard ${shardName}: id ${id} hashes to shard ${shardIndexOf(id, SHARD_COUNT)}, not ${shardIndex}; file preserved in place`,\n );\n }\n }\n}\n\nexport function isInvalidStringLengthError(err: unknown): boolean {\n return err instanceof RangeError && /invalid string length/i.test(err.message);\n}\n\n/**\n * Parse and FULLY validate one serialized index document (legacy or shard).\n * The single read boundary shared by disk reads and offline-sync pulls: a\n * document with a valid header but a malformed body (`entries` not a plain\n * record, or an entry whose `path` is not a string / whose `vector` is not a\n * finite-number array) is `unreadable`, not `ok` — recall fails open on the\n * tagged reason, mutations fail closed, and the bytes are never overwritten\n * (issue #3148 review, round 4).\n */\nexport function parseEmbeddingIndexDocument(raw: string): ManagedIndexRead {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n return {\n outcome: \"unreadable\",\n reason: `JSON parse failed: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n Array.isArray(parsed) ||\n (parsed as Partial<EmbeddingIndexFile>).version !== 1 ||\n typeof (parsed as Partial<EmbeddingIndexFile>).provider !== \"string\" ||\n typeof (parsed as Partial<EmbeddingIndexFile>).model !== \"string\" ||\n ((parsed as Partial<EmbeddingIndexFile>).model as string).length === 0\n ) {\n return { outcome: \"foreign\" };\n }\n // An unsupported identity must fail closed like any other malformed\n // document: recall cannot serve it and mutations must not adopt it as\n // authoritative (issue #3148, round 6).\n if (\n (parsed as Partial<EmbeddingIndexFile>).provider !== \"openai\" &&\n (parsed as Partial<EmbeddingIndexFile>).provider !== \"local\" &&\n (parsed as Partial<EmbeddingIndexFile>).provider !== \"host\"\n ) {\n return { outcome: \"foreign\" };\n }\n const entries = (parsed as Partial<EmbeddingIndexFile>).entries;\n if (!entries || typeof entries !== \"object\" || Array.isArray(entries)) {\n return {\n outcome: \"unreadable\",\n reason: \"entries must be a plain record of memory id -> entry\",\n };\n }\n for (const [id, entry] of Object.entries(entries as Record<string, unknown>)) {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) {\n return { outcome: \"unreadable\", reason: `entry ${id} must be an object` };\n }\n const candidate = entry as Partial<EmbeddingIndexEntry>;\n if (typeof candidate.path !== \"string\") {\n return { outcome: \"unreadable\", reason: `entry ${id}.path must be a string` };\n }\n if (!Array.isArray(candidate.vector)) {\n return { outcome: \"unreadable\", reason: `entry ${id}.vector must be an array` };\n }\n for (const component of candidate.vector) {\n if (!Number.isFinite(component)) {\n return {\n outcome: \"unreadable\",\n reason: `entry ${id}.vector must contain only finite numbers`,\n };\n }\n }\n }\n return { outcome: \"ok\", file: parsed as EmbeddingIndexFile };\n}\n\n/**\n * Group a complete index into per-shard entry records using the stable FNV\n * assignment. Shared by `persist()` and the offline-sync generation\n * transaction so a converted generation shards identically everywhere.\n */\nexport function shardEntriesForIndex(\n index: EmbeddingIndexFile,\n): Map<number, Record<string, EmbeddingIndexEntry>> {\n const groups = new Map<number, Record<string, EmbeddingIndexEntry>>();\n for (const id of Object.keys(index.entries)) {\n const shardIndex = shardIndexOf(id, SHARD_COUNT);\n const group = groups.get(shardIndex);\n if (group) group[id] = index.entries[id];\n else groups.set(shardIndex, { [id]: index.entries[id] });\n }\n return groups;\n}\n\n/**\n * Serialize one shard, translating a ceiling overflow into a tagged,\n * recoverable capacity error. Exported for the offline-sync generation\n * transaction, which stages incoming generations through the same budget.\n */\nexport function serializeEmbeddingShard(\n index: EmbeddingIndexFile,\n shardIndex: number,\n entries: Record<string, EmbeddingIndexEntry>,\n limit: number = resolveIndexFileCharLimit(),\n): string {\n let body: string;\n try {\n body = JSON.stringify({\n version: 1 as const,\n provider: index.provider,\n model: index.model,\n entries,\n });\n } catch (err) {\n throw new EmbeddingIndexCapacityError(\n `embedding index shard ${shardFileName(shardIndex)} exceeded the single-string ceiling while serializing: ${err instanceof Error ? err.message : String(err)}`,\n largestEntryId(entries) || undefined,\n );\n }\n if (body.length > limit) {\n const worstId = largestEntryId(entries);\n const worstLength = worstId && entries[worstId] ? JSON.stringify(entries[worstId]).length : 0;\n throw new EmbeddingIndexCapacityError(\n `embedding index shard ${shardFileName(shardIndex)} needs ${body.length} chars, over the ${limit}-char single-file limit; largest entry ${worstId || \"(unknown)\"} serializes to ${worstLength} chars`,\n worstId || undefined,\n );\n }\n return body;\n}\n\nfunction largestEntryId(entries: Record<string, EmbeddingIndexEntry>): string {\n let worstId = \"\";\n let worstLength = 0;\n for (const [id, entry] of Object.entries(entries)) {\n let length: number;\n try {\n length = JSON.stringify(entry).length;\n } catch {\n return id;\n }\n if (length > worstLength) {\n worstLength = length;\n worstId = id;\n }\n }\n return worstId;\n}\n\n/**\n * Optional canonical secure-store IO for the daemon. When provided (both\n * halves), file content is read/written through the SAME\n * StorageManager-backed contract offline sync uses: canonical-path AAD,\n * secure-key-aware, atomic. Shard STAGING writes carry the FINAL published\n * path so the post-publish rename keeps the ciphertext decryptable; dirty\n * shard, status, and legacy writes are their own canonical final path.\n * When omitted, the store behaves exactly as before (raw plaintext fs).\n * The diagnostics status file is intentionally ALWAYS plain: it holds no\n * memory content (failure strings/counts only) and console_state reads it\n * without unlocking the store.\n */\nexport interface EmbeddingIndexStoreIo {\n readUtf8(filePath: string): Promise<string>;\n writeUtf8(filePath: string, contents: string, opts?: { finalAadFilePath?: string }): Promise<void>;\n}\n\nexport class EmbeddingIndexFileStore {\n constructor(\n private readonly indexPath: string,\n private readonly shardDir: string,\n private readonly statusPath: string,\n private readonly io?: EmbeddingIndexStoreIo,\n ) {\n if (io && (Boolean(io.readUtf8) !== Boolean(io.writeUtf8))) {\n throw new EmbeddingIndexStorageError(\n \"embedding index store io requires both readUtf8 and writeUtf8; a half-wired io would mix encrypted and plaintext files\",\n );\n }\n }\n\n /** Legacy single-file layout path (public for error messages). */\n get legacyPath(): string {\n return this.indexPath;\n }\n\n /** Record an unreadable-authoritative-file reason (both read and mutation paths). */\n async recordIndexStatusForLoad(reason: string): Promise<void> {\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: reason },\n });\n }\n\n /**\n * Which generation owns the index right now. The published\n * `state/embeddings/` directory IS the layout marker: until it exists the\n * legacy file is authoritative; from the atomic rename onward it never\n * merges again.\n */\n async detectLayout(): Promise<IndexLayout> {\n // An interrupted identity replacement leaves the published directory\n // absent with the fixed transaction backup still on disk (the publish\n // rename never completed). Report SHARDED for that gap: the backup holds\n // the authoritative generation, so neither the legacy file nor a fresh\n // index may win, and reads fail open to empty until the mutation queue\n // performs the rollback. The rollback itself only ever runs inside\n // persist() — a write — so read paths never race an in-flight\n // replacement with a recovery rename (issue #3148 review).\n try {\n // lstat, NOT stat: a symlinked generation directory must be rejected\n // outright — following it would read or write OUTSIDE the state\n // directory through the link (codex PRRT_kwDORJXyws6mZ72r).\n const info = await lstat(this.shardDir);\n if (info.isSymbolicLink()) {\n throw new EmbeddingIndexStorageError(\n `embedding index shard directory is a symlink; refusing to read or write through it: ${this.shardDir}`,\n );\n }\n return \"sharded\";\n } catch (err) {\n if (err instanceof EmbeddingIndexStorageError) throw err;\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n // The pointer exists but cannot be stat'ed — fail towards the\n // published generation so its readers surface the I/O error.\n log.warn(`embedding index: shard dir stat failed (${err instanceof Error ? err.message : String(err)}); assuming sharded layout`);\n return \"sharded\";\n }\n if (await this.replacementBackupExists()) return \"sharded\";\n return this.legacyOrEmptyLayout();\n }\n }\n\n /**\n * Roll the fixed transaction backup back into the published position.\n * Returns true when a rollback happened.\n */\n private async replacementBackupExists(): Promise<boolean> {\n const backupPath = replacementBackupPath(this.shardDir);\n try {\n await stat(backupPath);\n return true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n // An unreadable backup must not be silently treated as absent —\n // that would permit the legacy fallback over a generation we cannot\n // inspect (issue #3148 review, round 1).\n throw new EmbeddingIndexStorageError(\n `cannot stat embedding index replacement backup ${backupPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /**\n * The fixed transaction backup is a path with well-known semantics; a\n * planted symlink must never be restored into the published position (it\n * would point the generation outside the memory dir) nor removed through\n * as if it were the former generation. Symlinks are rejected outright.\n */\n private async assertBackupNotSymlink(backupPath: string): Promise<void> {\n const info = await lstat(backupPath).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw err;\n });\n if (info?.isSymbolicLink()) {\n throw new EmbeddingIndexStorageError(\n `embedding index replacement backup is a symlink; refusing to touch it: ${backupPath}`,\n );\n }\n }\n\n /**\n * Mutation-queue entry point: roll the fixed transaction backup back into\n * the published position after an interrupted replacement. Returns true\n * when a rollback happened. Only fires in the actual rename gap\n * (published directory absent); callers must invalidate any cached index\n * view when this returns true. A successful rollback finalizes the\n * generation marker strictly: a failed completion write fails the\n * recovery instead of leaving every search reloading forever.\n */\n async recoverIfInterrupted(fence?: EmbeddingGenerationFence): Promise<boolean> {\n // Only the rename gap (published directory absent) is recoverable. When\n // the directory is present - including after a crash that followed a\n // successful publish - the generation on disk is already authoritative\n // and renaming the backup onto it would fail with EEXIST (issue #3148\n // review, round 2).\n try {\n await stat(this.shardDir);\n return false;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") {\n log.warn(`embedding index: recovery skipped because shard dir stat failed: ${err instanceof Error ? err.message : String(err)}`);\n return false;\n }\n }\n const backupPath = replacementBackupPath(this.shardDir);\n if (!(await this.replacementBackupExists())) return false;\n await this.assertBackupNotSymlink(backupPath);\n // The rollback rename is destructive: reassert lock ownership first.\n await fence?.();\n await rename(backupPath, this.shardDir);\n await this.writeGenerationMarker(randomUUID());\n log.warn(\n `embedding index: recovered interrupted replacement; former generation restored from ${backupPath}`,\n );\n await this.recordIndexStatus({\n lastReadRecovery: {\n ts: new Date().toISOString(),\n message: \"interrupted identity replacement rolled back to the former generation\",\n },\n });\n return true;\n }\n\n private async legacyOrEmptyLayout(): Promise<IndexLayout> {\n try {\n await stat(this.indexPath);\n return \"legacy\";\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return \"empty\";\n throw err;\n }\n }\n\n /** Barrier first half: strict, so bytes can never change under a stable stamp. */\n private async beginGenerationMarker(): Promise<void> {\n await this.writeGenerationMarker(GENERATION_MARKER_IN_FLIGHT);\n }\n\n /**\n * Barrier second half: written after the last content write. Best-effort:\n * a failure leaves the in-flight marker, whose stamp identityStamp()\n * never repeats, so cached probes revalidate on every search until a\n * publication completes.\n */\n private async completeGenerationMarker(): Promise<void> {\n await this.writeGenerationMarker(randomUUID()).catch((err) => {\n log.warn(`embedding index: could not write generation completion marker: ${err instanceof Error ? err.message : String(err)}`);\n });\n }\n\n private async writeGenerationMarker(value: string): Promise<void> {\n await this.writeAtomicFile(\n path.join(path.dirname(this.shardDir), GENERATION_MARKER_BASENAME),\n value,\n { bypassSecureIo: true },\n );\n }\n\n /** Marker value, or null when absent/unreadable (pre-marker stores keep the stat stamp). */\n private async readGenerationMarker(): Promise<string | null> {\n try {\n const raw = await readFile(\n path.join(path.dirname(this.shardDir), GENERATION_MARKER_BASENAME),\n \"utf-8\",\n );\n const value = raw.trim();\n return value.length > 0 ? value : null;\n } catch {\n return null;\n }\n }\n\n /**\n * Warm-cache stamp. A completed generation marker alone would freeze the\n * stamp for a pre-marker writer in a rolling deployment (it publishes\n * shard/index changes without updating the marker), so the marker is\n * combined with the watched path's stat: the marker keeps consecutive\n * publications distinct on coarse-tick filesystems, the stat keeps\n * legacy writers visible. In-flight never repeats, forcing revalidation\n * until a publication completes. Stores never published through this\n * module keep the plain stat stamp — a directory stat alone aliases\n * consecutive publications on coarse-tick filesystems and across inode\n * reuse.\n */\n async identityStamp(): Promise<string> {\n const layout = await this.detectLayout();\n if (layout === \"sharded\") {\n const info = await lstat(this.shardDir).catch((err) => {\n // SAFETY: only the errno code is inspected; any other rejection shape rethrows uninterpreted.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new EmbeddingIndexStorageError(\n `cannot stat embedding index shard directory ${this.shardDir}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n if (!info) return \"rename-gap\";\n const marker = await this.readGenerationMarker();\n // An in-flight marker must never produce a repeatable stamp: a probe\n // that cached one (completion write failed) must revalidate on every\n // later probe until a publication completes.\n if (marker) {\n if (marker === GENERATION_MARKER_IN_FLIGHT) return `gen:${randomUUID()}`;\n return `gen:${marker}:${info.ino}:${info.mtimeMs}`;\n }\n return `shard:${info.ino}:${info.mtimeMs}`;\n }\n if (layout === \"legacy\") {\n const info = await stat(this.indexPath).catch((err) => {\n // SAFETY: only the errno code is inspected; any other rejection shape rethrows uninterpreted.\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new EmbeddingIndexStorageError(\n `cannot stat embedding index file ${this.indexPath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n if (!info) return \"rename-gap\";\n const marker = await this.readGenerationMarker();\n if (marker) {\n if (marker === GENERATION_MARKER_IN_FLIGHT) return `gen:${randomUUID()}`;\n return `gen:${marker}:${info.ino}:${info.mtimeMs}`;\n }\n return `legacy:${info.ino}:${info.mtimeMs}`;\n }\n return \"empty\";\n }\n\n /** Header identity of the authoritative generation, read leniently. */\n async identityFromDisk(): Promise<EmbeddingIndexIdentity | null> {\n const layout = await this.detectLayout();\n if (layout === \"sharded\") {\n // Shard files are bounded by the per-file budget, so the identity\n // probe never reads a ceiling-sized string.\n for (const name of await this.readShardNames()) {\n const read = await this.readFileAt(path.join(this.shardDir, name));\n if (read.outcome === \"ok\") {\n return { provider: read.file.provider, model: read.file.model };\n }\n }\n return null;\n }\n if (layout === \"legacy\") {\n const read = await this.readFileAt(this.indexPath);\n if (read.outcome === \"ok\") {\n return { provider: read.file.provider, model: read.file.model };\n }\n }\n return null;\n }\n\n /**\n * Load every readable shard into `merged`. Returns the generation identity.\n * Unreadable shards record durable diagnostics; `strict` additionally\n * fails the mutation closed.\n */\n async readShardGenerationInto(\n merged: Record<string, EmbeddingIndexEntry>,\n ): Promise<EmbeddingIndexIdentity | null> {\n let identity: EmbeddingIndexIdentity | null = null;\n for (const name of await this.readShardNames()) {\n const read = await this.readFileAt(path.join(this.shardDir, name));\n if (read.outcome !== \"ok\") {\n if (read.outcome === \"unreadable\") {\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: `${name}: ${read.reason}` },\n });\n throw new EmbeddingIndexStorageError(\n `refusing to continue from unreadable embedding index shard ${name}: ${read.reason}`,\n );\n }\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: `${name}: unrecognized index format` },\n });\n throw new EmbeddingIndexStorageError(\n `refusing to continue from malformed embedding index shard ${name} (unrecognized format); file preserved in place`,\n );\n }\n try {\n validateShardMembership(name, read.file);\n } catch (error) {\n await this.recordIndexStatus({\n lastReadRecovery: { ts: new Date().toISOString(), message: String(error) },\n });\n throw error;\n }\n const shardIdentity = { provider: read.file.provider, model: read.file.model };\n if (!identity) identity = shardIdentity;\n if (!sameIndexIdentity(shardIdentity, identity)) {\n // A partial rewrite from this map would silently drop the foreign\n // shard's entries, so even recall must not serve this generation.\n throw new EmbeddingIndexStorageError(\n `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`,\n );\n }\n Object.assign(merged, read.file.entries);\n }\n return identity;\n }\n\n async readLegacy(): Promise<ManagedIndexRead> {\n return this.readFileAt(this.indexPath);\n }\n\n /**\n * Read one managed index file (legacy or shard). The file is never\n * modified or moved here: an unreadable file stays in place for recovery\n * and is reported through the returned outcome (mutation paths fail\n * closed on it; read-only paths fail open and record the reason).\n */\n /**\n * Read/mutation boundary for any generation member or the legacy marker\n * (codex PRRT_kwDORJXyws6mZ72r): a symlinked member is rejected outright\n * and its real target must stay inside the canonical state directory —\n * following a planted link would read or write outside the memory root.\n * Runs BEFORE the secure IO so storage-backed reads are covered too.\n */\n private async assertGenerationFileNotSymlinked(filePath: string): Promise<void> {\n const info = await lstat(filePath).catch((err) => {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw new EmbeddingIndexStorageError(\n `cannot lstat embedding index file ${filePath}: ${err instanceof Error ? err.message : String(err)}`,\n );\n });\n const stateDir = path.dirname(this.shardDir);\n // Containment must be checked even when the member itself is a regular\n // file: a SYMLINKED GENERATION DIRECTORY lets reads through to whatever\n // path the link resolves to — a regular file in a linked dir is still\n // outside the memory root and must be rejected.\n const real = await realpath(filePath).catch(() => null);\n if (real) {\n const scope = await realpath(stateDir).catch(() => stateDir);\n const rel = path.relative(scope, real);\n if (rel.startsWith(\"..\") || path.isAbsolute(rel)) {\n throw new EmbeddingIndexStorageError(\n `embedding index file escapes the state directory via symlink; refusing: ${filePath} -> ${real}`,\n );\n }\n }\n if (!info || !info.isSymbolicLink()) return;\n throw new EmbeddingIndexStorageError(\n `embedding index file is a symlink; refusing to read or write through it: ${filePath}`,\n );\n }\n\n async readFileAt(filePath: string): Promise<ManagedIndexRead> {\n await this.assertGenerationFileNotSymlinked(filePath);\n const hardLimit = resolveIndexHardReadCharLimit();\n let size = 0;\n try {\n size = (await stat(filePath)).size;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return { outcome: \"absent\" };\n return { outcome: \"unreadable\", reason: `stat failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n // Reads are gated by the HARD single-string ceiling only — NOT by the\n // soft write budget — so a legacy index from an older version sitting\n // between the two still loads and migrates (issue #3146). Byte size is\n // only a conservative bound for UTF-8 (bytes ≥ chars): at or below it a\n // readFile is guaranteed materializable; past it the file MAY still be\n // readable as a string, but refusing is the safe side. An over-ceiling\n // file is preserved in place; the caller decides fail-open (recall) or\n // fail-closed (mutation).\n if (size > hardLimit) {\n return {\n outcome: \"unreadable\",\n reason: `file is ${size} bytes, over the ${hardLimit}-char hard single-string read ceiling (V8 RangeError territory); bytes preserved in place — move or prune the file to recover`,\n };\n }\n let raw: string;\n try {\n raw = this.io\n ? await this.io.readUtf8(filePath)\n : await readFile(filePath, \"utf-8\");\n } catch (err) {\n return { outcome: \"unreadable\", reason: `read failed: ${err instanceof Error ? err.message : String(err)}` };\n }\n return parseEmbeddingIndexDocument(raw);\n }\n\n private async readShardNames(): Promise<string[]> {\n try {\n return (await readdir(this.shardDir))\n .filter((name) => SHARD_FILE_PATTERN.test(name))\n .sort();\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return [];\n // The published generation exists but cannot be enumerated. Treating\n // it as empty would let a mutation overwrite shards it never saw, so\n // this must propagate — strict mutation fails closed, recall fails\n // open at its boundary (issue #3146 review).\n throw new EmbeddingIndexStorageError(\n `cannot enumerate embedding index shard directory ${this.shardDir}: ${err instanceof Error ? `${err.message} (${(err as NodeJS.ErrnoException).code ?? \"unknown\"})` : String(err)}`,\n );\n }\n }\n\n /**\n * Persist the index. Layout decisions, in order:\n * - published generation: rewrite only the shards this mutation touched;\n * - legacy/empty generation and the whole index fits one bounded JSON\n * string: stay the single `state/embeddings.json` file;\n * - over budget: ONE-WAY migration — stage a complete shard set, publish\n * it with a single directory rename, then rename the legacy file aside\n * as a non-authoritative recovery artifact (issue #3146).\n */\n async persist(\n index: EmbeddingIndexFile,\n opts: {\n touchedIds?: readonly string[];\n memoryId?: string;\n /** Reasserts mutation-lock ownership immediately before each destructive write (after serialization). */\n fence?: EmbeddingGenerationFence;\n } = {},\n ): Promise<void> {\n const stateDir = path.dirname(this.indexPath);\n await mkdir(stateDir, { recursive: true });\n await this.cleanForeignStagingDirs();\n // NOTE: interrupted-replacement recovery runs in the mutation wrapper\n // (EmbeddingFallback.enqueueIndexMutation) BEFORE this method, so the\n // caller's index already reflects the restored generation.\n\n const groups = shardEntriesForIndex(index);\n\n const layout = await this.detectLayout();\n\n if (layout === \"sharded\") {\n const limit = resolveIndexFileCharLimit();\n // An identity change on a published generation (a sharded host index\n // replaced by a fallback provider, or a host-model change) must\n // publish a COMPLETE replacement: the dirty-shard fast path would\n // rewrite only the touched shard under the new identity and leave the\n // former identity's shards in place — a mixed generation that every\n // later load rejects (codex P1, PR #3148).\n const diskIdentity = await this.identityFromDisk();\n if (groups.size === 0) {\n // An empty directory has no transport-visible deletion evidence.\n // Publish a valid empty shard so a peer without a shared base also\n // replaces its stale generation rather than preserving old vectors.\n groups.set(0, {});\n await this.publishReplacementGeneration(index, groups, limit, opts.fence);\n return;\n }\n const identityChanged =\n diskIdentity !== null &&\n (diskIdentity.provider !== index.provider || diskIdentity.model !== index.model);\n if (identityChanged) {\n await this.publishReplacementGeneration(index, groups, limit, opts.fence);\n return;\n }\n const dirtyShards = opts.touchedIds?.length\n ? new Set(opts.touchedIds.map((id) => shardIndexOf(id, SHARD_COUNT)))\n : null;\n // Serialize every touched shard BEFORE writing anything so a capacity\n // failure cannot leave a partial rewrite.\n const targets = dirtyShards ? [...dirtyShards] : [...groups.keys()];\n const payloads = targets\n .filter((shardIndex) => groups.has(shardIndex))\n .map((shardIndex) => ({\n shardIndex,\n body: serializeEmbeddingShard(index, shardIndex, groups.get(shardIndex)!, limit),\n }));\n // Serialization is done: reassert lock ownership immediately before\n // the destructive live-generation writes.\n await opts.fence?.();\n await this.beginGenerationMarker();\n await opts.fence?.();\n for (const payload of payloads) {\n await this.writeAtomicFile(\n path.join(this.shardDir, shardFileName(payload.shardIndex)),\n payload.body,\n );\n }\n // A dirty shard with no remaining entries (its last entry was just\n // removed) must be removed from disk, not silently skipped.\n for (const shardIndex of targets) {\n if (!groups.has(shardIndex)) {\n await rm(path.join(this.shardDir, shardFileName(shardIndex)), { force: true });\n }\n }\n await this.completeGenerationMarker();\n return;\n }\n\n let whole: string | null;\n try {\n whole = JSON.stringify(index);\n } catch (err) {\n if (!isInvalidStringLengthError(err)) throw err;\n whole = null;\n }\n const limit = resolveIndexFileCharLimit();\n if (whole !== null && whole.length <= limit) {\n // Serialized above: reassert lock ownership immediately before the\n // destructive atomic write.\n await opts.fence?.();\n await this.beginGenerationMarker();\n await opts.fence?.();\n await this.writeAtomicFile(this.indexPath, whole);\n await this.completeGenerationMarker();\n return;\n }\n\n // One-way migration. Until the publish rename, the legacy file stays\n // authoritative; afterwards the published directory is the only\n // generation and the legacy file is demoted to a recovery artifact.\n const stagingDir = path.join(\n stateDir,\n `embeddings.staging.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n );\n await mkdir(stagingDir, { recursive: true });\n try {\n await this.stageShardSet(stagingDir, index, groups, limit);\n // Atomic publish: the directory appearing IS the layout marker.\n await this.publishSwappedGeneration(stagingDir, opts.fence);\n } catch (err) {\n await rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n await rename(\n this.indexPath,\n `${this.indexPath}.pre-migration.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n ).catch(() => undefined);\n }\n\n /**\n * Replace a published generation wholesale after a provider/model identity\n * change (replacement semantics — the former entries are obsolete by\n * definition). Crash-safe by the same construction as the migration: the\n * COMPLETE new-identity set is staged and validated first (old bytes are\n * not touched until staging succeeds), then published atomically through\n * `publishSwappedGeneration`.\n */\n private async publishReplacementGeneration(\n index: EmbeddingIndexFile,\n groups: Map<number, Record<string, EmbeddingIndexEntry>>,\n limit: number,\n fence?: EmbeddingGenerationFence,\n ): Promise<void> {\n const stateDir = path.dirname(this.indexPath);\n const stagingDir = path.join(\n stateDir,\n `embeddings.staging.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n );\n await mkdir(stagingDir, { recursive: true });\n try {\n await this.stageShardSet(stagingDir, index, groups, limit);\n await this.publishSwappedGeneration(stagingDir, fence);\n } catch (err) {\n await rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Write a complete shard set into a staging directory. Every shard is\n * serialized before the first write, so a capacity failure cannot leave a\n * partial stage behind.\n */\n private async stageShardSet(\n stagingDir: string,\n index: EmbeddingIndexFile,\n groups: Map<number, Record<string, EmbeddingIndexEntry>>,\n limit: number,\n ): Promise<void> {\n const payloads = [...groups]\n .map(([shardIndex, entries]) => ({\n shardIndex,\n body: serializeEmbeddingShard(index, shardIndex, entries, limit),\n }));\n for (const payload of payloads) {\n const finalAadFilePath = path.join(this.shardDir, shardFileName(payload.shardIndex));\n await this.writeAtomicFile(\n path.join(stagingDir, shardFileName(payload.shardIndex)),\n payload.body,\n { finalAadFilePath },\n );\n }\n }\n\n /**\n * Atomically remove the published generation (offline-sync: the remote\n * deleted the whole index) by publishing an EMPTY staged directory\n * through the shared swap: the rename is atomic, so the former generation\n * moves whole into the fixed backup and back — a failure before the\n * commit rolls the former generation back intact, and a post-commit\n * backup-cleanup failure is non-fatal (the empty published directory\n * stays the authoritative one-way layout marker). The fixed backup is\n * symlink-checked first (round 5).\n */\n async removePublishedGeneration(fence?: EmbeddingGenerationFence): Promise<void> {\n const backupPath = replacementBackupPath(this.shardDir);\n await this.assertBackupNotSymlink(backupPath);\n await rm(backupPath, { recursive: true, force: true });\n // Remove by publishing an EMPTY staged directory through the SAME swap\n // state machine as every other publication: rename is atomic, so the\n // former generation moves whole into the fixed backup and back — a\n // partially-deleted generation can never be exposed. The empty\n // published directory stays the one-way layout marker, so a stray\n // legacy file can never become authoritative after removal. The\n // demoted backup's post-commit cleanup is non-fatal (existing\n // publishSwappedGeneration semantics).\n await mkdir(path.dirname(this.shardDir), { recursive: true });\n const emptyStaging = await mkdtemp(\n path.join(path.dirname(this.shardDir), \"embeddings.staging.tmp-empty-\"),\n );\n try {\n await this.publishSwappedGeneration(emptyStaging, fence);\n } catch (err) {\n await rm(emptyStaging, { recursive: true, force: true }).catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Atomically publish a fully staged generation directory as THE published\n * generation. The former published directory is demoted to the FIXED\n * transaction backup and the staging dir is renamed in with a single\n * rename; if the publish rename fails the former generation is rolled\n * back, and if the process dies in the rename gap `detectLayout()` /\n * `recoverIfInterrupted()` perform the same rollback on next use.\n * Shared by the mutation-side migration/replacement and the offline-sync\n * generation transaction (issue #3148, round 4) so there is exactly one\n * swap state machine. Staging-dir cleanup stays with the caller.\n */\n async publishSwappedGeneration(stagingDir: string, fence?: EmbeddingGenerationFence): Promise<void> {\n // The replacement is published: the backup is obsolete disk. A cleanup\n // failure is non-fatal (the next replacement removes it) but must stay\n // visible.\n const backupPath = replacementBackupPath(this.shardDir);\n await this.assertBackupNotSymlink(backupPath);\n // Cheap ownership check before the destructive backup cleanup: a peer\n // stale-break would otherwise see the backup vanish under it before we\n // even reach the live renames (Kilo PRRT_kwDORJXyws6mZ8i5).\n await fence?.();\n await rm(backupPath, { recursive: true, force: true });\n // Second ownership check immediately before the live renames: any\n // delay between the cleanup and the rename still allows a stale-break,\n // and publishing now would clobber the peer's write.\n await fence?.();\n // Publication barrier: in-flight before the renames, final at commit.\n await this.beginGenerationMarker();\n await fence?.();\n let demoted = false;\n try {\n await stat(this.shardDir);\n await rename(this.shardDir, backupPath);\n demoted = true;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"ENOENT\") throw err;\n }\n try {\n await rename(stagingDir, this.shardDir);\n } catch (err) {\n if (demoted) {\n try {\n await rename(backupPath, this.shardDir);\n await this.completeGenerationMarker();\n } catch (rollbackErr) {\n log.warn(`embedding index: publication rollback did not restore the generation; marker stays in-flight: ${rollbackErr instanceof Error ? rollbackErr.message : String(rollbackErr)}`);\n }\n } else {\n await this.completeGenerationMarker();\n }\n throw err;\n }\n await this.completeGenerationMarker();\n await rm(backupPath, { recursive: true, force: true }).catch((err) => {\n log.warn(`embedding index: could not remove replacement backup ${backupPath}: ${err}`);\n });\n }\n\n /** 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). */\n private static readonly STAGING_CLEANUP_GRACE_MS = 10 * 60 * 1000;\n\n private async cleanForeignStagingDirs(): Promise<void> {\n const stateDir = path.dirname(this.indexPath);\n let names: string[] = [];\n try {\n names = await readdir(stateDir);\n } catch {\n return;\n }\n const ownPrefix = `embeddings.staging.tmp-${process.pid}-`;\n const cutoff = Date.now() - EmbeddingIndexFileStore.STAGING_CLEANUP_GRACE_MS;\n for (const name of names) {\n if (!name.startsWith(\"embeddings.staging.tmp-\") || name.startsWith(ownPrefix)) continue;\n try {\n const stats = await stat(path.join(stateDir, name));\n if (stats.mtimeMs > cutoff) continue;\n } catch {\n continue;\n }\n await rm(path.join(stateDir, name), { recursive: true, force: true }).catch(() => undefined);\n }\n }\n\n /**\n * Temp-file + rename write. The temp name carries `.tmp-` so offline sync\n * ignores in-flight files under `state/`. With a wired io, the write goes\n * through the canonical secure-store contract instead, optionally binding\n * the ciphertext to the FINAL published path (staged shards).\n */\n private async writeAtomicFile(\n filePath: string,\n contents: string,\n opts?: { finalAadFilePath?: string; bypassSecureIo?: boolean },\n ): Promise<void> {\n if (this.io && opts?.bypassSecureIo !== true) {\n await this.io.writeUtf8(filePath, contents, opts?.finalAadFilePath === undefined ? undefined : { finalAadFilePath: opts.finalAadFilePath });\n return;\n }\n const tempPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n try {\n await writeFile(tempPath, contents, \"utf-8\");\n await rename(tempPath, filePath);\n } catch (err) {\n await rm(tempPath, { force: true }).catch(() => undefined);\n throw err;\n }\n }\n\n /**\n * Record the outcome of one index mutation durably. This is diagnostics,\n * NOT recovery: write failures are recorded and then re-thrown by the\n * caller so the extraction/consolidation task fails explicitly instead of\n * declaring success over an index that could not be persisted (issue\n * #3146).\n */\n async recordIndexWriteOutcome(err: unknown, memoryId?: string): Promise<void> {\n if (!err) {\n await this.recordIndexStatus({\n lastWriteFailure: null,\n lastSuccessAt: new Date().toISOString(),\n });\n return;\n }\n const kind: EmbeddingIndexWriteFailure[\"kind\"] =\n err instanceof EmbeddingIndexCapacityError || isInvalidStringLengthError(err)\n ? \"capacity\"\n : \"io\";\n await this.recordIndexStatus({\n failureCountIncrement: 1,\n lastWriteFailure: {\n ts: new Date().toISOString(),\n kind,\n message: err instanceof Error ? err.message : String(err),\n ...(memoryId ? { memoryId } : {}),\n },\n });\n }\n\n /**\n * Merge diagnostic state into `state/embedding-fallback-status.json`.\n * Best-effort: a diagnostics failure never masks the real outcome.\n */\n private async recordIndexStatus(\n patch: {\n failureCountIncrement?: number;\n lastWriteFailure?: EmbeddingIndexWriteFailure | null;\n lastReadRecovery?: { ts: string; message: string };\n lastSuccessAt?: string;\n },\n ): Promise<void> {\n try {\n let current: EmbeddingIndexStatusFile = { version: 1, failureCount: 0 };\n try {\n const parsed = JSON.parse(await readFile(this.statusPath, \"utf-8\")) as Partial<EmbeddingIndexStatusFile>;\n if (parsed && typeof parsed === \"object\" && typeof parsed.failureCount === \"number\") {\n current = { ...parsed, version: 1, failureCount: parsed.failureCount };\n }\n } catch {\n // Absent or unreadable status starts fresh; the index files own the truth.\n }\n const next: EmbeddingIndexStatusFile = { ...current };\n if (patch.failureCountIncrement) next.failureCount += patch.failureCountIncrement;\n if (patch.lastWriteFailure !== undefined) {\n if (patch.lastWriteFailure === null) delete next.lastWriteFailure;\n else next.lastWriteFailure = patch.lastWriteFailure;\n }\n if (patch.lastReadRecovery !== undefined) next.lastReadRecovery = patch.lastReadRecovery;\n if (patch.lastSuccessAt !== undefined) next.lastSuccessAt = patch.lastSuccessAt;\n // The status file is ALWAYS plain (non-secret diagnostics: failure\n // strings and counts only). It bypasses the secure io so console_state\n // keeps reading it on locked stores and failure counts never reset.\n await mkdir(path.dirname(this.statusPath), { recursive: true });\n await this.writeAtomicFile(this.statusPath, JSON.stringify(next), { bypassSecureIo: true });\n } catch (err) {\n log.debug(`embedding fallback status write failed: ${err}`);\n }\n }\n}\n\nfunction sameIndexIdentity(\n left: EmbeddingIndexIdentity,\n right: EmbeddingIndexIdentity,\n): boolean {\n return left.provider === right.provider && left.model === right.model;\n}\n"],"mappings":"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","names":["limit"]}
|