@remnic/core 9.27.0 → 9.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +13 -10
- package/dist/access-admin-ops-surface.js +43 -38
- package/dist/access-audit.js +2 -2
- package/dist/access-authorization-probe.d.ts +13 -10
- package/dist/access-authorization-probe.js +44 -39
- package/dist/access-boundary.d.ts +13 -10
- package/dist/access-boundary.js +43 -38
- package/dist/access-cli.js +100 -94
- package/dist/access-cli.js.map +1 -1
- package/dist/access-health-types.d.ts +10 -1
- package/dist/access-http-lcm-compaction.d.ts +13 -10
- package/dist/access-http.d.ts +13 -10
- package/dist/access-http.js +53 -48
- package/dist/access-identity-continuity-surface.d.ts +9 -8
- package/dist/access-identity-continuity-surface.js +43 -38
- package/dist/access-lcm-surface.d.ts +13 -10
- package/dist/access-lcm-surface.js +43 -38
- package/dist/access-mcp.d.ts +13 -10
- package/dist/access-mcp.js +48 -43
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-wRSZEO5V.d.ts → access-namespace-preflight-BiQ_-iUJ.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +43 -38
- package/dist/access-observe-write-surface.d.ts +13 -10
- package/dist/access-observe-write-surface.js +43 -38
- package/dist/access-operations-batch.js +45 -40
- package/dist/access-operations.d.ts +16 -13
- package/dist/access-operations.js +47 -42
- package/dist/access-recall-concurrency.d.ts +13 -10
- package/dist/access-recall-concurrency.js +43 -38
- package/dist/access-recall-response.d.ts +13 -10
- package/dist/access-recall-response.js +43 -38
- package/dist/access-recall-surface.d.ts +13 -10
- package/dist/access-recall-surface.js +43 -38
- package/dist/access-schema.d.ts +80 -80
- package/dist/access-schema.js +4 -4
- package/dist/{access-service-B4-Fu9x2.d.ts → access-service-CXkIgcGh.d.ts} +12 -8
- package/dist/access-service.d.ts +13 -10
- package/dist/access-service.js +43 -38
- package/dist/access-surface-catalog.d.ts +13 -10
- 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 +4 -2
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +7 -7
- package/dist/adapters/registry.js +3 -3
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -8
- package/dist/briefing.d.ts +3 -2
- package/dist/briefing.js +12 -10
- 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/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-7FJQINUR.js} +2 -2
- package/dist/{catalog-BLLxZ5L5.d.ts → catalog-Bouj8Gfg.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 +13 -11
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-7RZJWZ46.js → chunk-227UFSEQ.js} +5 -5
- package/dist/{chunk-UT3OQPFA.js → chunk-2BEJ4XCF.js} +2 -2
- package/dist/{chunk-EOLC6EXV.js → chunk-2FYNLDKJ.js} +24 -3
- package/dist/chunk-2FYNLDKJ.js.map +1 -0
- package/dist/{chunk-BXLOS5AJ.js → chunk-2NLLXCJG.js} +2 -2
- package/dist/{chunk-6NNNURLK.js → chunk-2PHX3I5C.js} +8 -8
- package/dist/{chunk-HORJVJ6K.js → chunk-2TQ2UFKH.js} +65 -60
- package/dist/chunk-2TQ2UFKH.js.map +1 -0
- package/dist/{chunk-RPA2RX5P.js → chunk-3BSJBIZ3.js} +2 -2
- package/dist/{chunk-JCVQV6QX.js → chunk-3UOSVTFR.js} +11 -11
- package/dist/{chunk-AJ7QCSIS.js → chunk-7CPOZH6Y.js} +2 -2
- package/dist/{chunk-MEBQFQ26.js → chunk-AHFZXUZM.js} +7 -5
- package/dist/chunk-AHFZXUZM.js.map +1 -0
- package/dist/{chunk-U3TH5Q2Q.js → chunk-ALTP3MFS.js} +2 -2
- package/dist/{chunk-77CNV7DY.js → chunk-BLAKS3CP.js} +4 -4
- package/dist/{chunk-XQ4EH6DI.js → chunk-BR4UFMLO.js} +2 -2
- package/dist/chunk-CVILPI77.js +432 -0
- package/dist/chunk-CVILPI77.js.map +1 -0
- package/dist/{chunk-WEAO45M4.js → chunk-DKQM5Q6Q.js} +2 -2
- package/dist/{chunk-CSXHRAJJ.js → chunk-EG64LXKL.js} +3 -3
- package/dist/{chunk-PTN6UAUA.js → chunk-ES36HTSC.js} +4 -4
- package/dist/{chunk-5BRT4Z2I.js → chunk-EZWWCN7I.js} +2 -2
- package/dist/{chunk-XKGPIWGI.js → chunk-FBZTWUK5.js} +5 -5
- package/dist/{chunk-GJJBPI6I.js → chunk-FOCEI3TN.js} +6 -3
- package/dist/chunk-FOCEI3TN.js.map +1 -0
- package/dist/{chunk-FRVTCCGD.js → chunk-GJPCP5CA.js} +2 -2
- package/dist/{chunk-CI26JDZA.js → chunk-GQSMYXKL.js} +6 -4
- package/dist/{chunk-CI26JDZA.js.map → chunk-GQSMYXKL.js.map} +1 -1
- package/dist/{chunk-TGQ2NTWH.js → chunk-IJ6WHMFB.js} +16 -2
- package/dist/{chunk-TGQ2NTWH.js.map → chunk-IJ6WHMFB.js.map} +1 -1
- package/dist/{chunk-VDX2J7OX.js → chunk-IPLYGWQF.js} +6 -6
- package/dist/{chunk-YSGBD3QN.js → chunk-IYT632D5.js} +3 -3
- package/dist/{chunk-2QSZNTDO.js → chunk-JBPKEARU.js} +7 -7
- package/dist/{chunk-RSYJV25N.js → chunk-JIF3VRKQ.js} +8 -8
- package/dist/{chunk-V25OWXAC.js → chunk-JQ7T5VDC.js} +1 -1
- package/dist/chunk-JQ7T5VDC.js.map +1 -0
- package/dist/{chunk-EUSLKI4X.js → chunk-KKBTBUVT.js} +2 -2
- package/dist/{chunk-RNY3BO35.js → chunk-LIHC3T43.js} +2 -2
- package/dist/{chunk-RGJBM7Z3.js → chunk-MJPV4LBH.js} +79 -79
- package/dist/{chunk-RGJBM7Z3.js.map → chunk-MJPV4LBH.js.map} +1 -1
- package/dist/{chunk-UGH6VPPO.js → chunk-MWTMTW2T.js} +2 -2
- package/dist/{chunk-MG6MNSP4.js → chunk-MXJ3VP6S.js} +6 -6
- package/dist/{chunk-XKO7BM2C.js → chunk-NBNDVNJ2.js} +186 -25
- package/dist/chunk-NBNDVNJ2.js.map +1 -0
- package/dist/{chunk-M5EPMNS4.js → chunk-P2CLYN6H.js} +2 -2
- package/dist/{chunk-BNGPHGVZ.js → chunk-PXQM2HOG.js} +13 -6
- package/dist/{chunk-BNGPHGVZ.js.map → chunk-PXQM2HOG.js.map} +1 -1
- package/dist/{chunk-CIQXI47W.js → chunk-Q5VJUNXA.js} +1192 -268
- package/dist/chunk-Q5VJUNXA.js.map +1 -0
- package/dist/{chunk-KGTD2KZJ.js → chunk-Q6YJW6KE.js} +2 -2
- package/dist/{chunk-X3SQYAPP.js → chunk-R372P2EH.js} +2 -2
- package/dist/{chunk-4MJZTF42.js → chunk-RKXZ7P4V.js} +3 -3
- package/dist/chunk-RMMVQQA6.js +70 -0
- package/dist/chunk-RMMVQQA6.js.map +1 -0
- package/dist/{chunk-2RAC4JIG.js → chunk-S24E4MBQ.js} +14 -14
- package/dist/chunk-S24E4MBQ.js.map +1 -0
- package/dist/{chunk-RYHWZ52A.js → chunk-S2BBISHU.js} +4 -4
- package/dist/{chunk-LLISELSY.js → chunk-TBMRQZDY.js} +2 -2
- package/dist/{chunk-FMLKOL2Z.js → chunk-TC3VNKGX.js} +4 -4
- package/dist/{chunk-7K5Q6COX.js → chunk-TVVEYCNW.js} +4 -4
- package/dist/{chunk-NUWMZIYZ.js → chunk-UR5SIN7H.js} +53 -35
- package/dist/chunk-UR5SIN7H.js.map +1 -0
- package/dist/{chunk-JDOJZTFF.js → chunk-VFNI3RPT.js} +2 -2
- package/dist/chunk-VNA7W7QF.js +134 -0
- package/dist/chunk-VNA7W7QF.js.map +1 -0
- package/dist/{chunk-EYJSW2RV.js → chunk-VYYNBJXH.js} +5 -5
- package/dist/chunk-WNIUPHWV.js +152 -0
- package/dist/chunk-WNIUPHWV.js.map +1 -0
- package/dist/chunk-WRJVB3C2.js +118 -0
- package/dist/chunk-WRJVB3C2.js.map +1 -0
- package/dist/{chunk-BHLN7L7M.js → chunk-WVVK2RFM.js} +6 -6
- package/dist/{chunk-DQEMWVMT.js → chunk-X7Y7WX73.js} +1 -1
- package/dist/{chunk-HEXSNYJD.js → chunk-YOBNCD3Z.js} +6 -6
- package/dist/chunk-YXZG5TVN.js +40 -0
- package/dist/chunk-YXZG5TVN.js.map +1 -0
- package/dist/{chunk-4DJQYKMN.js → chunk-ZIKENA2C.js} +2 -2
- package/dist/chunk-ZIKENA2C.js.map +1 -0
- package/dist/{chunk-SK3EYPKC.js → chunk-ZN4UZWMT.js} +2 -2
- package/dist/{cli-Bz7X7dGx.d.ts → cli-DJbkaZBU.d.ts} +5 -5
- package/dist/cli.d.ts +14 -12
- package/dist/cli.js +79 -74
- package/dist/compounding/engine.d.ts +3 -2
- package/dist/compounding/engine.js +12 -10
- 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 +4 -2
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +12 -10
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +12 -10
- 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/backend.js +2 -2
- 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 +15 -2
- package/dist/corpus-watermark.js +25 -21
- 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-id-normalization.d.ts +4 -0
- package/dist/entity-id-normalization.js +10 -0
- package/dist/entity-id-normalization.js.map +1 -0
- package/dist/entity-retrieval-boundaries.d.ts +3 -0
- package/dist/entity-retrieval-boundaries.js +8 -0
- package/dist/entity-retrieval-boundaries.js.map +1 -0
- package/dist/entity-retrieval.d.ts +3 -2
- package/dist/entity-retrieval.js +13 -10
- package/dist/entity-schema.d.ts +1 -1
- package/dist/entity-schema.js +1 -1
- package/dist/explicit-capture.d.ts +9 -8
- 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.d.ts +1 -1
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-LEXL3XYL.js → first-start-migration-BKNGOFED.js} +4 -4
- 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 +409 -407
- package/dist/index.js +147 -141
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/index.js +9 -9
- 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 +12 -10
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +12 -10
- package/dist/maintenance/rebuild-memory-projection.d.ts +3 -2
- package/dist/maintenance/rebuild-memory-projection.js +13 -11
- package/dist/{maintenance-DgnD2z8S.d.ts → maintenance-DveW6azo.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +13 -10
- 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-mutations.d.ts +4 -0
- package/dist/memory-projection-mutations.js +15 -0
- package/dist/memory-projection-mutations.js.map +1 -0
- 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 +22 -20
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +4 -3
- package/dist/namespaces/storage.js +12 -10
- 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 +3 -3
- package/dist/operator-doctor-corpus.d.ts +22 -3
- package/dist/operator-doctor-corpus.js +32 -22
- package/dist/operator-doctor-replica.d.ts +52 -0
- package/dist/operator-doctor-replica.js +15 -0
- package/dist/operator-doctor-replica.js.map +1 -0
- package/dist/operator-toolkit.d.ts +7 -3
- package/dist/operator-toolkit.js +33 -27
- package/dist/orchestration/compression-guideline-coordinator.d.ts +3 -2
- package/dist/orchestration/maintenance.d.ts +5 -4
- package/dist/orchestration/maintenance.js +17 -15
- package/dist/{orchestrator-rIHmHpv1.d.ts → orchestrator-Cvlgpe6I.d.ts} +7 -7
- package/dist/orchestrator.d.ts +9 -8
- package/dist/orchestrator.js +99 -93
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-Bq5zU6Bp.d.ts → pipeline-BveYVaz7.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/{qmd-3ILJkC70.d.ts → qmd-CLWDsbYR.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- 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-explain-renderer.js +3 -3
- 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-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- 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 +279 -0
- package/dist/replica-divergence.js +28 -0
- package/dist/replica-divergence.js.map +1 -0
- package/dist/replica-peers-config.d.ts +5 -0
- package/dist/replica-peers-config.js +13 -0
- package/dist/replica-peers-config.js.map +1 -0
- package/dist/resolve-auth-token.d.ts +21 -2
- package/dist/resolve-auth-token.js +5 -1
- package/dist/resume-bundles.js +5 -3
- 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/schemas.d.ts +76 -76
- package/dist/{scope-profiles-D0-ff4xT.d.ts → scope-profiles-BVf3DZ7z.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +13 -13
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-4Tfh7c1g.d.ts → semantic-consolidation-DXLV47y2.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +13 -11
- package/dist/semantic-rule-promotion.js +12 -10
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +12 -10
- package/dist/{service-cTvil7D_.d.ts → service-DbgPTeMJ.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 +9 -9
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-C9ibpTme.d.ts → storage-DOzTm8wG.d.ts} +13 -19
- package/dist/storage.d.ts +3 -2
- package/dist/storage.js +13 -10
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- 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/transfer/autodetect.js +1 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +3 -3
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/types.d.ts +66 -66
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-DHBxIF0s.d.ts → types-BA7W-_xy.d.ts} +53 -4
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +12 -10
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-health-types.ts +8 -0
- package/src/access-http.test.ts +4 -0
- package/src/access-service-health.test.ts +23 -0
- package/src/access-service.ts +27 -4
- package/src/cli.ts +9 -7
- package/src/config.ts +2 -1
- package/src/corpus-watermark.test.ts +21 -0
- package/src/corpus-watermark.ts +25 -3
- package/src/entity-id-normalization.ts +55 -0
- package/src/entity-retrieval-boundaries.ts +134 -0
- package/src/entity-retrieval.ts +253 -27
- package/src/entity-schema.ts +2 -1
- package/src/memory-projection-mutations.ts +132 -0
- package/src/namespaces/storage.ts +5 -3
- package/src/operator-doctor-corpus.ts +41 -0
- package/src/operator-doctor-replica.test.ts +261 -0
- package/src/operator-doctor-replica.ts +129 -0
- package/src/operator-toolkit.ts +5 -4
- package/src/orchestration/orchestrator-init.ts +2 -2
- package/src/replica-divergence.test.ts +1475 -0
- package/src/replica-divergence.ts +910 -0
- package/src/replica-peers-config.ts +250 -0
- package/src/resolve-auth-token.ts +38 -0
- package/src/storage/entity-canonical-id-migration-adapter.ts +72 -0
- package/src/storage/entity-canonical-id-migration-runner.ts +68 -0
- package/src/storage/entity-canonical-id-migration.ts +904 -0
- package/src/storage/entity-store.ts +44 -0
- package/src/storage/memory-frontmatter-metadata.ts +17 -0
- package/src/storage/memory-migration-serialization.ts +37 -0
- package/src/storage/memory-read-store.ts +3 -2
- package/src/storage/profile-header.ts +8 -7
- package/src/storage-profile-timestamp.test.ts +68 -0
- package/src/storage.ts +68 -69
- package/src/types.ts +3 -3
- package/dist/chunk-2RAC4JIG.js.map +0 -1
- package/dist/chunk-4DJQYKMN.js.map +0 -1
- package/dist/chunk-CIQXI47W.js.map +0 -1
- package/dist/chunk-EOLC6EXV.js.map +0 -1
- package/dist/chunk-GJJBPI6I.js.map +0 -1
- package/dist/chunk-HORJVJ6K.js.map +0 -1
- package/dist/chunk-MEBQFQ26.js.map +0 -1
- package/dist/chunk-NUWMZIYZ.js.map +0 -1
- package/dist/chunk-V25OWXAC.js.map +0 -1
- package/dist/chunk-XKO7BM2C.js.map +0 -1
- /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-7FJQINUR.js.map} +0 -0
- /package/dist/{chunk-7RZJWZ46.js.map → chunk-227UFSEQ.js.map} +0 -0
- /package/dist/{chunk-UT3OQPFA.js.map → chunk-2BEJ4XCF.js.map} +0 -0
- /package/dist/{chunk-BXLOS5AJ.js.map → chunk-2NLLXCJG.js.map} +0 -0
- /package/dist/{chunk-6NNNURLK.js.map → chunk-2PHX3I5C.js.map} +0 -0
- /package/dist/{chunk-RPA2RX5P.js.map → chunk-3BSJBIZ3.js.map} +0 -0
- /package/dist/{chunk-JCVQV6QX.js.map → chunk-3UOSVTFR.js.map} +0 -0
- /package/dist/{chunk-AJ7QCSIS.js.map → chunk-7CPOZH6Y.js.map} +0 -0
- /package/dist/{chunk-U3TH5Q2Q.js.map → chunk-ALTP3MFS.js.map} +0 -0
- /package/dist/{chunk-77CNV7DY.js.map → chunk-BLAKS3CP.js.map} +0 -0
- /package/dist/{chunk-XQ4EH6DI.js.map → chunk-BR4UFMLO.js.map} +0 -0
- /package/dist/{chunk-WEAO45M4.js.map → chunk-DKQM5Q6Q.js.map} +0 -0
- /package/dist/{chunk-CSXHRAJJ.js.map → chunk-EG64LXKL.js.map} +0 -0
- /package/dist/{chunk-PTN6UAUA.js.map → chunk-ES36HTSC.js.map} +0 -0
- /package/dist/{chunk-5BRT4Z2I.js.map → chunk-EZWWCN7I.js.map} +0 -0
- /package/dist/{chunk-XKGPIWGI.js.map → chunk-FBZTWUK5.js.map} +0 -0
- /package/dist/{chunk-FRVTCCGD.js.map → chunk-GJPCP5CA.js.map} +0 -0
- /package/dist/{chunk-VDX2J7OX.js.map → chunk-IPLYGWQF.js.map} +0 -0
- /package/dist/{chunk-YSGBD3QN.js.map → chunk-IYT632D5.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-RSYJV25N.js.map → chunk-JIF3VRKQ.js.map} +0 -0
- /package/dist/{chunk-EUSLKI4X.js.map → chunk-KKBTBUVT.js.map} +0 -0
- /package/dist/{chunk-RNY3BO35.js.map → chunk-LIHC3T43.js.map} +0 -0
- /package/dist/{chunk-UGH6VPPO.js.map → chunk-MWTMTW2T.js.map} +0 -0
- /package/dist/{chunk-MG6MNSP4.js.map → chunk-MXJ3VP6S.js.map} +0 -0
- /package/dist/{chunk-M5EPMNS4.js.map → chunk-P2CLYN6H.js.map} +0 -0
- /package/dist/{chunk-KGTD2KZJ.js.map → chunk-Q6YJW6KE.js.map} +0 -0
- /package/dist/{chunk-X3SQYAPP.js.map → chunk-R372P2EH.js.map} +0 -0
- /package/dist/{chunk-4MJZTF42.js.map → chunk-RKXZ7P4V.js.map} +0 -0
- /package/dist/{chunk-RYHWZ52A.js.map → chunk-S2BBISHU.js.map} +0 -0
- /package/dist/{chunk-LLISELSY.js.map → chunk-TBMRQZDY.js.map} +0 -0
- /package/dist/{chunk-FMLKOL2Z.js.map → chunk-TC3VNKGX.js.map} +0 -0
- /package/dist/{chunk-7K5Q6COX.js.map → chunk-TVVEYCNW.js.map} +0 -0
- /package/dist/{chunk-JDOJZTFF.js.map → chunk-VFNI3RPT.js.map} +0 -0
- /package/dist/{chunk-EYJSW2RV.js.map → chunk-VYYNBJXH.js.map} +0 -0
- /package/dist/{chunk-BHLN7L7M.js.map → chunk-WVVK2RFM.js.map} +0 -0
- /package/dist/{chunk-DQEMWVMT.js.map → chunk-X7Y7WX73.js.map} +0 -0
- /package/dist/{chunk-HEXSNYJD.js.map → chunk-YOBNCD3Z.js.map} +0 -0
- /package/dist/{chunk-SK3EYPKC.js.map → chunk-ZN4UZWMT.js.map} +0 -0
- /package/dist/{first-start-migration-LEXL3XYL.js.map → first-start-migration-BKNGOFED.js.map} +0 -0
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TombstoneStore,
|
|
3
|
+
buildRetiredFactTombstoneInputs,
|
|
4
|
+
collectRetiredMemoriesForRebuild
|
|
5
|
+
} from "./chunk-2QMKMVHH.js";
|
|
1
6
|
import {
|
|
2
7
|
WEARABLES_DIR_NAME,
|
|
3
8
|
escapeSegmentText,
|
|
@@ -10,26 +15,23 @@ import {
|
|
|
10
15
|
unescapeSpeakerLabel
|
|
11
16
|
} from "./chunk-UHVXE24Y.js";
|
|
12
17
|
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
collectRetiredMemoriesForRebuild
|
|
16
|
-
} from "./chunk-2QMKMVHH.js";
|
|
17
|
-
import {
|
|
18
|
+
computeSupersessionKey,
|
|
19
|
+
normalizeSupersessionKey,
|
|
18
20
|
supersessionKeysForFact
|
|
19
21
|
} from "./chunk-DNHVPZOD.js";
|
|
20
|
-
import {
|
|
21
|
-
assertMemoryFrontmatterId,
|
|
22
|
-
warnProjectionFallback
|
|
23
|
-
} from "./chunk-HKF5VASQ.js";
|
|
24
22
|
import {
|
|
25
23
|
DEFAULT_CITATION_FORMAT,
|
|
26
24
|
hasCitation,
|
|
27
25
|
stripCitationForTemplate
|
|
28
26
|
} from "./chunk-J6A3CX5N.js";
|
|
27
|
+
import {
|
|
28
|
+
assertMemoryFrontmatterId,
|
|
29
|
+
warnProjectionFallback
|
|
30
|
+
} from "./chunk-HKF5VASQ.js";
|
|
29
31
|
import {
|
|
30
32
|
SPECULATIVE_TTL_DAYS,
|
|
31
33
|
confidenceTier
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-JQ7T5VDC.js";
|
|
33
35
|
import {
|
|
34
36
|
appendLifecycleEventsSerialized,
|
|
35
37
|
drainPendingLifecycleLedgerForSync,
|
|
@@ -40,14 +42,10 @@ import {
|
|
|
40
42
|
readMaybeEncryptedLines,
|
|
41
43
|
readMemoryActionEventRowsFromLines,
|
|
42
44
|
serializeLifecycleAppendPayload
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-FBZTWUK5.js";
|
|
44
46
|
import {
|
|
45
47
|
namespaceIdentityFromToken
|
|
46
48
|
} from "./chunk-EGGE52UU.js";
|
|
47
|
-
import {
|
|
48
|
-
assertPathInsideRoot,
|
|
49
|
-
pathIsInside
|
|
50
|
-
} from "./chunk-GCASGYIO.js";
|
|
51
49
|
import {
|
|
52
50
|
withHeldFileLock
|
|
53
51
|
} from "./chunk-WL5EBRDH.js";
|
|
@@ -60,6 +58,14 @@ import {
|
|
|
60
58
|
normalizeProjectionPreview,
|
|
61
59
|
normalizeProjectionTags
|
|
62
60
|
} from "./chunk-SCU65EZI.js";
|
|
61
|
+
import {
|
|
62
|
+
rewriteProjectedEntityReferences,
|
|
63
|
+
rewriteProjectedMemoryEntityReference
|
|
64
|
+
} from "./chunk-WRJVB3C2.js";
|
|
65
|
+
import {
|
|
66
|
+
assertPathInsideRoot,
|
|
67
|
+
pathIsInside
|
|
68
|
+
} from "./chunk-GCASGYIO.js";
|
|
63
69
|
import {
|
|
64
70
|
markProjectedMemoryPathInvalid,
|
|
65
71
|
openProjectionReadonly,
|
|
@@ -81,6 +87,10 @@ import {
|
|
|
81
87
|
serializeContinuityIncident,
|
|
82
88
|
upsertContinuityLoopInMarkdown
|
|
83
89
|
} from "./chunk-LDXUBPMO.js";
|
|
90
|
+
import {
|
|
91
|
+
parseFaithfulnessField,
|
|
92
|
+
serializeFaithfulnessFields
|
|
93
|
+
} from "./chunk-JYIJNNN3.js";
|
|
84
94
|
import {
|
|
85
95
|
composeMemoryEnvelope,
|
|
86
96
|
isSealedMemoryEnvelope,
|
|
@@ -91,9 +101,8 @@ import {
|
|
|
91
101
|
stripAttributesSuffix
|
|
92
102
|
} from "./chunk-R3DJVOSM.js";
|
|
93
103
|
import {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
} from "./chunk-JYIJNNN3.js";
|
|
104
|
+
normalizeEntityName
|
|
105
|
+
} from "./chunk-YXZG5TVN.js";
|
|
97
106
|
import {
|
|
98
107
|
createVersion
|
|
99
108
|
} from "./chunk-5VQJBH2G.js";
|
|
@@ -131,14 +140,14 @@ import {
|
|
|
131
140
|
reconcileProvenanceRead,
|
|
132
141
|
serializeProvenanceFields
|
|
133
142
|
} from "./chunk-5I4M26Q7.js";
|
|
134
|
-
import {
|
|
135
|
-
sanitizeMemoryContent
|
|
136
|
-
} from "./chunk-5GKPFL3S.js";
|
|
137
143
|
import {
|
|
138
144
|
matchEntitySchemaSection,
|
|
139
145
|
normalizeEntityStructuredSection,
|
|
140
146
|
sortStructuredSectionsBySchema
|
|
141
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-ZIKENA2C.js";
|
|
148
|
+
import {
|
|
149
|
+
sanitizeMemoryContent
|
|
150
|
+
} from "./chunk-5GKPFL3S.js";
|
|
142
151
|
import {
|
|
143
152
|
parseFlexibleIsoTimestamp
|
|
144
153
|
} from "./chunk-P7FMDTKL.js";
|
|
@@ -195,24 +204,40 @@ function assertInjectedStorageRooted(label, memoryDir, injected) {
|
|
|
195
204
|
|
|
196
205
|
// src/storage.ts
|
|
197
206
|
import {
|
|
198
|
-
lstat as
|
|
199
|
-
readdir as
|
|
200
|
-
readFile,
|
|
201
|
-
realpath as
|
|
207
|
+
lstat as lstat3,
|
|
208
|
+
readdir as readdir5,
|
|
209
|
+
readFile as readFile2,
|
|
210
|
+
realpath as realpath3,
|
|
202
211
|
stat as stat3,
|
|
203
|
-
writeFile,
|
|
212
|
+
writeFile as writeFile2,
|
|
204
213
|
mkdir as mkdir2,
|
|
205
|
-
unlink as
|
|
214
|
+
unlink as unlink3,
|
|
206
215
|
appendFile,
|
|
207
216
|
open
|
|
208
217
|
} from "fs/promises";
|
|
209
|
-
import { appendFileSync, createReadStream, mkdirSync, readFileSync, statSync } from "fs";
|
|
218
|
+
import { appendFileSync, createReadStream, mkdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
210
219
|
import { createHash as createHash5 } from "crypto";
|
|
211
|
-
import
|
|
220
|
+
import path10 from "path";
|
|
212
221
|
|
|
213
222
|
// src/storage/memory-read-store.ts
|
|
214
223
|
import { lstat, readdir, realpath, stat } from "fs/promises";
|
|
215
224
|
import path2 from "path";
|
|
225
|
+
|
|
226
|
+
// src/storage/memory-frontmatter-metadata.ts
|
|
227
|
+
var rawFrontmatterByMemory = /* @__PURE__ */ new WeakMap();
|
|
228
|
+
function extractRawFrontmatter(raw) {
|
|
229
|
+
return raw.match(/^---\n([\s\S]*?)\n---\n?/)?.[1] ?? null;
|
|
230
|
+
}
|
|
231
|
+
function rememberRawFrontmatter(owner, raw) {
|
|
232
|
+
const frontmatter = extractRawFrontmatter(raw);
|
|
233
|
+
if (frontmatter !== null) rawFrontmatterByMemory.set(owner, frontmatter);
|
|
234
|
+
return owner;
|
|
235
|
+
}
|
|
236
|
+
function readRawFrontmatter(owner) {
|
|
237
|
+
return rawFrontmatterByMemory.get(owner);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/storage/memory-read-store.ts
|
|
216
241
|
function isPropagatableReadError(err) {
|
|
217
242
|
const code = err.code;
|
|
218
243
|
return typeof code === "string" && code !== "ENOENT";
|
|
@@ -491,7 +516,7 @@ var MemoryReadStore = class {
|
|
|
491
516
|
const raw = await readMaybeEncryptedFile(filePath, this.deps._secureStoreKey, this.deps.baseDir);
|
|
492
517
|
const parsed = parseFrontmatter(raw);
|
|
493
518
|
if (parsed) {
|
|
494
|
-
return {
|
|
519
|
+
return rememberRawFrontmatter({
|
|
495
520
|
path: filePath,
|
|
496
521
|
frontmatter: normalizeFrontmatterForPath(
|
|
497
522
|
parsed.frontmatter,
|
|
@@ -499,7 +524,7 @@ var MemoryReadStore = class {
|
|
|
499
524
|
parsed.content
|
|
500
525
|
),
|
|
501
526
|
content: parsed.content
|
|
502
|
-
};
|
|
527
|
+
}, raw);
|
|
503
528
|
}
|
|
504
529
|
const normalizedPath = filePath.split(path2.sep).join("/");
|
|
505
530
|
if (normalizedPath.includes("/entities/") && filePath.endsWith(".md")) {
|
|
@@ -714,10 +739,10 @@ var MARKDOWN_LIST_ITEM_CAN_INTERRUPT = /^(?:[-+*]|1[.)])[ \t]+/;
|
|
|
714
739
|
var MARKDOWN_THEMATIC_BREAK = /^(?:(?:\*[ \t]*){3,}|(?:-[ \t]*){3,}|(?:_[ \t]*){3,})$/;
|
|
715
740
|
var MARKDOWN_SETEXT_UNDERLINE = /^(?:=+|-+)$/;
|
|
716
741
|
var MARKDOWN_BLOCK_QUOTE = /^>/;
|
|
717
|
-
var MARKDOWN_LINK_REFERENCE = /^\[(?:\\.|[^\\\[\]])+\]
|
|
718
|
-
var MARKDOWN_LINK_REFERENCE_LABEL = /^\[(?:\\.|[^\\\[\]])+\]
|
|
742
|
+
var MARKDOWN_LINK_REFERENCE = /^\[(?:\\.|[^\\\[\]])+\]:[ \t]*(?:<[^>\n]*>|([^\s]+))(?:[ \t]+(?:"[^"\n]*"|'[^'\n]*'|\([^)\n]*\)))?$/;
|
|
743
|
+
var MARKDOWN_LINK_REFERENCE_LABEL = /^\[(?:\\.|[^\\\[\]])+\]:[ \t]*$/;
|
|
719
744
|
var MARKDOWN_LINK_REFERENCE_CONTINUATION = /^ {1,3}(?:"[^"\n]*"|'[^'\n]*'|\([^)\n]*\))$/;
|
|
720
|
-
var MARKDOWN_LINK_REFERENCE_DESTINATION_CONTINUATION = /^ {1,3}(?:<[^>\n]*>|(?!["'(])[^\s]+(
|
|
745
|
+
var MARKDOWN_LINK_REFERENCE_DESTINATION_CONTINUATION = /^ {1,3}(?:<[^>\n]*>|(?!["'(])[^\s]+(?:[ \t]+(?:"[^"\n]*"|'[^'\n]*'|\([^)\n]*\)))?)$/;
|
|
721
746
|
var UTF8_BOM = "\uFEFF";
|
|
722
747
|
function isLastUpdatedHeader(line) {
|
|
723
748
|
const withoutBom = line.startsWith(UTF8_BOM) ? line.slice(1) : line;
|
|
@@ -1036,7 +1061,7 @@ function shouldCloseHtmlBlock(line, endMarker) {
|
|
|
1036
1061
|
function canStartGenericHtmlBlock(lines, index, previousHtmlTerminator) {
|
|
1037
1062
|
const previousLine = lines[index - 1]?.content.trim() ?? "";
|
|
1038
1063
|
if (previousLine === "") return true;
|
|
1039
|
-
return isMetadataBoundary(previousLine, previousHtmlTerminator) && !isLastUpdatedHeader(previousLine) && !MARKDOWN_LIST_ITEM.test(previousLine) && !MARKDOWN_BLOCK_QUOTE.test(previousLine);
|
|
1064
|
+
return isCompletedIndentedCodeBlock(lines, index) || isMetadataBoundary(previousLine, previousHtmlTerminator) && !isLastUpdatedHeader(previousLine) && !MARKDOWN_LIST_ITEM.test(previousLine) && !MARKDOWN_BLOCK_QUOTE.test(previousLine);
|
|
1040
1065
|
}
|
|
1041
1066
|
function isHtmlBlockBoundary(lines, index, htmlTerminatorIndexes) {
|
|
1042
1067
|
if (htmlTerminatorIndexes.has(index)) return true;
|
|
@@ -1392,8 +1417,748 @@ function selfDeps(self) {
|
|
|
1392
1417
|
}
|
|
1393
1418
|
|
|
1394
1419
|
// src/storage/entity-store.ts
|
|
1395
|
-
import { readdir as
|
|
1420
|
+
import { readdir as readdir3, unlink as unlink2 } from "fs/promises";
|
|
1421
|
+
import path4 from "path";
|
|
1422
|
+
|
|
1423
|
+
// src/storage/entity-canonical-id-migration.ts
|
|
1424
|
+
import { randomUUID } from "crypto";
|
|
1425
|
+
import {
|
|
1426
|
+
access,
|
|
1427
|
+
lstat as lstat2,
|
|
1428
|
+
readFile,
|
|
1429
|
+
readdir as readdir2,
|
|
1430
|
+
realpath as realpath2,
|
|
1431
|
+
rename,
|
|
1432
|
+
stat as statFile,
|
|
1433
|
+
unlink,
|
|
1434
|
+
writeFile
|
|
1435
|
+
} from "fs/promises";
|
|
1396
1436
|
import path3 from "path";
|
|
1437
|
+
import { readFileSync } from "fs";
|
|
1438
|
+
import { z } from "zod";
|
|
1439
|
+
var ENTITY_CANONICAL_ID_MIGRATION_VERSION = 1;
|
|
1440
|
+
var ENTITY_CANONICAL_ID_MIGRATION_FILE = "entity-canonical-id-migration-v1.json";
|
|
1441
|
+
var ENTITY_CANONICAL_ID_MIGRATION_LOCK_STALE_MS = 6e4;
|
|
1442
|
+
var ENTITY_CANONICAL_ID_MIGRATION_LOCK_MAX_WAIT_MS = 3e5;
|
|
1443
|
+
var TOMBSTONE_LOCK_STALE_MS = 3e4;
|
|
1444
|
+
var TOMBSTONE_LOCK_MAX_WAIT_MS = 5e3;
|
|
1445
|
+
var ENTITY_CANONICAL_ID_MUTATION_LOCK_STALE_MS = 6e4;
|
|
1446
|
+
var ENTITY_CANONICAL_ID_MUTATION_LOCK_MAX_WAIT_MS = 3e5;
|
|
1447
|
+
var MEMORY_REWRITE_MAX_PASSES = 3;
|
|
1448
|
+
var ENTITY_MAPPING_RESCAN_MAX_PASSES = 3;
|
|
1449
|
+
function loadHistoricalEntityCanonicalIds(stateDir) {
|
|
1450
|
+
const statePath2 = path3.join(stateDir, ENTITY_CANONICAL_ID_MIGRATION_FILE);
|
|
1451
|
+
try {
|
|
1452
|
+
const parsed = JSON.parse(readFileSync(statePath2, "utf-8"));
|
|
1453
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
1454
|
+
const mappings = parsed.mappings;
|
|
1455
|
+
if (!mappings || typeof mappings !== "object" || Array.isArray(mappings)) return {};
|
|
1456
|
+
const cleaned = {};
|
|
1457
|
+
for (const [legacyId, canonicalId] of Object.entries(mappings)) {
|
|
1458
|
+
if (legacyId.length > 0 && typeof canonicalId === "string" && canonicalId.length > 0) {
|
|
1459
|
+
cleaned[legacyId] = canonicalId;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return cleaned;
|
|
1463
|
+
} catch {
|
|
1464
|
+
return {};
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
function resolveHistoricalEntityCanonicalId(normalized, mappings) {
|
|
1468
|
+
let current = normalized;
|
|
1469
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1470
|
+
while (!seen.has(current)) {
|
|
1471
|
+
seen.add(current);
|
|
1472
|
+
const next = mappings[current];
|
|
1473
|
+
if (!next || next === current) break;
|
|
1474
|
+
current = next;
|
|
1475
|
+
}
|
|
1476
|
+
return current;
|
|
1477
|
+
}
|
|
1478
|
+
var EntityCanonicalIdMigrationStateSchema = z.object({
|
|
1479
|
+
version: z.literal(ENTITY_CANONICAL_ID_MIGRATION_VERSION),
|
|
1480
|
+
complete: z.boolean(),
|
|
1481
|
+
mappings: z.record(z.string().min(1))
|
|
1482
|
+
});
|
|
1483
|
+
async function fingerprintPath(filePath) {
|
|
1484
|
+
let fileStat;
|
|
1485
|
+
try {
|
|
1486
|
+
fileStat = await lstat2(filePath);
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
if (!isErrnoCode(error, "ENOENT")) throw error;
|
|
1489
|
+
}
|
|
1490
|
+
return fileStat ? `${fileStat.dev}:${fileStat.ino}:${fileStat.mtimeMs}:${fileStat.ctimeMs}:${fileStat.size}` : "missing";
|
|
1491
|
+
}
|
|
1492
|
+
async function getFingerprint(baseDir, entitiesDir, getCorpusScanVersion) {
|
|
1493
|
+
const [entityFingerprint, aliasFingerprint] = await Promise.all([
|
|
1494
|
+
fingerprintPath(entitiesDir),
|
|
1495
|
+
fingerprintPath(path3.join(baseDir, "config", "aliases.json"))
|
|
1496
|
+
]);
|
|
1497
|
+
return `${entityFingerprint}:${aliasFingerprint}:${getCorpusScanVersion()}`;
|
|
1498
|
+
}
|
|
1499
|
+
async function validateRoots(baseDir, entitiesDir, stateDir, archiveDir) {
|
|
1500
|
+
let baseStat;
|
|
1501
|
+
try {
|
|
1502
|
+
baseStat = await lstat2(baseDir);
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
if (isErrnoCode(error, "ENOENT")) return;
|
|
1505
|
+
throw error;
|
|
1506
|
+
}
|
|
1507
|
+
if (baseStat.isSymbolicLink() || !baseStat.isDirectory()) {
|
|
1508
|
+
throw new Error(`Refusing entity canonical-id migration through unsafe memory root: ${baseDir}.`);
|
|
1509
|
+
}
|
|
1510
|
+
const memoryRoot = await realpath2(baseDir);
|
|
1511
|
+
const roots = [
|
|
1512
|
+
entitiesDir,
|
|
1513
|
+
stateDir,
|
|
1514
|
+
...RECALL_FALLBACK_DIRS.map((directory) => path3.join(baseDir, directory)),
|
|
1515
|
+
path3.join(baseDir, "cold"),
|
|
1516
|
+
archiveDir
|
|
1517
|
+
];
|
|
1518
|
+
for (const root of roots) {
|
|
1519
|
+
let rootStat;
|
|
1520
|
+
try {
|
|
1521
|
+
rootStat = await lstat2(root);
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
if (isErrnoCode(error, "ENOENT")) continue;
|
|
1524
|
+
throw error;
|
|
1525
|
+
}
|
|
1526
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
1527
|
+
throw new Error(`Refusing entity canonical-id migration through unsafe memory root: ${root}.`);
|
|
1528
|
+
}
|
|
1529
|
+
assertPathInsideRoot(memoryRoot, await realpath2(root), root);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
function statePath(deps) {
|
|
1533
|
+
return path3.join(deps.stateDir, ENTITY_CANONICAL_ID_MIGRATION_FILE);
|
|
1534
|
+
}
|
|
1535
|
+
async function assertSafeMigrationStateFile(deps) {
|
|
1536
|
+
const migrationStatePath = statePath(deps);
|
|
1537
|
+
let fileStat;
|
|
1538
|
+
try {
|
|
1539
|
+
fileStat = await lstat2(migrationStatePath);
|
|
1540
|
+
} catch (error) {
|
|
1541
|
+
if (isErrnoCode(error, "ENOENT")) return;
|
|
1542
|
+
throw error;
|
|
1543
|
+
}
|
|
1544
|
+
if (fileStat.isSymbolicLink() || !fileStat.isFile()) {
|
|
1545
|
+
throw new Error(
|
|
1546
|
+
`Refusing entity canonical-id migration through unsafe migration state file: ${migrationStatePath}.`
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
const [stateRoot, stateFile] = await Promise.all([
|
|
1550
|
+
realpath2(deps.stateDir),
|
|
1551
|
+
realpath2(migrationStatePath)
|
|
1552
|
+
]);
|
|
1553
|
+
assertPathInsideRoot(stateRoot, stateFile, migrationStatePath);
|
|
1554
|
+
}
|
|
1555
|
+
async function assertSafeTombstoneFile(deps, tombstonePath) {
|
|
1556
|
+
let fileStat;
|
|
1557
|
+
try {
|
|
1558
|
+
fileStat = await lstat2(tombstonePath);
|
|
1559
|
+
} catch (error) {
|
|
1560
|
+
if (isErrnoCode(error, "ENOENT")) return false;
|
|
1561
|
+
throw error;
|
|
1562
|
+
}
|
|
1563
|
+
if (fileStat.isSymbolicLink() || !fileStat.isFile()) {
|
|
1564
|
+
throw new Error(
|
|
1565
|
+
`Refusing entity canonical-id migration through unsafe tombstone ledger: ${tombstonePath}.`
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
const [stateRoot, tombstoneFile] = await Promise.all([
|
|
1569
|
+
realpath2(deps.stateDir),
|
|
1570
|
+
realpath2(tombstonePath)
|
|
1571
|
+
]);
|
|
1572
|
+
assertPathInsideRoot(stateRoot, tombstoneFile, tombstonePath);
|
|
1573
|
+
return true;
|
|
1574
|
+
}
|
|
1575
|
+
function lockPath(deps) {
|
|
1576
|
+
return path3.join(deps.stateDir, "entity-canonical-id-migration.lock");
|
|
1577
|
+
}
|
|
1578
|
+
async function withEntityCanonicalMutationLock(stateDir, task) {
|
|
1579
|
+
return withHeldFileLock(
|
|
1580
|
+
path3.join(stateDir, "entity-canonical-id-mutation.lock"),
|
|
1581
|
+
{
|
|
1582
|
+
staleMs: ENTITY_CANONICAL_ID_MUTATION_LOCK_STALE_MS,
|
|
1583
|
+
maxWaitMs: ENTITY_CANONICAL_ID_MUTATION_LOCK_MAX_WAIT_MS
|
|
1584
|
+
},
|
|
1585
|
+
async (acquired, lock) => {
|
|
1586
|
+
if (!acquired) throw new Error("Timed out waiting for entity mutation lock.");
|
|
1587
|
+
return task(() => lock.refresh());
|
|
1588
|
+
}
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
async function readState(deps) {
|
|
1592
|
+
await assertSafeMigrationStateFile(deps);
|
|
1593
|
+
let raw;
|
|
1594
|
+
try {
|
|
1595
|
+
raw = await readFile(statePath(deps), "utf-8");
|
|
1596
|
+
} catch (error) {
|
|
1597
|
+
if (isErrnoCode(error, "ENOENT")) return null;
|
|
1598
|
+
throw error;
|
|
1599
|
+
}
|
|
1600
|
+
let parsed;
|
|
1601
|
+
try {
|
|
1602
|
+
parsed = JSON.parse(raw);
|
|
1603
|
+
} catch {
|
|
1604
|
+
throw new Error("Invalid entity canonical-id migration state.");
|
|
1605
|
+
}
|
|
1606
|
+
const result = EntityCanonicalIdMigrationStateSchema.safeParse(parsed);
|
|
1607
|
+
if (!result.success) throw new Error("Invalid entity canonical-id migration state.");
|
|
1608
|
+
return result.data;
|
|
1609
|
+
}
|
|
1610
|
+
async function writeState(deps, state) {
|
|
1611
|
+
const destination = statePath(deps);
|
|
1612
|
+
const temporary = `${destination}.${process.pid}.${randomUUID()}.tmp`;
|
|
1613
|
+
try {
|
|
1614
|
+
await writeFile(temporary, `${JSON.stringify(state, null, 2)}
|
|
1615
|
+
`, "utf-8");
|
|
1616
|
+
await rename(temporary, destination);
|
|
1617
|
+
} finally {
|
|
1618
|
+
await unlink(temporary).catch(() => void 0);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
async function fileExists(filePath) {
|
|
1622
|
+
try {
|
|
1623
|
+
await access(filePath);
|
|
1624
|
+
return true;
|
|
1625
|
+
} catch (error) {
|
|
1626
|
+
if (isErrnoCode(error, "ENOENT")) return false;
|
|
1627
|
+
throw error;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
async function sameFileIdentity(leftPath, rightPath) {
|
|
1631
|
+
try {
|
|
1632
|
+
const [left, right] = await Promise.all([statFile(leftPath), statFile(rightPath)]);
|
|
1633
|
+
return left.dev === right.dev && left.ino === right.ino;
|
|
1634
|
+
} catch (error) {
|
|
1635
|
+
if (isErrnoCode(error, "ENOENT")) return false;
|
|
1636
|
+
throw error;
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
function assertSafeEntityId(entityId) {
|
|
1640
|
+
if (/[\/\\\0]/.test(entityId)) {
|
|
1641
|
+
throw new Error(`Refusing entity canonical-id migration through unsafe entity id: ${entityId}.`);
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
async function assertNotSymlink(filePath, label) {
|
|
1645
|
+
let stat4;
|
|
1646
|
+
try {
|
|
1647
|
+
stat4 = await lstat2(filePath);
|
|
1648
|
+
} catch (error) {
|
|
1649
|
+
if (isErrnoCode(error, "ENOENT")) return;
|
|
1650
|
+
throw error;
|
|
1651
|
+
}
|
|
1652
|
+
if (stat4.isSymbolicLink()) {
|
|
1653
|
+
throw new Error(`Refusing entity canonical-id migration through symlink: ${label}.`);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
async function assertNotMemorySymlink(filePath) {
|
|
1657
|
+
let stat4;
|
|
1658
|
+
try {
|
|
1659
|
+
stat4 = await lstat2(filePath);
|
|
1660
|
+
} catch (error) {
|
|
1661
|
+
if (isErrnoCode(error, "ENOENT")) return;
|
|
1662
|
+
throw error;
|
|
1663
|
+
}
|
|
1664
|
+
if (stat4.isSymbolicLink()) {
|
|
1665
|
+
throw new Error(`Refusing entity canonical-id migration through symlinked memory: ${filePath}.`);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
async function assertEntityContentUnchanged(deps, filePath, expectedContent, legacyId) {
|
|
1669
|
+
const latestContent = await deps.readStorageSecureFile(filePath);
|
|
1670
|
+
if (latestContent !== expectedContent) {
|
|
1671
|
+
throw new Error(`Legacy entity id ${legacyId} changed while migration was running; retry migration.`);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
async function readSafeEntityEntries(entitiesDir) {
|
|
1675
|
+
const rootStat = await lstat2(entitiesDir);
|
|
1676
|
+
if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
|
|
1677
|
+
throw new Error("Refusing entity canonical-id migration through a symlinked or non-directory entities root.");
|
|
1678
|
+
}
|
|
1679
|
+
const entries = await readdir2(entitiesDir);
|
|
1680
|
+
for (const entry of entries) {
|
|
1681
|
+
await assertNotSymlink(path3.join(entitiesDir, entry), entry);
|
|
1682
|
+
}
|
|
1683
|
+
return entries;
|
|
1684
|
+
}
|
|
1685
|
+
async function discoverMappings(deps, knownMappings = {}) {
|
|
1686
|
+
const mappings = {};
|
|
1687
|
+
const canonicalOwners = /* @__PURE__ */ new Map();
|
|
1688
|
+
const entityEntries = await readSafeEntityEntries(deps.entitiesDir);
|
|
1689
|
+
for (const entry of entityEntries) {
|
|
1690
|
+
if (!entry.endsWith(".md")) continue;
|
|
1691
|
+
const legacyId = entry.slice(0, -".md".length);
|
|
1692
|
+
assertSafeEntityId(legacyId);
|
|
1693
|
+
const legacyPath = deps.resolveEntityFilePath(legacyId);
|
|
1694
|
+
if (legacyPath === null) continue;
|
|
1695
|
+
const entityContent = await deps.readStorageSecureFile(legacyPath);
|
|
1696
|
+
if (!/^#[ \t]+[^\r\n \t]/m.test(entityContent) || !/^\*\*Type:\*\*[ \t]*[^\r\n \t]/m.test(entityContent)) {
|
|
1697
|
+
throw new Error(`Cannot migrate malformed entity file ${entry}: missing name heading or type.`);
|
|
1698
|
+
}
|
|
1699
|
+
const entity = deps.parseEntityFile(entityContent);
|
|
1700
|
+
if (!entity.name.trim() || !entity.type.trim()) {
|
|
1701
|
+
throw new Error(`Cannot migrate malformed entity file ${entry}: missing name or type.`);
|
|
1702
|
+
}
|
|
1703
|
+
const canonicalId = deps.normalizeEntityName(entity.name, entity.type);
|
|
1704
|
+
assertSafeEntityId(canonicalId);
|
|
1705
|
+
if (legacyId === canonicalId) continue;
|
|
1706
|
+
const canonicalPath = deps.resolveEntityFilePath(canonicalId);
|
|
1707
|
+
if (canonicalPath === null) continue;
|
|
1708
|
+
const canonicalExists = await fileExists(canonicalPath);
|
|
1709
|
+
if (canonicalExists && await sameFileIdentity(legacyPath, canonicalPath) && knownMappings[legacyId] === canonicalId) continue;
|
|
1710
|
+
if (canonicalExists) {
|
|
1711
|
+
const canonicalContent2 = await deps.readStorageSecureFile(canonicalPath);
|
|
1712
|
+
if (entityContent !== canonicalContent2) {
|
|
1713
|
+
throw new Error(`Cannot migrate legacy entity id ${legacyId}: ${canonicalId} already exists.`);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
const previousOwner = canonicalOwners.get(canonicalId);
|
|
1717
|
+
if (previousOwner !== void 0 && previousOwner !== legacyId) {
|
|
1718
|
+
throw new Error(
|
|
1719
|
+
`Cannot migrate legacy entity ids ${previousOwner} and ${legacyId}: both normalize to ${canonicalId}.`
|
|
1720
|
+
);
|
|
1721
|
+
}
|
|
1722
|
+
canonicalOwners.set(canonicalId, legacyId);
|
|
1723
|
+
mappings[legacyId] = canonicalId;
|
|
1724
|
+
}
|
|
1725
|
+
return mappings;
|
|
1726
|
+
}
|
|
1727
|
+
async function rewriteReferences(deps, mappings, refreshLock) {
|
|
1728
|
+
let rewroteColdMemory = false;
|
|
1729
|
+
for (let pass = 0; pass < MEMORY_REWRITE_MAX_PASSES; pass += 1) {
|
|
1730
|
+
deps.invalidateAllMemoriesCache();
|
|
1731
|
+
deps.invalidateColdMemoriesCache();
|
|
1732
|
+
const [hotMemories, coldMemories, archivedMemories] = await Promise.all([
|
|
1733
|
+
deps.readAllMemories(),
|
|
1734
|
+
deps.readAllColdMemories(),
|
|
1735
|
+
deps.readArchivedMemories()
|
|
1736
|
+
]);
|
|
1737
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
1738
|
+
let rewroteAny = false;
|
|
1739
|
+
for (const memory of [...hotMemories, ...coldMemories, ...archivedMemories]) {
|
|
1740
|
+
const memoryPath = path3.resolve(memory.path);
|
|
1741
|
+
if (seenPaths.has(memoryPath)) continue;
|
|
1742
|
+
seenPaths.add(memoryPath);
|
|
1743
|
+
const previousEntityRef = memory.frontmatter.entityRef;
|
|
1744
|
+
const canonicalId = previousEntityRef ? mappings[previousEntityRef] : void 0;
|
|
1745
|
+
if (!previousEntityRef || !canonicalId) continue;
|
|
1746
|
+
await assertNotMemorySymlink(memory.path);
|
|
1747
|
+
const observedMemoryFingerprint = await fingerprintPath(memory.path);
|
|
1748
|
+
if (observedMemoryFingerprint === "missing") continue;
|
|
1749
|
+
if (!await refreshLock()) throw new Error("Lost entity canonical-id migration lock.");
|
|
1750
|
+
const latestMemory = await deps.readMemoryByPath(memory.path);
|
|
1751
|
+
if (!latestMemory) continue;
|
|
1752
|
+
const latestMemoryFingerprint = await fingerprintPath(latestMemory.path);
|
|
1753
|
+
if (latestMemoryFingerprint !== observedMemoryFingerprint) continue;
|
|
1754
|
+
const latestPreviousEntityRef = latestMemory.frontmatter.entityRef;
|
|
1755
|
+
const latestCanonicalId = latestPreviousEntityRef ? mappings[latestPreviousEntityRef] : void 0;
|
|
1756
|
+
if (!latestPreviousEntityRef || !latestCanonicalId) continue;
|
|
1757
|
+
const memoryEncrypted = await deps.isEncryptedStorageFile(latestMemory.path);
|
|
1758
|
+
if (await fingerprintPath(latestMemory.path) !== latestMemoryFingerprint) continue;
|
|
1759
|
+
await deps.snapshotBeforeWrite(latestMemory.path, "write");
|
|
1760
|
+
if (await fingerprintPath(latestMemory.path) !== latestMemoryFingerprint) continue;
|
|
1761
|
+
await deps.writeStorageSecureFile(
|
|
1762
|
+
latestMemory.path,
|
|
1763
|
+
deps.serializeMemoryWithEntityRef(latestMemory, latestCanonicalId),
|
|
1764
|
+
memoryEncrypted
|
|
1765
|
+
);
|
|
1766
|
+
await deps.rewriteProjectedMemoryEntityReference(
|
|
1767
|
+
latestMemory.frontmatter.id,
|
|
1768
|
+
latestPreviousEntityRef,
|
|
1769
|
+
latestCanonicalId
|
|
1770
|
+
);
|
|
1771
|
+
rewroteAny = true;
|
|
1772
|
+
rewroteColdMemory ||= latestMemory.path.includes(`${path3.sep}cold${path3.sep}`);
|
|
1773
|
+
}
|
|
1774
|
+
if (!rewroteAny) return { rewroteColdMemory };
|
|
1775
|
+
}
|
|
1776
|
+
throw new Error("Memory references changed while entity canonical-id migration was running; retry migration.");
|
|
1777
|
+
}
|
|
1778
|
+
function rewriteTombstoneLine(line, mappings, normalizedMappings) {
|
|
1779
|
+
let parsed;
|
|
1780
|
+
try {
|
|
1781
|
+
parsed = JSON.parse(line);
|
|
1782
|
+
} catch {
|
|
1783
|
+
return line;
|
|
1784
|
+
}
|
|
1785
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return line;
|
|
1786
|
+
const tombstone = parsed;
|
|
1787
|
+
let changed = false;
|
|
1788
|
+
if (typeof tombstone.entityRef === "string") {
|
|
1789
|
+
const canonicalId = mappings[tombstone.entityRef];
|
|
1790
|
+
if (canonicalId) {
|
|
1791
|
+
tombstone.entityRef = canonicalId;
|
|
1792
|
+
changed = true;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
if (typeof tombstone.supersessionKey === "string") {
|
|
1796
|
+
const separator = tombstone.supersessionKey.indexOf("::");
|
|
1797
|
+
if (separator < 0) return changed ? JSON.stringify(tombstone) : line;
|
|
1798
|
+
const normalizedLegacyId = normalizeSupersessionKey(tombstone.supersessionKey.slice(0, separator));
|
|
1799
|
+
const canonicalId = normalizedMappings.get(normalizedLegacyId);
|
|
1800
|
+
if (canonicalId) {
|
|
1801
|
+
const canonicalKey = computeSupersessionKey(canonicalId, tombstone.supersessionKey.slice(separator + 2));
|
|
1802
|
+
if (canonicalKey) {
|
|
1803
|
+
tombstone.supersessionKey = canonicalKey;
|
|
1804
|
+
changed = true;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return changed ? JSON.stringify(tombstone) : line;
|
|
1809
|
+
}
|
|
1810
|
+
async function rewriteTombstoneReferences(deps, mappings, refreshLock) {
|
|
1811
|
+
const tombstonePath = path3.join(deps.stateDir, "tombstones.jsonl");
|
|
1812
|
+
const normalizedMappings = /* @__PURE__ */ new Map();
|
|
1813
|
+
for (const [legacyId, canonicalId] of Object.entries(mappings)) {
|
|
1814
|
+
const normalizedLegacyId = normalizeSupersessionKey(legacyId);
|
|
1815
|
+
if (normalizedLegacyId && !normalizedMappings.has(normalizedLegacyId)) {
|
|
1816
|
+
normalizedMappings.set(normalizedLegacyId, canonicalId);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
await withHeldFileLock(
|
|
1820
|
+
path3.join(deps.stateDir, "tombstones.lock"),
|
|
1821
|
+
{
|
|
1822
|
+
staleMs: TOMBSTONE_LOCK_STALE_MS,
|
|
1823
|
+
maxWaitMs: TOMBSTONE_LOCK_MAX_WAIT_MS
|
|
1824
|
+
},
|
|
1825
|
+
async (acquired, tombstoneLock) => {
|
|
1826
|
+
if (!acquired) throw new Error("Timed out waiting for active tombstone migration.");
|
|
1827
|
+
if (!await assertSafeTombstoneFile(deps, tombstonePath)) return;
|
|
1828
|
+
const tombstoneEncrypted = await deps.isEncryptedStorageFile(tombstonePath);
|
|
1829
|
+
const original = await deps.readStorageSecureFile(tombstonePath);
|
|
1830
|
+
let changed = false;
|
|
1831
|
+
const rewritten = original.split("\n").map((line) => {
|
|
1832
|
+
const next = rewriteTombstoneLine(line, mappings, normalizedMappings);
|
|
1833
|
+
changed ||= next !== line;
|
|
1834
|
+
return next;
|
|
1835
|
+
}).join("\n");
|
|
1836
|
+
if (!changed) return;
|
|
1837
|
+
if (!await refreshLock()) throw new Error("Lost entity canonical-id migration lock.");
|
|
1838
|
+
if (!await tombstoneLock.refresh()) throw new Error("Lost tombstone migration lock.");
|
|
1839
|
+
await deps.snapshotBeforeWrite(tombstonePath, "write");
|
|
1840
|
+
await deps.writeStorageSecureFile(tombstonePath, rewritten, tombstoneEncrypted);
|
|
1841
|
+
}
|
|
1842
|
+
);
|
|
1843
|
+
}
|
|
1844
|
+
async function rewriteRelationshipTargets(deps, mappings, refreshLock) {
|
|
1845
|
+
let rewritten = false;
|
|
1846
|
+
const entries = await readSafeEntityEntries(deps.entitiesDir);
|
|
1847
|
+
for (const entry of entries) {
|
|
1848
|
+
if (!entry.endsWith(".md")) continue;
|
|
1849
|
+
const entityPath = deps.resolveEntityFilePath(entry.slice(0, -".md".length));
|
|
1850
|
+
if (entityPath === null) continue;
|
|
1851
|
+
const observedEntityFingerprint = await fingerprintPath(entityPath);
|
|
1852
|
+
if (observedEntityFingerprint === "missing") continue;
|
|
1853
|
+
const entity = deps.parseEntityFile(await deps.readStorageSecureFile(entityPath));
|
|
1854
|
+
const relationships = entity.relationships.map((relationship) => {
|
|
1855
|
+
const target = mappings[relationship.target];
|
|
1856
|
+
return target ? { ...relationship, target } : relationship;
|
|
1857
|
+
});
|
|
1858
|
+
if (relationships.every((relationship, index) => relationship === entity.relationships[index])) continue;
|
|
1859
|
+
const rewroteEntity = await withEntityCanonicalMutationLock(
|
|
1860
|
+
deps.stateDir,
|
|
1861
|
+
async (refreshEntityLock) => {
|
|
1862
|
+
if (!await refreshLock()) throw new Error("Lost entity canonical-id migration lock.");
|
|
1863
|
+
const latestEntity = deps.parseEntityFile(await deps.readStorageSecureFile(entityPath));
|
|
1864
|
+
if (await fingerprintPath(entityPath) !== observedEntityFingerprint) return false;
|
|
1865
|
+
const latestRelationships = latestEntity.relationships.map((relationship) => {
|
|
1866
|
+
const target = mappings[relationship.target];
|
|
1867
|
+
return target ? { ...relationship, target } : relationship;
|
|
1868
|
+
});
|
|
1869
|
+
if (latestRelationships.every((relationship, index) => relationship === latestEntity.relationships[index])) {
|
|
1870
|
+
return false;
|
|
1871
|
+
}
|
|
1872
|
+
const entityEncrypted = await deps.isEncryptedStorageFile(entityPath);
|
|
1873
|
+
if (!await refreshEntityLock()) throw new Error("Lost entity mutation lock.");
|
|
1874
|
+
if (await fingerprintPath(entityPath) !== observedEntityFingerprint) return false;
|
|
1875
|
+
await deps.snapshotBeforeWrite(entityPath, "write");
|
|
1876
|
+
if (await fingerprintPath(entityPath) !== observedEntityFingerprint) return false;
|
|
1877
|
+
await deps.writeStorageSecureFile(
|
|
1878
|
+
entityPath,
|
|
1879
|
+
deps.serializeEntityFile({ ...latestEntity, relationships: latestRelationships }),
|
|
1880
|
+
entityEncrypted
|
|
1881
|
+
);
|
|
1882
|
+
return true;
|
|
1883
|
+
}
|
|
1884
|
+
);
|
|
1885
|
+
rewritten ||= rewroteEntity;
|
|
1886
|
+
}
|
|
1887
|
+
return rewritten;
|
|
1888
|
+
}
|
|
1889
|
+
async function rewriteKnownReferences(deps, mappings, refreshLock) {
|
|
1890
|
+
await rewriteRelationshipTargets(deps, mappings, refreshLock);
|
|
1891
|
+
let rewroteColdMemory = (await rewriteReferences(deps, mappings, refreshLock)).rewroteColdMemory;
|
|
1892
|
+
await rewriteTombstoneReferences(deps, mappings, refreshLock);
|
|
1893
|
+
await deps.rewriteProjectedEntityReferences(mappings);
|
|
1894
|
+
const finalMemoryPass = await rewriteReferences(deps, mappings, refreshLock);
|
|
1895
|
+
rewroteColdMemory ||= finalMemoryPass.rewroteColdMemory;
|
|
1896
|
+
return rewroteColdMemory;
|
|
1897
|
+
}
|
|
1898
|
+
function wouldCreateMappingCycle(mappings, legacyId, canonicalId) {
|
|
1899
|
+
let current = canonicalId;
|
|
1900
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1901
|
+
while (true) {
|
|
1902
|
+
if (current === legacyId) return true;
|
|
1903
|
+
if (seen.has(current)) return false;
|
|
1904
|
+
seen.add(current);
|
|
1905
|
+
const next = mappings[current];
|
|
1906
|
+
if (!next || next === current) return false;
|
|
1907
|
+
current = next;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
function rejectMappingCycles(existing, discovered) {
|
|
1911
|
+
const accepted = {};
|
|
1912
|
+
const merged = { ...existing };
|
|
1913
|
+
for (const [legacyId, canonicalId] of Object.entries(discovered)) {
|
|
1914
|
+
if (merged[legacyId] === void 0 && wouldCreateMappingCycle(merged, legacyId, canonicalId)) continue;
|
|
1915
|
+
accepted[legacyId] = canonicalId;
|
|
1916
|
+
merged[legacyId] = canonicalId;
|
|
1917
|
+
}
|
|
1918
|
+
return accepted;
|
|
1919
|
+
}
|
|
1920
|
+
function mergeDiscoveredMappings(existing, discovered) {
|
|
1921
|
+
const merged = { ...existing };
|
|
1922
|
+
const canonicalOwners = /* @__PURE__ */ new Map();
|
|
1923
|
+
for (const [legacyId, canonicalId] of Object.entries(discovered)) {
|
|
1924
|
+
const previousMapping = merged[legacyId];
|
|
1925
|
+
if (previousMapping !== void 0 && previousMapping !== canonicalId) {
|
|
1926
|
+
throw new Error(`Legacy entity id ${legacyId} changed canonical target during migration.`);
|
|
1927
|
+
}
|
|
1928
|
+
if (previousMapping === void 0 && wouldCreateMappingCycle(merged, legacyId, canonicalId)) continue;
|
|
1929
|
+
const previousOwner = canonicalOwners.get(canonicalId);
|
|
1930
|
+
if (previousOwner !== void 0 && previousOwner !== legacyId) {
|
|
1931
|
+
throw new Error(
|
|
1932
|
+
`Cannot migrate legacy entity ids ${previousOwner} and ${legacyId}: both normalize to ${canonicalId}.`
|
|
1933
|
+
);
|
|
1934
|
+
}
|
|
1935
|
+
merged[legacyId] = canonicalId;
|
|
1936
|
+
canonicalOwners.set(canonicalId, legacyId);
|
|
1937
|
+
}
|
|
1938
|
+
return merged;
|
|
1939
|
+
}
|
|
1940
|
+
function collapseMappings(mappings) {
|
|
1941
|
+
const collapsed = {};
|
|
1942
|
+
for (const legacyId of Object.keys(mappings)) {
|
|
1943
|
+
let current = legacyId;
|
|
1944
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1945
|
+
while (true) {
|
|
1946
|
+
const next = mappings[current];
|
|
1947
|
+
if (!next || next === current) break;
|
|
1948
|
+
if (seen.has(current)) {
|
|
1949
|
+
throw new Error(`Entity canonical-id migration mapping cycle includes ${legacyId}.`);
|
|
1950
|
+
}
|
|
1951
|
+
seen.add(current);
|
|
1952
|
+
current = next;
|
|
1953
|
+
}
|
|
1954
|
+
if (current !== legacyId) collapsed[legacyId] = current;
|
|
1955
|
+
}
|
|
1956
|
+
return collapsed;
|
|
1957
|
+
}
|
|
1958
|
+
async function migrateEntityFilePair(deps, legacyId, canonicalId, refreshMigrationLock) {
|
|
1959
|
+
return withEntityCanonicalMutationLock(deps.stateDir, async (refreshEntityLock) => {
|
|
1960
|
+
assertSafeEntityId(legacyId);
|
|
1961
|
+
assertSafeEntityId(canonicalId);
|
|
1962
|
+
const legacyPath = deps.resolveEntityFilePath(legacyId);
|
|
1963
|
+
const canonicalPath = deps.resolveEntityFilePath(canonicalId);
|
|
1964
|
+
if (legacyPath === null || canonicalPath === null) {
|
|
1965
|
+
throw new Error("Invalid entity canonical-id migration path.");
|
|
1966
|
+
}
|
|
1967
|
+
await assertNotSymlink(legacyPath, legacyId);
|
|
1968
|
+
await assertNotSymlink(canonicalPath, canonicalId);
|
|
1969
|
+
const legacyExists = await fileExists(legacyPath);
|
|
1970
|
+
const canonicalExists = await fileExists(canonicalPath);
|
|
1971
|
+
if (!legacyExists && !canonicalExists) return { deferred: true, progressed: false };
|
|
1972
|
+
if (legacyExists && canonicalExists) {
|
|
1973
|
+
if (await sameFileIdentity(legacyPath, canonicalPath)) return { deferred: false, progressed: false };
|
|
1974
|
+
const [legacyContent, canonicalContent2, legacyEncrypted, canonicalEncrypted] = await Promise.all([
|
|
1975
|
+
deps.readStorageSecureFile(legacyPath),
|
|
1976
|
+
deps.readStorageSecureFile(canonicalPath),
|
|
1977
|
+
deps.isEncryptedStorageFile(legacyPath),
|
|
1978
|
+
deps.isEncryptedStorageFile(canonicalPath)
|
|
1979
|
+
]);
|
|
1980
|
+
if (legacyContent !== canonicalContent2) {
|
|
1981
|
+
throw new Error(`Cannot migrate legacy entity id ${legacyId}: ${canonicalId} already exists.`);
|
|
1982
|
+
}
|
|
1983
|
+
if (legacyEncrypted && !canonicalEncrypted) {
|
|
1984
|
+
if (!await refreshMigrationLock() || !await refreshEntityLock()) {
|
|
1985
|
+
throw new Error("Lost entity canonical-id migration lock.");
|
|
1986
|
+
}
|
|
1987
|
+
await deps.snapshotBeforeWrite(canonicalPath, "write");
|
|
1988
|
+
await deps.writeStorageSecureFile(canonicalPath, legacyContent, true);
|
|
1989
|
+
if (await deps.readStorageSecureFile(canonicalPath) !== legacyContent) {
|
|
1990
|
+
throw new Error(`Cannot verify migrated entity id ${canonicalId}.`);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
if (!await refreshMigrationLock() || !await refreshEntityLock()) {
|
|
1994
|
+
throw new Error("Lost entity canonical-id migration lock.");
|
|
1995
|
+
}
|
|
1996
|
+
await deps.snapshotBeforeWrite(legacyPath, "write");
|
|
1997
|
+
await assertNotSymlink(legacyPath, legacyId);
|
|
1998
|
+
await assertEntityContentUnchanged(deps, legacyPath, legacyContent, legacyId);
|
|
1999
|
+
await unlink(legacyPath);
|
|
2000
|
+
return { deferred: false, progressed: true };
|
|
2001
|
+
}
|
|
2002
|
+
if (!legacyExists) return { deferred: false, progressed: false };
|
|
2003
|
+
if (!await refreshMigrationLock() || !await refreshEntityLock()) {
|
|
2004
|
+
throw new Error("Lost entity canonical-id migration lock.");
|
|
2005
|
+
}
|
|
2006
|
+
const entityEncrypted = await deps.isEncryptedStorageFile(legacyPath);
|
|
2007
|
+
const entityContent = await deps.readStorageSecureFile(legacyPath);
|
|
2008
|
+
await assertNotSymlink(legacyPath, legacyId);
|
|
2009
|
+
await assertEntityContentUnchanged(deps, legacyPath, entityContent, legacyId);
|
|
2010
|
+
let canonicalFingerprintAfterWrite = null;
|
|
2011
|
+
try {
|
|
2012
|
+
await deps.writeStorageSecureFile(canonicalPath, entityContent, entityEncrypted);
|
|
2013
|
+
canonicalFingerprintAfterWrite = await fingerprintPath(canonicalPath);
|
|
2014
|
+
if (await deps.readStorageSecureFile(canonicalPath) !== entityContent) {
|
|
2015
|
+
throw new Error(`Cannot verify migrated entity id ${canonicalId}.`);
|
|
2016
|
+
}
|
|
2017
|
+
if (!await refreshMigrationLock() || !await refreshEntityLock()) {
|
|
2018
|
+
throw new Error("Lost entity canonical-id migration lock.");
|
|
2019
|
+
}
|
|
2020
|
+
await deps.snapshotBeforeWrite(legacyPath, "write");
|
|
2021
|
+
await assertNotSymlink(legacyPath, legacyId);
|
|
2022
|
+
await assertEntityContentUnchanged(deps, legacyPath, entityContent, legacyId);
|
|
2023
|
+
await unlink(legacyPath);
|
|
2024
|
+
} catch (error) {
|
|
2025
|
+
if (!canonicalExists && canonicalFingerprintAfterWrite !== null) {
|
|
2026
|
+
try {
|
|
2027
|
+
if (await fingerprintPath(canonicalPath) === canonicalFingerprintAfterWrite) {
|
|
2028
|
+
await assertNotSymlink(canonicalPath, canonicalId);
|
|
2029
|
+
await unlink(canonicalPath);
|
|
2030
|
+
}
|
|
2031
|
+
} catch {
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
throw error;
|
|
2035
|
+
}
|
|
2036
|
+
return { deferred: false, progressed: true };
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
async function migrateLegacyEntityCanonicalIds(deps) {
|
|
2040
|
+
return withHeldFileLock(
|
|
2041
|
+
lockPath(deps),
|
|
2042
|
+
{
|
|
2043
|
+
staleMs: ENTITY_CANONICAL_ID_MIGRATION_LOCK_STALE_MS,
|
|
2044
|
+
maxWaitMs: ENTITY_CANONICAL_ID_MIGRATION_LOCK_MAX_WAIT_MS
|
|
2045
|
+
},
|
|
2046
|
+
async (acquired, lock) => {
|
|
2047
|
+
if (!acquired) throw new Error("Timed out waiting for active entity canonical-id migration.");
|
|
2048
|
+
await deps.validateMemoryScanRoots?.();
|
|
2049
|
+
await readSafeEntityEntries(deps.entitiesDir);
|
|
2050
|
+
let state = await readState(deps);
|
|
2051
|
+
if (!state) {
|
|
2052
|
+
state = {
|
|
2053
|
+
version: ENTITY_CANONICAL_ID_MIGRATION_VERSION,
|
|
2054
|
+
complete: false,
|
|
2055
|
+
mappings: {}
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
const previousMappings = state.mappings;
|
|
2059
|
+
const collapsedMappings = collapseMappings(previousMappings);
|
|
2060
|
+
const mappingsChanged = Object.keys(collapsedMappings).length !== Object.keys(previousMappings).length || Object.entries(collapsedMappings).some(
|
|
2061
|
+
([legacyId, canonicalId]) => previousMappings[legacyId] !== canonicalId
|
|
2062
|
+
);
|
|
2063
|
+
state = { ...state, mappings: collapsedMappings };
|
|
2064
|
+
for (let migrationPass = 0; migrationPass < ENTITY_MAPPING_RESCAN_MAX_PASSES; migrationPass += 1) {
|
|
2065
|
+
if (!await lock.refresh()) throw new Error("Lost entity canonical-id migration lock.");
|
|
2066
|
+
const discoveredBefore = rejectMappingCycles(
|
|
2067
|
+
state.mappings,
|
|
2068
|
+
await discoverMappings(deps, state.mappings)
|
|
2069
|
+
);
|
|
2070
|
+
const mergedBefore = collapseMappings(mergeDiscoveredMappings(state.mappings, discoveredBefore));
|
|
2071
|
+
if (state.complete && Object.keys(discoveredBefore).length === 0 && !mappingsChanged) {
|
|
2072
|
+
if (Object.keys(state.mappings).length === 0) return deps.readMigrationFingerprint?.();
|
|
2073
|
+
const rewroteColdMemory2 = await rewriteKnownReferences(
|
|
2074
|
+
deps,
|
|
2075
|
+
state.mappings,
|
|
2076
|
+
() => lock.refresh()
|
|
2077
|
+
);
|
|
2078
|
+
deps.invalidateKnowledgeIndexCache();
|
|
2079
|
+
deps.invalidateAllMemoriesCache();
|
|
2080
|
+
if (rewroteColdMemory2) deps.invalidateColdMemoriesCache();
|
|
2081
|
+
deps.bumpMemoryStatusVersion();
|
|
2082
|
+
return deps.readMigrationFingerprint?.();
|
|
2083
|
+
}
|
|
2084
|
+
if (state.complete || Object.keys(discoveredBefore).length > 0 || Object.keys(mergedBefore).length !== Object.keys(state.mappings).length) {
|
|
2085
|
+
state = { ...state, complete: false, mappings: mergedBefore };
|
|
2086
|
+
await writeState(deps, state);
|
|
2087
|
+
}
|
|
2088
|
+
if (Object.keys(state.mappings).length === 0) {
|
|
2089
|
+
if (!await lock.refresh()) throw new Error("Lost entity canonical-id migration lock.");
|
|
2090
|
+
const discoveredAfter2 = rejectMappingCycles(
|
|
2091
|
+
state.mappings,
|
|
2092
|
+
await discoverMappings(deps, state.mappings)
|
|
2093
|
+
);
|
|
2094
|
+
if (Object.keys(discoveredAfter2).length === 0) {
|
|
2095
|
+
await writeState(deps, { ...state, complete: true });
|
|
2096
|
+
return deps.readMigrationFingerprint?.();
|
|
2097
|
+
}
|
|
2098
|
+
state = {
|
|
2099
|
+
...state,
|
|
2100
|
+
complete: false,
|
|
2101
|
+
mappings: collapseMappings(mergeDiscoveredMappings(state.mappings, discoveredAfter2))
|
|
2102
|
+
};
|
|
2103
|
+
await writeState(deps, state);
|
|
2104
|
+
continue;
|
|
2105
|
+
}
|
|
2106
|
+
let pendingMappings = Object.entries(state.mappings);
|
|
2107
|
+
while (pendingMappings.length > 0) {
|
|
2108
|
+
const deferredMappings = [];
|
|
2109
|
+
let progressed = false;
|
|
2110
|
+
for (const [legacyId, canonicalId] of pendingMappings) {
|
|
2111
|
+
const result = await migrateEntityFilePair(
|
|
2112
|
+
deps,
|
|
2113
|
+
legacyId,
|
|
2114
|
+
canonicalId,
|
|
2115
|
+
() => lock.refresh()
|
|
2116
|
+
);
|
|
2117
|
+
if (result.deferred) {
|
|
2118
|
+
deferredMappings.push([legacyId, canonicalId]);
|
|
2119
|
+
}
|
|
2120
|
+
progressed ||= result.progressed;
|
|
2121
|
+
}
|
|
2122
|
+
if (deferredMappings.length === 0) break;
|
|
2123
|
+
if (!progressed) {
|
|
2124
|
+
const [legacyId, canonicalId] = deferredMappings[0];
|
|
2125
|
+
throw new Error(
|
|
2126
|
+
`Cannot migrate legacy entity id ${legacyId}: both files are missing and no intermediate mapping can advance migration.`
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
pendingMappings = deferredMappings;
|
|
2130
|
+
}
|
|
2131
|
+
const rewroteColdMemory = await rewriteKnownReferences(
|
|
2132
|
+
deps,
|
|
2133
|
+
state.mappings,
|
|
2134
|
+
() => lock.refresh()
|
|
2135
|
+
);
|
|
2136
|
+
deps.invalidateKnowledgeIndexCache();
|
|
2137
|
+
deps.invalidateAllMemoriesCache();
|
|
2138
|
+
if (rewroteColdMemory) deps.invalidateColdMemoriesCache();
|
|
2139
|
+
deps.bumpMemoryStatusVersion();
|
|
2140
|
+
if (!await lock.refresh()) throw new Error("Lost entity canonical-id migration lock.");
|
|
2141
|
+
const discoveredAfter = rejectMappingCycles(
|
|
2142
|
+
state.mappings,
|
|
2143
|
+
await discoverMappings(deps, state.mappings)
|
|
2144
|
+
);
|
|
2145
|
+
if (Object.keys(discoveredAfter).length === 0) {
|
|
2146
|
+
await writeState(deps, { ...state, complete: true });
|
|
2147
|
+
return deps.readMigrationFingerprint?.();
|
|
2148
|
+
}
|
|
2149
|
+
state = {
|
|
2150
|
+
...state,
|
|
2151
|
+
complete: false,
|
|
2152
|
+
mappings: collapseMappings(mergeDiscoveredMappings(state.mappings, discoveredAfter))
|
|
2153
|
+
};
|
|
2154
|
+
await writeState(deps, state);
|
|
2155
|
+
}
|
|
2156
|
+
throw new Error("Entity canonical-id migration mappings changed while migration was running; retry migration.");
|
|
2157
|
+
}
|
|
2158
|
+
);
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// src/storage/entity-store.ts
|
|
1397
2162
|
var EntityStore = class {
|
|
1398
2163
|
constructor(deps) {
|
|
1399
2164
|
this.deps = deps;
|
|
@@ -1405,6 +2170,12 @@ var EntityStore = class {
|
|
|
1405
2170
|
}
|
|
1406
2171
|
async writeEntity(name, type, facts, options = {}) {
|
|
1407
2172
|
await this.deps.ensureDirectories();
|
|
2173
|
+
return withEntityCanonicalMutationLock(
|
|
2174
|
+
path4.join(this.deps.baseDir, "state"),
|
|
2175
|
+
() => this.writeEntityUnlocked(name, type, facts, options)
|
|
2176
|
+
);
|
|
2177
|
+
}
|
|
2178
|
+
async writeEntityUnlocked(name, type, facts, options = {}) {
|
|
1408
2179
|
if (typeof name !== "string" || !name.trim() || typeof type !== "string" || !type.trim()) {
|
|
1409
2180
|
log.warn("writeEntity: invalid entity payload, skipping", {
|
|
1410
2181
|
nameType: typeof name,
|
|
@@ -1421,7 +2192,7 @@ var EntityStore = class {
|
|
|
1421
2192
|
log.debug(`fuzzy match: "${normalized}" \u2192 existing "${match}"`);
|
|
1422
2193
|
normalized = match;
|
|
1423
2194
|
}
|
|
1424
|
-
const filePath =
|
|
2195
|
+
const filePath = path4.join(this.deps.entitiesDir, `${normalized}.md`);
|
|
1425
2196
|
let entity = {
|
|
1426
2197
|
name,
|
|
1427
2198
|
type,
|
|
@@ -1522,7 +2293,7 @@ var EntityStore = class {
|
|
|
1522
2293
|
/** Return sorted list of entity filenames (without .md extension) */
|
|
1523
2294
|
async listEntityNames() {
|
|
1524
2295
|
try {
|
|
1525
|
-
const entries = await
|
|
2296
|
+
const entries = await readdir3(this.deps.entitiesDir);
|
|
1526
2297
|
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", "")).sort();
|
|
1527
2298
|
} catch {
|
|
1528
2299
|
return [];
|
|
@@ -1532,7 +2303,13 @@ var EntityStore = class {
|
|
|
1532
2303
|
* Set or rewrite the synthesis layer of an entity file.
|
|
1533
2304
|
*/
|
|
1534
2305
|
async updateEntitySynthesis(name, synthesis, options = {}) {
|
|
1535
|
-
|
|
2306
|
+
return withEntityCanonicalMutationLock(
|
|
2307
|
+
path4.join(this.deps.baseDir, "state"),
|
|
2308
|
+
() => this.updateEntitySynthesisUnlocked(name, synthesis, options)
|
|
2309
|
+
);
|
|
2310
|
+
}
|
|
2311
|
+
async updateEntitySynthesisUnlocked(name, synthesis, options = {}) {
|
|
2312
|
+
const filePath = path4.join(this.deps.entitiesDir, `${name}.md`);
|
|
1536
2313
|
let entity;
|
|
1537
2314
|
try {
|
|
1538
2315
|
const content = await this.deps.readStorageSecureFile(filePath);
|
|
@@ -1574,7 +2351,7 @@ var EntityStore = class {
|
|
|
1574
2351
|
const cached = getCachedEntities(this.deps.baseDir, currentVersion, cacheKey);
|
|
1575
2352
|
if (cached) return cached;
|
|
1576
2353
|
try {
|
|
1577
|
-
const entries = await
|
|
2354
|
+
const entries = await readdir3(this.deps.entitiesDir);
|
|
1578
2355
|
const mdFiles = entries.filter((e) => e.endsWith(".md"));
|
|
1579
2356
|
if (mdFiles.length === 0) return [];
|
|
1580
2357
|
const BATCH_SIZE = 100;
|
|
@@ -1584,7 +2361,7 @@ var EntityStore = class {
|
|
|
1584
2361
|
const results = await Promise.all(
|
|
1585
2362
|
batch.map(async (entry) => {
|
|
1586
2363
|
try {
|
|
1587
|
-
return await this.deps.readStorageSecureFile(
|
|
2364
|
+
return await this.deps.readStorageSecureFile(path4.join(this.deps.entitiesDir, entry));
|
|
1588
2365
|
} catch (err) {
|
|
1589
2366
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
1590
2367
|
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
@@ -1657,9 +2434,15 @@ ${rows.join("\n")}
|
|
|
1657
2434
|
* Returns count of files merged.
|
|
1658
2435
|
*/
|
|
1659
2436
|
async mergeFragmentedEntities() {
|
|
2437
|
+
return withEntityCanonicalMutationLock(
|
|
2438
|
+
path4.join(this.deps.baseDir, "state"),
|
|
2439
|
+
() => this.mergeFragmentedEntitiesUnlocked()
|
|
2440
|
+
);
|
|
2441
|
+
}
|
|
2442
|
+
async mergeFragmentedEntitiesUnlocked() {
|
|
1660
2443
|
let merged = 0;
|
|
1661
2444
|
try {
|
|
1662
|
-
const entries = await
|
|
2445
|
+
const entries = await readdir3(this.deps.entitiesDir);
|
|
1663
2446
|
const mdFiles = entries.filter((e) => e.endsWith(".md"));
|
|
1664
2447
|
const groups = /* @__PURE__ */ new Map();
|
|
1665
2448
|
for (const file of mdFiles) {
|
|
@@ -1697,7 +2480,7 @@ ${rows.join("\n")}
|
|
|
1697
2480
|
extraSections: []
|
|
1698
2481
|
};
|
|
1699
2482
|
for (const file of files) {
|
|
1700
|
-
const filePath =
|
|
2483
|
+
const filePath = path4.join(this.deps.entitiesDir, file);
|
|
1701
2484
|
try {
|
|
1702
2485
|
const content = await this.deps.readStorageSecureFile(filePath);
|
|
1703
2486
|
const parsed = parseEntityFile(content, this.deps.entitySchemas);
|
|
@@ -1828,13 +2611,13 @@ ${rows.join("\n")}
|
|
|
1828
2611
|
}
|
|
1829
2612
|
mergedEntity.created = mergedEntity.created || mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
1830
2613
|
mergedEntity.updated = mergedEntity.updated || (/* @__PURE__ */ new Date()).toISOString();
|
|
1831
|
-
const canonicalPath =
|
|
2614
|
+
const canonicalPath = path4.join(this.deps.entitiesDir, `${canonical}.md`);
|
|
1832
2615
|
await this.deps.writeStorageSecureFile(canonicalPath, serializeEntityFile(mergedEntity, this.deps.entitySchemas));
|
|
1833
2616
|
for (const file of files) {
|
|
1834
|
-
const filePath =
|
|
2617
|
+
const filePath = path4.join(this.deps.entitiesDir, file);
|
|
1835
2618
|
if (filePath !== canonicalPath) {
|
|
1836
2619
|
try {
|
|
1837
|
-
await
|
|
2620
|
+
await unlink2(filePath);
|
|
1838
2621
|
merged++;
|
|
1839
2622
|
log.debug(`merged entity ${file} \u2192 ${canonical}.md`);
|
|
1840
2623
|
} catch {
|
|
@@ -1850,8 +2633,8 @@ ${rows.join("\n")}
|
|
|
1850
2633
|
};
|
|
1851
2634
|
|
|
1852
2635
|
// src/storage/identity-continuity-store.ts
|
|
1853
|
-
import { readdir as
|
|
1854
|
-
import
|
|
2636
|
+
import { readdir as readdir4 } from "fs/promises";
|
|
2637
|
+
import path5 from "path";
|
|
1855
2638
|
var IdentityContinuityStore = class {
|
|
1856
2639
|
constructor(deps) {
|
|
1857
2640
|
this.deps = deps;
|
|
@@ -1876,7 +2659,7 @@ var IdentityContinuityStore = class {
|
|
|
1876
2659
|
const date = nowIso.slice(0, 10);
|
|
1877
2660
|
const id = this.deps.generateId("incident");
|
|
1878
2661
|
const incident = createContinuityIncidentRecord(id, input, nowIso);
|
|
1879
|
-
const filePath =
|
|
2662
|
+
const filePath = path5.join(this.deps.identityIncidentsDir, `${date}-${id}.md`);
|
|
1880
2663
|
await this.deps.writeStorageSecureFile(filePath, serializeContinuityIncident(incident));
|
|
1881
2664
|
return { ...incident, filePath };
|
|
1882
2665
|
}
|
|
@@ -1889,7 +2672,7 @@ var IdentityContinuityStore = class {
|
|
|
1889
2672
|
const incidents = [];
|
|
1890
2673
|
for (const file of candidates) {
|
|
1891
2674
|
if (incidents.length >= cappedLimit) break;
|
|
1892
|
-
const filePath =
|
|
2675
|
+
const filePath = path5.join(this.deps.identityIncidentsDir, file);
|
|
1893
2676
|
try {
|
|
1894
2677
|
const raw = await this.deps.readStorageSecureFile(filePath);
|
|
1895
2678
|
const parsed = parseContinuityIncident(raw);
|
|
@@ -1920,7 +2703,7 @@ var IdentityContinuityStore = class {
|
|
|
1920
2703
|
await this.deps.ensureDirectories();
|
|
1921
2704
|
const safeKey = this.sanitizeIdentityAuditKey(key);
|
|
1922
2705
|
const dir = period === "weekly" ? this.deps.identityAuditsWeeklyDir : this.deps.identityAuditsMonthlyDir;
|
|
1923
|
-
const filePath =
|
|
2706
|
+
const filePath = path5.join(dir, `${safeKey}.md`);
|
|
1924
2707
|
await this.deps.writeStorageSecureFile(filePath, content);
|
|
1925
2708
|
return filePath;
|
|
1926
2709
|
}
|
|
@@ -1928,7 +2711,7 @@ var IdentityContinuityStore = class {
|
|
|
1928
2711
|
try {
|
|
1929
2712
|
const safeKey = this.sanitizeIdentityAuditKey(key);
|
|
1930
2713
|
const dir = period === "weekly" ? this.deps.identityAuditsWeeklyDir : this.deps.identityAuditsMonthlyDir;
|
|
1931
|
-
return await this.deps.readStorageSecureFile(
|
|
2714
|
+
return await this.deps.readStorageSecureFile(path5.join(dir, `${safeKey}.md`));
|
|
1932
2715
|
} catch (err) {
|
|
1933
2716
|
if (err instanceof Error && err.message === "Invalid identity audit key") return null;
|
|
1934
2717
|
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
@@ -1967,7 +2750,7 @@ var IdentityContinuityStore = class {
|
|
|
1967
2750
|
return loop;
|
|
1968
2751
|
}
|
|
1969
2752
|
async readContinuityIncidentFileNames() {
|
|
1970
|
-
const files = await
|
|
2753
|
+
const files = await readdir4(this.deps.identityIncidentsDir);
|
|
1971
2754
|
return files.filter((file) => file.endsWith(".md")).sort().reverse();
|
|
1972
2755
|
}
|
|
1973
2756
|
async readContinuityIncidentFile(filePath) {
|
|
@@ -1984,12 +2767,12 @@ var IdentityContinuityStore = class {
|
|
|
1984
2767
|
const fileNames = await this.readContinuityIncidentFileNames();
|
|
1985
2768
|
const directMatch = fileNames.find((name) => name.endsWith(`-${id}.md`));
|
|
1986
2769
|
if (directMatch) {
|
|
1987
|
-
const directPath =
|
|
2770
|
+
const directPath = path5.join(this.deps.identityIncidentsDir, directMatch);
|
|
1988
2771
|
const parsed = await this.readContinuityIncidentFile(directPath);
|
|
1989
2772
|
if (parsed?.id === id) return directPath;
|
|
1990
2773
|
}
|
|
1991
2774
|
for (const fileName of fileNames) {
|
|
1992
|
-
const filePath =
|
|
2775
|
+
const filePath = path5.join(this.deps.identityIncidentsDir, fileName);
|
|
1993
2776
|
const parsed = await this.readContinuityIncidentFile(filePath);
|
|
1994
2777
|
if (parsed?.id === id) return filePath;
|
|
1995
2778
|
}
|
|
@@ -2004,6 +2787,127 @@ var IdentityContinuityStore = class {
|
|
|
2004
2787
|
}
|
|
2005
2788
|
};
|
|
2006
2789
|
|
|
2790
|
+
// src/storage/entity-canonical-id-migration-adapter.ts
|
|
2791
|
+
async function runLegacyEntityCanonicalIdMigration(host, parseEntityFile2, serializeEntityFile2, serializeMemoryWithEntityRef2) {
|
|
2792
|
+
const readMigrationFingerprint = () => getFingerprint(host.baseDir, host.entitiesDir, () => host.getCorpusScanVersion());
|
|
2793
|
+
const completionFingerprint = await migrateLegacyEntityCanonicalIds({
|
|
2794
|
+
stateDir: host.stateDir,
|
|
2795
|
+
entitiesDir: host.entitiesDir,
|
|
2796
|
+
normalizeEntityName: host.normalizeEntityName.bind(host),
|
|
2797
|
+
resolveEntityFilePath: host.resolveEntityFilePath.bind(host),
|
|
2798
|
+
readStorageSecureFile: host.readStorageSecureFile.bind(host),
|
|
2799
|
+
writeStorageSecureFile: host.writeStorageSecureFile.bind(host),
|
|
2800
|
+
isEncryptedStorageFile: host.isEncryptedFileHeader.bind(host),
|
|
2801
|
+
snapshotBeforeWrite: host.snapshotBeforeWrite.bind(host),
|
|
2802
|
+
parseEntityFile: parseEntityFile2,
|
|
2803
|
+
serializeEntityFile: serializeEntityFile2,
|
|
2804
|
+
serializeMemoryWithEntityRef: serializeMemoryWithEntityRef2,
|
|
2805
|
+
readMemoryByPath: host.readMemoryByPath.bind(host),
|
|
2806
|
+
readAllMemories: host.readAllMemories.bind(host),
|
|
2807
|
+
readAllColdMemories: host.readAllColdMemories.bind(host),
|
|
2808
|
+
readArchivedMemories: host.readArchivedMemories.bind(host),
|
|
2809
|
+
validateMemoryScanRoots: () => validateRoots(host.baseDir, host.entitiesDir, host.stateDir, host.archiveDir),
|
|
2810
|
+
rewriteProjectedEntityReferences: (mappings) => rewriteProjectedEntityReferences(host.baseDir, mappings),
|
|
2811
|
+
rewriteProjectedMemoryEntityReference: (memoryId, previousEntityRef, nextEntityRef) => rewriteProjectedMemoryEntityReference(host.baseDir, memoryId, previousEntityRef, nextEntityRef),
|
|
2812
|
+
invalidateKnowledgeIndexCache: host.invalidateKnowledgeIndexCache.bind(host),
|
|
2813
|
+
invalidateAllMemoriesCache: host.invalidateAllMemoriesCache.bind(host),
|
|
2814
|
+
invalidateColdMemoriesCache: host.invalidateColdMemoriesCache.bind(host),
|
|
2815
|
+
bumpMemoryStatusVersion: host.bumpMemoryStatusVersion.bind(host),
|
|
2816
|
+
readMigrationFingerprint
|
|
2817
|
+
});
|
|
2818
|
+
return completionFingerprint ?? readMigrationFingerprint();
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2821
|
+
// src/storage/entity-canonical-id-migration-runner.ts
|
|
2822
|
+
var EntityCanonicalIdMigrationRunner = class {
|
|
2823
|
+
constructor(canRun, runMigration, readFingerprint = async () => "static") {
|
|
2824
|
+
this.canRun = canRun;
|
|
2825
|
+
this.runMigration = runMigration;
|
|
2826
|
+
this.readFingerprint = readFingerprint;
|
|
2827
|
+
}
|
|
2828
|
+
canRun;
|
|
2829
|
+
runMigration;
|
|
2830
|
+
readFingerprint;
|
|
2831
|
+
inFlight = null;
|
|
2832
|
+
directoriesInitialized = false;
|
|
2833
|
+
migrationComplete = false;
|
|
2834
|
+
completedFingerprint = null;
|
|
2835
|
+
async markDirectoriesInitialized() {
|
|
2836
|
+
this.directoriesInitialized = true;
|
|
2837
|
+
await this.ensure();
|
|
2838
|
+
}
|
|
2839
|
+
ensure() {
|
|
2840
|
+
if (!this.canRun()) return Promise.resolve();
|
|
2841
|
+
if (this.inFlight) return this.inFlight;
|
|
2842
|
+
const migration = this.runOrReuse();
|
|
2843
|
+
this.inFlight = migration;
|
|
2844
|
+
void migration.then(
|
|
2845
|
+
() => this.clearInFlight(migration),
|
|
2846
|
+
() => this.clearInFlight(migration)
|
|
2847
|
+
);
|
|
2848
|
+
return migration;
|
|
2849
|
+
}
|
|
2850
|
+
triggerAfterUnlock() {
|
|
2851
|
+
if (!this.directoriesInitialized) return Promise.resolve();
|
|
2852
|
+
return this.ensure();
|
|
2853
|
+
}
|
|
2854
|
+
async runOrReuse() {
|
|
2855
|
+
let fingerprintAtStart = await this.readFingerprint();
|
|
2856
|
+
if (this.migrationComplete && fingerprintAtStart === this.completedFingerprint) return;
|
|
2857
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
2858
|
+
const completionFingerprint = await this.runMigration();
|
|
2859
|
+
const fingerprintAfterMigration = await this.readFingerprint();
|
|
2860
|
+
const fingerprintAfterStabilityRead = await this.readFingerprint();
|
|
2861
|
+
const returnedFingerprint = typeof completionFingerprint === "string" ? completionFingerprint : fingerprintAfterMigration;
|
|
2862
|
+
if (returnedFingerprint === fingerprintAfterMigration && fingerprintAfterMigration === fingerprintAfterStabilityRead && (typeof completionFingerprint === "string" || fingerprintAfterMigration === fingerprintAtStart)) {
|
|
2863
|
+
this.migrationComplete = true;
|
|
2864
|
+
this.completedFingerprint = fingerprintAfterStabilityRead;
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
fingerprintAtStart = fingerprintAfterStabilityRead;
|
|
2868
|
+
}
|
|
2869
|
+
this.migrationComplete = false;
|
|
2870
|
+
this.completedFingerprint = null;
|
|
2871
|
+
throw new Error("Entity canonical-id migration fingerprint changed while migration was completing; retry migration.");
|
|
2872
|
+
}
|
|
2873
|
+
clearInFlight(migration) {
|
|
2874
|
+
if (this.inFlight === migration) this.inFlight = null;
|
|
2875
|
+
}
|
|
2876
|
+
};
|
|
2877
|
+
|
|
2878
|
+
// src/storage/memory-migration-serialization.ts
|
|
2879
|
+
function serializeMemoryWithEntityRef(memory, entityRef, fallback) {
|
|
2880
|
+
const rawFrontmatter = readRawFrontmatter(memory);
|
|
2881
|
+
if (rawFrontmatter === void 0) return fallback(memory, entityRef);
|
|
2882
|
+
const lines = rawFrontmatter.split("\n");
|
|
2883
|
+
let entityRefLine = -1;
|
|
2884
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
2885
|
+
if (/^\s*entityRef\s*:/.test(lines[index] ?? "")) entityRefLine = index;
|
|
2886
|
+
}
|
|
2887
|
+
if (entityRefLine >= 0) {
|
|
2888
|
+
const indent = lines[entityRefLine].match(/^\s*/)?.[0] ?? "";
|
|
2889
|
+
lines[entityRefLine] = `${indent}entityRef: ${entityRef}`;
|
|
2890
|
+
} else {
|
|
2891
|
+
lines.push(`entityRef: ${entityRef}`);
|
|
2892
|
+
}
|
|
2893
|
+
return `---
|
|
2894
|
+
${lines.join("\n")}
|
|
2895
|
+
---
|
|
2896
|
+
|
|
2897
|
+
${memory.content}
|
|
2898
|
+
`;
|
|
2899
|
+
}
|
|
2900
|
+
function createMemoryEntityRefSerializer(serializeFrontmatter2) {
|
|
2901
|
+
return (memory, entityRef) => serializeMemoryWithEntityRef(
|
|
2902
|
+
memory,
|
|
2903
|
+
entityRef,
|
|
2904
|
+
(fallbackMemory, fallbackEntityRef) => `${serializeFrontmatter2({ ...fallbackMemory.frontmatter, entityRef: fallbackEntityRef })}
|
|
2905
|
+
|
|
2906
|
+
${fallbackMemory.content}
|
|
2907
|
+
`
|
|
2908
|
+
);
|
|
2909
|
+
}
|
|
2910
|
+
|
|
2007
2911
|
// src/utils/yaml-scalar.ts
|
|
2008
2912
|
function decodeYamlScalar(value) {
|
|
2009
2913
|
const trimmed = value.trim();
|
|
@@ -2019,9 +2923,9 @@ function decodeYamlScalar(value) {
|
|
|
2019
2923
|
}
|
|
2020
2924
|
|
|
2021
2925
|
// src/orchestration/qmd-result-resolver.ts
|
|
2022
|
-
import
|
|
2926
|
+
import path6 from "path";
|
|
2023
2927
|
function qmdCollectionPathParts(resultPath) {
|
|
2024
|
-
if (!resultPath ||
|
|
2928
|
+
if (!resultPath || path6.isAbsolute(resultPath)) return null;
|
|
2025
2929
|
const normalized = resultPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
2026
2930
|
const slashIndex = normalized.indexOf("/");
|
|
2027
2931
|
if (slashIndex <= 0 || slashIndex >= normalized.length - 1) return null;
|
|
@@ -2034,9 +2938,9 @@ function qmdCollectionPathParts(resultPath) {
|
|
|
2034
2938
|
}
|
|
2035
2939
|
function qmdResultPathCandidates(storageDir, resultPath) {
|
|
2036
2940
|
const candidates = /* @__PURE__ */ new Set();
|
|
2037
|
-
const storageRoot =
|
|
2941
|
+
const storageRoot = path6.resolve(storageDir);
|
|
2038
2942
|
const addCandidate = (candidate) => {
|
|
2039
|
-
const resolved =
|
|
2943
|
+
const resolved = path6.resolve(candidate);
|
|
2040
2944
|
if (isPathInsideStorageRoot(storageRoot, resolved)) {
|
|
2041
2945
|
candidates.add(resolved);
|
|
2042
2946
|
}
|
|
@@ -2044,18 +2948,18 @@ function qmdResultPathCandidates(storageDir, resultPath) {
|
|
|
2044
2948
|
const addRelativeCandidates = (relativePath) => {
|
|
2045
2949
|
const normalized = relativePath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
2046
2950
|
if (!normalized) return;
|
|
2047
|
-
addCandidate(
|
|
2951
|
+
addCandidate(path6.join(storageRoot, normalized));
|
|
2048
2952
|
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
2049
|
-
addCandidate(
|
|
2953
|
+
addCandidate(path6.join(storageRoot, "facts", normalized));
|
|
2050
2954
|
}
|
|
2051
2955
|
};
|
|
2052
|
-
if (
|
|
2956
|
+
if (path6.isAbsolute(resultPath)) {
|
|
2053
2957
|
addCandidate(resultPath);
|
|
2054
|
-
const relative2 =
|
|
2055
|
-
if (relative2 && !relative2.startsWith("..") && !
|
|
2056
|
-
const normalized = relative2.split(
|
|
2958
|
+
const relative2 = path6.relative(storageRoot, path6.resolve(resultPath));
|
|
2959
|
+
if (relative2 && !relative2.startsWith("..") && !path6.isAbsolute(relative2)) {
|
|
2960
|
+
const normalized = relative2.split(path6.sep).join("/");
|
|
2057
2961
|
if (/^\d{4}-\d{2}-\d{2}\//.test(normalized)) {
|
|
2058
|
-
addCandidate(
|
|
2962
|
+
addCandidate(path6.join(storageRoot, "facts", normalized));
|
|
2059
2963
|
}
|
|
2060
2964
|
}
|
|
2061
2965
|
} else {
|
|
@@ -2090,7 +2994,7 @@ var QmdResultResolver = class {
|
|
|
2090
2994
|
const seenStorageDirs = /* @__PURE__ */ new Set();
|
|
2091
2995
|
const addStorage = (storage) => {
|
|
2092
2996
|
const storageDir = storageDirOrNull(storage);
|
|
2093
|
-
const storageKey = storageDir ?
|
|
2997
|
+
const storageKey = storageDir ? path6.resolve(storageDir) : `storage-without-dir-${storages.length}`;
|
|
2094
2998
|
if (seenStorageDirs.has(storageKey)) return;
|
|
2095
2999
|
seenStorageDirs.add(storageKey);
|
|
2096
3000
|
storages.push(storage);
|
|
@@ -2120,7 +3024,7 @@ var QmdResultResolver = class {
|
|
|
2120
3024
|
continue;
|
|
2121
3025
|
}
|
|
2122
3026
|
try {
|
|
2123
|
-
const coldRoot =
|
|
3027
|
+
const coldRoot = path6.join(storageDir, "cold");
|
|
2124
3028
|
for (const candidate of qmdResultPathCandidates(
|
|
2125
3029
|
coldRoot,
|
|
2126
3030
|
parts.relativePath
|
|
@@ -2179,9 +3083,9 @@ var QmdResultResolver = class {
|
|
|
2179
3083
|
error: err.message
|
|
2180
3084
|
});
|
|
2181
3085
|
}
|
|
2182
|
-
if (!
|
|
3086
|
+
if (!path6.isAbsolute(resultPath)) return null;
|
|
2183
3087
|
}
|
|
2184
|
-
if (
|
|
3088
|
+
if (path6.isAbsolute(resultPath)) {
|
|
2185
3089
|
if (!fallbackStorageDir) {
|
|
2186
3090
|
return await fallbackStorage.readMemoryByPath(resultPath);
|
|
2187
3091
|
}
|
|
@@ -2221,7 +3125,7 @@ var QmdResultResolver = class {
|
|
|
2221
3125
|
);
|
|
2222
3126
|
if (!memory) return null;
|
|
2223
3127
|
let ownerNamespace = null;
|
|
2224
|
-
if (
|
|
3128
|
+
if (path6.isAbsolute(memory.path)) {
|
|
2225
3129
|
const ownerStorage = await this.storageForAbsoluteQmdResultPath(
|
|
2226
3130
|
memory.path,
|
|
2227
3131
|
fallbackStorage,
|
|
@@ -2245,17 +3149,17 @@ var QmdResultResolver = class {
|
|
|
2245
3149
|
};
|
|
2246
3150
|
}
|
|
2247
3151
|
async storageForAbsoluteQmdResultPath(resultPath, fallbackStorage, recallNamespaces = []) {
|
|
2248
|
-
const resolvedPath =
|
|
3152
|
+
const resolvedPath = path6.resolve(resultPath);
|
|
2249
3153
|
const config = this.getConfig();
|
|
2250
|
-
const memoryRoot =
|
|
2251
|
-
const namespacesRoot =
|
|
3154
|
+
const memoryRoot = path6.resolve(config.memoryDir);
|
|
3155
|
+
const namespacesRoot = path6.join(memoryRoot, "namespaces");
|
|
2252
3156
|
const fallbackStorageDir = storageDirOrNull(fallbackStorage);
|
|
2253
3157
|
const matches = [];
|
|
2254
3158
|
const seenDirs = /* @__PURE__ */ new Set();
|
|
2255
3159
|
const maybeAddStorage = (storage, namespace) => {
|
|
2256
3160
|
const storageDir = storageDirOrNull(storage);
|
|
2257
3161
|
if (!storageDir) return;
|
|
2258
|
-
const candidateRoot =
|
|
3162
|
+
const candidateRoot = path6.resolve(storageDir);
|
|
2259
3163
|
if (seenDirs.has(candidateRoot)) return;
|
|
2260
3164
|
if (!isPathInsideStorageRoot(candidateRoot, resolvedPath)) return;
|
|
2261
3165
|
if (candidateRoot === memoryRoot && isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
@@ -2273,7 +3177,7 @@ var QmdResultResolver = class {
|
|
|
2273
3177
|
candidateNamespaces.add(ns);
|
|
2274
3178
|
}
|
|
2275
3179
|
if (isPathInsideStorageRoot(namespacesRoot, resolvedPath)) {
|
|
2276
|
-
const relativeToNamespaces =
|
|
3180
|
+
const relativeToNamespaces = path6.relative(namespacesRoot, resolvedPath);
|
|
2277
3181
|
const [namespaceSegment] = relativeToNamespaces.split(/[\\/]/);
|
|
2278
3182
|
if (namespaceSegment) {
|
|
2279
3183
|
candidateNamespaces.add(
|
|
@@ -2922,7 +3826,7 @@ function fuseDay(date, inputs, options = {}) {
|
|
|
2922
3826
|
|
|
2923
3827
|
// src/wearables/fusion/store.ts
|
|
2924
3828
|
import { createHash as createHash2 } from "crypto";
|
|
2925
|
-
import * as
|
|
3829
|
+
import * as path7 from "path";
|
|
2926
3830
|
var FUSION_KIND = "wearable-fusion";
|
|
2927
3831
|
function hashFusionBody(conversations) {
|
|
2928
3832
|
return createHash2("sha256").update(JSON.stringify(conversations), "utf-8").digest("hex");
|
|
@@ -3109,7 +4013,7 @@ var FusionArtifactStore = class {
|
|
|
3109
4013
|
`invalid wearable fusion date '${String(date)}' \u2014 expected YYYY-MM-DD`
|
|
3110
4014
|
);
|
|
3111
4015
|
}
|
|
3112
|
-
return
|
|
4016
|
+
return path7.join(this.wearablesDir, FUSION_DIR_NAME, `${date}.md`);
|
|
3113
4017
|
}
|
|
3114
4018
|
async writeFusedDay(date, serialized) {
|
|
3115
4019
|
const filePath = this.fusedDayPath(date);
|
|
@@ -3146,7 +4050,7 @@ var FusionArtifactStore = class {
|
|
|
3146
4050
|
}
|
|
3147
4051
|
/** List dates with stored fused artifacts, newest first. */
|
|
3148
4052
|
async listFusedDays() {
|
|
3149
|
-
const fusionDir =
|
|
4053
|
+
const fusionDir = path7.join(this.wearablesDir, FUSION_DIR_NAME);
|
|
3150
4054
|
await this.assertPathContained(fusionDir);
|
|
3151
4055
|
let entries;
|
|
3152
4056
|
try {
|
|
@@ -3190,10 +4094,10 @@ var FusionArtifactStore = class {
|
|
|
3190
4094
|
try {
|
|
3191
4095
|
rootReal = await this.io.realpath(this.memoryDir);
|
|
3192
4096
|
} catch {
|
|
3193
|
-
rootReal =
|
|
4097
|
+
rootReal = path7.resolve(this.memoryDir);
|
|
3194
4098
|
}
|
|
3195
4099
|
await this.assertNoEscape(rootReal, this.wearablesDir);
|
|
3196
|
-
await this.assertNoEscape(rootReal,
|
|
4100
|
+
await this.assertNoEscape(rootReal, path7.dirname(targetPath));
|
|
3197
4101
|
await this.assertNoEscape(rootReal, targetPath);
|
|
3198
4102
|
await this.assertFusionDirNotSymlinked();
|
|
3199
4103
|
}
|
|
@@ -3207,7 +4111,7 @@ var FusionArtifactStore = class {
|
|
|
3207
4111
|
* (a fresh write creates a real directory lexically under the root).
|
|
3208
4112
|
*/
|
|
3209
4113
|
async assertFusionDirNotSymlinked() {
|
|
3210
|
-
const fusionDir =
|
|
4114
|
+
const fusionDir = path7.join(this.wearablesDir, FUSION_DIR_NAME);
|
|
3211
4115
|
let stat4;
|
|
3212
4116
|
try {
|
|
3213
4117
|
stat4 = await this.io.lstat(fusionDir);
|
|
@@ -3343,7 +4247,7 @@ function reconstructFusionInputs(date, dayTranscripts) {
|
|
|
3343
4247
|
|
|
3344
4248
|
// src/meetings/store.ts
|
|
3345
4249
|
import { createHash as createHash3 } from "crypto";
|
|
3346
|
-
import * as
|
|
4250
|
+
import * as path8 from "path";
|
|
3347
4251
|
var MEETINGS_DIR_NAME = "meetings";
|
|
3348
4252
|
var MEETING_KIND = "meeting";
|
|
3349
4253
|
var MEETING_FORMAT_VERSION = 1;
|
|
@@ -3512,7 +4416,7 @@ var MeetingRecordStore = class {
|
|
|
3512
4416
|
constructor(memoryDir, io) {
|
|
3513
4417
|
this.memoryDir = memoryDir;
|
|
3514
4418
|
this.io = io;
|
|
3515
|
-
this.meetingsDir =
|
|
4419
|
+
this.meetingsDir = path8.join(memoryDir, MEETINGS_DIR_NAME);
|
|
3516
4420
|
}
|
|
3517
4421
|
memoryDir;
|
|
3518
4422
|
io;
|
|
@@ -3528,7 +4432,7 @@ var MeetingRecordStore = class {
|
|
|
3528
4432
|
if (idDate !== date) {
|
|
3529
4433
|
throw new Error(`meeting id '${id}' does not belong to date '${date}'`);
|
|
3530
4434
|
}
|
|
3531
|
-
return
|
|
4435
|
+
return path8.join(this.meetingsDir, date, `${id}.md`);
|
|
3532
4436
|
}
|
|
3533
4437
|
/** Idempotently persist a record: skip the write when contentHash is unchanged. */
|
|
3534
4438
|
async saveMeetingRecord(record) {
|
|
@@ -3559,7 +4463,7 @@ var MeetingRecordStore = class {
|
|
|
3559
4463
|
if (!isValidTranscriptDate(date)) {
|
|
3560
4464
|
throw new Error(`invalid meeting date '${String(date)}' \u2014 expected YYYY-MM-DD`);
|
|
3561
4465
|
}
|
|
3562
|
-
const dayDir =
|
|
4466
|
+
const dayDir = path8.join(this.meetingsDir, date);
|
|
3563
4467
|
await this.assertPathContained(dayDir);
|
|
3564
4468
|
let entries;
|
|
3565
4469
|
try {
|
|
@@ -3627,10 +4531,10 @@ var MeetingRecordStore = class {
|
|
|
3627
4531
|
try {
|
|
3628
4532
|
rootReal = await this.io.realpath(this.memoryDir);
|
|
3629
4533
|
} catch {
|
|
3630
|
-
rootReal =
|
|
4534
|
+
rootReal = path8.resolve(this.memoryDir);
|
|
3631
4535
|
}
|
|
3632
4536
|
await this.assertNoEscape(rootReal, this.meetingsDir);
|
|
3633
|
-
await this.assertNoEscape(rootReal,
|
|
4537
|
+
await this.assertNoEscape(rootReal, path8.dirname(targetPath));
|
|
3634
4538
|
await this.assertNoEscape(rootReal, targetPath);
|
|
3635
4539
|
await this.assertNotSymlink(this.meetingsDir, "directory");
|
|
3636
4540
|
const dayDir = this.dayDirWithin(targetPath);
|
|
@@ -3640,11 +4544,11 @@ var MeetingRecordStore = class {
|
|
|
3640
4544
|
/** The `<meetings>/<date>` directory on the path to `targetPath`, or null when
|
|
3641
4545
|
* `targetPath` is the meetings root itself. */
|
|
3642
4546
|
dayDirWithin(targetPath) {
|
|
3643
|
-
const rel =
|
|
3644
|
-
if (rel === "" || rel.startsWith("..") ||
|
|
3645
|
-
const first = rel.split(
|
|
4547
|
+
const rel = path8.relative(this.meetingsDir, targetPath);
|
|
4548
|
+
if (rel === "" || rel.startsWith("..") || path8.isAbsolute(rel)) return null;
|
|
4549
|
+
const first = rel.split(path8.sep)[0];
|
|
3646
4550
|
if (first === void 0 || first.length === 0) return null;
|
|
3647
|
-
return
|
|
4551
|
+
return path8.join(this.meetingsDir, first);
|
|
3648
4552
|
}
|
|
3649
4553
|
async assertNotSymlink(target, kind) {
|
|
3650
4554
|
let stat4;
|
|
@@ -3655,7 +4559,7 @@ var MeetingRecordStore = class {
|
|
|
3655
4559
|
throw err;
|
|
3656
4560
|
}
|
|
3657
4561
|
if (stat4.isSymbolicLink) {
|
|
3658
|
-
const label =
|
|
4562
|
+
const label = path8.relative(this.memoryDir, target) || MEETINGS_DIR_NAME;
|
|
3659
4563
|
throw new Error(
|
|
3660
4564
|
`meetings ${kind} '${label}' is a symbolic link \u2014 refusing to follow it even when it resolves inside the memory dir (AGENTS.md pattern #3)`
|
|
3661
4565
|
);
|
|
@@ -4073,7 +4977,7 @@ function isEntitySynthesisStale(entity) {
|
|
|
4073
4977
|
|
|
4074
4978
|
// src/storage/content-hash-index.ts
|
|
4075
4979
|
import { mkdir, stat as stat2 } from "fs/promises";
|
|
4076
|
-
import
|
|
4980
|
+
import path9 from "path";
|
|
4077
4981
|
var CONTENT_HASH_INDEX_LOCK_STALE_MS = 3e4;
|
|
4078
4982
|
var CONTENT_HASH_INDEX_RETRY_MAX_ATTEMPTS = 5;
|
|
4079
4983
|
var CONTENT_HASH_INDEX_RETRY_BASE_MS = 500;
|
|
@@ -4133,8 +5037,8 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
4133
5037
|
* re-read on the hot path.
|
|
4134
5038
|
*/
|
|
4135
5039
|
lastSyncedFingerprint = null;
|
|
4136
|
-
constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir =
|
|
4137
|
-
this.filePath =
|
|
5040
|
+
constructor(stateDir, secureStoreKeyProvider = () => null, secureStoreWriteKeyProvider = secureStoreKeyProvider, memoryDir = path9.dirname(stateDir), lockOptions = {}) {
|
|
5041
|
+
this.filePath = path9.join(stateDir, "fact-hashes.txt");
|
|
4138
5042
|
this.secureStoreKeyProvider = secureStoreKeyProvider;
|
|
4139
5043
|
this.secureStoreWriteKeyProvider = secureStoreWriteKeyProvider;
|
|
4140
5044
|
this.memoryDir = memoryDir;
|
|
@@ -4226,7 +5130,7 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
4226
5130
|
/** Persist index to disk if changed (plain whole-file overwrite). */
|
|
4227
5131
|
async save() {
|
|
4228
5132
|
if (!this.dirty) return;
|
|
4229
|
-
await mkdir(
|
|
5133
|
+
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
4230
5134
|
const publishedAdded = new Set(this.added);
|
|
4231
5135
|
const publishedRemoved = new Set(this.removed);
|
|
4232
5136
|
const serialized = [...this.hashes].join("\n") + "\n";
|
|
@@ -4273,7 +5177,7 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
4273
5177
|
*/
|
|
4274
5178
|
async saveMergingWithDisk() {
|
|
4275
5179
|
if (!this.dirty) return;
|
|
4276
|
-
await mkdir(
|
|
5180
|
+
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
4277
5181
|
const publish = async () => {
|
|
4278
5182
|
const publishedAdded = new Set(this.added);
|
|
4279
5183
|
const publishedRemoved = new Set(this.removed);
|
|
@@ -4292,7 +5196,7 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
4292
5196
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
4293
5197
|
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
4294
5198
|
}
|
|
4295
|
-
await mkdir(
|
|
5199
|
+
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
4296
5200
|
await writeMaybeEncryptedFile(
|
|
4297
5201
|
this.filePath,
|
|
4298
5202
|
[...merged].join("\n") + "\n",
|
|
@@ -4359,7 +5263,7 @@ var ContentHashIndex = class _ContentHashIndex {
|
|
|
4359
5263
|
* fired retry) simply yields `acquired=false` after the bounded wait.
|
|
4360
5264
|
*/
|
|
4361
5265
|
async rebuildUnderLock(populate) {
|
|
4362
|
-
await mkdir(
|
|
5266
|
+
await mkdir(path9.dirname(this.filePath), { recursive: true });
|
|
4363
5267
|
let published = false;
|
|
4364
5268
|
await withHeldFileLock(
|
|
4365
5269
|
`${this.filePath}.lock`,
|
|
@@ -5160,17 +6064,17 @@ function inferEntityTypeFromContent(content) {
|
|
|
5160
6064
|
}
|
|
5161
6065
|
var KNOWN_ENTITY_FILENAME_PREFIXES = /* @__PURE__ */ new Set(["company", "other", "person", "place", "project", "tool", "topic"]);
|
|
5162
6066
|
function inferEntityTypeFromFilename(pathRel) {
|
|
5163
|
-
const basename =
|
|
6067
|
+
const basename = path10.basename(pathRel, ".md").toLowerCase();
|
|
5164
6068
|
const separator = basename.indexOf("-");
|
|
5165
6069
|
if (separator <= 0) return void 0;
|
|
5166
6070
|
const candidate = basename.slice(0, separator);
|
|
5167
6071
|
return KNOWN_ENTITY_FILENAME_PREFIXES.has(candidate) ? candidate : void 0;
|
|
5168
6072
|
}
|
|
5169
6073
|
function normalizeFrontmatterForPath(frontmatter, pathRel, content = "") {
|
|
5170
|
-
const normalizedPath = pathRel.split(
|
|
6074
|
+
const normalizedPath = pathRel.split(path10.sep).join("/");
|
|
5171
6075
|
let normalizedFrontmatter = frontmatter;
|
|
5172
6076
|
if (normalizedPath === "entities" || normalizedPath.startsWith("entities/") || normalizedPath.includes("/entities/")) {
|
|
5173
|
-
const basename =
|
|
6077
|
+
const basename = path10.basename(pathRel, ".md");
|
|
5174
6078
|
const inferredType = inferEntityTypeFromContent(content) || inferEntityTypeFromFilename(pathRel) || "entity";
|
|
5175
6079
|
const existingTags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
|
|
5176
6080
|
normalizedFrontmatter = {
|
|
@@ -5191,27 +6095,6 @@ function normalizeFrontmatterForPath(frontmatter, pathRel, content = "") {
|
|
|
5191
6095
|
function inferCurrentStateStatus(frontmatter, pathRel, fallbackStatus) {
|
|
5192
6096
|
return inferMemoryStatus(frontmatter, pathRel, fallbackStatus);
|
|
5193
6097
|
}
|
|
5194
|
-
var BUILTIN_ALIASES = {
|
|
5195
|
-
openclaw: "openclaw",
|
|
5196
|
-
"open-claw": "openclaw"
|
|
5197
|
-
};
|
|
5198
|
-
function normalizeEntityName(raw, type, aliases) {
|
|
5199
|
-
const rawStr = typeof raw === "string" ? raw : "";
|
|
5200
|
-
const typeStr = typeof type === "string" && type.trim().length > 0 ? type : "entity";
|
|
5201
|
-
let name = rawStr.toLowerCase().trim();
|
|
5202
|
-
const typePrefix = `${typeStr.toLowerCase()}-`;
|
|
5203
|
-
if (name.startsWith(typePrefix)) {
|
|
5204
|
-
name = name.slice(typePrefix.length);
|
|
5205
|
-
}
|
|
5206
|
-
let normalized = name.replace(/[^a-z0-9]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
5207
|
-
const userAlias = aliases !== void 0 && Object.hasOwn(aliases, normalized) ? aliases[normalized] : void 0;
|
|
5208
|
-
if (typeof userAlias === "string" && userAlias.length > 0) {
|
|
5209
|
-
normalized = userAlias;
|
|
5210
|
-
} else if (Object.hasOwn(BUILTIN_ALIASES, normalized)) {
|
|
5211
|
-
normalized = BUILTIN_ALIASES[normalized];
|
|
5212
|
-
}
|
|
5213
|
-
return `${typeStr.toLowerCase()}-${normalized}`;
|
|
5214
|
-
}
|
|
5215
6098
|
function levenshtein(a, b) {
|
|
5216
6099
|
const m = a.length;
|
|
5217
6100
|
const n = b.length;
|
|
@@ -5657,6 +6540,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
5657
6540
|
this.entitySchemas = entitySchemas;
|
|
5658
6541
|
this.hotMemoriesCacheEnabled = hotMemoriesCacheEnabledOverride ?? _StorageManager.hotMemoriesCacheDefaultByDir.get(baseDir) ?? _StorageManager.hotMemoriesCacheDefault;
|
|
5659
6542
|
this.loadAliasesSync();
|
|
6543
|
+
this.loadHistoricalEntityCanonicalIdsSync();
|
|
5660
6544
|
}
|
|
5661
6545
|
baseDir;
|
|
5662
6546
|
entitySchemas;
|
|
@@ -5836,8 +6720,8 @@ var StorageManager3 = class _StorageManager {
|
|
|
5836
6720
|
}
|
|
5837
6721
|
/** Whether `filePath` resolves to `stateDir` itself or a path contained in it. */
|
|
5838
6722
|
isStateFilePath(filePath) {
|
|
5839
|
-
const rel =
|
|
5840
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
6723
|
+
const rel = path10.relative(this.stateDir, path10.resolve(filePath));
|
|
6724
|
+
return rel === "" || !rel.startsWith("..") && !path10.isAbsolute(rel);
|
|
5841
6725
|
}
|
|
5842
6726
|
// ── Tombstone store (issue #1579) ───────────────────────────────────────
|
|
5843
6727
|
// Instance-scoped (rule 11) + namespace-scoped (rule 42) tombstone index.
|
|
@@ -5880,17 +6764,34 @@ var StorageManager3 = class _StorageManager {
|
|
|
5880
6764
|
* Set or clear the at-rest encryption key.
|
|
5881
6765
|
*
|
|
5882
6766
|
* Pass a 32-byte Buffer to enable encryption; pass null to clear
|
|
5883
|
-
* (lock) the store.
|
|
6767
|
+
* (lock) the store. The caller is responsible for key lifecycle —
|
|
5884
6768
|
* this method does not zero the buffer on replacement; the keyring
|
|
5885
|
-
* module (`keyring.ts`) owns zeroization.
|
|
6769
|
+
* module (`keyring.ts`) owns zeroization. The setter remains synchronous
|
|
6770
|
+
* for existing consumers; call `setSecureStoreKeyAndWait` when unlock must
|
|
6771
|
+
* await migration.
|
|
5886
6772
|
*/
|
|
5887
6773
|
setSecureStoreKey(key, encryptOnWrite = true) {
|
|
6774
|
+
if (!this.applySecureStoreKey(key, encryptOnWrite) || key === null) return;
|
|
6775
|
+
void this.entityCanonicalIdMigration.triggerAfterUnlock().catch((error) => {
|
|
6776
|
+
log.warn(
|
|
6777
|
+
`secure-store unlock migration failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6778
|
+
);
|
|
6779
|
+
});
|
|
6780
|
+
}
|
|
6781
|
+
async setSecureStoreKeyAndWait(key, encryptOnWrite = true) {
|
|
6782
|
+
if (!this.applySecureStoreKey(key, encryptOnWrite) || key === null) return;
|
|
6783
|
+
await this.entityCanonicalIdMigration.triggerAfterUnlock();
|
|
6784
|
+
}
|
|
6785
|
+
applySecureStoreKey(key, encryptOnWrite) {
|
|
6786
|
+
const sameKey = this._secureStoreKey === key || this._secureStoreKey !== null && key !== null && this._secureStoreKey.equals(key);
|
|
6787
|
+
if (sameKey && this._secureStoreEncryptOnWrite === encryptOnWrite) return false;
|
|
5888
6788
|
this._secureStoreKey = key;
|
|
5889
6789
|
this._secureStoreEncryptOnWrite = encryptOnWrite;
|
|
5890
6790
|
invalidateAllForDir(this.baseDir);
|
|
5891
6791
|
this.invalidateKnowledgeIndexCache();
|
|
5892
6792
|
this.secureFileEncryptionSniffCache.clear();
|
|
5893
6793
|
this.behaviorSignalsKeyCache = null;
|
|
6794
|
+
return true;
|
|
5894
6795
|
}
|
|
5895
6796
|
getEntityCacheSecureStoreKey() {
|
|
5896
6797
|
if (!this._secureStoreKey) return "secure-store:locked";
|
|
@@ -5937,8 +6838,8 @@ var StorageManager3 = class _StorageManager {
|
|
|
5937
6838
|
* /srv/cold/remnic), skipping the patch and stranding a stale hot entry.
|
|
5938
6839
|
*/
|
|
5939
6840
|
isColdOrArchiveTierPath(p) {
|
|
5940
|
-
const rel =
|
|
5941
|
-
return rel === "cold" || rel === "archive" || rel.startsWith(`cold${
|
|
6841
|
+
const rel = path10.relative(this.baseDir, p);
|
|
6842
|
+
return rel === "cold" || rel === "archive" || rel.startsWith(`cold${path10.sep}`) || rel.startsWith(`archive${path10.sep}`);
|
|
5942
6843
|
}
|
|
5943
6844
|
/**
|
|
5944
6845
|
* Mark the secure-store as required for this storage instance.
|
|
@@ -6025,7 +6926,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6025
6926
|
void 0,
|
|
6026
6927
|
this.baseDir
|
|
6027
6928
|
);
|
|
6028
|
-
const rel =
|
|
6929
|
+
const rel = path10.relative(this.baseDir, filePath).split(path10.sep).join("/");
|
|
6029
6930
|
return `${rel}:${version.versionId}`;
|
|
6030
6931
|
} catch (err) {
|
|
6031
6932
|
log.warn(
|
|
@@ -6105,13 +7006,13 @@ var StorageManager3 = class _StorageManager {
|
|
|
6105
7006
|
}
|
|
6106
7007
|
identityFilePath(workspaceDir, namespace) {
|
|
6107
7008
|
const rawNamespace = typeof namespace === "string" ? namespace.trim() : "";
|
|
6108
|
-
if (!rawNamespace) return
|
|
7009
|
+
if (!rawNamespace) return path10.join(workspaceDir, "IDENTITY.md");
|
|
6109
7010
|
const safeNamespace = rawNamespace.replace(/[^a-zA-Z0-9._-]/g, "-");
|
|
6110
|
-
return
|
|
7011
|
+
return path10.join(workspaceDir, `IDENTITY.${safeNamespace}.md`);
|
|
6111
7012
|
}
|
|
6112
7013
|
versionFilePath(kind) {
|
|
6113
7014
|
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";
|
|
6114
|
-
return
|
|
7015
|
+
return path10.join(this.stateDir, fileName);
|
|
6115
7016
|
}
|
|
6116
7017
|
bumpSharedVersion(kind, fallbackMap) {
|
|
6117
7018
|
const filePath = this.versionFilePath(kind);
|
|
@@ -6203,7 +7104,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6203
7104
|
// memories.
|
|
6204
7105
|
// -------------------------------------------------------------------------
|
|
6205
7106
|
get wearablesDir() {
|
|
6206
|
-
return
|
|
7107
|
+
return path10.join(this.baseDir, WEARABLES_DIR_NAME);
|
|
6207
7108
|
}
|
|
6208
7109
|
/**
|
|
6209
7110
|
* Resolve the on-disk path for a source/day transcript. Throws on
|
|
@@ -6219,7 +7120,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6219
7120
|
if (!isValidTranscriptDate(date)) {
|
|
6220
7121
|
throw new Error(`invalid wearable transcript date '${String(date)}' \u2014 expected YYYY-MM-DD`);
|
|
6221
7122
|
}
|
|
6222
|
-
return
|
|
7123
|
+
return path10.join(this.wearablesDir, sourceId, `${date}.md`);
|
|
6223
7124
|
}
|
|
6224
7125
|
async writeWearableDayTranscript(sourceId, date, serialized) {
|
|
6225
7126
|
const targetPath = this.wearableTranscriptPath(sourceId, date);
|
|
@@ -6245,10 +7146,10 @@ var StorageManager3 = class _StorageManager {
|
|
|
6245
7146
|
return this._fusionStore = new FusionArtifactStore(this.wearablesDir, this.baseDir, {
|
|
6246
7147
|
writeFile: (p, c) => this.writeStorageSecureFile(p, c),
|
|
6247
7148
|
readFile: (p) => readMaybeEncryptedFile(p, this._secureStoreKey, this.baseDir),
|
|
6248
|
-
readDir: (d) =>
|
|
6249
|
-
deleteFile: (p) =>
|
|
6250
|
-
realpath: (p) =>
|
|
6251
|
-
lstat: (p) =>
|
|
7149
|
+
readDir: (d) => readdir5(d),
|
|
7150
|
+
deleteFile: (p) => unlink3(p),
|
|
7151
|
+
realpath: (p) => realpath3(p),
|
|
7152
|
+
lstat: (p) => lstat3(p).then((st) => ({ isSymbolicLink: st.isSymbolicLink() }))
|
|
6252
7153
|
});
|
|
6253
7154
|
}
|
|
6254
7155
|
_meetingRecordStore;
|
|
@@ -6263,10 +7164,10 @@ var StorageManager3 = class _StorageManager {
|
|
|
6263
7164
|
return this._meetingRecordStore = new MeetingRecordStore(this.baseDir, {
|
|
6264
7165
|
writeFile: (p, c) => this.writeStorageSecureFile(p, c),
|
|
6265
7166
|
readFile: (p) => readMaybeEncryptedFile(p, this._secureStoreKey, this.baseDir),
|
|
6266
|
-
readDir: (d) =>
|
|
6267
|
-
deleteFile: (p) =>
|
|
6268
|
-
realpath: (p) =>
|
|
6269
|
-
lstat: (p) =>
|
|
7167
|
+
readDir: (d) => readdir5(d),
|
|
7168
|
+
deleteFile: (p) => unlink3(p),
|
|
7169
|
+
realpath: (p) => realpath3(p),
|
|
7170
|
+
lstat: (p) => lstat3(p).then((st) => ({ isSymbolicLink: st.isSymbolicLink() }))
|
|
6270
7171
|
});
|
|
6271
7172
|
}
|
|
6272
7173
|
/**
|
|
@@ -6335,25 +7236,25 @@ var StorageManager3 = class _StorageManager {
|
|
|
6335
7236
|
});
|
|
6336
7237
|
}
|
|
6337
7238
|
get factsDir() {
|
|
6338
|
-
return
|
|
7239
|
+
return path10.join(this.baseDir, "facts");
|
|
6339
7240
|
}
|
|
6340
7241
|
get correctionsDir() {
|
|
6341
|
-
return
|
|
7242
|
+
return path10.join(this.baseDir, "corrections");
|
|
6342
7243
|
}
|
|
6343
7244
|
get proceduresDir() {
|
|
6344
|
-
return
|
|
7245
|
+
return path10.join(this.baseDir, "procedures");
|
|
6345
7246
|
}
|
|
6346
7247
|
get reasoningTracesDir() {
|
|
6347
|
-
return
|
|
7248
|
+
return path10.join(this.baseDir, "reasoning-traces");
|
|
6348
7249
|
}
|
|
6349
7250
|
get entitiesDir() {
|
|
6350
|
-
return
|
|
7251
|
+
return path10.join(this.baseDir, "entities");
|
|
6351
7252
|
}
|
|
6352
7253
|
resolveEntityFilePath(name) {
|
|
6353
7254
|
if (typeof name !== "string") return null;
|
|
6354
|
-
const filePath =
|
|
6355
|
-
const relative2 =
|
|
6356
|
-
if (relative2 === "" || relative2.startsWith("..") ||
|
|
7255
|
+
const filePath = path10.resolve(this.entitiesDir, `${name}.md`);
|
|
7256
|
+
const relative2 = path10.relative(this.entitiesDir, filePath);
|
|
7257
|
+
if (relative2 === "" || relative2.startsWith("..") || path10.isAbsolute(relative2)) {
|
|
6357
7258
|
return null;
|
|
6358
7259
|
}
|
|
6359
7260
|
return filePath;
|
|
@@ -6374,10 +7275,10 @@ var StorageManager3 = class _StorageManager {
|
|
|
6374
7275
|
});
|
|
6375
7276
|
}
|
|
6376
7277
|
assertManagedStoragePath(filePath, method) {
|
|
6377
|
-
const resolved =
|
|
6378
|
-
const base =
|
|
6379
|
-
const rel =
|
|
6380
|
-
if (rel === "" || rel === ".." || rel.startsWith(`..${
|
|
7278
|
+
const resolved = path10.resolve(filePath);
|
|
7279
|
+
const base = path10.resolve(this.baseDir);
|
|
7280
|
+
const rel = path10.relative(base, resolved);
|
|
7281
|
+
if (rel === "" || rel === ".." || rel.startsWith(`..${path10.sep}`) || path10.isAbsolute(rel)) {
|
|
6381
7282
|
throw new Error(`${method}: file path escapes memory dir`);
|
|
6382
7283
|
}
|
|
6383
7284
|
return resolved;
|
|
@@ -6389,7 +7290,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6389
7290
|
async digestOfflineSyncFile(filePath) {
|
|
6390
7291
|
const target = this.assertManagedStoragePath(filePath, "storage.digestOfflineSyncFile");
|
|
6391
7292
|
const st = await stat3(target);
|
|
6392
|
-
const relPath =
|
|
7293
|
+
const relPath = path10.relative(this.baseDir, target).split(path10.sep).join("/");
|
|
6393
7294
|
const cache = await this.loadOfflineSyncDigestCache();
|
|
6394
7295
|
const cached = cache.get(relPath);
|
|
6395
7296
|
if (cached && cached.statBytes === st.size && cached.mtimeMs === st.mtimeMs && cached.ctimeMs === st.ctimeMs && !cached.encrypted) {
|
|
@@ -6435,7 +7336,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6435
7336
|
async readOfflineSyncDigestCache() {
|
|
6436
7337
|
const cache = /* @__PURE__ */ new Map();
|
|
6437
7338
|
try {
|
|
6438
|
-
const raw = await
|
|
7339
|
+
const raw = await readFile2(this.offlineSyncDigestCachePath, "utf-8");
|
|
6439
7340
|
const parsed = JSON.parse(raw);
|
|
6440
7341
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return cache;
|
|
6441
7342
|
const entries = parsed.entries;
|
|
@@ -6450,7 +7351,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6450
7351
|
const bytes = typeof record.bytes === "number" ? record.bytes : NaN;
|
|
6451
7352
|
const sha256 = typeof record.sha256 === "string" ? record.sha256 : "";
|
|
6452
7353
|
const encrypted = record.encrypted === true;
|
|
6453
|
-
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") ||
|
|
7354
|
+
if (cachePath.length === 0 || cachePath === ".." || cachePath.startsWith("../") || path10.isAbsolute(cachePath) || !Number.isFinite(statBytes) || !Number.isFinite(mtimeMs) || !Number.isFinite(ctimeMs) || !Number.isFinite(bytes) || !/^[a-f0-9]{64}$/i.test(sha256)) {
|
|
6454
7355
|
continue;
|
|
6455
7356
|
}
|
|
6456
7357
|
cache.set(cachePath, { statBytes, mtimeMs, ctimeMs, encrypted, sha256, bytes });
|
|
@@ -6492,8 +7393,8 @@ var StorageManager3 = class _StorageManager {
|
|
|
6492
7393
|
const cache = this.offlineSyncDigestCache;
|
|
6493
7394
|
if (!cache) return;
|
|
6494
7395
|
const entries = [...cache.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([entryPath, entry]) => ({ path: entryPath, ...entry }));
|
|
6495
|
-
await mkdir2(
|
|
6496
|
-
await
|
|
7396
|
+
await mkdir2(path10.dirname(this.offlineSyncDigestCachePath), { recursive: true });
|
|
7397
|
+
await writeFile2(this.offlineSyncDigestCachePath, `${JSON.stringify({ version: 1, entries })}
|
|
6497
7398
|
`, "utf-8");
|
|
6498
7399
|
}).catch((err) => {
|
|
6499
7400
|
log.warn(
|
|
@@ -6527,7 +7428,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6527
7428
|
}
|
|
6528
7429
|
async deleteOfflineSyncFile(filePath) {
|
|
6529
7430
|
const target = this.assertManagedStoragePath(filePath, "storage.deleteOfflineSyncFile");
|
|
6530
|
-
await
|
|
7431
|
+
await unlink3(target).catch((error) => {
|
|
6531
7432
|
if (isErrnoCode(error, "ENOENT")) return;
|
|
6532
7433
|
throw error;
|
|
6533
7434
|
});
|
|
@@ -6537,10 +7438,10 @@ var StorageManager3 = class _StorageManager {
|
|
|
6537
7438
|
this.invalidateAllMemoriesCache();
|
|
6538
7439
|
this.invalidateKnowledgeIndexCache();
|
|
6539
7440
|
this.factHashIndexAuthoritative = false;
|
|
6540
|
-
if (filePath.includes(`${
|
|
7441
|
+
if (filePath.includes(`${path10.sep}cold${path10.sep}`)) {
|
|
6541
7442
|
this.invalidateColdMemoriesCache();
|
|
6542
7443
|
}
|
|
6543
|
-
if (filePath.includes(`${
|
|
7444
|
+
if (filePath.includes(`${path10.sep}artifacts${path10.sep}`)) {
|
|
6544
7445
|
this.bumpArtifactWriteVersion();
|
|
6545
7446
|
}
|
|
6546
7447
|
this.bumpMemoryStatusVersion();
|
|
@@ -6611,7 +7512,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6611
7512
|
}
|
|
6612
7513
|
async appendStorageSecureFileUnlocked(filePath, content) {
|
|
6613
7514
|
const writeKey = this.resolveWriteKey();
|
|
6614
|
-
await mkdir2(
|
|
7515
|
+
await mkdir2(path10.dirname(filePath), { recursive: true });
|
|
6615
7516
|
if (writeKey === null) {
|
|
6616
7517
|
try {
|
|
6617
7518
|
if (await this.isEncryptedFileHeader(filePath)) {
|
|
@@ -6637,13 +7538,13 @@ var StorageManager3 = class _StorageManager {
|
|
|
6637
7538
|
this.notifyCatalogWriteForPath(filePath);
|
|
6638
7539
|
}
|
|
6639
7540
|
get stateDir() {
|
|
6640
|
-
return
|
|
7541
|
+
return path10.join(this.baseDir, "state");
|
|
6641
7542
|
}
|
|
6642
7543
|
get offlineSyncDigestCachePath() {
|
|
6643
|
-
return
|
|
7544
|
+
return path10.join(this.baseDir, ".offline-sync", "digest-cache.v1.json");
|
|
6644
7545
|
}
|
|
6645
7546
|
get entitySynthesisQueuePath() {
|
|
6646
|
-
return
|
|
7547
|
+
return path10.join(this.stateDir, "entity-synthesis-queue.json");
|
|
6647
7548
|
}
|
|
6648
7549
|
// ── Tombstone store access (issue #1579) ─────────────────────────────────
|
|
6649
7550
|
/**
|
|
@@ -6653,7 +7554,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
6653
7554
|
* helpers are injected into the store).
|
|
6654
7555
|
*/
|
|
6655
7556
|
get tombstonesPath() {
|
|
6656
|
-
return
|
|
7557
|
+
return path10.join(this.stateDir, "tombstones.jsonl");
|
|
6657
7558
|
}
|
|
6658
7559
|
/**
|
|
6659
7560
|
* Configure the tombstone invariant for this storage instance. Installed
|
|
@@ -6926,55 +7827,55 @@ var StorageManager3 = class _StorageManager {
|
|
|
6926
7827
|
return null;
|
|
6927
7828
|
}
|
|
6928
7829
|
get questionsDir() {
|
|
6929
|
-
return
|
|
7830
|
+
return path10.join(this.baseDir, "questions");
|
|
6930
7831
|
}
|
|
6931
7832
|
get artifactsDir() {
|
|
6932
|
-
return
|
|
7833
|
+
return path10.join(this.baseDir, "artifacts");
|
|
6933
7834
|
}
|
|
6934
7835
|
get identityDir() {
|
|
6935
|
-
return
|
|
7836
|
+
return path10.join(this.baseDir, "identity");
|
|
6936
7837
|
}
|
|
6937
7838
|
get identityAnchorPath() {
|
|
6938
|
-
return
|
|
7839
|
+
return path10.join(this.identityDir, "identity-anchor.md");
|
|
6939
7840
|
}
|
|
6940
7841
|
get identityIncidentsDir() {
|
|
6941
|
-
return
|
|
7842
|
+
return path10.join(this.identityDir, "incidents");
|
|
6942
7843
|
}
|
|
6943
7844
|
get identityAuditsWeeklyDir() {
|
|
6944
|
-
return
|
|
7845
|
+
return path10.join(this.identityDir, "audits", "weekly");
|
|
6945
7846
|
}
|
|
6946
7847
|
get identityAuditsMonthlyDir() {
|
|
6947
|
-
return
|
|
7848
|
+
return path10.join(this.identityDir, "audits", "monthly");
|
|
6948
7849
|
}
|
|
6949
7850
|
get identityImprovementLoopsPath() {
|
|
6950
|
-
return
|
|
7851
|
+
return path10.join(this.identityDir, "improvement-loops.md");
|
|
6951
7852
|
}
|
|
6952
7853
|
get identityReflectionsPath() {
|
|
6953
|
-
return
|
|
7854
|
+
return path10.join(this.identityDir, "reflections.md");
|
|
6954
7855
|
}
|
|
6955
7856
|
get profilePath() {
|
|
6956
|
-
return
|
|
7857
|
+
return path10.join(this.baseDir, "profile.md");
|
|
6957
7858
|
}
|
|
6958
7859
|
get memoryActionsPath() {
|
|
6959
|
-
return
|
|
7860
|
+
return path10.join(this.stateDir, "memory-actions.jsonl");
|
|
6960
7861
|
}
|
|
6961
7862
|
get memoryLifecycleLedgerPath() {
|
|
6962
|
-
return
|
|
7863
|
+
return path10.join(this.stateDir, "memory-lifecycle-ledger.jsonl");
|
|
6963
7864
|
}
|
|
6964
7865
|
get compressionGuidelinesPath() {
|
|
6965
|
-
return
|
|
7866
|
+
return path10.join(this.stateDir, "compression-guidelines.md");
|
|
6966
7867
|
}
|
|
6967
7868
|
get compressionGuidelineDraftPath() {
|
|
6968
|
-
return
|
|
7869
|
+
return path10.join(this.stateDir, "compression-guidelines.draft.md");
|
|
6969
7870
|
}
|
|
6970
7871
|
get compressionGuidelineStatePath() {
|
|
6971
|
-
return
|
|
7872
|
+
return path10.join(this.stateDir, "compression-guideline-state.json");
|
|
6972
7873
|
}
|
|
6973
7874
|
get compressionGuidelineDraftStatePath() {
|
|
6974
|
-
return
|
|
7875
|
+
return path10.join(this.stateDir, "compression-guideline-draft-state.json");
|
|
6975
7876
|
}
|
|
6976
7877
|
get behaviorSignalsPath() {
|
|
6977
|
-
return
|
|
7878
|
+
return path10.join(this.stateDir, "behavior-signals.jsonl");
|
|
6978
7879
|
}
|
|
6979
7880
|
/**
|
|
6980
7881
|
* In-memory dedup key set for behavior-signals appends (issue #1909),
|
|
@@ -7006,7 +7907,7 @@ var StorageManager3 = class _StorageManager {
|
|
|
7006
7907
|
* governance sweeps can treat it uniformly.
|
|
7007
7908
|
*/
|
|
7008
7909
|
get bufferSurpriseLedgerPath() {
|
|
7009
|
-
return
|
|
7910
|
+
return path10.join(this.stateDir, "buffer-surprise-ledger.jsonl");
|
|
7010
7911
|
}
|
|
7011
7912
|
/**
|
|
7012
7913
|
* Entity alias table loaded from THIS store's config/aliases.json.
|
|
@@ -7016,8 +7917,17 @@ var StorageManager3 = class _StorageManager {
|
|
|
7016
7917
|
* store loaded last rewrite every other store's canonical entity ids.
|
|
7017
7918
|
*/
|
|
7018
7919
|
userAliases = {};
|
|
7920
|
+
historicalEntityCanonicalIds = {};
|
|
7921
|
+
entityCanonicalIdMigration = new EntityCanonicalIdMigrationRunner(
|
|
7922
|
+
() => !(this._secureStoreRequired && !this.isSecureStoreUnlocked()),
|
|
7923
|
+
() => this.runLegacyEntityCanonicalIdMigration(),
|
|
7924
|
+
() => getFingerprint(this.baseDir, this.entitiesDir, () => this.getCorpusScanVersion())
|
|
7925
|
+
);
|
|
7019
7926
|
normalizeEntityName(raw, type) {
|
|
7020
|
-
return
|
|
7927
|
+
return resolveHistoricalEntityCanonicalId(
|
|
7928
|
+
this.entityStore.normalizeEntityName(raw, type),
|
|
7929
|
+
this.historicalEntityCanonicalIds
|
|
7930
|
+
);
|
|
7021
7931
|
}
|
|
7022
7932
|
/**
|
|
7023
7933
|
* Read-only view of this store's user alias table, for call sites that
|
|
@@ -7037,10 +7947,10 @@ var StorageManager3 = class _StorageManager {
|
|
|
7037
7947
|
this.loadAliasesSync();
|
|
7038
7948
|
}
|
|
7039
7949
|
loadAliasesSync() {
|
|
7040
|
-
const aliasPath =
|
|
7950
|
+
const aliasPath = path10.join(this.baseDir, "config", "aliases.json");
|
|
7041
7951
|
this.userAliases = {};
|
|
7042
7952
|
try {
|
|
7043
|
-
const raw =
|
|
7953
|
+
const raw = readFileSync2(aliasPath, "utf-8");
|
|
7044
7954
|
const parsed = JSON.parse(raw);
|
|
7045
7955
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
7046
7956
|
const cleaned = {};
|
|
@@ -7058,11 +7968,26 @@ var StorageManager3 = class _StorageManager {
|
|
|
7058
7968
|
log.debug("no config/aliases.json found \u2014 using built-in aliases only");
|
|
7059
7969
|
}
|
|
7060
7970
|
}
|
|
7971
|
+
loadHistoricalEntityCanonicalIdsSync() {
|
|
7972
|
+
this.historicalEntityCanonicalIds = loadHistoricalEntityCanonicalIds(this.stateDir);
|
|
7973
|
+
}
|
|
7974
|
+
async runLegacyEntityCanonicalIdMigration() {
|
|
7975
|
+
const completionFingerprint = await runLegacyEntityCanonicalIdMigration(
|
|
7976
|
+
this,
|
|
7977
|
+
(content) => parseEntityFile(content, this.entitySchemas),
|
|
7978
|
+
(entity) => serializeEntityFile(entity, this.entitySchemas),
|
|
7979
|
+
createMemoryEntityRefSerializer(serializeFrontmatter)
|
|
7980
|
+
);
|
|
7981
|
+
this.loadHistoricalEntityCanonicalIdsSync();
|
|
7982
|
+
return completionFingerprint;
|
|
7983
|
+
}
|
|
7061
7984
|
async ensureDirectories() {
|
|
7985
|
+
await mkdir2(this.baseDir, { recursive: true });
|
|
7986
|
+
await validateRoots(this.baseDir, this.entitiesDir, this.stateDir, this.archiveDir);
|
|
7062
7987
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7063
|
-
await mkdir2(
|
|
7064
|
-
await mkdir2(
|
|
7065
|
-
await mkdir2(
|
|
7988
|
+
await mkdir2(path10.join(this.factsDir, today), { recursive: true });
|
|
7989
|
+
await mkdir2(path10.join(this.proceduresDir, today), { recursive: true });
|
|
7990
|
+
await mkdir2(path10.join(this.reasoningTracesDir, today), { recursive: true });
|
|
7066
7991
|
await mkdir2(this.correctionsDir, { recursive: true });
|
|
7067
7992
|
await mkdir2(this.entitiesDir, { recursive: true });
|
|
7068
7993
|
await mkdir2(this.stateDir, { recursive: true });
|
|
@@ -7072,10 +7997,12 @@ var StorageManager3 = class _StorageManager {
|
|
|
7072
7997
|
await mkdir2(this.identityIncidentsDir, { recursive: true });
|
|
7073
7998
|
await mkdir2(this.identityAuditsWeeklyDir, { recursive: true });
|
|
7074
7999
|
await mkdir2(this.identityAuditsMonthlyDir, { recursive: true });
|
|
7075
|
-
await mkdir2(
|
|
8000
|
+
await mkdir2(path10.join(this.baseDir, "config"), { recursive: true });
|
|
8001
|
+
await this.entityCanonicalIdMigration.ensure();
|
|
7076
8002
|
if (this.hotMemoriesCacheEnabled && this.getMemoryCorpusVersion() === 0) {
|
|
7077
8003
|
this.bumpMemoryCorpusVersion();
|
|
7078
8004
|
}
|
|
8005
|
+
await this.entityCanonicalIdMigration.markDirectoriesInitialized();
|
|
7079
8006
|
}
|
|
7080
8007
|
/**
|
|
7081
8008
|
* Resolve the on-disk write path for a memory of the given category, creating
|
|
@@ -7092,11 +8019,11 @@ var StorageManager3 = class _StorageManager {
|
|
|
7092
8019
|
async resolveCategoryWritePath(category, id, today) {
|
|
7093
8020
|
if (category === "correction") {
|
|
7094
8021
|
await mkdir2(this.correctionsDir, { recursive: true });
|
|
7095
|
-
return
|
|
8022
|
+
return path10.join(this.correctionsDir, `${id}.md`);
|
|
7096
8023
|
}
|
|
7097
|
-
const datedDir =
|
|
8024
|
+
const datedDir = path10.join(getCategoryDir(this.baseDir, category), today);
|
|
7098
8025
|
await mkdir2(datedDir, { recursive: true });
|
|
7099
|
-
return
|
|
8026
|
+
return path10.join(datedDir, `${id}.md`);
|
|
7100
8027
|
}
|
|
7101
8028
|
async writeMemory(category, content, options = {}) {
|
|
7102
8029
|
await this.ensureDirectories();
|
|
@@ -7401,7 +8328,7 @@ ${sanitized.text}
|
|
|
7401
8328
|
await this.ensureDirectories();
|
|
7402
8329
|
const now = /* @__PURE__ */ new Date();
|
|
7403
8330
|
const day = now.toISOString().slice(0, 10);
|
|
7404
|
-
const dir =
|
|
8331
|
+
const dir = path10.join(this.artifactsDir, day);
|
|
7405
8332
|
await mkdir2(dir, { recursive: true });
|
|
7406
8333
|
const id = `artifact-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
7407
8334
|
const fm = {
|
|
@@ -7426,7 +8353,7 @@ ${sanitized.text}
|
|
|
7426
8353
|
log.warn(`artifact content rejected for ${id}; violations=${sanitized.violations.join(", ")}`);
|
|
7427
8354
|
return "";
|
|
7428
8355
|
}
|
|
7429
|
-
const filePath =
|
|
8356
|
+
const filePath = path10.join(dir, `${id}.md`);
|
|
7430
8357
|
await this.writeStorageSecureFile(filePath, `${serializeFrontmatter(fm)}
|
|
7431
8358
|
|
|
7432
8359
|
${sanitized.text}
|
|
@@ -7697,7 +8624,7 @@ ${sanitized.text}
|
|
|
7697
8624
|
* per-process in-memory cache safe across process boundaries.
|
|
7698
8625
|
*/
|
|
7699
8626
|
invalidateColdMemoriesCache() {
|
|
7700
|
-
const coldRoot =
|
|
8627
|
+
const coldRoot = path10.join(this.baseDir, "cold");
|
|
7701
8628
|
_StorageManager.coldMemoriesCache.delete(coldRoot);
|
|
7702
8629
|
this.bumpColdWriteVersion();
|
|
7703
8630
|
invalidateAllForDir(this.baseDir);
|
|
@@ -7757,7 +8684,7 @@ ${sanitized.text}
|
|
|
7757
8684
|
const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
|
|
7758
8685
|
const parsed = parseFrontmatter(raw);
|
|
7759
8686
|
if (!parsed) return null;
|
|
7760
|
-
return {
|
|
8687
|
+
return rememberRawFrontmatter({
|
|
7761
8688
|
path: fullPath,
|
|
7762
8689
|
frontmatter: normalizeFrontmatterForPath(
|
|
7763
8690
|
parsed.frontmatter,
|
|
@@ -7765,7 +8692,7 @@ ${sanitized.text}
|
|
|
7765
8692
|
parsed.content
|
|
7766
8693
|
),
|
|
7767
8694
|
content: parsed.content
|
|
7768
|
-
};
|
|
8695
|
+
}, raw);
|
|
7769
8696
|
} catch (err) {
|
|
7770
8697
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
7771
8698
|
return null;
|
|
@@ -7812,7 +8739,7 @@ ${sanitized.text}
|
|
|
7812
8739
|
const correctionPaths = [];
|
|
7813
8740
|
const factPaths = [];
|
|
7814
8741
|
for (const filePath of filePaths) {
|
|
7815
|
-
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${
|
|
8742
|
+
if (filePath === this.correctionsDir || filePath.startsWith(`${this.correctionsDir}${path10.sep}`)) {
|
|
7816
8743
|
correctionPaths.push(filePath);
|
|
7817
8744
|
} else {
|
|
7818
8745
|
factPaths.push(filePath);
|
|
@@ -7873,9 +8800,9 @@ ${sanitized.text}
|
|
|
7873
8800
|
const root = this.archiveDir;
|
|
7874
8801
|
const readDir = async (dir) => {
|
|
7875
8802
|
try {
|
|
7876
|
-
const entries = await
|
|
8803
|
+
const entries = await readdir5(dir, { withFileTypes: true });
|
|
7877
8804
|
for (const entry of entries) {
|
|
7878
|
-
const fullPath =
|
|
8805
|
+
const fullPath = path10.join(dir, entry.name);
|
|
7879
8806
|
if (entry.isDirectory()) {
|
|
7880
8807
|
await readDir(fullPath);
|
|
7881
8808
|
} else if (entry.name.endsWith(".md")) {
|
|
@@ -7883,7 +8810,7 @@ ${sanitized.text}
|
|
|
7883
8810
|
const raw = await readMaybeEncryptedFile(fullPath, this._secureStoreKey, this.baseDir);
|
|
7884
8811
|
const parsed = parseFrontmatter(raw);
|
|
7885
8812
|
if (parsed) {
|
|
7886
|
-
memories.push({
|
|
8813
|
+
memories.push(rememberRawFrontmatter({
|
|
7887
8814
|
path: fullPath,
|
|
7888
8815
|
frontmatter: normalizeFrontmatterForPath(
|
|
7889
8816
|
parsed.frontmatter,
|
|
@@ -7891,7 +8818,7 @@ ${sanitized.text}
|
|
|
7891
8818
|
parsed.content
|
|
7892
8819
|
),
|
|
7893
8820
|
content: parsed.content
|
|
7894
|
-
});
|
|
8821
|
+
}, raw));
|
|
7895
8822
|
}
|
|
7896
8823
|
} catch (err) {
|
|
7897
8824
|
if (err instanceof SecureStoreLockedError) throw err;
|
|
@@ -7908,7 +8835,7 @@ ${sanitized.text}
|
|
|
7908
8835
|
return this.memoryReadStore.readMemoryByPath(filePath);
|
|
7909
8836
|
}
|
|
7910
8837
|
resolveTierRootDir(tier) {
|
|
7911
|
-
return tier === "cold" ?
|
|
8838
|
+
return tier === "cold" ? path10.join(this.baseDir, "cold") : this.baseDir;
|
|
7912
8839
|
}
|
|
7913
8840
|
resolveMemoryDateDir(memory) {
|
|
7914
8841
|
const preferred = memory.frontmatter.created || memory.frontmatter.updated;
|
|
@@ -7923,13 +8850,13 @@ ${sanitized.text}
|
|
|
7923
8850
|
buildTierMemoryPath(memory, tier) {
|
|
7924
8851
|
const root = this.resolveTierRootDir(tier);
|
|
7925
8852
|
if (this.isArtifactMemory(memory)) {
|
|
7926
|
-
return
|
|
8853
|
+
return path10.join(root, "artifacts", this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
7927
8854
|
}
|
|
7928
8855
|
if (memory.frontmatter.category === "correction") {
|
|
7929
|
-
return
|
|
8856
|
+
return path10.join(root, "corrections", `${memory.frontmatter.id}.md`);
|
|
7930
8857
|
}
|
|
7931
8858
|
const dir = categoryDirName(memory.frontmatter.category);
|
|
7932
|
-
return
|
|
8859
|
+
return path10.join(root, dir, this.resolveMemoryDateDir(memory), `${memory.frontmatter.id}.md`);
|
|
7933
8860
|
}
|
|
7934
8861
|
async writeMemoryFileAtomic(targetPath, memory) {
|
|
7935
8862
|
const fileContent = `${serializeFrontmatter(memory.frontmatter)}
|
|
@@ -7942,11 +8869,11 @@ ${memory.content}
|
|
|
7942
8869
|
}
|
|
7943
8870
|
async moveMemoryToPath(memory, targetPath) {
|
|
7944
8871
|
await this.writeMemoryFileAtomic(targetPath, memory);
|
|
7945
|
-
const sourcePath =
|
|
7946
|
-
const destPath =
|
|
8872
|
+
const sourcePath = path10.resolve(memory.path);
|
|
8873
|
+
const destPath = path10.resolve(targetPath);
|
|
7947
8874
|
if (sourcePath !== destPath) {
|
|
7948
8875
|
try {
|
|
7949
|
-
await
|
|
8876
|
+
await unlink3(memory.path);
|
|
7950
8877
|
} catch (err) {
|
|
7951
8878
|
const message = err instanceof Error ? err.message : String(err);
|
|
7952
8879
|
if (!message.includes("ENOENT")) {
|
|
@@ -7959,15 +8886,15 @@ ${memory.content}
|
|
|
7959
8886
|
}
|
|
7960
8887
|
async migrateMemoryToTier(memory, targetTier) {
|
|
7961
8888
|
const targetPath = this.buildTierMemoryPath(memory, targetTier);
|
|
7962
|
-
const sourcePath =
|
|
7963
|
-
const destPath =
|
|
8889
|
+
const sourcePath = path10.resolve(memory.path);
|
|
8890
|
+
const destPath = path10.resolve(targetPath);
|
|
7964
8891
|
if (sourcePath === destPath) {
|
|
7965
8892
|
return { changed: false, targetPath };
|
|
7966
8893
|
}
|
|
7967
8894
|
const existing = await this.readMemoryByPath(targetPath);
|
|
7968
8895
|
if (existing?.frontmatter.id === memory.frontmatter.id) {
|
|
7969
8896
|
try {
|
|
7970
|
-
await
|
|
8897
|
+
await unlink3(memory.path);
|
|
7971
8898
|
} catch (err) {
|
|
7972
8899
|
const message = err instanceof Error ? err.message : String(err);
|
|
7973
8900
|
if (!message.includes("ENOENT")) {
|
|
@@ -7987,7 +8914,7 @@ ${memory.content}
|
|
|
7987
8914
|
return { changed: true, targetPath };
|
|
7988
8915
|
}
|
|
7989
8916
|
get archiveDir() {
|
|
7990
|
-
return
|
|
8917
|
+
return path10.join(this.baseDir, "archive");
|
|
7991
8918
|
}
|
|
7992
8919
|
/**
|
|
7993
8920
|
* Archive a memory by moving it from facts/ to archive/YYYY-MM-DD/.
|
|
@@ -7998,7 +8925,7 @@ ${memory.content}
|
|
|
7998
8925
|
try {
|
|
7999
8926
|
const now = lifecycle?.at ?? /* @__PURE__ */ new Date();
|
|
8000
8927
|
const today = now.toISOString().slice(0, 10);
|
|
8001
|
-
const destDir =
|
|
8928
|
+
const destDir = path10.join(this.archiveDir, today);
|
|
8002
8929
|
await mkdir2(destDir, { recursive: true });
|
|
8003
8930
|
const updatedFm = {
|
|
8004
8931
|
...memory.frontmatter,
|
|
@@ -8010,9 +8937,9 @@ ${memory.content}
|
|
|
8010
8937
|
|
|
8011
8938
|
${memory.content}
|
|
8012
8939
|
`;
|
|
8013
|
-
const destPath =
|
|
8940
|
+
const destPath = path10.join(destDir, path10.basename(memory.path));
|
|
8014
8941
|
await this.writeStorageSecureFile(destPath, fileContent);
|
|
8015
|
-
await
|
|
8942
|
+
await unlink3(memory.path);
|
|
8016
8943
|
markProjectedMemoryPathInvalid(this.baseDir, memory.frontmatter.id);
|
|
8017
8944
|
this.invalidateAllMemoriesCache();
|
|
8018
8945
|
await this.appendGeneratedMemoryLifecycleEventFailOpen(
|
|
@@ -8040,7 +8967,7 @@ ${memory.content}
|
|
|
8040
8967
|
}
|
|
8041
8968
|
async readEntities() {
|
|
8042
8969
|
try {
|
|
8043
|
-
const entries = await
|
|
8970
|
+
const entries = await readdir5(this.entitiesDir);
|
|
8044
8971
|
return entries.filter((e) => e.endsWith(".md")).map((e) => e.replace(".md", ""));
|
|
8045
8972
|
} catch {
|
|
8046
8973
|
return [];
|
|
@@ -8092,7 +9019,7 @@ ${memory.content}
|
|
|
8092
9019
|
const memory = memories.find((m) => m.frontmatter.id === id);
|
|
8093
9020
|
if (!memory) return false;
|
|
8094
9021
|
try {
|
|
8095
|
-
await
|
|
9022
|
+
await unlink3(memory.path);
|
|
8096
9023
|
markProjectedMemoryPathInvalid(this.baseDir, id);
|
|
8097
9024
|
this.invalidateAllMemoriesCache();
|
|
8098
9025
|
this.bumpMemoryStatusVersion();
|
|
@@ -8158,7 +9085,7 @@ ${memory.content}
|
|
|
8158
9085
|
`;
|
|
8159
9086
|
await this.writeStorageSecureFile(memory.path, fileContent);
|
|
8160
9087
|
await this.patchHotMemoriesCache({ addedPath: memory.path });
|
|
8161
|
-
if (memory.path.includes(`${
|
|
9088
|
+
if (memory.path.includes(`${path10.sep}cold${path10.sep}`)) {
|
|
8162
9089
|
this.invalidateColdMemoriesCache();
|
|
8163
9090
|
}
|
|
8164
9091
|
try {
|
|
@@ -8213,7 +9140,7 @@ ${memory.content}
|
|
|
8213
9140
|
const expiresAt = new Date(m.frontmatter.expiresAt).getTime();
|
|
8214
9141
|
if (expiresAt < now) {
|
|
8215
9142
|
try {
|
|
8216
|
-
await
|
|
9143
|
+
await unlink3(m.path);
|
|
8217
9144
|
markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
|
|
8218
9145
|
this.bumpMemoryCorpusVersion();
|
|
8219
9146
|
deleted.push(m);
|
|
@@ -8229,7 +9156,7 @@ ${memory.content}
|
|
|
8229
9156
|
return deleted;
|
|
8230
9157
|
}
|
|
8231
9158
|
async loadBuffer() {
|
|
8232
|
-
const bufferPath =
|
|
9159
|
+
const bufferPath = path10.join(this.stateDir, "buffer.json");
|
|
8233
9160
|
try {
|
|
8234
9161
|
const raw = await this.readStorageSecureFile(bufferPath);
|
|
8235
9162
|
return JSON.parse(raw);
|
|
@@ -8241,11 +9168,11 @@ ${memory.content}
|
|
|
8241
9168
|
}
|
|
8242
9169
|
async saveBuffer(state) {
|
|
8243
9170
|
await this.ensureDirectories();
|
|
8244
|
-
const bufferPath =
|
|
9171
|
+
const bufferPath = path10.join(this.stateDir, "buffer.json");
|
|
8245
9172
|
await this.writeStorageSecureFile(bufferPath, JSON.stringify(state, null, 2));
|
|
8246
9173
|
}
|
|
8247
9174
|
async loadMeta() {
|
|
8248
|
-
const metaPath =
|
|
9175
|
+
const metaPath = path10.join(this.stateDir, "meta.json");
|
|
8249
9176
|
try {
|
|
8250
9177
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
8251
9178
|
const parsed = JSON.parse(raw);
|
|
@@ -8278,7 +9205,7 @@ ${memory.content}
|
|
|
8278
9205
|
}
|
|
8279
9206
|
async saveMeta(state) {
|
|
8280
9207
|
await this.ensureDirectories();
|
|
8281
|
-
const metaPath =
|
|
9208
|
+
const metaPath = path10.join(this.stateDir, "meta.json");
|
|
8282
9209
|
await this.writeStorageSecureFile(metaPath, JSON.stringify(state, null, 2));
|
|
8283
9210
|
}
|
|
8284
9211
|
async appendMemoryActionEvents(events) {
|
|
@@ -8345,7 +9272,7 @@ ${memory.content}
|
|
|
8345
9272
|
},
|
|
8346
9273
|
(payload) => this.appendStorageSecureFile(target, payload),
|
|
8347
9274
|
async () => {
|
|
8348
|
-
await mkdir2(
|
|
9275
|
+
await mkdir2(path10.dirname(target), { recursive: true });
|
|
8349
9276
|
}
|
|
8350
9277
|
);
|
|
8351
9278
|
}
|
|
@@ -8443,7 +9370,7 @@ ${memory.content}
|
|
|
8443
9370
|
async appendReextractJobs(events) {
|
|
8444
9371
|
if (events.length === 0) return 0;
|
|
8445
9372
|
await this.ensureDirectories();
|
|
8446
|
-
const filePath =
|
|
9373
|
+
const filePath = path10.join(this.stateDir, "reextract-jobs.jsonl");
|
|
8447
9374
|
const lines = events.map((event) => JSON.stringify(event)).join("\n") + "\n";
|
|
8448
9375
|
try {
|
|
8449
9376
|
await this.appendStorageSecureFile(filePath, lines);
|
|
@@ -8456,7 +9383,7 @@ ${memory.content}
|
|
|
8456
9383
|
}
|
|
8457
9384
|
async readReextractJobs(limit = 200) {
|
|
8458
9385
|
const safeLimit = Number.isFinite(limit) ? Math.max(1, Math.min(1e3, Math.floor(limit))) : 200;
|
|
8459
|
-
const filePath =
|
|
9386
|
+
const filePath = path10.join(this.stateDir, "reextract-jobs.jsonl");
|
|
8460
9387
|
try {
|
|
8461
9388
|
const raw = await this.readStorageSecureFile(filePath);
|
|
8462
9389
|
const lines = raw.split("\n").filter((line) => line.trim().length > 0);
|
|
@@ -8617,8 +9544,8 @@ ${memory.content}
|
|
|
8617
9544
|
activationState: "active"
|
|
8618
9545
|
});
|
|
8619
9546
|
await Promise.all([
|
|
8620
|
-
|
|
8621
|
-
|
|
9547
|
+
unlink3(this.compressionGuidelineDraftPath).catch(() => void 0),
|
|
9548
|
+
unlink3(this.compressionGuidelineDraftStatePath).catch(() => void 0)
|
|
8622
9549
|
]);
|
|
8623
9550
|
return true;
|
|
8624
9551
|
}
|
|
@@ -8686,7 +9613,7 @@ ${question}
|
|
|
8686
9613
|
|
|
8687
9614
|
**Context:** ${context}
|
|
8688
9615
|
`;
|
|
8689
|
-
const filePath =
|
|
9616
|
+
const filePath = path10.join(this.questionsDir, `${id}.md`);
|
|
8690
9617
|
await this.writeStorageSecureFile(filePath, content);
|
|
8691
9618
|
log.debug(`wrote question ${id} to ${filePath}`);
|
|
8692
9619
|
this.invalidateQuestionsCache();
|
|
@@ -8704,7 +9631,7 @@ ${question}
|
|
|
8704
9631
|
if (!match) return null;
|
|
8705
9632
|
const frontmatterStr = match[1];
|
|
8706
9633
|
const body = match[2].trim();
|
|
8707
|
-
const id = this.extractFrontmatterValue(frontmatterStr, "id") ??
|
|
9634
|
+
const id = this.extractFrontmatterValue(frontmatterStr, "id") ?? path10.basename(filePath, ".md");
|
|
8708
9635
|
const created = this.extractFrontmatterValue(frontmatterStr, "created") ?? "";
|
|
8709
9636
|
const priority = parseFloat(this.extractFrontmatterValue(frontmatterStr, "priority") ?? "0.5");
|
|
8710
9637
|
const resolved = this.extractFrontmatterValue(frontmatterStr, "resolved") === "true";
|
|
@@ -8738,14 +9665,14 @@ ${question}
|
|
|
8738
9665
|
async readIdentity(workspaceDir, namespace) {
|
|
8739
9666
|
const identityPath = this.identityFilePath(workspaceDir, namespace);
|
|
8740
9667
|
try {
|
|
8741
|
-
return await
|
|
9668
|
+
return await readFile2(identityPath, "utf-8");
|
|
8742
9669
|
} catch {
|
|
8743
9670
|
return "";
|
|
8744
9671
|
}
|
|
8745
9672
|
}
|
|
8746
9673
|
async writeIdentity(workspaceDir, content, namespace) {
|
|
8747
9674
|
const identityPath = this.identityFilePath(workspaceDir, namespace);
|
|
8748
|
-
await
|
|
9675
|
+
await writeFile2(identityPath, content, "utf-8");
|
|
8749
9676
|
log.debug(`wrote consolidated IDENTITY.md (${content.length} chars)`);
|
|
8750
9677
|
}
|
|
8751
9678
|
/** Max size for IDENTITY.md before we stop appending reflections (15KB leaves room under 20KB gateway limit) */
|
|
@@ -8756,22 +9683,22 @@ ${question}
|
|
|
8756
9683
|
const identityPath = this.identityFilePath(workspaceDir, opts?.namespace);
|
|
8757
9684
|
let existing = "";
|
|
8758
9685
|
try {
|
|
8759
|
-
existing = await
|
|
9686
|
+
existing = await readFile2(identityPath, "utf-8");
|
|
8760
9687
|
} catch {
|
|
8761
9688
|
}
|
|
8762
9689
|
const hygiene = opts?.hygiene;
|
|
8763
|
-
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(
|
|
9690
|
+
const rotateEnabled = hygiene?.enabled === true && hygiene.rotateEnabled === true && Array.isArray(hygiene.rotatePaths) && hygiene.rotatePaths.includes(path10.basename(identityPath));
|
|
8764
9691
|
if (rotateEnabled) {
|
|
8765
9692
|
const maxBytes = hygiene.rotateMaxBytes;
|
|
8766
9693
|
if (existing.length > maxBytes) {
|
|
8767
|
-
const archiveDir =
|
|
9694
|
+
const archiveDir = path10.join(workspaceDir, hygiene.archiveDir);
|
|
8768
9695
|
const { newContent } = await rotateMarkdownFileToArchive({
|
|
8769
9696
|
filePath: identityPath,
|
|
8770
9697
|
archiveDir,
|
|
8771
9698
|
archivePrefix: "IDENTITY",
|
|
8772
9699
|
keepTailChars: hygiene.rotateKeepTailChars
|
|
8773
9700
|
});
|
|
8774
|
-
await
|
|
9701
|
+
await writeFile2(identityPath, newContent, "utf-8");
|
|
8775
9702
|
existing = newContent;
|
|
8776
9703
|
log.info(`rotated IDENTITY.md to archive (size=${existing.length} chars, maxBytes=${maxBytes})`);
|
|
8777
9704
|
}
|
|
@@ -8804,7 +9731,7 @@ ${question}
|
|
|
8804
9731
|
|
|
8805
9732
|
${reflection}
|
|
8806
9733
|
`;
|
|
8807
|
-
await
|
|
9734
|
+
await writeFile2(identityPath, existing + section, "utf-8");
|
|
8808
9735
|
log.debug(`appended reflection to ${identityPath}`);
|
|
8809
9736
|
}
|
|
8810
9737
|
async readIdentityReflections() {
|
|
@@ -8860,7 +9787,7 @@ ${reflection}
|
|
|
8860
9787
|
* Deduplicates by target+label.
|
|
8861
9788
|
*/
|
|
8862
9789
|
async addEntityRelationship(name, rel) {
|
|
8863
|
-
const filePath =
|
|
9790
|
+
const filePath = path10.join(this.entitiesDir, `${name}.md`);
|
|
8864
9791
|
let entity;
|
|
8865
9792
|
try {
|
|
8866
9793
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -8883,7 +9810,7 @@ ${reflection}
|
|
|
8883
9810
|
* Prepends to the beginning, prunes oldest entries beyond maxEntries.
|
|
8884
9811
|
*/
|
|
8885
9812
|
async addEntityActivity(name, entry, maxEntries) {
|
|
8886
|
-
const filePath =
|
|
9813
|
+
const filePath = path10.join(this.entitiesDir, `${name}.md`);
|
|
8887
9814
|
let entity;
|
|
8888
9815
|
try {
|
|
8889
9816
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -8902,11 +9829,8 @@ ${reflection}
|
|
|
8902
9829
|
await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
|
|
8903
9830
|
this.invalidateKnowledgeIndexCache();
|
|
8904
9831
|
}
|
|
8905
|
-
/**
|
|
8906
|
-
* Add an alias to an entity file. Deduplicates.
|
|
8907
|
-
*/
|
|
8908
9832
|
async addEntityAlias(name, alias) {
|
|
8909
|
-
const filePath =
|
|
9833
|
+
const filePath = path10.join(this.entitiesDir, `${name}.md`);
|
|
8910
9834
|
let entity;
|
|
8911
9835
|
try {
|
|
8912
9836
|
const content = await this.readStorageSecureFile(filePath);
|
|
@@ -8922,6 +9846,7 @@ ${reflection}
|
|
|
8922
9846
|
entity.updated = (/* @__PURE__ */ new Date()).toISOString();
|
|
8923
9847
|
await this.writeStorageSecureFile(filePath, serializeEntityFile(entity, this.entitySchemas));
|
|
8924
9848
|
this.invalidateKnowledgeIndexCache();
|
|
9849
|
+
this.bumpMemoryStatusVersion();
|
|
8925
9850
|
}
|
|
8926
9851
|
async updateEntitySynthesis(name, synthesis, options = {}) {
|
|
8927
9852
|
return this.entityStore.updateEntitySynthesis(name, synthesis, options);
|
|
@@ -8933,7 +9858,7 @@ ${reflection}
|
|
|
8933
9858
|
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8934
9859
|
let synthesisTimelineCount;
|
|
8935
9860
|
try {
|
|
8936
|
-
const filePath =
|
|
9861
|
+
const filePath = path10.join(this.entitiesDir, `${name}.md`);
|
|
8937
9862
|
const content = await this.readStorageSecureFile(filePath);
|
|
8938
9863
|
synthesisTimelineCount = parseEntityFile(content, this.entitySchemas).timeline.length;
|
|
8939
9864
|
} catch (err) {
|
|
@@ -9016,7 +9941,7 @@ ${reflection}
|
|
|
9016
9941
|
if (!raw) continue;
|
|
9017
9942
|
const serialized = serializeEntityFile(parseEntityFile(raw, this.entitySchemas), this.entitySchemas);
|
|
9018
9943
|
if (raw.trimEnd() === serialized.trimEnd()) continue;
|
|
9019
|
-
await this.writeStorageSecureFile(
|
|
9944
|
+
await this.writeStorageSecureFile(path10.join(this.entitiesDir, `${entityName}.md`), serialized);
|
|
9020
9945
|
migrated += 1;
|
|
9021
9946
|
}
|
|
9022
9947
|
if (migrated > 0) {
|
|
@@ -9084,7 +10009,7 @@ ${reflection}
|
|
|
9084
10009
|
const updatedAt = new Date(m.frontmatter.updated).getTime();
|
|
9085
10010
|
if (updatedAt < cutoff) {
|
|
9086
10011
|
try {
|
|
9087
|
-
await
|
|
10012
|
+
await unlink3(m.path);
|
|
9088
10013
|
markProjectedMemoryPathInvalid(this.baseDir, m.frontmatter.id);
|
|
9089
10014
|
this.bumpMemoryCorpusVersion();
|
|
9090
10015
|
deleted.push(m);
|
|
@@ -9109,14 +10034,14 @@ ${reflection}
|
|
|
9109
10034
|
if (entries.length === 0) return 0;
|
|
9110
10035
|
const memories = await this.readAllMemories();
|
|
9111
10036
|
const memoryMap = new Map(memories.map((m) => [m.frontmatter.id, m]));
|
|
9112
|
-
const memoryPathMap = new Map(memories.map((m) => [
|
|
10037
|
+
const memoryPathMap = new Map(memories.map((m) => [path10.resolve(m.path), m]));
|
|
9113
10038
|
let updated = 0;
|
|
9114
10039
|
const prevVersion = this.getMemoryCorpusVersion();
|
|
9115
10040
|
const keyId = this.hotCacheKeyId();
|
|
9116
10041
|
const warm = this.hotMemoriesCacheEnabled && getCachedMemories(this.baseDir, prevVersion, keyId, this.hotCacheTtlMs()) !== null;
|
|
9117
10042
|
const appliedPatches = /* @__PURE__ */ new Map();
|
|
9118
10043
|
for (const entry of entries) {
|
|
9119
|
-
const memory = entry.memoryPath ? memoryPathMap.get(
|
|
10044
|
+
const memory = entry.memoryPath ? memoryPathMap.get(path10.resolve(entry.memoryPath)) : memoryMap.get(entry.memoryId);
|
|
9120
10045
|
if (!memory) continue;
|
|
9121
10046
|
const newFm = {
|
|
9122
10047
|
...memory.frontmatter,
|
|
@@ -9132,7 +10057,7 @@ ${memory.content}
|
|
|
9132
10057
|
if (warm) {
|
|
9133
10058
|
updateCacheOnWrite(this.baseDir, { ...memory, frontmatter: newFm }, keyId);
|
|
9134
10059
|
}
|
|
9135
|
-
appliedPatches.set(
|
|
10060
|
+
appliedPatches.set(path10.resolve(memory.path), {
|
|
9136
10061
|
accessCount: entry.newCount,
|
|
9137
10062
|
lastAccessed: entry.lastAccessed
|
|
9138
10063
|
});
|
|
@@ -9148,7 +10073,7 @@ ${memory.content}
|
|
|
9148
10073
|
const cur = getCachedMemories(this.baseDir, prevVersion, keyId);
|
|
9149
10074
|
if (cur) {
|
|
9150
10075
|
const reapplied = cur.map((m) => {
|
|
9151
|
-
const patch = appliedPatches.get(
|
|
10076
|
+
const patch = appliedPatches.get(path10.resolve(m.path));
|
|
9152
10077
|
return patch ? { ...m, frontmatter: { ...m.frontmatter, accessCount: patch.accessCount, lastAccessed: patch.lastAccessed } } : m;
|
|
9153
10078
|
});
|
|
9154
10079
|
setCachedMemories(this.baseDir, reapplied, produced, keyId, this.hotCacheTtlMs());
|
|
@@ -9180,7 +10105,7 @@ ${memory.content}
|
|
|
9180
10105
|
const pathsById = /* @__PURE__ */ new Map();
|
|
9181
10106
|
const filePathsById = /* @__PURE__ */ new Map();
|
|
9182
10107
|
for (const filePath of filePaths) {
|
|
9183
|
-
const memoryId =
|
|
10108
|
+
const memoryId = path10.basename(filePath, ".md");
|
|
9184
10109
|
if (!wantedIds.has(memoryId)) continue;
|
|
9185
10110
|
const paths = filePathsById.get(memoryId) ?? [];
|
|
9186
10111
|
paths.push(filePath);
|
|
@@ -9195,7 +10120,7 @@ ${memory.content}
|
|
|
9195
10120
|
qmdResultPathCandidates(this.baseDir, preferredPath)
|
|
9196
10121
|
);
|
|
9197
10122
|
const directMatch = existingPaths.find(
|
|
9198
|
-
(filePath) => directCandidates.has(
|
|
10123
|
+
(filePath) => directCandidates.has(path10.resolve(filePath))
|
|
9199
10124
|
);
|
|
9200
10125
|
if (directMatch) {
|
|
9201
10126
|
preferredMatches.push(directMatch);
|
|
@@ -9208,7 +10133,7 @@ ${memory.content}
|
|
|
9208
10133
|
candidates.add(candidate);
|
|
9209
10134
|
}
|
|
9210
10135
|
}
|
|
9211
|
-
const match = existingPaths.find((filePath) => candidates.has(
|
|
10136
|
+
const match = existingPaths.find((filePath) => candidates.has(path10.resolve(filePath)));
|
|
9212
10137
|
if (match) preferredMatches.push(match);
|
|
9213
10138
|
}
|
|
9214
10139
|
if (preferredMatches.length > 0) {
|
|
@@ -9427,14 +10352,14 @@ Reason: ${reason}`;
|
|
|
9427
10352
|
// Memory Summarization (Phase 4A)
|
|
9428
10353
|
// ---------------------------------------------------------------------------
|
|
9429
10354
|
get summariesDir() {
|
|
9430
|
-
return
|
|
10355
|
+
return path10.join(this.baseDir, "summaries");
|
|
9431
10356
|
}
|
|
9432
10357
|
/**
|
|
9433
10358
|
* Write a memory summary.
|
|
9434
10359
|
*/
|
|
9435
10360
|
async writeSummary(summary) {
|
|
9436
10361
|
await mkdir2(this.summariesDir, { recursive: true });
|
|
9437
|
-
const filePath =
|
|
10362
|
+
const filePath = path10.join(this.summariesDir, `${summary.id}.json`);
|
|
9438
10363
|
await this.writeStorageSecureFile(filePath, JSON.stringify(summary, null, 2));
|
|
9439
10364
|
log.debug(`wrote summary ${summary.id}`);
|
|
9440
10365
|
}
|
|
@@ -9443,11 +10368,11 @@ Reason: ${reason}`;
|
|
|
9443
10368
|
*/
|
|
9444
10369
|
async readSummaries() {
|
|
9445
10370
|
try {
|
|
9446
|
-
const files = await
|
|
10371
|
+
const files = await readdir5(this.summariesDir);
|
|
9447
10372
|
const summaries = [];
|
|
9448
10373
|
for (const file of files) {
|
|
9449
10374
|
if (!file.endsWith(".json")) continue;
|
|
9450
|
-
const filePath =
|
|
10375
|
+
const filePath = path10.join(this.summariesDir, file);
|
|
9451
10376
|
const raw = await this.readStorageSecureFile(filePath);
|
|
9452
10377
|
summaries.push(JSON.parse(raw));
|
|
9453
10378
|
}
|
|
@@ -9509,7 +10434,7 @@ ${memory.content}
|
|
|
9509
10434
|
* Save topic scores to meta.json.
|
|
9510
10435
|
*/
|
|
9511
10436
|
async saveTopics(topics) {
|
|
9512
|
-
const metaPath =
|
|
10437
|
+
const metaPath = path10.join(this.stateDir, "topics.json");
|
|
9513
10438
|
await mkdir2(this.stateDir, { recursive: true });
|
|
9514
10439
|
await this.writeStorageSecureFile(
|
|
9515
10440
|
metaPath,
|
|
@@ -9521,7 +10446,7 @@ ${memory.content}
|
|
|
9521
10446
|
* Load topic scores from meta.json.
|
|
9522
10447
|
*/
|
|
9523
10448
|
async loadTopics() {
|
|
9524
|
-
const metaPath =
|
|
10449
|
+
const metaPath = path10.join(this.stateDir, "topics.json");
|
|
9525
10450
|
try {
|
|
9526
10451
|
const raw = await this.readStorageSecureFile(metaPath);
|
|
9527
10452
|
return JSON.parse(raw);
|
|
@@ -9637,11 +10562,10 @@ export {
|
|
|
9637
10562
|
parseFrontmatterStringValue,
|
|
9638
10563
|
parseFrontmatter,
|
|
9639
10564
|
normalizeFrontmatterForPath,
|
|
9640
|
-
normalizeEntityName,
|
|
9641
10565
|
parseEntityFile,
|
|
9642
10566
|
serializeEntityFile,
|
|
9643
10567
|
buildEntitySchemaCacheKey,
|
|
9644
10568
|
isValidBufferSurpriseEvent,
|
|
9645
10569
|
StorageManager3 as StorageManager
|
|
9646
10570
|
};
|
|
9647
|
-
//# sourceMappingURL=chunk-
|
|
10571
|
+
//# sourceMappingURL=chunk-Q5VJUNXA.js.map
|