@remnic/core 9.38.3 → 9.40.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 +10 -10
- package/dist/access-admin-ops-surface.js +15 -15
- package/dist/access-authorization-probe.d.ts +10 -10
- package/dist/access-authorization-probe.js +16 -16
- package/dist/access-boundary.d.ts +10 -10
- package/dist/access-boundary.js +15 -15
- package/dist/access-cli.js +37 -37
- package/dist/access-extraction-force-flush.d.ts +10 -10
- package/dist/access-extraction-force-flush.js +15 -15
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +10 -10
- package/dist/access-http-lifecycle-flush.d.ts +10 -10
- package/dist/access-http.d.ts +10 -10
- package/dist/access-http.js +20 -20
- package/dist/access-identity-continuity-surface.d.ts +8 -8
- package/dist/access-identity-continuity-surface.js +15 -15
- package/dist/access-lcm-surface.d.ts +10 -10
- package/dist/access-lcm-surface.js +15 -15
- package/dist/access-mcp.d.ts +10 -10
- package/dist/access-mcp.js +18 -18
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-CWyL4Zai.d.ts → access-namespace-preflight-BbDJBq0e.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +15 -15
- package/dist/access-observe-write-surface.d.ts +10 -10
- package/dist/access-observe-write-surface.js +15 -15
- package/dist/access-operations-batch.js +16 -16
- package/dist/access-operations.d.ts +10 -10
- package/dist/access-operations.js +17 -17
- package/dist/access-recall-concurrency.d.ts +10 -10
- package/dist/access-recall-concurrency.js +15 -15
- package/dist/access-recall-response.d.ts +10 -10
- package/dist/access-recall-response.js +15 -15
- package/dist/access-recall-surface.d.ts +10 -10
- package/dist/access-recall-surface.js +15 -15
- package/dist/{access-service-C2WYT1pO.d.ts → access-service-B2h6rITp.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +10 -10
- package/dist/access-service.d.ts +10 -10
- package/dist/access-service.js +15 -15
- package/dist/access-surface-catalog.d.ts +10 -10
- package/dist/access-wearables-meetings-surface.d.ts +3 -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 +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +8 -8
- package/dist/briefing.d.ts +2 -2
- package/dist/briefing.js +6 -6
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-Du4bpZWM.d.ts → catalog-Bz2LxGNb.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 +7 -7
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-6TF4M2LB.js → chunk-3C2TGBDB.js} +2 -2
- package/dist/{chunk-T3FVAHM7.js → chunk-4VXGRIH6.js} +286 -7
- package/dist/chunk-4VXGRIH6.js.map +1 -0
- package/dist/{chunk-UBWOCQ3I.js → chunk-5N26WBPQ.js} +2 -2
- package/dist/{chunk-PRP3M5Y3.js → chunk-5TOV3D6U.js} +2 -2
- package/dist/{chunk-DKKZ6SO2.js → chunk-6GIMMNVK.js} +22 -45
- package/dist/chunk-6GIMMNVK.js.map +1 -0
- package/dist/{chunk-ILGC6JH6.js → chunk-7RJUVTVF.js} +16 -1
- package/dist/chunk-7RJUVTVF.js.map +1 -0
- package/dist/{chunk-BGLDKAYY.js → chunk-AXHMCRS3.js} +2 -2
- package/dist/{chunk-AF4HOJMH.js → chunk-AYICY7IE.js} +2 -2
- package/dist/{chunk-6D5D5A4M.js → chunk-BI6RE2ZS.js} +2 -2
- package/dist/{chunk-6BNE62PM.js → chunk-EBWOOJEP.js} +2 -2
- package/dist/{chunk-PYTUBWAS.js → chunk-F2KMAFEB.js} +2 -2
- package/dist/{chunk-DMT2RGAZ.js → chunk-FXGLFGIG.js} +3 -3
- package/dist/{chunk-44SRDUI4.js → chunk-GLBK35BT.js} +20 -20
- package/dist/{chunk-HXDWZFNP.js → chunk-GSL47NPM.js} +1 -1
- package/dist/chunk-GSL47NPM.js.map +1 -0
- package/dist/{chunk-RY2XZTFN.js → chunk-JDXDA5OO.js} +2 -2
- package/dist/{chunk-5ZGHUWTT.js → chunk-KPJQTSRJ.js} +2 -2
- package/dist/{chunk-P2NCTIKE.js → chunk-KW4ZFTUS.js} +8 -7
- package/dist/chunk-KW4ZFTUS.js.map +1 -0
- package/dist/{chunk-POQGCS4H.js → chunk-KYYRJVE6.js} +2 -2
- package/dist/{chunk-IB26HVAJ.js → chunk-MOHZIKZM.js} +2 -2
- package/dist/chunk-MOHZIKZM.js.map +1 -0
- package/dist/{chunk-IJAQCBGV.js → chunk-MSXWUXFE.js} +2 -2
- package/dist/{chunk-WET4M3R6.js → chunk-MVW234LO.js} +2 -2
- package/dist/{chunk-RY7CHVGE.js → chunk-NEO4QCID.js} +2 -2
- package/dist/{chunk-JYLN6KHH.js → chunk-NHRNYJPJ.js} +7 -9
- package/dist/{chunk-JYLN6KHH.js.map → chunk-NHRNYJPJ.js.map} +1 -1
- package/dist/{chunk-4TAD3B56.js → chunk-OEXKYYVH.js} +2 -2
- package/dist/{chunk-RMOSIGGI.js → chunk-Q6QYN56R.js} +2 -2
- package/dist/{chunk-FE7MUX7M.js → chunk-QY66HJ4X.js} +2 -2
- package/dist/{chunk-KWU6MAD4.js → chunk-R4Q7CQGZ.js} +2 -2
- package/dist/{chunk-IMEXE4XS.js → chunk-RK6WMGTX.js} +7 -7
- package/dist/{chunk-C3HUHJ4G.js → chunk-SD4A6IZY.js} +3 -3
- package/dist/{chunk-MYXWPG5O.js → chunk-TXBU7YDV.js} +2 -2
- package/dist/{chunk-YVMVVE4B.js → chunk-UIVRPXRR.js} +2 -2
- package/dist/{chunk-NBAEGFXT.js → chunk-WGCTFGLW.js} +11 -14
- package/dist/chunk-WGCTFGLW.js.map +1 -0
- package/dist/{chunk-XBJC7RTW.js → chunk-XPQ5ZTL6.js} +19 -19
- package/dist/{chunk-XEM77GGX.js → chunk-XYXYEDPL.js} +10 -10
- package/dist/{chunk-HUS2XN3T.js → chunk-YFHMW37V.js} +2 -2
- package/dist/{chunk-HKF5VASQ.js → chunk-ZA53AZND.js} +17 -6
- package/dist/chunk-ZA53AZND.js.map +1 -0
- package/dist/{chunk-Q3CVY7LV.js → chunk-ZERSIPFC.js} +202 -232
- package/dist/chunk-ZERSIPFC.js.map +1 -0
- package/dist/{cli-jkmzdT8P.d.ts → cli-CO8qKPM5.d.ts} +5 -5
- package/dist/cli.d.ts +12 -12
- package/dist/cli.js +32 -32
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +6 -6
- 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 +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +6 -6
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +6 -6
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +2 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/corpus-watermark.js +8 -8
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-retrieval.js +6 -6
- package/dist/entity-schema.d.ts +1 -1
- package/dist/explicit-capture.d.ts +8 -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-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +6 -6
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/index.d.ts +19 -19
- package/dist/index.js +37 -37
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +6 -6
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +6 -6
- package/dist/maintenance/rebuild-memory-projection.d.ts +18 -11
- package/dist/maintenance/rebuild-memory-projection.js +7 -7
- package/dist/{maintenance-Ha9Dg93M.d.ts → maintenance-D2jkiT9r.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +10 -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 +9 -2
- package/dist/memory-lifecycle-ledger-utils.js +5 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/migrate.js +7 -7
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/namespaces/storage.js +6 -6
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/offline-sync-impression-drain.js +5 -3
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-corpus.js +9 -9
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +13 -13
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +11 -11
- package/dist/{orchestrator-DV_u9bfx.d.ts → orchestrator-SHaRXCUF.d.ts} +7 -7
- package/dist/orchestrator.d.ts +8 -8
- package/dist/orchestrator.js +37 -37
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-DSLWFeHz.d.ts → pipeline-B5_tS1Yc.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-BSDGyr0H.d.ts → qmd-CkEV_SPG.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-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 +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-C4zY-a2K.d.ts → scope-profiles-DeAdnocS.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-ChKRYpRO.d.ts → semantic-consolidation-DR_h-JUD.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +7 -7
- package/dist/semantic-rule-promotion.js +6 -6
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +6 -6
- package/dist/{service-JxXXY-Eh.d.ts → service-BTb94UPJ.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +6 -6
- package/dist/{storage-BbH4HDUS.d.ts → storage-ax2EIsVS.d.ts} +2 -2
- package/dist/storage-guards.d.ts +9 -2
- package/dist/storage-guards.js +3 -1
- package/dist/storage.d.ts +2 -2
- package/dist/storage.js +5 -5
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +2 -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 +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-5398p7Zm.d.ts → types-CCGL-9NX.d.ts} +1 -7
- 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 +6 -6
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/access-http.test.ts +60 -0
- package/src/access-http.ts +2 -1
- package/src/access-surface-catalog.test.ts +1 -0
- package/src/config.test.ts +4 -0
- package/src/config.ts +1 -5
- package/src/maintenance/projection-support.ts +192 -1
- package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +5 -16
- package/src/maintenance/rebuild-memory-projection.ts +28 -60
- package/src/memory-lifecycle-ledger-utils.ts +21 -0
- package/src/orchestration/maintenance.test.ts +30 -0
- package/src/storage/memory-lifecycle-ledger-access.ts +198 -1
- package/src/storage/secure-line-reader.ts +3 -1
- package/src/storage-guards.ts +28 -7
- package/src/storage-memory-lifecycle-ledger.test.ts +291 -1
- package/src/storage.ts +11 -21
- package/src/types.ts +1 -7
- package/dist/chunk-DKKZ6SO2.js.map +0 -1
- package/dist/chunk-HKF5VASQ.js.map +0 -1
- package/dist/chunk-HXDWZFNP.js.map +0 -1
- package/dist/chunk-IB26HVAJ.js.map +0 -1
- package/dist/chunk-ILGC6JH6.js.map +0 -1
- package/dist/chunk-NBAEGFXT.js.map +0 -1
- package/dist/chunk-P2NCTIKE.js.map +0 -1
- package/dist/chunk-Q3CVY7LV.js.map +0 -1
- package/dist/chunk-T3FVAHM7.js.map +0 -1
- /package/dist/{chunk-6TF4M2LB.js.map → chunk-3C2TGBDB.js.map} +0 -0
- /package/dist/{chunk-UBWOCQ3I.js.map → chunk-5N26WBPQ.js.map} +0 -0
- /package/dist/{chunk-PRP3M5Y3.js.map → chunk-5TOV3D6U.js.map} +0 -0
- /package/dist/{chunk-BGLDKAYY.js.map → chunk-AXHMCRS3.js.map} +0 -0
- /package/dist/{chunk-AF4HOJMH.js.map → chunk-AYICY7IE.js.map} +0 -0
- /package/dist/{chunk-6D5D5A4M.js.map → chunk-BI6RE2ZS.js.map} +0 -0
- /package/dist/{chunk-6BNE62PM.js.map → chunk-EBWOOJEP.js.map} +0 -0
- /package/dist/{chunk-PYTUBWAS.js.map → chunk-F2KMAFEB.js.map} +0 -0
- /package/dist/{chunk-DMT2RGAZ.js.map → chunk-FXGLFGIG.js.map} +0 -0
- /package/dist/{chunk-44SRDUI4.js.map → chunk-GLBK35BT.js.map} +0 -0
- /package/dist/{chunk-RY2XZTFN.js.map → chunk-JDXDA5OO.js.map} +0 -0
- /package/dist/{chunk-5ZGHUWTT.js.map → chunk-KPJQTSRJ.js.map} +0 -0
- /package/dist/{chunk-POQGCS4H.js.map → chunk-KYYRJVE6.js.map} +0 -0
- /package/dist/{chunk-IJAQCBGV.js.map → chunk-MSXWUXFE.js.map} +0 -0
- /package/dist/{chunk-WET4M3R6.js.map → chunk-MVW234LO.js.map} +0 -0
- /package/dist/{chunk-RY7CHVGE.js.map → chunk-NEO4QCID.js.map} +0 -0
- /package/dist/{chunk-4TAD3B56.js.map → chunk-OEXKYYVH.js.map} +0 -0
- /package/dist/{chunk-RMOSIGGI.js.map → chunk-Q6QYN56R.js.map} +0 -0
- /package/dist/{chunk-FE7MUX7M.js.map → chunk-QY66HJ4X.js.map} +0 -0
- /package/dist/{chunk-KWU6MAD4.js.map → chunk-R4Q7CQGZ.js.map} +0 -0
- /package/dist/{chunk-IMEXE4XS.js.map → chunk-RK6WMGTX.js.map} +0 -0
- /package/dist/{chunk-C3HUHJ4G.js.map → chunk-SD4A6IZY.js.map} +0 -0
- /package/dist/{chunk-MYXWPG5O.js.map → chunk-TXBU7YDV.js.map} +0 -0
- /package/dist/{chunk-YVMVVE4B.js.map → chunk-UIVRPXRR.js.map} +0 -0
- /package/dist/{chunk-XBJC7RTW.js.map → chunk-XPQ5ZTL6.js.map} +0 -0
- /package/dist/{chunk-XEM77GGX.js.map → chunk-XYXYEDPL.js.map} +0 -0
- /package/dist/{chunk-HUS2XN3T.js.map → chunk-YFHMW37V.js.map} +0 -0
|
@@ -6,7 +6,22 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import path from "node:path";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
openProjectionReadonly,
|
|
11
|
+
readProjectedEntityMentions,
|
|
12
|
+
readProjectedGovernanceRecord,
|
|
13
|
+
readProjectedNativeKnowledgeChunks,
|
|
14
|
+
type MemoryProjectionGovernanceAppliedActionRow,
|
|
15
|
+
type MemoryProjectionGovernanceReviewQueueRow,
|
|
16
|
+
type ProjectedEntityMentionRow,
|
|
17
|
+
type ProjectedNativeKnowledgeChunkRow,
|
|
18
|
+
} from "../memory-projection-store.js";
|
|
19
|
+
import {
|
|
20
|
+
isFrontmatterDerivedLifecycleEventType,
|
|
21
|
+
memoryLifecycleEventProjectionIdentity,
|
|
22
|
+
} from "../memory-lifecycle-ledger-utils.js";
|
|
23
|
+
import type { MemoryLifecycleEvent } from "../types.js";
|
|
24
|
+
import type { BetterSqlite3Database } from "../runtime/better-sqlite.js";
|
|
10
25
|
|
|
11
26
|
/** Read the projection meta rebuiltAt timestamp; null when absent/unopenable. */
|
|
12
27
|
export function readProjectionRebuiltAt(memoryDir: string): string | null {
|
|
@@ -24,6 +39,172 @@ export function readProjectionRebuiltAt(memoryDir: string): string | null {
|
|
|
24
39
|
}
|
|
25
40
|
}
|
|
26
41
|
|
|
42
|
+
/** Format projection age description string for fallback warning telemetry. */
|
|
43
|
+
export function formatProjectionAge(rebuiltAt: string | null): string {
|
|
44
|
+
if (!rebuiltAt) return "projection never rebuilt";
|
|
45
|
+
const ageMs = Date.now() - Date.parse(rebuiltAt);
|
|
46
|
+
if (!Number.isFinite(ageMs)) return `projection rebuiltAt=${rebuiltAt}`;
|
|
47
|
+
const ageMin = Math.max(0, Math.round(ageMs / 60_000));
|
|
48
|
+
const age = ageMin >= 120 ? `${Math.round(ageMin / 60)}h` : `${ageMin}m`;
|
|
49
|
+
return `projection ${age} stale, last rebuilt ${rebuiltAt}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface ProjectionLifecycleLedgerHighWater {
|
|
53
|
+
eventCount: number;
|
|
54
|
+
sourceEventCount: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readProjectionHighWaterFromDb(
|
|
58
|
+
db: BetterSqlite3Database,
|
|
59
|
+
): ProjectionLifecycleLedgerHighWater | null {
|
|
60
|
+
const sourceRow = db.prepare(
|
|
61
|
+
"SELECT value FROM meta WHERE key = 'sourceLifecycleLedgerEventCount'",
|
|
62
|
+
).get() as { value?: unknown } | undefined;
|
|
63
|
+
const projectedRow = db.prepare(
|
|
64
|
+
"SELECT value FROM meta WHERE key = 'projectedLifecycleLedgerEventCount'",
|
|
65
|
+
).get() as { value?: unknown } | undefined;
|
|
66
|
+
if (
|
|
67
|
+
typeof sourceRow?.value !== "string"
|
|
68
|
+
|| !/^\d+$/.test(sourceRow.value)
|
|
69
|
+
|| typeof projectedRow?.value !== "string"
|
|
70
|
+
|| !/^\d+$/.test(projectedRow.value)
|
|
71
|
+
) return null;
|
|
72
|
+
const sourceEventCount = Number(sourceRow.value);
|
|
73
|
+
const eventCount = Number(projectedRow.value);
|
|
74
|
+
if (!Number.isSafeInteger(sourceEventCount) || !Number.isSafeInteger(eventCount)) return null;
|
|
75
|
+
return { eventCount, sourceEventCount };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function readProjectionLifecycleLedgerHighWater(
|
|
79
|
+
memoryDir: string,
|
|
80
|
+
): ProjectionLifecycleLedgerHighWater | null {
|
|
81
|
+
const db = openProjectionReadonly(memoryDir);
|
|
82
|
+
if (!db) return null;
|
|
83
|
+
try {
|
|
84
|
+
return readProjectionHighWaterFromDb(db);
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
} finally {
|
|
88
|
+
db.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface ProjectionLifecycleLedgerLagTracker {
|
|
93
|
+
highWater: ProjectionLifecycleLedgerHighWater;
|
|
94
|
+
record(event: MemoryLifecycleEvent): { unique: boolean; projected: boolean };
|
|
95
|
+
close(): void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function openProjectionLifecycleLedgerLagTracker(
|
|
99
|
+
memoryDir: string,
|
|
100
|
+
): ProjectionLifecycleLedgerLagTracker | null {
|
|
101
|
+
const db = openProjectionReadonly(memoryDir);
|
|
102
|
+
if (!db) return null;
|
|
103
|
+
try {
|
|
104
|
+
const highWater = readProjectionHighWaterFromDb(db);
|
|
105
|
+
if (!highWater) {
|
|
106
|
+
db.close();
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
db.pragma("temp_store = FILE");
|
|
110
|
+
db.exec(`
|
|
111
|
+
CREATE TEMP TABLE projection_lag_current_identity (
|
|
112
|
+
identity TEXT PRIMARY KEY
|
|
113
|
+
) WITHOUT ROWID;
|
|
114
|
+
`);
|
|
115
|
+
db.pragma("temp.cache_size = -1024");
|
|
116
|
+
const insertCurrent = db.prepare(
|
|
117
|
+
"INSERT OR IGNORE INTO projection_lag_current_identity(identity) VALUES (?)",
|
|
118
|
+
);
|
|
119
|
+
const findProjectedEvent = db.prepare(
|
|
120
|
+
"SELECT 1 FROM memory_timeline WHERE event_id = ? LIMIT 1",
|
|
121
|
+
);
|
|
122
|
+
const findProjectedFrontmatterEvent = db.prepare(`
|
|
123
|
+
SELECT 1
|
|
124
|
+
FROM memory_timeline
|
|
125
|
+
WHERE memory_id = ? AND event_type = ? AND timestamp = ?
|
|
126
|
+
LIMIT 1
|
|
127
|
+
`);
|
|
128
|
+
return {
|
|
129
|
+
highWater,
|
|
130
|
+
record(event) {
|
|
131
|
+
if (!event.memoryId.trim()) return { unique: false, projected: false };
|
|
132
|
+
const identity = memoryLifecycleEventProjectionIdentity(event);
|
|
133
|
+
const inserted = insertCurrent.run(identity);
|
|
134
|
+
if (inserted.changes === 0) return { unique: false, projected: false };
|
|
135
|
+
const projected = isFrontmatterDerivedLifecycleEventType(event.eventType)
|
|
136
|
+
? findProjectedFrontmatterEvent.get(event.memoryId, event.eventType, event.timestamp)
|
|
137
|
+
: findProjectedEvent.get(event.eventId);
|
|
138
|
+
return { unique: true, projected: projected !== undefined };
|
|
139
|
+
},
|
|
140
|
+
close: () => db.close(),
|
|
141
|
+
};
|
|
142
|
+
} catch {
|
|
143
|
+
db.close();
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Write the high-water ledger marker counts to projection meta store. */
|
|
148
|
+
export function writeProjectionMetaHighWater(
|
|
149
|
+
insertMeta: { run: (key: string, value: string) => void },
|
|
150
|
+
sourceLifecycleLedgerEventCount: number,
|
|
151
|
+
timelineRows: MemoryLifecycleEvent[],
|
|
152
|
+
): void {
|
|
153
|
+
insertMeta.run("sourceLifecycleLedgerEventCount", String(sourceLifecycleLedgerEventCount));
|
|
154
|
+
insertMeta.run(
|
|
155
|
+
"projectedLifecycleLedgerEventCount",
|
|
156
|
+
String(new Set(timelineRows.map(memoryLifecycleEventProjectionIdentity)).size),
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function readProjectedEntityMentionRows(
|
|
161
|
+
memoryDir: string,
|
|
162
|
+
): { projectionExists: boolean; rows: ProjectedEntityMentionRow[] } {
|
|
163
|
+
const rows = readProjectedEntityMentions(memoryDir);
|
|
164
|
+
if (rows === null) return { projectionExists: false, rows: [] };
|
|
165
|
+
return { projectionExists: true, rows };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function readProjectedNativeKnowledgeRows(
|
|
169
|
+
memoryDir: string,
|
|
170
|
+
): { projectionExists: boolean; rows: ProjectedNativeKnowledgeChunkRow[] } {
|
|
171
|
+
const rows = readProjectedNativeKnowledgeChunks(memoryDir);
|
|
172
|
+
if (rows === null) return { projectionExists: false, rows: [] };
|
|
173
|
+
return { projectionExists: true, rows };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function readProjectedGovernanceRows(memoryDir: string): {
|
|
177
|
+
projectionExists: boolean;
|
|
178
|
+
runId: string | null;
|
|
179
|
+
summary: unknown;
|
|
180
|
+
metrics: unknown;
|
|
181
|
+
reviewQueueRows: MemoryProjectionGovernanceReviewQueueRow[];
|
|
182
|
+
appliedActionRows: MemoryProjectionGovernanceAppliedActionRow[];
|
|
183
|
+
report: string;
|
|
184
|
+
} {
|
|
185
|
+
const record = readProjectedGovernanceRecord(memoryDir);
|
|
186
|
+
if (record === null) {
|
|
187
|
+
return {
|
|
188
|
+
projectionExists: false,
|
|
189
|
+
runId: null,
|
|
190
|
+
summary: undefined,
|
|
191
|
+
metrics: undefined,
|
|
192
|
+
reviewQueueRows: [],
|
|
193
|
+
appliedActionRows: [],
|
|
194
|
+
report: "",
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
projectionExists: true,
|
|
199
|
+
runId: record.runId,
|
|
200
|
+
summary: record.summary,
|
|
201
|
+
metrics: record.metrics,
|
|
202
|
+
reviewQueueRows: record.reviewQueueRows,
|
|
203
|
+
appliedActionRows: record.appliedActionRows,
|
|
204
|
+
report: record.report,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
27
208
|
/**
|
|
28
209
|
* Assert an optionally injected storage (e.g. the daemon's live unlocked
|
|
29
210
|
* secure-store instance) is rooted at `memoryDir`, and hand it back. Shared
|
|
@@ -54,3 +235,13 @@ export interface SkippedDuplicateMemory {
|
|
|
54
235
|
export interface SkippedBlankIdMemory {
|
|
55
236
|
path: string;
|
|
56
237
|
}
|
|
238
|
+
export interface SkippedDuplicateTimelineEvent {
|
|
239
|
+
eventId: string;
|
|
240
|
+
keptPath: string;
|
|
241
|
+
skippedPath: string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface SkippedBlankIdTimelineEvent {
|
|
245
|
+
eventId: string;
|
|
246
|
+
path: string;
|
|
247
|
+
}
|
|
@@ -11,23 +11,14 @@ import {
|
|
|
11
11
|
buildLifecycleEventsForMemory,
|
|
12
12
|
compareMemoryLifecycleEvents,
|
|
13
13
|
sortMemoryLifecycleEvents,
|
|
14
|
+
isFrontmatterDerivedLifecycleEventType,
|
|
15
|
+
memoryLifecycleEventProjectionIdentity,
|
|
14
16
|
memoryLifecycleLedgerLockPath,
|
|
15
17
|
MEMORY_LIFECYCLE_LEDGER_LOCK_STALE_MS,
|
|
16
18
|
} from "../memory-lifecycle-ledger-utils.js";
|
|
17
19
|
import { withHeldFileLock, type HeldFileLockOptions, type HeldFileLockController } from "../utils/serialize-mutations.js";
|
|
18
20
|
import { probeEncryptedRegularFileHeader } from "../secure-store/secure-fs.js";
|
|
19
21
|
|
|
20
|
-
/**
|
|
21
|
-
* Event types `buildLifecycleEventsForMemory` reconstructs from frontmatter.
|
|
22
|
-
* Everything else in the ledger is append-only history with no frontmatter
|
|
23
|
-
* equivalent and must be carried over by a preserving rebuild (issue #1910).
|
|
24
|
-
*/
|
|
25
|
-
const FRONTMATTER_DERIVED_EVENT_TYPES: Record<string, true> = {
|
|
26
|
-
created: true,
|
|
27
|
-
updated: true,
|
|
28
|
-
superseded: true,
|
|
29
|
-
archived: true,
|
|
30
|
-
};
|
|
31
22
|
|
|
32
23
|
export interface RebuildMemoryLifecycleLedgerOptions {
|
|
33
24
|
memoryDir: string;
|
|
@@ -322,17 +313,15 @@ export async function rebuildMemoryLifecycleLedger(
|
|
|
322
313
|
// retried append of the same logical event still collapses to one. Rows
|
|
323
314
|
// that are not frontmatter-derived are append-only history with no
|
|
324
315
|
// reconstruction — always carried over, deduped by eventId in the merge.
|
|
325
|
-
const
|
|
326
|
-
`${event.memoryId}\u0000${event.eventType}\u0000${event.timestamp}`;
|
|
327
|
-
const reconstructedKeys = new Set(events.map(contentKey));
|
|
316
|
+
const reconstructedKeys = new Set(events.map(memoryLifecycleEventProjectionIdentity));
|
|
328
317
|
const racedFrontmatterByKey = new Map<string, MemoryLifecycleEvent>();
|
|
329
318
|
const appendOnly: MemoryLifecycleEvent[] = [];
|
|
330
319
|
for (const event of existing) {
|
|
331
|
-
if (!
|
|
320
|
+
if (!isFrontmatterDerivedLifecycleEventType(event.eventType)) {
|
|
332
321
|
appendOnly.push(event);
|
|
333
322
|
continue;
|
|
334
323
|
}
|
|
335
|
-
const key =
|
|
324
|
+
const key = memoryLifecycleEventProjectionIdentity(event);
|
|
336
325
|
if (reconstructedKeys.has(key)) continue; // collapsed into reconstruction
|
|
337
326
|
if (!racedFrontmatterByKey.has(key)) racedFrontmatterByKey.set(key, event);
|
|
338
327
|
}
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
buildLifecycleEventsForMemory,
|
|
19
19
|
inferMemoryStatus,
|
|
20
20
|
MEMORY_LIFECYCLE_EVENT_SORT_ORDER,
|
|
21
|
+
memoryLifecycleEventProjectionIdentity,
|
|
21
22
|
sortMemoryLifecycleEvents,
|
|
22
23
|
toMemoryPathRel,
|
|
23
24
|
} from "../memory-lifecycle-ledger-utils.js";
|
|
@@ -55,20 +56,26 @@ export interface RebuildMemoryProjectionOptions {
|
|
|
55
56
|
updatedBefore?: string;
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
export type {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
export type {
|
|
60
|
+
SkippedBlankIdMemory,
|
|
61
|
+
SkippedBlankIdTimelineEvent,
|
|
62
|
+
SkippedDuplicateMemory,
|
|
63
|
+
SkippedDuplicateTimelineEvent,
|
|
64
|
+
} from "./projection-support.js";
|
|
65
|
+
import {
|
|
66
|
+
assertInjectedStorageRooted,
|
|
67
|
+
readProjectedEntityMentionRows,
|
|
68
|
+
readProjectedGovernanceRows,
|
|
69
|
+
readProjectedNativeKnowledgeRows,
|
|
70
|
+
writeProjectionMetaHighWater,
|
|
71
|
+
} from "./projection-support.js";
|
|
72
|
+
import type {
|
|
73
|
+
SkippedBlankIdMemory,
|
|
74
|
+
SkippedBlankIdTimelineEvent,
|
|
75
|
+
SkippedDuplicateMemory,
|
|
76
|
+
SkippedDuplicateTimelineEvent,
|
|
77
|
+
} from "./projection-support.js";
|
|
67
78
|
|
|
68
|
-
export interface SkippedBlankIdTimelineEvent {
|
|
69
|
-
eventId: string;
|
|
70
|
-
path: string;
|
|
71
|
-
}
|
|
72
79
|
|
|
73
80
|
export interface RebuildMemoryProjectionResult {
|
|
74
81
|
dryRun: boolean;
|
|
@@ -569,6 +576,7 @@ async function loadAuthoritativeProjectionSnapshot(options: {
|
|
|
569
576
|
}
|
|
570
577
|
| null;
|
|
571
578
|
usedLifecycleLedger: boolean;
|
|
579
|
+
sourceLifecycleLedgerEventCount: number;
|
|
572
580
|
scope: {
|
|
573
581
|
updatedAfter: string | null;
|
|
574
582
|
updatedBefore: string | null;
|
|
@@ -607,6 +615,9 @@ async function loadAuthoritativeProjectionSnapshot(options: {
|
|
|
607
615
|
);
|
|
608
616
|
const events = deduplicatedTimeline.events;
|
|
609
617
|
const usedLifecycleLedger = timeline.usedLifecycleLedger;
|
|
618
|
+
const sourceLifecycleLedgerEventCount = new Set(
|
|
619
|
+
lifecycleEvents.map(memoryLifecycleEventProjectionIdentity),
|
|
620
|
+
).size;
|
|
610
621
|
const currentRows = projectionMemories.map((memory) => toCurrentStateRow(options.memoryDir, memory));
|
|
611
622
|
const scope = normalizeProjectionScope(options);
|
|
612
623
|
const scopedMemories = filterMemoriesForProjectionScope(projectionMemories, scope);
|
|
@@ -645,6 +656,7 @@ async function loadAuthoritativeProjectionSnapshot(options: {
|
|
|
645
656
|
nativeKnowledgeRows,
|
|
646
657
|
governance,
|
|
647
658
|
usedLifecycleLedger,
|
|
659
|
+
sourceLifecycleLedgerEventCount,
|
|
648
660
|
scope,
|
|
649
661
|
skippedDuplicateMemories: selected.skippedDuplicateMemories,
|
|
650
662
|
skippedBlankIdMemories: selected.skippedBlankIdMemories,
|
|
@@ -733,53 +745,6 @@ function readProjectedTimelineRows(
|
|
|
733
745
|
}
|
|
734
746
|
}
|
|
735
747
|
|
|
736
|
-
function readProjectedEntityMentionRows(
|
|
737
|
-
memoryDir: string,
|
|
738
|
-
): { projectionExists: boolean; rows: ProjectedEntityMentionRow[] } {
|
|
739
|
-
const rows = readProjectedEntityMentions(memoryDir);
|
|
740
|
-
if (rows === null) return { projectionExists: false, rows: [] };
|
|
741
|
-
return { projectionExists: true, rows };
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function readProjectedNativeKnowledgeRows(
|
|
745
|
-
memoryDir: string,
|
|
746
|
-
): { projectionExists: boolean; rows: ProjectedNativeKnowledgeChunkRow[] } {
|
|
747
|
-
const rows = readProjectedNativeKnowledgeChunks(memoryDir);
|
|
748
|
-
if (rows === null) return { projectionExists: false, rows: [] };
|
|
749
|
-
return { projectionExists: true, rows };
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
function readProjectedGovernanceRows(memoryDir: string): {
|
|
753
|
-
projectionExists: boolean;
|
|
754
|
-
runId: string | null;
|
|
755
|
-
summary: unknown;
|
|
756
|
-
metrics: unknown;
|
|
757
|
-
reviewQueueRows: MemoryProjectionGovernanceReviewQueueRow[];
|
|
758
|
-
appliedActionRows: MemoryProjectionGovernanceAppliedActionRow[];
|
|
759
|
-
report: string;
|
|
760
|
-
} {
|
|
761
|
-
const record = readProjectedGovernanceRecord(memoryDir);
|
|
762
|
-
if (record === null) {
|
|
763
|
-
return {
|
|
764
|
-
projectionExists: false,
|
|
765
|
-
runId: null,
|
|
766
|
-
summary: undefined,
|
|
767
|
-
metrics: undefined,
|
|
768
|
-
reviewQueueRows: [],
|
|
769
|
-
appliedActionRows: [],
|
|
770
|
-
report: "",
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
return {
|
|
774
|
-
projectionExists: true,
|
|
775
|
-
runId: record.runId,
|
|
776
|
-
summary: record.summary,
|
|
777
|
-
metrics: record.metrics,
|
|
778
|
-
reviewQueueRows: record.reviewQueueRows,
|
|
779
|
-
appliedActionRows: record.appliedActionRows,
|
|
780
|
-
report: record.report,
|
|
781
|
-
};
|
|
782
|
-
}
|
|
783
748
|
|
|
784
749
|
function writeProjectionDb(
|
|
785
750
|
dbPath: string,
|
|
@@ -790,6 +755,7 @@ function writeProjectionDb(
|
|
|
790
755
|
nativeKnowledgeRows: ProjectedNativeKnowledgeChunkRow[],
|
|
791
756
|
governance: Awaited<ReturnType<typeof loadLatestGovernanceProjection>>,
|
|
792
757
|
usedLifecycleLedger: boolean,
|
|
758
|
+
sourceLifecycleLedgerEventCount: number,
|
|
793
759
|
): void {
|
|
794
760
|
const db = openBetterSqlite3(dbPath);
|
|
795
761
|
const memoryDir = path.dirname(path.dirname(dbPath));
|
|
@@ -800,6 +766,7 @@ function writeProjectionDb(
|
|
|
800
766
|
insertMeta.run("schemaVersion", String(MEMORY_PROJECTION_SCHEMA_VERSION));
|
|
801
767
|
insertMeta.run("rebuiltAt", nowIso);
|
|
802
768
|
insertMeta.run("usedLifecycleLedger", usedLifecycleLedger ? "true" : "false");
|
|
769
|
+
writeProjectionMetaHighWater(insertMeta, sourceLifecycleLedgerEventCount, timelineRows);
|
|
803
770
|
insertMeta.run("latestGovernanceRunId", governance?.runId ?? "");
|
|
804
771
|
|
|
805
772
|
const insertCurrent = db.prepare(`
|
|
@@ -1161,6 +1128,7 @@ export async function rebuildMemoryProjection(
|
|
|
1161
1128
|
nextNativeKnowledgeRows,
|
|
1162
1129
|
nextGovernance,
|
|
1163
1130
|
snapshot.usedLifecycleLedger,
|
|
1131
|
+
snapshot.sourceLifecycleLedgerEventCount,
|
|
1164
1132
|
);
|
|
1165
1133
|
backupPath = await backupExistingProjection(options.memoryDir, outputPath, now);
|
|
1166
1134
|
backupPath = await commitPreparedFileAtomically(tempPath, outputPath, backupPath);
|
|
@@ -24,6 +24,27 @@ export const MEMORY_LIFECYCLE_EVENT_SORT_ORDER: Record<MemoryLifecycleEventType,
|
|
|
24
24
|
archived: 10,
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
const FRONTMATTER_DERIVED_EVENT_TYPES: Record<string, true> = {
|
|
28
|
+
created: true,
|
|
29
|
+
updated: true,
|
|
30
|
+
superseded: true,
|
|
31
|
+
archived: true,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export function isFrontmatterDerivedLifecycleEventType(eventType: string): boolean {
|
|
35
|
+
return FRONTMATTER_DERIVED_EVENT_TYPES[eventType] === true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function memoryLifecycleEventProjectionIdentity(event: {
|
|
39
|
+
eventId: string;
|
|
40
|
+
memoryId: string;
|
|
41
|
+
eventType: string;
|
|
42
|
+
timestamp: string;
|
|
43
|
+
}): string {
|
|
44
|
+
if (!isFrontmatterDerivedLifecycleEventType(event.eventType)) return `event\u0000${event.eventId}`;
|
|
45
|
+
return `frontmatter\u0000${event.memoryId}\u0000${event.eventType}\u0000${event.timestamp}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
27
48
|
/**
|
|
28
49
|
* Deterministic sort rank for a lifecycle event type. The ledger readers admit
|
|
29
50
|
* any structurally valid row (permissive `eventType`, issue #1910), so an
|
|
@@ -21,6 +21,7 @@ import path from "node:path";
|
|
|
21
21
|
import { readProjectionRebuiltAt } from "../maintenance/projection-support.js";
|
|
22
22
|
import { lstat, mkdir, mkdtemp, readdir, readFile, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
23
23
|
import { execFileSync } from "node:child_process";
|
|
24
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
24
25
|
|
|
25
26
|
import { MaintenanceScheduler } from "./maintenance.js";
|
|
26
27
|
import {
|
|
@@ -2952,3 +2953,32 @@ test("scheduled projection rebuild skips when the projection was rebuilt within
|
|
|
2952
2953
|
await rm(memoryDir, { recursive: true, force: true });
|
|
2953
2954
|
}
|
|
2954
2955
|
});
|
|
2956
|
+
|
|
2957
|
+
test("maintenance tool request invokes the scheduled projection rebuild wiring (#2119)", async () => {
|
|
2958
|
+
const memoryDir = await seedMemoryDirWithOneFact();
|
|
2959
|
+
const scheduler = new MaintenanceScheduler({
|
|
2960
|
+
config: fixtureConfig({
|
|
2961
|
+
memoryDir,
|
|
2962
|
+
projectionRebuildEnabled: true,
|
|
2963
|
+
projectionRebuildIntervalMs: 6 * 60 * 60 * 1000,
|
|
2964
|
+
}),
|
|
2965
|
+
getQmd: () => ({ isAvailable: () => false }) as unknown as SearchBackend,
|
|
2966
|
+
namespaceSearchRouter: {} as unknown as NamespaceSearchRouter,
|
|
2967
|
+
namespaceCatalog: {} as unknown as NamespaceCatalog,
|
|
2968
|
+
});
|
|
2969
|
+
try {
|
|
2970
|
+
scheduler.requestQmdMaintenanceForTool("test");
|
|
2971
|
+
for (let attempt = 0; attempt < 100 && probeProjectionHealth(memoryDir).state === "absent"; attempt += 1) {
|
|
2972
|
+
await delay(10);
|
|
2973
|
+
}
|
|
2974
|
+
assert.equal(
|
|
2975
|
+
probeProjectionHealth(memoryDir).state,
|
|
2976
|
+
"openable",
|
|
2977
|
+
"the public maintenance request entrypoint must reach the projection rebuild",
|
|
2978
|
+
);
|
|
2979
|
+
assert.equal(readProjectedMemoryBrowse(memoryDir, { limit: 5, offset: 0 })?.total, 1);
|
|
2980
|
+
} finally {
|
|
2981
|
+
await scheduler.dispose();
|
|
2982
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2983
|
+
}
|
|
2984
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SecureStoreLockedError } from "../secure-store/secure-fs.js";
|
|
2
2
|
import { isErrnoCode } from "../utils/errno.js";
|
|
3
3
|
import { displayErrorDetail } from "../runtime/better-sqlite.js";
|
|
4
|
-
import { rename, stat, unlink } from "node:fs/promises";
|
|
4
|
+
import { lstat, rename, stat, unlink } from "node:fs/promises";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import type { MemoryLifecycleEvent } from "../types.js";
|
|
@@ -19,6 +19,15 @@ import {
|
|
|
19
19
|
readMemoryLifecycleEventsFromLines,
|
|
20
20
|
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
21
21
|
} from "./secure-line-reader.js";
|
|
22
|
+
import { getMemoryProjectionPath } from "../memory-projection-store.js";
|
|
23
|
+
import {
|
|
24
|
+
openProjectionLifecycleLedgerLagTracker,
|
|
25
|
+
type ProjectionLifecycleLedgerLagTracker,
|
|
26
|
+
} from "../maintenance/projection-support.js";
|
|
27
|
+
import {
|
|
28
|
+
type ProjectionLedgerLagTelemetry,
|
|
29
|
+
warnProjectionFallback,
|
|
30
|
+
} from "../storage-guards.js";
|
|
22
31
|
|
|
23
32
|
/** Reads a lifecycle-ledger file through the secure whole-file/plaintext line source. */
|
|
24
33
|
export type LedgerSecureReader = (filePath: string) => Promise<string>;
|
|
@@ -190,6 +199,194 @@ export async function readBoundedLifecycleEventsFromLedger(
|
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
201
|
|
|
202
|
+
export interface LifecycleLedgerProjectionLagRead {
|
|
203
|
+
events: MemoryLifecycleEvent[];
|
|
204
|
+
currentEventCount: number;
|
|
205
|
+
deltaEvents: number;
|
|
206
|
+
telemetryAvailable: boolean;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export async function readBoundedLifecycleEventsWithProjectionLag(
|
|
210
|
+
ledgerPath: string,
|
|
211
|
+
readSecureFile: LedgerSecureReader,
|
|
212
|
+
limit: number,
|
|
213
|
+
memoryId: string,
|
|
214
|
+
tracker: ProjectionLifecycleLedgerLagTracker,
|
|
215
|
+
): Promise<LifecycleLedgerProjectionLagRead> {
|
|
216
|
+
let currentEventCount = 0;
|
|
217
|
+
let deltaEvents = 0;
|
|
218
|
+
let telemetryAvailable = true;
|
|
219
|
+
try {
|
|
220
|
+
const events = await readMemoryLifecycleEventsFromLines(
|
|
221
|
+
readMaybeEncryptedLines(
|
|
222
|
+
ledgerPath,
|
|
223
|
+
() => readSecureFile(ledgerPath),
|
|
224
|
+
STATE_FILE_MAX_DECRYPT_BYTES,
|
|
225
|
+
),
|
|
226
|
+
Math.max(0, Math.floor(limit)),
|
|
227
|
+
memoryId,
|
|
228
|
+
compareMemoryLifecycleEvents,
|
|
229
|
+
(event) => {
|
|
230
|
+
if (!telemetryAvailable) return;
|
|
231
|
+
try {
|
|
232
|
+
const tracked = tracker.record(event);
|
|
233
|
+
if (!tracked.unique) return;
|
|
234
|
+
currentEventCount += 1;
|
|
235
|
+
if (!tracked.projected) deltaEvents += 1;
|
|
236
|
+
} catch {
|
|
237
|
+
telemetryAvailable = false;
|
|
238
|
+
currentEventCount = 0;
|
|
239
|
+
deltaEvents = 0;
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
);
|
|
243
|
+
return { events, currentEventCount, deltaEvents, telemetryAvailable };
|
|
244
|
+
} catch (err) {
|
|
245
|
+
if (err instanceof SecureStoreLockedError) throw err;
|
|
246
|
+
if (!isErrnoCode(err, "ENOENT")) throw err;
|
|
247
|
+
return { events: [], currentEventCount: 0, deltaEvents: 0, telemetryAvailable: false };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Probe filesystem identity stamps (dev:ino:size:mtimeMs:ctimeMs) for the
|
|
252
|
+
* projection database and lifecycle-ledger file. Returns `null` when either
|
|
253
|
+
* file is missing or unstatable so caller can disable the lag cache without
|
|
254
|
+
* throwing on transient I/O or permission errors (#2119 / review).
|
|
255
|
+
*/
|
|
256
|
+
export async function probeProjectionLedgerIdentities(
|
|
257
|
+
projectionPath: string,
|
|
258
|
+
ledgerPath: string,
|
|
259
|
+
): Promise<{ projectionIdentity: string; ledgerIdentity: string } | null> {
|
|
260
|
+
try {
|
|
261
|
+
const [projectionIdentity, ledgerIdentity] = await Promise.all(
|
|
262
|
+
[projectionPath, ledgerPath].map(async (filePath) => {
|
|
263
|
+
try {
|
|
264
|
+
const file = await lstat(filePath);
|
|
265
|
+
return `${file.dev}:${file.ino}:${file.size}:${file.mtimeMs}:${file.ctimeMs}`;
|
|
266
|
+
} catch (err) {
|
|
267
|
+
if (isErrnoCode(err, "ENOENT")) return "absent";
|
|
268
|
+
// Probe failure: disable lag cache, do not block the primary timeline read (#2119 review).
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
}),
|
|
272
|
+
);
|
|
273
|
+
if (!projectionIdentity || !ledgerIdentity) return null;
|
|
274
|
+
return { projectionIdentity, ledgerIdentity };
|
|
275
|
+
} catch {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export interface ProjectionLedgerLagScan {
|
|
281
|
+
telemetry: ProjectionLedgerLagTelemetry | null;
|
|
282
|
+
events: MemoryLifecycleEvent[];
|
|
283
|
+
memoryId: string;
|
|
284
|
+
limit: number;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export class ProjectionLedgerLagManager {
|
|
288
|
+
private lagCache: {
|
|
289
|
+
generation: string;
|
|
290
|
+
telemetry: ProjectionLedgerLagTelemetry;
|
|
291
|
+
} | null = null;
|
|
292
|
+
private lagInFlight: {
|
|
293
|
+
generation: string;
|
|
294
|
+
scan: Promise<ProjectionLedgerLagScan>;
|
|
295
|
+
} | null = null;
|
|
296
|
+
|
|
297
|
+
async resolveTimelineWithLag(options: {
|
|
298
|
+
baseDir: string;
|
|
299
|
+
ledgerPath: string;
|
|
300
|
+
memoryId: string;
|
|
301
|
+
cappedLimit: number;
|
|
302
|
+
readSecureFile: LedgerSecureReader;
|
|
303
|
+
projectionAge: () => string;
|
|
304
|
+
}): Promise<MemoryLifecycleEvent[]> {
|
|
305
|
+
const { baseDir, ledgerPath, memoryId, cappedLimit, readSecureFile, projectionAge } = options;
|
|
306
|
+
const projectionPath = getMemoryProjectionPath(baseDir);
|
|
307
|
+
const identities = await probeProjectionLedgerIdentities(projectionPath, ledgerPath);
|
|
308
|
+
|
|
309
|
+
if (!identities) {
|
|
310
|
+
warnProjectionFallback(baseDir, "getMemoryTimeline", projectionAge);
|
|
311
|
+
return readBoundedLifecycleEventsFromLedger(ledgerPath, readSecureFile, cappedLimit, memoryId);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const { projectionIdentity, ledgerIdentity } = identities;
|
|
315
|
+
const generation = `${projectionIdentity}\0${ledgerIdentity}`;
|
|
316
|
+
|
|
317
|
+
const cachedLag = this.lagCache?.generation === generation
|
|
318
|
+
? this.lagCache.telemetry
|
|
319
|
+
: null;
|
|
320
|
+
if (cachedLag) {
|
|
321
|
+
warnProjectionFallback(baseDir, "getMemoryTimeline", projectionAge, cachedLag);
|
|
322
|
+
return readBoundedLifecycleEventsFromLedger(ledgerPath, readSecureFile, cappedLimit, memoryId);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const inFlight = this.lagInFlight?.generation === generation
|
|
326
|
+
? this.lagInFlight.scan
|
|
327
|
+
: null;
|
|
328
|
+
if (inFlight) {
|
|
329
|
+
const result = await inFlight;
|
|
330
|
+
if (result.telemetry) {
|
|
331
|
+
this.lagCache = { generation, telemetry: result.telemetry };
|
|
332
|
+
}
|
|
333
|
+
warnProjectionFallback(baseDir, "getMemoryTimeline", projectionAge, result.telemetry ?? undefined);
|
|
334
|
+
if (result.memoryId === memoryId && result.limit === cappedLimit) return result.events;
|
|
335
|
+
return readBoundedLifecycleEventsFromLedger(ledgerPath, readSecureFile, cappedLimit, memoryId);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const tracker = openProjectionLifecycleLedgerLagTracker(baseDir);
|
|
339
|
+
if (tracker) {
|
|
340
|
+
const highWater = tracker.highWater;
|
|
341
|
+
const scan = (async (): Promise<ProjectionLedgerLagScan> => {
|
|
342
|
+
try {
|
|
343
|
+
const fallback = await readBoundedLifecycleEventsWithProjectionLag(
|
|
344
|
+
ledgerPath,
|
|
345
|
+
readSecureFile,
|
|
346
|
+
cappedLimit,
|
|
347
|
+
memoryId,
|
|
348
|
+
tracker,
|
|
349
|
+
);
|
|
350
|
+
return {
|
|
351
|
+
telemetry: fallback.telemetryAvailable
|
|
352
|
+
? {
|
|
353
|
+
projectedEvents: highWater.eventCount,
|
|
354
|
+
currentLedgerEvents: fallback.currentEventCount,
|
|
355
|
+
deltaEvents: fallback.deltaEvents,
|
|
356
|
+
}
|
|
357
|
+
: null,
|
|
358
|
+
events: fallback.events,
|
|
359
|
+
memoryId,
|
|
360
|
+
limit: cappedLimit,
|
|
361
|
+
};
|
|
362
|
+
} finally {
|
|
363
|
+
try {
|
|
364
|
+
tracker.close();
|
|
365
|
+
} catch {
|
|
366
|
+
// Lag telemetry cleanup must not replace a successful ledger fallback.
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
})();
|
|
370
|
+
this.lagInFlight = { generation, scan };
|
|
371
|
+
try {
|
|
372
|
+
const result = await scan;
|
|
373
|
+
if (result.telemetry) {
|
|
374
|
+
this.lagCache = { generation, telemetry: result.telemetry };
|
|
375
|
+
}
|
|
376
|
+
warnProjectionFallback(baseDir, "getMemoryTimeline", projectionAge, result.telemetry ?? undefined);
|
|
377
|
+
return result.events;
|
|
378
|
+
} finally {
|
|
379
|
+
if (this.lagInFlight?.scan === scan) {
|
|
380
|
+
this.lagInFlight = null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
warnProjectionFallback(baseDir, "getMemoryTimeline", projectionAge);
|
|
386
|
+
return readBoundedLifecycleEventsFromLedger(ledgerPath, readSecureFile, cappedLimit, memoryId);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
193
390
|
/**
|
|
194
391
|
* Directory holding the durable pending-append spill for a lifecycle ledger
|
|
195
392
|
* (#2033). When an append cannot acquire the ledger lock within its budget —
|