@remnic/core 9.29.1 → 9.30.0
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.d.ts +11 -11
- package/dist/access-admin-ops-surface.js +19 -18
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +20 -19
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +19 -18
- package/dist/access-citation-namespace.js +2 -2
- package/dist/access-cli.js +45 -44
- package/dist/access-cli.js.map +1 -1
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http.d.ts +11 -11
- package/dist/access-http.js +25 -24
- package/dist/access-identity-continuity-surface.d.ts +8 -8
- package/dist/access-identity-continuity-surface.js +19 -18
- package/dist/access-lcm-surface.d.ts +10 -10
- package/dist/access-lcm-surface.js +19 -18
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +23 -22
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-DCXBkDp0.d.ts → access-namespace-preflight-VfXssvoI.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +19 -18
- package/dist/access-observe-write-surface.d.ts +10 -10
- package/dist/access-observe-write-surface.js +19 -18
- package/dist/access-operations-batch.js +20 -19
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +22 -21
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +19 -18
- package/dist/access-recall-response.d.ts +10 -10
- package/dist/access-recall-response.js +19 -18
- package/dist/access-recall-surface.d.ts +10 -10
- package/dist/access-recall-surface.js +19 -18
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-DvPW_M5j.d.ts → access-service-B5RwVsTV.d.ts} +7 -7
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +19 -18
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-wearables-meetings-surface.d.ts +4 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +8 -8
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +10 -9
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +3 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-BIVgftWu.d.ts → catalog-V7inUQdp.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +11 -10
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-FZRQZW4H.js → chunk-2EUVEDF5.js} +2 -2
- package/dist/{chunk-PBUZNGMX.js → chunk-4WKA6I65.js} +20 -20
- package/dist/{chunk-PBUZNGMX.js.map → chunk-4WKA6I65.js.map} +1 -1
- package/dist/{chunk-AIK5W3IW.js → chunk-5TFP4CUL.js} +68 -2
- package/dist/chunk-5TFP4CUL.js.map +1 -0
- package/dist/{chunk-3JNDOAQ2.js → chunk-5W4ZLBEZ.js} +9 -9
- package/dist/{chunk-5ZCUDWD4.js → chunk-62U4J2GN.js} +2 -2
- package/dist/{chunk-IGPEJVBY.js → chunk-66772WBP.js} +2 -2
- package/dist/{chunk-EP5BVCRI.js → chunk-6BNE62PM.js} +2 -2
- package/dist/{chunk-AAP5T6QG.js → chunk-6DNMUYEE.js} +2 -2
- package/dist/{chunk-35DL7IOI.js → chunk-6FUPCJUL.js} +7 -7
- package/dist/{chunk-JP3YPDAW.js → chunk-6U7F52DE.js} +19 -19
- package/dist/{chunk-E4OCKU67.js → chunk-7XC2HX75.js} +2 -2
- package/dist/{chunk-OVRQLAYR.js → chunk-AZ5BTEKX.js} +2 -2
- package/dist/{chunk-2AXMQWCN.js → chunk-CZRYLSSW.js} +3 -3
- package/dist/chunk-FBUE6PD3.js +1769 -0
- package/dist/chunk-FBUE6PD3.js.map +1 -0
- package/dist/{chunk-PTSNLEXK.js → chunk-FDAMS4PK.js} +2 -2
- package/dist/{chunk-WL5EBRDH.js → chunk-FFAZ2XFU.js} +9 -8
- package/dist/chunk-FFAZ2XFU.js.map +1 -0
- package/dist/{chunk-2GXM5N5A.js → chunk-GIBMDSJ2.js} +2 -2
- package/dist/{chunk-E5OBR6CJ.js → chunk-H5CPS5JP.js} +3 -3
- package/dist/{chunk-2D7FEUCK.js → chunk-I5NLEENJ.js} +2 -2
- package/dist/{chunk-DRBXHRXE.js → chunk-IB26HVAJ.js} +2 -2
- package/dist/{chunk-QDKK63Y7.js → chunk-IC6CBHCA.js} +3 -3
- package/dist/{chunk-IAEAFLF2.js → chunk-JCBWCYEJ.js} +2 -2
- package/dist/{chunk-3PDOVUHC.js → chunk-KXB4VXHY.js} +2 -2
- package/dist/{chunk-WU7TTVOD.js → chunk-LBOD4U5B.js} +7 -7
- package/dist/{chunk-UBKEQOTD.js → chunk-LGHKJABJ.js} +3 -3
- package/dist/{chunk-VSURVXKY.js → chunk-M3C2V3PV.js} +10 -10
- package/dist/{chunk-OFHFH655.js → chunk-MJCGLM25.js} +8 -8
- package/dist/{chunk-OYJKSZIQ.js → chunk-NHWB6HG2.js} +2 -2
- package/dist/{chunk-TC3VNKGX.js → chunk-NM2WP3B7.js} +4 -4
- package/dist/{chunk-GAWOEQEQ.js → chunk-P5PXWTW7.js} +2 -2
- package/dist/chunk-PH3ZWL3W.js +786 -0
- package/dist/chunk-PH3ZWL3W.js.map +1 -0
- package/dist/{chunk-FUYEVEX7.js → chunk-PUS63Q3G.js} +24 -22
- package/dist/{chunk-FUYEVEX7.js.map → chunk-PUS63Q3G.js.map} +1 -1
- package/dist/{chunk-2QMKMVHH.js → chunk-QNWXCJ76.js} +2 -2
- package/dist/{chunk-JMQUBRV6.js → chunk-RGIWINDN.js} +340 -771
- package/dist/chunk-RGIWINDN.js.map +1 -0
- package/dist/{chunk-WRFBW2TT.js → chunk-SPJXKAVF.js} +3 -3
- package/dist/{chunk-HKJCMOT6.js → chunk-TH2LSRMG.js} +2 -2
- package/dist/{chunk-CW4AGY3G.js → chunk-VI2JQPNQ.js} +2 -2
- package/dist/{chunk-OP6F6L3S.js → chunk-W5EG6LE3.js} +2 -2
- package/dist/{chunk-DEBHXHJR.js → chunk-WIPEXG6R.js} +2 -2
- package/dist/{chunk-SH6YO3LT.js → chunk-WVO4G37W.js} +2 -2
- package/dist/{chunk-7L45JFPW.js → chunk-YTQNQ4KB.js} +14 -14
- package/dist/{chunk-PKRIQL3V.js → chunk-YTSNAUXN.js} +2 -2
- package/dist/{chunk-5QDRGWT2.js → chunk-ZPSVBSOF.js} +4 -4
- package/dist/{cli-CxoSOiG0.d.ts → cli-C4GbETPC.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +36 -35
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +10 -9
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +3 -3
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +10 -9
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +10 -9
- package/dist/consolidation-provenance-check.d.ts +3 -2
- package/dist/consolidation-undo.d.ts +3 -2
- package/dist/contradiction/index.d.ts +3 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +12 -11
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +10 -9
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +52 -10
- package/dist/explicit-capture.js +13 -1
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{forget-HRPLVB3W.js → forget-4UY2EOKH.js} +3 -3
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +19 -19
- package/dist/index.js +47 -45
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +10 -9
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +10 -9
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +11 -10
- package/dist/{maintenance-Cb-PjxQd.d.ts → maintenance-CL30NWtc.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +3 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +4 -3
- package/dist/namespaces/migrate.js +11 -10
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +10 -9
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +5 -5
- package/dist/offline-sync.js +2 -2
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +13 -12
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +4 -3
- package/dist/operator-toolkit.js +18 -17
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +15 -14
- package/dist/{orchestrator-5GKqb_HL.d.ts → orchestrator-CKTCRwWV.d.ts} +7 -7
- package/dist/orchestrator.d.ts +8 -8
- package/dist/orchestrator.js +45 -44
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-3Qi1TpRo.d.ts → pipeline-B7wj4rRZ.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{purge-YSGX5BOG.js → purge-533FZLS4.js} +16 -9
- package/dist/purge-533FZLS4.js.map +1 -0
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/{qmd-D9JqzzRX.d.ts → qmd-q9tg4dHp.d.ts} +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-state.js +2 -2
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +4 -4
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-C4g6_UdP.d.ts → scope-profiles-CWDyglPM.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/secure-store/index.js +2 -2
- package/dist/{semantic-consolidation-BrxqnzTW.d.ts → semantic-consolidation-BUE3deLn.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +11 -10
- package/dist/semantic-rule-promotion.js +10 -9
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +10 -9
- package/dist/{service-Bi1JgxdP.d.ts → service-CXgM0bQG.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-CcH6yPk2.d.ts → storage-DysJq-XH.d.ts} +163 -27
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +12 -10
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/summary-snapshot.js +2 -2
- package/dist/temporal-supersession.d.ts +3 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +3 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-Dg0w9ADr.d.ts → types-aPPzhnjT.d.ts} +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/utils/serialize-mutations.d.ts +11 -7
- package/dist/utils/serialize-mutations.js +1 -1
- package/dist/verified-recall.js +10 -9
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-observe-write-surface.ts +1 -1
- package/src/access-service-coding-write.test.ts +40 -0
- package/src/explicit-capture.test.ts +1110 -1
- package/src/explicit-capture.ts +534 -135
- package/src/maintenance/purge.test.ts +157 -1
- package/src/maintenance/purge.ts +15 -9
- package/src/secure-store/secure-fs.ts +78 -1
- package/src/storage/content-hash-index.ts +43 -17
- package/src/storage/tombstone-blocked-capture-index.ts +562 -0
- package/src/storage/tombstone-blocked-capture-mutation.ts +284 -0
- package/src/storage/tombstone-blocked-capture-sync.ts +872 -0
- package/src/storage-fact-hash-index-defer.test.ts +2900 -57
- package/src/storage.ts +266 -265
- package/src/testing/subjects/tombstone-blocked-capture.test.ts +181 -0
- package/src/utils/serialize-mutations.test.ts +4 -1
- package/src/utils/serialize-mutations.ts +26 -27
- package/dist/chunk-55MDBZLC.js +0 -490
- package/dist/chunk-55MDBZLC.js.map +0 -1
- package/dist/chunk-AIK5W3IW.js.map +0 -1
- package/dist/chunk-JMQUBRV6.js.map +0 -1
- package/dist/chunk-WL5EBRDH.js.map +0 -1
- package/dist/purge-YSGX5BOG.js.map +0 -1
- /package/dist/{chunk-FZRQZW4H.js.map → chunk-2EUVEDF5.js.map} +0 -0
- /package/dist/{chunk-3JNDOAQ2.js.map → chunk-5W4ZLBEZ.js.map} +0 -0
- /package/dist/{chunk-5ZCUDWD4.js.map → chunk-62U4J2GN.js.map} +0 -0
- /package/dist/{chunk-IGPEJVBY.js.map → chunk-66772WBP.js.map} +0 -0
- /package/dist/{chunk-EP5BVCRI.js.map → chunk-6BNE62PM.js.map} +0 -0
- /package/dist/{chunk-AAP5T6QG.js.map → chunk-6DNMUYEE.js.map} +0 -0
- /package/dist/{chunk-35DL7IOI.js.map → chunk-6FUPCJUL.js.map} +0 -0
- /package/dist/{chunk-JP3YPDAW.js.map → chunk-6U7F52DE.js.map} +0 -0
- /package/dist/{chunk-E4OCKU67.js.map → chunk-7XC2HX75.js.map} +0 -0
- /package/dist/{chunk-OVRQLAYR.js.map → chunk-AZ5BTEKX.js.map} +0 -0
- /package/dist/{chunk-2AXMQWCN.js.map → chunk-CZRYLSSW.js.map} +0 -0
- /package/dist/{chunk-PTSNLEXK.js.map → chunk-FDAMS4PK.js.map} +0 -0
- /package/dist/{chunk-2GXM5N5A.js.map → chunk-GIBMDSJ2.js.map} +0 -0
- /package/dist/{chunk-E5OBR6CJ.js.map → chunk-H5CPS5JP.js.map} +0 -0
- /package/dist/{chunk-2D7FEUCK.js.map → chunk-I5NLEENJ.js.map} +0 -0
- /package/dist/{chunk-DRBXHRXE.js.map → chunk-IB26HVAJ.js.map} +0 -0
- /package/dist/{chunk-QDKK63Y7.js.map → chunk-IC6CBHCA.js.map} +0 -0
- /package/dist/{chunk-IAEAFLF2.js.map → chunk-JCBWCYEJ.js.map} +0 -0
- /package/dist/{chunk-3PDOVUHC.js.map → chunk-KXB4VXHY.js.map} +0 -0
- /package/dist/{chunk-WU7TTVOD.js.map → chunk-LBOD4U5B.js.map} +0 -0
- /package/dist/{chunk-UBKEQOTD.js.map → chunk-LGHKJABJ.js.map} +0 -0
- /package/dist/{chunk-VSURVXKY.js.map → chunk-M3C2V3PV.js.map} +0 -0
- /package/dist/{chunk-OFHFH655.js.map → chunk-MJCGLM25.js.map} +0 -0
- /package/dist/{chunk-OYJKSZIQ.js.map → chunk-NHWB6HG2.js.map} +0 -0
- /package/dist/{chunk-TC3VNKGX.js.map → chunk-NM2WP3B7.js.map} +0 -0
- /package/dist/{chunk-GAWOEQEQ.js.map → chunk-P5PXWTW7.js.map} +0 -0
- /package/dist/{chunk-2QMKMVHH.js.map → chunk-QNWXCJ76.js.map} +0 -0
- /package/dist/{chunk-WRFBW2TT.js.map → chunk-SPJXKAVF.js.map} +0 -0
- /package/dist/{chunk-HKJCMOT6.js.map → chunk-TH2LSRMG.js.map} +0 -0
- /package/dist/{chunk-CW4AGY3G.js.map → chunk-VI2JQPNQ.js.map} +0 -0
- /package/dist/{chunk-OP6F6L3S.js.map → chunk-W5EG6LE3.js.map} +0 -0
- /package/dist/{chunk-DEBHXHJR.js.map → chunk-WIPEXG6R.js.map} +0 -0
- /package/dist/{chunk-SH6YO3LT.js.map → chunk-WVO4G37W.js.map} +0 -0
- /package/dist/{chunk-7L45JFPW.js.map → chunk-YTQNQ4KB.js.map} +0 -0
- /package/dist/{chunk-PKRIQL3V.js.map → chunk-YTSNAUXN.js.map} +0 -0
- /package/dist/{chunk-5QDRGWT2.js.map → chunk-ZPSVBSOF.js.map} +0 -0
- /package/dist/{forget-HRPLVB3W.js.map → forget-4UY2EOKH.js.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { access, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { access, mkdir, mkdtemp, readFile, rm, unlink, writeFile } from "node:fs/promises";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
@@ -9,6 +9,7 @@ import { rebuildMemoryProjection } from "./rebuild-memory-projection.js";
|
|
|
9
9
|
import { readProjectedMemoryBrowse } from "../memory-projection-store.js";
|
|
10
10
|
import { StorageManager } from "../storage.js";
|
|
11
11
|
import type { MemoryFile } from "../types.js";
|
|
12
|
+
import { buildExplicitCaptureDedupKey } from "../storage/tombstone-blocked-capture-index.js";
|
|
12
13
|
|
|
13
14
|
test("purgeMemories records audit errors without blocking hard delete", async () => {
|
|
14
15
|
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-purge-"));
|
|
@@ -38,6 +39,10 @@ test("purgeMemories records audit errors without blocking hard delete", async ()
|
|
|
38
39
|
readAllMemories: async () => [],
|
|
39
40
|
readAllColdMemories: async () => [memory],
|
|
40
41
|
readArchivedMemories: async () => [],
|
|
42
|
+
deleteMemoryForMaintenance: async (candidate: MemoryFile) => {
|
|
43
|
+
await unlink(candidate.path);
|
|
44
|
+
return candidate;
|
|
45
|
+
},
|
|
41
46
|
};
|
|
42
47
|
|
|
43
48
|
const result = await purgeMemories({
|
|
@@ -108,6 +113,10 @@ test("purgeMemories invalidates projection rows for already-absent files but not
|
|
|
108
113
|
dir,
|
|
109
114
|
readAllMemories: async () => [firstMemory, secondMemory],
|
|
110
115
|
readAllColdMemories: async () => [],
|
|
116
|
+
deleteMemoryForMaintenance: async (candidate: MemoryFile) => {
|
|
117
|
+
await unlink(candidate.path);
|
|
118
|
+
return candidate;
|
|
119
|
+
},
|
|
111
120
|
readArchivedMemories: async () => [],
|
|
112
121
|
};
|
|
113
122
|
const future = () => new Date(Date.now() + 86_400_000);
|
|
@@ -130,6 +139,153 @@ test("purgeMemories invalidates projection rows for already-absent files but not
|
|
|
130
139
|
}
|
|
131
140
|
});
|
|
132
141
|
|
|
142
|
+
test("cleanExpiredCommitments coordinates blocked capture deletion", async () => {
|
|
143
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-commitment-delete-lock-"));
|
|
144
|
+
try {
|
|
145
|
+
const storage = new StorageManager(dir);
|
|
146
|
+
await storage.ensureDirectories();
|
|
147
|
+
storage.setTombstonesConfig({
|
|
148
|
+
enabled: true,
|
|
149
|
+
semanticMatch: false,
|
|
150
|
+
semanticThreshold: 0.9,
|
|
151
|
+
namespace: "default",
|
|
152
|
+
});
|
|
153
|
+
const content = "A blocked commitment must not disappear under a concurrent capture.";
|
|
154
|
+
const tombstoneId = await storage.appendTombstone({
|
|
155
|
+
reason: "correction",
|
|
156
|
+
createdBy: "user_correction",
|
|
157
|
+
sourceMemoryId: "commitment-delete-lock",
|
|
158
|
+
rawContent: content,
|
|
159
|
+
});
|
|
160
|
+
assert.ok(tombstoneId);
|
|
161
|
+
const result = await storage.writeMemory("fact", content, {
|
|
162
|
+
source: "test",
|
|
163
|
+
sourceConnector: "provider-a",
|
|
164
|
+
});
|
|
165
|
+
assert.equal(result.tombstoneBlocked, true);
|
|
166
|
+
const blocked = await storage.getMemoryById(result.id);
|
|
167
|
+
assert.ok(blocked);
|
|
168
|
+
assert.equal(
|
|
169
|
+
await storage.writeMemoryFrontmatter(blocked, {
|
|
170
|
+
category: "commitment",
|
|
171
|
+
tags: ["fulfilled"],
|
|
172
|
+
}),
|
|
173
|
+
true,
|
|
174
|
+
);
|
|
175
|
+
const identity = buildExplicitCaptureDedupKey(content, "commitment", "provider-a");
|
|
176
|
+
const release = Promise.withResolvers<void>();
|
|
177
|
+
const entered = Promise.withResolvers<void>();
|
|
178
|
+
const held = storage.withTombstoneBlockedCaptureWriteLock(async () => {
|
|
179
|
+
entered.resolve();
|
|
180
|
+
await release.promise;
|
|
181
|
+
}, identity);
|
|
182
|
+
await entered.promise;
|
|
183
|
+
|
|
184
|
+
let completed = false;
|
|
185
|
+
const pending = storage.cleanExpiredCommitments(-1).then((deleted) => {
|
|
186
|
+
completed = true;
|
|
187
|
+
return deleted;
|
|
188
|
+
});
|
|
189
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
190
|
+
assert.equal(completed, false, "commitment cleanup must wait for the blocked capture identity lock");
|
|
191
|
+
release.resolve();
|
|
192
|
+
await held;
|
|
193
|
+
const deleted = await pending;
|
|
194
|
+
assert.equal(deleted.length, 1);
|
|
195
|
+
assert.equal(deleted[0]?.frontmatter.id, result.id);
|
|
196
|
+
} finally {
|
|
197
|
+
await rm(dir, { recursive: true, force: true });
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("purgeMemories coordinates blocked capture deletion", async () => {
|
|
202
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-purge-delete-lock-"));
|
|
203
|
+
try {
|
|
204
|
+
const storage = new StorageManager(dir);
|
|
205
|
+
await storage.ensureDirectories();
|
|
206
|
+
storage.setTombstonesConfig({
|
|
207
|
+
enabled: true,
|
|
208
|
+
semanticMatch: false,
|
|
209
|
+
semanticThreshold: 0.9,
|
|
210
|
+
namespace: "default",
|
|
211
|
+
});
|
|
212
|
+
const content = "A blocked memory must not disappear under a concurrent capture.";
|
|
213
|
+
const tombstoneId = await storage.appendTombstone({
|
|
214
|
+
reason: "correction",
|
|
215
|
+
createdBy: "user_correction",
|
|
216
|
+
sourceMemoryId: "purge-delete-lock",
|
|
217
|
+
rawContent: content,
|
|
218
|
+
});
|
|
219
|
+
assert.ok(tombstoneId);
|
|
220
|
+
const result = await storage.writeMemory("fact", content, {
|
|
221
|
+
source: "test",
|
|
222
|
+
sourceConnector: "provider-a",
|
|
223
|
+
});
|
|
224
|
+
assert.equal(result.tombstoneBlocked, true);
|
|
225
|
+
const blocked = await storage.getMemoryById(result.id);
|
|
226
|
+
assert.ok(blocked);
|
|
227
|
+
const identity = buildExplicitCaptureDedupKey(content, "fact", "provider-a");
|
|
228
|
+
const release = Promise.withResolvers<void>();
|
|
229
|
+
const entered = Promise.withResolvers<void>();
|
|
230
|
+
const held = storage.withTombstoneBlockedCaptureWriteLock(async () => {
|
|
231
|
+
entered.resolve();
|
|
232
|
+
await release.promise;
|
|
233
|
+
}, identity);
|
|
234
|
+
await entered.promise;
|
|
235
|
+
|
|
236
|
+
let completed = false;
|
|
237
|
+
const pending = purgeMemories({
|
|
238
|
+
storage,
|
|
239
|
+
olderThanMs: 1,
|
|
240
|
+
tier: "all",
|
|
241
|
+
dryRun: false,
|
|
242
|
+
now: () => new Date(Date.now() + 86_400_000),
|
|
243
|
+
}).then((purged) => {
|
|
244
|
+
completed = true;
|
|
245
|
+
return purged;
|
|
246
|
+
});
|
|
247
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
248
|
+
assert.equal(completed, false, "purge must wait for the blocked capture identity lock");
|
|
249
|
+
release.resolve();
|
|
250
|
+
await held;
|
|
251
|
+
const purged = await pending;
|
|
252
|
+
assert.equal(purged.purgedCount, 1);
|
|
253
|
+
assert.equal(await fileExists(blocked.path), false);
|
|
254
|
+
} finally {
|
|
255
|
+
await rm(dir, { recursive: true, force: true });
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("maintenance deletion prefers the candidate path when IDs overlap across tiers", async () => {
|
|
260
|
+
const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-delete-tier-path-"));
|
|
261
|
+
try {
|
|
262
|
+
StorageManager.clearAllStaticCaches();
|
|
263
|
+
const storage = new StorageManager(dir);
|
|
264
|
+
await storage.ensureDirectories();
|
|
265
|
+
const content = "The cold candidate path must win when a hot copy shares its ID.";
|
|
266
|
+
const result = await storage.writeMemory("fact", content, {
|
|
267
|
+
source: "test",
|
|
268
|
+
sourceConnector: "provider-a",
|
|
269
|
+
});
|
|
270
|
+
const hot = await storage.getMemoryById(result.id);
|
|
271
|
+
assert.ok(hot);
|
|
272
|
+
const coldPath = storage.buildTierMemoryPath(hot, "cold");
|
|
273
|
+
await mkdir(path.dirname(coldPath), { recursive: true });
|
|
274
|
+
await writeFile(coldPath, await readFile(hot.path));
|
|
275
|
+
StorageManager.clearAllStaticCaches();
|
|
276
|
+
|
|
277
|
+
const cold = (await storage.readAllColdMemories()).find((memory) => memory.frontmatter.id === result.id);
|
|
278
|
+
assert.ok(cold);
|
|
279
|
+
const removed = await storage.deleteMemoryForMaintenance(cold);
|
|
280
|
+
assert.equal(removed?.path, coldPath);
|
|
281
|
+
assert.equal(await fileExists(coldPath), false);
|
|
282
|
+
assert.equal(await fileExists(hot.path), true);
|
|
283
|
+
} finally {
|
|
284
|
+
StorageManager.clearAllStaticCaches();
|
|
285
|
+
await rm(dir, { recursive: true, force: true });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
|
|
133
289
|
async function fileExists(filePath: string): Promise<boolean> {
|
|
134
290
|
try {
|
|
135
291
|
await access(filePath);
|
package/src/maintenance/purge.ts
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import path from "node:path";
|
|
23
|
-
import { appendFile, mkdir
|
|
23
|
+
import { appendFile, mkdir } from "node:fs/promises";
|
|
24
24
|
import type { StorageManager } from "../storage.js";
|
|
25
25
|
import type { MemoryFile } from "../types.js";
|
|
26
26
|
import { markProjectedMemoryPathInvalid } from "../memory-projection-store.js";
|
|
@@ -168,7 +168,7 @@ export async function purgeMemories(
|
|
|
168
168
|
|
|
169
169
|
// Build candidate list
|
|
170
170
|
const candidates: PurgeCandidate[] = [];
|
|
171
|
-
const
|
|
171
|
+
const candidateMemoriesByPath = new Map<string, MemoryFile>();
|
|
172
172
|
for (const { memory, resolvedTier } of poolEntries) {
|
|
173
173
|
const ts = resolveTimestamp(memory);
|
|
174
174
|
if (ts.length === 0) continue;
|
|
@@ -193,7 +193,7 @@ export async function purgeMemories(
|
|
|
193
193
|
updatedOrCreated: ts,
|
|
194
194
|
ageMs,
|
|
195
195
|
});
|
|
196
|
-
|
|
196
|
+
candidateMemoriesByPath.set(memory.path, memory);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
if (dryRun) {
|
|
@@ -234,13 +234,19 @@ export async function purgeMemories(
|
|
|
234
234
|
|
|
235
235
|
for (const candidate of candidates) {
|
|
236
236
|
try {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
237
|
+
const memory = candidateMemoriesByPath.get(candidate.path);
|
|
238
|
+
if (!memory) throw new Error(`purge candidate memory missing: ${candidate.id}`);
|
|
239
|
+
const removed = await storage.deleteMemoryForMaintenance(memory);
|
|
240
|
+
if (removed) {
|
|
241
|
+
actuallyPurged.push(candidate);
|
|
242
|
+
addCollectionForCandidate(candidate);
|
|
243
|
+
} else {
|
|
244
|
+
alreadyAbsent.push(candidate);
|
|
245
|
+
addCollectionForCandidate(candidate);
|
|
246
|
+
markProjectedMemoryPathInvalid(storage.dir, candidate.id);
|
|
247
|
+
}
|
|
241
248
|
} catch (err) {
|
|
242
249
|
const message = err instanceof Error ? err.message : String(err);
|
|
243
|
-
// ENOENT is fine — already gone
|
|
244
250
|
if (hasErrorCode(err, "ENOENT")) {
|
|
245
251
|
alreadyAbsent.push(candidate);
|
|
246
252
|
addCollectionForCandidate(candidate);
|
|
@@ -302,7 +308,7 @@ export async function purgeMemories(
|
|
|
302
308
|
}
|
|
303
309
|
|
|
304
310
|
const purgedFactMemories = resolvedPurges
|
|
305
|
-
.map((candidate) =>
|
|
311
|
+
.map((candidate) => candidateMemoriesByPath.get(candidate.path))
|
|
306
312
|
.filter((memory): memory is MemoryFile => memory?.frontmatter.category === "fact");
|
|
307
313
|
if (purgedFactMemories.length > 0) {
|
|
308
314
|
const removeFactHashes = (
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
* Naming: `secure-fs.ts` (not `vault-fs.ts`) — see `kdf.ts` naming note.
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
|
-
import { createCipheriv, randomBytes, randomUUID } from "node:crypto";
|
|
46
|
+
import { createCipheriv, createDecipheriv, randomBytes, randomUUID } from "node:crypto";
|
|
47
47
|
import { lstat, mkdir, open as openFile, readFile, readdir, rename, unlink, writeFile } from "node:fs/promises";
|
|
48
48
|
import path from "node:path";
|
|
49
49
|
|
|
@@ -416,6 +416,83 @@ export async function writeMaybeEncryptedFileFromChunks(
|
|
|
416
416
|
}
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
/**
|
|
420
|
+
* Stream a file through the secure-store reader without buffering the whole
|
|
421
|
+
* payload. Plaintext files are passed through unchanged; encrypted files are
|
|
422
|
+
* authenticated and decrypted incrementally.
|
|
423
|
+
*/
|
|
424
|
+
export async function* readMaybeEncryptedFileFromChunks(
|
|
425
|
+
filePath: string,
|
|
426
|
+
key: Buffer | null,
|
|
427
|
+
memoryDir?: string,
|
|
428
|
+
chunkSize = 64 * 1024
|
|
429
|
+
): AsyncIterable<Buffer> {
|
|
430
|
+
const handle = await openFile(filePath, "r");
|
|
431
|
+
const headerSize = MAGIC_HEADER_SIZE + ENVELOPE_HEADER_SIZE;
|
|
432
|
+
try {
|
|
433
|
+
const prefix = Buffer.alloc(headerSize);
|
|
434
|
+
const { bytesRead: prefixBytes } = await handle.read(prefix, 0, prefix.length, 0);
|
|
435
|
+
const prefixView = prefix.subarray(0, prefixBytes);
|
|
436
|
+
if (!isEncryptedFile(prefixView)) {
|
|
437
|
+
if (prefixBytes > 0) yield Buffer.from(prefixView);
|
|
438
|
+
let position = prefixBytes;
|
|
439
|
+
const buffer = Buffer.alloc(chunkSize);
|
|
440
|
+
for (;;) {
|
|
441
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
442
|
+
if (bytesRead === 0) break;
|
|
443
|
+
position += bytesRead;
|
|
444
|
+
yield Buffer.from(buffer.subarray(0, bytesRead));
|
|
445
|
+
}
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
if (prefixBytes < headerSize) {
|
|
449
|
+
throw new Error(`encrypted file header is truncated: ${filePath}`);
|
|
450
|
+
}
|
|
451
|
+
if (key === null) {
|
|
452
|
+
throw new SecureStoreLockedError(
|
|
453
|
+
`secure-store is locked — cannot read encrypted file at ${filePath}. ` +
|
|
454
|
+
"Run `remnic secure-store unlock` to decrypt."
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const envelope = prefix.subarray(MAGIC_HEADER_SIZE);
|
|
458
|
+
const version = envelope.readUInt8(ENVELOPE_LAYOUT.version);
|
|
459
|
+
if (version !== ENVELOPE_VERSION) {
|
|
460
|
+
throw new Error(`secure-store: unsupported envelope version ${version}`);
|
|
461
|
+
}
|
|
462
|
+
const salt = envelope.subarray(
|
|
463
|
+
ENVELOPE_LAYOUT.salt,
|
|
464
|
+
ENVELOPE_LAYOUT.salt + ENVELOPE_SALT_LENGTH
|
|
465
|
+
);
|
|
466
|
+
const iv = envelope.subarray(ENVELOPE_LAYOUT.iv, ENVELOPE_LAYOUT.iv + IV_LENGTH);
|
|
467
|
+
const authTag = envelope.subarray(
|
|
468
|
+
ENVELOPE_LAYOUT.authTag,
|
|
469
|
+
ENVELOPE_LAYOUT.authTag + AUTH_TAG_LENGTH
|
|
470
|
+
);
|
|
471
|
+
const decipher = createDecipheriv("aes-256-gcm", key, iv, { authTagLength: AUTH_TAG_LENGTH });
|
|
472
|
+
decipher.setAAD(Buffer.concat([buildHeaderAad(salt), filePathAad(filePath, memoryDir)]));
|
|
473
|
+
decipher.setAuthTag(authTag);
|
|
474
|
+
let position = headerSize;
|
|
475
|
+
const buffer = Buffer.alloc(chunkSize);
|
|
476
|
+
try {
|
|
477
|
+
for (;;) {
|
|
478
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, position);
|
|
479
|
+
if (bytesRead === 0) break;
|
|
480
|
+
position += bytesRead;
|
|
481
|
+
const plain = decipher.update(buffer.subarray(0, bytesRead));
|
|
482
|
+
if (plain.length > 0) yield plain;
|
|
483
|
+
}
|
|
484
|
+
const final = decipher.final();
|
|
485
|
+
if (final.length > 0) yield final;
|
|
486
|
+
} catch (err) {
|
|
487
|
+
if (err instanceof SecureStoreDecryptError) throw err;
|
|
488
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
489
|
+
throw new SecureStoreDecryptError(`secure-store decryption failed: ${message}`);
|
|
490
|
+
}
|
|
491
|
+
} finally {
|
|
492
|
+
await handle.close();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
419
496
|
// ---------------------------------------------------------------------------
|
|
420
497
|
// Migration
|
|
421
498
|
// ---------------------------------------------------------------------------
|
|
@@ -76,6 +76,14 @@ export class FactHashIndexNotAuthoritativeError extends Error {
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
+
type DiskFingerprint = {
|
|
80
|
+
mtimeMs: number;
|
|
81
|
+
size: number;
|
|
82
|
+
dev: number;
|
|
83
|
+
ino: number;
|
|
84
|
+
ctimeMs: number;
|
|
85
|
+
birthtimeMs: number;
|
|
86
|
+
};
|
|
79
87
|
/**
|
|
80
88
|
* Content-hash dedup index for facts.
|
|
81
89
|
* Normalizes content (lowercase, strip punctuation, collapse whitespace),
|
|
@@ -120,15 +128,13 @@ export class ContentHashIndex {
|
|
|
120
128
|
private reconcileRetryBarrier: Promise<void> | null = null;
|
|
121
129
|
private reconcileRetryResolve: (() => void) | null = null;
|
|
122
130
|
/**
|
|
123
|
-
* On-disk fingerprint
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* a stale-but-"authoritative" snapshot is detected without an O(file-size)
|
|
129
|
-
* re-read on the hot path.
|
|
131
|
+
* On-disk fingerprint captured at the last point THIS instance's in-memory
|
|
132
|
+
* set matched disk — after `load()`, `save()`, or a reconcile publish. A peer
|
|
133
|
+
* process that advances the durable index changes its file generation even
|
|
134
|
+
* when byte size and mtime collide, so the freshness check remains safe
|
|
135
|
+
* without an O(file-size) read on the hot path.
|
|
130
136
|
*/
|
|
131
|
-
private lastSyncedFingerprint:
|
|
137
|
+
private lastSyncedFingerprint: DiskFingerprint | null = null;
|
|
132
138
|
|
|
133
139
|
constructor(
|
|
134
140
|
stateDir: string,
|
|
@@ -166,10 +172,17 @@ export class ContentHashIndex {
|
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
/** Cheap `stat` of the durable index file; null when it does not exist yet. */
|
|
169
|
-
private async statIndexFile(): Promise<
|
|
175
|
+
private async statIndexFile(): Promise<DiskFingerprint | null> {
|
|
170
176
|
try {
|
|
171
177
|
const s = await stat(this.filePath);
|
|
172
|
-
return {
|
|
178
|
+
return {
|
|
179
|
+
mtimeMs: s.mtimeMs,
|
|
180
|
+
size: s.size,
|
|
181
|
+
dev: s.dev,
|
|
182
|
+
ino: s.ino,
|
|
183
|
+
ctimeMs: s.ctimeMs,
|
|
184
|
+
birthtimeMs: s.birthtimeMs,
|
|
185
|
+
};
|
|
173
186
|
} catch (err) {
|
|
174
187
|
if (isErrnoCode(err, "ENOENT")) return null;
|
|
175
188
|
throw err;
|
|
@@ -190,14 +203,15 @@ export class ContentHashIndex {
|
|
|
190
203
|
|
|
191
204
|
/**
|
|
192
205
|
* True when the durable index file is unchanged since this instance last
|
|
193
|
-
* synced it (load / save / reconcile). False when a peer advanced it, when
|
|
194
|
-
* file appeared or vanished, or when freshness cannot be established
|
|
195
|
-
* error) — the caller then treats the in-memory snapshot as
|
|
196
|
-
* and confirms a dedup miss against the durable corpus
|
|
197
|
-
*
|
|
206
|
+
* synced it (load / save / reconcile). False when a peer advanced it, when
|
|
207
|
+
* the file appeared or vanished, or when freshness cannot be established
|
|
208
|
+
* (stat error) — the caller then treats the in-memory snapshot as
|
|
209
|
+
* non-authoritative and confirms a dedup miss against the durable corpus.
|
|
210
|
+
* The stat includes file identity and generation metadata so same-size,
|
|
211
|
+
* same-mtime atomic replacements are detected without reading the file body.
|
|
198
212
|
*/
|
|
199
213
|
async isDiskFingerprintCurrent(): Promise<boolean> {
|
|
200
|
-
let current:
|
|
214
|
+
let current: DiskFingerprint | null;
|
|
201
215
|
try {
|
|
202
216
|
current = await this.statIndexFile();
|
|
203
217
|
} catch {
|
|
@@ -207,7 +221,14 @@ export class ContentHashIndex {
|
|
|
207
221
|
if (last === null || current === null) {
|
|
208
222
|
return last === null && current === null;
|
|
209
223
|
}
|
|
210
|
-
return
|
|
224
|
+
return (
|
|
225
|
+
current.mtimeMs === last.mtimeMs &&
|
|
226
|
+
current.size === last.size &&
|
|
227
|
+
current.dev === last.dev &&
|
|
228
|
+
current.ino === last.ino &&
|
|
229
|
+
current.ctimeMs === last.ctimeMs &&
|
|
230
|
+
current.birthtimeMs === last.birthtimeMs
|
|
231
|
+
);
|
|
211
232
|
}
|
|
212
233
|
|
|
213
234
|
/** Check if content already exists in the index. */
|
|
@@ -475,6 +496,11 @@ export class ContentHashIndex {
|
|
|
475
496
|
return this.reconcileRetryTimer !== null;
|
|
476
497
|
}
|
|
477
498
|
|
|
499
|
+
/** True while this index has in-memory changes not yet published to disk. */
|
|
500
|
+
get hasPendingChanges(): boolean {
|
|
501
|
+
return this.dirty;
|
|
502
|
+
}
|
|
503
|
+
|
|
478
504
|
/**
|
|
479
505
|
* Resolve once any armed background reconcile-save retry chain has settled —
|
|
480
506
|
* either it published the deferred additions, or it exhausted its bounded
|