@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
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { unlink } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
5
|
+
import { log } from "../logger.js";
|
|
6
|
+
import type { MemoryFile, MemoryFrontmatter } from "../types.js";
|
|
7
|
+
import { markProjectedMemoryPathInvalid } from "../memory-projection-store.js";
|
|
8
|
+
import { RECALL_FALLBACK_DIRS } from "../utils/category-dir.js";
|
|
9
|
+
import { isErrnoCode } from "../utils/errno.js";
|
|
10
|
+
import {
|
|
11
|
+
readMaybeEncryptedFileFromChunks,
|
|
12
|
+
writeMaybeEncryptedFileFromChunks,
|
|
13
|
+
} from "../secure-store/secure-fs.js";
|
|
14
|
+
import {
|
|
15
|
+
buildExplicitCaptureDedupKey,
|
|
16
|
+
captureIdentityHash,
|
|
17
|
+
tombstoneBlocked,
|
|
18
|
+
isQueuedReviewFrontmatter,
|
|
19
|
+
isQueuedReviewMemory,
|
|
20
|
+
runTombstoneBlockedChunkMutation,
|
|
21
|
+
runTombstoneBlockedMutation,
|
|
22
|
+
type TombstoneBlockedMutationHost,
|
|
23
|
+
} from "./tombstone-blocked-capture-mutation.js";
|
|
24
|
+
import { TombstoneBlockedCaptureIndex } from "./tombstone-blocked-capture-index.js";
|
|
25
|
+
import type { TombstoneBlockedCaptureIndexOptions } from "./tombstone-blocked-capture-index.js";
|
|
26
|
+
|
|
27
|
+
const OFFLINE_SYNC_BUFFER_LIMIT_BYTES = 1_048_576;
|
|
28
|
+
const OFFLINE_SYNC_STAGE_READ_BYTES = 64 * 1024;
|
|
29
|
+
const OFFLINE_SYNC_FRONTMATTER_DELIMITER = Buffer.from("\n---\n", "utf8");
|
|
30
|
+
|
|
31
|
+
type StreamedOfflineSyncIdentity = {
|
|
32
|
+
identityHash: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
type PreparedOfflineSyncChunks = {
|
|
36
|
+
after: MemoryFile | null;
|
|
37
|
+
chunks: AsyncIterable<Buffer>;
|
|
38
|
+
stagePath?: string;
|
|
39
|
+
streamedIdentity?: StreamedOfflineSyncIdentity;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function createExplicitContentNormalizer(onChunk: (chunk: string) => void): {
|
|
43
|
+
push: (text: string) => void;
|
|
44
|
+
finish: () => void;
|
|
45
|
+
} {
|
|
46
|
+
let started = false;
|
|
47
|
+
let pendingSpace = false;
|
|
48
|
+
let output = "";
|
|
49
|
+
const flush = () => {
|
|
50
|
+
if (output.length > 0) {
|
|
51
|
+
onChunk(output);
|
|
52
|
+
output = "";
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
return {
|
|
56
|
+
push(text) {
|
|
57
|
+
for (const character of text.toLowerCase()) {
|
|
58
|
+
if (/\s/u.test(character)) {
|
|
59
|
+
if (started) pendingSpace = true;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (pendingSpace) {
|
|
63
|
+
output += " ";
|
|
64
|
+
pendingSpace = false;
|
|
65
|
+
}
|
|
66
|
+
output += character;
|
|
67
|
+
started = true;
|
|
68
|
+
if (output.length >= OFFLINE_SYNC_STAGE_READ_BYTES) flush();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
finish: flush,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function forEachStagedTextChunk(
|
|
76
|
+
stagePath: string,
|
|
77
|
+
offset: number,
|
|
78
|
+
onText: (text: string) => void,
|
|
79
|
+
key: Buffer | null,
|
|
80
|
+
memoryDir: string
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
const decoder = new TextDecoder("utf-8");
|
|
83
|
+
let remaining = offset;
|
|
84
|
+
for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {
|
|
85
|
+
const textChunk =
|
|
86
|
+
remaining >= chunk.length
|
|
87
|
+
? null
|
|
88
|
+
: chunk.subarray(remaining);
|
|
89
|
+
remaining = Math.max(0, remaining - chunk.length);
|
|
90
|
+
if (textChunk === null || textChunk.length === 0) continue;
|
|
91
|
+
const text = decoder.decode(textChunk, { stream: true });
|
|
92
|
+
if (text.length > 0) onText(text);
|
|
93
|
+
}
|
|
94
|
+
const finalText = decoder.decode();
|
|
95
|
+
if (finalText.length > 0) onText(finalText);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function findStagedBodyOffset(
|
|
99
|
+
stagePath: string,
|
|
100
|
+
key: Buffer | null,
|
|
101
|
+
memoryDir: string
|
|
102
|
+
): Promise<number | null> {
|
|
103
|
+
let scannedBytes = 0;
|
|
104
|
+
let tail = Buffer.alloc(0);
|
|
105
|
+
for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {
|
|
106
|
+
const window = tail.length > 0 ? Buffer.concat([tail, chunk]) : chunk;
|
|
107
|
+
const windowStart = scannedBytes - tail.length;
|
|
108
|
+
for (let searchFrom = 0; ; ) {
|
|
109
|
+
const delimiterStart = window.indexOf(OFFLINE_SYNC_FRONTMATTER_DELIMITER, searchFrom);
|
|
110
|
+
if (delimiterStart < 0) break;
|
|
111
|
+
if (windowStart + delimiterStart >= 4) {
|
|
112
|
+
return windowStart + delimiterStart + OFFLINE_SYNC_FRONTMATTER_DELIMITER.length;
|
|
113
|
+
}
|
|
114
|
+
searchFrom = delimiterStart + 1;
|
|
115
|
+
}
|
|
116
|
+
scannedBytes += chunk.length;
|
|
117
|
+
const tailLength = Math.min(OFFLINE_SYNC_FRONTMATTER_DELIMITER.length - 1, window.length);
|
|
118
|
+
tail = Buffer.from(window.subarray(window.length - tailLength));
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function readStagedPrefix(
|
|
124
|
+
stagePath: string,
|
|
125
|
+
byteLimit: number,
|
|
126
|
+
key: Buffer | null,
|
|
127
|
+
memoryDir: string
|
|
128
|
+
): Promise<Buffer> {
|
|
129
|
+
const chunks: Buffer[] = [];
|
|
130
|
+
let bytesRead = 0;
|
|
131
|
+
for await (const chunk of readMaybeEncryptedFileFromChunks(stagePath, key, memoryDir)) {
|
|
132
|
+
const length = Math.min(chunk.length, byteLimit - bytesRead);
|
|
133
|
+
if (length > 0) chunks.push(chunk.subarray(0, length));
|
|
134
|
+
bytesRead += length;
|
|
135
|
+
if (bytesRead >= byteLimit) break;
|
|
136
|
+
}
|
|
137
|
+
return Buffer.concat(chunks, bytesRead);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export type OfflineSyncMemoryParser = (filePath: string, content: Buffer) => MemoryFile | null;
|
|
141
|
+
|
|
142
|
+
export function parseTombstoneBlockedOfflineSyncMemory(
|
|
143
|
+
filePath: string,
|
|
144
|
+
content: Buffer,
|
|
145
|
+
memoryDir: string,
|
|
146
|
+
parseFrontmatter: (raw: string) => { frontmatter: MemoryFrontmatter; content: string } | null,
|
|
147
|
+
normalizeFrontmatterForPath: (frontmatter: MemoryFrontmatter, pathRel: string, content?: string) => MemoryFrontmatter,
|
|
148
|
+
toMemoryPathRel: (baseDir: string, filePath: string) => string
|
|
149
|
+
): MemoryFile | null {
|
|
150
|
+
const parsed = parseFrontmatter(content.toString("utf8"));
|
|
151
|
+
if (!parsed) return null;
|
|
152
|
+
return {
|
|
153
|
+
path: filePath,
|
|
154
|
+
frontmatter: normalizeFrontmatterForPath(parsed.frontmatter, toMemoryPathRel(memoryDir, filePath), parsed.content),
|
|
155
|
+
content: parsed.content,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export abstract class TombstoneBlockedCaptureIndexHost {
|
|
160
|
+
private tombstoneBlockedCaptureIndex: TombstoneBlockedCaptureIndex | null = null;
|
|
161
|
+
protected abstract tombstoneBlockedCaptureIndexOptions(): TombstoneBlockedCaptureIndexOptions;
|
|
162
|
+
protected abstract writeStorageSecureFile(
|
|
163
|
+
filePath: string,
|
|
164
|
+
content: string | Buffer,
|
|
165
|
+
forceEncrypt?: boolean
|
|
166
|
+
): Promise<void>;
|
|
167
|
+
protected abstract assertManagedStoragePath(filePath: string, method: string): string;
|
|
168
|
+
protected abstract notifyCatalogWriteForPath(filePath: string): void;
|
|
169
|
+
protected abstract readMemoryByPath(filePath: string): Promise<MemoryFile | null>;
|
|
170
|
+
protected abstract invalidateAllMemoriesCache(): void;
|
|
171
|
+
protected abstract invalidateKnowledgeIndexCache(): void;
|
|
172
|
+
protected abstract invalidateColdMemoriesCache(): void;
|
|
173
|
+
protected abstract bumpArtifactWriteVersion(): number;
|
|
174
|
+
protected abstract bumpMemoryStatusVersion(): void;
|
|
175
|
+
protected abstract bumpMemoryCorpusVersion(): void;
|
|
176
|
+
protected abstract markFactHashIndexNotAuthoritative(): void;
|
|
177
|
+
|
|
178
|
+
private async writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
|
|
179
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
180
|
+
await writeMaybeEncryptedFileFromChunks(
|
|
181
|
+
filePath,
|
|
182
|
+
chunks,
|
|
183
|
+
options.secureStoreWriteKeyProvider(),
|
|
184
|
+
{},
|
|
185
|
+
options.memoryDir
|
|
186
|
+
);
|
|
187
|
+
this.notifyCatalogWriteForPath(filePath);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
protected getTombstoneBlockedCaptureIndex(): TombstoneBlockedCaptureIndex {
|
|
191
|
+
if (this.tombstoneBlockedCaptureIndex === null) {
|
|
192
|
+
this.tombstoneBlockedCaptureIndex = new TombstoneBlockedCaptureIndex(this.tombstoneBlockedCaptureIndexOptions());
|
|
193
|
+
}
|
|
194
|
+
return this.tombstoneBlockedCaptureIndex;
|
|
195
|
+
}
|
|
196
|
+
async withTombstoneBlockedCaptureWriteLock<T>(
|
|
197
|
+
task: () => Promise<T>,
|
|
198
|
+
identity?: string | readonly string[]
|
|
199
|
+
): Promise<T> {
|
|
200
|
+
return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLock(task, identity);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
protected async withTombstoneBlockedCaptureWriteLockAndHashes<T>(
|
|
204
|
+
task: () => Promise<T>,
|
|
205
|
+
identity: string | readonly string[] | undefined,
|
|
206
|
+
identityHashes: readonly string[]
|
|
207
|
+
): Promise<T> {
|
|
208
|
+
return await this.getTombstoneBlockedCaptureIndex().withCaptureWriteLockAndHashes(task, identity, identityHashes);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private async writeTombstoneBlockedMutation(
|
|
212
|
+
blocked: boolean,
|
|
213
|
+
pathname: string,
|
|
214
|
+
fileContent: string,
|
|
215
|
+
identity: string | readonly string[],
|
|
216
|
+
updateIndex: (rebuildMarker?: string, current?: MemoryFile | null) => Promise<void>,
|
|
217
|
+
beforeIndexUpdate?: () => Promise<void>,
|
|
218
|
+
coordinate = false
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
const host: TombstoneBlockedMutationHost = {
|
|
221
|
+
readCurrent: () => this.readMemoryByPath(pathname),
|
|
222
|
+
isBlocked: (memory) => this.isTombstoneBlockedMemory(memory),
|
|
223
|
+
isQueuedReview: (memory) => isQueuedReviewMemory(memory),
|
|
224
|
+
memoryIdentity: (memory) => this.offlineSyncMemoryIdentity(memory),
|
|
225
|
+
prepareWrite: () => this.getTombstoneBlockedCaptureIndex().prepareWrite(),
|
|
226
|
+
commitWrite: (marker) => this.getTombstoneBlockedCaptureIndex().commitWrite(marker),
|
|
227
|
+
discardWrite: (marker) => this.getTombstoneBlockedCaptureIndex().discardWrite(marker),
|
|
228
|
+
markUntrusted: () => this.getTombstoneBlockedCaptureIndex().markUntrusted(),
|
|
229
|
+
writeStorageSecureFile: (target, content) => this.writeStorageSecureFile(target, content),
|
|
230
|
+
withCaptureWriteLock: (task, lockIdentity) =>
|
|
231
|
+
this.withTombstoneBlockedCaptureWriteLock(task, lockIdentity),
|
|
232
|
+
logWarning: (message) => log.warn(message),
|
|
233
|
+
};
|
|
234
|
+
await runTombstoneBlockedMutation(host, {
|
|
235
|
+
blocked,
|
|
236
|
+
pathname,
|
|
237
|
+
fileContent,
|
|
238
|
+
identity,
|
|
239
|
+
updateIndex,
|
|
240
|
+
beforeIndexUpdate,
|
|
241
|
+
coordinate,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
protected async writeTombstoneBlockedMemory(
|
|
246
|
+
pathname: string,
|
|
247
|
+
fileContent: string,
|
|
248
|
+
frontmatter: MemoryFrontmatter,
|
|
249
|
+
content: string,
|
|
250
|
+
beforeIndexUpdate?: () => Promise<void>
|
|
251
|
+
): Promise<void> {
|
|
252
|
+
await this.writeTombstoneBlockedMutation(
|
|
253
|
+
tombstoneBlocked(frontmatter),
|
|
254
|
+
pathname,
|
|
255
|
+
fileContent,
|
|
256
|
+
buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),
|
|
257
|
+
(rebuildMarker) =>
|
|
258
|
+
this.getTombstoneBlockedCaptureIndex().addWrittenMemory(pathname, frontmatter, content, rebuildMarker),
|
|
259
|
+
beforeIndexUpdate
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
protected async writeTombstoneBlockedChunk(
|
|
264
|
+
pathname: string,
|
|
265
|
+
fileContent: string,
|
|
266
|
+
frontmatter: MemoryFrontmatter,
|
|
267
|
+
content: string,
|
|
268
|
+
findDuplicate: () => Promise<MemoryFile | null>,
|
|
269
|
+
afterWrite: () => Promise<void>
|
|
270
|
+
): Promise<string> {
|
|
271
|
+
return await runTombstoneBlockedChunkMutation({
|
|
272
|
+
blocked: tombstoneBlocked(frontmatter),
|
|
273
|
+
id: frontmatter.id,
|
|
274
|
+
identity: buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector),
|
|
275
|
+
findDuplicate,
|
|
276
|
+
persistMemory: () => this.writeTombstoneBlockedMemory(pathname, fileContent, frontmatter, content),
|
|
277
|
+
afterWrite,
|
|
278
|
+
withCaptureWriteLock: (task, identity) => this.withTombstoneBlockedCaptureWriteLock(task, identity),
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
protected async writeTombstoneBlockedUpdate(
|
|
283
|
+
before: MemoryFile,
|
|
284
|
+
fileContent: string,
|
|
285
|
+
frontmatter: MemoryFrontmatter,
|
|
286
|
+
content: string,
|
|
287
|
+
beforeIndexUpdate?: () => Promise<void>
|
|
288
|
+
): Promise<void> {
|
|
289
|
+
await this.writeTombstoneBlockedMutation(
|
|
290
|
+
tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),
|
|
291
|
+
before.path,
|
|
292
|
+
fileContent,
|
|
293
|
+
[
|
|
294
|
+
...(tombstoneBlocked(before.frontmatter)
|
|
295
|
+
? [
|
|
296
|
+
buildExplicitCaptureDedupKey(
|
|
297
|
+
before.content,
|
|
298
|
+
before.frontmatter.category,
|
|
299
|
+
before.frontmatter.sourceConnector
|
|
300
|
+
),
|
|
301
|
+
]
|
|
302
|
+
: []),
|
|
303
|
+
...(tombstoneBlocked(frontmatter)
|
|
304
|
+
? [buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector)]
|
|
305
|
+
: []),
|
|
306
|
+
...(isQueuedReviewMemory(before)
|
|
307
|
+
? [
|
|
308
|
+
buildExplicitCaptureDedupKey(
|
|
309
|
+
before.content,
|
|
310
|
+
before.frontmatter.category,
|
|
311
|
+
before.frontmatter.sourceConnector
|
|
312
|
+
),
|
|
313
|
+
]
|
|
314
|
+
: []),
|
|
315
|
+
...(isQueuedReviewFrontmatter(frontmatter)
|
|
316
|
+
? [buildExplicitCaptureDedupKey(content, frontmatter.category, frontmatter.sourceConnector)]
|
|
317
|
+
: []),
|
|
318
|
+
],
|
|
319
|
+
(rebuildMarker, current) =>
|
|
320
|
+
this.getTombstoneBlockedCaptureIndex().syncUpdatedMemory(
|
|
321
|
+
current ?? before,
|
|
322
|
+
frontmatter,
|
|
323
|
+
content,
|
|
324
|
+
rebuildMarker
|
|
325
|
+
),
|
|
326
|
+
beforeIndexUpdate,
|
|
327
|
+
isQueuedReviewMemory(before) || isQueuedReviewFrontmatter(frontmatter)
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
protected async writeTombstoneBlockedFrontmatter(
|
|
332
|
+
before: MemoryFile,
|
|
333
|
+
fileContent: string,
|
|
334
|
+
frontmatter: MemoryFrontmatter,
|
|
335
|
+
beforeIndexUpdate?: () => Promise<void>
|
|
336
|
+
): Promise<void> {
|
|
337
|
+
await this.writeTombstoneBlockedMutation(
|
|
338
|
+
tombstoneBlocked(before.frontmatter) || tombstoneBlocked(frontmatter),
|
|
339
|
+
before.path,
|
|
340
|
+
fileContent,
|
|
341
|
+
[
|
|
342
|
+
...(tombstoneBlocked(before.frontmatter)
|
|
343
|
+
? [
|
|
344
|
+
buildExplicitCaptureDedupKey(
|
|
345
|
+
before.content,
|
|
346
|
+
before.frontmatter.category,
|
|
347
|
+
before.frontmatter.sourceConnector
|
|
348
|
+
),
|
|
349
|
+
]
|
|
350
|
+
: []),
|
|
351
|
+
...(tombstoneBlocked(frontmatter)
|
|
352
|
+
? [buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector)]
|
|
353
|
+
: []),
|
|
354
|
+
...(isQueuedReviewMemory(before)
|
|
355
|
+
? [
|
|
356
|
+
buildExplicitCaptureDedupKey(
|
|
357
|
+
before.content,
|
|
358
|
+
before.frontmatter.category,
|
|
359
|
+
before.frontmatter.sourceConnector
|
|
360
|
+
),
|
|
361
|
+
]
|
|
362
|
+
: []),
|
|
363
|
+
...(isQueuedReviewFrontmatter(frontmatter)
|
|
364
|
+
? [buildExplicitCaptureDedupKey(before.content, frontmatter.category, frontmatter.sourceConnector)]
|
|
365
|
+
: []),
|
|
366
|
+
],
|
|
367
|
+
(rebuildMarker, current) =>
|
|
368
|
+
this.getTombstoneBlockedCaptureIndex().syncUpdatedFrontmatter(
|
|
369
|
+
current ?? before,
|
|
370
|
+
frontmatter,
|
|
371
|
+
rebuildMarker
|
|
372
|
+
),
|
|
373
|
+
beforeIndexUpdate,
|
|
374
|
+
isQueuedReviewMemory(before) || isQueuedReviewFrontmatter(frontmatter)
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private isTombstoneBlockedMemory(memory: MemoryFile | null): memory is MemoryFile {
|
|
379
|
+
return memory !== null && memory.frontmatter.status === "pending_review" && Boolean(memory.frontmatter.blockedBy);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private offlineSyncMemoryIdentity(memory: MemoryFile): string {
|
|
383
|
+
return buildExplicitCaptureDedupKey(
|
|
384
|
+
memory.content,
|
|
385
|
+
memory.frontmatter.category,
|
|
386
|
+
memory.frontmatter.sourceConnector
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
private async isDuplicateTombstoneBlockedOfflineSync(
|
|
390
|
+
target: string,
|
|
391
|
+
incoming: MemoryFile,
|
|
392
|
+
streamedIdentity?: StreamedOfflineSyncIdentity
|
|
393
|
+
): Promise<boolean> {
|
|
394
|
+
const before = await this.readMemoryByPath(target);
|
|
395
|
+
const duplicate = streamedIdentity
|
|
396
|
+
? true
|
|
397
|
+
: await this.getTombstoneBlockedCaptureIndex().has(
|
|
398
|
+
incoming.content,
|
|
399
|
+
incoming.frontmatter.category,
|
|
400
|
+
incoming.frontmatter.sourceConnector
|
|
401
|
+
);
|
|
402
|
+
if (!duplicate) return false;
|
|
403
|
+
const incomingIdentity = streamedIdentity?.identityHash ?? this.offlineSyncMemoryIdentity(incoming);
|
|
404
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
405
|
+
const existing = [...(await options.readAllMemories()), ...(await options.readAllColdMemories())];
|
|
406
|
+
const exactMatch = existing.some(
|
|
407
|
+
(memory) =>
|
|
408
|
+
this.isTombstoneBlockedMemory(memory) &&
|
|
409
|
+
(streamedIdentity ? captureIdentityHash(memory) : this.offlineSyncMemoryIdentity(memory)) === incomingIdentity
|
|
410
|
+
);
|
|
411
|
+
if (!exactMatch) return false;
|
|
412
|
+
if (before === null) return true;
|
|
413
|
+
if (!this.isTombstoneBlockedMemory(before)) return false;
|
|
414
|
+
const beforeIdentity = streamedIdentity ? captureIdentityHash(before) : this.offlineSyncMemoryIdentity(before);
|
|
415
|
+
if (beforeIdentity !== incomingIdentity) return false;
|
|
416
|
+
return streamedIdentity
|
|
417
|
+
? isDeepStrictEqual(before.frontmatter, incoming.frontmatter)
|
|
418
|
+
: before.content === incoming.content && isDeepStrictEqual(before.frontmatter, incoming.frontmatter);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private async invalidateAfterOfflineSyncMutation(filePath: string, ownedMarker?: string): Promise<void> {
|
|
422
|
+
this.invalidateAllMemoriesCache();
|
|
423
|
+
this.invalidateKnowledgeIndexCache();
|
|
424
|
+
this.markFactHashIndexNotAuthoritative();
|
|
425
|
+
if (filePath.includes(`${path.sep}cold${path.sep}`)) {
|
|
426
|
+
this.invalidateColdMemoriesCache();
|
|
427
|
+
}
|
|
428
|
+
await this.rebuildTombstoneBlockedCaptureAfterInvalidationForPath(filePath, ownedMarker);
|
|
429
|
+
if (filePath.includes(`${path.sep}artifacts${path.sep}`)) {
|
|
430
|
+
this.bumpArtifactWriteVersion();
|
|
431
|
+
}
|
|
432
|
+
this.bumpMemoryStatusVersion();
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
protected async runTombstoneBlockedArchive(
|
|
436
|
+
memory: MemoryFile,
|
|
437
|
+
task: (current: MemoryFile, markDurable: () => void) => Promise<string | null>
|
|
438
|
+
): Promise<string | null> {
|
|
439
|
+
if (!this.isTombstoneBlockedMemory(memory) && !isQueuedReviewMemory(memory)) {
|
|
440
|
+
return await task(memory, () => {});
|
|
441
|
+
}
|
|
442
|
+
let archivedPath: string | null = null;
|
|
443
|
+
const archived = await this.runTombstoneBlockedInvalidation(
|
|
444
|
+
memory,
|
|
445
|
+
async (current, rebuildMarker, markDurable) => {
|
|
446
|
+
let archiveDurable = false;
|
|
447
|
+
const markArchiveDurable = () => {
|
|
448
|
+
archiveDurable = true;
|
|
449
|
+
markDurable();
|
|
450
|
+
};
|
|
451
|
+
archivedPath = await task(current, markArchiveDurable);
|
|
452
|
+
if (rebuildMarker !== undefined && (archivedPath !== null || archiveDurable)) {
|
|
453
|
+
await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
|
|
454
|
+
} else if (rebuildMarker !== undefined) {
|
|
455
|
+
const index = this.getTombstoneBlockedCaptureIndex();
|
|
456
|
+
try { await index.discardWrite(rebuildMarker); } catch { index.markUntrusted(); }
|
|
457
|
+
}
|
|
458
|
+
return archivedPath !== null;
|
|
459
|
+
}
|
|
460
|
+
);
|
|
461
|
+
return archived ? archivedPath : null;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
protected async runTombstoneBlockedInvalidation(
|
|
465
|
+
memory: MemoryFile,
|
|
466
|
+
task: (
|
|
467
|
+
current: MemoryFile,
|
|
468
|
+
rebuildMarker: string | undefined,
|
|
469
|
+
markDurable: () => void
|
|
470
|
+
) => Promise<boolean>,
|
|
471
|
+
propagateErrors = false,
|
|
472
|
+
coordinate = false
|
|
473
|
+
): Promise<boolean> {
|
|
474
|
+
let lockIdentity =
|
|
475
|
+
coordinate || this.isTombstoneBlockedMemory(memory) || isQueuedReviewMemory(memory)
|
|
476
|
+
? this.offlineSyncMemoryIdentity(memory)
|
|
477
|
+
: undefined;
|
|
478
|
+
for (;;) {
|
|
479
|
+
const attempt = async () => {
|
|
480
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
481
|
+
const current =
|
|
482
|
+
(await this.readMemoryByPath(memory.path)) ??
|
|
483
|
+
(await options.readAllMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id) ??
|
|
484
|
+
(await options.readAllColdMemories()).find((candidate) => candidate.frontmatter.id === memory.frontmatter.id);
|
|
485
|
+
if (!current) return { result: false };
|
|
486
|
+
const blocked = this.isTombstoneBlockedMemory(current);
|
|
487
|
+
const queuedReview = isQueuedReviewMemory(current);
|
|
488
|
+
if (blocked || queuedReview) {
|
|
489
|
+
const currentIdentity = this.offlineSyncMemoryIdentity(current);
|
|
490
|
+
if (lockIdentity !== currentIdentity) return { retryIdentity: currentIdentity };
|
|
491
|
+
}
|
|
492
|
+
const blockedIndex = blocked ? this.getTombstoneBlockedCaptureIndex() : null;
|
|
493
|
+
let rebuildMarker: string | undefined;
|
|
494
|
+
let durable = false;
|
|
495
|
+
try {
|
|
496
|
+
if (blockedIndex) rebuildMarker = await blockedIndex.prepareWrite();
|
|
497
|
+
const result = await task(current, rebuildMarker, () => {
|
|
498
|
+
durable = true;
|
|
499
|
+
});
|
|
500
|
+
if (!result && rebuildMarker && !durable && blockedIndex) {
|
|
501
|
+
try {
|
|
502
|
+
await blockedIndex.discardWrite(rebuildMarker);
|
|
503
|
+
} catch {
|
|
504
|
+
blockedIndex.markUntrusted();
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return { result };
|
|
508
|
+
} catch (error) {
|
|
509
|
+
if (rebuildMarker && !durable && blockedIndex) {
|
|
510
|
+
try {
|
|
511
|
+
await blockedIndex.discardWrite(rebuildMarker);
|
|
512
|
+
} catch {
|
|
513
|
+
blockedIndex.markUntrusted();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (propagateErrors) throw error;
|
|
517
|
+
return { result: false };
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
const result =
|
|
521
|
+
lockIdentity === undefined
|
|
522
|
+
? await attempt()
|
|
523
|
+
: await this.withTombstoneBlockedCaptureWriteLock(attempt, lockIdentity);
|
|
524
|
+
if (result.retryIdentity !== undefined) {
|
|
525
|
+
lockIdentity = result.retryIdentity;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
return result.result;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async deleteMemoryForMaintenance(
|
|
532
|
+
memory: MemoryFile,
|
|
533
|
+
shouldDelete: (current: MemoryFile) => boolean = () => true
|
|
534
|
+
): Promise<MemoryFile | null> {
|
|
535
|
+
let deleted: MemoryFile | null = null;
|
|
536
|
+
const removed = await this.runTombstoneBlockedInvalidation(
|
|
537
|
+
memory,
|
|
538
|
+
async (current, rebuildMarker, markDurable) => {
|
|
539
|
+
if (!shouldDelete(current)) return false;
|
|
540
|
+
await unlink(current.path);
|
|
541
|
+
markDurable();
|
|
542
|
+
deleted = current;
|
|
543
|
+
const memoryDir = this.tombstoneBlockedCaptureIndexOptions().memoryDir;
|
|
544
|
+
markProjectedMemoryPathInvalid(memoryDir, current.frontmatter.id);
|
|
545
|
+
this.invalidateAllMemoriesCache();
|
|
546
|
+
if (current.path.includes(`${path.sep}cold${path.sep}`)) {
|
|
547
|
+
this.invalidateColdMemoriesCache();
|
|
548
|
+
}
|
|
549
|
+
await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
|
|
550
|
+
this.bumpMemoryCorpusVersion();
|
|
551
|
+
this.bumpMemoryStatusVersion();
|
|
552
|
+
return true;
|
|
553
|
+
},
|
|
554
|
+
true,
|
|
555
|
+
true
|
|
556
|
+
);
|
|
557
|
+
return removed ? deleted : null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
protected async runTombstoneBlockedOfflineSyncMutation(
|
|
561
|
+
target: string,
|
|
562
|
+
after: MemoryFile | null,
|
|
563
|
+
write: () => Promise<void>,
|
|
564
|
+
coordinate = false,
|
|
565
|
+
streamedIdentity?: StreamedOfflineSyncIdentity
|
|
566
|
+
): Promise<void> {
|
|
567
|
+
for (;;) {
|
|
568
|
+
const before = await this.readMemoryByPath(target);
|
|
569
|
+
const targetIdentity = this.isTombstoneBlockedMemory(before)
|
|
570
|
+
? this.offlineSyncMemoryIdentity(before)
|
|
571
|
+
: undefined;
|
|
572
|
+
const blocked = coordinate || targetIdentity !== undefined || this.isTombstoneBlockedMemory(after);
|
|
573
|
+
const identities = [
|
|
574
|
+
...(targetIdentity === undefined ? [] : [targetIdentity]),
|
|
575
|
+
...(!streamedIdentity && this.isTombstoneBlockedMemory(after)
|
|
576
|
+
? [this.offlineSyncMemoryIdentity(after)]
|
|
577
|
+
: []),
|
|
578
|
+
...(coordinate ? [target] : []),
|
|
579
|
+
...(coordinate && after === null && streamedIdentity === undefined ? [""] : []),
|
|
580
|
+
];
|
|
581
|
+
let retryIdentity: string | undefined;
|
|
582
|
+
const mutate = async (): Promise<void> => {
|
|
583
|
+
const current = await this.readMemoryByPath(target);
|
|
584
|
+
if (this.isTombstoneBlockedMemory(current)) {
|
|
585
|
+
const currentIdentity = this.offlineSyncMemoryIdentity(current);
|
|
586
|
+
if (currentIdentity !== targetIdentity) {
|
|
587
|
+
retryIdentity = currentIdentity;
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (
|
|
592
|
+
this.isTombstoneBlockedMemory(after) &&
|
|
593
|
+
(await this.isDuplicateTombstoneBlockedOfflineSync(target, after, streamedIdentity))
|
|
594
|
+
) {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const marker = blocked ? await this.getTombstoneBlockedCaptureIndex().prepareWrite() : undefined;
|
|
598
|
+
let durable = false;
|
|
599
|
+
try {
|
|
600
|
+
await write();
|
|
601
|
+
durable = true;
|
|
602
|
+
if (marker) {
|
|
603
|
+
try {
|
|
604
|
+
await this.getTombstoneBlockedCaptureIndex().commitWrite(marker);
|
|
605
|
+
} catch (err) {
|
|
606
|
+
this.getTombstoneBlockedCaptureIndex().markUntrusted();
|
|
607
|
+
log.warn(`storage.offlineSyncFile committed write marker failed: ${err}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
await this.invalidateAfterOfflineSyncMutation(target, marker);
|
|
611
|
+
} catch (err) {
|
|
612
|
+
if (marker && !durable) {
|
|
613
|
+
try {
|
|
614
|
+
await this.getTombstoneBlockedCaptureIndex().discardWrite(marker);
|
|
615
|
+
} catch (cleanupError) {
|
|
616
|
+
this.getTombstoneBlockedCaptureIndex().markUntrusted();
|
|
617
|
+
log.warn(`storage.offlineSyncFile failed to clear write marker: ${cleanupError}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
throw err;
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
if (!blocked) {
|
|
624
|
+
await mutate();
|
|
625
|
+
} else {
|
|
626
|
+
if (streamedIdentity) {
|
|
627
|
+
await this.withTombstoneBlockedCaptureWriteLockAndHashes(
|
|
628
|
+
mutate,
|
|
629
|
+
identities,
|
|
630
|
+
[streamedIdentity.identityHash]
|
|
631
|
+
);
|
|
632
|
+
} else {
|
|
633
|
+
await this.withTombstoneBlockedCaptureWriteLock(mutate, identities);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (retryIdentity !== undefined) continue;
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
protected async writeTombstoneBlockedOfflineSyncFile(target: string, content: Buffer): Promise<void> {
|
|
642
|
+
await this.runTombstoneBlockedOfflineSyncMutation(
|
|
643
|
+
target,
|
|
644
|
+
this.tombstoneBlockedCaptureIndexOptions().parseMemory?.(target, content) ?? null,
|
|
645
|
+
() => this.writeStorageSecureFile(target, content)
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
private async prepareTombstoneBlockedOfflineSyncChunks(
|
|
650
|
+
target: string,
|
|
651
|
+
chunks: AsyncIterable<Buffer>
|
|
652
|
+
): Promise<PreparedOfflineSyncChunks> {
|
|
653
|
+
const parseMemory = this.tombstoneBlockedCaptureIndexOptions().parseMemory;
|
|
654
|
+
if (!parseMemory) return { after: null, chunks };
|
|
655
|
+
|
|
656
|
+
const iterator = chunks[Symbol.asyncIterator]();
|
|
657
|
+
const buffered: Buffer[] = [];
|
|
658
|
+
let bufferedBytes = 0;
|
|
659
|
+
let firstOverflow: Buffer | undefined;
|
|
660
|
+
for (;;) {
|
|
661
|
+
const next = await iterator.next();
|
|
662
|
+
if (next.done) break;
|
|
663
|
+
if (bufferedBytes + next.value.length > OFFLINE_SYNC_BUFFER_LIMIT_BYTES) {
|
|
664
|
+
firstOverflow = next.value;
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
bufferedBytes += next.value.length;
|
|
668
|
+
buffered.push(next.value);
|
|
669
|
+
}
|
|
670
|
+
const overflow = firstOverflow;
|
|
671
|
+
if (overflow === undefined) {
|
|
672
|
+
let after: MemoryFile | null = null;
|
|
673
|
+
try {
|
|
674
|
+
after = parseMemory(target, Buffer.concat(buffered));
|
|
675
|
+
} catch {
|
|
676
|
+
after = null;
|
|
677
|
+
}
|
|
678
|
+
const replay = (async function* (): AsyncIterable<Buffer> {
|
|
679
|
+
for (const chunk of buffered) yield chunk;
|
|
680
|
+
})();
|
|
681
|
+
return { after, chunks: replay };
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
685
|
+
const stagePath = path.join(
|
|
686
|
+
options.stateDir,
|
|
687
|
+
"tombstone-blocked-capture",
|
|
688
|
+
"offline-sync-staging",
|
|
689
|
+
`${randomUUID()}.stage`
|
|
690
|
+
);
|
|
691
|
+
const stagedChunks = (async function* (): AsyncIterable<Buffer> {
|
|
692
|
+
for (const chunk of buffered) yield chunk;
|
|
693
|
+
yield overflow;
|
|
694
|
+
for (;;) {
|
|
695
|
+
const next = await iterator.next();
|
|
696
|
+
if (next.done) break;
|
|
697
|
+
yield next.value;
|
|
698
|
+
}
|
|
699
|
+
})();
|
|
700
|
+
try {
|
|
701
|
+
await writeMaybeEncryptedFileFromChunks(
|
|
702
|
+
stagePath,
|
|
703
|
+
stagedChunks,
|
|
704
|
+
options.secureStoreWriteKeyProvider(),
|
|
705
|
+
{ atomic: false },
|
|
706
|
+
options.memoryDir
|
|
707
|
+
);
|
|
708
|
+
return await this.describeStagedOfflineSyncChunks(target, stagePath, parseMemory);
|
|
709
|
+
} catch (err) {
|
|
710
|
+
await unlink(stagePath).catch(() => {});
|
|
711
|
+
throw err;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
private async describeStagedOfflineSyncChunks(
|
|
716
|
+
target: string,
|
|
717
|
+
stagePath: string,
|
|
718
|
+
parseMemory: OfflineSyncMemoryParser
|
|
719
|
+
): Promise<PreparedOfflineSyncChunks> {
|
|
720
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
721
|
+
const key = options.secureStoreKeyProvider();
|
|
722
|
+
const bodyOffset = await findStagedBodyOffset(stagePath, key, options.memoryDir);
|
|
723
|
+
if (bodyOffset === null) {
|
|
724
|
+
return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };
|
|
725
|
+
}
|
|
726
|
+
const prefix = await readStagedPrefix(stagePath, bodyOffset, key, options.memoryDir);
|
|
727
|
+
let after: MemoryFile | null = null;
|
|
728
|
+
try {
|
|
729
|
+
after = parseMemory(target, prefix.subarray(0, bodyOffset));
|
|
730
|
+
} catch {
|
|
731
|
+
after = null;
|
|
732
|
+
}
|
|
733
|
+
if (after === null) {
|
|
734
|
+
return { after: null, chunks: this.readStagedOfflineSyncChunks(stagePath), stagePath };
|
|
735
|
+
}
|
|
736
|
+
const streamedIdentity = await this.computeStagedOfflineSyncIdentity(stagePath, bodyOffset, after);
|
|
737
|
+
return {
|
|
738
|
+
after,
|
|
739
|
+
chunks: this.readStagedOfflineSyncChunks(stagePath),
|
|
740
|
+
stagePath,
|
|
741
|
+
streamedIdentity,
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
private async computeStagedOfflineSyncIdentity(
|
|
746
|
+
stagePath: string,
|
|
747
|
+
bodyOffset: number,
|
|
748
|
+
after: MemoryFile
|
|
749
|
+
): Promise<StreamedOfflineSyncIdentity> {
|
|
750
|
+
let normalizedLength = 0;
|
|
751
|
+
const countNormalizer = createExplicitContentNormalizer((chunk) => {
|
|
752
|
+
normalizedLength += chunk.length;
|
|
753
|
+
});
|
|
754
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
755
|
+
const key = options.secureStoreKeyProvider();
|
|
756
|
+
await forEachStagedTextChunk(
|
|
757
|
+
stagePath,
|
|
758
|
+
bodyOffset,
|
|
759
|
+
(text) => countNormalizer.push(text),
|
|
760
|
+
key,
|
|
761
|
+
options.memoryDir
|
|
762
|
+
);
|
|
763
|
+
countNormalizer.finish();
|
|
764
|
+
|
|
765
|
+
const category = after.frontmatter.category;
|
|
766
|
+
const connector = after.frontmatter.sourceConnector?.trim() ?? "";
|
|
767
|
+
const encode = (label: string, value: string): string => `${label} ${value.length} ${value}`;
|
|
768
|
+
const identityPrefix =
|
|
769
|
+
[encode("category", category), encode("connector", connector), `content ${normalizedLength} `].join(" ");
|
|
770
|
+
const identityHash = createHash("sha256");
|
|
771
|
+
identityHash.update(identityPrefix);
|
|
772
|
+
const identityNormalizer = createExplicitContentNormalizer((chunk) => identityHash.update(chunk));
|
|
773
|
+
await forEachStagedTextChunk(
|
|
774
|
+
stagePath,
|
|
775
|
+
bodyOffset,
|
|
776
|
+
(text) => identityNormalizer.push(text),
|
|
777
|
+
key,
|
|
778
|
+
options.memoryDir
|
|
779
|
+
);
|
|
780
|
+
identityNormalizer.finish();
|
|
781
|
+
return {
|
|
782
|
+
identityHash: identityHash.digest("hex"),
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
private async *readStagedOfflineSyncChunks(stagePath: string): AsyncIterable<Buffer> {
|
|
787
|
+
const options = this.tombstoneBlockedCaptureIndexOptions();
|
|
788
|
+
for await (const chunk of readMaybeEncryptedFileFromChunks(
|
|
789
|
+
stagePath,
|
|
790
|
+
options.secureStoreKeyProvider(),
|
|
791
|
+
options.memoryDir
|
|
792
|
+
)) {
|
|
793
|
+
yield chunk;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
protected async writeTombstoneBlockedOfflineSyncFileChunks(
|
|
798
|
+
target: string,
|
|
799
|
+
chunks: AsyncIterable<Buffer>
|
|
800
|
+
): Promise<void> {
|
|
801
|
+
const coordinate = this.getTombstoneBlockedCaptureIndex().shouldRebuildAfterInvalidationForPath(target);
|
|
802
|
+
const prepared: PreparedOfflineSyncChunks = coordinate
|
|
803
|
+
? await this.prepareTombstoneBlockedOfflineSyncChunks(target, chunks)
|
|
804
|
+
: { after: null, chunks };
|
|
805
|
+
const streamedIdentity = prepared.streamedIdentity;
|
|
806
|
+
const mutate = async (): Promise<void> => {
|
|
807
|
+
try {
|
|
808
|
+
await this.runTombstoneBlockedOfflineSyncMutation(
|
|
809
|
+
target,
|
|
810
|
+
prepared.after,
|
|
811
|
+
() => this.writeStorageSecureFileChunks(target, prepared.chunks),
|
|
812
|
+
coordinate,
|
|
813
|
+
streamedIdentity
|
|
814
|
+
);
|
|
815
|
+
} finally {
|
|
816
|
+
if (prepared.stagePath !== undefined) await unlink(prepared.stagePath).catch(() => {});
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
await mutate();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
async writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void> {
|
|
823
|
+
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFile");
|
|
824
|
+
await this.writeTombstoneBlockedOfflineSyncFile(target, content);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
async writeOfflineSyncFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
|
|
828
|
+
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFileChunks");
|
|
829
|
+
await this.writeTombstoneBlockedOfflineSyncFileChunks(target, chunks);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
async deleteOfflineSyncFile(filePath: string): Promise<void> {
|
|
833
|
+
const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
|
|
834
|
+
await this.deleteTombstoneBlockedOfflineSyncFile(target);
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
protected async deleteTombstoneBlockedOfflineSyncFile(target: string): Promise<void> {
|
|
838
|
+
await this.runTombstoneBlockedOfflineSyncMutation(target, null, async () => {
|
|
839
|
+
await unlink(target).catch((error: unknown) => {
|
|
840
|
+
if (isErrnoCode(error, "ENOENT")) return;
|
|
841
|
+
throw error;
|
|
842
|
+
});
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
async checkTombstoneBlockedExplicitCapture(
|
|
847
|
+
...args: [string, string, string?]
|
|
848
|
+
): Promise<{ has: boolean; authoritative: boolean }> {
|
|
849
|
+
return this.getTombstoneBlockedCaptureIndex().check(...args);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
async hasTombstoneBlockedExplicitCapture(...args: [string, string, string?]): Promise<boolean> {
|
|
853
|
+
return this.getTombstoneBlockedCaptureIndex().has(...args);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
async isTombstoneBlockedExplicitCaptureIndexAuthoritative(): Promise<boolean> {
|
|
857
|
+
return this.getTombstoneBlockedCaptureIndex().isAuthoritative();
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
protected async rebuildTombstoneBlockedCaptureAfterInvalidationForPath(
|
|
861
|
+
filePath: string,
|
|
862
|
+
ownedMarker?: string
|
|
863
|
+
): Promise<void> {
|
|
864
|
+
const index = this.getTombstoneBlockedCaptureIndex();
|
|
865
|
+
if (index.shouldRebuildAfterInvalidationForPath(filePath)) {
|
|
866
|
+
await this.rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
protected async rebuildTombstoneBlockedCaptureAfterInvalidation(ownedMarker?: string): Promise<void> {
|
|
870
|
+
await this.getTombstoneBlockedCaptureIndex().rebuildAfterInvalidation(ownedMarker);
|
|
871
|
+
}
|
|
872
|
+
}
|