@remnic/core 9.69.87 → 9.69.89
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.js +56 -49
- package/dist/access-authorization-probe.js +57 -50
- package/dist/access-boundary.js +56 -49
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +129 -122
- package/dist/access-cli.js.map +1 -1
- package/dist/access-extraction-force-flush.js +56 -49
- package/dist/access-http-briefing.js +57 -50
- package/dist/access-http-offline-stream.d.ts.map +1 -1
- package/dist/access-http-offline-stream.js +1 -1
- package/dist/access-http-query.js +58 -51
- package/dist/access-http.js +78 -71
- package/dist/access-identity-continuity-surface.js +56 -49
- package/dist/access-lcm-surface.js +56 -49
- package/dist/access-mcp-output-schemas.d.ts.map +1 -1
- package/dist/access-mcp-output-schemas.js +1 -1
- package/dist/access-mcp.js +69 -62
- package/dist/access-namespace-preflight.js +56 -49
- package/dist/access-observe-write-category.js +20 -13
- package/dist/access-observe-write-surface.js +56 -49
- package/dist/access-offline-manifest.js +16 -10
- package/dist/access-operations-batch.js +60 -53
- package/dist/access-operations.js +67 -60
- package/dist/access-recall-concurrency.js +56 -49
- package/dist/access-recall-response.js +56 -49
- package/dist/access-recall-surface.js +56 -49
- package/dist/access-schema.js +16 -7
- package/dist/access-service.d.ts.map +1 -1
- package/dist/access-service.js +56 -49
- package/dist/access-surface-catalog.js +58 -51
- package/dist/access-surface-catalog.js.map +1 -1
- package/dist/active-recall.js +24 -18
- package/dist/active-recall.js.map +1 -1
- package/dist/{auto-sync-6IPX2BAC.js → auto-sync-U5N3CB5Y.js} +7 -7
- package/dist/briefing.js +11 -11
- package/dist/buffer.js +7 -7
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-GWVG7LGC.js} +2 -2
- package/dist/{capsule-merge-3MNURAQ3.js → capsule-merge-ZCXUPLNJ.js} +2 -2
- package/dist/causal-behavior.js +2 -2
- package/dist/causal-chain.js +2 -2
- package/dist/causal-consolidation.js +14 -14
- package/dist/causal-retrieval.js +2 -2
- package/dist/causal-trajectory-graph.js +2 -2
- package/dist/causal-trajectory.js +1 -1
- package/dist/{chunk-JE334SIA.js → chunk-22DYJLFN.js} +2 -2
- package/dist/{chunk-YTQGGO3K.js → chunk-22LMCQ5B.js} +85 -82
- package/dist/{chunk-YTQGGO3K.js.map → chunk-22LMCQ5B.js.map} +1 -1
- package/dist/{chunk-JGSQ6P4D.js → chunk-2FX374UI.js} +2 -2
- package/dist/{chunk-SY5KDWGU.js → chunk-2WLMWP2T.js} +2 -2
- package/dist/{chunk-6TT6HRE4.js → chunk-3AGHSQPM.js} +2 -2
- package/dist/{chunk-P6WFK4ZJ.js → chunk-3O5BB45T.js} +4 -4
- package/dist/{chunk-3C3J243X.js → chunk-3QKO4QN7.js} +3 -3
- package/dist/{chunk-KFCMMDHH.js → chunk-3YD44CH2.js} +2 -2
- package/dist/{chunk-FRCESVV6.js → chunk-4HUCP4R4.js} +18 -18
- package/dist/chunk-4HUCP4R4.js.map +1 -0
- package/dist/chunk-4NSLQ7EU.js +137 -0
- package/dist/chunk-4NSLQ7EU.js.map +1 -0
- package/dist/{chunk-7DE2NOON.js → chunk-4O4XCBOS.js} +3 -3
- package/dist/{chunk-GDYU6VF4.js → chunk-5AYUHSAU.js} +2 -2
- package/dist/{chunk-ICZQG4XE.js → chunk-5FAGCOZD.js} +4 -4
- package/dist/{chunk-VKQI47DJ.js → chunk-5FNZ3G6X.js} +4 -4
- package/dist/{chunk-CK5KOVHW.js → chunk-5R627MYY.js} +3 -2
- package/dist/{chunk-CK5KOVHW.js.map → chunk-5R627MYY.js.map} +1 -1
- package/dist/{chunk-VQNONC4E.js → chunk-5STPRPE7.js} +2 -2
- package/dist/{chunk-52II7PB6.js → chunk-645WOU5I.js} +4 -4
- package/dist/{chunk-SRZW7FNU.js → chunk-64ZUALXK.js} +2 -2
- package/dist/{chunk-4BZP2B4U.js → chunk-6MNKDTDI.js} +2 -2
- package/dist/{chunk-DUJYRVUD.js → chunk-6O2VUWV6.js} +3 -3
- package/dist/chunk-75PT4N7Q.js +75 -0
- package/dist/chunk-75PT4N7Q.js.map +1 -0
- package/dist/{chunk-HESDNYRB.js → chunk-76M5OK7V.js} +2 -2
- package/dist/{chunk-FAN73COB.js → chunk-7D7OUO5W.js} +2 -2
- package/dist/{chunk-DZ525SNQ.js → chunk-7QE46QXI.js} +1 -1
- package/dist/{chunk-VX6GCKBS.js → chunk-7WBSMXGC.js} +4 -4
- package/dist/{chunk-VVDWZRZG.js → chunk-A6RVIHHK.js} +3 -3
- package/dist/{chunk-M536WNTQ.js → chunk-AEJ3TE5H.js} +2 -2
- package/dist/{chunk-72JNNJJJ.js → chunk-AXC4WSF3.js} +3 -3
- package/dist/chunk-AXGJMDBS.js +684 -0
- package/dist/chunk-AXGJMDBS.js.map +1 -0
- package/dist/{chunk-UA5DX5C3.js → chunk-AZIOEOK4.js} +2 -2
- package/dist/{chunk-N4S4NUX5.js → chunk-B3O3IRZN.js} +1 -1
- package/dist/{chunk-XTYX6V52.js → chunk-BJVO4H5C.js} +2 -2
- package/dist/{chunk-7LCGFUMB.js → chunk-BUKK5SWA.js} +4 -4
- package/dist/{chunk-TM3RXL4G.js → chunk-BUQKSQXL.js} +2 -2
- package/dist/chunk-C6HPNPNK.js +64 -0
- package/dist/chunk-C6HPNPNK.js.map +1 -0
- package/dist/{chunk-RB776UH3.js → chunk-C7V3KPKJ.js} +235 -93
- package/dist/chunk-C7V3KPKJ.js.map +1 -0
- package/dist/{chunk-FOH7AMMI.js → chunk-CALBQ2NG.js} +71 -72
- package/dist/chunk-CALBQ2NG.js.map +1 -0
- package/dist/{chunk-37S3IMYB.js → chunk-DGS34QSY.js} +2 -2
- package/dist/{chunk-4IEOGIPB.js → chunk-DO4C4AXD.js} +2 -2
- package/dist/{chunk-SPSRGEDQ.js → chunk-EAQQ2FD3.js} +9 -9
- package/dist/{chunk-3BTFBQLO.js → chunk-EFHNFTWB.js} +49 -3
- package/dist/chunk-EFHNFTWB.js.map +1 -0
- package/dist/{chunk-C3QXITIP.js → chunk-EQUD7FP4.js} +2 -2
- package/dist/chunk-ETUZFFPU.js +218 -0
- package/dist/chunk-ETUZFFPU.js.map +1 -0
- package/dist/{chunk-YNDLCWXS.js → chunk-EZ25VE3G.js} +4 -4
- package/dist/{chunk-YITN5WXI.js → chunk-FC52YWAO.js} +2 -2
- package/dist/{chunk-ZI7QA4MK.js → chunk-FEIXG5FA.js} +2 -2
- package/dist/{chunk-CLXTDOKV.js → chunk-FUNBJQYR.js} +2 -2
- package/dist/{chunk-CLSYE5OZ.js → chunk-G2STTRYB.js} +2 -2
- package/dist/{chunk-TDYAIXHK.js → chunk-H36X4NRH.js} +5 -5
- package/dist/{chunk-3GQC3NMB.js → chunk-H56VY4BY.js} +5 -5
- package/dist/{chunk-PUKTBULQ.js → chunk-HWLIMQE2.js} +2 -2
- package/dist/{chunk-V2ES4WCZ.js → chunk-IIARQTZ2.js} +2 -2
- package/dist/{chunk-QEV26L2F.js → chunk-IKDRGVVJ.js} +2 -2
- package/dist/{chunk-WQZZDN5M.js → chunk-IVEHY2VT.js} +254 -377
- package/dist/chunk-IVEHY2VT.js.map +1 -0
- package/dist/{chunk-LS3XH3UE.js → chunk-IYIBGSHJ.js} +2 -2
- package/dist/{chunk-YSJTCOHR.js → chunk-J27JML5O.js} +2 -2
- package/dist/{chunk-AQK54Q7O.js → chunk-JB56XYHP.js} +2 -2
- package/dist/{chunk-NE6PHPLG.js → chunk-JBH26P62.js} +10 -10
- package/dist/{chunk-CWHV574S.js → chunk-JUT3OR2L.js} +6 -6
- package/dist/{chunk-6ET2VII7.js → chunk-JYG2334B.js} +2 -2
- package/dist/{chunk-LF5IGOOD.js → chunk-JYM4Y6SO.js} +16 -16
- package/dist/{chunk-FB64XE3R.js → chunk-JZ4TJHR2.js} +2 -2
- package/dist/{chunk-EHS5MJEF.js → chunk-KTKHO3UI.js} +3 -3
- package/dist/{chunk-WIKO4SCR.js → chunk-LHRQBMJN.js} +2 -2
- package/dist/{chunk-K4J4E7WV.js → chunk-LO4UKPKX.js} +2 -2
- package/dist/{chunk-JN72EPOE.js → chunk-MAAL6OZV.js} +7 -7
- package/dist/chunk-MAAL6OZV.js.map +1 -0
- package/dist/{chunk-MY2PLXSM.js → chunk-MCTM4YPL.js} +2 -1
- package/dist/{chunk-MY2PLXSM.js.map → chunk-MCTM4YPL.js.map} +1 -1
- package/dist/{chunk-6NQ4LSMS.js → chunk-MGZMC6WN.js} +47 -47
- package/dist/chunk-MSAI3Q7D.js +372 -0
- package/dist/chunk-MSAI3Q7D.js.map +1 -0
- package/dist/{chunk-BMYZTZ5D.js → chunk-MY7B3HYC.js} +2 -2
- package/dist/{chunk-NB3LRY4T.js → chunk-N4PYB235.js} +2 -2
- package/dist/{chunk-S3YM4SZQ.js → chunk-N7QV3CMX.js} +2 -2
- package/dist/{chunk-L6UZEAID.js → chunk-NJVR724O.js} +4 -4
- package/dist/{chunk-VLLD4TOB.js → chunk-NMZVDRVP.js} +2 -2
- package/dist/{chunk-P2WZHLUF.js → chunk-NOL7SEWN.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-6L3PBSAR.js → chunk-PMNLL2TL.js} +2 -2
- package/dist/{chunk-VYCOJKSX.js → chunk-PO7NXNGC.js} +2 -2
- package/dist/{chunk-4IIMMZZK.js → chunk-PULDSMCJ.js} +10 -10
- package/dist/{chunk-G6SVZWPB.js → chunk-PZX3AQLF.js} +44 -1
- package/dist/chunk-PZX3AQLF.js.map +1 -0
- package/dist/{chunk-HBBHL4YF.js → chunk-QOXNTKXB.js} +2 -2
- package/dist/{chunk-CUCWX3MO.js → chunk-QZLCVO2V.js} +2 -2
- package/dist/{chunk-NE7I2RCT.js → chunk-R3X2T2LP.js} +2 -2
- package/dist/{chunk-S7GCT7NU.js → chunk-RBITPCMK.js} +8 -8
- package/dist/{chunk-EVBI7S4J.js → chunk-RMDGAAZF.js} +4 -4
- package/dist/{chunk-BJRAELDH.js → chunk-SCXURCVT.js} +3 -3
- package/dist/{chunk-EQ6YOIKO.js → chunk-ST2RQUXC.js} +3 -3
- package/dist/{chunk-CS52MFDV.js → chunk-SZG4WFAG.js} +2 -2
- package/dist/{chunk-BFO3IR56.js → chunk-TPEZ2SHV.js} +2 -2
- package/dist/chunk-TYT4TEQV.js +45 -0
- package/dist/chunk-TYT4TEQV.js.map +1 -0
- package/dist/{chunk-YRLHEEQT.js → chunk-U6VC6RP3.js} +2 -2
- package/dist/chunk-UCPGOUDZ.js +726 -0
- package/dist/chunk-UCPGOUDZ.js.map +1 -0
- package/dist/{chunk-MOQPEFLN.js → chunk-UP75KL3L.js} +2 -2
- package/dist/{chunk-W4AOR52H.js → chunk-UQDC3W77.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-UVYI6VIX.js} +1 -1
- package/dist/{chunk-D5F7TKMH.js → chunk-V4JZCTMK.js} +2 -2
- package/dist/{chunk-MQJL6CDZ.js → chunk-VA4O3U7U.js} +4 -4
- package/dist/{chunk-4CDYONWO.js → chunk-VDO32NLK.js} +3 -3
- package/dist/{chunk-SOZUJCME.js → chunk-VHDKPGMG.js} +6 -6
- package/dist/{chunk-QWW2JDDX.js → chunk-WNRTXEWW.js} +6 -6
- package/dist/{chunk-NR2ASHBL.js → chunk-XOV7NRY5.js} +2 -2
- package/dist/{chunk-KP7SANCF.js → chunk-YPI4RQWV.js} +2 -2
- package/dist/{chunk-JKMHZF3Q.js → chunk-YSWJXOOL.js} +2 -2
- package/dist/{chunk-LQG5O5EP.js → chunk-Z3O53WZR.js} +2 -2
- package/dist/{chunk-QVS22WTO.js → chunk-ZG5DWWKK.js} +2 -2
- package/dist/cli.js +99 -92
- package/dist/coding/export-okf-codegraph.js +12 -12
- package/dist/coding/pre-action-gate.js +2 -2
- package/dist/compounding/engine.js +12 -12
- package/dist/config.js +24 -18
- package/dist/connectors/codex-materialize-runner.js +11 -11
- package/dist/connectors/index.js +11 -11
- package/dist/console/state.d.ts +30 -0
- package/dist/console/state.d.ts.map +1 -1
- package/dist/contradiction/index.js +5 -5
- package/dist/convergence-refresh.js +13 -13
- package/dist/corpus-watermark.js +13 -13
- package/dist/dashboard-runtime.js +2 -2
- package/dist/deep-recall-http-glue.js +16 -8
- package/dist/embedding-fallback.d.ts +41 -31
- package/dist/embedding-fallback.d.ts.map +1 -1
- package/dist/embedding-fallback.js +9 -1
- package/dist/embedding-generation-lock.d.ts +49 -0
- package/dist/embedding-generation-lock.d.ts.map +1 -0
- package/dist/embedding-generation-lock.js +19 -0
- package/dist/embedding-index-storage.d.ts +276 -0
- package/dist/embedding-index-storage.d.ts.map +1 -0
- package/dist/embedding-index-storage.js +28 -0
- package/dist/entity-retrieval.js +12 -12
- package/dist/event-order-recall.js +3 -3
- package/dist/explicit-capture.js +6 -6
- package/dist/external-wiki-access.js +57 -50
- package/dist/extraction.js +17 -17
- package/dist/{first-start-migration-7OATHJKB.js → first-start-migration-GAT7G733.js} +4 -4
- package/dist/{graph-edge-decay-KQGS3L3B.js → graph-edge-decay-MKIRZ762.js} +5 -5
- package/dist/graph-jsonl.js +3 -3
- package/dist/graph-snapshot.js +3 -3
- package/dist/graph.js +2 -2
- package/dist/harmonic-retrieval.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +195 -164
- package/dist/lcm/engine.js +2 -2
- package/dist/lcm/index.js +5 -5
- package/dist/local-llm.js +2 -2
- package/dist/location/index.js +14 -14
- package/dist/maintenance/memory-governance.js +11 -11
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +11 -11
- package/dist/maintenance/rebuild-memory-projection.js +12 -12
- package/dist/memory-browse-operations.js +57 -50
- package/dist/memory-subject.js +3 -3
- package/dist/namespaces/migrate.js +12 -12
- package/dist/namespaces/storage.js +11 -11
- package/dist/offline-sync-changeset-generations.d.ts +82 -0
- package/dist/offline-sync-changeset-generations.d.ts.map +1 -0
- package/dist/offline-sync-changeset-generations.js +21 -0
- package/dist/offline-sync-embedding-generation.d.ts +257 -0
- package/dist/offline-sync-embedding-generation.d.ts.map +1 -0
- package/dist/offline-sync-embedding-generation.js +44 -0
- package/dist/offline-sync-embedding-generation.js.map +1 -0
- package/dist/offline-sync-exclude-globs.d.ts +15 -0
- package/dist/offline-sync-exclude-globs.d.ts.map +1 -1
- package/dist/offline-sync-exclude-globs.js +11 -3
- package/dist/offline-sync-file-io.d.ts +7 -0
- package/dist/offline-sync-file-io.d.ts.map +1 -1
- package/dist/offline-sync-file-io.js +14 -2
- package/dist/offline-sync-impression-drain.js +3 -3
- package/dist/offline-sync-runtime-state.d.ts +5 -0
- package/dist/offline-sync-runtime-state.d.ts.map +1 -0
- package/dist/offline-sync-runtime-state.js +14 -0
- package/dist/offline-sync-runtime-state.js.map +1 -0
- package/dist/offline-sync-snapshot-stream.d.ts +26 -0
- package/dist/offline-sync-snapshot-stream.d.ts.map +1 -0
- package/dist/offline-sync-snapshot-stream.js +29 -0
- package/dist/offline-sync-snapshot-stream.js.map +1 -0
- package/dist/offline-sync-upload-staging.d.ts +34 -0
- package/dist/offline-sync-upload-staging.d.ts.map +1 -0
- package/dist/offline-sync-upload-staging.js +26 -0
- package/dist/offline-sync-upload-staging.js.map +1 -0
- package/dist/offline-sync.d.ts +58 -28
- package/dist/offline-sync.d.ts.map +1 -1
- package/dist/offline-sync.js +22 -8
- package/dist/operator-doctor-corpus.js +14 -14
- package/dist/operator-toolkit.js +35 -29
- package/dist/orchestration/maintenance.js +22 -22
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +129 -122
- package/dist/recall-navigation-operations.js +57 -50
- package/dist/recall-navigation.js +56 -49
- package/dist/reconcile/manifest.js +4 -4
- package/dist/{resolution-DNX7FO7A.js → resolution-K4A3VPKZ.js} +6 -6
- package/dist/resolution-K4A3VPKZ.js.map +1 -0
- package/dist/resume-bundles.js +25 -19
- package/dist/salvage-envelope.js +5 -5
- package/dist/secure-store/index.js +2 -2
- package/dist/secure-store/secure-fs.d.ts +14 -1
- package/dist/secure-store/secure-fs.d.ts.map +1 -1
- package/dist/semantic-consolidation.js +12 -12
- package/dist/semantic-rule-promotion.js +11 -11
- package/dist/semantic-rule-verifier.js +11 -11
- package/dist/source-agent-qualifier.js +14 -14
- package/dist/standup/build.js +11 -11
- package/dist/{state-ITXJKXRE.js → state-JB47KHYV.js} +2 -2
- package/dist/state-JB47KHYV.js.map +1 -0
- package/dist/storage.d.ts +9 -3
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +10 -10
- package/dist/subject-config.js +4 -4
- package/dist/summarizer.js +3 -3
- package/dist/support-passport/index.js +59 -52
- package/dist/support-passport/offline-sync-guard.d.ts +6 -2
- package/dist/support-passport/offline-sync-guard.d.ts.map +1 -1
- package/dist/{trace-ZFVRH4GO.js → trace-LXXCMZJA.js} +3 -3
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +3 -3
- package/dist/transfer/export-okf.js +12 -12
- package/dist/{tui-55UJMJX7.js → tui-2Z5WOV7L.js} +3 -3
- package/dist/tui-2Z5WOV7L.js.map +1 -0
- package/dist/verified-recall.js +11 -11
- package/dist/who-knows-cli.js +12 -12
- package/dist/who-knows.js +11 -11
- package/dist/write-envelope.js +4 -4
- package/package.json +2 -2
- package/src/access-http-offline-stream.ts +3 -0
- package/src/access-mcp-output-schemas.ts +1 -0
- package/src/access-service.ts +7 -18
- package/src/console/state.test.ts +56 -0
- package/src/console/state.ts +83 -0
- package/src/embedding-fallback.ts +325 -141
- package/src/embedding-generation-lock.ts +141 -0
- package/src/embedding-index-storage.test.ts +245 -0
- package/src/embedding-index-storage.ts +1068 -0
- package/src/index.ts +3 -3
- package/src/offline-sync-changeset-generations.test.ts +962 -0
- package/src/offline-sync-changeset-generations.ts +549 -0
- package/src/offline-sync-embedding-generation.test.ts +2173 -0
- package/src/offline-sync-embedding-generation.ts +1153 -0
- package/src/offline-sync-exclude-globs.ts +112 -0
- package/src/offline-sync-file-io.ts +48 -0
- package/src/offline-sync-runtime-state.ts +48 -0
- package/src/offline-sync-snapshot-stream.ts +99 -0
- package/src/offline-sync-upload-staging.ts +291 -0
- package/src/offline-sync.test.ts +164 -1
- package/src/offline-sync.ts +372 -454
- package/src/orchestrator.ts +2 -1
- package/src/secure-store/secure-fs.ts +26 -6
- package/src/storage.ts +11 -6
- package/src/support-passport/offline-sync-guard.ts +16 -2
- package/dist/chunk-3BTFBQLO.js.map +0 -1
- package/dist/chunk-AREGMX2J.js +0 -50
- package/dist/chunk-AREGMX2J.js.map +0 -1
- package/dist/chunk-FOH7AMMI.js.map +0 -1
- package/dist/chunk-FRCESVV6.js.map +0 -1
- package/dist/chunk-G6SVZWPB.js.map +0 -1
- package/dist/chunk-JN72EPOE.js.map +0 -1
- package/dist/chunk-RB776UH3.js.map +0 -1
- package/dist/chunk-WQZZDN5M.js.map +0 -1
- /package/dist/{auto-sync-6IPX2BAC.js.map → auto-sync-U5N3CB5Y.js.map} +0 -0
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
- /package/dist/{capsule-merge-3MNURAQ3.js.map → capsule-merge-ZCXUPLNJ.js.map} +0 -0
- /package/dist/{chunk-JE334SIA.js.map → chunk-22DYJLFN.js.map} +0 -0
- /package/dist/{chunk-JGSQ6P4D.js.map → chunk-2FX374UI.js.map} +0 -0
- /package/dist/{chunk-SY5KDWGU.js.map → chunk-2WLMWP2T.js.map} +0 -0
- /package/dist/{chunk-6TT6HRE4.js.map → chunk-3AGHSQPM.js.map} +0 -0
- /package/dist/{chunk-P6WFK4ZJ.js.map → chunk-3O5BB45T.js.map} +0 -0
- /package/dist/{chunk-3C3J243X.js.map → chunk-3QKO4QN7.js.map} +0 -0
- /package/dist/{chunk-KFCMMDHH.js.map → chunk-3YD44CH2.js.map} +0 -0
- /package/dist/{chunk-7DE2NOON.js.map → chunk-4O4XCBOS.js.map} +0 -0
- /package/dist/{chunk-GDYU6VF4.js.map → chunk-5AYUHSAU.js.map} +0 -0
- /package/dist/{chunk-ICZQG4XE.js.map → chunk-5FAGCOZD.js.map} +0 -0
- /package/dist/{chunk-VKQI47DJ.js.map → chunk-5FNZ3G6X.js.map} +0 -0
- /package/dist/{chunk-VQNONC4E.js.map → chunk-5STPRPE7.js.map} +0 -0
- /package/dist/{chunk-52II7PB6.js.map → chunk-645WOU5I.js.map} +0 -0
- /package/dist/{chunk-SRZW7FNU.js.map → chunk-64ZUALXK.js.map} +0 -0
- /package/dist/{chunk-4BZP2B4U.js.map → chunk-6MNKDTDI.js.map} +0 -0
- /package/dist/{chunk-DUJYRVUD.js.map → chunk-6O2VUWV6.js.map} +0 -0
- /package/dist/{chunk-HESDNYRB.js.map → chunk-76M5OK7V.js.map} +0 -0
- /package/dist/{chunk-FAN73COB.js.map → chunk-7D7OUO5W.js.map} +0 -0
- /package/dist/{chunk-DZ525SNQ.js.map → chunk-7QE46QXI.js.map} +0 -0
- /package/dist/{chunk-VX6GCKBS.js.map → chunk-7WBSMXGC.js.map} +0 -0
- /package/dist/{chunk-VVDWZRZG.js.map → chunk-A6RVIHHK.js.map} +0 -0
- /package/dist/{chunk-M536WNTQ.js.map → chunk-AEJ3TE5H.js.map} +0 -0
- /package/dist/{chunk-72JNNJJJ.js.map → chunk-AXC4WSF3.js.map} +0 -0
- /package/dist/{chunk-UA5DX5C3.js.map → chunk-AZIOEOK4.js.map} +0 -0
- /package/dist/{chunk-N4S4NUX5.js.map → chunk-B3O3IRZN.js.map} +0 -0
- /package/dist/{chunk-XTYX6V52.js.map → chunk-BJVO4H5C.js.map} +0 -0
- /package/dist/{chunk-7LCGFUMB.js.map → chunk-BUKK5SWA.js.map} +0 -0
- /package/dist/{chunk-TM3RXL4G.js.map → chunk-BUQKSQXL.js.map} +0 -0
- /package/dist/{chunk-37S3IMYB.js.map → chunk-DGS34QSY.js.map} +0 -0
- /package/dist/{chunk-4IEOGIPB.js.map → chunk-DO4C4AXD.js.map} +0 -0
- /package/dist/{chunk-SPSRGEDQ.js.map → chunk-EAQQ2FD3.js.map} +0 -0
- /package/dist/{chunk-C3QXITIP.js.map → chunk-EQUD7FP4.js.map} +0 -0
- /package/dist/{chunk-YNDLCWXS.js.map → chunk-EZ25VE3G.js.map} +0 -0
- /package/dist/{chunk-YITN5WXI.js.map → chunk-FC52YWAO.js.map} +0 -0
- /package/dist/{chunk-ZI7QA4MK.js.map → chunk-FEIXG5FA.js.map} +0 -0
- /package/dist/{chunk-CLXTDOKV.js.map → chunk-FUNBJQYR.js.map} +0 -0
- /package/dist/{chunk-CLSYE5OZ.js.map → chunk-G2STTRYB.js.map} +0 -0
- /package/dist/{chunk-TDYAIXHK.js.map → chunk-H36X4NRH.js.map} +0 -0
- /package/dist/{chunk-3GQC3NMB.js.map → chunk-H56VY4BY.js.map} +0 -0
- /package/dist/{chunk-PUKTBULQ.js.map → chunk-HWLIMQE2.js.map} +0 -0
- /package/dist/{chunk-V2ES4WCZ.js.map → chunk-IIARQTZ2.js.map} +0 -0
- /package/dist/{chunk-QEV26L2F.js.map → chunk-IKDRGVVJ.js.map} +0 -0
- /package/dist/{chunk-LS3XH3UE.js.map → chunk-IYIBGSHJ.js.map} +0 -0
- /package/dist/{chunk-YSJTCOHR.js.map → chunk-J27JML5O.js.map} +0 -0
- /package/dist/{chunk-AQK54Q7O.js.map → chunk-JB56XYHP.js.map} +0 -0
- /package/dist/{chunk-NE6PHPLG.js.map → chunk-JBH26P62.js.map} +0 -0
- /package/dist/{chunk-CWHV574S.js.map → chunk-JUT3OR2L.js.map} +0 -0
- /package/dist/{chunk-6ET2VII7.js.map → chunk-JYG2334B.js.map} +0 -0
- /package/dist/{chunk-LF5IGOOD.js.map → chunk-JYM4Y6SO.js.map} +0 -0
- /package/dist/{chunk-FB64XE3R.js.map → chunk-JZ4TJHR2.js.map} +0 -0
- /package/dist/{chunk-EHS5MJEF.js.map → chunk-KTKHO3UI.js.map} +0 -0
- /package/dist/{chunk-WIKO4SCR.js.map → chunk-LHRQBMJN.js.map} +0 -0
- /package/dist/{chunk-K4J4E7WV.js.map → chunk-LO4UKPKX.js.map} +0 -0
- /package/dist/{chunk-6NQ4LSMS.js.map → chunk-MGZMC6WN.js.map} +0 -0
- /package/dist/{chunk-BMYZTZ5D.js.map → chunk-MY7B3HYC.js.map} +0 -0
- /package/dist/{chunk-NB3LRY4T.js.map → chunk-N4PYB235.js.map} +0 -0
- /package/dist/{chunk-S3YM4SZQ.js.map → chunk-N7QV3CMX.js.map} +0 -0
- /package/dist/{chunk-L6UZEAID.js.map → chunk-NJVR724O.js.map} +0 -0
- /package/dist/{chunk-VLLD4TOB.js.map → chunk-NMZVDRVP.js.map} +0 -0
- /package/dist/{chunk-P2WZHLUF.js.map → chunk-NOL7SEWN.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-OWHERGF2.js.map} +0 -0
- /package/dist/{chunk-6L3PBSAR.js.map → chunk-PMNLL2TL.js.map} +0 -0
- /package/dist/{chunk-VYCOJKSX.js.map → chunk-PO7NXNGC.js.map} +0 -0
- /package/dist/{chunk-4IIMMZZK.js.map → chunk-PULDSMCJ.js.map} +0 -0
- /package/dist/{chunk-HBBHL4YF.js.map → chunk-QOXNTKXB.js.map} +0 -0
- /package/dist/{chunk-CUCWX3MO.js.map → chunk-QZLCVO2V.js.map} +0 -0
- /package/dist/{chunk-NE7I2RCT.js.map → chunk-R3X2T2LP.js.map} +0 -0
- /package/dist/{chunk-S7GCT7NU.js.map → chunk-RBITPCMK.js.map} +0 -0
- /package/dist/{chunk-EVBI7S4J.js.map → chunk-RMDGAAZF.js.map} +0 -0
- /package/dist/{chunk-BJRAELDH.js.map → chunk-SCXURCVT.js.map} +0 -0
- /package/dist/{chunk-EQ6YOIKO.js.map → chunk-ST2RQUXC.js.map} +0 -0
- /package/dist/{chunk-CS52MFDV.js.map → chunk-SZG4WFAG.js.map} +0 -0
- /package/dist/{chunk-BFO3IR56.js.map → chunk-TPEZ2SHV.js.map} +0 -0
- /package/dist/{chunk-YRLHEEQT.js.map → chunk-U6VC6RP3.js.map} +0 -0
- /package/dist/{chunk-MOQPEFLN.js.map → chunk-UP75KL3L.js.map} +0 -0
- /package/dist/{chunk-W4AOR52H.js.map → chunk-UQDC3W77.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-UVYI6VIX.js.map} +0 -0
- /package/dist/{chunk-D5F7TKMH.js.map → chunk-V4JZCTMK.js.map} +0 -0
- /package/dist/{chunk-MQJL6CDZ.js.map → chunk-VA4O3U7U.js.map} +0 -0
- /package/dist/{chunk-4CDYONWO.js.map → chunk-VDO32NLK.js.map} +0 -0
- /package/dist/{chunk-SOZUJCME.js.map → chunk-VHDKPGMG.js.map} +0 -0
- /package/dist/{chunk-QWW2JDDX.js.map → chunk-WNRTXEWW.js.map} +0 -0
- /package/dist/{chunk-NR2ASHBL.js.map → chunk-XOV7NRY5.js.map} +0 -0
- /package/dist/{chunk-KP7SANCF.js.map → chunk-YPI4RQWV.js.map} +0 -0
- /package/dist/{chunk-JKMHZF3Q.js.map → chunk-YSWJXOOL.js.map} +0 -0
- /package/dist/{chunk-LQG5O5EP.js.map → chunk-Z3O53WZR.js.map} +0 -0
- /package/dist/{chunk-QVS22WTO.js.map → chunk-ZG5DWWKK.js.map} +0 -0
- /package/dist/{graph-edge-decay-KQGS3L3B.js.map → embedding-generation-lock.js.map} +0 -0
- /package/dist/{resolution-DNX7FO7A.js.map → embedding-index-storage.js.map} +0 -0
- /package/dist/{first-start-migration-7OATHJKB.js.map → first-start-migration-GAT7G733.js.map} +0 -0
- /package/dist/{state-ITXJKXRE.js.map → graph-edge-decay-MKIRZ762.js.map} +0 -0
- /package/dist/{tui-55UJMJX7.js.map → offline-sync-changeset-generations.js.map} +0 -0
- /package/dist/{trace-ZFVRH4GO.js.map → trace-LXXCMZJA.js.map} +0 -0
|
@@ -55,4 +55,116 @@ export const DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS: readonly string[] = [
|
|
|
55
55
|
// a crash-orphan can never be enumerated into a snapshot; the leading `**/`
|
|
56
56
|
// matches the root-level dir (zero segments) and any nested placement.
|
|
57
57
|
"**/.remnic-offline-decrypt-*/**",
|
|
58
|
+
// Embedding-index transaction leftovers (issue #3146, PR #3148). The
|
|
59
|
+
// identity-replacement backup (`embeddings.pre-replace.tmp`) can hold a
|
|
60
|
+
// full obsolete generation and `embeddings.staging.tmp-*` holds a
|
|
61
|
+
// partially staged one; both are node-local recovery state that must
|
|
62
|
+
// never be enumerated into a snapshot. The demoted legacy recovery file
|
|
63
|
+
// (`embeddings.json.pre-migration.tmp-*`) is likewise node-local.
|
|
64
|
+
"**/state/embeddings.pre-replace.tmp/**",
|
|
65
|
+
"**/state/embeddings.staging.tmp-*/**",
|
|
66
|
+
"**/state/embeddings.json.pre-migration.tmp-*",
|
|
67
|
+
"**/namespaces/*/state/embeddings.pre-replace.tmp/**",
|
|
68
|
+
"**/namespaces/*/state/embeddings.staging.tmp-*/**",
|
|
69
|
+
"**/namespaces/*/state/embeddings.json.pre-migration.tmp-*",
|
|
58
70
|
];
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
const DEFAULT_OFFLINE_SYNC_EXCLUDE_REGEXPS: readonly RegExp[] =
|
|
74
|
+
DEFAULT_OFFLINE_SYNC_EXCLUDE_GLOBS.map((glob) => globToRegExp(glob));
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Precompiled-once default-exclude check for the snapshot enumeration hot
|
|
78
|
+
* path (Kilo review, PR #1793). Moved here from offline-sync.ts with the
|
|
79
|
+
* glob list so the two can never drift (issue #1995).
|
|
80
|
+
*/
|
|
81
|
+
export function matchesOfflineSyncDefaultExclude(relPosix: string): boolean {
|
|
82
|
+
for (const regexp of DEFAULT_OFFLINE_SYNC_EXCLUDE_REGEXPS) {
|
|
83
|
+
if (regexp.test(relPosix)) return true;
|
|
84
|
+
}
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function globToRegExp(glob: string): RegExp {
|
|
89
|
+
if (typeof glob !== "string" || glob.length === 0) {
|
|
90
|
+
throw new Error("offlineSyncExcludes entry must be a non-empty string");
|
|
91
|
+
}
|
|
92
|
+
if (glob.includes("\0")) {
|
|
93
|
+
throw new Error("offlineSyncExcludes entry must not contain NUL bytes");
|
|
94
|
+
}
|
|
95
|
+
let source = "";
|
|
96
|
+
for (let i = 0; i < glob.length; i += 1) {
|
|
97
|
+
const ch = glob[i];
|
|
98
|
+
if (ch === "*") {
|
|
99
|
+
if (glob[i + 1] === "*") {
|
|
100
|
+
// `**` is cross-segment wherever it appears:
|
|
101
|
+
// leading `**/` -> zero or more whole segments
|
|
102
|
+
// `/**` at end -> everything under the directory
|
|
103
|
+
// `a/**/b` -> any depth between segments
|
|
104
|
+
// (Cursor review on PR #1793: trailing `scratch/**` must match
|
|
105
|
+
// nested `scratch/a/b.md`, matching the offline-mode guide.)
|
|
106
|
+
if (glob[i + 2] === "/") {
|
|
107
|
+
source += "(?:.*/)?";
|
|
108
|
+
i += 2;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
source += ".*";
|
|
112
|
+
i += 1;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
source += "[^/]*";
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (ch === "?") {
|
|
119
|
+
source += "[^/]";
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (ch === "/") {
|
|
123
|
+
source += "/";
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
source += ch.replace(/[\\^$.+()|{}\[\]]/g, "\\$&");
|
|
127
|
+
}
|
|
128
|
+
return new RegExp(`^${source}$`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function compileOfflineSyncExcludeGlobs(
|
|
132
|
+
globs: readonly unknown[],
|
|
133
|
+
): RegExp[] {
|
|
134
|
+
const out: RegExp[] = [];
|
|
135
|
+
for (const entry of globs) {
|
|
136
|
+
if (typeof entry !== "string" || entry.length === 0) {
|
|
137
|
+
throw new Error("offlineSyncExcludes must contain only non-empty strings");
|
|
138
|
+
}
|
|
139
|
+
out.push(globToRegExp(entry));
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Validate the operator-supplied offline-sync exclude list (#1786).
|
|
146
|
+
* Rejects loudly instead of silently defaulting (CLAUDE.md rule 39):
|
|
147
|
+
* a misspelled key value must fail config parse, not be ignored.
|
|
148
|
+
* Lives next to the glob compiler so config.ts only carries the call.
|
|
149
|
+
*/
|
|
150
|
+
export function parseOfflineSyncExcludes(raw: unknown): string[] {
|
|
151
|
+
if (raw === undefined || raw === null) return [];
|
|
152
|
+
if (!Array.isArray(raw)) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`offlineSyncExcludes must be an array of non-empty glob strings; got ${typeof raw}`,
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
for (const entry of raw) {
|
|
158
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
159
|
+
throw new Error(
|
|
160
|
+
"offlineSyncExcludes must contain only non-empty glob strings",
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const globs = raw.map((entry) => (entry as string).trim());
|
|
165
|
+
// Compile-check every glob now so a bad pattern fails at parse time
|
|
166
|
+
// rather than mid-sync. compileOfflineSyncExcludeGlobs throws with a
|
|
167
|
+
// per-entry message.
|
|
168
|
+
compileOfflineSyncExcludeGlobs(globs);
|
|
169
|
+
return globs;
|
|
170
|
+
}
|
|
@@ -76,3 +76,51 @@ export async function plainFileDigest(filePath: string): Promise<string> {
|
|
|
76
76
|
}
|
|
77
77
|
return hash.digest("hex");
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
import { lstat, utimes } from "node:fs/promises";
|
|
81
|
+
import { isCensusMtimeMs } from "./census-validation.js";
|
|
82
|
+
import { resolveSafeArchiveTarget, type SafeArchiveRoot } from "./transfer/fs-utils.js";
|
|
83
|
+
|
|
84
|
+
/** Sync-internal directory spooling partial uploads and coordination state. */
|
|
85
|
+
export const SYNC_INTERNAL_DIR = ".offline-sync";
|
|
86
|
+
|
|
87
|
+
export const OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS = 1_000;
|
|
88
|
+
|
|
89
|
+
export function assertNonNegativeFinite(value: unknown, field: string): number {
|
|
90
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
91
|
+
throw new Error(`${field} must be a non-negative finite number`);
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function assertOfflineSyncMtimeMs(value: unknown, field: string): number {
|
|
97
|
+
const mtimeMs = assertNonNegativeFinite(value, field);
|
|
98
|
+
if (!isCensusMtimeMs(mtimeMs)) {
|
|
99
|
+
throw new Error(`${field} must be within JavaScript Date range`);
|
|
100
|
+
}
|
|
101
|
+
return mtimeMs;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function setSafeFileMtime(
|
|
105
|
+
root: SafeArchiveRoot,
|
|
106
|
+
relPath: string,
|
|
107
|
+
mtimeMs: number | undefined,
|
|
108
|
+
): Promise<boolean> {
|
|
109
|
+
if (mtimeMs === undefined) return true;
|
|
110
|
+
const target = await resolveSafeArchiveTarget(root, relPath);
|
|
111
|
+
const targetStat = await lstat(target).catch((error: unknown) => {
|
|
112
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
113
|
+
throw error;
|
|
114
|
+
});
|
|
115
|
+
if (!targetStat) return false;
|
|
116
|
+
if (targetStat.isSymbolicLink()) {
|
|
117
|
+
throw new Error(`offline sync target is a symlink: ${relPath}`);
|
|
118
|
+
}
|
|
119
|
+
const targetMtimeMs = assertOfflineSyncMtimeMs(mtimeMs, "mtimeMs");
|
|
120
|
+
if (Math.abs(targetStat.mtimeMs - targetMtimeMs) <= OFFLINE_SYNC_FAST_BASE_MTIME_TOLERANCE_MS) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const mtime = new Date(targetMtimeMs);
|
|
124
|
+
await utimes(target, mtime, mtime);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Runtime-state generation rules for offline sync (issue #1786, #3146).
|
|
2
|
+
// Extracted from offline-sync.ts so the god-file line-count ratchet does not
|
|
3
|
+
// grow when the rules gain entries; behavior is unchanged.
|
|
4
|
+
|
|
5
|
+
export const EMBEDDING_SHARD_FILE_PATTERN = /^shard-\d{4}\.json$/;
|
|
6
|
+
|
|
7
|
+
export function isCanonicalRuntimeStatePath(parts: string[]): boolean {
|
|
8
|
+
if (parts[0] === "state") return true;
|
|
9
|
+
return parts[0] === "namespaces" && parts.length >= 4 && parts[2] === "state";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const REMOTE_AUTHORITATIVE_RUNTIME_STATE_FILES = new Set([
|
|
13
|
+
".artifact-write-version.log",
|
|
14
|
+
".memory-status-version.log",
|
|
15
|
+
"buffer.json",
|
|
16
|
+
"embeddings.json",
|
|
17
|
+
"index_time.json",
|
|
18
|
+
"last_intent.json",
|
|
19
|
+
"last_qmd_recall.json",
|
|
20
|
+
"last_recall.json",
|
|
21
|
+
"lcm.sqlite-shm",
|
|
22
|
+
"lcm.sqlite-wal",
|
|
23
|
+
"memory-lifecycle-ledger.jsonl",
|
|
24
|
+
"recall_impressions.jsonl",
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const ABSENT_INCOMING_RUNTIME_DELETE_FILES = new Set([
|
|
28
|
+
"lcm.sqlite-shm",
|
|
29
|
+
"lcm.sqlite-wal",
|
|
30
|
+
]);
|
|
31
|
+
|
|
32
|
+
// Sharded embedding index (#3146): remote-authoritative like embeddings.json.
|
|
33
|
+
function isRemoteAuthoritativeRuntimeShard(basename: string, parts: string[]): boolean {
|
|
34
|
+
return EMBEDDING_SHARD_FILE_PATTERN.test(basename) && parts[parts.length - 2] === "embeddings";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function shouldPreferIncomingOfflineRuntimeFile(relPosix: string): boolean {
|
|
38
|
+
const parts = relPosix.split("/");
|
|
39
|
+
const basename = parts[parts.length - 1] ?? "";
|
|
40
|
+
return isCanonicalRuntimeStatePath(parts) &&
|
|
41
|
+
(REMOTE_AUTHORITATIVE_RUNTIME_STATE_FILES.has(basename) || isRemoteAuthoritativeRuntimeShard(basename, parts));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function shouldDeleteAbsentIncomingOfflineRuntimeFile(relPosix: string): boolean {
|
|
45
|
+
const parts = relPosix.split("/");
|
|
46
|
+
const basename = parts[parts.length - 1] ?? "";
|
|
47
|
+
return isCanonicalRuntimeStatePath(parts) && ABSENT_INCOMING_RUNTIME_DELETE_FILES.has(basename);
|
|
48
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Streaming offline-sync snapshot builder (PR #3148 round 5). Extracted from
|
|
2
|
+
// access-service.ts so the god-file line-count ratchet does not grow: the
|
|
3
|
+
// stream carries whole-generation omission metadata alongside the file walk,
|
|
4
|
+
// so a filtered embedding generation is announced instead of looking deleted.
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import type {
|
|
7
|
+
OfflineSyncDeletionRevision,
|
|
8
|
+
OfflineSyncFileRecord,
|
|
9
|
+
OfflineSyncSnapshot,
|
|
10
|
+
} from "./offline-sync.js";
|
|
11
|
+
import {
|
|
12
|
+
filterOfflineSyncDeletionRevisions,
|
|
13
|
+
OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
14
|
+
iterateOfflineSyncSnapshotFileRecords,
|
|
15
|
+
} from "./offline-sync.js";
|
|
16
|
+
import {
|
|
17
|
+
computeOmittedEmbeddingGenerationPaths,
|
|
18
|
+
embeddingGenerationMembership,
|
|
19
|
+
} from "./offline-sync-embedding-generation.js";
|
|
20
|
+
import type { OfflineSyncExcludeFile, OfflineSyncFileTarget } from "./offline-sync-file-io.js";
|
|
21
|
+
import { matchesOfflineSyncDefaultExclude } from "./offline-sync-exclude-globs.js";
|
|
22
|
+
import type { OfflineSyncFileDigest } from "./offline-sync.js";
|
|
23
|
+
|
|
24
|
+
export interface OfflineSyncSnapshotStreamBuildOptions {
|
|
25
|
+
root: string;
|
|
26
|
+
namespace: string;
|
|
27
|
+
includeContent: boolean;
|
|
28
|
+
includeTranscripts: boolean;
|
|
29
|
+
userExcludeRegexps?: readonly RegExp[];
|
|
30
|
+
excludeFile: OfflineSyncExcludeFile;
|
|
31
|
+
readFile: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
32
|
+
readFileDigest: (target: OfflineSyncFileTarget) => Promise<OfflineSyncFileDigest>;
|
|
33
|
+
deletions: readonly OfflineSyncDeletionRevision[];
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type OfflineSyncSnapshotStreamBuild = Omit<OfflineSyncSnapshot, "files"> & {
|
|
38
|
+
namespace: string;
|
|
39
|
+
files: AsyncIterable<OfflineSyncFileRecord>;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Build the streamed snapshot response: whole-generation omission computed
|
|
44
|
+
* with the SAME filters the walk uses and announced in the header, so the
|
|
45
|
+
* receiver never interprets a filtered generation as deleted.
|
|
46
|
+
*/
|
|
47
|
+
export async function buildEmbeddingAwareSnapshotStream(
|
|
48
|
+
options: OfflineSyncSnapshotStreamBuildOptions,
|
|
49
|
+
): Promise<OfflineSyncSnapshotStreamBuild> {
|
|
50
|
+
const storageHash = createHash("sha256").update(options.root).digest("hex").slice(0, 16);
|
|
51
|
+
// Deletions: the canonical filter (exclusions + stale-tombstone exists
|
|
52
|
+
// check) FIRST, then whole-generation suppression so a filtered generation
|
|
53
|
+
// is never announced as deleted (PR #3148 round 6).
|
|
54
|
+
const deletions = await filterOfflineSyncDeletionRevisions({
|
|
55
|
+
root: options.root,
|
|
56
|
+
deletions: options.deletions,
|
|
57
|
+
includeTranscripts: options.includeTranscripts,
|
|
58
|
+
userExcludeRegexps: options.userExcludeRegexps,
|
|
59
|
+
excludeFile: options.excludeFile,
|
|
60
|
+
});
|
|
61
|
+
const omission = await computeOmittedEmbeddingGenerationPaths({
|
|
62
|
+
rootAbs: options.root,
|
|
63
|
+
userExcludeRegexps: options.userExcludeRegexps,
|
|
64
|
+
excludeFile: options.excludeFile,
|
|
65
|
+
// RAW pre-filter tombstones (see resolvePushEmbeddingGenerationState).
|
|
66
|
+
tombstonedPaths: options.deletions.map((deletion) => deletion.path),
|
|
67
|
+
isExcludedRelPath: (relPath) =>
|
|
68
|
+
matchesOfflineSyncDefaultExclude(relPath) ||
|
|
69
|
+
Boolean(options.userExcludeRegexps?.some((re) => re.test(relPath))),
|
|
70
|
+
});
|
|
71
|
+
const omittedDirs = new Set(omission.omittedDirs);
|
|
72
|
+
const deletionsAfterGenerationSuppression = deletions.filter((deletion) => {
|
|
73
|
+
const membership = embeddingGenerationMembership(deletion.path);
|
|
74
|
+
return !(membership && omittedDirs.has(membership.shardDir));
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
namespace: options.namespace,
|
|
78
|
+
format: OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
79
|
+
schemaVersion: 1,
|
|
80
|
+
createdAt: new Date().toISOString(),
|
|
81
|
+
sourceId: `remnic:${options.namespace}:${storageHash}`,
|
|
82
|
+
includeTranscripts: options.includeTranscripts,
|
|
83
|
+
deletions: deletionsAfterGenerationSuppression,
|
|
84
|
+
...(omission.omittedDirs.length > 0
|
|
85
|
+
? { omittedEmbeddingGenerationDirs: omission.omittedDirs }
|
|
86
|
+
: {}),
|
|
87
|
+
files: iterateOfflineSyncSnapshotFileRecords({
|
|
88
|
+
root: options.root,
|
|
89
|
+
includeContent: options.includeContent,
|
|
90
|
+
includeTranscripts: options.includeTranscripts,
|
|
91
|
+
readFile: options.readFile,
|
|
92
|
+
readFileDigest: options.readFileDigest,
|
|
93
|
+
signal: options.signal,
|
|
94
|
+
userExcludeRegexps: options.userExcludeRegexps,
|
|
95
|
+
excludeFile: options.excludeFile,
|
|
96
|
+
skipEmbeddingGenerationPaths: omission.omittedPaths,
|
|
97
|
+
}),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// Offline-sync upload staging: chunked upload spool under
|
|
2
|
+
// `.offline-sync/uploads/` (issue #2033). Extracted from offline-sync.ts so
|
|
3
|
+
// the god-file line-count ratchet does not grow; behavior is unchanged.
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
import { lstat, mkdir, open, readFile, readdir, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { SYNC_INTERNAL_DIR, setSafeFileMtime } from "./offline-sync-file-io.js";
|
|
8
|
+
import type { OfflineSyncFileTarget } from "./offline-sync-file-io.js";
|
|
9
|
+
import type {
|
|
10
|
+
OfflineSyncFileStagingWriteTarget,
|
|
11
|
+
OfflineSyncFileWriteChunksTarget,
|
|
12
|
+
OfflineSyncFileWriteTarget,
|
|
13
|
+
} from "./offline-sync.js";
|
|
14
|
+
import { type SafeArchiveRoot, resolveSafeArchiveTarget } from "./transfer/fs-utils.js";
|
|
15
|
+
|
|
16
|
+
const OFFLINE_SYNC_UPLOAD_STAGING_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
17
|
+
|
|
18
|
+
export function hashText(value: string): string {
|
|
19
|
+
return createHash("sha256").update(value).digest("hex");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface OfflineUploadStaging {
|
|
23
|
+
kind: "single" | "chunks";
|
|
24
|
+
relPath: string;
|
|
25
|
+
filePath: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function offlineUploadRelPath(options: {
|
|
29
|
+
sourceId: string;
|
|
30
|
+
relPath: string;
|
|
31
|
+
sha256: string;
|
|
32
|
+
bytes: number;
|
|
33
|
+
}): string {
|
|
34
|
+
const key = hashText([options.sourceId, options.relPath, options.sha256, String(options.bytes)].join("\0"));
|
|
35
|
+
return `${SYNC_INTERNAL_DIR}/uploads/${key}.part`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function offlineUploadPath(
|
|
39
|
+
root: SafeArchiveRoot,
|
|
40
|
+
options: {
|
|
41
|
+
sourceId: string;
|
|
42
|
+
relPath: string;
|
|
43
|
+
sha256: string;
|
|
44
|
+
bytes: number;
|
|
45
|
+
}
|
|
46
|
+
): Promise<OfflineUploadStaging> {
|
|
47
|
+
const relPath = offlineUploadRelPath(options);
|
|
48
|
+
return {
|
|
49
|
+
kind: "single",
|
|
50
|
+
relPath,
|
|
51
|
+
filePath: await resolveSafeArchiveTarget(root, relPath),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function offlineUploadChunkPath(
|
|
56
|
+
root: SafeArchiveRoot,
|
|
57
|
+
options: {
|
|
58
|
+
sourceId: string;
|
|
59
|
+
relPath: string;
|
|
60
|
+
sha256: string;
|
|
61
|
+
bytes: number;
|
|
62
|
+
offset: number;
|
|
63
|
+
}
|
|
64
|
+
): Promise<OfflineUploadStaging> {
|
|
65
|
+
const uploadRelPath = offlineUploadRelPath(options);
|
|
66
|
+
const relPath = `${uploadRelPath}/${String(options.offset).padStart(20, "0")}.part`;
|
|
67
|
+
return {
|
|
68
|
+
kind: "chunks",
|
|
69
|
+
relPath,
|
|
70
|
+
filePath: await resolveSafeArchiveTarget(root, relPath),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function writeOfflineUploadChunk(options: {
|
|
75
|
+
root: SafeArchiveRoot;
|
|
76
|
+
sourceId: string;
|
|
77
|
+
relPath: string;
|
|
78
|
+
sha256: string;
|
|
79
|
+
bytes: number;
|
|
80
|
+
offset: number;
|
|
81
|
+
content: Buffer;
|
|
82
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
83
|
+
writeFile?: (target: OfflineSyncFileWriteTarget) => Promise<void>;
|
|
84
|
+
writeStagingFile?: (target: OfflineSyncFileStagingWriteTarget) => Promise<void>;
|
|
85
|
+
}): Promise<OfflineUploadStaging> {
|
|
86
|
+
if ((options.writeFile || options.writeStagingFile) && !options.readFile) {
|
|
87
|
+
throw new Error("offline sync upload chunk storage hooks require readFile");
|
|
88
|
+
}
|
|
89
|
+
const uploadRoot = {
|
|
90
|
+
...(await offlineUploadPath(options.root, options)),
|
|
91
|
+
kind: "chunks" as const,
|
|
92
|
+
};
|
|
93
|
+
if (options.offset === 0) {
|
|
94
|
+
await rm(uploadRoot.filePath, { recursive: true, force: true }).catch(() => {});
|
|
95
|
+
} else {
|
|
96
|
+
const existing = await stat(uploadRoot.filePath).catch((error: unknown) => {
|
|
97
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
98
|
+
throw error;
|
|
99
|
+
});
|
|
100
|
+
if (!existing || !existing.isDirectory()) {
|
|
101
|
+
throw new Error(`offline sync upload is missing initial chunk for ${options.relPath}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const chunk = await offlineUploadChunkPath(options.root, { ...options, offset: options.offset });
|
|
105
|
+
|
|
106
|
+
const writeStagingFile = options.writeStagingFile ?? options.writeFile;
|
|
107
|
+
if (writeStagingFile) {
|
|
108
|
+
// Storage-backed services provide these hooks so secure-store deployments
|
|
109
|
+
// keep staged partial uploads encrypted at rest without mutating indexes.
|
|
110
|
+
await writeOfflineUploadContent({
|
|
111
|
+
root: options.root,
|
|
112
|
+
relPath: chunk.relPath,
|
|
113
|
+
filePath: chunk.filePath,
|
|
114
|
+
content: options.content,
|
|
115
|
+
writeFile: writeStagingFile,
|
|
116
|
+
});
|
|
117
|
+
return uploadRoot;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await mkdir(path.dirname(chunk.filePath), { recursive: true });
|
|
121
|
+
const existingChunk = await lstat(chunk.filePath).catch((error: unknown) => {
|
|
122
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
123
|
+
throw error;
|
|
124
|
+
});
|
|
125
|
+
if (existingChunk?.isSymbolicLink()) {
|
|
126
|
+
throw new Error(`offline sync upload chunk is a symlink: ${chunk.relPath}`);
|
|
127
|
+
}
|
|
128
|
+
await writeFile(chunk.filePath, options.content, { mode: 0o600 });
|
|
129
|
+
return uploadRoot;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function pruneOfflineUploadStaging(root: SafeArchiveRoot): Promise<void> {
|
|
133
|
+
const uploadsRelPath = `${SYNC_INTERNAL_DIR}/uploads`;
|
|
134
|
+
const uploadsPath = await resolveSafeArchiveTarget(root, uploadsRelPath);
|
|
135
|
+
const entries = await readdir(uploadsPath, { withFileTypes: true }).catch((error: unknown) => {
|
|
136
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
137
|
+
throw error;
|
|
138
|
+
});
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
await Promise.all(
|
|
141
|
+
entries.map(async (entry) => {
|
|
142
|
+
if (!/^[a-f0-9]{64}\.part$/i.test(entry.name)) return;
|
|
143
|
+
const relPath = `${uploadsRelPath}/${entry.name}`;
|
|
144
|
+
const filePath = await resolveSafeArchiveTarget(root, relPath);
|
|
145
|
+
const info = await lstat(filePath).catch((error: unknown) => {
|
|
146
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
147
|
+
throw error;
|
|
148
|
+
});
|
|
149
|
+
if (!info) return;
|
|
150
|
+
if (now - info.mtimeMs <= OFFLINE_SYNC_UPLOAD_STAGING_MAX_AGE_MS) return;
|
|
151
|
+
await rm(filePath, { recursive: true, force: true });
|
|
152
|
+
})
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function* readOfflineUploadStagingChunks(options: {
|
|
157
|
+
root: SafeArchiveRoot;
|
|
158
|
+
upload: OfflineUploadStaging;
|
|
159
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
160
|
+
}): AsyncGenerator<Buffer> {
|
|
161
|
+
if (options.upload.kind === "single") {
|
|
162
|
+
yield await readOfflineUploadContent({
|
|
163
|
+
root: options.root,
|
|
164
|
+
relPath: options.upload.relPath,
|
|
165
|
+
filePath: options.upload.filePath,
|
|
166
|
+
readFile: options.readFile,
|
|
167
|
+
});
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const entries = await readdir(options.upload.filePath);
|
|
172
|
+
const chunkNames = entries.filter((entry) => /^\d{20}\.part$/.test(entry)).sort();
|
|
173
|
+
if (chunkNames.length === 0) {
|
|
174
|
+
throw new Error(`offline sync upload is missing chunks for ${options.upload.relPath}`);
|
|
175
|
+
}
|
|
176
|
+
let expectedOffset = 0;
|
|
177
|
+
for (const chunkName of chunkNames) {
|
|
178
|
+
const offset = Number(chunkName.slice(0, 20));
|
|
179
|
+
if (!Number.isSafeInteger(offset) || offset !== expectedOffset) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`offline sync upload offset mismatch for ${options.upload.relPath}: expected ${expectedOffset}, got ${offset}`
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
const relPath = `${options.upload.relPath}/${chunkName}`;
|
|
185
|
+
const filePath = await resolveSafeArchiveTarget(options.root, relPath);
|
|
186
|
+
const content = await readOfflineUploadContent({
|
|
187
|
+
root: options.root,
|
|
188
|
+
relPath,
|
|
189
|
+
filePath,
|
|
190
|
+
readFile: options.readFile,
|
|
191
|
+
});
|
|
192
|
+
expectedOffset += content.length;
|
|
193
|
+
yield content;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function digestOfflineUploadStagingContent(options: {
|
|
198
|
+
root: SafeArchiveRoot;
|
|
199
|
+
upload: OfflineUploadStaging;
|
|
200
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
201
|
+
}): Promise<{ sha256: string; bytes: number }> {
|
|
202
|
+
const hash = createHash("sha256");
|
|
203
|
+
let bytes = 0;
|
|
204
|
+
for await (const chunk of readOfflineUploadStagingChunks(options)) {
|
|
205
|
+
hash.update(chunk);
|
|
206
|
+
bytes += chunk.length;
|
|
207
|
+
}
|
|
208
|
+
return { sha256: hash.digest("hex"), bytes };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function writeSafeFileFromUpload(
|
|
212
|
+
root: SafeArchiveRoot,
|
|
213
|
+
relPath: string,
|
|
214
|
+
upload: OfflineUploadStaging,
|
|
215
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>,
|
|
216
|
+
writeFileChunks?: (target: OfflineSyncFileWriteChunksTarget) => Promise<void>,
|
|
217
|
+
mtimeMs?: number
|
|
218
|
+
): Promise<void> {
|
|
219
|
+
const target = await resolveSafeArchiveTarget(root, relPath);
|
|
220
|
+
const chunks = readOfflineUploadStagingChunks({ root, upload, readFile });
|
|
221
|
+
if (writeFileChunks) {
|
|
222
|
+
await writeFileChunks({ root: root.abs, path: relPath, filePath: target, chunks });
|
|
223
|
+
await setSafeFileMtime(root, relPath, mtimeMs);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
await mkdir(path.dirname(target), { recursive: true });
|
|
228
|
+
const tmp = path.join(path.dirname(target), `.remnic-sync.${process.pid}.${randomUUID()}.tmp`);
|
|
229
|
+
const handle = await open(tmp, "w", 0o600);
|
|
230
|
+
try {
|
|
231
|
+
for await (const chunk of chunks) {
|
|
232
|
+
if (chunk.length > 0) await handle.write(chunk);
|
|
233
|
+
}
|
|
234
|
+
await handle.close();
|
|
235
|
+
const targetStat = await lstat(target).catch((error: unknown) => {
|
|
236
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return null;
|
|
237
|
+
throw error;
|
|
238
|
+
});
|
|
239
|
+
if (targetStat?.isSymbolicLink()) {
|
|
240
|
+
throw new Error(`offline sync target is a symlink: ${relPath}`);
|
|
241
|
+
}
|
|
242
|
+
await rename(tmp, target);
|
|
243
|
+
await setSafeFileMtime(root, relPath, mtimeMs);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
await handle.close().catch(() => {});
|
|
246
|
+
await unlink(tmp).catch(() => {});
|
|
247
|
+
throw error;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function cleanupOfflineUpload(upload: OfflineUploadStaging): Promise<void> {
|
|
252
|
+
if (upload.kind === "chunks") {
|
|
253
|
+
await rm(upload.filePath, { recursive: true, force: true });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
await unlink(upload.filePath).catch((error: unknown) => {
|
|
257
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") return;
|
|
258
|
+
throw error;
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function readOfflineUploadContent(options: {
|
|
263
|
+
root: SafeArchiveRoot;
|
|
264
|
+
relPath: string;
|
|
265
|
+
filePath: string;
|
|
266
|
+
readFile?: (target: OfflineSyncFileTarget) => Promise<Buffer>;
|
|
267
|
+
}): Promise<Buffer> {
|
|
268
|
+
if (options.readFile) {
|
|
269
|
+
return options.readFile({
|
|
270
|
+
root: options.root.abs,
|
|
271
|
+
path: options.relPath,
|
|
272
|
+
filePath: options.filePath,
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return readFile(options.filePath);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async function writeOfflineUploadContent(options: {
|
|
279
|
+
root: SafeArchiveRoot;
|
|
280
|
+
relPath: string;
|
|
281
|
+
filePath: string;
|
|
282
|
+
content: Buffer;
|
|
283
|
+
writeFile: (target: OfflineSyncFileWriteTarget) => Promise<void>;
|
|
284
|
+
}): Promise<void> {
|
|
285
|
+
await options.writeFile({
|
|
286
|
+
root: options.root.abs,
|
|
287
|
+
path: options.relPath,
|
|
288
|
+
filePath: options.filePath,
|
|
289
|
+
content: options.content,
|
|
290
|
+
});
|
|
291
|
+
}
|