@remnic/core 9.29.1 → 9.30.1
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-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-FUYEVEX7.js → chunk-YNQBRLC5.js} +24 -22
- package/dist/chunk-YNQBRLC5.js.map +1 -0
- 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/orchestration/recall-internal-deps.ts +385 -0
- package/src/orchestration/recall-internal.ts +16 -375
- 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-FUYEVEX7.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
package/src/storage.ts
CHANGED
|
@@ -45,10 +45,7 @@ export { normalizeEntityName } from "./entity-id-normalization.js";
|
|
|
45
45
|
import { isErrnoCode } from "./utils/errno.js";
|
|
46
46
|
import { getCategoryDir, categoryDirName } from "./utils/category-dir.js";
|
|
47
47
|
import { decodeYamlScalar } from "./utils/yaml-scalar.js";
|
|
48
|
-
import {
|
|
49
|
-
qmdCollectionPathParts,
|
|
50
|
-
qmdResultPathCandidates,
|
|
51
|
-
} from "./orchestration/qmd-result-resolver.js";
|
|
48
|
+
import { qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
|
|
52
49
|
import {
|
|
53
50
|
clearMemoryCache,
|
|
54
51
|
getCachedEntities,
|
|
@@ -90,7 +87,6 @@ import {
|
|
|
90
87
|
readMaybeEncryptedFileBuffer,
|
|
91
88
|
readMaybeEncryptedFile,
|
|
92
89
|
writeMaybeEncryptedFile,
|
|
93
|
-
writeMaybeEncryptedFileFromChunks,
|
|
94
90
|
} from "./secure-store/secure-fs.js";
|
|
95
91
|
import {
|
|
96
92
|
isConsolidationOperator,
|
|
@@ -181,6 +177,12 @@ import {
|
|
|
181
177
|
CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,
|
|
182
178
|
type ContentHashIndexLockOptions,
|
|
183
179
|
} from "./storage/content-hash-index.js";
|
|
180
|
+
import {
|
|
181
|
+
TombstoneBlockedCaptureIndexHost,
|
|
182
|
+
buildExplicitCaptureDedupKey,
|
|
183
|
+
parseTombstoneBlockedOfflineSyncMemory,
|
|
184
|
+
type TombstoneBlockedCaptureIndexOptions,
|
|
185
|
+
} from "./storage/tombstone-blocked-capture-index.js";
|
|
184
186
|
export { ContentHashIndex, FactHashIndexNotAuthoritativeError };
|
|
185
187
|
export type { ContentHashIndexLockOptions };
|
|
186
188
|
export {
|
|
@@ -201,11 +203,7 @@ import {
|
|
|
201
203
|
readProjectedMemoryTimeline,
|
|
202
204
|
updateProjectedMemoryPath,
|
|
203
205
|
} from "./memory-projection-store.js";
|
|
204
|
-
import {
|
|
205
|
-
inferMemoryStatus,
|
|
206
|
-
isArchivedMemoryPath,
|
|
207
|
-
toMemoryPathRel,
|
|
208
|
-
} from "./memory-lifecycle-ledger-utils.js";
|
|
206
|
+
import { inferMemoryStatus, isArchivedMemoryPath, toMemoryPathRel } from "./memory-lifecycle-ledger-utils.js";
|
|
209
207
|
import { normalizeProjectionPreview, normalizeProjectionTags } from "./memory-projection-format.js";
|
|
210
208
|
import { parseFlexibleIsoTimestamp } from "./utils/iso-timestamp.js";
|
|
211
209
|
import {
|
|
@@ -652,8 +650,10 @@ function quotedArrayValues(raw: string): string[] {
|
|
|
652
650
|
while (i < inner.length) {
|
|
653
651
|
const ch = inner[i++];
|
|
654
652
|
if (ch === "\\" && i < inner.length) value += ch + inner[i++];
|
|
655
|
-
else if (ch === '"') {
|
|
656
|
-
|
|
653
|
+
else if (ch === '"') {
|
|
654
|
+
values.push(value);
|
|
655
|
+
break;
|
|
656
|
+
} else value += ch;
|
|
657
657
|
}
|
|
658
658
|
}
|
|
659
659
|
return values;
|
|
@@ -1698,6 +1698,8 @@ export interface MemoryWriteResult {
|
|
|
1698
1698
|
tombstoneBlocked: boolean;
|
|
1699
1699
|
/** The tombstone id that blocked the write, when `tombstoneBlocked`. */
|
|
1700
1700
|
blockedBy?: string;
|
|
1701
|
+
/** Existing pending-review memory id when a blocked write coalesced with it. */
|
|
1702
|
+
duplicateOf?: string;
|
|
1701
1703
|
}
|
|
1702
1704
|
|
|
1703
1705
|
/**
|
|
@@ -1706,9 +1708,7 @@ export interface MemoryWriteResult {
|
|
|
1706
1708
|
* loader: unknown/legacy shapes are dropped, never thrown on. Uses `in`/typeof
|
|
1707
1709
|
* narrowing (no inline casts) so a malformed entry can't slip through typed.
|
|
1708
1710
|
*/
|
|
1709
|
-
function parseExtractionRetryStateEntries(
|
|
1710
|
-
raw: unknown,
|
|
1711
|
-
): NonNullable<MetaState["extractionRetryState"]> {
|
|
1711
|
+
function parseExtractionRetryStateEntries(raw: unknown): NonNullable<MetaState["extractionRetryState"]> {
|
|
1712
1712
|
if (!Array.isArray(raw)) return [];
|
|
1713
1713
|
const valid: NonNullable<MetaState["extractionRetryState"]> = [];
|
|
1714
1714
|
for (const entry of raw) {
|
|
@@ -1821,17 +1821,10 @@ export interface WriteMemoryOptions {
|
|
|
1821
1821
|
*/
|
|
1822
1822
|
export type SealedWriteExtras = Omit<
|
|
1823
1823
|
WriteMemoryOptions,
|
|
1824
|
-
| "
|
|
1825
|
-
| "tags"
|
|
1826
|
-
| "entityRef"
|
|
1827
|
-
| "source"
|
|
1828
|
-
| "expiresAt"
|
|
1829
|
-
| "validAt"
|
|
1830
|
-
| "structuredAttributes"
|
|
1831
|
-
| "sourceConnector"
|
|
1824
|
+
"confidence" | "tags" | "entityRef" | "source" | "expiresAt" | "validAt" | "structuredAttributes" | "sourceConnector"
|
|
1832
1825
|
>;
|
|
1833
1826
|
|
|
1834
|
-
export class StorageManager {
|
|
1827
|
+
export class StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
1835
1828
|
private knowledgeIndexCache: { result: string; builtAt: number } | null = null;
|
|
1836
1829
|
private artifactIndexCache: { memories: MemoryFile[]; loadedAtMs: number; writeVersion: number } | null = null;
|
|
1837
1830
|
/** Read by storage/entity-store.ts (decomposition), hence not `private`. */
|
|
@@ -2031,7 +2024,7 @@ export class StorageManager {
|
|
|
2031
2024
|
* OUTSIDE `stateDir`, so a data write always touches; a write under `stateDir`
|
|
2032
2025
|
* is a pure state write and is skipped unless `touchStateWrites` is enabled.
|
|
2033
2026
|
*/
|
|
2034
|
-
|
|
2027
|
+
protected notifyCatalogWriteForPath(filePath: string): void {
|
|
2035
2028
|
if (!this.touchStateWrites && this.isStateFilePath(filePath)) return;
|
|
2036
2029
|
this.notifyCatalogWrite();
|
|
2037
2030
|
}
|
|
@@ -2180,10 +2173,7 @@ export class StorageManager {
|
|
|
2180
2173
|
private isColdOrArchiveTierPath(p: string): boolean {
|
|
2181
2174
|
const rel = path.relative(this.baseDir, p);
|
|
2182
2175
|
return (
|
|
2183
|
-
rel === "cold" ||
|
|
2184
|
-
rel === "archive" ||
|
|
2185
|
-
rel.startsWith(`cold${path.sep}`) ||
|
|
2186
|
-
rel.startsWith(`archive${path.sep}`)
|
|
2176
|
+
rel === "cold" || rel === "archive" || rel.startsWith(`cold${path.sep}`) || rel.startsWith(`archive${path.sep}`)
|
|
2187
2177
|
);
|
|
2188
2178
|
}
|
|
2189
2179
|
|
|
@@ -2302,8 +2292,9 @@ export class StorageManager {
|
|
|
2302
2292
|
* (falling back to the process-wide default); pass an explicit boolean to
|
|
2303
2293
|
* override per-instance (tests, memory-constrained readers).
|
|
2304
2294
|
*/
|
|
2305
|
-
hotMemoriesCacheEnabledOverride?: boolean
|
|
2295
|
+
hotMemoriesCacheEnabledOverride?: boolean
|
|
2306
2296
|
) {
|
|
2297
|
+
super();
|
|
2307
2298
|
this.hotMemoriesCacheEnabled =
|
|
2308
2299
|
hotMemoriesCacheEnabledOverride ??
|
|
2309
2300
|
StorageManager.hotMemoriesCacheDefaultByDir.get(baseDir) ??
|
|
@@ -2323,11 +2314,7 @@ export class StorageManager {
|
|
|
2323
2314
|
* StorageManager, including ephemeral recall-sub-stage instances. Only an
|
|
2324
2315
|
* explicit `false` disables; `undefined` (e.g. a cast test config missing
|
|
2325
2316
|
* the field) leaves the cache on. */
|
|
2326
|
-
static setHotMemoriesCacheDefault(
|
|
2327
|
-
memoryDir: string,
|
|
2328
|
-
enabled: boolean | undefined,
|
|
2329
|
-
ttlMs?: number,
|
|
2330
|
-
): void {
|
|
2317
|
+
static setHotMemoriesCacheDefault(memoryDir: string, enabled: boolean | undefined, ttlMs?: number): void {
|
|
2331
2318
|
// Register per-dir so each orchestrator's config reaches every
|
|
2332
2319
|
// StorageManager built over its own memory dir (Codex P2). The per-dir map
|
|
2333
2320
|
// is authoritative for every registered dir.
|
|
@@ -2459,7 +2446,7 @@ export class StorageManager {
|
|
|
2459
2446
|
}
|
|
2460
2447
|
}
|
|
2461
2448
|
|
|
2462
|
-
|
|
2449
|
+
protected bumpMemoryStatusVersion(): void {
|
|
2463
2450
|
this.bumpSharedVersion("memory-status", StorageManager.memoryStatusVersionByDir);
|
|
2464
2451
|
// Also bump the corpus sentinel so peer processes rescan the hot-memories
|
|
2465
2452
|
// cache after status/lifecycle/bulk mutations (issue #1902). invalidateAllForDir
|
|
@@ -2491,7 +2478,7 @@ export class StorageManager {
|
|
|
2491
2478
|
return this.readSharedVersion("memory-corpus", StorageManager.memoryCorpusVersionByDir);
|
|
2492
2479
|
}
|
|
2493
2480
|
|
|
2494
|
-
|
|
2481
|
+
protected bumpMemoryCorpusVersion(): void {
|
|
2495
2482
|
// Bump only — unlike bumpMemoryStatusVersion this must NOT invalidateAllForDir
|
|
2496
2483
|
// (that would drop the very hot entry the write path just patched).
|
|
2497
2484
|
this.bumpSharedVersion("memory-corpus", StorageManager.memoryCorpusVersionByDir);
|
|
@@ -2534,7 +2521,7 @@ export class StorageManager {
|
|
|
2534
2521
|
}
|
|
2535
2522
|
}
|
|
2536
2523
|
|
|
2537
|
-
|
|
2524
|
+
protected bumpArtifactWriteVersion(): number {
|
|
2538
2525
|
return this.bumpSharedVersion("artifact-write", StorageManager.artifactWriteVersionByDir);
|
|
2539
2526
|
}
|
|
2540
2527
|
|
|
@@ -2735,7 +2722,7 @@ export class StorageManager {
|
|
|
2735
2722
|
private readStorageSecureFile(filePath: string): Promise<string> {
|
|
2736
2723
|
return readMaybeEncryptedFile(filePath, this._secureStoreKey, this.baseDir);
|
|
2737
2724
|
}
|
|
2738
|
-
|
|
2725
|
+
protected writeStorageSecureFile(filePath: string, content: string | Buffer, forceEncrypt = false): Promise<void> {
|
|
2739
2726
|
const writeKey = this.resolveWriteKey(forceEncrypt);
|
|
2740
2727
|
return writeMaybeEncryptedFile(filePath, content, writeKey, {}, this.baseDir).then(() => {
|
|
2741
2728
|
// No manual sniff-cache update needed (issue #1909 round 10): this rewrite
|
|
@@ -2745,14 +2732,8 @@ export class StorageManager {
|
|
|
2745
2732
|
this.notifyCatalogWriteForPath(filePath);
|
|
2746
2733
|
});
|
|
2747
2734
|
}
|
|
2748
|
-
private writeStorageSecureFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
|
|
2749
|
-
const writeKey = this.resolveWriteKey();
|
|
2750
|
-
return writeMaybeEncryptedFileFromChunks(filePath, chunks, writeKey, {}, this.baseDir).then(() => {
|
|
2751
|
-
this.notifyCatalogWriteForPath(filePath);
|
|
2752
|
-
});
|
|
2753
|
-
}
|
|
2754
2735
|
|
|
2755
|
-
|
|
2736
|
+
protected assertManagedStoragePath(filePath: string, method: string): string {
|
|
2756
2737
|
const resolved = path.resolve(filePath);
|
|
2757
2738
|
const base = path.resolve(this.baseDir);
|
|
2758
2739
|
const rel = path.relative(base, resolved);
|
|
@@ -2924,56 +2905,18 @@ export class StorageManager {
|
|
|
2924
2905
|
}
|
|
2925
2906
|
}
|
|
2926
2907
|
|
|
2927
|
-
async writeOfflineSyncFile(filePath: string, content: Buffer): Promise<void> {
|
|
2928
|
-
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFile");
|
|
2929
|
-
await this.writeStorageSecureFile(target, content);
|
|
2930
|
-
await this.invalidateAfterOfflineSyncMutation(target);
|
|
2931
|
-
}
|
|
2932
|
-
|
|
2933
2908
|
async writeOfflineSyncStagingFile(filePath: string, content: Buffer): Promise<void> {
|
|
2934
2909
|
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncStagingFile");
|
|
2935
2910
|
await this.writeStorageSecureFile(target, content);
|
|
2936
2911
|
}
|
|
2937
2912
|
|
|
2938
|
-
async writeOfflineSyncFileChunks(filePath: string, chunks: AsyncIterable<Buffer>): Promise<void> {
|
|
2939
|
-
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFileChunks");
|
|
2940
|
-
await this.writeStorageSecureFileChunks(target, chunks);
|
|
2941
|
-
await this.invalidateAfterOfflineSyncMutation(target);
|
|
2942
|
-
}
|
|
2943
|
-
|
|
2944
|
-
async deleteOfflineSyncFile(filePath: string): Promise<void> {
|
|
2945
|
-
const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
|
|
2946
|
-
await unlink(target).catch((error: unknown) => {
|
|
2947
|
-
if (isErrnoCode(error, "ENOENT")) return;
|
|
2948
|
-
throw error;
|
|
2949
|
-
});
|
|
2950
|
-
await this.invalidateAfterOfflineSyncMutation(target);
|
|
2951
|
-
}
|
|
2952
|
-
|
|
2953
|
-
private async invalidateAfterOfflineSyncMutation(filePath: string): Promise<void> {
|
|
2954
|
-
// invalidateAllMemoriesCache() routes through the invalidateAllForDir()
|
|
2955
|
-
// chokepoint, which also covers the entity cache (issue #1535).
|
|
2956
|
-
this.invalidateAllMemoriesCache();
|
|
2957
|
-
this.invalidateKnowledgeIndexCache();
|
|
2958
|
-
// Force a corpus rebuild of the fact-hash index on next use (round 11: no
|
|
2959
|
-
// on-disk ready marker — the in-memory authoritative flag is the only gate).
|
|
2960
|
-
this.factHashIndexAuthoritative = false;
|
|
2961
|
-
if (filePath.includes(`${path.sep}cold${path.sep}`)) {
|
|
2962
|
-
this.invalidateColdMemoriesCache();
|
|
2963
|
-
}
|
|
2964
|
-
if (filePath.includes(`${path.sep}artifacts${path.sep}`)) {
|
|
2965
|
-
this.bumpArtifactWriteVersion();
|
|
2966
|
-
}
|
|
2967
|
-
this.bumpMemoryStatusVersion();
|
|
2968
|
-
}
|
|
2969
|
-
|
|
2970
2913
|
createContentHashIndex(): ContentHashIndex {
|
|
2971
2914
|
return new ContentHashIndex(
|
|
2972
2915
|
this.stateDir,
|
|
2973
2916
|
() => this._secureStoreKey,
|
|
2974
2917
|
() => this.resolveWriteKey(),
|
|
2975
2918
|
this.baseDir,
|
|
2976
|
-
this.factHashIndexLockOptions
|
|
2919
|
+
this.factHashIndexLockOptions
|
|
2977
2920
|
);
|
|
2978
2921
|
}
|
|
2979
2922
|
|
|
@@ -3262,7 +3205,29 @@ export class StorageManager {
|
|
|
3262
3205
|
});
|
|
3263
3206
|
return await store.rebuild(retired);
|
|
3264
3207
|
}
|
|
3265
|
-
|
|
3208
|
+
protected markFactHashIndexNotAuthoritative(): void {
|
|
3209
|
+
this.factHashIndexAuthoritative = false;
|
|
3210
|
+
}
|
|
3211
|
+
protected tombstoneBlockedCaptureIndexOptions(): TombstoneBlockedCaptureIndexOptions {
|
|
3212
|
+
return {
|
|
3213
|
+
stateDir: this.stateDir,
|
|
3214
|
+
memoryDir: this.baseDir,
|
|
3215
|
+
secureStoreKeyProvider: () => this._secureStoreKey,
|
|
3216
|
+
secureStoreWriteKeyProvider: () => this.resolveWriteKey(),
|
|
3217
|
+
lockOptions: () => this.factHashIndexLockOptions,
|
|
3218
|
+
parseMemory: (filePath, content) =>
|
|
3219
|
+
parseTombstoneBlockedOfflineSyncMemory(
|
|
3220
|
+
filePath,
|
|
3221
|
+
content,
|
|
3222
|
+
this.baseDir,
|
|
3223
|
+
parseFrontmatter,
|
|
3224
|
+
normalizeFrontmatterForPath,
|
|
3225
|
+
toMemoryPathRel
|
|
3226
|
+
),
|
|
3227
|
+
readAllMemories: () => this.readAllMemories(),
|
|
3228
|
+
readAllColdMemories: () => this.readAllColdMemories(),
|
|
3229
|
+
};
|
|
3230
|
+
}
|
|
3266
3231
|
private async getFactHashIndex(): Promise<ContentHashIndex> {
|
|
3267
3232
|
if (this.factHashIndex) {
|
|
3268
3233
|
return this.factHashIndex;
|
|
@@ -3282,7 +3247,6 @@ export class StorageManager {
|
|
|
3282
3247
|
}
|
|
3283
3248
|
return this.factHashIndexLoadPromise;
|
|
3284
3249
|
}
|
|
3285
|
-
|
|
3286
3250
|
/**
|
|
3287
3251
|
* Return the fact-hash index after ensuring it is authoritative — i.e. rebuilt
|
|
3288
3252
|
* from the durable fact corpus (issue #1909 review round 12). This is the ONE
|
|
@@ -3298,7 +3262,6 @@ export class StorageManager {
|
|
|
3298
3262
|
}
|
|
3299
3263
|
return this.getFactHashIndex();
|
|
3300
3264
|
}
|
|
3301
|
-
|
|
3302
3265
|
/**
|
|
3303
3266
|
* Return the shared fact-hash index instance, corpus-rebuilt under the lock
|
|
3304
3267
|
* when the lock is free. Unlike {@link getAuthoritativeFactHashIndex} this
|
|
@@ -3312,7 +3275,6 @@ export class StorageManager {
|
|
|
3312
3275
|
await this.ensureFactHashIndexAuthoritative();
|
|
3313
3276
|
return this.getFactHashIndex();
|
|
3314
3277
|
}
|
|
3315
|
-
|
|
3316
3278
|
private async ensureFactHashIndexAuthoritative(): Promise<boolean> {
|
|
3317
3279
|
if (this.factHashIndexAuthoritative === true) {
|
|
3318
3280
|
// PR #2016 review: authority is NOT permanent. A peer process can advance
|
|
@@ -3332,7 +3294,6 @@ export class StorageManager {
|
|
|
3332
3294
|
if (this.factHashIndexAuthoritativePromise) {
|
|
3333
3295
|
return this.factHashIndexAuthoritativePromise;
|
|
3334
3296
|
}
|
|
3335
|
-
|
|
3336
3297
|
this.factHashIndexAuthoritative = false;
|
|
3337
3298
|
this.factHashIndexAuthoritativePromise = (async () => {
|
|
3338
3299
|
// Round 11: ALWAYS rebuild the fact-hash index from the durable corpus on
|
|
@@ -3352,15 +3313,11 @@ export class StorageManager {
|
|
|
3352
3313
|
// hasFactContentHash()/isFactContentHashAuthoritative() fall back to the
|
|
3353
3314
|
// durable corpus so a stale loaded snapshot never answers as current.
|
|
3354
3315
|
const factHashIndex = await this.getFactHashIndex();
|
|
3355
|
-
const maxAttempts =
|
|
3356
|
-
|
|
3357
|
-
const baseMs =
|
|
3358
|
-
this.factHashIndexLockOptions.retryBaseMs ?? FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS;
|
|
3316
|
+
const maxAttempts = this.factHashIndexLockOptions.retryMaxAttempts ?? FACT_HASH_INDEX_REBUILD_MAX_ATTEMPTS;
|
|
3317
|
+
const baseMs = this.factHashIndexLockOptions.retryBaseMs ?? FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS;
|
|
3359
3318
|
let published = false;
|
|
3360
3319
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
3361
|
-
published = await factHashIndex.rebuildUnderLock(() =>
|
|
3362
|
-
this.rebuildFactHashIndexFromCorpus(factHashIndex),
|
|
3363
|
-
);
|
|
3320
|
+
published = await factHashIndex.rebuildUnderLock(() => this.rebuildFactHashIndexFromCorpus(factHashIndex));
|
|
3364
3321
|
if (published || attempt === maxAttempts - 1) break;
|
|
3365
3322
|
const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
|
|
3366
3323
|
await new Promise<void>((resolve) => setTimeout(resolve, wait));
|
|
@@ -3369,7 +3326,7 @@ export class StorageManager {
|
|
|
3369
3326
|
if (!published) {
|
|
3370
3327
|
log.warn(
|
|
3371
3328
|
`ensureFactHashIndexAuthoritative: fact-hash index lock unavailable after ${maxAttempts} attempt(s); ` +
|
|
3372
|
-
`index left non-authoritative (reads verify against the durable corpus; next use retries the locked rebuild)
|
|
3329
|
+
`index left non-authoritative (reads verify against the durable corpus; next use retries the locked rebuild)`
|
|
3373
3330
|
);
|
|
3374
3331
|
}
|
|
3375
3332
|
return published;
|
|
@@ -3378,7 +3335,6 @@ export class StorageManager {
|
|
|
3378
3335
|
});
|
|
3379
3336
|
return this.factHashIndexAuthoritativePromise;
|
|
3380
3337
|
}
|
|
3381
|
-
|
|
3382
3338
|
/**
|
|
3383
3339
|
* Repopulate `factHashIndex` in memory from the durable HOT+COLD corpus. Runs
|
|
3384
3340
|
* WHILE the per-index cross-process lock is held (see
|
|
@@ -3401,10 +3357,7 @@ export class StorageManager {
|
|
|
3401
3357
|
// survive the corpus rebuild, or a restart would drop the hash and let the
|
|
3402
3358
|
// next extraction re-create the demoted memory. Matches the hot+cold union
|
|
3403
3359
|
// that removeFactContentHashesForMemories already reconciles against.
|
|
3404
|
-
const existing = [
|
|
3405
|
-
...(await this.readAllMemories()),
|
|
3406
|
-
...(await this.readAllColdMemories()),
|
|
3407
|
-
];
|
|
3360
|
+
const existing = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
|
|
3408
3361
|
let legacyRecovered = 0;
|
|
3409
3362
|
for (const memory of existing) {
|
|
3410
3363
|
// #1909 review round 15 (PR #2016): the content-hash dedup index is
|
|
@@ -3444,7 +3397,6 @@ export class StorageManager {
|
|
|
3444
3397
|
);
|
|
3445
3398
|
}
|
|
3446
3399
|
}
|
|
3447
|
-
|
|
3448
3400
|
/**
|
|
3449
3401
|
* The content-hash the corpus rebuild registers for `memory`, or null when the
|
|
3450
3402
|
* body carries a citation from an unknown/custom template that cannot be
|
|
@@ -3536,9 +3488,7 @@ export class StorageManager {
|
|
|
3536
3488
|
* every append. A foreign write changes size/mtime, forcing a reload; this
|
|
3537
3489
|
* process's own appends refresh the identity in place. null => reload.
|
|
3538
3490
|
*/
|
|
3539
|
-
private behaviorSignalsKeyCache:
|
|
3540
|
-
| { identity: { size: number; mtimeMs: number }; keys: Set<string> }
|
|
3541
|
-
| null = null;
|
|
3491
|
+
private behaviorSignalsKeyCache: { identity: { size: number; mtimeMs: number }; keys: Set<string> } | null = null;
|
|
3542
3492
|
/**
|
|
3543
3493
|
* Per-instance serializer for `appendBehaviorSignals` (issue #1909 review
|
|
3544
3494
|
* round 3). The read→dedup→append→cache-commit transaction must run to
|
|
@@ -3700,6 +3650,26 @@ export class StorageManager {
|
|
|
3700
3650
|
return path.join(datedDir, `${id}.md`);
|
|
3701
3651
|
}
|
|
3702
3652
|
|
|
3653
|
+
private async findExistingTombstoneBlockedMemory(
|
|
3654
|
+
content: string,
|
|
3655
|
+
category: MemoryCategory,
|
|
3656
|
+
sourceConnector?: string
|
|
3657
|
+
): Promise<MemoryFile | null> {
|
|
3658
|
+
const identity = buildExplicitCaptureDedupKey(content, category, sourceConnector);
|
|
3659
|
+
const memories = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
|
|
3660
|
+
return (
|
|
3661
|
+
memories.find((memory) => {
|
|
3662
|
+
if (memory.frontmatter.status !== "pending_review" || !memory.frontmatter.blockedBy) return false;
|
|
3663
|
+
return (
|
|
3664
|
+
buildExplicitCaptureDedupKey(
|
|
3665
|
+
memory.content,
|
|
3666
|
+
memory.frontmatter.category,
|
|
3667
|
+
memory.frontmatter.sourceConnector
|
|
3668
|
+
) === identity
|
|
3669
|
+
);
|
|
3670
|
+
}) ?? null
|
|
3671
|
+
);
|
|
3672
|
+
}
|
|
3703
3673
|
async writeMemory(
|
|
3704
3674
|
category: MemoryCategory,
|
|
3705
3675
|
content: string,
|
|
@@ -3869,9 +3839,31 @@ export class StorageManager {
|
|
|
3869
3839
|
const fileContent = `${serializeFrontmatter(fm)}\n\n${sanitized.text}\n`;
|
|
3870
3840
|
|
|
3871
3841
|
const filePath = await this.resolveCategoryWritePath(category, id, today);
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3842
|
+
const persistFile = async (): Promise<MemoryFile | null> => {
|
|
3843
|
+
if (tombstoneBlocked) {
|
|
3844
|
+
const duplicate = await this.findExistingTombstoneBlockedMemory(sanitized.text, category, fm.sourceConnector);
|
|
3845
|
+
if (duplicate) return duplicate;
|
|
3846
|
+
}
|
|
3847
|
+
await this.snapshotBeforeWrite(filePath, "write");
|
|
3848
|
+
await this.writeTombstoneBlockedMemory(filePath, fileContent, fm, sanitized.text, async () => {
|
|
3849
|
+
this.invalidateAllMemoriesCache();
|
|
3850
|
+
});
|
|
3851
|
+
return null;
|
|
3852
|
+
};
|
|
3853
|
+
const duplicateBlocked = tombstoneBlocked
|
|
3854
|
+
? await this.withTombstoneBlockedCaptureWriteLock(
|
|
3855
|
+
persistFile,
|
|
3856
|
+
buildExplicitCaptureDedupKey(sanitized.text, category, fm.sourceConnector)
|
|
3857
|
+
)
|
|
3858
|
+
: await persistFile();
|
|
3859
|
+
if (duplicateBlocked) {
|
|
3860
|
+
return {
|
|
3861
|
+
id: duplicateBlocked.frontmatter.id,
|
|
3862
|
+
tombstoneBlocked: true,
|
|
3863
|
+
blockedBy: duplicateBlocked.frontmatter.blockedBy,
|
|
3864
|
+
duplicateOf: duplicateBlocked.frontmatter.id,
|
|
3865
|
+
};
|
|
3866
|
+
}
|
|
3875
3867
|
await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
|
|
3876
3868
|
this.notifyMemoryWrite(filePath);
|
|
3877
3869
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeMemory", {
|
|
@@ -3948,13 +3940,10 @@ export class StorageManager {
|
|
|
3948
3940
|
* `envelope.sourceReason` is access-layer metadata with no frontmatter
|
|
3949
3941
|
* field and is deliberately not persisted here.
|
|
3950
3942
|
*/
|
|
3951
|
-
async writeSealedMemory(
|
|
3952
|
-
envelope: SealedMemoryEnvelope,
|
|
3953
|
-
extras: SealedWriteExtras = {},
|
|
3954
|
-
): Promise<MemoryWriteResult> {
|
|
3943
|
+
async writeSealedMemory(envelope: SealedMemoryEnvelope, extras: SealedWriteExtras = {}): Promise<MemoryWriteResult> {
|
|
3955
3944
|
if (!isSealedMemoryEnvelope(envelope)) {
|
|
3956
3945
|
throw new Error(
|
|
3957
|
-
"writeSealedMemory: value is not a valid sealed memory envelope (fails the composeMemoryEnvelope contract)"
|
|
3946
|
+
"writeSealedMemory: value is not a valid sealed memory envelope (fails the composeMemoryEnvelope contract)"
|
|
3958
3947
|
);
|
|
3959
3948
|
}
|
|
3960
3949
|
const { category, content, options } = sealedWriteToLegacyArgs(envelope, extras);
|
|
@@ -3986,10 +3975,7 @@ export class StorageManager {
|
|
|
3986
3975
|
}
|
|
3987
3976
|
|
|
3988
3977
|
private async factContentHashPresentInCorpus(targetHash: string): Promise<boolean> {
|
|
3989
|
-
const existing = [
|
|
3990
|
-
...(await this.readAllMemories()),
|
|
3991
|
-
...(await this.readAllColdMemories()),
|
|
3992
|
-
];
|
|
3978
|
+
const existing = [...(await this.readAllMemories()), ...(await this.readAllColdMemories())];
|
|
3993
3979
|
for (const memory of existing) {
|
|
3994
3980
|
if (memory.frontmatter.category !== "fact") continue;
|
|
3995
3981
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
@@ -4341,7 +4327,7 @@ export class StorageManager {
|
|
|
4341
4327
|
this.baseDir,
|
|
4342
4328
|
this.getMemoryCorpusVersion(),
|
|
4343
4329
|
this.hotCacheKeyId(),
|
|
4344
|
-
this.hotCacheTtlMs()
|
|
4330
|
+
this.hotCacheTtlMs()
|
|
4345
4331
|
);
|
|
4346
4332
|
// Null-check, not truthiness (kilo WARNING): getCachedMemories returns []
|
|
4347
4333
|
// for a cached EMPTY corpus, which is falsy — a truthiness guard would
|
|
@@ -4372,11 +4358,7 @@ export class StorageManager {
|
|
|
4372
4358
|
// during the scan. The version guard prevents clobbering a newer patched +
|
|
4373
4359
|
// re-keyed entry; the keyId guard prevents publishing this key's decrypted
|
|
4374
4360
|
// corpus under a since-changed identity (Codex Medium, #1902).
|
|
4375
|
-
if (
|
|
4376
|
-
this.hotMemoriesCacheEnabled &&
|
|
4377
|
-
this.getMemoryCorpusVersion() === version &&
|
|
4378
|
-
this.hotCacheKeyId() === keyId
|
|
4379
|
-
) {
|
|
4361
|
+
if (this.hotMemoriesCacheEnabled && this.getMemoryCorpusVersion() === version && this.hotCacheKeyId() === keyId) {
|
|
4380
4362
|
setCachedMemories(this.baseDir, memories, version, keyId, this.hotCacheTtlMs());
|
|
4381
4363
|
}
|
|
4382
4364
|
return memories;
|
|
@@ -4455,7 +4437,7 @@ export class StorageManager {
|
|
|
4455
4437
|
* exclusively by invalidateColdMemoriesCache(), which is called only when
|
|
4456
4438
|
* cold content actually changes (hot→cold demotions, writeMemoryFileAtomic
|
|
4457
4439
|
* inside cold/, archiveMemory, etc.). */
|
|
4458
|
-
|
|
4440
|
+
protected invalidateAllMemoriesCache(): void {
|
|
4459
4441
|
deleteInFlightReadsForDir(this.baseDir);
|
|
4460
4442
|
// Bulk/ambiguous mutations drop the hot layer wholesale (below); bump the
|
|
4461
4443
|
// corpus sentinel too so PEER processes rescan instead of serving a warm
|
|
@@ -4506,19 +4488,17 @@ export class StorageManager {
|
|
|
4506
4488
|
*/
|
|
4507
4489
|
private async patchHotMemoriesCache(
|
|
4508
4490
|
opts: { addedPath?: string; removedPath?: string },
|
|
4509
|
-
scope: "memory-create" | "memory-mutate" = "memory-mutate"
|
|
4491
|
+
scope: "memory-create" | "memory-mutate" = "memory-mutate"
|
|
4510
4492
|
): Promise<void> {
|
|
4511
4493
|
const prevVersion = this.getMemoryCorpusVersion();
|
|
4512
4494
|
// Snapshot the key identity once (issue #1902, Codex Medium): a mid-await
|
|
4513
4495
|
// setSecureStoreKey change would otherwise let the post-await patch/re-key
|
|
4514
4496
|
// store this key's decrypted memory under a since-changed identity.
|
|
4515
4497
|
const keyId = this.hotCacheKeyId();
|
|
4516
|
-
const warm =
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
!this.isColdOrArchiveTierPath(opts.removedPath)
|
|
4521
|
-
) {
|
|
4498
|
+
const warm =
|
|
4499
|
+
this.hotMemoriesCacheEnabled &&
|
|
4500
|
+
getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
|
|
4501
|
+
if (warm && opts.removedPath !== undefined && !this.isColdOrArchiveTierPath(opts.removedPath)) {
|
|
4522
4502
|
updateCacheOnDelete(this.baseDir, opts.removedPath, keyId);
|
|
4523
4503
|
}
|
|
4524
4504
|
// Bump the corpus sentinel AND drop the in-flight slot BEFORE the awaited
|
|
@@ -4532,11 +4512,7 @@ export class StorageManager {
|
|
|
4532
4512
|
const { produced, exclusive } = this.bumpMemoryCorpusVersionExclusive();
|
|
4533
4513
|
deleteInFlightReadsForDir(this.baseDir);
|
|
4534
4514
|
let patchedOk = warm;
|
|
4535
|
-
if (
|
|
4536
|
-
warm &&
|
|
4537
|
-
opts.addedPath !== undefined &&
|
|
4538
|
-
!this.isColdOrArchiveTierPath(opts.addedPath)
|
|
4539
|
-
) {
|
|
4515
|
+
if (warm && opts.addedPath !== undefined && !this.isColdOrArchiveTierPath(opts.addedPath)) {
|
|
4540
4516
|
// Re-read + parse the ONE just-written file so the cached object is
|
|
4541
4517
|
// exactly what a fresh disk scan would yield — serialize→parse normalizes
|
|
4542
4518
|
// fields (e.g. an empty array becomes undefined), and the in-memory write
|
|
@@ -4566,7 +4542,13 @@ export class StorageManager {
|
|
|
4566
4542
|
patchedOk = false;
|
|
4567
4543
|
}
|
|
4568
4544
|
}
|
|
4569
|
-
if (
|
|
4545
|
+
if (
|
|
4546
|
+
patchedOk &&
|
|
4547
|
+
exclusive &&
|
|
4548
|
+
produced === prevVersion + 1 &&
|
|
4549
|
+
this.getMemoryCorpusVersion() === produced &&
|
|
4550
|
+
this.hotCacheKeyId() === keyId
|
|
4551
|
+
) {
|
|
4570
4552
|
// Re-key the patched entry (still tagged prevVersion) to the version THIS
|
|
4571
4553
|
// mutation produced — ONLY if no peer appended between prevVersion capture
|
|
4572
4554
|
// and our bump (produced === prevVersion + 1), our bump was exclusive, and
|
|
@@ -4609,7 +4591,7 @@ export class StorageManager {
|
|
|
4609
4591
|
* Finding UvUy (PR #402 round-11): bumping the sentinel here makes the
|
|
4610
4592
|
* per-process in-memory cache safe across process boundaries.
|
|
4611
4593
|
*/
|
|
4612
|
-
|
|
4594
|
+
protected invalidateColdMemoriesCache(): void {
|
|
4613
4595
|
const coldRoot = path.join(this.baseDir, "cold");
|
|
4614
4596
|
StorageManager.coldMemoriesCache.delete(coldRoot);
|
|
4615
4597
|
this.bumpColdWriteVersion();
|
|
@@ -4981,51 +4963,49 @@ export class StorageManager {
|
|
|
4981
4963
|
* Returns the new file path on success, null on failure.
|
|
4982
4964
|
*/
|
|
4983
4965
|
async archiveMemory(memory: MemoryFile, lifecycle?: MemoryLifecycleEventWriteOptions): Promise<string | null> {
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
}
|
|
5026
|
-
|
|
5027
|
-
return null;
|
|
5028
|
-
}
|
|
4966
|
+
const archiveCurrent = async (current: MemoryFile, markDurable: () => void): Promise<string | null> => {
|
|
4967
|
+
try {
|
|
4968
|
+
const now = lifecycle?.at ?? new Date();
|
|
4969
|
+
const today = now.toISOString().slice(0, 10);
|
|
4970
|
+
const destDir = path.join(this.archiveDir, today);
|
|
4971
|
+
await mkdir(destDir, { recursive: true });
|
|
4972
|
+
const updatedFm: MemoryFrontmatter = {
|
|
4973
|
+
...current.frontmatter,
|
|
4974
|
+
status: "archived",
|
|
4975
|
+
archivedAt: now.toISOString(),
|
|
4976
|
+
updated: now.toISOString(),
|
|
4977
|
+
};
|
|
4978
|
+
const fileContent = `${serializeFrontmatter(updatedFm)}\n\n${current.content}\n`;
|
|
4979
|
+
const destPath = path.join(destDir, path.basename(current.path));
|
|
4980
|
+
await this.writeStorageSecureFile(destPath, fileContent);
|
|
4981
|
+
await unlink(current.path);
|
|
4982
|
+
markDurable();
|
|
4983
|
+
markProjectedMemoryPathInvalid(this.baseDir, current.frontmatter.id);
|
|
4984
|
+
this.invalidateAllMemoriesCache();
|
|
4985
|
+
await this.appendGeneratedMemoryLifecycleEventFailOpen(
|
|
4986
|
+
"storage.archiveMemory",
|
|
4987
|
+
{
|
|
4988
|
+
memoryId: current.frontmatter.id,
|
|
4989
|
+
eventType: "archived",
|
|
4990
|
+
timestamp: updatedFm.archivedAt ?? updatedFm.updated,
|
|
4991
|
+
actor: lifecycle?.actor ?? "storage.archiveMemory",
|
|
4992
|
+
reasonCode: lifecycle?.reasonCode,
|
|
4993
|
+
before: this.summarizeLifecycleState(current.frontmatter, current.path),
|
|
4994
|
+
after: this.summarizeLifecycleState(updatedFm, destPath),
|
|
4995
|
+
relatedMemoryIds: lifecycle?.relatedMemoryIds,
|
|
4996
|
+
correlationId: lifecycle?.correlationId,
|
|
4997
|
+
},
|
|
4998
|
+
lifecycle?.ruleVersion
|
|
4999
|
+
);
|
|
5000
|
+
this.bumpMemoryStatusVersion();
|
|
5001
|
+
log.debug(`archived memory ${current.frontmatter.id} → ${destPath}`);
|
|
5002
|
+
return destPath;
|
|
5003
|
+
} catch (err) {
|
|
5004
|
+
log.warn(`failed to archive memory ${current.frontmatter.id}: ${err}`);
|
|
5005
|
+
return null;
|
|
5006
|
+
}
|
|
5007
|
+
};
|
|
5008
|
+
return await this.runTombstoneBlockedArchive(memory, archiveCurrent);
|
|
5029
5009
|
}
|
|
5030
5010
|
|
|
5031
5011
|
async readEntities(): Promise<string[]> {
|
|
@@ -5095,21 +5075,25 @@ export class StorageManager {
|
|
|
5095
5075
|
return null;
|
|
5096
5076
|
}
|
|
5097
5077
|
|
|
5078
|
+
|
|
5098
5079
|
async invalidateMemory(id: string): Promise<boolean> {
|
|
5099
5080
|
const memories = await this.readAllMemories();
|
|
5100
|
-
const memory = memories.find((
|
|
5081
|
+
const memory = memories.find((candidate) => candidate.frontmatter.id === id);
|
|
5101
5082
|
if (!memory) return false;
|
|
5102
5083
|
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5084
|
+
return await this.runTombstoneBlockedInvalidation(
|
|
5085
|
+
memory,
|
|
5086
|
+
async (current, rebuildMarker, markDurable) => {
|
|
5087
|
+
await unlink(current.path);
|
|
5088
|
+
markDurable();
|
|
5089
|
+
markProjectedMemoryPathInvalid(this.baseDir, id);
|
|
5090
|
+
this.invalidateAllMemoriesCache();
|
|
5091
|
+
await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
|
|
5092
|
+
this.bumpMemoryStatusVersion();
|
|
5093
|
+
log.debug(`invalidated memory ${id}`);
|
|
5094
|
+
return true;
|
|
5095
|
+
}
|
|
5096
|
+
);
|
|
5113
5097
|
}
|
|
5114
5098
|
|
|
5115
5099
|
async updateMemory(
|
|
@@ -5139,7 +5123,9 @@ export class StorageManager {
|
|
|
5139
5123
|
log.warn(`updated memory content sanitized for ${id}; violations=${sanitized.violations.join(", ")}`);
|
|
5140
5124
|
}
|
|
5141
5125
|
const fileContent = `${serializeFrontmatter(updated)}\n\n${sanitized.text}\n`;
|
|
5142
|
-
await this.
|
|
5126
|
+
await this.writeTombstoneBlockedUpdate(memory, fileContent, updated, sanitized.text, async () => {
|
|
5127
|
+
this.invalidateAllMemoriesCache();
|
|
5128
|
+
});
|
|
5143
5129
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
5144
5130
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.updateMemory", {
|
|
5145
5131
|
memoryId: id,
|
|
@@ -5174,11 +5160,14 @@ export class StorageManager {
|
|
|
5174
5160
|
const afterStatus = updated.status ?? "active";
|
|
5175
5161
|
|
|
5176
5162
|
const fileContent = `${serializeFrontmatter(updated)}\n\n${memory.content}\n`;
|
|
5177
|
-
await this.
|
|
5163
|
+
await this.writeTombstoneBlockedFrontmatter(memory, fileContent, updated, async () => {
|
|
5164
|
+
this.invalidateAllMemoriesCache();
|
|
5165
|
+
// Rebuild the blocked index from the post-write cold-tier cache.
|
|
5166
|
+
if (memory.path.includes(`${path.sep}cold${path.sep}`)) {
|
|
5167
|
+
this.invalidateColdMemoriesCache();
|
|
5168
|
+
}
|
|
5169
|
+
});
|
|
5178
5170
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
5179
|
-
// If the target file lives in cold/, bump the cold-version sentinel so
|
|
5180
|
-
// other processes detect the change on their next readAllColdMemories()
|
|
5181
|
-
// call (Finding UvUy fix).
|
|
5182
5171
|
if (memory.path.includes(`${path.sep}cold${path.sep}`)) {
|
|
5183
5172
|
this.invalidateColdMemoriesCache();
|
|
5184
5173
|
}
|
|
@@ -5238,19 +5227,16 @@ export class StorageManager {
|
|
|
5238
5227
|
for (const m of memories) {
|
|
5239
5228
|
if (!m.frontmatter.expiresAt) continue;
|
|
5240
5229
|
const expiresAt = new Date(m.frontmatter.expiresAt).getTime();
|
|
5241
|
-
if (expiresAt
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
} catch {
|
|
5252
|
-
// Ignore
|
|
5253
|
-
}
|
|
5230
|
+
if (expiresAt >= now) continue;
|
|
5231
|
+
const removed = await this.deleteMemoryForMaintenance(m, (current) => {
|
|
5232
|
+
const currentExpiresAt = current.frontmatter.expiresAt
|
|
5233
|
+
? new Date(current.frontmatter.expiresAt).getTime()
|
|
5234
|
+
: Number.NaN;
|
|
5235
|
+
return Number.isFinite(currentExpiresAt) && currentExpiresAt < now;
|
|
5236
|
+
});
|
|
5237
|
+
if (removed) {
|
|
5238
|
+
deleted.push(removed);
|
|
5239
|
+
log.debug(`cleaned expired memory ${removed.frontmatter.id} (TTL expired)`);
|
|
5254
5240
|
}
|
|
5255
5241
|
}
|
|
5256
5242
|
|
|
@@ -5355,7 +5341,7 @@ export class StorageManager {
|
|
|
5355
5341
|
this.memoryLifecycleLedgerPath,
|
|
5356
5342
|
(p) => this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, p),
|
|
5357
5343
|
serializeLifecycleAppendPayload(events),
|
|
5358
|
-
{ writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) }
|
|
5344
|
+
{ writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) }
|
|
5359
5345
|
);
|
|
5360
5346
|
return events.length;
|
|
5361
5347
|
}
|
|
@@ -5367,7 +5353,7 @@ export class StorageManager {
|
|
|
5367
5353
|
this.memoryLifecycleLedgerPath,
|
|
5368
5354
|
{ writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) },
|
|
5369
5355
|
(p) => this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, p),
|
|
5370
|
-
() => this.ensureDirectories()
|
|
5356
|
+
() => this.ensureDirectories()
|
|
5371
5357
|
);
|
|
5372
5358
|
}
|
|
5373
5359
|
|
|
@@ -5380,20 +5366,15 @@ export class StorageManager {
|
|
|
5380
5366
|
this.memoryLifecycleLedgerPath,
|
|
5381
5367
|
{ writeSecure: (p, c) => this.writeStorageSecureFile(p, c), readSecure: (p) => this.readStorageSecureFile(p) },
|
|
5382
5368
|
(p) => this.appendStorageSecureFile(this.memoryLifecycleLedgerPath, p),
|
|
5383
|
-
() => this.ensureDirectories()
|
|
5369
|
+
() => this.ensureDirectories()
|
|
5384
5370
|
);
|
|
5385
5371
|
}
|
|
5386
5372
|
|
|
5387
5373
|
/** Drain pending lifecycle spills for any ledger inside this storage root.
|
|
5388
5374
|
* The offline CLI uses this path-aware variant for per-namespace ledgers so
|
|
5389
5375
|
* secure-store encryption and path-bound authentication remain consistent. */
|
|
5390
|
-
async drainPendingMemoryLifecycleEventsForSyncAt(
|
|
5391
|
-
ledgerPath
|
|
5392
|
-
): Promise<DrainPendingLifecycleForSyncResult> {
|
|
5393
|
-
const target = this.assertManagedStoragePath(
|
|
5394
|
-
ledgerPath,
|
|
5395
|
-
"storage.drainPendingMemoryLifecycleEventsForSyncAt",
|
|
5396
|
-
);
|
|
5376
|
+
async drainPendingMemoryLifecycleEventsForSyncAt(ledgerPath: string): Promise<DrainPendingLifecycleForSyncResult> {
|
|
5377
|
+
const target = this.assertManagedStoragePath(ledgerPath, "storage.drainPendingMemoryLifecycleEventsForSyncAt");
|
|
5397
5378
|
return drainPendingLifecycleLedgerForSync(
|
|
5398
5379
|
target,
|
|
5399
5380
|
{
|
|
@@ -5403,7 +5384,7 @@ export class StorageManager {
|
|
|
5403
5384
|
(payload) => this.appendStorageSecureFile(target, payload),
|
|
5404
5385
|
async () => {
|
|
5405
5386
|
await mkdir(path.dirname(target), { recursive: true });
|
|
5406
|
-
}
|
|
5387
|
+
}
|
|
5407
5388
|
);
|
|
5408
5389
|
}
|
|
5409
5390
|
|
|
@@ -5416,7 +5397,7 @@ export class StorageManager {
|
|
|
5416
5397
|
async writeMemoryLifecycleLedgerContent(
|
|
5417
5398
|
content: string | Buffer,
|
|
5418
5399
|
targetPath: string = this.memoryLifecycleLedgerPath,
|
|
5419
|
-
forceEncrypt = false
|
|
5400
|
+
forceEncrypt = false
|
|
5420
5401
|
): Promise<void> {
|
|
5421
5402
|
await this.ensureDirectories();
|
|
5422
5403
|
await this.writeStorageSecureFile(targetPath, content, forceEncrypt);
|
|
@@ -5664,20 +5645,19 @@ export class StorageManager {
|
|
|
5664
5645
|
}
|
|
5665
5646
|
async readAllMemoryLifecycleEventsForCompaction(): Promise<MemoryLifecycleEvent[]> {
|
|
5666
5647
|
if (!(await probeEncryptedRegularFileHeader(this.memoryLifecycleLedgerPath))) {
|
|
5667
|
-
return readAllLifecycleEventsFromLedger(
|
|
5668
|
-
this.memoryLifecycleLedgerPath,
|
|
5669
|
-
(p) => this.readStorageSecureFile(p),
|
|
5670
|
-
);
|
|
5648
|
+
return readAllLifecycleEventsFromLedger(this.memoryLifecycleLedgerPath, (p) => this.readStorageSecureFile(p));
|
|
5671
5649
|
}
|
|
5672
|
-
return readAllLifecycleEventsFromLedgerBuffer(
|
|
5673
|
-
this.
|
|
5674
|
-
(p) => readMaybeEncryptedFileBuffer(p, this._secureStoreKey, this.baseDir),
|
|
5650
|
+
return readAllLifecycleEventsFromLedgerBuffer(this.memoryLifecycleLedgerPath, (p) =>
|
|
5651
|
+
readMaybeEncryptedFileBuffer(p, this._secureStoreKey, this.baseDir)
|
|
5675
5652
|
);
|
|
5676
5653
|
}
|
|
5677
5654
|
|
|
5678
5655
|
async readMemoryLifecycleEvents(limit: number = 200): Promise<MemoryLifecycleEvent[]> {
|
|
5679
5656
|
return readBoundedLifecycleEventsFromLedger(
|
|
5680
|
-
this.memoryLifecycleLedgerPath,
|
|
5657
|
+
this.memoryLifecycleLedgerPath,
|
|
5658
|
+
(p) => this.readStorageSecureFile(p),
|
|
5659
|
+
limit
|
|
5660
|
+
);
|
|
5681
5661
|
}
|
|
5682
5662
|
|
|
5683
5663
|
async writeCompressionGuidelines(content: string): Promise<void> {
|
|
@@ -6366,20 +6346,19 @@ export class StorageManager {
|
|
|
6366
6346
|
if (!isResolved) continue;
|
|
6367
6347
|
|
|
6368
6348
|
const updatedAt = new Date(m.frontmatter.updated).getTime();
|
|
6369
|
-
if (updatedAt
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
log.debug(`cleaned expired commitment ${m.frontmatter.id}`);
|
|
6380
|
-
} catch {
|
|
6381
|
-
// Ignore
|
|
6349
|
+
if (updatedAt >= cutoff) continue;
|
|
6350
|
+
try {
|
|
6351
|
+
const removed = await this.deleteMemoryForMaintenance(m, (current) => {
|
|
6352
|
+
const currentResolved = current.frontmatter.tags.some((tag) => tag === "fulfilled" || tag === "expired");
|
|
6353
|
+
const currentUpdatedAt = new Date(current.frontmatter.updated).getTime();
|
|
6354
|
+
return currentResolved && currentUpdatedAt < cutoff;
|
|
6355
|
+
});
|
|
6356
|
+
if (removed) {
|
|
6357
|
+
deleted.push(removed);
|
|
6358
|
+
log.debug(`cleaned expired commitment ${removed.frontmatter.id}`);
|
|
6382
6359
|
}
|
|
6360
|
+
} catch {
|
|
6361
|
+
// Ignore
|
|
6383
6362
|
}
|
|
6384
6363
|
}
|
|
6385
6364
|
|
|
@@ -6416,7 +6395,8 @@ export class StorageManager {
|
|
|
6416
6395
|
// different identity than the one that decrypted this corpus.
|
|
6417
6396
|
const keyId = this.hotCacheKeyId();
|
|
6418
6397
|
const warm =
|
|
6419
|
-
this.hotMemoriesCacheEnabled &&
|
|
6398
|
+
this.hotMemoriesCacheEnabled &&
|
|
6399
|
+
getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
|
|
6420
6400
|
// Record each applied patch so the end-of-flush re-key can re-apply them on
|
|
6421
6401
|
// top of whatever is cached at prevVersion — robust to a concurrent scan
|
|
6422
6402
|
// that republished an UNpatched corpus mid-flush (Cursor Medium #1902).
|
|
@@ -6469,7 +6449,13 @@ export class StorageManager {
|
|
|
6469
6449
|
// and could republish it at the old version, clobbering our patches. New
|
|
6470
6450
|
// readers now start a fresh scan at the bumped version.
|
|
6471
6451
|
deleteInFlightReadsForDir(this.baseDir);
|
|
6472
|
-
if (
|
|
6452
|
+
if (
|
|
6453
|
+
warm &&
|
|
6454
|
+
exclusive &&
|
|
6455
|
+
produced === prevVersion + 1 &&
|
|
6456
|
+
this.getMemoryCorpusVersion() === produced &&
|
|
6457
|
+
this.hotCacheKeyId() === keyId
|
|
6458
|
+
) {
|
|
6473
6459
|
const cur = getCachedMemories(this.baseDir, prevVersion, keyId);
|
|
6474
6460
|
if (cur) {
|
|
6475
6461
|
// Re-apply our patches on top of whatever is cached at prevVersion
|
|
@@ -6482,7 +6468,10 @@ export class StorageManager {
|
|
|
6482
6468
|
const reapplied = cur.map((m) => {
|
|
6483
6469
|
const patch = appliedPatches.get(path.resolve(m.path));
|
|
6484
6470
|
return patch
|
|
6485
|
-
? {
|
|
6471
|
+
? {
|
|
6472
|
+
...m,
|
|
6473
|
+
frontmatter: { ...m.frontmatter, accessCount: patch.accessCount, lastAccessed: patch.lastAccessed },
|
|
6474
|
+
}
|
|
6486
6475
|
: m;
|
|
6487
6476
|
});
|
|
6488
6477
|
setCachedMemories(this.baseDir, reapplied, produced, keyId, this.hotCacheTtlMs());
|
|
@@ -6516,7 +6505,7 @@ export class StorageManager {
|
|
|
6516
6505
|
*/
|
|
6517
6506
|
async findExistingMemoryPaths(
|
|
6518
6507
|
ids: string[],
|
|
6519
|
-
preferredPaths: Map<string, string[]> = new Map()
|
|
6508
|
+
preferredPaths: Map<string, string[]> = new Map()
|
|
6520
6509
|
): Promise<Map<string, string[]>> {
|
|
6521
6510
|
if (ids.length === 0) return new Map();
|
|
6522
6511
|
const wantedIds = new Set(ids);
|
|
@@ -6535,12 +6524,8 @@ export class StorageManager {
|
|
|
6535
6524
|
const preferred = preferredPaths.get(id) ?? [];
|
|
6536
6525
|
const preferredMatches: string[] = [];
|
|
6537
6526
|
for (const preferredPath of preferred) {
|
|
6538
|
-
const directCandidates = new Set(
|
|
6539
|
-
|
|
6540
|
-
);
|
|
6541
|
-
const directMatch = existingPaths.find((filePath) =>
|
|
6542
|
-
directCandidates.has(path.resolve(filePath)),
|
|
6543
|
-
);
|
|
6527
|
+
const directCandidates = new Set(qmdResultPathCandidates(this.baseDir, preferredPath));
|
|
6528
|
+
const directMatch = existingPaths.find((filePath) => directCandidates.has(path.resolve(filePath)));
|
|
6544
6529
|
if (directMatch) {
|
|
6545
6530
|
preferredMatches.push(directMatch);
|
|
6546
6531
|
continue;
|
|
@@ -6642,7 +6627,11 @@ export class StorageManager {
|
|
|
6642
6627
|
return `projection ${age} stale, last rebuilt ${rebuiltAt}`;
|
|
6643
6628
|
});
|
|
6644
6629
|
return readBoundedLifecycleEventsFromLedger(
|
|
6645
|
-
this.memoryLifecycleLedgerPath,
|
|
6630
|
+
this.memoryLifecycleLedgerPath,
|
|
6631
|
+
(p) => this.readStorageSecureFile(p),
|
|
6632
|
+
cappedLimit,
|
|
6633
|
+
memoryId
|
|
6634
|
+
);
|
|
6646
6635
|
}
|
|
6647
6636
|
|
|
6648
6637
|
// ---------------------------------------------------------------------------
|
|
@@ -6742,12 +6731,19 @@ export class StorageManager {
|
|
|
6742
6731
|
|
|
6743
6732
|
const filePath = await this.resolveCategoryWritePath(category, id, today);
|
|
6744
6733
|
|
|
6745
|
-
await this.
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6734
|
+
return await this.writeTombstoneBlockedChunk(
|
|
6735
|
+
filePath,
|
|
6736
|
+
fileContent,
|
|
6737
|
+
fm,
|
|
6738
|
+
sanitized.text,
|
|
6739
|
+
() => this.findExistingTombstoneBlockedMemory(sanitized.text, category, fm.sourceConnector),
|
|
6740
|
+
async () => {
|
|
6741
|
+
// Keep the version-keyed hot-memories cache coherent with the new chunk
|
|
6742
|
+
// file (issue #1902) — same single-file patch path writeMemory uses.
|
|
6743
|
+
await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
|
|
6744
|
+
log.debug(`wrote chunk ${id} (${chunkIndex + 1}/${chunkTotal}) to ${filePath}`);
|
|
6745
|
+
}
|
|
6746
|
+
);
|
|
6751
6747
|
}
|
|
6752
6748
|
|
|
6753
6749
|
/**
|
|
@@ -6835,7 +6831,12 @@ export class StorageManager {
|
|
|
6835
6831
|
|
|
6836
6832
|
// Audit-trail correction — sealed even INSIDE the engine (#2022 review).
|
|
6837
6833
|
const auditBody = `Superseded: ${oldMemory.content}\n\nReason: ${reason}`;
|
|
6838
|
-
const auditInput = {
|
|
6834
|
+
const auditInput = {
|
|
6835
|
+
content: auditBody,
|
|
6836
|
+
category: "correction" as const,
|
|
6837
|
+
confidence: 1.0,
|
|
6838
|
+
tags: ["supersession", "auto-resolved"],
|
|
6839
|
+
};
|
|
6839
6840
|
const auditEnvelope = composeMemoryEnvelope(auditInput, { source: "contradiction-detection" });
|
|
6840
6841
|
await this.writeSealedMemory(auditEnvelope, { lineage: [oldMemoryId, newMemoryId] });
|
|
6841
6842
|
|