@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
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
TombstoneStore,
|
|
14
14
|
buildRetiredFactTombstoneInputs,
|
|
15
15
|
collectRetiredMemoriesForRebuild
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-QNWXCJ76.js";
|
|
17
17
|
import {
|
|
18
18
|
computeSupersessionKey,
|
|
19
19
|
normalizeSupersessionKey,
|
|
@@ -42,13 +42,10 @@ import {
|
|
|
42
42
|
readMaybeEncryptedLines,
|
|
43
43
|
readMemoryActionEventRowsFromLines,
|
|
44
44
|
serializeLifecycleAppendPayload
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-MJCGLM25.js";
|
|
46
46
|
import {
|
|
47
47
|
namespaceIdentityFromToken
|
|
48
48
|
} from "./chunk-EGGE52UU.js";
|
|
49
|
-
import {
|
|
50
|
-
withHeldFileLock
|
|
51
|
-
} from "./chunk-WL5EBRDH.js";
|
|
52
49
|
import {
|
|
53
50
|
rewriteProjectedEntityReferences,
|
|
54
51
|
rewriteProjectedMemoryEntityReference
|
|
@@ -57,15 +54,6 @@ import {
|
|
|
57
54
|
assertPathInsideRoot,
|
|
58
55
|
pathIsInside
|
|
59
56
|
} from "./chunk-GCASGYIO.js";
|
|
60
|
-
import {
|
|
61
|
-
markProjectedMemoryPathInvalid,
|
|
62
|
-
openProjectionReadonly,
|
|
63
|
-
readProjectedGovernanceRecord,
|
|
64
|
-
readProjectedMemoryBrowse,
|
|
65
|
-
readProjectedMemoryState,
|
|
66
|
-
readProjectedMemoryTimeline,
|
|
67
|
-
updateProjectedMemoryPath
|
|
68
|
-
} from "./chunk-3CSX3ETE.js";
|
|
69
57
|
import {
|
|
70
58
|
inferMemoryStatus,
|
|
71
59
|
isArchivedMemoryPath,
|
|
@@ -91,6 +79,26 @@ import {
|
|
|
91
79
|
parseFaithfulnessField,
|
|
92
80
|
serializeFaithfulnessFields
|
|
93
81
|
} from "./chunk-JYIJNNN3.js";
|
|
82
|
+
import {
|
|
83
|
+
CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS,
|
|
84
|
+
ContentHashIndex,
|
|
85
|
+
FactHashIndexNotAuthoritativeError,
|
|
86
|
+
TombstoneBlockedCaptureIndexHost,
|
|
87
|
+
buildExplicitCaptureDedupKey,
|
|
88
|
+
parseTombstoneBlockedOfflineSyncMemory
|
|
89
|
+
} from "./chunk-FBUE6PD3.js";
|
|
90
|
+
import {
|
|
91
|
+
withHeldFileLock
|
|
92
|
+
} from "./chunk-FFAZ2XFU.js";
|
|
93
|
+
import {
|
|
94
|
+
markProjectedMemoryPathInvalid,
|
|
95
|
+
openProjectionReadonly,
|
|
96
|
+
readProjectedGovernanceRecord,
|
|
97
|
+
readProjectedMemoryBrowse,
|
|
98
|
+
readProjectedMemoryState,
|
|
99
|
+
readProjectedMemoryTimeline,
|
|
100
|
+
updateProjectedMemoryPath
|
|
101
|
+
} from "./chunk-3CSX3ETE.js";
|
|
94
102
|
import {
|
|
95
103
|
composeMemoryEnvelope,
|
|
96
104
|
isSealedMemoryEnvelope,
|
|
@@ -126,10 +134,6 @@ import {
|
|
|
126
134
|
getInFlightRead,
|
|
127
135
|
setInFlightRead
|
|
128
136
|
} from "./chunk-PDJTHRAW.js";
|
|
129
|
-
import {
|
|
130
|
-
computeContentHash,
|
|
131
|
-
normalizeContent
|
|
132
|
-
} from "./chunk-W5H6ZRPT.js";
|
|
133
137
|
import {
|
|
134
138
|
isConsolidationOperator,
|
|
135
139
|
isValidDerivedFromEntry
|
|
@@ -164,9 +168,8 @@ import {
|
|
|
164
168
|
probeEncryptedRegularFileHeader,
|
|
165
169
|
readMaybeEncryptedFile,
|
|
166
170
|
readMaybeEncryptedFileBuffer,
|
|
167
|
-
writeMaybeEncryptedFile
|
|
168
|
-
|
|
169
|
-
} from "./chunk-AIK5W3IW.js";
|
|
171
|
+
writeMaybeEncryptedFile
|
|
172
|
+
} from "./chunk-5TFP4CUL.js";
|
|
170
173
|
import {
|
|
171
174
|
isErrnoCode
|
|
172
175
|
} from "./chunk-5UZXUTVO.js";
|
|
@@ -208,16 +211,16 @@ import {
|
|
|
208
211
|
readdir as readdir5,
|
|
209
212
|
readFile as readFile2,
|
|
210
213
|
realpath as realpath3,
|
|
211
|
-
stat as
|
|
214
|
+
stat as stat2,
|
|
212
215
|
writeFile as writeFile2,
|
|
213
|
-
mkdir
|
|
216
|
+
mkdir,
|
|
214
217
|
unlink as unlink3,
|
|
215
218
|
appendFile,
|
|
216
219
|
open
|
|
217
220
|
} from "fs/promises";
|
|
218
221
|
import { appendFileSync, createReadStream, mkdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
219
222
|
import { createHash as createHash5 } from "crypto";
|
|
220
|
-
import
|
|
223
|
+
import path9 from "path";
|
|
221
224
|
|
|
222
225
|
// src/storage/memory-read-store.ts
|
|
223
226
|
import { lstat, readdir, realpath, stat } from "fs/promises";
|
|
@@ -1650,26 +1653,26 @@ function assertSafeEntityId(entityId) {
|
|
|
1650
1653
|
}
|
|
1651
1654
|
}
|
|
1652
1655
|
async function assertNotSymlink(filePath, label) {
|
|
1653
|
-
let
|
|
1656
|
+
let stat3;
|
|
1654
1657
|
try {
|
|
1655
|
-
|
|
1658
|
+
stat3 = await lstat2(filePath);
|
|
1656
1659
|
} catch (error) {
|
|
1657
1660
|
if (isErrnoCode(error, "ENOENT")) return;
|
|
1658
1661
|
throw error;
|
|
1659
1662
|
}
|
|
1660
|
-
if (
|
|
1663
|
+
if (stat3.isSymbolicLink()) {
|
|
1661
1664
|
throw new Error(`Refusing entity canonical-id migration through symlink: ${label}.`);
|
|
1662
1665
|
}
|
|
1663
1666
|
}
|
|
1664
1667
|
async function assertNotMemorySymlink(filePath) {
|
|
1665
|
-
let
|
|
1668
|
+
let stat3;
|
|
1666
1669
|
try {
|
|
1667
|
-
|
|
1670
|
+
stat3 = await lstat2(filePath);
|
|
1668
1671
|
} catch (error) {
|
|
1669
1672
|
if (isErrnoCode(error, "ENOENT")) return;
|
|
1670
1673
|
throw error;
|
|
1671
1674
|
}
|
|
1672
|
-
if (
|
|
1675
|
+
if (stat3.isSymbolicLink()) {
|
|
1673
1676
|
throw new Error(`Refusing entity canonical-id migration through symlinked memory: ${filePath}.`);
|
|
1674
1677
|
}
|
|
1675
1678
|
}
|
|
@@ -4175,14 +4178,14 @@ var FusionArtifactStore = class {
|
|
|
4175
4178
|
*/
|
|
4176
4179
|
async assertFusionDirNotSymlinked() {
|
|
4177
4180
|
const fusionDir = path7.join(this.wearablesDir, FUSION_DIR_NAME);
|
|
4178
|
-
let
|
|
4181
|
+
let stat3;
|
|
4179
4182
|
try {
|
|
4180
|
-
|
|
4183
|
+
stat3 = await this.io.lstat(fusionDir);
|
|
4181
4184
|
} catch (err) {
|
|
4182
4185
|
if (err.code === "ENOENT") return;
|
|
4183
4186
|
throw err;
|
|
4184
4187
|
}
|
|
4185
|
-
if (
|
|
4188
|
+
if (stat3.isSymbolicLink) {
|
|
4186
4189
|
throw new Error(
|
|
4187
4190
|
"wearable fusion dir '_fusion' is a symbolic link \u2014 refusing to follow it even when it resolves inside the memory dir (AGENTS.md pattern #3)"
|
|
4188
4191
|
);
|
|
@@ -4614,14 +4617,14 @@ var MeetingRecordStore = class {
|
|
|
4614
4617
|
return path8.join(this.meetingsDir, first);
|
|
4615
4618
|
}
|
|
4616
4619
|
async assertNotSymlink(target, kind) {
|
|
4617
|
-
let
|
|
4620
|
+
let stat3;
|
|
4618
4621
|
try {
|
|
4619
|
-
|
|
4622
|
+
stat3 = await this.io.lstat(target);
|
|
4620
4623
|
} catch (err) {
|
|
4621
4624
|
if (err.code === "ENOENT") return;
|
|
4622
4625
|
throw err;
|
|
4623
4626
|
}
|
|
4624
|
-
if (
|
|
4627
|
+
if (stat3.isSymbolicLink) {
|
|
4625
4628
|
const label = path8.relative(this.memoryDir, target) || MEETINGS_DIR_NAME;
|
|
4626
4629
|
throw new Error(
|
|
4627
4630
|
`meetings ${kind} '${label}' is a symbolic link \u2014 refusing to follow it even when it resolves inside the memory dir (AGENTS.md pattern #3)`
|
|
@@ -5038,471 +5041,6 @@ function isEntitySynthesisStale(entity) {
|
|
|
5038
5041
|
return entity.timeline.length > entity.synthesisTimelineCount || structuredFactCount > (entity.synthesisStructuredFactCount ?? 0) || structuredFactDigest !== storedStructuredFactDigest;
|
|
5039
5042
|
}
|
|
5040
5043
|
|
|
5041
|
-
// src/storage/content-hash-index.ts
|
|
5042
|
-
import { mkdir, stat as stat2 } from "fs/promises";
|
|
5043
|
-
import path9 from "path";
|
|
5044
|
-
var CONTENT_HASH_INDEX_LOCK_STALE_MS = 3e4;
|
|
5045
|
-
var CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS = 5;
|
|
5046
|
-
var CONTENT_HASH_INDEX_RETRY_BASE_MS = 500;
|
|
5047
|
-
var CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS = 5e3;
|
|
5048
|
-
var FactHashIndexNotAuthoritativeError = class extends Error {
|
|
5049
|
-
constructor(stateDir) {
|
|
5050
|
-
super(
|
|
5051
|
-
`fact-hash index is not authoritative: the cross-process rebuild lock for ${stateDir} could not be acquired within the bounded retry budget`
|
|
5052
|
-
);
|
|
5053
|
-
this.name = "FactHashIndexNotAuthoritativeError";
|
|
5054
|
-
}
|
|
5055
|
-
};
|
|
5056
|
-
var ContentHashIndex = class _ContentHashIndex {
|
|
5057
|
-
hashes = /* @__PURE__ */ new Set();
|
|
5058
|
-
dirty = false;
|
|
5059
|
-
/**
|
|
5060
|
-
* Hashes explicitly removed since the last successful save (issue #1909 review
|
|
5061
|
-
* round 8 thread 3). Tracked separately so `saveMergingWithDisk` can be
|
|
5062
|
-
* removal-AWARE: under the file lock it reads the latest on-disk set, DROPS
|
|
5063
|
-
* these, then unions our additions — so a removal batch cannot resurrect a
|
|
5064
|
-
* hash (union alone would) and cannot clobber a concurrent extraction's
|
|
5065
|
-
* appended hash (blind overwrite would). Consumed + cleared by every save.
|
|
5066
|
-
*/
|
|
5067
|
-
removed = /* @__PURE__ */ new Set();
|
|
5068
|
-
/**
|
|
5069
|
-
* Hashes added by THIS instance since the last successful save (issue #1909
|
|
5070
|
-
* review round 9 finding 3). Tracked separately from the loaded snapshot so a
|
|
5071
|
-
* reconciling save publishes `(on-disk \ removed) ∪ added` — it re-applies only
|
|
5072
|
-
* OUR additions, never the stale loaded entries. Without this, seeding the
|
|
5073
|
-
* merge from `this.hashes` (loaded ∪ added) would resurrect a hash a peer
|
|
5074
|
-
* removed on disk while this instance held a stale snapshot.
|
|
5075
|
-
*/
|
|
5076
|
-
added = /* @__PURE__ */ new Set();
|
|
5077
|
-
filePath;
|
|
5078
|
-
secureStoreKeyProvider;
|
|
5079
|
-
secureStoreWriteKeyProvider;
|
|
5080
|
-
memoryDir;
|
|
5081
|
-
lockOptions;
|
|
5082
|
-
/**
|
|
5083
|
-
* Deferred reconcile-save durable-retry state (issue #1909 / PR #2016). At
|
|
5084
|
-
* most ONE retry is ever armed (`reconcileRetryTimer` is the reentrancy guard)
|
|
5085
|
-
* so successive timed-out saves cannot stack duplicate/parallel retries.
|
|
5086
|
-
* `reconcileRetryBarrier` resolves when the chain settles (published or gave
|
|
5087
|
-
* up) — awaited by tests and any caller wanting the eventual result.
|
|
5088
|
-
*/
|
|
5089
|
-
reconcileRetryTimer = null;
|
|
5090
|
-
reconcileRetryAttempts = 0;
|
|
5091
|
-
reconcileRetryBarrier = null;
|
|
5092
|
-
reconcileRetryResolve = null;
|
|
5093
|
-
/**
|
|
5094
|
-
* On-disk fingerprint (mtime + byte size) of `fact-hashes.txt` captured at the
|
|
5095
|
-
* last point THIS instance's in-memory set matched disk — after `load()`,
|
|
5096
|
-
* `save()`, or a reconcile publish (PR #2016 review). A peer process that
|
|
5097
|
-
* advances the durable index changes this fingerprint;
|
|
5098
|
-
* {@link isDiskFingerprintCurrent} compares a single cheap `stat` against it so
|
|
5099
|
-
* a stale-but-"authoritative" snapshot is detected without an O(file-size)
|
|
5100
|
-
* re-read on the hot path.
|
|
5101
|
-
*/
|
|
5102
|
-
lastSyncedFingerprint = null;
|
|
5103
|
-
constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir = path9.dirname(stateDir), lockOptions = {}) {
|
|
5104
|
-
this.filePath = path9.join(stateDir, "fact-hashes.txt");
|
|
5105
|
-
this.secureStoreKeyProvider = secureStoreKeyProvider;
|
|
5106
|
-
this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;
|
|
5107
|
-
this.memoryDir = memoryDir;
|
|
5108
|
-
this.lockOptions = lockOptions;
|
|
5109
|
-
}
|
|
5110
|
-
/** Load existing hashes from disk. Safe to call multiple times. */
|
|
5111
|
-
async load() {
|
|
5112
|
-
try {
|
|
5113
|
-
const raw = await readMaybeEncryptedFile(this.filePath, this.secureStoreKeyProvider(), this.memoryDir);
|
|
5114
|
-
for (const line of raw.split("\n")) {
|
|
5115
|
-
const trimmed = line.trim();
|
|
5116
|
-
if (trimmed.length > 0) {
|
|
5117
|
-
this.hashes.add(trimmed);
|
|
5118
|
-
}
|
|
5119
|
-
}
|
|
5120
|
-
log.debug(`content-hash index: loaded ${this.hashes.size} hashes`);
|
|
5121
|
-
} catch (err) {
|
|
5122
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5123
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
5124
|
-
log.debug("content-hash index: no existing index \u2014 starting fresh");
|
|
5125
|
-
}
|
|
5126
|
-
await this.captureSyncedFingerprint();
|
|
5127
|
-
}
|
|
5128
|
-
/** Cheap `stat` of the durable index file; null when it does not exist yet. */
|
|
5129
|
-
async statIndexFile() {
|
|
5130
|
-
try {
|
|
5131
|
-
const s = await stat2(this.filePath);
|
|
5132
|
-
return { mtimeMs: s.mtimeMs, size: s.size };
|
|
5133
|
-
} catch (err) {
|
|
5134
|
-
if (isErrnoCode(err, "ENOENT")) return null;
|
|
5135
|
-
throw err;
|
|
5136
|
-
}
|
|
5137
|
-
}
|
|
5138
|
-
/** Record the current on-disk fingerprint as this instance's synced baseline. */
|
|
5139
|
-
async captureSyncedFingerprint() {
|
|
5140
|
-
try {
|
|
5141
|
-
this.lastSyncedFingerprint = await this.statIndexFile();
|
|
5142
|
-
} catch (err) {
|
|
5143
|
-
this.lastSyncedFingerprint = null;
|
|
5144
|
-
log.debug(`content-hash index: could not fingerprint ${this.filePath}: ${err}`);
|
|
5145
|
-
}
|
|
5146
|
-
}
|
|
5147
|
-
/**
|
|
5148
|
-
* True when the durable index file is unchanged since this instance last
|
|
5149
|
-
* synced it (load / save / reconcile). False when a peer advanced it, when the
|
|
5150
|
-
* file appeared or vanished, or when freshness cannot be established (stat
|
|
5151
|
-
* error) — the caller then treats the in-memory snapshot as non-authoritative
|
|
5152
|
-
* and confirms a dedup miss against the durable corpus (PR #2016 review). One
|
|
5153
|
-
* `stat`, never an O(file-size) read, so the hot path stays cheap.
|
|
5154
|
-
*/
|
|
5155
|
-
async isDiskFingerprintCurrent() {
|
|
5156
|
-
let current;
|
|
5157
|
-
try {
|
|
5158
|
-
current = await this.statIndexFile();
|
|
5159
|
-
} catch {
|
|
5160
|
-
return false;
|
|
5161
|
-
}
|
|
5162
|
-
const last = this.lastSyncedFingerprint;
|
|
5163
|
-
if (last === null || current === null) {
|
|
5164
|
-
return last === null && current === null;
|
|
5165
|
-
}
|
|
5166
|
-
return current.mtimeMs === last.mtimeMs && current.size === last.size;
|
|
5167
|
-
}
|
|
5168
|
-
/** Check if content already exists in the index. */
|
|
5169
|
-
has(content) {
|
|
5170
|
-
return this.hashes.has(_ContentHashIndex.computeHash(content));
|
|
5171
|
-
}
|
|
5172
|
-
/** Add content hash to the index. */
|
|
5173
|
-
add(content) {
|
|
5174
|
-
const hash = _ContentHashIndex.computeHash(content);
|
|
5175
|
-
this.removed.delete(hash);
|
|
5176
|
-
this.hashes.add(hash);
|
|
5177
|
-
this.added.add(hash);
|
|
5178
|
-
this.dirty = true;
|
|
5179
|
-
}
|
|
5180
|
-
get size() {
|
|
5181
|
-
return this.hashes.size;
|
|
5182
|
-
}
|
|
5183
|
-
/** Clear all loaded hashes so the next save rewrites the index from scratch. */
|
|
5184
|
-
clear() {
|
|
5185
|
-
if (this.hashes.size > 0) {
|
|
5186
|
-
this.hashes.clear();
|
|
5187
|
-
}
|
|
5188
|
-
this.added.clear();
|
|
5189
|
-
this.removed.clear();
|
|
5190
|
-
this.cancelReconcileRetry();
|
|
5191
|
-
this.dirty = true;
|
|
5192
|
-
}
|
|
5193
|
-
/** Persist index to disk if changed (plain whole-file overwrite). */
|
|
5194
|
-
async save() {
|
|
5195
|
-
if (!this.dirty) return;
|
|
5196
|
-
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
5197
|
-
const publishedAdded = new Set(this.added);
|
|
5198
|
-
const publishedRemoved = new Set(this.removed);
|
|
5199
|
-
const serialized = [...this.hashes].join("\n") + "\n";
|
|
5200
|
-
await writeMaybeEncryptedFile(
|
|
5201
|
-
this.filePath,
|
|
5202
|
-
serialized,
|
|
5203
|
-
this.secureStoreWriteKeyProvider(),
|
|
5204
|
-
{},
|
|
5205
|
-
this.memoryDir
|
|
5206
|
-
);
|
|
5207
|
-
for (const h of publishedAdded) this.added.delete(h);
|
|
5208
|
-
for (const h of publishedRemoved) this.removed.delete(h);
|
|
5209
|
-
this.dirty = this.added.size > 0 || this.removed.size > 0;
|
|
5210
|
-
await this.captureSyncedFingerprint();
|
|
5211
|
-
if (this.dirty) {
|
|
5212
|
-
this.scheduleReconcileRetry();
|
|
5213
|
-
} else {
|
|
5214
|
-
this.cancelReconcileRetry();
|
|
5215
|
-
}
|
|
5216
|
-
log.debug(`content-hash index: saved ${this.hashes.size} hashes`);
|
|
5217
|
-
}
|
|
5218
|
-
/**
|
|
5219
|
-
* Persist the index by RECONCILING with the latest on-disk state under a
|
|
5220
|
-
* cross-process lock (issue #1909). Used by the extraction-persist batch save
|
|
5221
|
-
* for BOTH append and removal batches, so appends and removals to the same
|
|
5222
|
-
* index serialize against each other:
|
|
5223
|
-
* final = (on-disk \ this.removed) ∪ this.added
|
|
5224
|
-
* Note it re-applies only OUR local additions (`added`), NOT the loaded
|
|
5225
|
-
* snapshot (review round 9 finding 3): seeding from `this.hashes` would
|
|
5226
|
-
* resurrect a hash a peer removed on disk while we held a stale snapshot. This
|
|
5227
|
-
* preserves a concurrent writer's appended hashes (a blind overwrite would
|
|
5228
|
-
* drop them), never resurrects a removed hash, and never loses our additions.
|
|
5229
|
-
*
|
|
5230
|
-
* Hardening:
|
|
5231
|
-
* - Dirty short-circuit (round 7 finding 3): when this instance neither added
|
|
5232
|
-
* nor removed anything this run, skip the O(file) read+rewrite entirely.
|
|
5233
|
-
* - Cross-process lock (round 7 finding 2): serialize the read→reconcile→write
|
|
5234
|
-
* window on a per-file advisory lock. If the lock is NOT acquired (timeout),
|
|
5235
|
-
* do NOT publish (round 9 finding 2): keep `dirty` set AND schedule a bounded
|
|
5236
|
-
* durable background retry (PR #2016) that keeps re-attempting the locked
|
|
5237
|
-
* publish until the deferred additions land — never an unlocked write that
|
|
5238
|
-
* could clobber a peer, and never a silent "batch save complete" that leaves
|
|
5239
|
-
* the addition in this process's memory only.
|
|
5240
|
-
*/
|
|
5241
|
-
async saveMergingWithDisk() {
|
|
5242
|
-
if (!this.dirty) return;
|
|
5243
|
-
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
5244
|
-
const publish = async () => {
|
|
5245
|
-
const publishedAdded = new Set(this.added);
|
|
5246
|
-
const publishedRemoved = new Set(this.removed);
|
|
5247
|
-
const merged = new Set(publishedAdded);
|
|
5248
|
-
try {
|
|
5249
|
-
const raw = await readMaybeEncryptedFile(
|
|
5250
|
-
this.filePath,
|
|
5251
|
-
this.secureStoreKeyProvider(),
|
|
5252
|
-
this.memoryDir
|
|
5253
|
-
);
|
|
5254
|
-
for (const line of raw.split("\n")) {
|
|
5255
|
-
const trimmed = line.trim();
|
|
5256
|
-
if (trimmed.length > 0 && !publishedRemoved.has(trimmed)) merged.add(trimmed);
|
|
5257
|
-
}
|
|
5258
|
-
} catch (err) {
|
|
5259
|
-
if (err instanceof SecureStoreLockedError) throw err;
|
|
5260
|
-
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
5261
|
-
}
|
|
5262
|
-
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
5263
|
-
await writeMaybeEncryptedFile(
|
|
5264
|
-
this.filePath,
|
|
5265
|
-
[...merged].join("\n") + "\n",
|
|
5266
|
-
this.secureStoreWriteKeyProvider(),
|
|
5267
|
-
{},
|
|
5268
|
-
this.memoryDir
|
|
5269
|
-
);
|
|
5270
|
-
for (const h of publishedAdded) this.added.delete(h);
|
|
5271
|
-
for (const h of publishedRemoved) this.removed.delete(h);
|
|
5272
|
-
for (const h of this.added) merged.add(h);
|
|
5273
|
-
for (const h of this.removed) merged.delete(h);
|
|
5274
|
-
this.hashes = merged;
|
|
5275
|
-
this.dirty = this.added.size > 0 || this.removed.size > 0;
|
|
5276
|
-
await this.captureSyncedFingerprint();
|
|
5277
|
-
};
|
|
5278
|
-
await withHeldFileLock(
|
|
5279
|
-
`${this.filePath}.lock`,
|
|
5280
|
-
{
|
|
5281
|
-
staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,
|
|
5282
|
-
...this.lockOptions.maxWaitMs !== void 0 ? { maxWaitMs: this.lockOptions.maxWaitMs } : {},
|
|
5283
|
-
...this.lockOptions.pollMs !== void 0 ? { pollMs: this.lockOptions.pollMs } : {}
|
|
5284
|
-
},
|
|
5285
|
-
async (acquired) => {
|
|
5286
|
-
if (!acquired) {
|
|
5287
|
-
log.warn(
|
|
5288
|
-
`content-hash index: lock not acquired for ${this.filePath}; deferring reconcile save (dirty retained, scheduling durable retry)`
|
|
5289
|
-
);
|
|
5290
|
-
this.scheduleReconcileRetry();
|
|
5291
|
-
return;
|
|
5292
|
-
}
|
|
5293
|
-
await publish();
|
|
5294
|
-
if (this.dirty) {
|
|
5295
|
-
this.scheduleReconcileRetry();
|
|
5296
|
-
} else {
|
|
5297
|
-
this.cancelReconcileRetry();
|
|
5298
|
-
}
|
|
5299
|
-
}
|
|
5300
|
-
);
|
|
5301
|
-
log.debug(`content-hash index: reconcile-saved ${this.hashes.size} hashes`);
|
|
5302
|
-
}
|
|
5303
|
-
/**
|
|
5304
|
-
* Rebuild-and-publish the index from an authoritative source (the durable
|
|
5305
|
-
* corpus) under the SAME per-file cross-process lock the reconciling
|
|
5306
|
-
* append/removal saves (`saveMergingWithDisk`) use (issue #1909 / PR #2016).
|
|
5307
|
-
* The `populate` callback runs WHILE the lock is held and MUST repopulate this
|
|
5308
|
-
* index from the corpus (`clear()` then `addByHash(...)` for every corpus
|
|
5309
|
-
* hash); the rebuilt set is then published with a plain overwrite that is now
|
|
5310
|
-
* serialized against every locked writer.
|
|
5311
|
-
*
|
|
5312
|
-
* This closes the finding where the previous unlocked clear→scan→save() could
|
|
5313
|
-
* overwrite a peer's newer lock-merged or deferred additions: a locked writer
|
|
5314
|
-
* that committed BEFORE the rebuild acquired the lock has its memory `.md` on
|
|
5315
|
-
* disk and is re-scanned; one that commits AFTER the rebuild releases
|
|
5316
|
-
* reconciles its additions on top of the freshly-published set. No union with
|
|
5317
|
-
* the raw on-disk file is performed, so the rebuild keeps its
|
|
5318
|
-
* garbage-collection property (orphaned hashes for deleted memories drop).
|
|
5319
|
-
*
|
|
5320
|
-
* Lock-acquisition failure is explicit and non-destructive: `populate` is NOT
|
|
5321
|
-
* run and nothing is written (never an unlocked overwrite). Returns true when
|
|
5322
|
-
* the rebuild was published under the lock, false when the lock timed out —
|
|
5323
|
-
* the caller keeps the index non-authoritative and retries on next use. Uses
|
|
5324
|
-
* the same non-reentrant file lock as the reconcile path, so it can never
|
|
5325
|
-
* deadlock with the deferred reconcile-retry: a concurrent locked writer (or a
|
|
5326
|
-
* fired retry) simply yields `acquired=false` after the bounded wait.
|
|
5327
|
-
*/
|
|
5328
|
-
async rebuildUnderLock(populate) {
|
|
5329
|
-
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
5330
|
-
let published = false;
|
|
5331
|
-
await withHeldFileLock(
|
|
5332
|
-
`${this.filePath}.lock`,
|
|
5333
|
-
{
|
|
5334
|
-
staleMs: CONTENT_HASH_INDEX_LOCK_STALE_MS,
|
|
5335
|
-
...this.lockOptions.maxWaitMs !== void 0 ? { maxWaitMs: this.lockOptions.maxWaitMs } : {},
|
|
5336
|
-
...this.lockOptions.pollMs !== void 0 ? { pollMs: this.lockOptions.pollMs } : {}
|
|
5337
|
-
},
|
|
5338
|
-
async (acquired) => {
|
|
5339
|
-
if (!acquired) {
|
|
5340
|
-
log.warn(
|
|
5341
|
-
`content-hash index: lock not acquired for ${this.filePath}; deferring authoritative rebuild (index left non-authoritative, retried on next use)`
|
|
5342
|
-
);
|
|
5343
|
-
return;
|
|
5344
|
-
}
|
|
5345
|
-
await populate();
|
|
5346
|
-
await this.save();
|
|
5347
|
-
published = true;
|
|
5348
|
-
}
|
|
5349
|
-
);
|
|
5350
|
-
return published;
|
|
5351
|
-
}
|
|
5352
|
-
/**
|
|
5353
|
-
* True while a background durable reconcile-save retry is armed (PR #2016).
|
|
5354
|
-
* Test/introspection hook.
|
|
5355
|
-
*/
|
|
5356
|
-
get hasPendingReconcileRetry() {
|
|
5357
|
-
return this.reconcileRetryTimer !== null;
|
|
5358
|
-
}
|
|
5359
|
-
/**
|
|
5360
|
-
* Resolve once any armed background reconcile-save retry chain has settled —
|
|
5361
|
-
* either it published the deferred additions, or it exhausted its bounded
|
|
5362
|
-
* attempts and fell back to the corpus-rebuild safety net. Resolves
|
|
5363
|
-
* immediately when nothing is armed. Lets a caller (or a test) await the
|
|
5364
|
-
* eventual durability outcome of a lock-timed-out save.
|
|
5365
|
-
*/
|
|
5366
|
-
async whenReconcileRetrySettled() {
|
|
5367
|
-
await (this.reconcileRetryBarrier ?? Promise.resolve());
|
|
5368
|
-
}
|
|
5369
|
-
/**
|
|
5370
|
-
* Drive any deferred lock-timeout reconcile save to completion INLINE at a
|
|
5371
|
-
* lifecycle boundary (PR #2016 finding 3). The background retry timer is
|
|
5372
|
-
* `unref`'d so it never keeps a long-lived daemon alive — but a short-lived
|
|
5373
|
-
* writer (a one-shot CLI) can exit before it fires, leaving a durable fact
|
|
5374
|
-
* `.md` whose hash never reached `fact-hashes.txt` for peers that already
|
|
5375
|
-
* built their in-memory index. `orchestrator.destroy()` calls this so the
|
|
5376
|
-
* addition publishes before the process exits; long-lived hosts only reach it
|
|
5377
|
-
* at their own shutdown, so their in-flight retries keep running in the
|
|
5378
|
-
* background until then. Bounded by the same attempt ceiling as the background
|
|
5379
|
-
* retry; a permanently contended lock falls back to the corpus-rebuild-on-
|
|
5380
|
-
* restart safety net (the fact `.md` is already durable). No deadlock: each
|
|
5381
|
-
* attempt is the same non-reentrant, bounded-wait file lock.
|
|
5382
|
-
*/
|
|
5383
|
-
async flushReconcileRetry() {
|
|
5384
|
-
if (!this.dirty) return;
|
|
5385
|
-
const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;
|
|
5386
|
-
const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;
|
|
5387
|
-
for (let attempt = 0; this.dirty && attempt < maxAttempts; attempt += 1) {
|
|
5388
|
-
if (this.reconcileRetryTimer) {
|
|
5389
|
-
clearTimeout(this.reconcileRetryTimer);
|
|
5390
|
-
this.reconcileRetryTimer = null;
|
|
5391
|
-
}
|
|
5392
|
-
await this.saveMergingWithDisk();
|
|
5393
|
-
if (!this.dirty) break;
|
|
5394
|
-
if (attempt < maxAttempts - 1) {
|
|
5395
|
-
const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
|
|
5396
|
-
await new Promise((resolve3) => setTimeout(resolve3, wait));
|
|
5397
|
-
}
|
|
5398
|
-
}
|
|
5399
|
-
if (this.reconcileRetryTimer) {
|
|
5400
|
-
clearTimeout(this.reconcileRetryTimer);
|
|
5401
|
-
this.reconcileRetryTimer = null;
|
|
5402
|
-
}
|
|
5403
|
-
this.reconcileRetryAttempts = 0;
|
|
5404
|
-
this.settleReconcileRetry();
|
|
5405
|
-
}
|
|
5406
|
-
/**
|
|
5407
|
-
* Schedule ONE bounded, exponentially-backed-off background retry of the
|
|
5408
|
-
* deferred reconcile save (PR #2016). Reentrant-safe: an already-armed timer
|
|
5409
|
-
* short-circuits, so successive timed-out saves never stack duplicate retries.
|
|
5410
|
-
* When attempts are exhausted the chain settles without publishing — the
|
|
5411
|
-
* durable fact `.md` is on disk and a process restart rebuilds the index from
|
|
5412
|
-
* the corpus, so this degrades to that safety net, never a fatal failure.
|
|
5413
|
-
*/
|
|
5414
|
-
scheduleReconcileRetry() {
|
|
5415
|
-
if (this.reconcileRetryTimer) return;
|
|
5416
|
-
const maxAttempts = this.lockOptions.retryMaxAttempts ?? CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS;
|
|
5417
|
-
if (this.reconcileRetryAttempts >= maxAttempts) {
|
|
5418
|
-
log.warn(
|
|
5419
|
-
`content-hash index: exhausted ${maxAttempts} deferred reconcile-save retries for ${this.filePath}; ${this.added.size} addition(s) remain dirty in-memory (a process restart rebuilds the index from the durable corpus)`
|
|
5420
|
-
);
|
|
5421
|
-
this.reconcileRetryAttempts = 0;
|
|
5422
|
-
this.settleReconcileRetry();
|
|
5423
|
-
return;
|
|
5424
|
-
}
|
|
5425
|
-
if (!this.reconcileRetryBarrier) {
|
|
5426
|
-
this.reconcileRetryBarrier = new Promise((resolve3) => {
|
|
5427
|
-
this.reconcileRetryResolve = resolve3;
|
|
5428
|
-
});
|
|
5429
|
-
}
|
|
5430
|
-
const attempt = this.reconcileRetryAttempts + 1;
|
|
5431
|
-
const baseMs = this.lockOptions.retryBaseMs ?? CONTENT_HASH_INDEX_RETRY_BASE_MS;
|
|
5432
|
-
const delay = Math.min(baseMs * 2 ** (attempt - 1), CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
|
|
5433
|
-
this.reconcileRetryTimer = setTimeout(() => {
|
|
5434
|
-
this.reconcileRetryTimer = null;
|
|
5435
|
-
this.reconcileRetryAttempts = attempt;
|
|
5436
|
-
void this.saveMergingWithDisk().catch((err) => {
|
|
5437
|
-
log.warn(`content-hash index: deferred reconcile-save retry failed for ${this.filePath}: ${err}`);
|
|
5438
|
-
}).finally(() => {
|
|
5439
|
-
if (this.dirty && !this.reconcileRetryTimer) this.settleReconcileRetry();
|
|
5440
|
-
});
|
|
5441
|
-
}, delay);
|
|
5442
|
-
this.reconcileRetryTimer.unref?.();
|
|
5443
|
-
}
|
|
5444
|
-
/** Cancel any armed reconcile-save retry and settle the barrier (a publish landed). */
|
|
5445
|
-
cancelReconcileRetry() {
|
|
5446
|
-
if (this.reconcileRetryTimer) {
|
|
5447
|
-
clearTimeout(this.reconcileRetryTimer);
|
|
5448
|
-
this.reconcileRetryTimer = null;
|
|
5449
|
-
}
|
|
5450
|
-
this.reconcileRetryAttempts = 0;
|
|
5451
|
-
this.settleReconcileRetry();
|
|
5452
|
-
}
|
|
5453
|
-
/** Resolve and clear the retry barrier if one is outstanding. */
|
|
5454
|
-
settleReconcileRetry() {
|
|
5455
|
-
const resolve3 = this.reconcileRetryResolve;
|
|
5456
|
-
this.reconcileRetryBarrier = null;
|
|
5457
|
-
this.reconcileRetryResolve = null;
|
|
5458
|
-
resolve3?.();
|
|
5459
|
-
}
|
|
5460
|
-
/** Remove a hash from the index (used when archiving/deleting). */
|
|
5461
|
-
remove(content) {
|
|
5462
|
-
const hash = _ContentHashIndex.computeHash(content);
|
|
5463
|
-
this.added.delete(hash);
|
|
5464
|
-
this.removed.add(hash);
|
|
5465
|
-
this.hashes.delete(hash);
|
|
5466
|
-
this.dirty = true;
|
|
5467
|
-
}
|
|
5468
|
-
/**
|
|
5469
|
-
* Remove a pre-computed SHA-256 hash directly from the index without
|
|
5470
|
-
* re-hashing. Use this when the caller already holds the stored hash
|
|
5471
|
-
* (e.g. `memory.frontmatter.contentHash`) to avoid the double-hash bug
|
|
5472
|
-
* where `remove(hash)` would compute `hash(hash)` and never match the
|
|
5473
|
-
* entry.
|
|
5474
|
-
*/
|
|
5475
|
-
removeByHash(hash) {
|
|
5476
|
-
this.added.delete(hash);
|
|
5477
|
-
this.removed.add(hash);
|
|
5478
|
-
this.hashes.delete(hash);
|
|
5479
|
-
this.dirty = true;
|
|
5480
|
-
}
|
|
5481
|
-
/**
|
|
5482
|
-
* Add a pre-computed SHA-256 hash directly to the index without re-hashing.
|
|
5483
|
-
* Use this when the caller already holds the stored hash
|
|
5484
|
-
* (e.g. `memory.frontmatter.contentHash`) so that the index records the raw
|
|
5485
|
-
* content hash rather than re-hashing the citation-annotated body.
|
|
5486
|
-
*
|
|
5487
|
-
* @internal Only called from `StorageManager.ensureFactHashIndexAuthoritative`.
|
|
5488
|
-
* Not part of the public API — prefer `add(content)` for external callers.
|
|
5489
|
-
*/
|
|
5490
|
-
addByHash(hash) {
|
|
5491
|
-
this.removed.delete(hash);
|
|
5492
|
-
this.hashes.add(hash);
|
|
5493
|
-
this.added.add(hash);
|
|
5494
|
-
this.dirty = true;
|
|
5495
|
-
}
|
|
5496
|
-
/** Normalize content (delegates to content-hash.ts for a single source of truth). */
|
|
5497
|
-
static normalizeContent(content) {
|
|
5498
|
-
return normalizeContent(content);
|
|
5499
|
-
}
|
|
5500
|
-
/** Compute SHA-256 hash (delegates to content-hash.ts for a single source of truth). */
|
|
5501
|
-
static computeHash(content) {
|
|
5502
|
-
return computeContentHash(content);
|
|
5503
|
-
}
|
|
5504
|
-
};
|
|
5505
|
-
|
|
5506
5044
|
// src/storage.ts
|
|
5507
5045
|
var ARTIFACT_SEARCH_STOPWORDS = /* @__PURE__ */ new Set([
|
|
5508
5046
|
"a",
|
|
@@ -6127,17 +5665,17 @@ function inferEntityTypeFromContent(content) {
|
|
|
6127
5665
|
}
|
|
6128
5666
|
var KNOWN_ENTITY_FILENAME_PREFIXES = /* @__PURE__ */ new Set(["company", "other", "person", "place", "project", "tool", "topic"]);
|
|
6129
5667
|
function inferEntityTypeFromFilename(pathRel) {
|
|
6130
|
-
const basename =
|
|
5668
|
+
const basename = path9.basename(pathRel, ".md").toLowerCase();
|
|
6131
5669
|
const separator = basename.indexOf("-");
|
|
6132
5670
|
if (separator <= 0) return void 0;
|
|
6133
5671
|
const candidate = basename.slice(0, separator);
|
|
6134
5672
|
return KNOWN_ENTITY_FILENAME_PREFIXES.has(candidate) ? candidate : void 0;
|
|
6135
5673
|
}
|
|
6136
5674
|
function normalizeFrontmatterForPath(frontmatter, pathRel, content = "") {
|
|
6137
|
-
const normalizedPath = pathRel.split(
|
|
5675
|
+
const normalizedPath = pathRel.split(path9.sep).join("/");
|
|
6138
5676
|
let normalizedFrontmatter = frontmatter;
|
|
6139
5677
|
if (normalizedPath === "entities" || normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/")) {
|
|
6140
|
-
const basename =
|
|
5678
|
+
const basename = path9.basename(pathRel, ".md");
|
|
6141
5679
|
const inferredType = inferEntityTypeFromContent(content) || inferEntityTypeFromFilename(pathRel) || "entity";
|
|
6142
5680
|
const existingTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
|
|
6143
5681
|
normalizedFrontmatter = {
|
|
@@ -6597,8 +6135,9 @@ function parseExtractionRetryStateEntries(raw) {
|
|
|
6597
6135
|
}
|
|
6598
6136
|
return valid;
|
|
6599
6137
|
}
|
|
6600
|
-
var StorageManager3 = class _StorageManager {
|
|
6138
|
+
var StorageManager3 = class _StorageManager extends TombstoneBlockedCaptureIndexHost {
|
|
6601
6139
|
constructor(baseDir, entitySchemas, hotMemoriesCacheEnabledOverride) {
|
|
6140
|
+
super();
|
|
6602
6141
|
this.baseDir = baseDir;
|
|
6603
6142
|
this.entitySchemas = entitySchemas;
|
|
6604
6143
|
this.hotMemoriesCacheEnabled = hotMemoriesCacheEnabledOverride ?? _StorageManager.hotMemoriesCacheDefaultByDir.get(baseDir) ?? _StorageManager.hotMemoriesCacheDefault;
|
|
@@ -6783,8 +6322,8 @@ var StorageManager3 = class _StorageManager {
|
|
|
6783
6322
|
}
|
|
6784
6323
|
/** Whether `filePath` resolves to `stateDir` itself or a path contained in it. */
|
|
6785
6324
|
isStateFilePath(filePath) {
|
|
6786
|
-
const rel =
|
|
6787
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6325
|
+
const rel = path9.relative(this.stateDir, path9.resolve(filePath));
|
|
6326
|
+
return rel === "" || !rel.startsWith("..") && !path9.isAbsolute(rel);
|
|
6788
6327
|
}
|
|
6789
6328
|
// ── Tombstone store (issue #1579) ───────────────────────────────────────
|
|
6790
6329
|
// Instance-scoped (rule 11) + namespace-scoped (rule 42) tombstone index.
|
|
@@ -6901,8 +6440,8 @@ var StorageManager3 = class _StorageManager {
|
|
|
6901
6440
|
* /srv/cold/remnic), skipping the patch and stranding a stale hot entry.
|
|
6902
6441
|
*/
|
|
6903
6442
|
isColdOrArchiveTierPath(p) {
|
|
6904
|
-
const rel =
|
|
6905
|
-
return rel === "cold" || rel === "archive" || rel.startsWith(`cold${
|
|
6443
|
+
const rel = path9.relative(this.baseDir, p);
|
|
6444
|
+
return rel === "cold" || rel === "archive" || rel.startsWith(`cold${path9.sep}`) || rel.startsWith(`archive${path9.sep}`);
|
|
6906
6445
|
}
|
|
6907
6446
|
/**
|
|
6908
6447
|
* Mark the secure-store as required for this storage instance.
|
|
@@ -6989,7 +6528,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6989
6528
|
void 0,
|
|
6990
6529
|
this.baseDir
|
|
6991
6530
|
);
|
|
6992
|
-
const rel =
|
|
6531
|
+
const rel = path9.relative(this.baseDir, filePath).split(path9.sep).join("/");
|
|
6993
6532
|
return `${rel}:${version.versionId}`;
|
|
6994
6533
|
} catch (err) {
|
|
6995
6534
|
log.warn(
|
|
@@ -7069,13 +6608,13 @@ var StorageManager3 = class _StorageManager {
|
|
|
7069
6608
|
}
|
|
7070
6609
|
identityFilePath(workspaceDir, namespace) {
|
|
7071
6610
|
const rawNamespace = typeof namespace === "string" ? namespace.trim() : "";
|
|
7072
|
-
if (!rawNamespace) return
|
|
6611
|
+
if (!rawNamespace) return path9.join(workspaceDir, "IDENTITY.md");
|
|
7073
6612
|
const safeNamespace = rawNamespace.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
7074
|
-
return
|
|
6613
|
+
return path9.join(workspaceDir, `IDENTITY.${safeNamespace}.md`);
|
|
7075
6614
|
}
|
|
7076
6615
|
versionFilePath(kind) {
|
|
7077
6616
|
const fileName = kind === "memory-status" ? ".memory-status-version.log" : kind === "artifact-write" ? ".artifact-write-version.log" : kind === "cold-write" ? ".cold-write-version.log" : ".memory-corpus-version.log";
|
|
7078
|
-
return
|
|
6617
|
+
return path9.join(this.stateDir, fileName);
|
|
7079
6618
|
}
|
|
7080
6619
|
bumpSharedVersion(kind, fallbackMap) {
|
|
7081
6620
|
const filePath = this.versionFilePath(kind);
|
|
@@ -7167,7 +6706,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7167
6706
|
// memories.
|
|
7168
6707
|
// -------------------------------------------------------------------------
|
|
7169
6708
|
get wearablesDir() {
|
|
7170
|
-
return
|
|
6709
|
+
return path9.join(this.baseDir, WEARABLES_DIR_NAME);
|
|
7171
6710
|
}
|
|
7172
6711
|
/**
|
|
7173
6712
|
* Resolve the on-disk path for a source/day transcript. Throws on
|
|
@@ -7183,7 +6722,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7183
6722
|
if (!isValidTranscriptDate(date)) {
|
|
7184
6723
|
throw new Error(`invalid wearable transcript date '${String(date)}' \u2014 expected YYYY-MM-DD`);
|
|
7185
6724
|
}
|
|
7186
|
-
return
|
|
6725
|
+
return path9.join(this.wearablesDir, sourceId, `${date}.md`);
|
|
7187
6726
|
}
|
|
7188
6727
|
async writeWearableDayTranscript(sourceId, date, serialized) {
|
|
7189
6728
|
const targetPath = this.wearableTranscriptPath(sourceId, date);
|
|
@@ -7299,25 +6838,25 @@ var StorageManager3 = class _StorageManager {
|
|
|
7299
6838
|
});
|
|
7300
6839
|
}
|
|
7301
6840
|
get factsDir() {
|
|
7302
|
-
return
|
|
6841
|
+
return path9.join(this.baseDir, "facts");
|
|
7303
6842
|
}
|
|
7304
6843
|
get correctionsDir() {
|
|
7305
|
-
return
|
|
6844
|
+
return path9.join(this.baseDir, "corrections");
|
|
7306
6845
|
}
|
|
7307
6846
|
get proceduresDir() {
|
|
7308
|
-
return
|
|
6847
|
+
return path9.join(this.baseDir, "procedures");
|
|
7309
6848
|
}
|
|
7310
6849
|
get reasoningTracesDir() {
|
|
7311
|
-
return
|
|
6850
|
+
return path9.join(this.baseDir, "reasoning-traces");
|
|
7312
6851
|
}
|
|
7313
6852
|
get entitiesDir() {
|
|
7314
|
-
return
|
|
6853
|
+
return path9.join(this.baseDir, "entities");
|
|
7315
6854
|
}
|
|
7316
6855
|
resolveEntityFilePath(name) {
|
|
7317
6856
|
if (typeof name !== "string") return null;
|
|
7318
|
-
const filePath =
|
|
7319
|
-
const relative2 =
|
|
7320
|
-
if (relative2 === "" || relative2.startsWith("..") ||
|
|
6857
|
+
const filePath = path9.resolve(this.entitiesDir, `${name}.md`);
|
|
6858
|
+
const relative2 = path9.relative(this.entitiesDir, filePath);
|
|
6859
|
+
if (relative2 === "" || relative2.startsWith("..") || path9.isAbsolute(relative2)) {
|
|
7321
6860
|
return null;
|
|
7322
6861
|
}
|
|
7323
6862
|
return filePath;
|
|
@@ -7331,17 +6870,11 @@ var StorageManager3 = class _StorageManager {
|
|
|
7331
6870
|
this.notifyCatalogWriteForPath(filePath);
|
|
7332
6871
|
});
|
|
7333
6872
|
}
|
|
7334
|
-
writeStorageSecureFileChunks(filePath, chunks) {
|
|
7335
|
-
const writeKey = this.resolveWriteKey();
|
|
7336
|
-
return writeMaybeEncryptedFileFromChunks(filePath, chunks, writeKey, {}, this.baseDir).then(() => {
|
|
7337
|
-
this.notifyCatalogWriteForPath(filePath);
|
|
7338
|
-
});
|
|
7339
|
-
}
|
|
7340
6873
|
assertManagedStoragePath(filePath, method) {
|
|
7341
|
-
const resolved =
|
|
7342
|
-
const base =
|
|
7343
|
-
const rel =
|
|
7344
|
-
if (rel === "" || rel === ".." || rel.startsWith(`..${
|
|
6874
|
+
const resolved = path9.resolve(filePath);
|
|
6875
|
+
const base = path9.resolve(this.baseDir);
|
|
6876
|
+
const rel = path9.relative(base, resolved);
|
|
6877
|
+
if (rel === "" || rel === ".." || rel.startsWith(`..${path9.sep}`) || path9.isAbsolute(rel)) {
|
|
7345
6878
|
throw new Error(`${method}: file path escapes memory dir`);
|
|
7346
6879
|
}
|
|
7347
6880
|
return resolved;
|
|
@@ -7352,8 +6885,8 @@ var StorageManager3 = class _StorageManager {
|
|
|
7352
6885
|
}
|
|
7353
6886
|
async digestOfflineSyncFile(filePath) {
|
|
7354
6887
|
const target = this.assertManagedStoragePath(filePath, "storage.digestOfflineSyncFile");
|
|
7355
|
-
const st = await
|
|
7356
|
-
const relPath =
|
|
6888
|
+
const st = await stat2(target);
|
|
6889
|
+
const relPath = path9.relative(this.baseDir, target).split(path9.sep).join("/");
|
|
7357
6890
|
const cache = await this.loadOfflineSyncDigestCache();
|
|
7358
6891
|
const cached = cache.get(relPath);
|
|
7359
6892
|
if (cached && cached.statBytes === st.size && cached.mtimeMs === st.mtimeMs && cached.ctimeMs === st.ctimeMs && !cached.encrypted) {
|
|
@@ -7414,7 +6947,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7414
6947
|
const bytes = typeof record.bytes === "number" ? record.bytes : NaN;
|
|
7415
6948
|
const sha256 = typeof record.sha256 === "string" ? record.sha256 : "";
|
|
7416
6949
|
const encrypted = record.encrypted === true;
|
|
7417
|
-
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") ||
|
|
6950
|
+
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") || path9.isAbsolute(cachePath) || !Number.isFinite(statBytes) || !Number.isFinite(mtimeMs) || !Number.isFinite(ctimeMs) || !Number.isFinite(bytes) || !/^[a-f0-9]{64}$/i.test(sha256)) {
|
|
7418
6951
|
continue;
|
|
7419
6952
|
}
|
|
7420
6953
|
cache.set(cachePath, { statBytes, mtimeMs, ctimeMs, encrypted, sha256, bytes });
|
|
@@ -7456,7 +6989,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7456
6989
|
const cache = this.offlineSyncDigestCache;
|
|
7457
6990
|
if (!cache) return;
|
|
7458
6991
|
const entries = [...cache.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
|
|
7459
|
-
await
|
|
6992
|
+
await mkdir(path9.dirname(this.offlineSyncDigestCachePath), { recursive: true });
|
|
7460
6993
|
await writeFile2(this.offlineSyncDigestCachePath, `${JSON.stringify({ version: 1, entries })}
|
|
7461
6994
|
`, "utf-8");
|
|
7462
6995
|
}).catch((err) => {
|
|
@@ -7475,40 +7008,10 @@ var StorageManager3 = class _StorageManager {
|
|
|
7475
7008
|
await handle.close();
|
|
7476
7009
|
}
|
|
7477
7010
|
}
|
|
7478
|
-
async writeOfflineSyncFile(filePath, content) {
|
|
7479
|
-
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFile");
|
|
7480
|
-
await this.writeStorageSecureFile(target, content);
|
|
7481
|
-
await this.invalidateAfterOfflineSyncMutation(target);
|
|
7482
|
-
}
|
|
7483
7011
|
async writeOfflineSyncStagingFile(filePath, content) {
|
|
7484
7012
|
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncStagingFile");
|
|
7485
7013
|
await this.writeStorageSecureFile(target, content);
|
|
7486
7014
|
}
|
|
7487
|
-
async writeOfflineSyncFileChunks(filePath, chunks) {
|
|
7488
|
-
const target = this.assertManagedStoragePath(filePath, "storage.writeOfflineSyncFileChunks");
|
|
7489
|
-
await this.writeStorageSecureFileChunks(target, chunks);
|
|
7490
|
-
await this.invalidateAfterOfflineSyncMutation(target);
|
|
7491
|
-
}
|
|
7492
|
-
async deleteOfflineSyncFile(filePath) {
|
|
7493
|
-
const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
|
|
7494
|
-
await unlink3(target).catch((error) => {
|
|
7495
|
-
if (isErrnoCode(error, "ENOENT")) return;
|
|
7496
|
-
throw error;
|
|
7497
|
-
});
|
|
7498
|
-
await this.invalidateAfterOfflineSyncMutation(target);
|
|
7499
|
-
}
|
|
7500
|
-
async invalidateAfterOfflineSyncMutation(filePath) {
|
|
7501
|
-
this.invalidateAllMemoriesCache();
|
|
7502
|
-
this.invalidateKnowledgeIndexCache();
|
|
7503
|
-
this.factHashIndexAuthoritative = false;
|
|
7504
|
-
if (filePath.includes(`${path10.sep}cold${path10.sep}`)) {
|
|
7505
|
-
this.invalidateColdMemoriesCache();
|
|
7506
|
-
}
|
|
7507
|
-
if (filePath.includes(`${path10.sep}artifacts${path10.sep}`)) {
|
|
7508
|
-
this.bumpArtifactWriteVersion();
|
|
7509
|
-
}
|
|
7510
|
-
this.bumpMemoryStatusVersion();
|
|
7511
|
-
}
|
|
7512
7015
|
createContentHashIndex() {
|
|
7513
7016
|
return new ContentHashIndex(
|
|
7514
7017
|
this.stateDir,
|
|
@@ -7544,7 +7047,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7544
7047
|
async isEncryptedFileHeader(filePath) {
|
|
7545
7048
|
let st;
|
|
7546
7049
|
try {
|
|
7547
|
-
st = await
|
|
7050
|
+
st = await stat2(filePath);
|
|
7548
7051
|
} catch (err) {
|
|
7549
7052
|
if (isErrnoCode(err, "ENOENT")) return false;
|
|
7550
7053
|
throw err;
|
|
@@ -7575,7 +7078,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7575
7078
|
}
|
|
7576
7079
|
async appendStorageSecureFileUnlocked(filePath, content) {
|
|
7577
7080
|
const writeKey = this.resolveWriteKey();
|
|
7578
|
-
await
|
|
7081
|
+
await mkdir(path9.dirname(filePath), { recursive: true });
|
|
7579
7082
|
if (writeKey === null) {
|
|
7580
7083
|
try {
|
|
7581
7084
|
if (await this.isEncryptedFileHeader(filePath)) {
|
|
@@ -7601,13 +7104,13 @@ var StorageManager3 = class _StorageManager {
|
|
|
7601
7104
|
this.notifyCatalogWriteForPath(filePath);
|
|
7602
7105
|
}
|
|
7603
7106
|
get stateDir() {
|
|
7604
|
-
return
|
|
7107
|
+
return path9.join(this.baseDir, "state");
|
|
7605
7108
|
}
|
|
7606
7109
|
get offlineSyncDigestCachePath() {
|
|
7607
|
-
return
|
|
7110
|
+
return path9.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
|
|
7608
7111
|
}
|
|
7609
7112
|
get entitySynthesisQueuePath() {
|
|
7610
|
-
return
|
|
7113
|
+
return path9.join(this.stateDir, "entity-synthesis-queue.json");
|
|
7611
7114
|
}
|
|
7612
7115
|
// ── Tombstone store access (issue #1579) ─────────────────────────────────
|
|
7613
7116
|
/**
|
|
@@ -7617,7 +7120,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7617
7120
|
* helpers are injected into the store).
|
|
7618
7121
|
*/
|
|
7619
7122
|
get tombstonesPath() {
|
|
7620
|
-
return
|
|
7123
|
+
return path9.join(this.stateDir, "tombstones.jsonl");
|
|
7621
7124
|
}
|
|
7622
7125
|
/**
|
|
7623
7126
|
* Configure the tombstone invariant for this storage instance. Installed
|
|
@@ -7746,6 +7249,28 @@ var StorageManager3 = class _StorageManager {
|
|
|
7746
7249
|
});
|
|
7747
7250
|
return await store.rebuild(retired);
|
|
7748
7251
|
}
|
|
7252
|
+
markFactHashIndexNotAuthoritative() {
|
|
7253
|
+
this.factHashIndexAuthoritative = false;
|
|
7254
|
+
}
|
|
7255
|
+
tombstoneBlockedCaptureIndexOptions() {
|
|
7256
|
+
return {
|
|
7257
|
+
stateDir: this.stateDir,
|
|
7258
|
+
memoryDir: this.baseDir,
|
|
7259
|
+
secureStoreKeyProvider: () => this._secureStoreKey,
|
|
7260
|
+
secureStoreWriteKeyProvider: () => this.resolveWriteKey(),
|
|
7261
|
+
lockOptions: () => this.factHashIndexLockOptions,
|
|
7262
|
+
parseMemory: (filePath, content) => parseTombstoneBlockedOfflineSyncMemory(
|
|
7263
|
+
filePath,
|
|
7264
|
+
content,
|
|
7265
|
+
this.baseDir,
|
|
7266
|
+
parseFrontmatter,
|
|
7267
|
+
normalizeFrontmatterForPath,
|
|
7268
|
+
toMemoryPathRel
|
|
7269
|
+
),
|
|
7270
|
+
readAllMemories: () => this.readAllMemories(),
|
|
7271
|
+
readAllColdMemories: () => this.readAllColdMemories()
|
|
7272
|
+
};
|
|
7273
|
+
}
|
|
7749
7274
|
async getFactHashIndex() {
|
|
7750
7275
|
if (this.factHashIndex) {
|
|
7751
7276
|
return this.factHashIndex;
|
|
@@ -7807,9 +7332,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7807
7332
|
const baseMs = this.factHashIndexLockOptions.retryBaseMs ?? FACT_HASH_INDEX_REBUILD_RETRY_BASE_MS;
|
|
7808
7333
|
let published = false;
|
|
7809
7334
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
7810
|
-
published = await factHashIndex.rebuildUnderLock(
|
|
7811
|
-
() => this.rebuildFactHashIndexFromCorpus(factHashIndex)
|
|
7812
|
-
);
|
|
7335
|
+
published = await factHashIndex.rebuildUnderLock(() => this.rebuildFactHashIndexFromCorpus(factHashIndex));
|
|
7813
7336
|
if (published || attempt === maxAttempts - 1) break;
|
|
7814
7337
|
const wait = Math.min(baseMs * 2 ** attempt, CONTENT_HASH_INDEX_RETRY_MAX_DELAY_MS);
|
|
7815
7338
|
await new Promise((resolve3) => setTimeout(resolve3, wait));
|
|
@@ -7835,10 +7358,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7835
7358
|
async rebuildFactHashIndexFromCorpus(factHashIndex) {
|
|
7836
7359
|
factHashIndex.clear();
|
|
7837
7360
|
this.factOnlyHashes.clear();
|
|
7838
|
-
const existing = [
|
|
7839
|
-
...await this.readAllMemories(),
|
|
7840
|
-
...await this.readAllColdMemories()
|
|
7841
|
-
];
|
|
7361
|
+
const existing = [...await this.readAllMemories(), ...await this.readAllColdMemories()];
|
|
7842
7362
|
let legacyRecovered = 0;
|
|
7843
7363
|
for (const memory of existing) {
|
|
7844
7364
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
@@ -7890,55 +7410,55 @@ var StorageManager3 = class _StorageManager {
|
|
|
7890
7410
|
return null;
|
|
7891
7411
|
}
|
|
7892
7412
|
get questionsDir() {
|
|
7893
|
-
return
|
|
7413
|
+
return path9.join(this.baseDir, "questions");
|
|
7894
7414
|
}
|
|
7895
7415
|
get artifactsDir() {
|
|
7896
|
-
return
|
|
7416
|
+
return path9.join(this.baseDir, "artifacts");
|
|
7897
7417
|
}
|
|
7898
7418
|
get identityDir() {
|
|
7899
|
-
return
|
|
7419
|
+
return path9.join(this.baseDir, "identity");
|
|
7900
7420
|
}
|
|
7901
7421
|
get identityAnchorPath() {
|
|
7902
|
-
return
|
|
7422
|
+
return path9.join(this.identityDir, "identity-anchor.md");
|
|
7903
7423
|
}
|
|
7904
7424
|
get identityIncidentsDir() {
|
|
7905
|
-
return
|
|
7425
|
+
return path9.join(this.identityDir, "incidents");
|
|
7906
7426
|
}
|
|
7907
7427
|
get identityAuditsWeeklyDir() {
|
|
7908
|
-
return
|
|
7428
|
+
return path9.join(this.identityDir, "audits", "weekly");
|
|
7909
7429
|
}
|
|
7910
7430
|
get identityAuditsMonthlyDir() {
|
|
7911
|
-
return
|
|
7431
|
+
return path9.join(this.identityDir, "audits", "monthly");
|
|
7912
7432
|
}
|
|
7913
7433
|
get identityImprovementLoopsPath() {
|
|
7914
|
-
return
|
|
7434
|
+
return path9.join(this.identityDir, "improvement-loops.md");
|
|
7915
7435
|
}
|
|
7916
7436
|
get identityReflectionsPath() {
|
|
7917
|
-
return
|
|
7437
|
+
return path9.join(this.identityDir, "reflections.md");
|
|
7918
7438
|
}
|
|
7919
7439
|
get profilePath() {
|
|
7920
|
-
return
|
|
7440
|
+
return path9.join(this.baseDir, "profile.md");
|
|
7921
7441
|
}
|
|
7922
7442
|
get memoryActionsPath() {
|
|
7923
|
-
return
|
|
7443
|
+
return path9.join(this.stateDir, "memory-actions.jsonl");
|
|
7924
7444
|
}
|
|
7925
7445
|
get memoryLifecycleLedgerPath() {
|
|
7926
|
-
return
|
|
7446
|
+
return path9.join(this.stateDir, "memory-lifecycle-ledger.jsonl");
|
|
7927
7447
|
}
|
|
7928
7448
|
get compressionGuidelinesPath() {
|
|
7929
|
-
return
|
|
7449
|
+
return path9.join(this.stateDir, "compression-guidelines.md");
|
|
7930
7450
|
}
|
|
7931
7451
|
get compressionGuidelineDraftPath() {
|
|
7932
|
-
return
|
|
7452
|
+
return path9.join(this.stateDir, "compression-guidelines.draft.md");
|
|
7933
7453
|
}
|
|
7934
7454
|
get compressionGuidelineStatePath() {
|
|
7935
|
-
return
|
|
7455
|
+
return path9.join(this.stateDir, "compression-guideline-state.json");
|
|
7936
7456
|
}
|
|
7937
7457
|
get compressionGuidelineDraftStatePath() {
|
|
7938
|
-
return
|
|
7458
|
+
return path9.join(this.stateDir, "compression-guideline-draft-state.json");
|
|
7939
7459
|
}
|
|
7940
7460
|
get behaviorSignalsPath() {
|
|
7941
|
-
return
|
|
7461
|
+
return path9.join(this.stateDir, "behavior-signals.jsonl");
|
|
7942
7462
|
}
|
|
7943
7463
|
/**
|
|
7944
7464
|
* In-memory dedup key set for behavior-signals appends (issue #1909),
|
|
@@ -7970,7 +7490,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7970
7490
|
* governance sweeps can treat it uniformly.
|
|
7971
7491
|
*/
|
|
7972
7492
|
get bufferSurpriseLedgerPath() {
|
|
7973
|
-
return
|
|
7493
|
+
return path9.join(this.stateDir, "buffer-surprise-ledger.jsonl");
|
|
7974
7494
|
}
|
|
7975
7495
|
/**
|
|
7976
7496
|
* Entity alias table loaded from THIS store's config/aliases.json.
|
|
@@ -8010,7 +7530,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
8010
7530
|
this.loadAliasesSync();
|
|
8011
7531
|
}
|
|
8012
7532
|
loadAliasesSync() {
|
|
8013
|
-
const aliasPath =
|
|
7533
|
+
const aliasPath = path9.join(this.baseDir, "config", "aliases.json");
|
|
8014
7534
|
this.userAliases = {};
|
|
8015
7535
|
try {
|
|
8016
7536
|
const raw = readFileSync2(aliasPath, "utf-8");
|
|
@@ -8045,22 +7565,22 @@ var StorageManager3 = class _StorageManager {
|
|
|
8045
7565
|
return completionFingerprint;
|
|
8046
7566
|
}
|
|
8047
7567
|
async ensureDirectories() {
|
|
8048
|
-
await
|
|
7568
|
+
await mkdir(this.baseDir, { recursive: true });
|
|
8049
7569
|
await validateRoots(this.baseDir, this.entitiesDir, this.stateDir, this.archiveDir);
|
|
8050
7570
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8051
|
-
await
|
|
8052
|
-
await
|
|
8053
|
-
await
|
|
8054
|
-
await
|
|
8055
|
-
await
|
|
8056
|
-
await
|
|
8057
|
-
await
|
|
8058
|
-
await
|
|
8059
|
-
await
|
|
8060
|
-
await
|
|
8061
|
-
await
|
|
8062
|
-
await
|
|
8063
|
-
await
|
|
7571
|
+
await mkdir(path9.join(this.factsDir, today), { recursive: true });
|
|
7572
|
+
await mkdir(path9.join(this.proceduresDir, today), { recursive: true });
|
|
7573
|
+
await mkdir(path9.join(this.reasoningTracesDir, today), { recursive: true });
|
|
7574
|
+
await mkdir(this.correctionsDir, { recursive: true });
|
|
7575
|
+
await mkdir(this.entitiesDir, { recursive: true });
|
|
7576
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
7577
|
+
await mkdir(this.questionsDir, { recursive: true });
|
|
7578
|
+
await mkdir(this.artifactsDir, { recursive: true });
|
|
7579
|
+
await mkdir(this.identityDir, { recursive: true });
|
|
7580
|
+
await mkdir(this.identityIncidentsDir, { recursive: true });
|
|
7581
|
+
await mkdir(this.identityAuditsWeeklyDir, { recursive: true });
|
|
7582
|
+
await mkdir(this.identityAuditsMonthlyDir, { recursive: true });
|
|
7583
|
+
await mkdir(path9.join(this.baseDir, "config"), { recursive: true });
|
|
8064
7584
|
await this.entityCanonicalIdMigration.ensure();
|
|
8065
7585
|
if (this.hotMemoriesCacheEnabled && this.getMemoryCorpusVersion() === 0) {
|
|
8066
7586
|
this.bumpMemoryCorpusVersion();
|
|
@@ -8081,12 +7601,24 @@ var StorageManager3 = class _StorageManager {
|
|
|
8081
7601
|
*/
|
|
8082
7602
|
async resolveCategoryWritePath(category, id, today) {
|
|
8083
7603
|
if (category === "correction") {
|
|
8084
|
-
await
|
|
8085
|
-
return
|
|
8086
|
-
}
|
|
8087
|
-
const datedDir =
|
|
8088
|
-
await
|
|
8089
|
-
return
|
|
7604
|
+
await mkdir(this.correctionsDir, { recursive: true });
|
|
7605
|
+
return path9.join(this.correctionsDir, `${id}.md`);
|
|
7606
|
+
}
|
|
7607
|
+
const datedDir = path9.join(getCategoryDir(this.baseDir, category), today);
|
|
7608
|
+
await mkdir(datedDir, { recursive: true });
|
|
7609
|
+
return path9.join(datedDir, `${id}.md`);
|
|
7610
|
+
}
|
|
7611
|
+
async findExistingTombstoneBlockedMemory(content, category, sourceConnector) {
|
|
7612
|
+
const identity = buildExplicitCaptureDedupKey(content, category, sourceConnector);
|
|
7613
|
+
const memories = [...await this.readAllMemories(), ...await this.readAllColdMemories()];
|
|
7614
|
+
return memories.find((memory) => {
|
|
7615
|
+
if (memory.frontmatter.status !== "pending_review" || !memory.frontmatter.blockedBy) return false;
|
|
7616
|
+
return buildExplicitCaptureDedupKey(
|
|
7617
|
+
memory.content,
|
|
7618
|
+
memory.frontmatter.category,
|
|
7619
|
+
memory.frontmatter.sourceConnector
|
|
7620
|
+
) === identity;
|
|
7621
|
+
}) ?? null;
|
|
8090
7622
|
}
|
|
8091
7623
|
async writeMemory(category, content, options = {}) {
|
|
8092
7624
|
await this.ensureDirectories();
|
|
@@ -8202,8 +7734,29 @@ var StorageManager3 = class _StorageManager {
|
|
|
8202
7734
|
${sanitized.text}
|
|
8203
7735
|
`;
|
|
8204
7736
|
const filePath = await this.resolveCategoryWritePath(category, id, today);
|
|
8205
|
-
|
|
8206
|
-
|
|
7737
|
+
const persistFile = async () => {
|
|
7738
|
+
if (tombstoneBlocked) {
|
|
7739
|
+
const duplicate = await this.findExistingTombstoneBlockedMemory(sanitized.text, category, fm.sourceConnector);
|
|
7740
|
+
if (duplicate) return duplicate;
|
|
7741
|
+
}
|
|
7742
|
+
await this.snapshotBeforeWrite(filePath, "write");
|
|
7743
|
+
await this.writeTombstoneBlockedMemory(filePath, fileContent, fm, sanitized.text, async () => {
|
|
7744
|
+
this.invalidateAllMemoriesCache();
|
|
7745
|
+
});
|
|
7746
|
+
return null;
|
|
7747
|
+
};
|
|
7748
|
+
const duplicateBlocked = tombstoneBlocked ? await this.withTombstoneBlockedCaptureWriteLock(
|
|
7749
|
+
persistFile,
|
|
7750
|
+
buildExplicitCaptureDedupKey(sanitized.text, category, fm.sourceConnector)
|
|
7751
|
+
) : await persistFile();
|
|
7752
|
+
if (duplicateBlocked) {
|
|
7753
|
+
return {
|
|
7754
|
+
id: duplicateBlocked.frontmatter.id,
|
|
7755
|
+
tombstoneBlocked: true,
|
|
7756
|
+
blockedBy: duplicateBlocked.frontmatter.blockedBy,
|
|
7757
|
+
duplicateOf: duplicateBlocked.frontmatter.id
|
|
7758
|
+
};
|
|
7759
|
+
}
|
|
8207
7760
|
await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
|
|
8208
7761
|
this.notifyMemoryWrite(filePath);
|
|
8209
7762
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.writeMemory", {
|
|
@@ -8272,10 +7825,7 @@ ${sanitized.text}
|
|
|
8272
7825
|
return await this.factContentHashPresentInCorpus(hash);
|
|
8273
7826
|
}
|
|
8274
7827
|
async factContentHashPresentInCorpus(targetHash) {
|
|
8275
|
-
const existing = [
|
|
8276
|
-
...await this.readAllMemories(),
|
|
8277
|
-
...await this.readAllColdMemories()
|
|
8278
|
-
];
|
|
7828
|
+
const existing = [...await this.readAllMemories(), ...await this.readAllColdMemories()];
|
|
8279
7829
|
for (const memory of existing) {
|
|
8280
7830
|
if (memory.frontmatter.category !== "fact") continue;
|
|
8281
7831
|
if (inferMemoryStatus(memory.frontmatter, memory.path) !== "active") continue;
|
|
@@ -8391,8 +7941,8 @@ ${sanitized.text}
|
|
|
8391
7941
|
await this.ensureDirectories();
|
|
8392
7942
|
const now = /* @__PURE__ */ new Date();
|
|
8393
7943
|
const day = now.toISOString().slice(0, 10);
|
|
8394
|
-
const dir =
|
|
8395
|
-
await
|
|
7944
|
+
const dir = path9.join(this.artifactsDir, day);
|
|
7945
|
+
await mkdir(dir, { recursive: true });
|
|
8396
7946
|
const id = `artifact-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
8397
7947
|
const fm = {
|
|
8398
7948
|
id,
|
|
@@ -8416,7 +7966,7 @@ ${sanitized.text}
|
|
|
8416
7966
|
log.warn(`artifact content rejected for ${id}; violations=${sanitized.violations.join(", ")}`);
|
|
8417
7967
|
return "";
|
|
8418
7968
|
}
|
|
8419
|
-
const filePath =
|
|
7969
|
+
const filePath = path9.join(dir, `${id}.md`);
|
|
8420
7970
|
await this.writeStorageSecureFile(filePath, `${serializeFrontmatter(fm)}
|
|
8421
7971
|
|
|
8422
7972
|
${sanitized.text}
|
|
@@ -8687,7 +8237,7 @@ ${sanitized.text}
|
|
|
8687
8237
|
* per-process in-memory cache safe across process boundaries.
|
|
8688
8238
|
*/
|
|
8689
8239
|
invalidateColdMemoriesCache() {
|
|
8690
|
-
const coldRoot =
|
|
8240
|
+
const coldRoot = path9.join(this.baseDir, "cold");
|
|
8691
8241
|
_StorageManager.coldMemoriesCache.delete(coldRoot);
|
|
8692
8242
|
this.bumpColdWriteVersion();
|
|
8693
8243
|
invalidateAllForDir(this.baseDir);
|
|
@@ -8789,7 +8339,7 @@ ${sanitized.text}
|
|
|
8789
8339
|
return updatedMs >= updatedAfterMs ? filePath : null;
|
|
8790
8340
|
}
|
|
8791
8341
|
try {
|
|
8792
|
-
const fileStat = await
|
|
8342
|
+
const fileStat = await stat2(filePath);
|
|
8793
8343
|
return fileStat.mtimeMs >= updatedAfterMs ? filePath : null;
|
|
8794
8344
|
} catch {
|
|
8795
8345
|
return filePath;
|
|
@@ -8802,7 +8352,7 @@ ${sanitized.text}
|
|
|
8802
8352
|
const correctionPaths = [];
|
|
8803
8353
|
const factPaths = [];
|
|
8804
8354
|
for (const filePath of filePaths) {
|
|
8805
|
-
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${
|
|
8355
|
+
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${path9.sep}`)) {
|
|
8806
8356
|
correctionPaths.push(filePath);
|
|
8807
8357
|
} else {
|
|
8808
8358
|
factPaths.push(filePath);
|
|
@@ -8865,7 +8415,7 @@ ${sanitized.text}
|
|
|
8865
8415
|
try {
|
|
8866
8416
|
const entries = await readdir5(dir, { withFileTypes: true });
|
|
8867
8417
|
for (const entry of entries) {
|
|
8868
|
-
const fullPath =
|
|
8418
|
+
const fullPath = path9.join(dir, entry.name);
|
|
8869
8419
|
if (entry.isDirectory()) {
|
|
8870
8420
|
await readDir(fullPath);
|
|
8871
8421
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -8898,7 +8448,7 @@ ${sanitized.text}
|
|
|
8898
8448
|
return this.memoryReadStore.readMemoryByPath(filePath);
|
|
8899
8449
|
}
|
|
8900
8450
|
resolveTierRootDir(tier) {
|
|
8901
|
-
return tier === "cold" ?
|
|
8451
|
+
return tier === "cold" ? path9.join(this.baseDir, "cold") : this.baseDir;
|
|
8902
8452
|
}
|
|
8903
8453
|
resolveMemoryDateDir(memory) {
|
|
8904
8454
|
const preferred = memory.frontmatter.created || memory.frontmatter.updated;
|
|
@@ -8913,13 +8463,13 @@ ${sanitized.text}
|
|
|
8913
8463
|
buildTierMemoryPath(memory, tier) {
|
|
8914
8464
|
const root = this.resolveTierRootDir(tier);
|
|
8915
8465
|
if (this.isArtifactMemory(memory)) {
|
|
8916
|
-
return
|
|
8466
|
+
return path9.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
8917
8467
|
}
|
|
8918
8468
|
if (memory.frontmatter.category === "correction") {
|
|
8919
|
-
return
|
|
8469
|
+
return path9.join(root, "corrections", `${memory.frontmatter.id}.md`);
|
|
8920
8470
|
}
|
|
8921
8471
|
const dir = categoryDirName(memory.frontmatter.category);
|
|
8922
|
-
return
|
|
8472
|
+
return path9.join(root, dir, this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
8923
8473
|
}
|
|
8924
8474
|
async writeMemoryFileAtomic(targetPath, memory) {
|
|
8925
8475
|
const fileContent = `${serializeFrontmatter(memory.frontmatter)}
|
|
@@ -8932,8 +8482,8 @@ ${memory.content}
|
|
|
8932
8482
|
}
|
|
8933
8483
|
async moveMemoryToPath(memory, targetPath) {
|
|
8934
8484
|
await this.writeMemoryFileAtomic(targetPath, memory);
|
|
8935
|
-
const sourcePath =
|
|
8936
|
-
const destPath =
|
|
8485
|
+
const sourcePath = path9.resolve(memory.path);
|
|
8486
|
+
const destPath = path9.resolve(targetPath);
|
|
8937
8487
|
if (sourcePath !== destPath) {
|
|
8938
8488
|
try {
|
|
8939
8489
|
await unlink3(memory.path);
|
|
@@ -8949,8 +8499,8 @@ ${memory.content}
|
|
|
8949
8499
|
}
|
|
8950
8500
|
async migrateMemoryToTier(memory, targetTier) {
|
|
8951
8501
|
const targetPath = this.buildTierMemoryPath(memory, targetTier);
|
|
8952
|
-
const sourcePath =
|
|
8953
|
-
const destPath =
|
|
8502
|
+
const sourcePath = path9.resolve(memory.path);
|
|
8503
|
+
const destPath = path9.resolve(targetPath);
|
|
8954
8504
|
if (sourcePath === destPath) {
|
|
8955
8505
|
return { changed: false, targetPath };
|
|
8956
8506
|
}
|
|
@@ -8977,7 +8527,7 @@ ${memory.content}
|
|
|
8977
8527
|
return { changed: true, targetPath };
|
|
8978
8528
|
}
|
|
8979
8529
|
get archiveDir() {
|
|
8980
|
-
return
|
|
8530
|
+
return path9.join(this.baseDir, "archive");
|
|
8981
8531
|
}
|
|
8982
8532
|
/**
|
|
8983
8533
|
* Archive a memory by moving it from facts/ to archive/YYYY-MM-DD/.
|
|
@@ -8985,48 +8535,52 @@ ${memory.content}
|
|
|
8985
8535
|
* Returns the new file path on success, null on failure.
|
|
8986
8536
|
*/
|
|
8987
8537
|
async archiveMemory(memory, lifecycle) {
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
8538
|
+
const archiveCurrent = async (current, markDurable) => {
|
|
8539
|
+
try {
|
|
8540
|
+
const now = lifecycle?.at ?? /* @__PURE__ */ new Date();
|
|
8541
|
+
const today = now.toISOString().slice(0, 10);
|
|
8542
|
+
const destDir = path9.join(this.archiveDir, today);
|
|
8543
|
+
await mkdir(destDir, { recursive: true });
|
|
8544
|
+
const updatedFm = {
|
|
8545
|
+
...current.frontmatter,
|
|
8546
|
+
status: "archived",
|
|
8547
|
+
archivedAt: now.toISOString(),
|
|
8548
|
+
updated: now.toISOString()
|
|
8549
|
+
};
|
|
8550
|
+
const fileContent = `${serializeFrontmatter(updatedFm)}
|
|
9000
8551
|
|
|
9001
|
-
${
|
|
8552
|
+
${current.content}
|
|
9002
8553
|
`;
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
|
|
9016
|
-
|
|
9017
|
-
|
|
9018
|
-
|
|
9019
|
-
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
8554
|
+
const destPath = path9.join(destDir, path9.basename(current.path));
|
|
8555
|
+
await this.writeStorageSecureFile(destPath, fileContent);
|
|
8556
|
+
await unlink3(current.path);
|
|
8557
|
+
markDurable();
|
|
8558
|
+
markProjectedMemoryPathInvalid(this.baseDir, current.frontmatter.id);
|
|
8559
|
+
this.invalidateAllMemoriesCache();
|
|
8560
|
+
await this.appendGeneratedMemoryLifecycleEventFailOpen(
|
|
8561
|
+
"storage.archiveMemory",
|
|
8562
|
+
{
|
|
8563
|
+
memoryId: current.frontmatter.id,
|
|
8564
|
+
eventType: "archived",
|
|
8565
|
+
timestamp: updatedFm.archivedAt ?? updatedFm.updated,
|
|
8566
|
+
actor: lifecycle?.actor ?? "storage.archiveMemory",
|
|
8567
|
+
reasonCode: lifecycle?.reasonCode,
|
|
8568
|
+
before: this.summarizeLifecycleState(current.frontmatter, current.path),
|
|
8569
|
+
after: this.summarizeLifecycleState(updatedFm, destPath),
|
|
8570
|
+
relatedMemoryIds: lifecycle?.relatedMemoryIds,
|
|
8571
|
+
correlationId: lifecycle?.correlationId
|
|
8572
|
+
},
|
|
8573
|
+
lifecycle?.ruleVersion
|
|
8574
|
+
);
|
|
8575
|
+
this.bumpMemoryStatusVersion();
|
|
8576
|
+
log.debug(`archived memory ${current.frontmatter.id} \u2192 ${destPath}`);
|
|
8577
|
+
return destPath;
|
|
8578
|
+
} catch (err) {
|
|
8579
|
+
log.warn(`failed to archive memory ${current.frontmatter.id}: ${err}`);
|
|
8580
|
+
return null;
|
|
8581
|
+
}
|
|
8582
|
+
};
|
|
8583
|
+
return await this.runTombstoneBlockedArchive(memory, archiveCurrent);
|
|
9030
8584
|
}
|
|
9031
8585
|
async readEntities() {
|
|
9032
8586
|
try {
|
|
@@ -9079,18 +8633,21 @@ ${memory.content}
|
|
|
9079
8633
|
}
|
|
9080
8634
|
async invalidateMemory(id) {
|
|
9081
8635
|
const memories = await this.readAllMemories();
|
|
9082
|
-
const memory = memories.find((
|
|
8636
|
+
const memory = memories.find((candidate) => candidate.frontmatter.id === id);
|
|
9083
8637
|
if (!memory) return false;
|
|
9084
|
-
|
|
9085
|
-
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
8638
|
+
return await this.runTombstoneBlockedInvalidation(
|
|
8639
|
+
memory,
|
|
8640
|
+
async (current, rebuildMarker, markDurable) => {
|
|
8641
|
+
await unlink3(current.path);
|
|
8642
|
+
markDurable();
|
|
8643
|
+
markProjectedMemoryPathInvalid(this.baseDir, id);
|
|
8644
|
+
this.invalidateAllMemoriesCache();
|
|
8645
|
+
await this.rebuildTombstoneBlockedCaptureAfterInvalidation(rebuildMarker);
|
|
8646
|
+
this.bumpMemoryStatusVersion();
|
|
8647
|
+
log.debug(`invalidated memory ${id}`);
|
|
8648
|
+
return true;
|
|
8649
|
+
}
|
|
8650
|
+
);
|
|
9094
8651
|
}
|
|
9095
8652
|
async updateMemory(id, newContent, options) {
|
|
9096
8653
|
const memories = await this.readAllMemories();
|
|
@@ -9114,7 +8671,9 @@ ${memory.content}
|
|
|
9114
8671
|
|
|
9115
8672
|
${sanitized.text}
|
|
9116
8673
|
`;
|
|
9117
|
-
await this.
|
|
8674
|
+
await this.writeTombstoneBlockedUpdate(memory, fileContent, updated, sanitized.text, async () => {
|
|
8675
|
+
this.invalidateAllMemoriesCache();
|
|
8676
|
+
});
|
|
9118
8677
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
9119
8678
|
await this.appendGeneratedMemoryLifecycleEventFailOpen("storage.updateMemory", {
|
|
9120
8679
|
memoryId: id,
|
|
@@ -9146,9 +8705,14 @@ ${sanitized.text}
|
|
|
9146
8705
|
|
|
9147
8706
|
${memory.content}
|
|
9148
8707
|
`;
|
|
9149
|
-
await this.
|
|
8708
|
+
await this.writeTombstoneBlockedFrontmatter(memory, fileContent, updated, async () => {
|
|
8709
|
+
this.invalidateAllMemoriesCache();
|
|
8710
|
+
if (memory.path.includes(`${path9.sep}cold${path9.sep}`)) {
|
|
8711
|
+
this.invalidateColdMemoriesCache();
|
|
8712
|
+
}
|
|
8713
|
+
});
|
|
9150
8714
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
9151
|
-
if (memory.path.includes(`${
|
|
8715
|
+
if (memory.path.includes(`${path9.sep}cold${path9.sep}`)) {
|
|
9152
8716
|
this.invalidateColdMemoriesCache();
|
|
9153
8717
|
}
|
|
9154
8718
|
try {
|
|
@@ -9201,15 +8765,14 @@ ${memory.content}
|
|
|
9201
8765
|
for (const m of memories) {
|
|
9202
8766
|
if (!m.frontmatter.expiresAt) continue;
|
|
9203
8767
|
const expiresAt = new Date(m.frontmatter.expiresAt).getTime();
|
|
9204
|
-
if (expiresAt
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
}
|
|
9212
|
-
}
|
|
8768
|
+
if (expiresAt >= now) continue;
|
|
8769
|
+
const removed = await this.deleteMemoryForMaintenance(m, (current) => {
|
|
8770
|
+
const currentExpiresAt = current.frontmatter.expiresAt ? new Date(current.frontmatter.expiresAt).getTime() : Number.NaN;
|
|
8771
|
+
return Number.isFinite(currentExpiresAt) && currentExpiresAt < now;
|
|
8772
|
+
});
|
|
8773
|
+
if (removed) {
|
|
8774
|
+
deleted.push(removed);
|
|
8775
|
+
log.debug(`cleaned expired memory ${removed.frontmatter.id} (TTL expired)`);
|
|
9213
8776
|
}
|
|
9214
8777
|
}
|
|
9215
8778
|
if (deleted.length > 0) {
|
|
@@ -9219,7 +8782,7 @@ ${memory.content}
|
|
|
9219
8782
|
return deleted;
|
|
9220
8783
|
}
|
|
9221
8784
|
async loadBuffer() {
|
|
9222
|
-
const bufferPath =
|
|
8785
|
+
const bufferPath = path9.join(this.stateDir, "buffer.json");
|
|
9223
8786
|
try {
|
|
9224
8787
|
const raw = await this.readStorageSecureFile(bufferPath);
|
|
9225
8788
|
return JSON.parse(raw);
|
|
@@ -9231,11 +8794,11 @@ ${memory.content}
|
|
|
9231
8794
|
}
|
|
9232
8795
|
async saveBuffer(state) {
|
|
9233
8796
|
await this.ensureDirectories();
|
|
9234
|
-
const bufferPath =
|
|
8797
|
+
const bufferPath = path9.join(this.stateDir, "buffer.json");
|
|
9235
8798
|
await this.writeStorageSecureFile(bufferPath, JSON.stringify(state, null, 2));
|
|
9236
8799
|
}
|
|
9237
8800
|
async loadMeta() {
|
|
9238
|
-
const metaPath =
|
|
8801
|
+
const metaPath = path9.join(this.stateDir, "meta.json");
|
|
9239
8802
|
try {
|
|
9240
8803
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
9241
8804
|
const parsed = JSON.parse(raw);
|
|
@@ -9268,7 +8831,7 @@ ${memory.content}
|
|
|
9268
8831
|
}
|
|
9269
8832
|
async saveMeta(state) {
|
|
9270
8833
|
await this.ensureDirectories();
|
|
9271
|
-
const metaPath =
|
|
8834
|
+
const metaPath = path9.join(this.stateDir, "meta.json");
|
|
9272
8835
|
await this.writeStorageSecureFile(metaPath, JSON.stringify(state, null, 2));
|
|
9273
8836
|
}
|
|
9274
8837
|
async appendMemoryActionEvents(events) {
|
|
@@ -9323,10 +8886,7 @@ ${memory.content}
|
|
|
9323
8886
|
* The offline CLI uses this path-aware variant for per-namespace ledgers so
|
|
9324
8887
|
* secure-store encryption and path-bound authentication remain consistent. */
|
|
9325
8888
|
async drainPendingMemoryLifecycleEventsForSyncAt(ledgerPath) {
|
|
9326
|
-
const target = this.assertManagedStoragePath(
|
|
9327
|
-
ledgerPath,
|
|
9328
|
-
"storage.drainPendingMemoryLifecycleEventsForSyncAt"
|
|
9329
|
-
);
|
|
8889
|
+
const target = this.assertManagedStoragePath(ledgerPath, "storage.drainPendingMemoryLifecycleEventsForSyncAt");
|
|
9330
8890
|
return drainPendingLifecycleLedgerForSync(
|
|
9331
8891
|
target,
|
|
9332
8892
|
{
|
|
@@ -9335,7 +8895,7 @@ ${memory.content}
|
|
|
9335
8895
|
},
|
|
9336
8896
|
(payload) => this.appendStorageSecureFile(target, payload),
|
|
9337
8897
|
async () => {
|
|
9338
|
-
await
|
|
8898
|
+
await mkdir(path9.dirname(target), { recursive: true });
|
|
9339
8899
|
}
|
|
9340
8900
|
);
|
|
9341
8901
|
}
|
|
@@ -9367,7 +8927,7 @@ ${memory.content}
|
|
|
9367
8927
|
let existingKeys;
|
|
9368
8928
|
let identity = null;
|
|
9369
8929
|
try {
|
|
9370
|
-
const st = await
|
|
8930
|
+
const st = await stat2(this.behaviorSignalsPath);
|
|
9371
8931
|
identity = { size: st.size, mtimeMs: st.mtimeMs };
|
|
9372
8932
|
if (this.behaviorSignalsKeyCache && this.behaviorSignalsKeyCache.identity.size === identity.size && this.behaviorSignalsKeyCache.identity.mtimeMs === identity.mtimeMs) {
|
|
9373
8933
|
existingKeys = this.behaviorSignalsKeyCache.keys;
|
|
@@ -9415,7 +8975,7 @@ ${memory.content}
|
|
|
9415
8975
|
await this.appendStorageSecureFile(this.behaviorSignalsPath, payload);
|
|
9416
8976
|
for (const key of pending) existingKeys.add(key);
|
|
9417
8977
|
try {
|
|
9418
|
-
const st = await
|
|
8978
|
+
const st = await stat2(this.behaviorSignalsPath);
|
|
9419
8979
|
const expectedSize = (identity?.size ?? 0) + Buffer.byteLength(payload, "utf-8");
|
|
9420
8980
|
if (st.size === expectedSize) {
|
|
9421
8981
|
this.behaviorSignalsKeyCache = {
|
|
@@ -9433,7 +8993,7 @@ ${memory.content}
|
|
|
9433
8993
|
async appendReextractJobs(events) {
|
|
9434
8994
|
if (events.length === 0) return 0;
|
|
9435
8995
|
await this.ensureDirectories();
|
|
9436
|
-
const filePath =
|
|
8996
|
+
const filePath = path9.join(this.stateDir, "reextract-jobs.jsonl");
|
|
9437
8997
|
const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
|
|
9438
8998
|
try {
|
|
9439
8999
|
await this.appendStorageSecureFile(filePath, lines);
|
|
@@ -9446,7 +9006,7 @@ ${memory.content}
|
|
|
9446
9006
|
}
|
|
9447
9007
|
async readReextractJobs(limit = 200) {
|
|
9448
9008
|
const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
|
|
9449
|
-
const filePath =
|
|
9009
|
+
const filePath = path9.join(this.stateDir, "reextract-jobs.jsonl");
|
|
9450
9010
|
try {
|
|
9451
9011
|
const raw = await this.readStorageSecureFile(filePath);
|
|
9452
9012
|
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
@@ -9526,10 +9086,7 @@ ${memory.content}
|
|
|
9526
9086
|
}
|
|
9527
9087
|
async readAllMemoryLifecycleEventsForCompaction() {
|
|
9528
9088
|
if (!await probeEncryptedRegularFileHeader(this.memoryLifecycleLedgerPath)) {
|
|
9529
|
-
return readAllLifecycleEventsFromLedger(
|
|
9530
|
-
this.memoryLifecycleLedgerPath,
|
|
9531
|
-
(p) => this.readStorageSecureFile(p)
|
|
9532
|
-
);
|
|
9089
|
+
return readAllLifecycleEventsFromLedger(this.memoryLifecycleLedgerPath, (p) => this.readStorageSecureFile(p));
|
|
9533
9090
|
}
|
|
9534
9091
|
return readAllLifecycleEventsFromLedgerBuffer(
|
|
9535
9092
|
this.memoryLifecycleLedgerPath,
|
|
@@ -9660,7 +9217,7 @@ ${memory.content}
|
|
|
9660
9217
|
return `${prefix}-${ts}-${rand}`;
|
|
9661
9218
|
}
|
|
9662
9219
|
async writeQuestion(question, context, priority) {
|
|
9663
|
-
await
|
|
9220
|
+
await mkdir(this.questionsDir, { recursive: true });
|
|
9664
9221
|
const id = this.generateId("q");
|
|
9665
9222
|
const frontmatter = {
|
|
9666
9223
|
id,
|
|
@@ -9676,7 +9233,7 @@ ${question}
|
|
|
9676
9233
|
|
|
9677
9234
|
**Context:** ${context}
|
|
9678
9235
|
`;
|
|
9679
|
-
const filePath =
|
|
9236
|
+
const filePath = path9.join(this.questionsDir, `${id}.md`);
|
|
9680
9237
|
await this.writeStorageSecureFile(filePath, content);
|
|
9681
9238
|
log.debug(`wrote question ${id} to ${filePath}`);
|
|
9682
9239
|
this.invalidateQuestionsCache();
|
|
@@ -9694,7 +9251,7 @@ ${question}
|
|
|
9694
9251
|
if (!match) return null;
|
|
9695
9252
|
const frontmatterStr = match[1];
|
|
9696
9253
|
const body = match[2].trim();
|
|
9697
|
-
const id = this.extractFrontmatterValue(frontmatterStr, "id") ??
|
|
9254
|
+
const id = this.extractFrontmatterValue(frontmatterStr, "id") ?? path9.basename(filePath, ".md");
|
|
9698
9255
|
const created = this.extractFrontmatterValue(frontmatterStr, "created") ?? "";
|
|
9699
9256
|
const priority = parseFloat(this.extractFrontmatterValue(frontmatterStr, "priority") ?? "0.5");
|
|
9700
9257
|
const resolved = this.extractFrontmatterValue(frontmatterStr, "resolved") === "true";
|
|
@@ -9750,11 +9307,11 @@ ${question}
|
|
|
9750
9307
|
} catch {
|
|
9751
9308
|
}
|
|
9752
9309
|
const hygiene = opts?.hygiene;
|
|
9753
|
-
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(
|
|
9310
|
+
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(path9.basename(identityPath));
|
|
9754
9311
|
if (rotateEnabled) {
|
|
9755
9312
|
const maxBytes = hygiene.rotateMaxBytes;
|
|
9756
9313
|
if (existing.length > maxBytes) {
|
|
9757
|
-
const archiveDir =
|
|
9314
|
+
const archiveDir = path9.join(workspaceDir, hygiene.archiveDir);
|
|
9758
9315
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
9759
9316
|
filePath: identityPath,
|
|
9760
9317
|
archiveDir,
|
|
@@ -9806,7 +9363,7 @@ ${reflection}
|
|
|
9806
9363
|
}
|
|
9807
9364
|
}
|
|
9808
9365
|
async writeIdentityReflections(content) {
|
|
9809
|
-
await
|
|
9366
|
+
await mkdir(this.identityDir, { recursive: true });
|
|
9810
9367
|
await this.writeStorageSecureFile(this.identityReflectionsPath, content);
|
|
9811
9368
|
}
|
|
9812
9369
|
async appendIdentityReflection(reflection) {
|
|
@@ -9838,7 +9395,7 @@ ${reflection}
|
|
|
9838
9395
|
|
|
9839
9396
|
${reflection}
|
|
9840
9397
|
`;
|
|
9841
|
-
await
|
|
9398
|
+
await mkdir(this.identityDir, { recursive: true });
|
|
9842
9399
|
await this.writeStorageSecureFile(this.identityReflectionsPath, `${existing.trimEnd()}${section}`);
|
|
9843
9400
|
log.debug(`appended namespace-local reflection to ${this.identityReflectionsPath}`);
|
|
9844
9401
|
}
|
|
@@ -9850,7 +9407,7 @@ ${reflection}
|
|
|
9850
9407
|
* Deduplicates by target+label.
|
|
9851
9408
|
*/
|
|
9852
9409
|
async addEntityRelationship(name, rel) {
|
|
9853
|
-
const filePath =
|
|
9410
|
+
const filePath = path9.join(this.entitiesDir, `${name}.md`);
|
|
9854
9411
|
let entity;
|
|
9855
9412
|
try {
|
|
9856
9413
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -9873,7 +9430,7 @@ ${reflection}
|
|
|
9873
9430
|
* Prepends to the beginning, prunes oldest entries beyond maxEntries.
|
|
9874
9431
|
*/
|
|
9875
9432
|
async addEntityActivity(name, entry, maxEntries) {
|
|
9876
|
-
const filePath =
|
|
9433
|
+
const filePath = path9.join(this.entitiesDir, `${name}.md`);
|
|
9877
9434
|
let entity;
|
|
9878
9435
|
try {
|
|
9879
9436
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -9893,7 +9450,7 @@ ${reflection}
|
|
|
9893
9450
|
this.invalidateKnowledgeIndexCache();
|
|
9894
9451
|
}
|
|
9895
9452
|
async addEntityAlias(name, alias) {
|
|
9896
|
-
const filePath =
|
|
9453
|
+
const filePath = path9.join(this.entitiesDir, `${name}.md`);
|
|
9897
9454
|
let entity;
|
|
9898
9455
|
try {
|
|
9899
9456
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -9921,7 +9478,7 @@ ${reflection}
|
|
|
9921
9478
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
9922
9479
|
let synthesisTimelineCount;
|
|
9923
9480
|
try {
|
|
9924
|
-
const filePath =
|
|
9481
|
+
const filePath = path9.join(this.entitiesDir, `${name}.md`);
|
|
9925
9482
|
const content = await this.readStorageSecureFile(filePath);
|
|
9926
9483
|
synthesisTimelineCount = parseEntityFile(content, this.entitySchemas).timeline.length;
|
|
9927
9484
|
} catch (err) {
|
|
@@ -9963,7 +9520,7 @@ ${reflection}
|
|
|
9963
9520
|
const rightTs = latestEntityTimelineTimestamp(right.entity) ?? "";
|
|
9964
9521
|
return compareEntityTimestamps(rightTs, leftTs);
|
|
9965
9522
|
}).map(({ entityName }) => entityName);
|
|
9966
|
-
await
|
|
9523
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
9967
9524
|
await this.writeStorageSecureFile(
|
|
9968
9525
|
this.entitySynthesisQueuePath,
|
|
9969
9526
|
JSON.stringify(
|
|
@@ -9983,7 +9540,7 @@ ${reflection}
|
|
|
9983
9540
|
if (queue.length === 0) return;
|
|
9984
9541
|
const removals = new Set(entityNames);
|
|
9985
9542
|
const nextQueue = queue.filter((name) => !removals.has(name));
|
|
9986
|
-
await
|
|
9543
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
9987
9544
|
await this.writeStorageSecureFile(
|
|
9988
9545
|
this.entitySynthesisQueuePath,
|
|
9989
9546
|
JSON.stringify(
|
|
@@ -10004,7 +9561,7 @@ ${reflection}
|
|
|
10004
9561
|
if (!raw) continue;
|
|
10005
9562
|
const serialized = serializeEntityFile(parseEntityFile(raw, this.entitySchemas), this.entitySchemas);
|
|
10006
9563
|
if (raw.trimEnd() === serialized.trimEnd()) continue;
|
|
10007
|
-
await this.writeStorageSecureFile(
|
|
9564
|
+
await this.writeStorageSecureFile(path9.join(this.entitiesDir, `${entityName}.md`), serialized);
|
|
10008
9565
|
migrated += 1;
|
|
10009
9566
|
}
|
|
10010
9567
|
if (migrated > 0) {
|
|
@@ -10070,15 +9627,18 @@ ${reflection}
|
|
|
10070
9627
|
const isResolved = m.frontmatter.tags.some((t) => t === "fulfilled" || t === "expired");
|
|
10071
9628
|
if (!isResolved) continue;
|
|
10072
9629
|
const updatedAt = new Date(m.frontmatter.updated).getTime();
|
|
10073
|
-
if (updatedAt
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
|
|
9630
|
+
if (updatedAt >= cutoff) continue;
|
|
9631
|
+
try {
|
|
9632
|
+
const removed = await this.deleteMemoryForMaintenance(m, (current) => {
|
|
9633
|
+
const currentResolved = current.frontmatter.tags.some((tag) => tag === "fulfilled" || tag === "expired");
|
|
9634
|
+
const currentUpdatedAt = new Date(current.frontmatter.updated).getTime();
|
|
9635
|
+
return currentResolved && currentUpdatedAt < cutoff;
|
|
9636
|
+
});
|
|
9637
|
+
if (removed) {
|
|
9638
|
+
deleted.push(removed);
|
|
9639
|
+
log.debug(`cleaned expired commitment ${removed.frontmatter.id}`);
|
|
10081
9640
|
}
|
|
9641
|
+
} catch {
|
|
10082
9642
|
}
|
|
10083
9643
|
}
|
|
10084
9644
|
if (deleted.length > 0) {
|
|
@@ -10097,14 +9657,14 @@ ${reflection}
|
|
|
10097
9657
|
if (entries.length === 0) return 0;
|
|
10098
9658
|
const memories = await this.readAllMemories();
|
|
10099
9659
|
const memoryMap = new Map(memories.map((m) => [m.frontmatter.id, m]));
|
|
10100
|
-
const memoryPathMap = new Map(memories.map((m) => [
|
|
9660
|
+
const memoryPathMap = new Map(memories.map((m) => [path9.resolve(m.path), m]));
|
|
10101
9661
|
let updated = 0;
|
|
10102
9662
|
const prevVersion = this.getMemoryCorpusVersion();
|
|
10103
9663
|
const keyId = this.hotCacheKeyId();
|
|
10104
9664
|
const warm = this.hotMemoriesCacheEnabled && getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
|
|
10105
9665
|
const appliedPatches = /* @__PURE__ */ new Map();
|
|
10106
9666
|
for (const entry of entries) {
|
|
10107
|
-
const memory = entry.memoryPath ? memoryPathMap.get(
|
|
9667
|
+
const memory = entry.memoryPath ? memoryPathMap.get(path9.resolve(entry.memoryPath)) : memoryMap.get(entry.memoryId);
|
|
10108
9668
|
if (!memory) continue;
|
|
10109
9669
|
const newFm = {
|
|
10110
9670
|
...memory.frontmatter,
|
|
@@ -10120,7 +9680,7 @@ ${memory.content}
|
|
|
10120
9680
|
if (warm) {
|
|
10121
9681
|
updateCacheOnWrite(this.baseDir, { ...memory, frontmatter: newFm }, keyId);
|
|
10122
9682
|
}
|
|
10123
|
-
appliedPatches.set(
|
|
9683
|
+
appliedPatches.set(path9.resolve(memory.path), {
|
|
10124
9684
|
accessCount: entry.newCount,
|
|
10125
9685
|
lastAccessed: entry.lastAccessed
|
|
10126
9686
|
});
|
|
@@ -10136,8 +9696,11 @@ ${memory.content}
|
|
|
10136
9696
|
const cur = getCachedMemories(this.baseDir, prevVersion, keyId);
|
|
10137
9697
|
if (cur) {
|
|
10138
9698
|
const reapplied = cur.map((m) => {
|
|
10139
|
-
const patch = appliedPatches.get(
|
|
10140
|
-
return patch ? {
|
|
9699
|
+
const patch = appliedPatches.get(path9.resolve(m.path));
|
|
9700
|
+
return patch ? {
|
|
9701
|
+
...m,
|
|
9702
|
+
frontmatter: { ...m.frontmatter, accessCount: patch.accessCount, lastAccessed: patch.lastAccessed }
|
|
9703
|
+
} : m;
|
|
10141
9704
|
});
|
|
10142
9705
|
setCachedMemories(this.baseDir, reapplied, produced, keyId, this.hotCacheTtlMs());
|
|
10143
9706
|
}
|
|
@@ -10168,7 +9731,7 @@ ${memory.content}
|
|
|
10168
9731
|
const pathsById = /* @__PURE__ */ new Map();
|
|
10169
9732
|
const filePathsById = /* @__PURE__ */ new Map();
|
|
10170
9733
|
for (const filePath of filePaths) {
|
|
10171
|
-
const memoryId =
|
|
9734
|
+
const memoryId = path9.basename(filePath, ".md");
|
|
10172
9735
|
if (!wantedIds.has(memoryId)) continue;
|
|
10173
9736
|
const paths = filePathsById.get(memoryId) ?? [];
|
|
10174
9737
|
paths.push(filePath);
|
|
@@ -10179,12 +9742,8 @@ ${memory.content}
|
|
|
10179
9742
|
const preferred = preferredPaths.get(id) ?? [];
|
|
10180
9743
|
const preferredMatches = [];
|
|
10181
9744
|
for (const preferredPath of preferred) {
|
|
10182
|
-
const directCandidates = new Set(
|
|
10183
|
-
|
|
10184
|
-
);
|
|
10185
|
-
const directMatch = existingPaths.find(
|
|
10186
|
-
(filePath) => directCandidates.has(path10.resolve(filePath))
|
|
10187
|
-
);
|
|
9745
|
+
const directCandidates = new Set(qmdResultPathCandidates(this.baseDir, preferredPath));
|
|
9746
|
+
const directMatch = existingPaths.find((filePath) => directCandidates.has(path9.resolve(filePath)));
|
|
10188
9747
|
if (directMatch) {
|
|
10189
9748
|
preferredMatches.push(directMatch);
|
|
10190
9749
|
continue;
|
|
@@ -10196,7 +9755,7 @@ ${memory.content}
|
|
|
10196
9755
|
candidates.add(candidate);
|
|
10197
9756
|
}
|
|
10198
9757
|
}
|
|
10199
|
-
const match = existingPaths.find((filePath) => candidates.has(
|
|
9758
|
+
const match = existingPaths.find((filePath) => candidates.has(path9.resolve(filePath)));
|
|
10200
9759
|
if (match) preferredMatches.push(match);
|
|
10201
9760
|
}
|
|
10202
9761
|
if (preferredMatches.length > 0) {
|
|
@@ -10329,10 +9888,17 @@ ${memory.content}
|
|
|
10329
9888
|
${sanitized.text}
|
|
10330
9889
|
`;
|
|
10331
9890
|
const filePath = await this.resolveCategoryWritePath(category, id, today);
|
|
10332
|
-
await this.
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
9891
|
+
return await this.writeTombstoneBlockedChunk(
|
|
9892
|
+
filePath,
|
|
9893
|
+
fileContent,
|
|
9894
|
+
fm,
|
|
9895
|
+
sanitized.text,
|
|
9896
|
+
() => this.findExistingTombstoneBlockedMemory(sanitized.text, category, fm.sourceConnector),
|
|
9897
|
+
async () => {
|
|
9898
|
+
await this.patchHotMemoriesCache({ addedPath: filePath }, "memory-create");
|
|
9899
|
+
log.debug(`wrote chunk ${id} (${chunkIndex + 1}/${chunkTotal}) to ${filePath}`);
|
|
9900
|
+
}
|
|
9901
|
+
);
|
|
10336
9902
|
}
|
|
10337
9903
|
/**
|
|
10338
9904
|
* Get all chunks for a given parent memory ID.
|
|
@@ -10402,7 +9968,12 @@ ${oldMemory.content}
|
|
|
10402
9968
|
const auditBody = `Superseded: ${oldMemory.content}
|
|
10403
9969
|
|
|
10404
9970
|
Reason: ${reason}`;
|
|
10405
|
-
const auditInput = {
|
|
9971
|
+
const auditInput = {
|
|
9972
|
+
content: auditBody,
|
|
9973
|
+
category: "correction",
|
|
9974
|
+
confidence: 1,
|
|
9975
|
+
tags: ["supersession", "auto-resolved"]
|
|
9976
|
+
};
|
|
10406
9977
|
const auditEnvelope = composeMemoryEnvelope(auditInput, { source: "contradiction-detection" });
|
|
10407
9978
|
await this.writeSealedMemory(auditEnvelope, { lineage: [oldMemoryId, newMemoryId] });
|
|
10408
9979
|
return true;
|
|
@@ -10415,14 +9986,14 @@ Reason: ${reason}`;
|
|
|
10415
9986
|
// Memory Summarization (Phase 4A)
|
|
10416
9987
|
// ---------------------------------------------------------------------------
|
|
10417
9988
|
get summariesDir() {
|
|
10418
|
-
return
|
|
9989
|
+
return path9.join(this.baseDir, "summaries");
|
|
10419
9990
|
}
|
|
10420
9991
|
/**
|
|
10421
9992
|
* Write a memory summary.
|
|
10422
9993
|
*/
|
|
10423
9994
|
async writeSummary(summary) {
|
|
10424
|
-
await
|
|
10425
|
-
const filePath =
|
|
9995
|
+
await mkdir(this.summariesDir, { recursive: true });
|
|
9996
|
+
const filePath = path9.join(this.summariesDir, `${summary.id}.json`);
|
|
10426
9997
|
await this.writeStorageSecureFile(filePath, JSON.stringify(summary, null, 2));
|
|
10427
9998
|
log.debug(`wrote summary ${summary.id}`);
|
|
10428
9999
|
}
|
|
@@ -10435,7 +10006,7 @@ Reason: ${reason}`;
|
|
|
10435
10006
|
const summaries = [];
|
|
10436
10007
|
for (const file of files) {
|
|
10437
10008
|
if (!file.endsWith(".json")) continue;
|
|
10438
|
-
const filePath =
|
|
10009
|
+
const filePath = path9.join(this.summariesDir, file);
|
|
10439
10010
|
const raw = await this.readStorageSecureFile(filePath);
|
|
10440
10011
|
summaries.push(JSON.parse(raw));
|
|
10441
10012
|
}
|
|
@@ -10497,8 +10068,8 @@ ${memory.content}
|
|
|
10497
10068
|
* Save topic scores to meta.json.
|
|
10498
10069
|
*/
|
|
10499
10070
|
async saveTopics(topics) {
|
|
10500
|
-
const metaPath =
|
|
10501
|
-
await
|
|
10071
|
+
const metaPath = path9.join(this.stateDir, "topics.json");
|
|
10072
|
+
await mkdir(this.stateDir, { recursive: true });
|
|
10502
10073
|
await this.writeStorageSecureFile(
|
|
10503
10074
|
metaPath,
|
|
10504
10075
|
JSON.stringify({ topics, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)
|
|
@@ -10509,7 +10080,7 @@ ${memory.content}
|
|
|
10509
10080
|
* Load topic scores from meta.json.
|
|
10510
10081
|
*/
|
|
10511
10082
|
async loadTopics() {
|
|
10512
|
-
const metaPath =
|
|
10083
|
+
const metaPath = path9.join(this.stateDir, "topics.json");
|
|
10513
10084
|
try {
|
|
10514
10085
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
10515
10086
|
return JSON.parse(raw);
|
|
@@ -10620,8 +10191,6 @@ export {
|
|
|
10620
10191
|
countEntityStructuredFacts,
|
|
10621
10192
|
fingerprintEntityStructuredFacts,
|
|
10622
10193
|
isEntitySynthesisStale,
|
|
10623
|
-
FactHashIndexNotAuthoritativeError,
|
|
10624
|
-
ContentHashIndex,
|
|
10625
10194
|
parseFrontmatterStringValue,
|
|
10626
10195
|
parseFrontmatter,
|
|
10627
10196
|
normalizeFrontmatterForPath,
|
|
@@ -10631,4 +10200,4 @@ export {
|
|
|
10631
10200
|
isValidBufferSurpriseEvent,
|
|
10632
10201
|
StorageManager3 as StorageManager
|
|
10633
10202
|
};
|
|
10634
|
-
//# sourceMappingURL=chunk-
|
|
10203
|
+
//# sourceMappingURL=chunk-RGIWINDN.js.map
|