@remnic/core 9.17.1 → 9.19.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 +8 -8
- package/dist/access-admin-ops-surface.js +18 -18
- package/dist/access-boundary.d.ts +8 -8
- package/dist/access-boundary.js +18 -18
- package/dist/access-cli.js +52 -52
- package/dist/access-http.d.ts +8 -8
- package/dist/access-http.js +23 -23
- package/dist/access-identity-continuity-surface.d.ts +6 -6
- package/dist/access-identity-continuity-surface.js +18 -18
- package/dist/access-lcm-surface.d.ts +8 -8
- package/dist/access-lcm-surface.js +18 -18
- package/dist/access-mcp.d.ts +8 -8
- package/dist/access-mcp.js +22 -22
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/access-memory-search-fanout.js +3 -3
- package/dist/{access-namespace-preflight-_yxq3PYy.d.ts → access-namespace-preflight-BsawJa-L.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +18 -18
- package/dist/access-observe-write-surface.d.ts +8 -8
- package/dist/access-observe-write-surface.js +18 -18
- package/dist/access-operations-batch.js +20 -20
- package/dist/access-operations.d.ts +11 -11
- package/dist/access-operations.js +21 -21
- package/dist/access-recall-concurrency.d.ts +8 -8
- package/dist/access-recall-concurrency.js +18 -18
- package/dist/access-recall-response.d.ts +8 -8
- package/dist/access-recall-response.js +18 -18
- package/dist/access-recall-surface.d.ts +8 -8
- package/dist/access-recall-surface.js +18 -18
- package/dist/access-schema.d.ts +76 -76
- package/dist/{access-service-CSm242ks.d.ts → access-service-DLuVnV6a.d.ts} +6 -6
- package/dist/access-service.d.ts +8 -8
- package/dist/access-service.js +18 -18
- package/dist/access-surface-catalog.d.ts +8 -8
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-memory-bridge.js +3 -3
- 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 +6 -6
- 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.d.ts +2 -2
- package/dist/buffer.js +2 -2
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +4 -3
- package/dist/capabilities.js +1 -1
- package/dist/{catalog-9qLG_2OV.d.ts → catalog-O6P8694A.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-BV5WMSIJ.js → chunk-2AJ72VVS.js} +3 -3
- package/dist/{chunk-755Q3DGF.js → chunk-2H7OEVPO.js} +2 -2
- package/dist/{chunk-TG6IHVE2.js → chunk-354T54RR.js} +2 -2
- package/dist/{chunk-347WD2LE.js → chunk-3CSX3ETE.js} +2 -1
- package/dist/chunk-3CSX3ETE.js.map +1 -0
- package/dist/{chunk-KGKYTCDO.js → chunk-3LZSS77C.js} +3 -3
- package/dist/{chunk-EA2H5LML.js → chunk-44DUJETW.js} +2 -2
- package/dist/{chunk-WQ2I6ODB.js → chunk-5MFHOX5T.js} +3 -3
- package/dist/{chunk-JECBQBEX.js → chunk-B7GROTXD.js} +2 -2
- package/dist/{chunk-6WAFYNVT.js → chunk-BJJAX3IZ.js} +2 -2
- package/dist/{chunk-IZHXSLO7.js → chunk-CH3BRJ3P.js} +2 -2
- package/dist/{chunk-L3HAUO7K.js → chunk-D5H3B36B.js} +2 -2
- package/dist/{chunk-AWT7SCWQ.js → chunk-DQCUBRTZ.js} +2 -2
- package/dist/{chunk-WIUYMCPL.js → chunk-DR65EEXG.js} +15 -15
- package/dist/{chunk-7LXSPIVG.js → chunk-EZFVVEQG.js} +4 -4
- package/dist/{chunk-S2FSBU4H.js → chunk-FCCMFVS6.js} +2 -2
- package/dist/{chunk-2OH3ZRYH.js → chunk-FL7MNSH3.js} +7 -6
- package/dist/chunk-FL7MNSH3.js.map +1 -0
- package/dist/{chunk-QDTEH6BS.js → chunk-HCNG42P3.js} +2 -2
- package/dist/{chunk-V57NLO5P.js → chunk-HKF5VASQ.js} +11 -4
- package/dist/chunk-HKF5VASQ.js.map +1 -0
- package/dist/{chunk-7YJLCNPF.js → chunk-HRREDJSL.js} +204 -170
- package/dist/chunk-HRREDJSL.js.map +1 -0
- package/dist/{chunk-7ON2JW73.js → chunk-IKMAO55B.js} +2 -2
- package/dist/{chunk-B57BIBXI.js → chunk-IU3XJAQM.js} +3 -3
- package/dist/{chunk-RFDD3B7W.js → chunk-JGUXPJUQ.js} +2 -2
- package/dist/{chunk-KQED7RCV.js → chunk-KOFVOVJG.js} +22 -22
- package/dist/{chunk-7PK4W4RN.js → chunk-KUF7OIFK.js} +3 -3
- package/dist/{chunk-QCLQ4BGA.js → chunk-MJYYXODM.js} +3 -3
- package/dist/{chunk-C2RMQRPN.js → chunk-MLW3J7IC.js} +3 -3
- package/dist/{chunk-7VZWZVYG.js → chunk-MQVINOJQ.js} +3 -3
- package/dist/{chunk-BCNVNUQG.js → chunk-OFQUY5NZ.js} +12 -4
- package/dist/chunk-OFQUY5NZ.js.map +1 -0
- package/dist/{chunk-52EFQMQC.js → chunk-OOJQ6ID5.js} +30 -30
- package/dist/{chunk-F7BWFIDK.js → chunk-OZSL2QZC.js} +65 -4
- package/dist/chunk-OZSL2QZC.js.map +1 -0
- package/dist/{chunk-LMVAIZR5.js → chunk-PLRDZ2OO.js} +5 -9
- package/dist/chunk-PLRDZ2OO.js.map +1 -0
- package/dist/{chunk-BIRPR5N7.js → chunk-Q55XM4SU.js} +2 -2
- package/dist/{chunk-7O2C2OOF.js → chunk-QEEMLGCO.js} +7 -7
- package/dist/{chunk-ANEUPF6O.js → chunk-QH6CS6XJ.js} +2 -2
- package/dist/{chunk-SCCO46KZ.js → chunk-RDQGLJRR.js} +3 -2
- package/dist/chunk-RDQGLJRR.js.map +1 -0
- package/dist/{chunk-BGBPDAIN.js → chunk-RPOOJPMB.js} +6 -6
- package/dist/{chunk-I6ODVWQB.js → chunk-S5HCKQ7Q.js} +2 -2
- package/dist/{chunk-QDNE35FU.js → chunk-SB4OKS6C.js} +3 -3
- package/dist/{chunk-B6NCHYAK.js → chunk-SQEDDWAM.js} +4 -4
- package/dist/{chunk-K2B2TMF3.js → chunk-U7D5HWNG.js} +2 -2
- package/dist/{chunk-XSBUROEB.js → chunk-UGH6VPPO.js} +2 -2
- package/dist/{chunk-G2WCVFDC.js → chunk-UVSDFM7R.js} +4 -4
- package/dist/{chunk-AFH7PIFU.js → chunk-VSVX777Y.js} +2 -2
- package/dist/{chunk-B6P3QDBF.js → chunk-W3DJOT22.js} +2 -2
- package/dist/{chunk-AE7WMV4V.js → chunk-WMLHJ5UW.js} +2 -2
- package/dist/{chunk-N7DK67GZ.js → chunk-WYL5ODHJ.js} +1 -1
- package/dist/chunk-WYL5ODHJ.js.map +1 -0
- package/dist/{chunk-GBT4TFV7.js → chunk-X6E5WAGX.js} +9 -9
- package/dist/{chunk-VZC7ORJE.js → chunk-X7GOH7Y6.js} +2 -2
- package/dist/{chunk-6QYST5XN.js → chunk-XY7QPJDA.js} +2 -2
- package/dist/{chunk-WIS5JTJR.js → chunk-Y4JHE2ML.js} +2 -2
- package/dist/{chunk-SESF64QR.js → chunk-YUWFASAK.js} +4 -4
- package/dist/{chunk-BZ52BJ6C.js → chunk-YWJBAHTY.js} +2 -2
- package/dist/{cli-BLZHYY2A.d.ts → cli-0dQHR6e6.d.ts} +5 -5
- package/dist/cli.d.ts +10 -10
- package/dist/cli.js +36 -36
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/engine.js +7 -7
- 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 +7 -7
- 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/contradiction/index.js +2 -2
- package/dist/{contradiction-scan-RJBABNRH.js → contradiction-scan-HMF7CNKD.js} +3 -3
- 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/day-summary.d.ts +1 -1
- package/dist/day-summary.js +2 -2
- 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/embedding-fallback.js +2 -2
- 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 +6 -6
- package/dist/explicit-capture.js +2 -2
- 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.d.ts +1 -1
- package/dist/extraction.js +4 -4
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/{first-start-migration-CSRHCJIQ.js → first-start-migration-LEXL3XYL.js} +2 -2
- 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 +420 -420
- package/dist/index.js +52 -52
- 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-llm.js +2 -2
- 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 +26 -8
- package/dist/maintenance/rebuild-memory-projection.js +7 -7
- package/dist/mcp-memory-inspector-app.d.ts +8 -8
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +3 -2
- package/dist/memory-projection-store.js +3 -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 +10 -10
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/principal.js +2 -2
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +4 -4
- 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 +4 -4
- package/dist/operator-toolkit.d.ts +2 -2
- package/dist/operator-toolkit.js +16 -16
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/compression-guideline-coordinator.js +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -3
- package/dist/orchestration/maintenance.js +15 -11
- package/dist/{orchestrator-CplS9U3U.d.ts → orchestrator-B8ickLkd.d.ts} +5 -5
- package/dist/orchestrator.d.ts +6 -6
- package/dist/orchestrator.js +52 -52
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-DB4CbFqo.d.ts → pipeline-B669unGm.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{purge-IRF47ZTX.js → purge-YSGX5BOG.js} +2 -2
- package/dist/{qmd-BxZmOLHq.d.ts → qmd-D0PVXmea.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/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/schemas.d.ts +76 -76
- package/dist/{scope-profiles-NsQQo1Lt.d.ts → scope-profiles-BOp3gSzp.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/embed-helper.js +2 -2
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +3 -3
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +3 -3
- 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-DW85h30n.d.ts → semantic-consolidation-C7SZNzUJ.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/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +11 -11
- package/dist/shared-context/manager.js +2 -2
- package/dist/signal.d.ts +1 -1
- package/dist/{storage-o5BTAa8d.d.ts → storage-CJ_Fia4H.d.ts} +1 -1
- package/dist/storage-guards.d.ts +6 -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/summarizer.js +3 -3
- 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/{tier-stats-QEJSPFV2.js → tier-stats-XKD7SXYY.js} +2 -2
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- 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-V3dT-pen.d.ts → types-GeWGXq9E.d.ts} +18 -0
- 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/capabilities.test.ts +1 -0
- package/src/capabilities.ts +4 -1
- package/src/config.test.ts +33 -0
- package/src/config.ts +7 -3
- package/src/maintenance/projection-support.ts +56 -0
- package/src/maintenance/rebuild-memory-lifecycle-ledger.ts +2 -7
- package/src/maintenance/rebuild-memory-projection.ts +7 -10
- package/src/memory-projection-store.ts +1 -1
- package/src/orchestration/maintenance.test.ts +116 -0
- package/src/orchestration/maintenance.ts +16 -0
- package/src/orchestration/projection-rebuild-schedule.ts +115 -0
- package/src/storage-guards.ts +19 -2
- package/src/storage-memory-lifecycle-ledger.test.ts +32 -0
- package/src/storage.ts +15 -1
- package/src/testing/subjects/memory-projection.test.ts +78 -0
- package/src/types.ts +20 -0
- package/dist/chunk-2OH3ZRYH.js.map +0 -1
- package/dist/chunk-347WD2LE.js.map +0 -1
- package/dist/chunk-7YJLCNPF.js.map +0 -1
- package/dist/chunk-BCNVNUQG.js.map +0 -1
- package/dist/chunk-F7BWFIDK.js.map +0 -1
- package/dist/chunk-LMVAIZR5.js.map +0 -1
- package/dist/chunk-N7DK67GZ.js.map +0 -1
- package/dist/chunk-SCCO46KZ.js.map +0 -1
- package/dist/chunk-V57NLO5P.js.map +0 -1
- /package/dist/{chunk-BV5WMSIJ.js.map → chunk-2AJ72VVS.js.map} +0 -0
- /package/dist/{chunk-755Q3DGF.js.map → chunk-2H7OEVPO.js.map} +0 -0
- /package/dist/{chunk-TG6IHVE2.js.map → chunk-354T54RR.js.map} +0 -0
- /package/dist/{chunk-KGKYTCDO.js.map → chunk-3LZSS77C.js.map} +0 -0
- /package/dist/{chunk-EA2H5LML.js.map → chunk-44DUJETW.js.map} +0 -0
- /package/dist/{chunk-WQ2I6ODB.js.map → chunk-5MFHOX5T.js.map} +0 -0
- /package/dist/{chunk-JECBQBEX.js.map → chunk-B7GROTXD.js.map} +0 -0
- /package/dist/{chunk-6WAFYNVT.js.map → chunk-BJJAX3IZ.js.map} +0 -0
- /package/dist/{chunk-IZHXSLO7.js.map → chunk-CH3BRJ3P.js.map} +0 -0
- /package/dist/{chunk-L3HAUO7K.js.map → chunk-D5H3B36B.js.map} +0 -0
- /package/dist/{chunk-AWT7SCWQ.js.map → chunk-DQCUBRTZ.js.map} +0 -0
- /package/dist/{chunk-WIUYMCPL.js.map → chunk-DR65EEXG.js.map} +0 -0
- /package/dist/{chunk-7LXSPIVG.js.map → chunk-EZFVVEQG.js.map} +0 -0
- /package/dist/{chunk-S2FSBU4H.js.map → chunk-FCCMFVS6.js.map} +0 -0
- /package/dist/{chunk-QDTEH6BS.js.map → chunk-HCNG42P3.js.map} +0 -0
- /package/dist/{chunk-7ON2JW73.js.map → chunk-IKMAO55B.js.map} +0 -0
- /package/dist/{chunk-B57BIBXI.js.map → chunk-IU3XJAQM.js.map} +0 -0
- /package/dist/{chunk-RFDD3B7W.js.map → chunk-JGUXPJUQ.js.map} +0 -0
- /package/dist/{chunk-KQED7RCV.js.map → chunk-KOFVOVJG.js.map} +0 -0
- /package/dist/{chunk-7PK4W4RN.js.map → chunk-KUF7OIFK.js.map} +0 -0
- /package/dist/{chunk-QCLQ4BGA.js.map → chunk-MJYYXODM.js.map} +0 -0
- /package/dist/{chunk-C2RMQRPN.js.map → chunk-MLW3J7IC.js.map} +0 -0
- /package/dist/{chunk-7VZWZVYG.js.map → chunk-MQVINOJQ.js.map} +0 -0
- /package/dist/{chunk-52EFQMQC.js.map → chunk-OOJQ6ID5.js.map} +0 -0
- /package/dist/{chunk-BIRPR5N7.js.map → chunk-Q55XM4SU.js.map} +0 -0
- /package/dist/{chunk-7O2C2OOF.js.map → chunk-QEEMLGCO.js.map} +0 -0
- /package/dist/{chunk-ANEUPF6O.js.map → chunk-QH6CS6XJ.js.map} +0 -0
- /package/dist/{chunk-BGBPDAIN.js.map → chunk-RPOOJPMB.js.map} +0 -0
- /package/dist/{chunk-I6ODVWQB.js.map → chunk-S5HCKQ7Q.js.map} +0 -0
- /package/dist/{chunk-QDNE35FU.js.map → chunk-SB4OKS6C.js.map} +0 -0
- /package/dist/{chunk-B6NCHYAK.js.map → chunk-SQEDDWAM.js.map} +0 -0
- /package/dist/{chunk-K2B2TMF3.js.map → chunk-U7D5HWNG.js.map} +0 -0
- /package/dist/{chunk-XSBUROEB.js.map → chunk-UGH6VPPO.js.map} +0 -0
- /package/dist/{chunk-G2WCVFDC.js.map → chunk-UVSDFM7R.js.map} +0 -0
- /package/dist/{chunk-AFH7PIFU.js.map → chunk-VSVX777Y.js.map} +0 -0
- /package/dist/{chunk-B6P3QDBF.js.map → chunk-W3DJOT22.js.map} +0 -0
- /package/dist/{chunk-AE7WMV4V.js.map → chunk-WMLHJ5UW.js.map} +0 -0
- /package/dist/{chunk-GBT4TFV7.js.map → chunk-X6E5WAGX.js.map} +0 -0
- /package/dist/{chunk-VZC7ORJE.js.map → chunk-X7GOH7Y6.js.map} +0 -0
- /package/dist/{chunk-6QYST5XN.js.map → chunk-XY7QPJDA.js.map} +0 -0
- /package/dist/{chunk-WIS5JTJR.js.map → chunk-Y4JHE2ML.js.map} +0 -0
- /package/dist/{chunk-SESF64QR.js.map → chunk-YUWFASAK.js.map} +0 -0
- /package/dist/{chunk-BZ52BJ6C.js.map → chunk-YWJBAHTY.js.map} +0 -0
- /package/dist/{contradiction-scan-RJBABNRH.js.map → contradiction-scan-HMF7CNKD.js.map} +0 -0
- /package/dist/{first-start-migration-CSRHCJIQ.js.map → first-start-migration-LEXL3XYL.js.map} +0 -0
- /package/dist/{purge-IRF47ZTX.js.map → purge-YSGX5BOG.js.map} +0 -0
- /package/dist/{tier-stats-QEJSPFV2.js.map → tier-stats-XKD7SXYY.js.map} +0 -0
package/src/config.test.ts
CHANGED
|
@@ -2349,6 +2349,28 @@ test("parseConfig bounded-state knobs coerce valid strings, preserve 0, and reje
|
|
|
2349
2349
|
assert.throws(() => parseConfig({ recallImpressionsRotateKeep: "1000000" }), /between 1 and 1000/);
|
|
2350
2350
|
}));
|
|
2351
2351
|
|
|
2352
|
+
test("parseConfig projection-rebuild knobs: defaults, boolean coercion, numeric override, and 60s floor (#2119)", () => {
|
|
2353
|
+
const defaults = parseConfig({});
|
|
2354
|
+
// Enabled by default — the projection must stay fresh without operator action,
|
|
2355
|
+
// mirroring lifecycle-ledger auto-compaction's enabled-by-default posture.
|
|
2356
|
+
assert.equal(defaults.projectionRebuildEnabled, true);
|
|
2357
|
+
assert.equal(defaults.projectionRebuildIntervalMs, 6 * 60 * 60 * 1000);
|
|
2358
|
+
|
|
2359
|
+
// Boolean coerced via the shared helper (string "false" must disable, not read
|
|
2360
|
+
// as truthy — gotcha #36).
|
|
2361
|
+
assert.equal(parseConfig({ projectionRebuildEnabled: "false" }).projectionRebuildEnabled, false);
|
|
2362
|
+
assert.equal(parseConfig({ projectionRebuildEnabled: false }).projectionRebuildEnabled, false);
|
|
2363
|
+
|
|
2364
|
+
// Numeric override is honored; a sub-floor value is clamped to the 60s floor.
|
|
2365
|
+
assert.equal(parseConfig({ projectionRebuildIntervalMs: 3600000 }).projectionRebuildIntervalMs, 3600000);
|
|
2366
|
+
// String forms are coerced (CLI/--config inputs arrive as strings, gotcha #17)...
|
|
2367
|
+
assert.equal(parseConfig({ projectionRebuildIntervalMs: "3600000" }).projectionRebuildIntervalMs, 3600000);
|
|
2368
|
+
// ...and invalid values are REJECTED, never silently reinterpreted (pattern #39):
|
|
2369
|
+
assert.throws(() => parseConfig({ projectionRebuildIntervalMs: 1000 }), /projectionRebuildIntervalMs/);
|
|
2370
|
+
assert.throws(() => parseConfig({ projectionRebuildIntervalMs: 3600000.5 }), /projectionRebuildIntervalMs/);
|
|
2371
|
+
assert.throws(() => parseConfig({ projectionRebuildIntervalMs: "abc" }), /projectionRebuildIntervalMs/);
|
|
2372
|
+
});
|
|
2373
|
+
|
|
2352
2374
|
test("parseConfig forwards activity source settings", () => {
|
|
2353
2375
|
const config = parseConfig({
|
|
2354
2376
|
activity: {
|
|
@@ -2374,3 +2396,14 @@ test("parseConfig forwards activity source settings", () => {
|
|
|
2374
2396
|
maxMemoriesPerDay: 0,
|
|
2375
2397
|
});
|
|
2376
2398
|
});
|
|
2399
|
+
|
|
2400
|
+
test("parseConfig forwards bridgeMode as a raw passthrough (validation is in resolveBridgeMode)", () => {
|
|
2401
|
+
assert.equal(parseConfig({}).bridgeMode, "embedded");
|
|
2402
|
+
assert.equal(parseConfig({ bridgeMode: "delegate" }).bridgeMode, "delegate");
|
|
2403
|
+
assert.equal(parseConfig({ bridgeMode: "embedded" }).bridgeMode, "embedded");
|
|
2404
|
+
// Invalid values pass through unchanged — resolveBridgeMode rejects them.
|
|
2405
|
+
assert.equal(parseConfig({ bridgeMode: "daemon" }).bridgeMode, "daemon");
|
|
2406
|
+
// Non-strings stringify so they REACH the downstream validator instead of
|
|
2407
|
+
// silently defaulting to embedded.
|
|
2408
|
+
assert.equal(parseConfig({ bridgeMode: true }).bridgeMode, "true");
|
|
2409
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -2339,14 +2339,13 @@ export function parseConfig(
|
|
|
2339
2339
|
wearables,
|
|
2340
2340
|
activity,
|
|
2341
2341
|
provenance,
|
|
2342
|
-
// At-rest encryption (
|
|
2343
|
-
// coerceBool handles CLI string inputs: `--config secureStoreEnabled=true`
|
|
2344
|
-
// arrives as the string "true" which `=== true` would reject (CLAUDE.md #36).
|
|
2342
|
+
// At-rest encryption (#690): coerceBool handles CLI string "true" (#36).
|
|
2345
2343
|
secureStoreEnabled: coerceBool(cfg.secureStoreEnabled) === true,
|
|
2346
2344
|
secureStoreEncryptOnWrite: coerceBool(cfg.secureStoreEncryptOnWrite) !== false, // default: true
|
|
2347
2345
|
codingMode,
|
|
2348
2346
|
heartbeat,
|
|
2349
2347
|
slotBehavior,
|
|
2348
|
+
bridgeMode: cfg.bridgeMode === undefined ? "embedded" : String(cfg.bridgeMode),
|
|
2350
2349
|
codexCompat,
|
|
2351
2350
|
codingKnowledge: parseCodingKnowledgeConfig(cfg.codingKnowledge),
|
|
2352
2351
|
chat: parseChatConfig(cfg.chat),
|
|
@@ -3566,6 +3565,11 @@ export function parseConfig(
|
|
|
3566
3565
|
Number.isFinite(cfg.graphEdgeDecayVisibilityThreshold)
|
|
3567
3566
|
? Math.max(0, Math.min(1, cfg.graphEdgeDecayVisibilityThreshold))
|
|
3568
3567
|
: 0.2,
|
|
3568
|
+
// Issue #2119 — maintenance-scheduled memory-projection rebuild.
|
|
3569
|
+
// Enabled-by-default (gotcha #36 coercion); interval via the strict shared parser (strings coerced, non-integer/sub-60s rejected — pattern #39).
|
|
3570
|
+
projectionRebuildEnabled: coerceBooleanLike(cfg.projectionRebuildEnabled) ?? true,
|
|
3571
|
+
projectionRebuildIntervalMs: parseIntegerAtLeast(
|
|
3572
|
+
cfg.projectionRebuildIntervalMs, 6 * 60 * 60 * 1000, 60_000, "projectionRebuildIntervalMs"),
|
|
3569
3573
|
...parseBoundedJsonlStateConfig(cfg, parseIntegerAtLeast),
|
|
3570
3574
|
// Issue #681 PR 3/3 — confidence-aware traversal. Floor clamps to [0,1]; a
|
|
3571
3575
|
// documented 0 iterations disables PageRank refinement (BFS scores pass through).
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Projection maintenance support (issue #2119) — helpers shared by the
|
|
3
|
+
* scheduled projection rebuild, the CLI rebuild, and the ledger rebuild.
|
|
4
|
+
* Lives beside the rebuild ops so the at-ceiling store/rebuild modules stay
|
|
5
|
+
* within their structural ratchets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { openProjectionReadonly } from "../memory-projection-store.js";
|
|
10
|
+
|
|
11
|
+
/** Read the projection meta rebuiltAt timestamp; null when absent/unopenable. */
|
|
12
|
+
export function readProjectionRebuiltAt(memoryDir: string): string | null {
|
|
13
|
+
const db = openProjectionReadonly(memoryDir);
|
|
14
|
+
if (!db) return null;
|
|
15
|
+
try {
|
|
16
|
+
const row = db.prepare("SELECT value FROM meta WHERE key = 'rebuiltAt'").get() as
|
|
17
|
+
| { value?: unknown }
|
|
18
|
+
| undefined;
|
|
19
|
+
return typeof row?.value === "string" && row.value.length > 0 ? row.value : null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
} finally {
|
|
23
|
+
db.close();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Assert an optionally injected storage (e.g. the daemon's live unlocked
|
|
29
|
+
* secure-store instance) is rooted at `memoryDir`, and hand it back. Shared
|
|
30
|
+
* by the projection and lifecycle-ledger rebuilds so the containment guard
|
|
31
|
+
* cannot drift between them; callers construct their own StorageManager when
|
|
32
|
+
* nothing was injected (keeps this module off the direct-storage-import
|
|
33
|
+
* ratchet, issue #1533).
|
|
34
|
+
*/
|
|
35
|
+
export function assertInjectedStorageRooted<T extends { dir: string }>(
|
|
36
|
+
label: string,
|
|
37
|
+
memoryDir: string,
|
|
38
|
+
injected: T | undefined,
|
|
39
|
+
): T | undefined {
|
|
40
|
+
if (injected && path.resolve(injected.dir) !== path.resolve(memoryDir)) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`${label}: storage.dir (${injected.dir}) must match memoryDir (${memoryDir})`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return injected;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface SkippedDuplicateMemory {
|
|
49
|
+
memoryId: string;
|
|
50
|
+
keptPath: string;
|
|
51
|
+
skippedPath: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SkippedBlankIdMemory {
|
|
55
|
+
path: string;
|
|
56
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { stat } from "node:fs/promises";
|
|
3
3
|
import { StorageManager } from "../storage.js";
|
|
4
|
+
import { assertInjectedStorageRooted } from "./projection-support.js";
|
|
4
5
|
import { log } from "../logger.js";
|
|
5
6
|
import { displayErrorDetail } from "../runtime/better-sqlite.js";
|
|
6
7
|
import type { MemoryLifecycleEvent } from "../types.js";
|
|
@@ -205,13 +206,7 @@ export async function rebuildMemoryLifecycleLedger(
|
|
|
205
206
|
const dryRun = options.dryRun !== false;
|
|
206
207
|
const now = options.now ?? new Date();
|
|
207
208
|
const outputPath = path.join(options.memoryDir, "state", "memory-lifecycle-ledger.jsonl");
|
|
208
|
-
const storage = options.storage ?? new StorageManager(options.memoryDir);
|
|
209
|
-
if (options.storage && path.resolve(storage.dir) !== path.resolve(options.memoryDir)) {
|
|
210
|
-
throw new Error(
|
|
211
|
-
`rebuildMemoryLifecycleLedger: storage.dir (${storage.dir}) must match `
|
|
212
|
-
+ `memoryDir (${options.memoryDir})`,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
209
|
+
const storage = assertInjectedStorageRooted("rebuildMemoryLifecycleLedger", options.memoryDir, options.storage) ?? new StorageManager(options.memoryDir);
|
|
215
210
|
const secureRewrite = options.storage !== undefined;
|
|
216
211
|
const tiers = [
|
|
217
212
|
await storage.readAllMemories(),
|
|
@@ -46,6 +46,8 @@ import { commitPreparedFileAtomically } from "./atomic-file.js";
|
|
|
46
46
|
|
|
47
47
|
export interface RebuildMemoryProjectionOptions {
|
|
48
48
|
memoryDir: string;
|
|
49
|
+
/** Reuse the caller's live StorageManager (must be rooted at memoryDir). */
|
|
50
|
+
storage?: StorageManager;
|
|
49
51
|
defaultNamespace?: string;
|
|
50
52
|
dryRun?: boolean;
|
|
51
53
|
now?: Date;
|
|
@@ -53,15 +55,9 @@ export interface RebuildMemoryProjectionOptions {
|
|
|
53
55
|
updatedBefore?: string;
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
skippedPath: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface SkippedBlankIdMemory {
|
|
63
|
-
path: string;
|
|
64
|
-
}
|
|
58
|
+
export type { SkippedBlankIdMemory, SkippedDuplicateMemory } from "./projection-support.js";
|
|
59
|
+
import { assertInjectedStorageRooted } from "./projection-support.js";
|
|
60
|
+
import type { SkippedBlankIdMemory, SkippedDuplicateMemory } from "./projection-support.js";
|
|
65
61
|
|
|
66
62
|
export interface SkippedDuplicateTimelineEvent {
|
|
67
63
|
eventId: string;
|
|
@@ -550,6 +546,7 @@ function deduplicateTimelineEvents(
|
|
|
550
546
|
async function loadAuthoritativeProjectionSnapshot(options: {
|
|
551
547
|
memoryDir: string;
|
|
552
548
|
defaultNamespace?: string;
|
|
549
|
+
storage?: StorageManager;
|
|
553
550
|
updatedAfter?: string;
|
|
554
551
|
updatedBefore?: string;
|
|
555
552
|
}): Promise<{
|
|
@@ -581,7 +578,7 @@ async function loadAuthoritativeProjectionSnapshot(options: {
|
|
|
581
578
|
skippedDuplicateTimelineEvents: SkippedDuplicateTimelineEvent[];
|
|
582
579
|
skippedBlankIdTimelineEvents: SkippedBlankIdTimelineEvent[];
|
|
583
580
|
}> {
|
|
584
|
-
const storage = new StorageManager(options.memoryDir);
|
|
581
|
+
const storage = assertInjectedStorageRooted("rebuildMemoryProjection", options.memoryDir, options.storage) ?? new StorageManager(options.memoryDir);
|
|
585
582
|
// Force a fresh disk read — projection verify/rebuild must see the true
|
|
586
583
|
// on-disk state, not a potentially stale in-process cache.
|
|
587
584
|
storage.invalidateAllMemoriesCacheForDir();
|
|
@@ -366,7 +366,7 @@ function logProjectionOpenFailure(memoryDir: string, error: unknown): void {
|
|
|
366
366
|
);
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
function openProjectionReadonly(memoryDir: string): BetterSqlite3Database | null {
|
|
369
|
+
export function openProjectionReadonly(memoryDir: string): BetterSqlite3Database | null {
|
|
370
370
|
const dbPath = getMemoryProjectionPath(memoryDir);
|
|
371
371
|
// A MISSING file is a normal fallback (cold install, never built). A file
|
|
372
372
|
// that EXISTS but cannot open (wrong ABI / corrupt / bad perms) is a real
|
|
@@ -18,10 +18,22 @@ import test from "node:test";
|
|
|
18
18
|
import assert from "node:assert/strict";
|
|
19
19
|
import os from "node:os";
|
|
20
20
|
import path from "node:path";
|
|
21
|
+
import { readProjectionRebuiltAt } from "../maintenance/projection-support.js";
|
|
21
22
|
import { lstat, mkdir, mkdtemp, readdir, readFile, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
22
23
|
import { execFileSync } from "node:child_process";
|
|
23
24
|
|
|
24
25
|
import { MaintenanceScheduler } from "./maintenance.js";
|
|
26
|
+
import {
|
|
27
|
+
createProjectionRebuildScheduleState,
|
|
28
|
+
maybeRebuildMemoryProjectionScheduled,
|
|
29
|
+
type ProjectionRebuildScheduleState,
|
|
30
|
+
} from "./projection-rebuild-schedule.js";
|
|
31
|
+
import { rebuildMemoryProjection } from "../maintenance/rebuild-memory-projection.js";
|
|
32
|
+
import {
|
|
33
|
+
probeProjectionHealth,
|
|
34
|
+
readProjectedMemoryBrowse,
|
|
35
|
+
|
|
36
|
+
} from "../memory-projection-store.js";
|
|
25
37
|
import type { PluginConfig } from "../types.js";
|
|
26
38
|
import type { SearchBackend } from "../search/port.js";
|
|
27
39
|
import type {
|
|
@@ -2836,3 +2848,107 @@ test("catalog-disabled fallback pending drain skips unsafe spill entries (symlin
|
|
|
2836
2848
|
await rm(outside, { recursive: true, force: true });
|
|
2837
2849
|
}
|
|
2838
2850
|
});
|
|
2851
|
+
|
|
2852
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
2853
|
+
// Memory-projection scheduled rebuild (issue #2119)
|
|
2854
|
+
// ───────────────────────────────────────────────────────────────────────────
|
|
2855
|
+
|
|
2856
|
+
|
|
2857
|
+
async function seedMemoryDirWithOneFact(): Promise<string> {
|
|
2858
|
+
const memoryDir = await mkdtemp(path.join(os.tmpdir(), "remnic-projrebuild-"));
|
|
2859
|
+
await mkdir(path.join(memoryDir, "facts", "2026-07-01"), { recursive: true });
|
|
2860
|
+
await writeFile(
|
|
2861
|
+
path.join(memoryDir, "facts", "2026-07-01", "fact-1.md"),
|
|
2862
|
+
`---
|
|
2863
|
+
id: fact-1
|
|
2864
|
+
category: fact
|
|
2865
|
+
created: 2026-07-01T00:00:00.000Z
|
|
2866
|
+
updated: 2026-07-01T01:00:00.000Z
|
|
2867
|
+
source: test
|
|
2868
|
+
confidence: 0.8
|
|
2869
|
+
confidenceTier: implied
|
|
2870
|
+
tags: ["alpha"]
|
|
2871
|
+
---
|
|
2872
|
+
|
|
2873
|
+
alpha
|
|
2874
|
+
`,
|
|
2875
|
+
"utf-8",
|
|
2876
|
+
);
|
|
2877
|
+
return memoryDir;
|
|
2878
|
+
}
|
|
2879
|
+
/** Build the pure-function state the extracted scheduled rebuild owns. */
|
|
2880
|
+
function projectionScheduleState(): ProjectionRebuildScheduleState {
|
|
2881
|
+
return createProjectionRebuildScheduleState();
|
|
2882
|
+
}
|
|
2883
|
+
|
|
2884
|
+
test("scheduled projection rebuild runs when enabled and writes a populated projection (#2119)", async () => {
|
|
2885
|
+
const memoryDir = await seedMemoryDirWithOneFact();
|
|
2886
|
+
try {
|
|
2887
|
+
// No projection built yet — the exact issue-#2119 cold state where timeline
|
|
2888
|
+
// consumers would fall back to full-corpus scans.
|
|
2889
|
+
assert.equal(probeProjectionHealth(memoryDir).state, "absent");
|
|
2890
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
2891
|
+
config: fixtureConfig({
|
|
2892
|
+
memoryDir,
|
|
2893
|
+
projectionRebuildEnabled: true,
|
|
2894
|
+
projectionRebuildIntervalMs: 6 * 60 * 60 * 1000,
|
|
2895
|
+
}),
|
|
2896
|
+
state: projectionScheduleState(),
|
|
2897
|
+
});
|
|
2898
|
+
// The projection now exists, is openable, and carries the seeded memory.
|
|
2899
|
+
assert.equal(probeProjectionHealth(memoryDir).state, "openable");
|
|
2900
|
+
assert.equal(readProjectedMemoryBrowse(memoryDir, { limit: 5, offset: 0 })?.total, 1);
|
|
2901
|
+
assert.ok(readProjectionRebuiltAt(memoryDir), "rebuiltAt meta must be stamped");
|
|
2902
|
+
} finally {
|
|
2903
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2907
|
+
test("scheduled projection rebuild is a no-op when disabled by config (#2119)", async () => {
|
|
2908
|
+
const memoryDir = await seedMemoryDirWithOneFact();
|
|
2909
|
+
try {
|
|
2910
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
2911
|
+
config: fixtureConfig({
|
|
2912
|
+
memoryDir,
|
|
2913
|
+
projectionRebuildEnabled: false,
|
|
2914
|
+
projectionRebuildIntervalMs: 6 * 60 * 60 * 1000,
|
|
2915
|
+
}),
|
|
2916
|
+
state: projectionScheduleState(),
|
|
2917
|
+
});
|
|
2918
|
+
// Gate off: no projection is created, so the CLI/cron path remains the only
|
|
2919
|
+
// way to build it.
|
|
2920
|
+
assert.equal(probeProjectionHealth(memoryDir).state, "absent");
|
|
2921
|
+
} finally {
|
|
2922
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2923
|
+
}
|
|
2924
|
+
});
|
|
2925
|
+
|
|
2926
|
+
test("scheduled projection rebuild skips when the projection was rebuilt within the interval (#2119)", async () => {
|
|
2927
|
+
const memoryDir = await seedMemoryDirWithOneFact();
|
|
2928
|
+
try {
|
|
2929
|
+
// Seed a projection whose rebuiltAt is one minute ago — inside the 6h
|
|
2930
|
+
// interval, so a scheduled pass must NOT rebuild it again.
|
|
2931
|
+
const oneMinuteAgo = new Date(Date.now() - 60_000);
|
|
2932
|
+
await rebuildMemoryProjection({ memoryDir, dryRun: false, now: oneMinuteAgo });
|
|
2933
|
+
const before = readProjectionRebuiltAt(memoryDir);
|
|
2934
|
+
assert.equal(before, oneMinuteAgo.toISOString());
|
|
2935
|
+
|
|
2936
|
+
await maybeRebuildMemoryProjectionScheduled({
|
|
2937
|
+
config: fixtureConfig({
|
|
2938
|
+
memoryDir,
|
|
2939
|
+
projectionRebuildEnabled: true,
|
|
2940
|
+
projectionRebuildIntervalMs: 6 * 60 * 60 * 1000,
|
|
2941
|
+
}),
|
|
2942
|
+
state: projectionScheduleState(),
|
|
2943
|
+
});
|
|
2944
|
+
// Fresh-within-interval: rebuiltAt is unchanged (no rebuild happened) and no
|
|
2945
|
+
// backup was written (a rebuild would have archived the prior projection).
|
|
2946
|
+
assert.equal(readProjectionRebuiltAt(memoryDir), before, "projection must not be rebuilt when fresh");
|
|
2947
|
+
await assert.rejects(
|
|
2948
|
+
() => readdir(path.join(memoryDir, "archive", "memory-projection")),
|
|
2949
|
+
"no backup means no rebuild occurred",
|
|
2950
|
+
);
|
|
2951
|
+
} finally {
|
|
2952
|
+
await rm(memoryDir, { recursive: true, force: true });
|
|
2953
|
+
}
|
|
2954
|
+
});
|
|
@@ -51,6 +51,10 @@ import {
|
|
|
51
51
|
graphEdgeDecayCadenceToCronExpr,
|
|
52
52
|
} from "../maintenance/memory-governance-cron.js";
|
|
53
53
|
import { rebuildMemoryLifecycleLedger } from "../maintenance/rebuild-memory-lifecycle-ledger.js";
|
|
54
|
+
import {
|
|
55
|
+
createProjectionRebuildScheduleState,
|
|
56
|
+
maybeRebuildMemoryProjectionScheduled,
|
|
57
|
+
} from "./projection-rebuild-schedule.js";
|
|
54
58
|
import {
|
|
55
59
|
drainPendingLifecycleLedgerIfAny,
|
|
56
60
|
pendingLifecycleLedgerDir,
|
|
@@ -134,6 +138,9 @@ export class MaintenanceScheduler {
|
|
|
134
138
|
// fixture.
|
|
135
139
|
private lifecycleLedgerMaxBytes = STATE_FILE_MAX_DECRYPT_BYTES;
|
|
136
140
|
|
|
141
|
+
// ── Memory-projection scheduled-rebuild state (issue #2119) ──
|
|
142
|
+
private readonly projectionRebuild = createProjectionRebuildScheduleState();
|
|
143
|
+
|
|
137
144
|
// ── Activity (screen-capture) sync scheduler (issue #1900) ──
|
|
138
145
|
// The full arm/re-arm/teardown lifecycle lives in ActivitySyncRegistrar.
|
|
139
146
|
private readonly activityRegistrar: ActivitySyncRegistrar;
|
|
@@ -500,6 +507,15 @@ export class MaintenanceScheduler {
|
|
|
500
507
|
void this.maybeCompactMemoryLifecycleLedger().catch((err) =>
|
|
501
508
|
log.debug(`lifecycle ledger auto-compaction check failed (non-fatal): ${err}`),
|
|
502
509
|
);
|
|
510
|
+
// Memory-projection scheduled rebuild (#2119): interval-throttled,
|
|
511
|
+
// single-flighted, never awaited, never throws — see the module docblock.
|
|
512
|
+
void maybeRebuildMemoryProjectionScheduled({
|
|
513
|
+
config: this.deps.config,
|
|
514
|
+
getStorage: this.deps.getStorage,
|
|
515
|
+
state: this.projectionRebuild,
|
|
516
|
+
}).catch((err) =>
|
|
517
|
+
log.debug(`memory projection scheduled rebuild check failed (non-fatal): ${err}`),
|
|
518
|
+
);
|
|
503
519
|
}
|
|
504
520
|
|
|
505
521
|
/** Internal: run a single QMD maintenance pass under singleflight guard. */
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled rebuild of the root memory projection (issue #2119).
|
|
3
|
+
*
|
|
4
|
+
* The projection (`state/memory-projection.sqlite`) is otherwise rebuild-only
|
|
5
|
+
* (manual CLI / tests), so it goes stale as the lifecycle ledger grows and
|
|
6
|
+
* timeline/browse consumers silently fall back to full-corpus scans. The
|
|
7
|
+
* MaintenanceScheduler fires this alongside ledger compaction on every
|
|
8
|
+
* maintenance request — interval-throttled, single-flighted, never awaited,
|
|
9
|
+
* never throws.
|
|
10
|
+
*
|
|
11
|
+
* `projectionRebuildEnabled` gates it (via the lifecycle capabilities
|
|
12
|
+
* resolver, default true); `projectionRebuildIntervalMs` is the cadence.
|
|
13
|
+
*
|
|
14
|
+
* Skipped when the projection was rebuilt within the interval. The freshness
|
|
15
|
+
* check reads the on-disk `rebuiltAt` meta (not just an in-process timestamp)
|
|
16
|
+
* so a daemon restart or an operator's cron `remnic rebuild-memory-projection`
|
|
17
|
+
* both suppress a redundant rebuild. On a real rebuild it logs the lag it just
|
|
18
|
+
* cured (age of the projection before the rebuild) plus the fresh row counts,
|
|
19
|
+
* so operators can see the projection was drifting. Failures are non-fatal
|
|
20
|
+
* and leave the throttle un-advanced to retry.
|
|
21
|
+
*
|
|
22
|
+
* Scope: the root memoryDir projection — the one `getMemoryTimeline` /
|
|
23
|
+
* browse / current-state serve from `baseDir`. Per-namespace projections are
|
|
24
|
+
* not yet scheduled here (they are still served by their own rebuild paths);
|
|
25
|
+
* the fallback WARN surfaces staleness for any namespace that lags.
|
|
26
|
+
*/
|
|
27
|
+
import { log } from "../logger.js";
|
|
28
|
+
import type { PluginConfig } from "../types.js";
|
|
29
|
+
import { resolveMemoryLifecycleCapabilities } from "../capabilities.js";
|
|
30
|
+
import {
|
|
31
|
+
rebuildMemoryProjection,
|
|
32
|
+
type RebuildMemoryProjectionOptions,
|
|
33
|
+
} from "../maintenance/rebuild-memory-projection.js";
|
|
34
|
+
import { readProjectionRebuiltAt } from "../maintenance/projection-support.js";
|
|
35
|
+
|
|
36
|
+
type InjectedStorage = NonNullable<RebuildMemoryProjectionOptions["storage"]>;
|
|
37
|
+
|
|
38
|
+
export interface ProjectionRebuildScheduleState {
|
|
39
|
+
inFlight: boolean;
|
|
40
|
+
lastRebuildAtMs: number;
|
|
41
|
+
/** Timestamp of the most recent FAILURE; gates a short retry backoff that is
|
|
42
|
+
* separate from the success interval so a transient cause (e.g. a locked
|
|
43
|
+
* secure store) retries promptly once cleared instead of waiting a full
|
|
44
|
+
* interval. */
|
|
45
|
+
lastFailureAtMs: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Bounded retry cadence after a failure — short enough to self-heal promptly
|
|
49
|
+
* once a transient cause clears, long enough to keep a persistently-failing
|
|
50
|
+
* rebuild off every maintenance tick. */
|
|
51
|
+
const FAILURE_RETRY_BACKOFF_MS = 5 * 60 * 1000;
|
|
52
|
+
|
|
53
|
+
export function createProjectionRebuildScheduleState(): ProjectionRebuildScheduleState {
|
|
54
|
+
return { inFlight: false, lastRebuildAtMs: 0, lastFailureAtMs: 0 };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function maybeRebuildMemoryProjectionScheduled(opts: {
|
|
58
|
+
config: PluginConfig;
|
|
59
|
+
/** The daemon's live root storage, when available (secure-store unlock). */
|
|
60
|
+
getStorage?: () => InjectedStorage | undefined;
|
|
61
|
+
state: ProjectionRebuildScheduleState;
|
|
62
|
+
}): Promise<void> {
|
|
63
|
+
const { config, state } = opts;
|
|
64
|
+
if (!resolveMemoryLifecycleCapabilities(config).projectionRebuild) return;
|
|
65
|
+
if (state.inFlight) return;
|
|
66
|
+
const intervalMs = config.projectionRebuildIntervalMs;
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
if (now - state.lastRebuildAtMs < intervalMs) return;
|
|
69
|
+
if (now - state.lastFailureAtMs < FAILURE_RETRY_BACKOFF_MS) return;
|
|
70
|
+
|
|
71
|
+
const rootStorage = opts.getStorage?.();
|
|
72
|
+
const memoryDir = rootStorage ? rootStorage.dir : config.memoryDir;
|
|
73
|
+
|
|
74
|
+
// On-disk freshness check: survives restarts and cross-process CLI rebuilds.
|
|
75
|
+
const rebuiltAt = readProjectionRebuiltAt(memoryDir);
|
|
76
|
+
const rebuiltAtMs = rebuiltAt ? Date.parse(rebuiltAt) : Number.NaN;
|
|
77
|
+
if (Number.isFinite(rebuiltAtMs) && now - rebuiltAtMs < intervalMs) {
|
|
78
|
+
// Fresh already — adopt its timestamp so the next pass short-circuits on
|
|
79
|
+
// the cheap in-process guard without re-opening the projection each time.
|
|
80
|
+
state.lastRebuildAtMs = rebuiltAtMs;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
state.inFlight = true;
|
|
85
|
+
try {
|
|
86
|
+
const result = await rebuildMemoryProjection({
|
|
87
|
+
memoryDir,
|
|
88
|
+
// Reuse the daemon's live storage so secure-store deployments rebuild
|
|
89
|
+
// through the unlocked instance instead of a fresh locked one.
|
|
90
|
+
...(rootStorage ? { storage: rootStorage } : {}),
|
|
91
|
+
defaultNamespace: config.defaultNamespace,
|
|
92
|
+
dryRun: false,
|
|
93
|
+
now: new Date(now),
|
|
94
|
+
});
|
|
95
|
+
state.lastRebuildAtMs = Date.now();
|
|
96
|
+
const lag = Number.isFinite(rebuiltAtMs)
|
|
97
|
+
? `${Math.max(0, Math.round((now - rebuiltAtMs) / 60_000))}m stale`
|
|
98
|
+
: "never previously built";
|
|
99
|
+
log.info(
|
|
100
|
+
`memory projection rebuilt (scheduled) for ${memoryDir}: was ${lag}; `
|
|
101
|
+
+ `${result.currentRows} current, ${result.timelineRows} timeline, `
|
|
102
|
+
+ `${result.entityMentionRows} entity-mention rows`,
|
|
103
|
+
);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
// Non-fatal. Arm a SHORT backoff (not the full success interval) so a
|
|
106
|
+
// transient cause (locked secure store, momentarily-unavailable storage)
|
|
107
|
+
// retries promptly once it clears, while a persistently-failing rebuild
|
|
108
|
+
// still stays off every maintenance tick. lastRebuildAtMs is left as-is so
|
|
109
|
+
// the success interval is not falsely advanced.
|
|
110
|
+
state.lastFailureAtMs = Date.now();
|
|
111
|
+
log.warn(`memory projection scheduled rebuild failed (non-fatal) for ${memoryDir}: ${err}`);
|
|
112
|
+
} finally {
|
|
113
|
+
state.inFlight = false;
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/storage-guards.ts
CHANGED
|
@@ -18,13 +18,30 @@ export function assertMemoryFrontmatterId(
|
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export function warnProjectionFallback(
|
|
21
|
+
export function warnProjectionFallback(
|
|
22
|
+
memoryDir: string,
|
|
23
|
+
consumer: string,
|
|
24
|
+
// Lazy so the (potentially I/O-bound) lag computation runs only when this
|
|
25
|
+
// call actually logs, not on every rate-limited-suppressed fallback (#2119).
|
|
26
|
+
detail?: () => string | undefined,
|
|
27
|
+
): null {
|
|
22
28
|
const key = `${memoryDir}\0${consumer}`;
|
|
23
29
|
const now = Date.now();
|
|
24
30
|
const warnedAt = projectionFallbackWarnedAt.get(key) ?? 0;
|
|
25
31
|
if (now - warnedAt >= PROJECTION_FALLBACK_WARN_INTERVAL_MS) {
|
|
26
32
|
projectionFallbackWarnedAt.set(key, now);
|
|
27
|
-
|
|
33
|
+
const suffix = detail?.();
|
|
34
|
+
log.warn(
|
|
35
|
+
`storage.${consumer}: memory projection absent or empty; falling back to full corpus`
|
|
36
|
+
+ (suffix ? ` (${suffix})` : ""),
|
|
37
|
+
);
|
|
28
38
|
}
|
|
29
39
|
return null;
|
|
30
40
|
}
|
|
41
|
+
|
|
42
|
+
/** @internal Test seam (#2119): clear the fallback-warn rate-limit dedup map so
|
|
43
|
+
* a focused test can prove the once-per-interval spam suppression from a clean
|
|
44
|
+
* slate regardless of prior tests in the same process. */
|
|
45
|
+
export function __resetProjectionFallbackWarnSuppressionForTest(): void {
|
|
46
|
+
projectionFallbackWarnedAt.clear();
|
|
47
|
+
}
|
|
@@ -6,6 +6,8 @@ import test from "node:test";
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
|
|
8
8
|
import { StorageManager } from "./storage.js";
|
|
9
|
+
import { initLogger, resetLogger } from "./logger.js";
|
|
10
|
+
import { __resetProjectionFallbackWarnSuppressionForTest } from "./storage-guards.js";
|
|
9
11
|
import { encryptFileBody, filePathAad, isEncryptedFile, readMaybeEncryptedFile, readMaybeEncryptedFileBuffer } from "./secure-store/secure-fs.js";
|
|
10
12
|
import type { MemoryLifecycleEvent } from "./types.js";
|
|
11
13
|
import {
|
|
@@ -1271,3 +1273,33 @@ test("compaction buffer reader skips an oversized unterminated row instead of th
|
|
|
1271
1273
|
"valid rows returned; the oversized row is skipped without throwing",
|
|
1272
1274
|
);
|
|
1273
1275
|
});
|
|
1276
|
+
|
|
1277
|
+
test("getMemoryTimeline fallback WARN is loud with lag and rate-limited when the projection is missing (#2119)", async () => {
|
|
1278
|
+
const warns: string[] = [];
|
|
1279
|
+
initLogger({ info() {}, warn: (m) => warns.push(m), error() {}, debug() {} }, false);
|
|
1280
|
+
__resetProjectionFallbackWarnSuppressionForTest();
|
|
1281
|
+
try {
|
|
1282
|
+
const rows = [
|
|
1283
|
+
JSON.stringify(lifecycleEvent("t1", "memory-target", "2026-01-01T00:01:00.000Z")),
|
|
1284
|
+
JSON.stringify(lifecycleEvent("t2", "memory-target", "2026-01-01T00:02:00.000Z")),
|
|
1285
|
+
];
|
|
1286
|
+
await withLifecycleLedger(rows, async (storage) => {
|
|
1287
|
+
// No projection sqlite exists in this temp dir, so both calls take the
|
|
1288
|
+
// ledger fallback — the exact issue-#2119 regression path.
|
|
1289
|
+
const first = await storage.getMemoryTimeline("memory-target", 5);
|
|
1290
|
+
const second = await storage.getMemoryTimeline("memory-target", 5);
|
|
1291
|
+
assert.equal(first.length, 2);
|
|
1292
|
+
assert.equal(second.length, 2);
|
|
1293
|
+
|
|
1294
|
+
const timelineWarns = warns.filter((w) => w.includes("storage.getMemoryTimeline"));
|
|
1295
|
+
// Rate-limited: two fallbacks, but only ONE WARN in the interval (no spam).
|
|
1296
|
+
assert.equal(timelineWarns.length, 1, "fallback WARN must fire once, not per call");
|
|
1297
|
+
// Loud: the single WARN names the fallback AND the projection lag.
|
|
1298
|
+
assert.match(timelineWarns[0]!, /falling back to full corpus/);
|
|
1299
|
+
assert.match(timelineWarns[0]!, /projection never rebuilt/);
|
|
1300
|
+
});
|
|
1301
|
+
} finally {
|
|
1302
|
+
resetLogger();
|
|
1303
|
+
__resetProjectionFallbackWarnSuppressionForTest();
|
|
1304
|
+
}
|
|
1305
|
+
});
|
package/src/storage.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readProjectionRebuiltAt } from "./maintenance/projection-support.js";
|
|
1
2
|
import {
|
|
2
3
|
access,
|
|
3
4
|
lstat,
|
|
@@ -6584,7 +6585,20 @@ export class StorageManager {
|
|
|
6584
6585
|
|
|
6585
6586
|
const projected = readProjectedMemoryTimeline(this.baseDir, memoryId, cappedLimit);
|
|
6586
6587
|
if (projected && projected.length > 0) return projected;
|
|
6587
|
-
|
|
6588
|
+
// Loud, rate-limited staleness telemetry (#2119): a fallback here means the
|
|
6589
|
+
// projection is missing/empty/stale, so surface HOW stale it is. The lag is
|
|
6590
|
+
// computed lazily — only when warnProjectionFallback actually logs (once per
|
|
6591
|
+
// interval) — so a hot fallback path pays for a projection meta read at most
|
|
6592
|
+
// once per suppression window, not per call.
|
|
6593
|
+
warnProjectionFallback(this.baseDir, "getMemoryTimeline", () => {
|
|
6594
|
+
const rebuiltAt = readProjectionRebuiltAt(this.baseDir);
|
|
6595
|
+
if (!rebuiltAt) return "projection never rebuilt";
|
|
6596
|
+
const ageMs = Date.now() - Date.parse(rebuiltAt);
|
|
6597
|
+
if (!Number.isFinite(ageMs)) return `projection rebuiltAt=${rebuiltAt}`;
|
|
6598
|
+
const ageMin = Math.max(0, Math.round(ageMs / 60_000));
|
|
6599
|
+
const age = ageMin >= 120 ? `${Math.round(ageMin / 60)}h` : `${ageMin}m`;
|
|
6600
|
+
return `projection ${age} stale, last rebuilt ${rebuiltAt}`;
|
|
6601
|
+
});
|
|
6588
6602
|
return readBoundedLifecycleEventsFromLedger(
|
|
6589
6603
|
this.memoryLifecycleLedgerPath, (p) => this.readStorageSecureFile(p), cappedLimit, memoryId);
|
|
6590
6604
|
}
|