@remnic/core 9.69.12 → 9.69.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +11 -11
- package/dist/access-admin-ops-surface.js +4 -4
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +5 -5
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +4 -4
- package/dist/access-cli.js +28 -28
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +11 -11
- package/dist/access-extraction-force-flush.js +4 -4
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http-lifecycle-flush.d.ts +11 -11
- package/dist/access-http-offline-stream.d.ts +11 -11
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +4 -4
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +15 -15
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +4 -4
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +4 -4
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +11 -11
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-Cy0yPsaE.d.ts → access-namespace-preflight-Rzuo1nGR.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +4 -4
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +4 -4
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-operations-batch.js +5 -5
- package/dist/access-operations.d.ts +12 -12
- package/dist/access-operations.js +10 -10
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +4 -4
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +4 -4
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +4 -4
- package/dist/{access-service-Baz5xW5X.d.ts → access-service-Bb2Fykzc.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +4 -4
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +6 -6
- 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 +4 -4
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- 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/buffer.js +4 -4
- 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-9X8WpZNO.d.ts → catalog-DjTLVOYp.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-TEZ2HTC7.js → chunk-33JKYLPX.js} +2 -2
- package/dist/{chunk-MNH2GM4O.js → chunk-3BS2OGDT.js} +6 -19
- package/dist/{chunk-MNH2GM4O.js.map → chunk-3BS2OGDT.js.map} +1 -1
- package/dist/{chunk-5PTS43CH.js → chunk-4LG4CPOC.js} +2 -2
- package/dist/{chunk-Z33VUMKY.js → chunk-5H2G5X4R.js} +2 -2
- package/dist/{chunk-XFPHV65N.js → chunk-5JQV4T5Q.js} +2 -2
- package/dist/{chunk-OASGWNYL.js → chunk-63I7LMLG.js} +2 -2
- package/dist/{chunk-J3NGQ5SC.js → chunk-7GSSHAXC.js} +2 -2
- package/dist/{chunk-X4FIZAVQ.js → chunk-7JV4JST3.js} +854 -42
- package/dist/chunk-7JV4JST3.js.map +1 -0
- package/dist/{chunk-VHAKBUBD.js → chunk-B64IVEOH.js} +2 -2
- package/dist/{chunk-R7FXPG4T.js → chunk-C25N5HWX.js} +2 -2
- package/dist/{chunk-DH3V7RCQ.js → chunk-F2ERDKY2.js} +7 -7
- package/dist/{chunk-LJNLLNEM.js → chunk-HODFGQGT.js} +2 -2
- package/dist/{chunk-B75VCEX5.js → chunk-K2IJH4D3.js} +2 -2
- package/dist/{chunk-227WJVPY.js → chunk-KLSPKMIO.js} +2 -2
- package/dist/{chunk-5P2KNFJZ.js → chunk-MSDIETFC.js} +3 -3
- package/dist/{chunk-HFRGCNFY.js → chunk-OAKATRAN.js} +2 -2
- package/dist/chunk-OGUKSLYZ.js +560 -0
- package/dist/chunk-OGUKSLYZ.js.map +1 -0
- package/dist/{chunk-M6L7HI7R.js → chunk-P6FHFTZ7.js} +16 -1
- package/dist/chunk-P6FHFTZ7.js.map +1 -0
- package/dist/{chunk-LWBSEYW3.js → chunk-PAWIY5OQ.js} +5 -5
- package/dist/{chunk-3ITTF7YH.js → chunk-QVI63XSO.js} +90 -383
- package/dist/chunk-QVI63XSO.js.map +1 -0
- package/dist/{chunk-6TOWUN2R.js → chunk-RE3H6Y2X.js} +2 -2
- package/dist/{chunk-V3HUBF7T.js → chunk-S35IIP5P.js} +2 -2
- package/dist/{chunk-3YKOFCKP.js → chunk-SXHBZXHK.js} +2 -2
- package/dist/{chunk-7NCUHD5U.js → chunk-VAMZYOOV.js} +2 -2
- package/dist/{chunk-PGCZLHRB.js → chunk-W272SWN7.js} +3 -3
- package/dist/{chunk-5HO3QBAD.js → chunk-W6GEMUZM.js} +2 -2
- package/dist/{chunk-UCNQV43K.js → chunk-XQYTSZW3.js} +2 -2
- package/dist/{chunk-GR4C3FHT.js → chunk-ZKZCJRRO.js} +2 -2
- package/dist/{cli-CMtkGBdz.d.ts → cli-DXXy60A4.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +21 -21
- package/dist/coding/export-okf-codegraph.d.ts +1 -1
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +4 -4
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +3 -3
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.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/cross-lingual-recall.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/deep-recall-http-glue.d.ts +11 -11
- package/dist/deep-recall-mcp-tools.d.ts +11 -11
- package/dist/deep-recall-policy-llm.d.ts +1 -1
- package/dist/deep-recall-seeds.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-BZGUecZf.d.ts → dependency-propagation-delivery-37JdQv_3.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-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/eval-trajectory-contracts.d.ts +1 -1
- package/dist/eval-trajectory-store.d.ts +1 -1
- package/dist/eval-trajectory.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +5 -5
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +11 -11
- 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-normalization.d.ts +1 -1
- package/dist/extraction-prompt.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 +5 -5
- 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-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.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-B4iTvT-w.d.ts → index-DquhYqsc.d.ts} +2 -2
- package/dist/index.d.ts +200 -62
- package/dist/index.js +59 -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/location/index.d.ts +3 -3
- package/dist/location/index.js +5 -5
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/{maintenance-BBpNqEeJ.d.ts → maintenance-CizZOycr.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-controller.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +11 -11
- package/dist/memory-subject.d.ts +11 -11
- 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/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +5 -5
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +6 -6
- package/dist/{orchestrator-DglY1H0w.d.ts → orchestrator-DqLcuNMP.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +28 -28
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-C7qRsy6g.d.ts → pipeline-DbJpmcRu.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/{public-http-CU-Cwwdb.d.ts → public-http-NQ3UUp6W.d.ts} +1 -1
- package/dist/{qmd-YRLTa3AE.d.ts → qmd-DA_7fbSz.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-memory-map.d.ts +1 -1
- package/dist/recall-navigate-access.d.ts +1 -1
- package/dist/recall-navigate-disclosure.d.ts +1 -1
- package/dist/recall-navigate.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-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +5 -5
- 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-BVvqVzJX.d.ts → scope-profiles-BpiWKZzx.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-BzCI6Kuu.d.ts → semantic-consolidation-NJpaZpkY.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/{service-DQ3Gscy5.d.ts → service-DdlZjX1D.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 +4 -4
- package/dist/standup/build.js +2 -2
- package/dist/{storage-DmTbQdD0.d.ts → storage-C7BWrpU9.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/subject-config.d.ts +1 -1
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +14 -14
- package/dist/support-passport/index.js +7 -7
- 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/transfer/export-okf.d.ts +1 -1
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-CLRTESSD.d.ts → types-Cy1l6fzp.d.ts} +48 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/who-knows-cli.d.ts +11 -11
- package/dist/who-knows.d.ts +11 -11
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/activity/config.test.ts +94 -1
- package/src/activity/config.ts +256 -3
- package/src/activity/digest.ts +21 -0
- package/src/activity/index.ts +2 -0
- package/src/activity/runner.ts +2 -17
- package/src/activity/timeline/publish-cli.test.ts +169 -0
- package/src/activity/timeline/publish-cli.ts +144 -0
- package/src/activity/timeline/query.ts +51 -15
- package/src/activity/types.ts +45 -0
- package/src/activity/vault-insert.ts +14 -33
- package/src/activity/vault-path.ts +100 -22
- package/src/activity/vault-publish.test.ts +51 -0
- package/src/activity/vault-publish.ts +157 -17
- package/src/activity/vault-publisher.test.ts +881 -0
- package/src/activity/vault-publisher.ts +597 -0
- package/src/activity/vault-region.test.ts +5 -0
- package/src/activity/vault-region.ts +7 -2
- package/src/cli/timeline-publish-commands.ts +34 -0
- package/src/cli.ts +2 -2
- package/src/config.test.ts +24 -1
- package/dist/chunk-3ITTF7YH.js.map +0 -1
- package/dist/chunk-KJUWWN3T.js +0 -237
- package/dist/chunk-KJUWWN3T.js.map +0 -1
- package/dist/chunk-M6L7HI7R.js.map +0 -1
- package/dist/chunk-X4FIZAVQ.js.map +0 -1
- /package/dist/{chunk-TEZ2HTC7.js.map → chunk-33JKYLPX.js.map} +0 -0
- /package/dist/{chunk-5PTS43CH.js.map → chunk-4LG4CPOC.js.map} +0 -0
- /package/dist/{chunk-Z33VUMKY.js.map → chunk-5H2G5X4R.js.map} +0 -0
- /package/dist/{chunk-XFPHV65N.js.map → chunk-5JQV4T5Q.js.map} +0 -0
- /package/dist/{chunk-OASGWNYL.js.map → chunk-63I7LMLG.js.map} +0 -0
- /package/dist/{chunk-J3NGQ5SC.js.map → chunk-7GSSHAXC.js.map} +0 -0
- /package/dist/{chunk-VHAKBUBD.js.map → chunk-B64IVEOH.js.map} +0 -0
- /package/dist/{chunk-R7FXPG4T.js.map → chunk-C25N5HWX.js.map} +0 -0
- /package/dist/{chunk-DH3V7RCQ.js.map → chunk-F2ERDKY2.js.map} +0 -0
- /package/dist/{chunk-LJNLLNEM.js.map → chunk-HODFGQGT.js.map} +0 -0
- /package/dist/{chunk-B75VCEX5.js.map → chunk-K2IJH4D3.js.map} +0 -0
- /package/dist/{chunk-227WJVPY.js.map → chunk-KLSPKMIO.js.map} +0 -0
- /package/dist/{chunk-5P2KNFJZ.js.map → chunk-MSDIETFC.js.map} +0 -0
- /package/dist/{chunk-HFRGCNFY.js.map → chunk-OAKATRAN.js.map} +0 -0
- /package/dist/{chunk-LWBSEYW3.js.map → chunk-PAWIY5OQ.js.map} +0 -0
- /package/dist/{chunk-6TOWUN2R.js.map → chunk-RE3H6Y2X.js.map} +0 -0
- /package/dist/{chunk-V3HUBF7T.js.map → chunk-S35IIP5P.js.map} +0 -0
- /package/dist/{chunk-3YKOFCKP.js.map → chunk-SXHBZXHK.js.map} +0 -0
- /package/dist/{chunk-7NCUHD5U.js.map → chunk-VAMZYOOV.js.map} +0 -0
- /package/dist/{chunk-PGCZLHRB.js.map → chunk-W272SWN7.js.map} +0 -0
- /package/dist/{chunk-5HO3QBAD.js.map → chunk-W6GEMUZM.js.map} +0 -0
- /package/dist/{chunk-UCNQV43K.js.map → chunk-XQYTSZW3.js.map} +0 -0
- /package/dist/{chunk-GR4C3FHT.js.map → chunk-ZKZCJRRO.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/orchestration/maintenance.ts","../src/maintenance/day-summary-cron.ts","../src/orchestration/projection-rebuild-schedule.ts","../src/activity/reindex.ts","../src/activity/store.ts","../src/activity/pipeline.ts","../src/activity/source-client.ts","../src/activity/runner.ts","../src/activity/scheduler.ts","../src/activity/sync-registration.ts"],"sourcesContent":["/**\n * Maintenance scheduler — extracted from the orchestrator (issue #1526 PR1).\n *\n * Owns the WHEN of memory maintenance:\n * - cron auto-registration (writes jobs.json entries the OpenClaw cron\n * daemon picks up)\n * - debounced + singleflight QMD index maintenance (re-index + embed\n * after writes)\n * - consolidation scheduling trigger (count + interval gated)\n *\n * Does NOT own the WHAT — the actual job runners (runSemanticConsolidation,\n * runPatternReinforcement, runDeepSleepGovernanceNow, runConsolidation)\n * remain on the orchestrator because they need the full LLM + extraction\n * dependency surface. The scheduler invokes consolidation through a\n * callback so the orchestrator stays the source of truth for that work.\n *\n * Behavior-preserving move from orchestrator.ts. No logic changes — the\n * orchestrator keeps thin delegating methods so existing tests that stub\n * `orchestrator.requestQmdMaintenance` etc. continue to work.\n */\n\nimport { resolveNamespaceCapabilities,\n resolveQmdCapabilities,resolveConsolidationCapabilities, resolveRecallAuxiliaryCapabilities } from \"../capabilities.js\";\nimport { existsSync, type Dirent } from \"node:fs\";\nimport { appendFile, lstat, mkdir, readFile, readdir, realpath, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { PluginConfig } from \"../types.js\";\nimport type { SearchBackend } from \"../search/port.js\";\nimport type { StorageManager } from \"../index.js\";\nimport {\n NamespaceSearchRouter,\n type NamespaceUpdateResult,\n} from \"../namespaces/search.js\";\nimport type { NamespaceCatalog, NamespaceRecord } from \"../namespaces/catalog.js\";\nimport { namespaceIdentityFromToken } from \"../namespaces/identity.js\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport {\n planNamespaceMaintenance,\n runNamespaceMaintenanceBatchPlan,\n type NamespaceMaintenancePlan,\n type NamespaceMaintenanceSkipReason,\n} from \"../maintenance/namespace-planner.js\";\nimport {\n ensureContradictionScanCron,\n ensureDaySummaryCron,\n ensureGraphEdgeDecayCron,\n ensureNightlyGovernanceCron,\n ensurePatternReinforcementCron,\n ensureProceduralMiningCron,\n graphEdgeDecayCadenceToCronExpr,\n} from \"../maintenance/memory-governance-cron.js\";\nimport { rebuildMemoryLifecycleLedger } from \"../maintenance/rebuild-memory-lifecycle-ledger.js\";\nimport { resolveDaySummaryCronModel } from \"../maintenance/day-summary-cron.js\";\nimport {\n createProjectionRebuildScheduleState,\n maybeRebuildMemoryProjectionScheduled,\n} from \"./projection-rebuild-schedule.js\";\nimport {\n drainPendingLifecycleLedgerIfAny,\n pendingLifecycleLedgerDir,\n type LifecyclePendingIo,\n} from \"../storage/memory-lifecycle-ledger-access.js\";\nimport { STATE_FILE_MAX_DECRYPT_BYTES } from \"../storage/secure-line-reader.js\";\nimport { startLocationAutoSync, type LocationAutoSyncHandle } from \"../location/scheduler.js\";\nimport { ActivitySyncRegistrar } from \"../activity/sync-registration.js\";\nimport type { ActivitySyncRunSummary } from \"../activity/runner.js\";\nimport { resolveHomeDir } from \"../runtime/env.js\";\nimport { log } from \"../logger.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { assertPathInsideRoot, listContainedSpillFiles } from \"../utils/path-containment.js\";\nimport {\n probeEncryptedRegularFileHeader,\n SECURE_STORE_ENVELOPE_OVERHEAD_BYTES,\n} from \"../secure-store/secure-fs.js\";\n\n/** Reason a QMD maintenance pass was skipped, used for status recording. */\nfunction qmdMaintenanceSkipReasonForError(\n error: unknown,\n): NamespaceMaintenanceSkipReason | null {\n const message = error instanceof Error ? error.message : String(error);\n return /^QMD (?:update|embed) skipped by .*min-interval gate$/.test(message)\n ? \"throttled\"\n : null;\n}\n\n/** Dependencies injected by the orchestrator. All stable references. */\nexport interface MaintenanceSchedulerDeps {\n config: PluginConfig;\n /** Live accessor — the orchestrator can reassign its qmd backend after\n * construction (e.g. swap to NoopSearchBackend when the collection is\n * missing), and the scheduler must always observe the current backend. */\n getQmd: () => SearchBackend;\n namespaceSearchRouter: NamespaceSearchRouter;\n namespaceCatalog: NamespaceCatalog;\n /**\n * Root storage (secure-store configured). Threaded so lifecycle-ledger\n * auto-compaction (#1910) rebuilds through the live secure context: encrypted\n * memories stay decryptable and the rewritten ledger stays encrypted at rest.\n * Omitted in focused tests, where compaction falls back to a fresh plaintext\n * StorageManager (the CLI-equivalent path).\n */\n getStorage?: () => StorageManager;\n /**\n * Resolve a namespace's (secure-store aware) storage. Threaded so\n * auto-compaction also bounds per-namespace lifecycle ledgers under\n * `memoryDir/namespaces/<token>/state/`, not just the root state path\n * (#1910). Consulted only when namespaces are enabled.\n */\n storageForNamespace?: (namespace: string) => Promise<StorageManager>;\n /**\n * Tail step (issue #1900): observe a durable activity sync tick so the\n * meetings builder can rebuild the affected day(s). Optional; omitted by\n * hosts that do not build meetings.\n */\n onActivitySynced?: (summary: ActivitySyncRunSummary) => void;\n}\n\n/**\n * Schedules and debounces memory maintenance work. Owns the cadence +\n * singleflight state that previously lived as private orchestrator fields.\n *\n * The orchestrator constructs one instance in its constructor and delegates\n * the public scheduling entrypoints to it. Job execution stays on the\n * orchestrator (LLM-heavy) and is invoked through callbacks.\n */\nexport class MaintenanceScheduler {\n // ── Consolidation scheduling state ──\n private nonZeroExtractionsSinceConsolidation = 0;\n private lastConsolidationRunAtMs = 0;\n private consolidationRunPromise: Promise<unknown> | null = null;\n private disposed = false;\n\n // ── QMD maintenance scheduling state ──\n private qmdMaintenanceTimer: NodeJS.Timeout | null = null;\n private qmdMaintenancePending = false;\n private qmdMaintenanceInFlight = false;\n private lastQmdEmbedAtMs = 0;\n private lastQmdEmbedAtMsByNamespace = new Map<string, number>();\n\n // ── Lifecycle-ledger auto-compaction state (issue #1910) ──\n private lifecycleCompactionInFlight = false;\n private lastLifecycleCompactionAtMs = 0;\n // Upper bound on a rewritten ledger; a preserving compaction is bounded to\n // this so it can never leave the ledger over the whole-file read/decrypt cap\n // (#2033). Defaults to the decrypt cap; overridable in focused tests to drive\n // the bounding + post-write verification deterministically without a 400MB\n // fixture.\n private lifecycleLedgerMaxBytes = STATE_FILE_MAX_DECRYPT_BYTES;\n\n // ── Memory-projection scheduled-rebuild state (issue #2119) ──\n private readonly projectionRebuild = createProjectionRebuildScheduleState();\n\n // ── Activity (screen-capture) sync scheduler (issue #1900) ──\n // The full arm/re-arm/teardown lifecycle lives in ActivitySyncRegistrar.\n private readonly activityRegistrar: ActivitySyncRegistrar;\n // ── Location day-sync scheduler (issue #2047) — master default-off; the\n // timer + lifecycle contract lives in location/scheduler.ts.\n private locationAutoSync: LocationAutoSyncHandle | null = null;\n\n\n constructor(private readonly deps: MaintenanceSchedulerDeps) {\n this.activityRegistrar = new ActivitySyncRegistrar({\n config: deps.config.activity,\n memoryDir: deps.config.memoryDir,\n qmdCollection: deps.config.qmdCollection,\n secureStoreEnabled: resolveRecallAuxiliaryCapabilities(deps.config).secureStore,\n getQmd: () => deps.getQmd(),\n requestReindexRetry: () => this.requestQmdMaintenanceForTool(\"activity-reindex-retry\"),\n ...(deps.onActivitySynced ? { onSynced: deps.onActivitySynced } : {}),\n });\n }\n\n // ───────────────────────────────────────────────────────────────────────\n // Cron auto-registration\n // ───────────────────────────────────────────────────────────────────────\n\n /**\n * Register every cron job the daemon should run, gated by its config flag.\n * Each registration is independent and non-fatal — a failure in one does\n * not block the others (mirrors the orchestrator's prior sequential try/\n * catch behavior exactly).\n *\n * Returns when all eligible registrations have completed so callers that\n * `await deferredReady` can rely on jobs.json being current.\n */\n async autoRegisterCrons(signal: AbortSignal): Promise<void> {\n if (resolveRecallAuxiliaryCapabilities(this.deps.config).daySummary) {\n try {\n await this.autoRegisterDaySummaryCron();\n } catch (err) {\n log.debug(`day-summary cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (this.deps.config.nightlyGovernanceCronAutoRegister) {\n try {\n await this.autoRegisterNightlyGovernanceCron();\n } catch (err) {\n log.debug(`nightly governance cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (this.deps.config.procedural?.proceduralMiningCronAutoRegister) {\n try {\n await this.autoRegisterProceduralMiningCron();\n } catch (err) {\n log.debug(`procedural mining cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (this.deps.config.contradictionScan?.enabled) {\n try {\n await this.autoRegisterContradictionScanCron();\n } catch (err) {\n log.debug(`contradiction scan cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (resolveConsolidationCapabilities(this.deps.config).patternReinforcement) {\n try {\n await this.autoRegisterPatternReinforcementCron();\n } catch (err) {\n log.debug(`pattern reinforcement cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (resolveConsolidationCapabilities(this.deps.config).graphEdgeDecay) {\n try {\n await this.autoRegisterGraphEdgeDecayCron();\n } catch (err) {\n log.debug(`graph edge decay cron auto-register failed (non-fatal): ${err}`);\n }\n }\n\n // Activity (screen-capture) in-process sync scheduler (issue #1900),\n // master default-off. The registrar owns the arm/re-arm/teardown contract\n // (stop-prior, abort/teardown guards, secure-store refusal, retry latch).\n await this.activityRegistrar.register(signal);\n // Location day sync (issue #2047): same deferred lifecycle as activity;\n // ticks run the SAME shared runner as the CLI/MCP/HTTP surfaces.\n if (\n !signal.aborted &&\n this.deps.config.location.enabled &&\n this.deps.config.location.sources.some((source) => source.enabled)\n ) {\n this.locationAutoSync = startLocationAutoSync({\n config: this.deps.config.location,\n memoryDir: this.deps.config.memoryDir,\n });\n }\n }\n\n async autoRegisterDaySummaryCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"day-summary cron: jobs.json not found, skipping auto-register\");\n return;\n }\n\n const { model, fallbacks } = resolveDaySummaryCronModel(this.deps.config);\n\n const timezone =\n this.deps.config.daySummaryTimezone ||\n Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n const result = await ensureDaySummaryCron(jobsPath, {\n timezone,\n ...(model ? { model } : {}),\n ...(fallbacks.length > 0 ? { fallbacks } : {}),\n });\n if (result.created) {\n log.info(\n `day-summary cron auto-registered (${result.jobId}, 23:47 ${timezone}${model ? `, model: ${model}` : \"\"})`,\n );\n } else if (result.updated) {\n log.info(\n `day-summary cron reconciled (${result.jobId}, timezone: ${timezone}${model ? `, model: ${model}` : \"\"})`,\n );\n } else {\n log.debug(\"day-summary cron already up to date\");\n }\n } catch (err) {\n log.debug(`day-summary cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterNightlyGovernanceCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"nightly governance cron: jobs.json not found, skipping auto-register\");\n return;\n }\n\n const created = await ensureNightlyGovernanceCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(\n `nightly governance cron auto-registered (${created.jobId}, 02:23 ${Intl.DateTimeFormat().resolvedOptions().timeZone})`,\n );\n } else {\n log.debug(\"nightly governance cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`nightly governance cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterProceduralMiningCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"procedural mining cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const created = await ensureProceduralMiningCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(`procedural mining cron auto-registered (${created.jobId})`);\n } else {\n log.debug(\"procedural mining cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`procedural mining cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterContradictionScanCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"contradiction scan cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const created = await ensureContradictionScanCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(`contradiction scan cron auto-registered (${created.jobId})`);\n } else {\n log.debug(\"contradiction scan cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`contradiction scan cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterPatternReinforcementCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"pattern reinforcement cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const created = await ensurePatternReinforcementCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(`pattern reinforcement cron auto-registered (${created.jobId})`);\n } else {\n log.debug(\"pattern reinforcement cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`pattern reinforcement cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterGraphEdgeDecayCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"graph edge decay cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const scheduleExpr = graphEdgeDecayCadenceToCronExpr(\n this.deps.config.graphEdgeDecayCadenceMs,\n );\n const created = await ensureGraphEdgeDecayCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n scheduleExpr,\n });\n if (created.created) {\n log.info(`graph edge decay cron auto-registered (${created.jobId}, ${scheduleExpr})`);\n } else {\n log.debug(\"graph edge decay cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`graph edge decay cron auto-register error: ${err}`);\n }\n }\n\n // ───────────────────────────────────────────────────────────────────────\n // Consolidation scheduling\n // ───────────────────────────────────────────────────────────────────────\n\n /**\n * Maybe trigger a background consolidation pass after an extraction.\n * Counted + interval gated so a burst of extractions does not fan into a\n * burst of consolidation passes. The consolidation runner itself is\n * passed in as a callback because it lives on the orchestrator (LLM +\n * storage dependency surface) and is not owned by this module.\n */\n maybeScheduleConsolidation(\n nonZeroExtraction: boolean,\n runConsolidation: () => Promise<unknown>,\n ): void {\n if (nonZeroExtraction) this.nonZeroExtractionsSinceConsolidation += 1;\n if (\n this.deps.config.consolidationRequireNonZeroExtraction &&\n !nonZeroExtraction\n ) {\n return;\n }\n if (\n this.nonZeroExtractionsSinceConsolidation <\n this.deps.config.consolidateEveryN\n ) {\n return;\n }\n\n const now = Date.now();\n if (\n now - this.lastConsolidationRunAtMs <\n this.deps.config.consolidationMinIntervalMs\n ) {\n return;\n }\n if (this.disposed || this.consolidationRunPromise) return;\n\n this.lastConsolidationRunAtMs = now;\n this.nonZeroExtractionsSinceConsolidation = 0;\n this.consolidationRunPromise = Promise.resolve()\n .then(runConsolidation)\n .catch((err) => {\n log.error(\"background consolidation failed\", err);\n })\n .finally(() => {\n this.consolidationRunPromise = null;\n });\n }\n\n /** Whether a background consolidation pass is currently running. Exposed so\n * the orchestrator's waitForConsolidationIdle can poll idle state without\n * owning the cadence state (issue #1526 PR1). */\n isConsolidationInFlight(): boolean {\n return this.consolidationRunPromise !== null;\n }\n\n /** Whether a debounced QMD maintenance pass is armed and waiting for the\n * debounce timer. Exposed so drain/teardown diagnostics (e.g. the bench\n * adapter) can observe scheduler idle state after the #1526 PR1 extraction\n * moved this flag off the orchestrator. */\n isQmdMaintenancePending(): boolean {\n return this.qmdMaintenancePending;\n }\n\n /** Whether a QMD maintenance pass is currently running (singleflight guard).\n * Exposed for drain/teardown diagnostics (see isQmdMaintenancePending). */\n isQmdMaintenanceInFlight(): boolean {\n return this.qmdMaintenanceInFlight;\n }\n // ───────────────────────────────────────────────────────────────────────\n // QMD maintenance scheduling (debounced + singleflight)\n // ───────────────────────────────────────────────────────────────────────\n\n /** Internal: queue a debounced QMD maintenance pass. */\n private requestQmdMaintenance(): void {\n if (!this.deps.getQmd().isAvailable()) return;\n if (!resolveQmdCapabilities(this.deps.config).qmdMaintenance) return;\n\n this.qmdMaintenancePending = true;\n if (this.qmdMaintenanceTimer) return;\n\n this.qmdMaintenanceTimer = setTimeout(() => {\n this.qmdMaintenanceTimer = null;\n this.runQmdMaintenance().catch((err) =>\n log.debug(`background qmd maintenance failed: ${err}`),\n );\n }, this.deps.config.qmdMaintenanceDebounceMs);\n }\n\n /**\n * Public entrypoint for tool-driven QMD maintenance requests.\n * Routes through existing debounced/singleflight maintenance controls.\n */\n requestQmdMaintenanceForTool(reason: string): void {\n try {\n this.requestQmdMaintenance();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log.warn(`qmd maintenance request failed (${reason}): ${msg}`);\n }\n // Lifecycle-ledger auto-compaction (#1910) must run independently of QMD.\n // When QMD is unavailable or its maintenance capability is off,\n // requestQmdMaintenance() short-circuits and runQmdMaintenance() never\n // fires — so a compaction check living only in that path would never run.\n // Fire it here on every maintenance request (throttled, size-gated,\n // single-flighted, never awaited, never throws) so a QMD-off deployment\n // still bounds the lifecycle ledger.\n void this.maybeCompactMemoryLifecycleLedger().catch((err) =>\n log.debug(`lifecycle ledger auto-compaction check failed (non-fatal): ${err}`),\n );\n // Memory-projection scheduled rebuild (#2119): interval-throttled,\n // single-flighted, never awaited, never throws — see the module docblock.\n void maybeRebuildMemoryProjectionScheduled({\n config: this.deps.config,\n getStorage: this.deps.getStorage,\n state: this.projectionRebuild,\n }).catch((err) =>\n log.debug(`memory projection scheduled rebuild check failed (non-fatal): ${err}`),\n );\n }\n\n /** Internal: run a single QMD maintenance pass under singleflight guard. */\n async runQmdMaintenance(): Promise<void> {\n if (this.qmdMaintenanceInFlight) return;\n if (!this.qmdMaintenancePending) return;\n this.qmdMaintenanceInFlight = true;\n this.qmdMaintenancePending = false;\n\n try {\n if (resolveNamespaceCapabilities(this.deps.config).namespaces) {\n // Include cataloged dynamic namespaces, not just the configured set\n // (NGnei), but run through the namespace-aware maintenance planner so\n // each namespace is budgeted, lock-protected, and status-recorded\n // independently (issue #1500).\n const plan = await this.namespaceMaintenancePlan(\"qmd\");\n const now = Date.now();\n const lastEmbedAtByNamespace = this.lastQmdEmbedAtMsByNamespace;\n const dueEmbedNamespaces = (namespaces: string[]): string[] => {\n if (!resolveQmdCapabilities(this.deps.config).qmdAutoEmbed) return [];\n return namespaces.filter(\n (namespace) =>\n now - (lastEmbedAtByNamespace.get(namespace) ?? 0) >=\n this.deps.config.qmdEmbedMinIntervalMs,\n );\n };\n const markEmbedded = (namespaces: string[]): void => {\n if (namespaces.length === 0) return;\n for (const namespace of namespaces) {\n lastEmbedAtByNamespace.set(namespace, now);\n }\n this.lastQmdEmbedAtMs = now;\n };\n await runNamespaceMaintenanceBatchPlan(\n this.deps.config,\n plan,\n async (candidates) => {\n const namespaces = candidates.map((candidate) => candidate.namespace);\n const embedNamespaces = dueEmbedNamespaces(namespaces);\n let result: NamespaceUpdateResult;\n try {\n result = await this.deps.namespaceSearchRouter.updateNamespacesDetailed(\n namespaces,\n undefined,\n { strict: true },\n );\n } catch (error) {\n if (\n embedNamespaces.length > 0 &&\n qmdMaintenanceSkipReasonForError(error) === \"throttled\"\n ) {\n await this.deps.namespaceSearchRouter.embedNamespaces(embedNamespaces, {\n strict: true,\n });\n markEmbedded(embedNamespaces);\n }\n throw error;\n }\n if (result.backendCount <= 0) {\n throw new Error(\"no eligible QMD backend for selected namespaces\");\n }\n if (result.eligibleNamespaces.length !== namespaces.length) {\n const eligible = new Set(result.eligibleNamespaces);\n const missing = namespaces.filter(\n (namespace) => !eligible.has(namespace),\n );\n throw new Error(\n `QMD backend ineligible for selected namespaces (${missing.length})`,\n );\n }\n if (embedNamespaces.length > 0) {\n await this.deps.namespaceSearchRouter.embedNamespaces(embedNamespaces, {\n strict: true,\n });\n markEmbedded(embedNamespaces);\n }\n return { itemCount: result.backendCount };\n },\n this.deps.namespaceCatalog,\n {\n skipReasonForError: qmdMaintenanceSkipReasonForError,\n },\n );\n } else {\n await this.deps.getQmd().update();\n const now = Date.now();\n if (\n resolveQmdCapabilities(this.deps.config).qmdAutoEmbed &&\n now - this.lastQmdEmbedAtMs >= this.deps.config.qmdEmbedMinIntervalMs\n ) {\n await this.deps.getQmd().embed();\n this.lastQmdEmbedAtMs = now;\n }\n }\n // Note: a successful QMD update/embed clears the global QMD result caches\n // itself (QmdClient.runUpdateForCollection / runEmbedForCollection), so\n // this scheduler does not invalidate them here — every refresh path\n // (direct update, namespace router, wearable/OpenClaw sync) funnels through\n // the backend and clears centrally. A no-op pass (throttle/unavailable/\n // backoff) does not advance lastUpdateRanAtMs, so it leaves caches warm\n // (#1904, Codex/Cursor).\n } finally {\n this.qmdMaintenanceInFlight = false;\n if (this.qmdMaintenancePending) {\n this.requestQmdMaintenance();\n }\n // Lifecycle-ledger auto-compaction is triggered from\n // requestQmdMaintenanceForTool (every maintenance request, QMD or not),\n // so it does not need to be re-fired here (issue #1910).\n }\n }\n\n /**\n * Size-gated, throttled, single-flighted auto-compaction of the lifecycle\n * ledger (issue #1910). Compacts the root ledger AND every namespace ledger\n * (`memoryDir/namespaces/<token>/state/`) when namespaces are enabled, each\n * through the existing `rebuildMemoryLifecycleLedger` so the\n * archive-then-atomic-write discipline is reused verbatim. `0` disables.\n *\n * Before compacting, drain EVERY target's durable pending-append spill (the\n * root ledger AND each namespace ledger) so a lifecycle event that spilled\n * while a prior long rewrite held the lock is folded back into its ledger and\n * cannot be silently lost (issue #2033). Draining is independent of both the\n * compaction threshold and the min-interval throttle: targets are enumerated\n * through the same safe (symlink/containment-checked) resolver and every\n * eligible queue is drained BEFORE any throttle return, so a namespace spill\n * created after the previous pass never waits out the whole interval.\n * Encrypted-store safety is preserved — a keyed target drains through its\n * secure StorageManager, a filesystem-fallback target only through a plaintext\n * context that refuses encrypted ledgers/spills. Work stays bounded: one\n * target enumeration plus a bounded per-queue drain.\n *\n * The min-interval throttle timestamp advances ONLY after real, fully\n * successful compaction work: a failed OR deferred target leaves the throttle\n * un-advanced so it stays eligible on the next maintenance pass, while\n * `lifecycleCompactionInFlight` still prevents overlapping runs. A \"deferred\"\n * target is oversized but could not be compacted because its encrypted ledger\n * has no unlocked secure store — arming the throttle for it would suppress the\n * retry of that still-oversized ledger for the whole interval (#2033).\n *\n * The min-interval throttle is bypassed for one case: an encrypted ledger\n * already at/over the whole-file decrypt cap is unreadable, so it must be\n * clamped on the next pass rather than wait out the interval. Ordinary\n * below-cap (or plaintext) targets stay throttled (#2033).\n */\n private async maybeCompactMemoryLifecycleLedger(): Promise<void> {\n // Enumerate every lifecycle target (root + namespaces) through the safe,\n // containment-checked resolver, and fold each one's durable pending spill\n // back into its ledger BEFORE any threshold or throttle return (#2033). A\n // spill created after the previous pass must land promptly even while\n // compaction is throttled or disabled; draining is bounded per queue and\n // never bypasses encrypted-store safety.\n const targets = await this.resolveLifecycleCompactionTargets();\n for (const target of targets) {\n await this.drainPendingForTarget(target);\n }\n\n const threshold = this.deps.config.memoryLifecycleLedgerCompactBytes;\n if (!(threshold > 0)) return; // 0 / negative / non-numeric disables compaction.\n if (this.lifecycleCompactionInFlight) return;\n const now = Date.now();\n if (\n now - this.lastLifecycleCompactionAtMs <\n this.deps.config.memoryLifecycleLedgerCompactMinIntervalMs &&\n !(await this.hasOverCapEncryptedLifecycleLedger(targets))\n ) {\n // Throttled: no ordinary target is due yet. The exception is an encrypted\n // ledger already at/over the whole-file decrypt cap — it is unreadable, so\n // the global min-interval must not defer clamping it for a whole interval.\n // The guard above lets such a target through immediately (compaction then\n // clamps it via the cap-aware effective threshold) while ordinary\n // below-cap targets stay throttled (#2033).\n return;\n }\n // The over-cap probe above awaits, so a second maintenance request can pass\n // the early `lifecycleCompactionInFlight` guard and reach here during that\n // await. Recheck the guard immediately before claiming it — otherwise two\n // racing requests both see an over-cap ledger and run duplicate 400MB-class\n // compactions/backups back-to-back (#2033). The set below is synchronous\n // with this recheck (no await between), so exactly one caller wins.\n if (this.lifecycleCompactionInFlight) return;\n this.lifecycleCompactionInFlight = true;\n try {\n let compacted = 0;\n let failed = 0;\n let deferred = 0;\n for (const target of targets) {\n const outcome = await this.compactLifecycleLedgerTarget(target, threshold);\n if (outcome === \"compacted\") compacted += 1;\n else if (outcome === \"failed\") failed += 1;\n else if (outcome === \"deferred\") deferred += 1;\n }\n // Arm the throttle only when at least one ledger actually compacted AND\n // nothing failed AND nothing was deferred. A below-threshold no-op\n // (compacted === 0), any failure, or a deferred oversized ledger (locked\n // encrypted store, still pending) leaves the throttle where it was so the\n // next pass retries the untouched targets (#2033).\n if (compacted > 0 && failed === 0 && deferred === 0) {\n this.lastLifecycleCompactionAtMs = now;\n }\n } finally {\n this.lifecycleCompactionInFlight = false;\n }\n }\n\n /**\n * True when any lifecycle target's ledger is encrypted at rest AND its\n * on-disk size is at/over the whole-file decrypt cap. Such a ledger is already\n * unreadable (the secure reader refuses at/over the cap), so it must reach the\n * bounded compaction path on the next maintenance pass regardless of the\n * global min-interval throttle — otherwise the throttle could keep an\n * unreadable ledger un-clamped for a whole interval (#2033). Ordinary\n * below-cap or plaintext targets never match, so the throttle still governs\n * them. A stat/probe failure yields \"no forced target\" and is left to the\n * normal compaction path, which classifies it as failed and retries.\n */\n private async hasOverCapEncryptedLifecycleLedger(\n targets: Array<{ memoryDir: string; storage?: StorageManager }>,\n ): Promise<boolean> {\n for (const target of targets) {\n const ledgerPath = path.join(target.memoryDir, \"state\", \"memory-lifecycle-ledger.jsonl\");\n let size: number;\n try {\n size = (await stat(ledgerPath)).size;\n } catch {\n continue; // absent/unstattable — nothing to force here.\n }\n if (size < this.lifecycleLedgerMaxBytes) continue;\n try {\n if (await probeEncryptedRegularFileHeader(ledgerPath)) return true;\n } catch {\n continue; // probe failure — the normal compaction path reports it.\n }\n }\n return false;\n }\n\n /**\n * Drain one compaction target's durable pending spill. A catalog-backed\n * target folds through its secure StorageManager; a filesystem-fallback\n * target (no storage) folds through a path-scoped plaintext context that\n * refuses encrypted ledgers/spills so a keyless drain never downgrades an\n * encrypted ledger (issue #2033).\n */\n private async drainPendingForTarget(\n target: { memoryDir: string; storage?: StorageManager },\n ): Promise<void> {\n if (target.storage) {\n await this.drainPendingLifecycleAppends(target.storage);\n } else {\n await this.drainPendingLifecycleAppendsForPath(target.memoryDir);\n }\n }\n\n /** Drain a target's durable pending lifecycle-append spill into its ledger\n * (issue #2033); non-fatal and a fast no-op when nothing is pending. */\n private async drainPendingLifecycleAppends(storage: StorageManager | undefined): Promise<void> {\n if (!storage) return;\n try {\n await storage.drainPendingMemoryLifecycleEvents();\n } catch (err) {\n log.debug(`lifecycle pending drain failed (non-fatal): ${err}`);\n }\n }\n\n /**\n * Drain a filesystem-fallback target's pending lifecycle-append spill when no\n * secure StorageManager is available (catalog-disabled namespace; issue\n * #2033). Uses the safe path-scoped PLAINTEXT context this fallback already\n * operates in: a keyless drain can only fold plaintext spills into a plaintext\n * ledger, so it refuses when the ledger or ANY spill is encrypted at rest,\n * leaving those pending for a keyed (catalog-path) drain — exactly mirroring\n * the fallback compaction's encrypted-ledger deferral. Non-fatal; a fast\n * no-op when nothing is pending. The shared spill lister enforces the symlink/\n * containment guard on every spill file it reads or deletes.\n */\n private async drainPendingLifecycleAppendsForPath(memoryDir: string): Promise<void> {\n const ledgerPath = path.join(memoryDir, \"state\", \"memory-lifecycle-ledger.jsonl\");\n const spillDir = pendingLifecycleLedgerDir(ledgerPath);\n // Enumerate spills through the SAME guarded lister the actual drain uses\n // (listContainedSpillFiles): it rejects a symlinked/non-directory spill dir\n // and skips any symlink/FIFO/device/escaping entry, returning only regular\n // files contained in the dir. Never raw-readdir + open() an untrusted name:\n // opening a FIFO would block until a writer appears and a symlink would\n // redirect the probe outside the dir (#2033). Enumerate BOTH live `*.jsonl`\n // spills and crash-orphaned `*.jsonl.claimed` files: a drain that died\n // between claiming a spill (rename to `.claimed`) and committing it leaves\n // the ONLY copy of those rows as a claimed orphan. Gating solely on live\n // spills would early-return here whenever the pending dir holds nothing but\n // orphans, stranding those rows forever; proceeding lets\n // drainPendingLifecycleLedgerIfAny -> recoverOrphanedClaims restore and\n // re-commit them under the ledger lock (#2033).\n let spillFiles: string[];\n let claimedOrphans: string[];\n try {\n spillFiles = await listContainedSpillFiles(spillDir);\n claimedOrphans = await listContainedSpillFiles(spillDir, \".jsonl.claimed\");\n } catch (err) {\n log.debug(`fallback lifecycle pending scan failed (non-fatal) for ${memoryDir}: ${err}`);\n return;\n }\n if (spillFiles.length === 0 && claimedOrphans.length === 0) return;\n // The pending encryption state is authoritative only UNDER the ledger lock:\n // drainPendingLifecycleLedgerIfAny waits for the lock and re-enumerates the\n // pending dir, so a secure-store writer that spills an encrypted file — or a\n // keyed compaction that rewrites the ledger encrypted — while this fallback\n // waits would defeat any pre-lock probe (#2033). Probe each file at the\n // moment of use inside the held lock instead: readSecure refuses an\n // encrypted spill (the fold reads each spill lazily and propagates the throw\n // BEFORE claiming that spill, leaving it intact) and the append refuses a\n // ledger that became encrypted (the fold rolls the claims back). Either way\n // the plaintext IO\n // never reads or writes ciphertext; the keyed catalog-path drain handles the\n // deferred rows.\n const io: LifecyclePendingIo = {\n writeSecure: async (filePath, payload) => {\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, payload, \"utf-8\");\n },\n readSecure: async (filePath) => {\n if (await probeEncryptedRegularFileHeader(filePath)) {\n throw new Error(`fallback lifecycle drain deferred: encrypted spill at ${filePath}`);\n }\n return readFile(filePath, \"utf-8\");\n },\n };\n try {\n await drainPendingLifecycleLedgerIfAny(\n ledgerPath,\n io,\n async (payload) => {\n if (await probeEncryptedRegularFileHeader(ledgerPath)) {\n throw new Error(`fallback lifecycle drain deferred: ledger became encrypted at ${ledgerPath}`);\n }\n await appendFile(ledgerPath, payload, \"utf-8\");\n },\n async () => {\n await mkdir(path.dirname(ledgerPath), { recursive: true });\n },\n );\n } catch (err) {\n log.debug(`fallback lifecycle pending drain failed (non-fatal) for ${memoryDir}: ${err}`);\n }\n }\n\n /**\n * Enumerate the lifecycle ledgers to consider for compaction: the root\n * (through the secure-configured root storage when available) plus every\n * cataloged namespace ledger when namespaces are enabled. Deduplicated by\n * resolved storage dir so a namespace that collapses onto the root is not\n * compacted twice. Namespace resolution failures are non-fatal.\n */\n private async resolveLifecycleCompactionTargets(): Promise<\n Array<{ memoryDir: string; storage?: StorageManager }>\n > {\n const targets: Array<{ memoryDir: string; storage?: StorageManager }> = [];\n const seen = new Set<string>();\n const addTarget = (memoryDir: string, storage?: StorageManager): void => {\n const key = path.resolve(memoryDir);\n if (seen.has(key)) return;\n seen.add(key);\n targets.push({ memoryDir, storage });\n };\n\n const rootStorage = this.deps.getStorage?.();\n addTarget(rootStorage ? rootStorage.dir : this.deps.config.memoryDir, rootStorage);\n\n const resolveNamespaceStorage = this.deps.storageForNamespace;\n if (resolveNamespaceStorage && this.deps.namespaceCatalog?.enabled) {\n let records: NamespaceRecord[] = [];\n try {\n records = await this.deps.namespaceCatalog.listNamespaces();\n } catch (err) {\n log.debug(`lifecycle compaction: namespace enumeration failed (non-fatal): ${err}`);\n }\n for (const record of records) {\n const namespace = record.namespace.trim();\n if (!namespace) continue;\n try {\n const storage = await resolveNamespaceStorage(namespace);\n addTarget(storage.dir, storage);\n } catch (err) {\n log.debug(\n `lifecycle compaction: storage resolve failed for namespace ${namespace} (non-fatal): ${err}`,\n );\n }\n }\n }\n\n // Filesystem fallback (codex P2): when namespaces are enabled but the\n // catalog is disabled (namespaceCatalogEnabled=false) — or a namespace is\n // not yet cataloged — the catalog walk above finds nothing, yet per-namespace\n // ledgers still grow under <memoryDir>/namespaces/<token>/state/. Scan that\n // base directly so those ledgers are still bounded. Deduped by resolved dir,\n // so a namespace already added via the catalog is not compacted twice.\n if (resolveNamespaceCapabilities(this.deps.config).namespaces) {\n const namespacesBase = path.join(this.deps.config.memoryDir, \"namespaces\");\n // Symlink/traversal containment (issue #2033 codex P2): a symlinked\n // <memoryDir>/namespaces (or a symlinked child) must not redirect the scan\n // — and later backup/ledger rewrites — outside memoryDir. Resolve the\n // memory root and the scan base through realpath, reject a symlinked or\n // escaping base, and skip any symlinked/escaping child, mirroring the\n // memory-store walkers' hardening (utils/path-containment).\n let memoryDirReal: string;\n try {\n memoryDirReal = await realpath(this.deps.config.memoryDir);\n const baseStat = await lstat(namespacesBase);\n if (baseStat.isSymbolicLink() || !baseStat.isDirectory()) {\n throw new Error(\"namespaces base is a symlink or not a directory\");\n }\n assertPathInsideRoot(memoryDirReal, await realpath(namespacesBase), namespacesBase);\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) {\n log.debug(`lifecycle compaction: namespaces base rejected (non-fatal): ${err}`);\n }\n return targets;\n }\n let entries: Dirent[] = [];\n try {\n entries = await readdir(namespacesBase, { withFileTypes: true });\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) {\n log.debug(`lifecycle compaction: namespaces dir scan failed (non-fatal): ${err}`);\n }\n }\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n if (!entry.isDirectory() || entry.isSymbolicLink()) continue;\n const childPath = path.join(namespacesBase, entry.name);\n try {\n assertPathInsideRoot(memoryDirReal, await realpath(childPath), childPath);\n } catch (err) {\n log.debug(`lifecycle compaction: skipping out-of-root namespace dir ${childPath}: ${err}`);\n continue;\n }\n // Resolve the namespace's secure-store-aware storage so an encrypted\n // fallback ledger compacts through the correct root key/context instead\n // of deferring forever behind a keyless plaintext StorageManager (#2033\n // thread PRRT_kwDORJXyws6SE5fv). The on-disk dir name is EITHER a\n // namespace identity TOKEN (ns-<hex>) OR a legacy raw namespace dir name\n // written before tokenization (namespaces/<name>/). Decode a token back\n // to its canonical namespace; treat a non-token dir as a raw namespace\n // only when it is a safe route namespace (isSafeRouteNamespace), so a\n // garbage/traversal dir name is never handed to the resolver. Passing the\n // raw token to the resolver would re-tokenize an already-tokenized value\n // and (for namespaces longer than 30 bytes) exceed the router's 64-char\n // safe-route limit, so the resolver throws and the encrypted over-cap\n // ledger defers forever — hence decode first. Resolve the route namespace\n // and accept the keyed storage only when it roots at THIS dir: the secure\n // store keys one master key per memory ROOT (not per namespace), the\n // router routes a token to namespaces/<token>/ and an existing legacy raw\n // name to its namespaces/<name>/ root, so a correct resolve returns THIS\n // childPath; anything else is an unexpected re-route we refuse (keeping\n // namespace isolation and never compacting the wrong ledger). A dir that\n // neither decodes nor is a safe raw namespace, or no resolver wired\n // (focused tests / plaintext deployments), leaves the keyless target\n // standing: the rebuild chokepoint still refuses to downgrade an\n // encrypted ledger, so plaintext behavior holds.\n const decodedNamespace = namespaceIdentityFromToken(entry.name);\n const routeNamespace =\n decodedNamespace !== null\n ? decodedNamespace\n : isSafeRouteNamespace(entry.name)\n ? entry.name\n : null;\n if (resolveNamespaceStorage && routeNamespace !== null) {\n try {\n const storage = await resolveNamespaceStorage(routeNamespace);\n if (path.resolve(storage.dir) === path.resolve(childPath)) {\n addTarget(storage.dir, storage);\n continue;\n }\n log.debug(\n `lifecycle compaction: fallback resolver routed ${childPath} to ${storage.dir}; `\n + \"using keyless target\",\n );\n } catch (err) {\n log.debug(\n `lifecycle compaction: fallback storage resolve failed for ${childPath} (non-fatal): ${err}`,\n );\n }\n }\n addTarget(childPath);\n }\n }\n return targets;\n }\n\n /**\n * Compact one lifecycle ledger when it is at/over `threshold`. Returns\n * `\"skipped\"` (absent or below threshold — nothing to do), `\"compacted\"`\n * (rewritten and verified under the read/decrypt cap), `\"failed\"`\n * (rebuild/probe threw, or the rewritten ledger is STILL over the cap — an\n * ineffective compaction — non-fatal, retried next pass), or `\"deferred\"`\n * (oversized but its encrypted ledger cannot be rewritten because no unlocked\n * secure store is available — real work still pending, so the caller must NOT\n * arm the throttle). #2033: a deferred/failed target is distinct from a genuine\n * no-op so one namespace compacting cannot suppress retries for another, and\n * an over-cap rewrite never arms the throttle.\n */\n private async compactLifecycleLedgerTarget(\n target: { memoryDir: string; storage?: StorageManager },\n threshold: number,\n ): Promise<\"skipped\" | \"compacted\" | \"failed\" | \"deferred\"> {\n const ledgerPath = path.join(target.memoryDir, \"state\", \"memory-lifecycle-ledger.jsonl\");\n let size = 0;\n try {\n size = (await stat(ledgerPath)).size;\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return \"skipped\"; // absent — nothing to compact yet.\n log.warn(\n `lifecycle ledger size check failed (non-fatal) for ${target.memoryDir}: ${err}`,\n );\n return \"failed\";\n }\n // #2033 finding (1): an encrypted ledger becomes unreadable once its on-disk\n // size reaches the whole-file decrypt cap (the reader refuses at/over it), so\n // the effective compaction trigger for an encrypted target is clamped to that\n // cap even when the configured threshold is LARGER — otherwise a ledger that\n // grows past the cap but stays below a bigger threshold would never compact\n // and stay permanently unreadable. Plaintext streaming is unaffected at any\n // size, so a plaintext target keeps the configured threshold. The cheap\n // pre-check skips anything below the smaller of the two before paying for the\n // extra lstat/open the encryption probe costs.\n if (size < Math.min(threshold, this.lifecycleLedgerMaxBytes)) return \"skipped\";\n let encrypted: boolean;\n try {\n encrypted = await probeEncryptedRegularFileHeader(ledgerPath);\n } catch (err) {\n log.warn(`lifecycle ledger encryption probe failed (non-fatal) for ${ledgerPath}: ${err}`);\n return \"failed\";\n }\n const effectiveThreshold = encrypted\n ? Math.min(threshold, this.lifecycleLedgerMaxBytes)\n : threshold;\n if (size < effectiveThreshold) return \"skipped\";\n // No plaintext rewrite of an encrypted-at-rest ledger (#2033): rebuilding an\n // encrypted ledger without an unlocked secure StorageManager would either\n // fail the preserve-read (locked store) or downgrade the ledger to plaintext.\n // Report \"deferred\" (NOT \"skipped\"): the ledger is over threshold and still\n // needs compaction, so the throttle must stay un-armed and retry once a key\n // is available — a \"skipped\" here would let another target's success arm the\n // throttle and suppress this pending work for the whole interval (#2033).\n if (encrypted && !(target.storage?.isSecureStoreUnlocked() ?? false)) {\n log.warn(\n `lifecycle ledger at ${ledgerPath} is encrypted at rest but no unlocked secure `\n + `storage is available; deferring auto-compaction to avoid a plaintext rewrite. `\n + `Run 'remnic rebuild-memory-lifecycle-ledger --write' after unlocking.`,\n );\n return \"deferred\";\n }\n // #2033 findings (2)+(4): the rewrite budget bounds the PLAINTEXT payload, but\n // an encrypted-at-rest target adds a fixed secure-store envelope on disk.\n // Reserve that envelope (plus one byte) from the plaintext budget so the\n // encrypted file lands STRICTLY below the reader's refusal cap and the\n // post-write check — which stats the on-disk (encrypted) size against the cap\n // — cannot fail forever on a plaintext budget that equals the cap (the\n // endless-retry bug). Base this on whether the REPLACEMENT will be encrypted\n // (the storage write mode), NOT only on the current file header: a\n // plaintext ledger rewritten under `secureStoreEncryptOnWrite` with the key\n // set becomes encrypted, so it needs the reserve even though its current\n // header is plaintext (#2033 write-mode finding).\n const replacementEncrypted =\n encrypted || (target.storage?.willEncryptStateWrites() ?? false);\n const plaintextBudget = replacementEncrypted\n ? this.lifecycleLedgerMaxBytes - SECURE_STORE_ENVELOPE_OVERHEAD_BYTES - 1\n : this.lifecycleLedgerMaxBytes - 1;\n try {\n const result = await rebuildMemoryLifecycleLedger({\n memoryDir: target.memoryDir,\n dryRun: false,\n storage: target.storage,\n // Background compaction must not lose append-only history that\n // frontmatter cannot reconstruct (issue #1910); the manual CLI repair\n // rebuild now preserves and bounds identically (#2033).\n preserveExistingEvents: true,\n // Bound the PLAINTEXT rewrite to a budget that reserves the secure-store\n // envelope, so the on-disk ledger (plaintext, or plaintext+envelope when\n // encrypted) is strictly below the read/decrypt cap; overflow lands in\n // the verbatim backup (#2033 findings 2+4).\n maxLedgerBytes: plaintextBudget,\n });\n // Verify the rewritten ledger is actually bounded. A compaction that left\n // the ledger at/over the read/decrypt cap is ineffective (the ledger would\n // be unreadable) — report \"failed\" so the throttle stays un-armed and the\n // next pass retries rather than declaring success (#2033).\n let rewrittenSize = 0;\n try {\n rewrittenSize = (await stat(ledgerPath)).size;\n } catch (err) {\n log.warn(`lifecycle ledger post-compaction size check failed (non-fatal) for ${target.memoryDir}: ${err}`);\n return \"failed\";\n }\n if (rewrittenSize >= this.lifecycleLedgerMaxBytes) {\n log.warn(\n `lifecycle ledger auto-compaction ineffective for ${target.memoryDir}: rewritten `\n + `ledger is ${rewrittenSize}B, still at/over the ${this.lifecycleLedgerMaxBytes}-byte `\n + `read/decrypt cap; leaving the throttle un-armed to retry.`,\n );\n return \"failed\";\n }\n if (result.rewritten === false) {\n // No-op pass (#2033 thread PRRT_kwDORJXyws6SExst): a preserving rebuild\n // would reproduce the current ledger byte-for-byte, so it skipped the\n // backup+rewrite. The ledger stays over the trigger but under the cap;\n // report \"compacted\" so the throttle still arms (nothing more can be\n // done until the ledger grows), which stops the periodic re-archive/\n // rewrite churn the review flagged — the archive no longer grows.\n log.debug(\n `lifecycle ledger already compact for ${target.memoryDir}: ${rewrittenSize}B, `\n + `no rewrite needed (throttle armed to avoid re-archiving an unchanged ledger).`,\n );\n } else {\n log.info(\n `lifecycle ledger auto-compacted (${target.memoryDir}): ${size}B -> `\n + `${rewrittenSize}B, ${result.rebuiltRows} rows `\n + `(${result.preservedAppendOnlyRows ?? 0} append-only preserved, `\n + `${result.archivedOverflowRows ?? 0} overflow archived), `\n + `backup=${result.backupPath ?? \"none\"}`,\n );\n }\n return \"compacted\";\n } catch (err) {\n log.warn(\n `lifecycle ledger auto-compaction failed (non-fatal) for ${target.memoryDir}: ${err}`,\n );\n return \"failed\";\n }\n }\n\n /** Build the namespace-aware maintenance plan for one job. */\n private async namespaceMaintenancePlan(\n jobName: string,\n ): Promise<NamespaceMaintenancePlan> {\n return planNamespaceMaintenance(this.deps.config, {\n jobName,\n catalog: this.deps.namespaceCatalog,\n });\n }\n\n // ───────────────────────────────────────────────────────────────────────\n // Lifecycle\n // ───────────────────────────────────────────────────────────────────────\n\n /**\n * Clear any pending debounce timer and drain the activity sync scheduler.\n * Called from the orchestrator's destroy() so a late tick does not fire on a\n * torn-down instance and no in-flight activity tick keeps writing after\n * teardown. Async: awaits the scheduler's abort+drain before resolving.\n */\n async dispose(): Promise<void> {\n this.disposed = true;\n const consolidationDrain = this.consolidationRunPromise;\n // The registrar latches teardown first (so a draining tick's onRun cannot\n // re-arm QMD maintenance) and aborts+drains the sync. Capture its drain and\n // await it last so the rest of teardown runs while the aborted tick unwinds.\n const activityDrain = this.activityRegistrar.dispose();\n const locationDrain = this.locationAutoSync?.stop() ?? Promise.resolve();\n this.locationAutoSync = null;\n if (this.qmdMaintenanceTimer) {\n clearTimeout(this.qmdMaintenanceTimer);\n this.qmdMaintenanceTimer = null;\n }\n this.qmdMaintenancePending = false;\n await activityDrain;\n await locationDrain;\n // Belt-and-suspenders: clear anything a late tick armed before the latch.\n if (this.qmdMaintenanceTimer) {\n clearTimeout(this.qmdMaintenanceTimer);\n this.qmdMaintenanceTimer = null;\n }\n this.qmdMaintenancePending = false;\n if (consolidationDrain) await consolidationDrain;\n }\n}\n","/**\n * Day-summary cron model resolution (extracted from\n * orchestration/maintenance.ts in #2047 so that capped file keeps only\n * scheduling wiring). Pure function of config — no I/O.\n */\n\nimport type { PluginConfig } from \"../types.js\";\n\n/**\n * Resolve the cron-routing model + fallback chain. Only in gateway mode is\n * summaryModel routable as an OpenClaw agentTurn model; in plugin mode it is\n * a direct-client model id for Remnic's own LLM calls. Task-chain fallbacks\n * attach only when the model matches the task-chain primary (a distinct\n * override's fallbacks would be unrelated); gateway default models are\n * appended as tail fallbacks (de-duped) so a task-chain outage doesn't stop\n * the cron before reaching the gateway default chain.\n */\nexport function resolveDaySummaryCronModel(config: PluginConfig): {\n model?: string;\n fallbacks: string[];\n} {\n const rawSummaryModel = config.summaryModel;\n const taskPrimary = config.taskModelChain?.primary;\n const isGateway = config.modelSource === \"gateway\";\n const model = isGateway ? rawSummaryModel || taskPrimary || undefined : undefined;\n const fallbacks: string[] = [];\n if (model && taskPrimary && model === taskPrimary) {\n const seen = new Set<string>(model ? [model] : []);\n const addUnique = (value: string | undefined) => {\n if (typeof value !== \"string\") return;\n const trimmed = value.trim();\n if (trimmed.length > 0 && !seen.has(trimmed)) {\n seen.add(trimmed);\n fallbacks.push(trimmed);\n }\n };\n for (const fb of config.taskModelChain?.fallbacks ?? []) addUnique(fb);\n const gwDefaults = config.gatewayConfig?.agents?.defaults?.model;\n addUnique(gwDefaults?.primary);\n if (Array.isArray(gwDefaults?.fallbacks)) {\n for (const fb of gwDefaults.fallbacks) addUnique(fb);\n }\n }\n return { ...(model ? { model } : {}), fallbacks };\n}\n","/**\n * Scheduled rebuild of the root memory projection (issue #2119).\n *\n * The projection (`state/memory-projection.sqlite`) is otherwise rebuild-only\n * (manual CLI / tests), so it goes stale as the lifecycle ledger grows and\n * timeline/browse consumers silently fall back to full-corpus scans. The\n * MaintenanceScheduler fires this alongside ledger compaction on every\n * maintenance request — interval-throttled, single-flighted, never awaited,\n * never throws.\n *\n * `projectionRebuildEnabled` gates it (via the lifecycle capabilities\n * resolver, default true); `projectionRebuildIntervalMs` is the cadence.\n *\n * Skipped when the projection was rebuilt within the interval. The freshness\n * check reads the on-disk `rebuiltAt` meta (not just an in-process timestamp)\n * so a daemon restart or an operator's cron `remnic rebuild-memory-projection`\n * both suppress a redundant rebuild. On a real rebuild it logs the lag it just\n * cured (age of the projection before the rebuild) plus the fresh row counts,\n * so operators can see the projection was drifting. Failures are non-fatal\n * and leave the throttle un-advanced to retry.\n *\n * Scope: the root memoryDir projection — the one `getMemoryTimeline` /\n * browse / current-state serve from `baseDir`. Per-namespace projections are\n * not yet scheduled here (they are still served by their own rebuild paths);\n * the fallback WARN surfaces staleness for any namespace that lags.\n */\nimport { log } from \"../logger.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { resolveMemoryLifecycleCapabilities } from \"../capabilities.js\";\nimport {\n rebuildMemoryProjection,\n type RebuildMemoryProjectionOptions,\n} from \"../maintenance/rebuild-memory-projection.js\";\nimport { readProjectionRebuiltAt } from \"../maintenance/projection-support.js\";\n\ntype InjectedStorage = NonNullable<RebuildMemoryProjectionOptions[\"storage\"]>;\n\nexport interface ProjectionRebuildScheduleState {\n inFlight: boolean;\n lastRebuildAtMs: number;\n /** Timestamp of the most recent FAILURE; gates a short retry backoff that is\n * separate from the success interval so a transient cause (e.g. a locked\n * secure store) retries promptly once cleared instead of waiting a full\n * interval. */\n lastFailureAtMs: number;\n}\n\n/** Bounded retry cadence after a failure — short enough to self-heal promptly\n * once a transient cause clears, long enough to keep a persistently-failing\n * rebuild off every maintenance tick. */\nconst FAILURE_RETRY_BACKOFF_MS = 5 * 60 * 1000;\n\nexport function createProjectionRebuildScheduleState(): ProjectionRebuildScheduleState {\n return { inFlight: false, lastRebuildAtMs: 0, lastFailureAtMs: 0 };\n}\n\nexport async function maybeRebuildMemoryProjectionScheduled(opts: {\n config: PluginConfig;\n /** The daemon's live root storage, when available (secure-store unlock). */\n getStorage?: () => InjectedStorage | undefined;\n state: ProjectionRebuildScheduleState;\n}): Promise<void> {\n const { config, state } = opts;\n if (!resolveMemoryLifecycleCapabilities(config).projectionRebuild) return;\n if (state.inFlight) return;\n const intervalMs = config.projectionRebuildIntervalMs;\n const now = Date.now();\n if (now - state.lastRebuildAtMs < intervalMs) return;\n if (now - state.lastFailureAtMs < FAILURE_RETRY_BACKOFF_MS) return;\n\n const rootStorage = opts.getStorage?.();\n const memoryDir = rootStorage ? rootStorage.dir : config.memoryDir;\n\n // On-disk freshness check: survives restarts and cross-process CLI rebuilds.\n const rebuiltAt = readProjectionRebuiltAt(memoryDir);\n const rebuiltAtMs = rebuiltAt ? Date.parse(rebuiltAt) : Number.NaN;\n if (Number.isFinite(rebuiltAtMs) && now - rebuiltAtMs < intervalMs) {\n // Fresh already — adopt its timestamp so the next pass short-circuits on\n // the cheap in-process guard without re-opening the projection each time.\n state.lastRebuildAtMs = rebuiltAtMs;\n return;\n }\n\n state.inFlight = true;\n try {\n const result = await rebuildMemoryProjection({\n memoryDir,\n // Reuse the daemon's live storage so secure-store deployments rebuild\n // through the unlocked instance instead of a fresh locked one.\n ...(rootStorage ? { storage: rootStorage } : {}),\n defaultNamespace: config.defaultNamespace,\n dryRun: false,\n now: new Date(now),\n });\n state.lastRebuildAtMs = Date.now();\n const lag = Number.isFinite(rebuiltAtMs)\n ? `${Math.max(0, Math.round((now - rebuiltAtMs) / 60_000))}m stale`\n : \"never previously built\";\n log.info(\n `memory projection rebuilt (scheduled) for ${memoryDir}: was ${lag}; `\n + `${result.currentRows} current, ${result.timelineRows} timeline, `\n + `${result.entityMentionRows} entity-mention rows`,\n );\n } catch (err) {\n // Non-fatal. Arm a SHORT backoff (not the full success interval) so a\n // transient cause (locked secure store, momentarily-unavailable storage)\n // retries promptly once it clears, while a persistently-failing rebuild\n // still stays off every maintenance tick. lastRebuildAtMs is left as-is so\n // the success interval is not falsely advanced.\n state.lastFailureAtMs = Date.now();\n log.warn(`memory projection scheduled rebuild failed (non-fatal) for ${memoryDir}: ${err}`);\n } finally {\n state.inFlight = false;\n }\n}\n","/**\n * Forced, non-fake search-index refresh for direct activity digest writes\n * (issue #1899; AGENTS.md rule 31). The digest is written straight to the QMD\n * collection root, bypassing the extraction->persist->index pipeline, so it must\n * explicitly trigger a refresh — and that refresh must actually run.\n *\n * Ordinary `SearchBackend.update()` is fail-open: it silently returns when the\n * backend is unavailable, in failure backoff, or inside its min-interval gate.\n * A freshly written digest hit by that gate would stay unsearchable until an\n * unrelated reindex, and the per-day digest-unchanged guard means the next tick\n * won't retry. So prefer the forced/strict contracts: `updateCollectionStrict`\n * bypasses the min-interval gate (force) AND throws on a genuine failure instead\n * of reporting a fake success, then `updateStrict`, then plain `update`.\n */\n\nimport type { SearchExecutionOptions } from \"../search/port.js\";\n\n/** The subset of the search backend the activity reindex needs. */\nexport interface ActivityIndexRefresher {\n update(execution?: SearchExecutionOptions): Promise<void>;\n updateStrict?(execution?: SearchExecutionOptions): Promise<void>;\n updateCollectionStrict?(collection: string, execution?: SearchExecutionOptions): Promise<void>;\n}\n\nexport async function refreshActivityIndex(\n qmd: ActivityIndexRefresher,\n collection: string,\n signal?: AbortSignal,\n): Promise<void> {\n const execution = signal === undefined ? undefined : { signal };\n if (typeof qmd.updateCollectionStrict === \"function\" && collection.trim().length > 0) {\n await qmd.updateCollectionStrict(collection, execution);\n return;\n }\n if (typeof qmd.updateStrict === \"function\") {\n await qmd.updateStrict(execution);\n return;\n }\n await qmd.update(execution);\n}\n","/**\n * Screen-activity SQLite store (issue #1899).\n *\n * Durable, capture-machine-agnostic store for on-screen text snapshots plus a\n * per-machine sync cursor. Mirrors the LCM store conventions\n * (packages/remnic-core/src/lcm/schema.ts): WAL, a `<name>_meta` schema-version\n * row, `CREATE TABLE IF NOT EXISTS`, and an FTS5 virtual table created\n * separately. better-sqlite3 is synchronous.\n *\n * Snapshots dedup on `(machine, content_hash)` so a re-sync is idempotent, and\n * day queries use half-open [start, end) UTC bounds (AGENTS.md §23).\n */\n\nimport { mkdirSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { openBetterSqlite3, type BetterSqlite3Database } from \"../runtime/better-sqlite.js\";\nimport type { ActivitySnapshot } from \"./types.js\";\n\nconst ACTIVITY_SCHEMA_VERSION = 1;\nconst MAX_SEARCH_RESULTS = 100;\n\nexport function activityDatabasePath(memoryDir: string): string {\n return path.join(memoryDir, \"state\", \"activity.sqlite\");\n}\n\nexport async function ensureActivityStateDir(memoryDir: string): Promise<void> {\n await mkdir(path.join(memoryDir, \"state\"), { recursive: true });\n}\n\nexport function openActivityDatabase(memoryDir: string): BetterSqlite3Database {\n // Create the state/ dir synchronously first: better-sqlite3 can't open a file\n // in a missing directory, and open() is the sync public entry point (callers\n // don't await ensureActivityStateDir). Idempotent.\n mkdirSync(path.join(memoryDir, \"state\"), { recursive: true });\n const db = openBetterSqlite3(activityDatabasePath(memoryDir));\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"busy_timeout = 5000\");\n db.pragma(\"synchronous = NORMAL\");\n applySchema(db);\n return db;\n}\n\n/** Apply the activity schema on an already-open handle (test/in-memory use). */\nexport function applyActivitySchema(db: BetterSqlite3Database): void {\n applySchema(db);\n}\n\nfunction applySchema(db: BetterSqlite3Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS activity_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS activity_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n machine TEXT NOT NULL,\n captured_at_utc TEXT NOT NULL,\n app_name TEXT NOT NULL,\n window_title TEXT NOT NULL,\n browser_url TEXT,\n text TEXT NOT NULL,\n text_source TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n simhash TEXT\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_activity_snapshots_dedup\n ON activity_snapshots(machine, captured_at_utc, content_hash);\n CREATE INDEX IF NOT EXISTS idx_activity_snapshots_time\n ON activity_snapshots(captured_at_utc, id);\n\n CREATE TABLE IF NOT EXISTS activity_sync_state (\n machine TEXT PRIMARY KEY,\n cursor TEXT,\n updated_at_utc TEXT NOT NULL\n );\n `);\n\n const hasFts = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='activity_snapshots_fts'\")\n .get();\n if (!hasFts) {\n // Standalone FTS5 (not external-content): populated explicitly on insert so\n // it never drifts from the base table.\n db.exec(`\n CREATE VIRTUAL TABLE activity_snapshots_fts USING fts5(\n text, app_name, window_title, browser_url\n );\n `);\n }\n\n db.prepare(\"INSERT OR REPLACE INTO activity_meta (key, value) VALUES ('schema_version', ?)\").run(\n String(ACTIVITY_SCHEMA_VERSION),\n );\n}\n\n/**\n * Canonicalize a UTC timestamp to `YYYY-MM-DDTHH:MM:SS.sssZ` so day-window\n * range filtering (a TEXT comparison against activityDayWindow bounds) is valid\n * regardless of the input ISO form (trailing `Z` vs `+00:00`, missing millis).\n * A non-parseable value is stored verbatim (an outlier the day filter skips).\n */\nfunction canonicalizeUtc(iso: string): string {\n const parsed = Date.parse(iso);\n return Number.isFinite(parsed) ? new Date(parsed).toISOString() : iso;\n}\n\nfunction assertRequiredSnapshotFields(s: ActivitySnapshot): void {\n const required: ReadonlyArray<readonly [string, unknown]> = [\n [\"machine\", s.machine],\n [\"app\", s.app],\n [\"windowTitle\", s.windowTitle],\n [\"text\", s.text],\n [\"textSource\", s.textSource],\n [\"contentHash\", s.contentHash],\n ];\n for (const [field, value] of required) {\n if (typeof value !== \"string\") {\n // INSERT OR IGNORE would otherwise swallow a NOT NULL failure as if it\n // were a dedup conflict, silently dropping a malformed capture.\n throw new RangeError(`activity: snapshot field \"${field}\" is required (got ${value === null ? \"null\" : typeof value}).`);\n }\n }\n if (s.machine.length === 0 || s.contentHash.length === 0) {\n throw new RangeError('activity: snapshot \"machine\" and \"contentHash\" must be non-empty.');\n }\n if (s.textSource !== \"ax\" && s.textSource !== \"ocr\") {\n // rowToSnapshot maps any non-\"ocr\" value to \"ax\"; reject unknown sources at\n // the boundary instead of silently coercing them on read.\n throw new RangeError(`activity: snapshot \"textSource\" must be \"ax\" or \"ocr\" (got \"${s.textSource}\").`);\n }\n}\n\n/**\n * Reject an impossible capture timestamp instead of letting Date.parse silently\n * roll it over (e.g. 2026-02-30 → Mar 2, in either `Z` or explicit-offset form),\n * which would file the snapshot under the wrong day. Validates the wall-clock\n * calendar fields in the string directly, independent of the zone designator.\n */\nfunction assertValidUtcInstant(iso: string, what: string): void {\n const m = iso.match(\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d+)?(?:Z|[+-](?:0\\d:[0-5]\\d|1[0-3]:[0-5]\\d|14:00))$/,\n );\n if (m === null || !Number.isFinite(Date.parse(iso))) {\n throw new RangeError(`activity: invalid ${what} \"${iso}\".`);\n }\n const [year, month, day, hour, minute, second] = m.slice(1).map(Number);\n const daysInMonth = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;\n if (day < 1 || day > daysInMonth || hour > 23 || minute > 59 || second > 59) {\n throw new RangeError(`activity: ${what} \"${iso}\" is not a real calendar instant.`);\n }\n}\n\n/** Narrow a sqlite row object so field reads are checked, not asserted. */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction str(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction optStr(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n/**\n * Build a safe FTS5 MATCH expression from free text: extract alphanumeric\n * tokens and quote each as a phrase, so punctuation common in captured\n * activity (URLs like github.com/x/pull/412, quotes, bare boolean operators)\n * can never be parsed as FTS5 syntax and make SQLite throw. Tokens are AND-ed\n * (implicit). Returns null when there is nothing to match.\n */\nfunction ftsMatchFor(query: string): string | null {\n if (typeof query !== \"string\") return null;\n const tokens = query.match(/[\\p{L}\\p{N}_]+/gu);\n if (!tokens || tokens.length === 0) return null;\n return tokens.map((token) => `\"${token}\"`).join(\" \");\n}\n\nfunction rowToSnapshot(row: unknown): ActivitySnapshot {\n if (!isRecord(row)) {\n throw new Error(\"activity store: unexpected non-object row\");\n }\n const textSource = row.text_source === \"ocr\" ? \"ocr\" : \"ax\";\n return {\n id: typeof row.id === \"number\" ? row.id : undefined,\n machine: str(row.machine),\n capturedAtUtc: str(row.captured_at_utc),\n app: str(row.app_name),\n windowTitle: str(row.window_title),\n ...(optStr(row.browser_url) !== undefined ? { browserUrl: optStr(row.browser_url) } : {}),\n text: str(row.text),\n textSource,\n contentHash: str(row.content_hash),\n ...(optStr(row.simhash) !== undefined ? { simhash: optStr(row.simhash) } : {}),\n };\n}\n\nexport class ActivityStore {\n private readonly db: BetterSqlite3Database;\n\n constructor(db: BetterSqlite3Database) {\n this.db = db;\n }\n\n static open(memoryDir: string): ActivityStore {\n return new ActivityStore(openActivityDatabase(memoryDir));\n }\n\n /**\n * Insert a snapshot, idempotent on (machine, captured_at_utc, content_hash):\n * the same screen content recurring at a *different* time is kept; only an\n * exact re-ingestion of the same capture dedups. Returns `inserted: false`\n * for a duplicate. The FTS row is written only on a real insert (atomically),\n * so it never drifts from the base table.\n */\n insertSnapshot(snapshot: ActivitySnapshot): { inserted: boolean; id: number } {\n // Base row + FTS row must be atomic. Without a transaction, a crash (or an\n // FTS throw) between the two statements leaves a snapshot in the base table\n // with no matching FTS row — silently unsearchable. The transaction rolls\n // back both on any throw.\n const runInsert = this.db.transaction((s: ActivitySnapshot): { inserted: boolean; id: number } => {\n assertValidUtcInstant(s.capturedAtUtc, \"capture timestamp\");\n assertRequiredSnapshotFields(s);\n const capturedAtUtc = canonicalizeUtc(s.capturedAtUtc);\n const info = this.db\n .prepare(\n `INSERT OR IGNORE INTO activity_snapshots\n (machine, captured_at_utc, app_name, window_title, browser_url, text, text_source, content_hash, simhash)\n VALUES (@machine, @captured_at_utc, @app_name, @window_title, @browser_url, @text, @text_source, @content_hash, @simhash)`,\n )\n .run({\n machine: s.machine,\n captured_at_utc: capturedAtUtc,\n app_name: s.app,\n window_title: s.windowTitle,\n browser_url: s.browserUrl ?? null,\n text: s.text,\n text_source: s.textSource,\n content_hash: s.contentHash,\n simhash: s.simhash ?? null,\n });\n if (info.changes === 0) {\n const existing = this.db\n .prepare(\"SELECT id FROM activity_snapshots WHERE machine = ? AND captured_at_utc = ? AND content_hash = ?\")\n .get(s.machine, capturedAtUtc, s.contentHash);\n const id = isRecord(existing) && typeof existing.id === \"number\" ? existing.id : -1;\n return { inserted: false, id };\n }\n const id = Number(info.lastInsertRowid);\n this.db\n .prepare(\n `INSERT INTO activity_snapshots_fts (rowid, text, app_name, window_title, browser_url)\n VALUES (?, ?, ?, ?, ?)`,\n )\n .run(id, s.text, s.app, s.windowTitle, s.browserUrl ?? \"\");\n return { inserted: true, id };\n });\n return runInsert(snapshot);\n }\n\n /** Snapshots whose capture instant is in the half-open [start, end) window. */\n listSnapshotsForDay(\n machine: string | null,\n startUtcInclusive: string,\n endUtcExclusive: string,\n ): ActivitySnapshot[] {\n assertValidUtcInstant(startUtcInclusive, \"range start\");\n assertValidUtcInstant(endUtcExclusive, \"range end\");\n const start = canonicalizeUtc(startUtcInclusive);\n const end = canonicalizeUtc(endUtcExclusive);\n const rows =\n machine === null\n ? this.db\n .prepare(\n `SELECT * FROM activity_snapshots\n WHERE captured_at_utc >= ? AND captured_at_utc < ?\n ORDER BY captured_at_utc ASC, id ASC`,\n )\n .all(start, end)\n : this.db\n .prepare(\n `SELECT * FROM activity_snapshots\n WHERE machine = ? AND captured_at_utc >= ? AND captured_at_utc < ?\n ORDER BY captured_at_utc ASC, id ASC`,\n )\n .all(machine, start, end);\n return rows.map(rowToSnapshot);\n }\n\n getCursor(machine: string): string | null {\n const row = this.db.prepare(\"SELECT cursor FROM activity_sync_state WHERE machine = ?\").get(machine);\n return isRecord(row) && typeof row.cursor === \"string\" ? row.cursor : null;\n }\n\n setCursor(machine: string, cursor: string | null, updatedAtUtc: string = new Date().toISOString()): void {\n this.db\n .prepare(\n `INSERT INTO activity_sync_state (machine, cursor, updated_at_utc)\n VALUES (?, ?, ?)\n ON CONFLICT(machine) DO UPDATE SET cursor = excluded.cursor, updated_at_utc = excluded.updated_at_utc`,\n )\n .run(machine, cursor, updatedAtUtc);\n }\n\n /** Full-text search over snapshot text/app/window/url; newest first. */\n searchSnapshots(query: string, limit: number): ActivitySnapshot[] {\n const capped = Number.isInteger(limit) && limit > 0 ? Math.min(limit, MAX_SEARCH_RESULTS) : 20;\n const match = ftsMatchFor(query);\n if (match === null) return [];\n const rows = this.db\n .prepare(\n `SELECT s.* FROM activity_snapshots_fts f\n JOIN activity_snapshots s ON s.id = f.rowid\n WHERE activity_snapshots_fts MATCH ?\n ORDER BY s.captured_at_utc DESC, s.id DESC\n LIMIT ?`,\n )\n .all(match, capped);\n // ftsMatchFor sanitizes the query to quoted alphanumeric phrases, so there\n // is no path to an FTS5 syntax error; a real backend failure (closed handle,\n // missing/corrupt table, disk I/O) propagates rather than reading as empty.\n return rows.map(rowToSnapshot);\n }\n\n /** Retention: drop snapshots captured strictly before `cutoffUtc`. */\n pruneOlderThan(cutoffUtc: string): number {\n assertValidUtcInstant(cutoffUtc, \"prune cutoff\");\n const ids = this.db\n .prepare(\"SELECT id FROM activity_snapshots WHERE captured_at_utc < ?\")\n .all(canonicalizeUtc(cutoffUtc))\n .map((row: unknown) => (isRecord(row) && typeof row.id === \"number\" ? row.id : -1))\n .filter((id: number) => id >= 0);\n const deleteFts = this.db.prepare(\"DELETE FROM activity_snapshots_fts WHERE rowid = ?\");\n const deleteRow = this.db.prepare(\"DELETE FROM activity_snapshots WHERE id = ?\");\n const tx = this.db.transaction((rowIds: number[]) => {\n for (const id of rowIds) {\n deleteFts.run(id);\n deleteRow.run(id);\n }\n });\n tx(ids);\n return ids.length;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\n\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\n\nimport {\n activityDayWindow,\n activityDigestPath,\n composeActivityDigestBody,\n composeActivityDigestMeta,\n isValidActivityDate,\n serializeActivityDigest,\n} from \"./digest.js\";\nimport type { ActivityStore } from \"./store.js\";\nimport type { ActivitySnapshot, ActivitySourceClient } from \"./types.js\";\n\nconst MAX_SYNC_PAGES = 10_000;\n\n/**\n * Sync cursors are scoped per (machine, local day): each date resumes its own\n * pagination independently, so a multi-day window never inherits an earlier\n * day's cursor (which could skip backfill). Encoded into the store's single\n * key column with a NUL separator that cannot occur in a machine label.\n */\nexport function activityCursorKey(machine: string, date: string): string {\n return `${machine}\\u0000${date}`;\n}\n\n// Serializes the list -> compose -> write digest section per (memoryDir, date)\n// so two sources syncing the same day cannot lose-update each other's digest:\n// the last writer in the chain lists every row present and wins.\nconst digestLocks = new Map<string, Promise<unknown>>();\n\nfunction withDigestLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prior = digestLocks.get(key) ?? Promise.resolve();\n const next = prior.then(fn, fn);\n const tail = next.then(\n () => undefined,\n () => undefined,\n );\n digestLocks.set(key, tail);\n void tail.then(() => {\n if (digestLocks.get(key) === tail) digestLocks.delete(key);\n });\n return next;\n}\n\nexport interface ActivitySyncOptions {\n date: string;\n timezone: string;\n memoryDir: string;\n store: ActivityStore;\n signal?: AbortSignal;\n /** Runaway-pagination guard; defaults to MAX_SYNC_PAGES. */\n maxPages?: number;\n /**\n * Search-index refresh, run after a digest is (re)written so the durable\n * markdown becomes discoverable (AGENTS.md rule 31; the digest lives in the\n * QMD collection root but bypasses the extraction->persist->index pipeline).\n * Host-agnostic: the caller injects the core index seam (SearchBackend.update).\n * Best-effort — a failure never fails the sync (rows/digest are durable and\n * index on the next update); it surfaces as `reindexError`.\n */\n afterWrites?: (signal?: AbortSignal) => Promise<void>;\n}\n\nexport interface ActivitySyncResult {\n machine: string;\n fetched: number;\n inserted: number;\n duplicates: number;\n cursor: string | null;\n digestWritten: boolean;\n /** Set when the post-write reindex threw; the sync still succeeded. */\n reindexError?: string;\n}\n\nfunction snapshotForMachine(snapshot: ActivitySnapshot, machine: string): ActivitySnapshot {\n return {\n machine,\n capturedAtUtc: snapshot.capturedAtUtc,\n app: snapshot.app,\n windowTitle: snapshot.windowTitle,\n ...(snapshot.browserUrl === undefined ? {} : { browserUrl: snapshot.browserUrl }),\n text: snapshot.text,\n textSource: snapshot.textSource,\n contentHash: snapshot.contentHash,\n ...(snapshot.simhash === undefined ? {} : { simhash: snapshot.simhash }),\n };\n}\n\nasync function writeDigestIfChanged(memoryDir: string, date: string, serialized: string): Promise<boolean> {\n const target = activityDigestPath(memoryDir, date);\n try {\n if ((await readFile(target, \"utf8\")) === serialized) return false;\n } catch (error: unknown) {\n if (!(error instanceof Error) || !(\"code\" in error) || error.code !== \"ENOENT\") throw error;\n }\n\n await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });\n const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;\n await writeFile(temporary, serialized, { encoding: \"utf8\", mode: 0o600 });\n await rename(temporary, target);\n return true;\n}\n\n/**\n * Pull one source's local-day snapshots and persist its cursor only after every\n * page and the derived digest are durable. Replays after a partial failure are\n * safe because ActivityStore deduplicates an exact captured snapshot.\n */\nexport async function syncActivitySource(\n source: ActivitySourceClient,\n options: ActivitySyncOptions,\n): Promise<ActivitySyncResult> {\n if (!isValidActivityDate(options.date)) {\n throw new RangeError(`Invalid activity date \"${options.date}\"; expected a real YYYY-MM-DD day.`);\n }\n if (source.machineLabel.trim().length === 0) {\n throw new RangeError(\"activity source machine label must not be empty\");\n }\n const maxPages = options.maxPages ?? MAX_SYNC_PAGES;\n if (!Number.isInteger(maxPages) || maxPages < 1) {\n throw new RangeError(\"activity sync maxPages must be a positive integer\");\n }\n\n const cursorKey = activityCursorKey(source.machineLabel, options.date);\n let cursor = options.store.getCursor(cursorKey);\n let pages = 0;\n let fetched = 0;\n let inserted = 0;\n let duplicates = 0;\n const seenCursors = new Set<string>();\n let completed = false;\n\n while (pages < maxPages) {\n options.signal?.throwIfAborted();\n const page = await source.fetchSnapshots({\n date: options.date,\n timezone: options.timezone,\n cursor,\n signal: options.signal,\n });\n pages += 1;\n fetched += page.snapshots.length;\n\n for (const snapshot of page.snapshots) {\n // Persist every row keyed by its own timestamp: an out-of-window snapshot\n // (replay file, timezone-boundary bug) is durable under its own day and\n // never lost, while the day digest below is a window query so it never\n // pollutes options.date. A malformed instant is rejected loudly here.\n const result = options.store.insertSnapshot(snapshotForMachine(snapshot, source.machineLabel));\n if (result.inserted) inserted += 1;\n else duplicates += 1;\n }\n if (page.nextCursor === null) {\n completed = true;\n break;\n }\n if (typeof page.nextCursor !== \"string\" || page.nextCursor.length === 0) {\n throw new TypeError(\"activity source returned an invalid cursor\");\n }\n if (seenCursors.has(page.nextCursor)) {\n throw new Error(\"activity source returned a repeated cursor\");\n }\n seenCursors.add(page.nextCursor);\n cursor = page.nextCursor;\n }\n\n // Only a loop that ran out of its page budget without seeing a terminal\n // null cursor is a runaway; completing on the final allowed page is normal.\n if (!completed) {\n throw new Error(`activity source exceeded ${maxPages} pages`);\n }\n\n // Re-check abort before the durable write section: stop() may have fired after\n // the last page returned, or while this date waited behind digestLocks. An\n // aborted tick must not compose/write a digest or advance the cursor.\n options.signal?.throwIfAborted();\n const digestWritten = await withDigestLock(`${options.memoryDir}\\u0000${options.date}`, async () => {\n options.signal?.throwIfAborted();\n const { startUtc, endUtc } = activityDayWindow(options.date, options.timezone);\n const snapshots = options.store.listSnapshotsForDay(null, startUtc, endUtc);\n const body = composeActivityDigestBody(options.date, options.timezone, snapshots);\n const machines = [...new Set(snapshots.map((snapshot) => snapshot.machine))].sort((a, b) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n const serialized = serializeActivityDigest(composeActivityDigestMeta(options.date, machines, snapshots, body), body);\n return writeDigestIfChanged(options.memoryDir, options.date, serialized);\n });\n\n options.signal?.throwIfAborted();\n options.store.setCursor(cursorKey, cursor);\n\n // Rows and digest are durable and the cursor has advanced; refresh the search\n // index so the new digest is discoverable (rule 31). Best-effort: a failure\n // is reported, not thrown — the data indexes on the next update.\n let reindexError: string | undefined;\n if (digestWritten && options.afterWrites !== undefined) {\n try {\n await options.afterWrites(options.signal);\n } catch (error: unknown) {\n // reindexError is part of the exported ActivitySyncResult. Keep our\n // controlled QMD strict messages, but reduce opaque backend errors (which\n // can embed absolute paths / loader stacks) to a sanitized name+code.\n reindexError =\n error instanceof Error && error.message.startsWith(\"QMD \")\n ? error.message\n : displayErrorDetail(error) || \"reindex failed\";\n }\n }\n\n return {\n machine: source.machineLabel,\n fetched,\n inserted,\n duplicates,\n cursor,\n digestWritten,\n ...(reindexError === undefined ? {} : { reindexError }),\n };\n}\n","import { validateActivityBaseUrl } from \"./config.js\";\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\nimport type { ActivitySnapshot, ActivitySnapshotPage, ActivitySourceCheck, ActivitySourceClient } from \"./types.js\";\n\n/** Abort a stalled capture-daemon request rather than hang the whole sync. */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\n\nexport interface ActivityHttpSourceClientOptions {\n machineLabel: string;\n baseUrl: string;\n token?: string;\n /** Per-request timeout in ms; defaults to DEFAULT_REQUEST_TIMEOUT_MS. */\n timeoutMs?: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction stringField(value: Record<string, unknown>, field: string): string {\n const result = value[field];\n if (typeof result !== \"string\" || result.length === 0) {\n throw new TypeError(`activity source response has no valid ${field}`);\n }\n return result;\n}\n\n/**\n * Content fields (app/windowTitle/text) may be legitimately empty: a foreground\n * window can be untitled or have no extractable text. Require the string TYPE\n * (a missing/non-string field still fails loudly) but tolerate \"\", matching the\n * durable store's contract — else a blank window poisons the day cursor by\n * failing every replay of the same page.\n */\nfunction contentStringField(value: Record<string, unknown>, field: string): string {\n const result = value[field];\n if (typeof result !== \"string\") {\n throw new TypeError(`activity source response has invalid ${field}`);\n }\n return result;\n}\n\nfunction optionalStringField(value: Record<string, unknown>, field: string): string | undefined {\n const result = value[field];\n if (result === undefined || result === null) return undefined;\n if (typeof result !== \"string\") throw new TypeError(`activity source response has invalid ${field}`);\n return result;\n}\n\nfunction snapshotFromWire(value: unknown, machine: string): ActivitySnapshot {\n if (!isRecord(value)) throw new TypeError(\"activity source returned a non-object snapshot\");\n const textSource = stringField(value, \"textSource\");\n if (textSource !== \"ax\" && textSource !== \"ocr\") {\n throw new TypeError(\"activity source response has invalid textSource\");\n }\n const browserUrl = optionalStringField(value, \"browserUrl\");\n const simhash = optionalStringField(value, \"simhash\");\n return {\n machine,\n capturedAtUtc: stringField(value, \"capturedAtUtc\"),\n app: contentStringField(value, \"app\"),\n windowTitle: contentStringField(value, \"windowTitle\"),\n ...(browserUrl === undefined ? {} : { browserUrl }),\n text: contentStringField(value, \"text\"),\n textSource,\n contentHash: stringField(value, \"contentHash\"),\n ...(simhash === undefined ? {} : { simhash }),\n };\n}\n\nfunction responsePage(value: unknown, machine: string): ActivitySnapshotPage {\n if (!isRecord(value) || !Array.isArray(value.snapshots)) {\n throw new TypeError(\"activity source response has no snapshots array\");\n }\n // A missing/undefined nextCursor (field omitted, not JSON null) is normal\n // end-of-pagination — only a present non-string value is invalid.\n const nextCursor = value.nextCursor === undefined ? null : value.nextCursor;\n if (nextCursor !== null && typeof nextCursor !== \"string\") {\n throw new TypeError(\"activity source response has invalid nextCursor\");\n }\n return {\n snapshots: value.snapshots.map((snapshot) => snapshotFromWire(snapshot, machine)),\n nextCursor,\n };\n}\n\nfunction requestUrl(baseUrl: string, path: string, query: Record<string, string | undefined>): string {\n const url = new URL(path, baseUrl);\n const parts = Object.entries(query)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n url.search = parts.join(\"&\");\n return url.toString();\n}\n\nexport class ActivityHttpSourceClient implements ActivitySourceClient {\n readonly machineLabel: string;\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private readonly timeoutMs: number;\n\n constructor(options: ActivityHttpSourceClientOptions) {\n if (options.machineLabel.trim().length === 0) throw new RangeError(\"activity source machine label must not be empty\");\n if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {\n throw new RangeError(\"activity source timeoutMs must be a positive number\");\n }\n this.machineLabel = options.machineLabel;\n this.baseUrl = validateActivityBaseUrl(options.baseUrl).toString();\n this.token = options.token;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n }\n\n private async request(url: string, signal?: AbortSignal): Promise<Response> {\n // Bound every request so a stalled daemon aborts instead of hanging the\n // sync forever; honor the caller's signal too when one is supplied.\n const timeout = AbortSignal.timeout(this.timeoutMs);\n const composed = signal === undefined ? timeout : AbortSignal.any([signal, timeout]);\n const response = await fetch(url, {\n headers: this.token === undefined ? undefined : { authorization: `Bearer ${this.token}` },\n signal: composed,\n });\n if (!response.ok) throw new Error(`activity source HTTP ${response.status}`);\n return response;\n }\n\n async verify(signal?: AbortSignal): Promise<ActivitySourceCheck> {\n try {\n await this.request(requestUrl(this.baseUrl, \"/v1/health\", {}), signal);\n return { ok: true };\n } catch (error: unknown) {\n // The controlled `HTTP <status>` message is safe to surface verbatim;\n // anything else (network/runtime fetch errors that can embed hostnames or\n // absolute paths) is reduced to a sanitized name+code via\n // displayErrorDetail, matching the wearables operator-facing path.\n const detail =\n error instanceof Error && error.message.startsWith(\"activity source HTTP \")\n ? error.message.slice(\"activity source \".length)\n : displayErrorDetail(error) || \"request failed\";\n return { ok: false, detail };\n }\n }\n\n async fetchSnapshots(opts: {\n date: string;\n timezone: string;\n cursor?: string | null;\n signal?: AbortSignal;\n }): Promise<ActivitySnapshotPage> {\n const response = await this.request(\n requestUrl(this.baseUrl, \"/v1/snapshots\", {\n date: opts.date,\n timezone: opts.timezone,\n cursor: opts.cursor ?? undefined,\n }),\n opts.signal,\n );\n return responsePage(await response.json(), this.machineLabel);\n }\n}\n","/**\n * Host-agnostic activity sync runner (issue #1900).\n *\n * Bridges parsed `config.activity` to the durable sync pipeline: the parser\n * (config.ts) validates settings but never contacts a daemon, and\n * `syncActivitySource` (pipeline.ts) syncs one source for one day. This runner\n * is the missing middle — it instantiates one `ActivityHttpSourceClient` per\n * configured source from the parsed config and drives a sync across the\n * `syncDays` local-day window, but ONLY when the feature is enabled.\n *\n * The feature is master default-off: source config alone never starts a sync.\n * When `config.enabled` is false the runner opens no store, builds no client,\n * and makes no HTTP call or write — it returns a disabled summary. This mirrors\n * `runLiveConnectorsOnce`'s disabled-vs-result reporting so a host scheduler\n * (CLI, cron) can trigger it through the runtime with no OpenClaw imports.\n *\n * Cursor semantics are delegated wholesale to `syncActivitySource`: the\n * per-machine cursor advances only after every page and the digest are durable,\n * so a runtime failure (or an aborted stop) leaves the cursor unadvanced and a\n * restart resumes from the persisted position. A per-source failure is captured\n * as an error item and never aborts the other sources or the host flow.\n */\n\nimport { ActivityStore } from \"./store.js\";\nimport { syncActivitySource } from \"./pipeline.js\";\nimport { ActivityHttpSourceClient } from \"./source-client.js\";\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\nimport type { ActivityConfig, ActivitySourceClient, ActivitySourceConfig } from \"./types.js\";\n\ntype ActivityNow = Date | (() => Date);\n\n/** One configured source's aggregate result across the synced day window. */\nexport interface ActivitySourceRunItem {\n machineLabel: string;\n /** True when at least one day synced durably (no error). */\n ran: boolean;\n fetched: number;\n inserted: number;\n duplicates: number;\n digestsWritten: number;\n /** Persisted cursor after the run; unadvanced from prior on failure. */\n cursor: string | null;\n /** Set when the source failed; distinguishes a fault from an empty page. */\n error?: string;\n /**\n * Set when a durable day synced but its post-digest search reindex failed.\n * The rows/digest/cursor are committed; the index is stale until the next\n * refresh, so this is a signal (not a sync failure) for the scheduler.\n */\n reindexError?: string;\n}\n\nexport interface ActivitySyncRunSummary {\n ranAt: string;\n /** False means the feature is off: no client built, no HTTP, no write. */\n enabled: boolean;\n ranCount: number;\n errorCount: number;\n /** Sources whose durable sync succeeded but whose search reindex failed. */\n reindexErrorCount: number;\n totalInserted: number;\n results: ActivitySourceRunItem[];\n /**\n * Local dates (YYYY-MM-DD, in the sync timezone) that gained new snapshots\n * this run — a backfill/rolling window can touch several. Meetings rebuild\n * uses this so a change to an OLDER day rebuilds that day, not just today.\n */\n touchedDates: string[];\n}\n\nexport interface ActivitySyncRunOptions {\n config: ActivityConfig;\n memoryDir: string;\n /** Injectable store; opened from memoryDir when omitted (and closed here). */\n store?: ActivityStore;\n /** Clock injection for deterministic day-window resolution in tests. */\n now?: ActivityNow;\n /** Abort a run in flight (stop); an aborted source leaves its cursor intact. */\n signal?: AbortSignal;\n /** Injectable client factory; builds an ActivityHttpSourceClient by default. */\n createSourceClient?: (source: ActivitySourceConfig) => ActivitySourceClient;\n /**\n * Search-index refresh forwarded to each source sync as `afterWrites`, run\n * after a digest is (re)written so it becomes discoverable (rule 31).\n * Host wires the core index seam (SearchBackend.update); best-effort.\n */\n reindexSearch?: (signal?: AbortSignal) => Promise<void>;\n}\n\n/** Format a Date as YYYY-MM-DD in an IANA timezone (local calendar day). */\nfunction localDateInTimezone(instant: Date, timezone: string): string {\n try {\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).formatToParts(instant);\n const get = (type: string): string => parts.find((part) => part.type === type)?.value ?? \"\";\n return `${get(\"year\")}-${get(\"month\")}-${get(\"day\")}`;\n } catch {\n return instant.toISOString().slice(0, 10);\n }\n}\n\n/** Previous calendar date via pure date arithmetic (no DST wall-clock drift). */\nfunction previousIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() - 1);\n return parsed.toISOString().slice(0, 10);\n}\n\n/**\n * The local-day window to sync, oldest first: today back `syncDays` calendar\n * days in the configured timezone. Walking by calendar days (not fixed 24h\n * intervals) keeps the window correct across DST transitions.\n */\nexport function resolveActivitySyncDates(syncDays: number, timezone: string, now: Date): string[] {\n const dates: string[] = [];\n let cursor = localDateInTimezone(now, timezone);\n for (let count = 0; count < syncDays; count++) {\n dates.unshift(cursor);\n cursor = previousIsoDate(cursor);\n }\n return dates;\n}\n\n/**\n * Operator-facing detail for a per-source/per-day sync fault. Our own\n * controlled messages (activity.../Invalid activity...) are informative and\n * safe to surface; opaque network/runtime errors can embed hostnames or paths,\n * so they are reduced to a sanitized name+code (matches ActivityHttpSourceClient.verify).\n */\nfunction sanitizedSyncError(error: unknown): string {\n return error instanceof Error && /^(activity|Invalid activity)/.test(error.message)\n ? error.message\n : displayErrorDetail(error) || \"activity sync failed\";\n}\n\n/**\n * Run one activity sync pass across every configured source. Disabled config\n * short-circuits before any store/client/HTTP work; an enabled config syncs\n * each source across the `syncDays` window and isolates per-source faults.\n */\nexport async function runActivitySyncOnce(options: ActivitySyncRunOptions): Promise<ActivitySyncRunSummary> {\n const ranAt = typeof options.now === \"function\" ? options.now() : options.now ?? new Date();\n\n if (!options.config.enabled) {\n return {\n ranAt: ranAt.toISOString(),\n enabled: false,\n ranCount: 0,\n errorCount: 0,\n reindexErrorCount: 0,\n totalInserted: 0,\n results: [],\n touchedDates: [],\n };\n }\n\n const dates = resolveActivitySyncDates(options.config.syncDays, options.config.timezone, ranAt);\n const createSourceClient =\n options.createSourceClient ??\n ((source: ActivitySourceConfig): ActivitySourceClient =>\n new ActivityHttpSourceClient({\n machineLabel: source.machineLabel,\n baseUrl: source.baseUrl,\n ...(source.token === undefined ? {} : { token: source.token }),\n }));\n const ownStore = options.store === undefined;\n const store = options.store ?? ActivityStore.open(options.memoryDir);\n const results: ActivitySourceRunItem[] = [];\n // Local dates that gained new snapshots this run (any source). Drives the\n // meetings rebuild fan-out so a backfilled OLDER day is rebuilt too.\n const touchedDates = new Set<string>();\n\n try {\n for (const sourceConfig of options.config.sources) {\n const item: ActivitySourceRunItem = {\n machineLabel: sourceConfig.machineLabel,\n ran: false,\n fetched: 0,\n inserted: 0,\n duplicates: 0,\n digestsWritten: 0,\n // Cursors are now per (machine, date); report the last synced day's\n // cursor. Null until a day advances it.\n cursor: null,\n };\n try {\n const client = createSourceClient(sourceConfig);\n for (const date of dates) {\n try {\n const result = await syncActivitySource(client, {\n date,\n timezone: options.config.timezone,\n memoryDir: options.memoryDir,\n store,\n signal: options.signal,\n ...(options.reindexSearch === undefined ? {} : { afterWrites: options.reindexSearch }),\n });\n item.fetched += result.fetched;\n item.inserted += result.inserted;\n if (result.inserted > 0) touchedDates.add(date);\n item.duplicates += result.duplicates;\n if (result.digestWritten) item.digestsWritten += 1;\n item.cursor = result.cursor;\n // A day that synced durably counts as ran, even if a later day in\n // the window fails (ran = at least one durable day).\n item.ran = true;\n // The day's rows/digest/cursor are durable; a failed post-digest\n // reindex is a separate, already-sanitized signal (not a sync\n // failure) so the scheduler can observe a stale index.\n if (result.reindexError !== undefined) item.reindexError = result.reindexError;\n } catch (dateError) {\n // One bad day (a malformed historical snapshot, a per-day daemon\n // error) must not starve the remaining dates — record it and move\n // on. Dates are processed oldest-first, so continuing keeps today's\n // sync alive when an old backfill day is broken. An abort, though,\n // halts the whole run.\n item.error = sanitizedSyncError(dateError);\n if (options.signal?.aborted) break;\n }\n }\n } catch (error) {\n // Client construction failed: no date could run for this source.\n item.error = sanitizedSyncError(error);\n }\n results.push(item);\n // An abort halts the WHOLE run: the in-flight source above is recorded\n // (its fast-failing attempt reports an error), but no later source builds\n // a client or contacts a daemon — teardown settles promptly.\n if (options.signal?.aborted) break;\n }\n } finally {\n if (ownStore) store.close();\n }\n\n return {\n ranAt: ranAt.toISOString(),\n enabled: true,\n ranCount: results.filter((item) => item.ran).length,\n errorCount: results.filter((item) => item.error !== undefined).length,\n reindexErrorCount: results.filter((item) => item.reindexError !== undefined).length,\n totalInserted: results.reduce((sum, item) => sum + item.inserted, 0),\n results,\n touchedDates: [...touchedDates].sort(),\n };\n}\n","/**\n * In-process periodic driver for activity (screen-capture) sync (issue #1900).\n *\n * The parser (config.ts) validates `config.activity` and the runner\n * (runner.ts) performs one durable sync pass, but nothing drove that pass on a\n * cadence — parsed config alone never synced. This scheduler is the missing\n * link: parser -> scheduler -> durable sync. It registers a repeating timer\n * that invokes the runner, and cancels it on stop.\n *\n * Master default-off: when `config.activity.enabled` is false, `start()`\n * registers no timer and never invokes — source config alone starts nothing.\n * `stop()` clears the timer and latches so no later tick invokes, which is what\n * a runtime teardown (or a config disable + restart) needs. Ticks are\n * overlap-guarded (a slow sync never stacks) and never throw out of the timer.\n *\n * Host-agnostic: the timer and clock are injectable, so a host schedules\n * through this without any OpenClaw import, and tests drive it deterministically.\n */\n\nimport { runActivitySyncOnce, type ActivitySyncRunSummary } from \"./runner.js\";\nimport type { ActivityConfig } from \"./types.js\";\n\n/**\n * Default sync cadence: 15 minutes. Issue #1899 contracts\n * `activity.autoSyncIntervalMinutes` with a default of 15, so the effective\n * default interval is 15 * 60_000 = 900_000 ms. Overridable via `intervalMs`.\n */\nexport const ACTIVITY_SYNC_DEFAULT_INTERVAL_MS = 900_000;\n\n/** An opaque timer handle from whichever timer implementation is injected. */\ntype TimerHandle = unknown;\n\nexport interface ActivitySyncSchedulerOptions {\n config: ActivityConfig;\n memoryDir: string;\n /** Cadence in ms; defaults to ACTIVITY_SYNC_DEFAULT_INTERVAL_MS. */\n intervalMs?: number;\n /** The sync pass to run each tick; defaults to the durable runner. */\n invoke?: (signal?: AbortSignal) => Promise<ActivitySyncRunSummary>;\n /** Search-index refresh forwarded to the default runner as `reindexSearch`. */\n reindexSearch?: (signal?: AbortSignal) => Promise<void>;\n /** Timer factory (injectable for tests); defaults to an unref'd setInterval. */\n setTimer?: (fn: () => void, ms: number) => TimerHandle;\n /** Timer canceller matching setTimer; defaults to clearInterval. */\n clearTimer?: (handle: TimerHandle) => void;\n /** Called with each successful run's summary (telemetry/tests). */\n onRun?: (summary: ActivitySyncRunSummary) => void;\n /** Called when a tick's sync rejects; defaults to swallowing (best-effort). */\n onError?: (error: unknown) => void;\n}\n\nexport interface ActivitySyncRegistration {\n /** True when a periodic timer was armed (feature enabled). */\n registered: boolean;\n /** The cadence the timer runs at (reported even when not registered). */\n intervalMs: number;\n}\n\n/**\n * Registers/cancels a periodic activity sync. One instance per memory dir;\n * `start()` and `stop()` are both idempotent.\n */\nexport class ActivitySyncScheduler {\n private readonly config: ActivityConfig;\n private readonly memoryDir: string;\n private readonly intervalMs: number;\n private readonly invoke: (signal?: AbortSignal) => Promise<ActivitySyncRunSummary>;\n private readonly setTimer: (fn: () => void, ms: number) => TimerHandle;\n private readonly clearTimer: (handle: TimerHandle) => void;\n private readonly onRun: ((summary: ActivitySyncRunSummary) => void) | undefined;\n private readonly onError: ((error: unknown) => void) | undefined;\n\n private handle: TimerHandle = null;\n private stopped = false;\n private inFlight = false;\n private inFlightPromise: Promise<void> | null = null;\n private readonly abortController = new AbortController();\n\n constructor(options: ActivitySyncSchedulerOptions) {\n this.config = options.config;\n this.memoryDir = options.memoryDir;\n this.intervalMs = options.intervalMs ?? ACTIVITY_SYNC_DEFAULT_INTERVAL_MS;\n this.invoke =\n options.invoke ??\n ((signal) =>\n runActivitySyncOnce({\n config: this.config,\n memoryDir: this.memoryDir,\n signal,\n ...(options.reindexSearch === undefined ? {} : { reindexSearch: options.reindexSearch }),\n }));\n this.setTimer =\n options.setTimer ??\n ((fn, ms) => {\n const timer = setInterval(fn, ms);\n // Never keep the host process alive solely for the sync cadence.\n timer.unref?.();\n return timer;\n });\n this.clearTimer =\n options.clearTimer ?? ((handle) => clearInterval(handle as NodeJS.Timeout));\n this.onRun = options.onRun;\n this.onError = options.onError;\n }\n\n /**\n * Arm the periodic sync. Disabled config (default-off) registers nothing.\n * Idempotent: a second call while running (or after stop) re-arms nothing.\n */\n start(): ActivitySyncRegistration {\n if (this.handle !== null || this.stopped || !this.config.enabled) {\n return { registered: this.handle !== null, intervalMs: this.intervalMs };\n }\n this.handle = this.setTimer(() => this.tick(), this.intervalMs);\n return { registered: true, intervalMs: this.intervalMs };\n }\n\n /**\n * Cancel the periodic sync and abort any in-flight tick, then resolve once it\n * has unwound. The abort + timer-clear + stop-latch happen synchronously, so\n * even a caller that cannot await (a sync teardown) still signals the running\n * sync to stop; async callers can await the returned drain. Idempotent.\n */\n async stop(): Promise<void> {\n this.stopped = true;\n this.abortController.abort();\n if (this.handle !== null) {\n this.clearTimer(this.handle);\n this.handle = null;\n }\n await this.inFlightPromise?.catch(() => undefined);\n }\n\n private tick(): void {\n // Latched-stop and overlap guards: a callback that fires after stop(), or\n // while a prior slow sync is still running, must not invoke.\n if (this.stopped || this.inFlight) return;\n this.inFlight = true;\n this.inFlightPromise = this.invoke(this.abortController.signal)\n .then((summary) => this.onRun?.(summary))\n .catch((error) => this.onError?.(error))\n .finally(() => {\n this.inFlight = false;\n this.inFlightPromise = null;\n });\n }\n}\n","/**\n * Activity (screen-capture) sync lifecycle owner (issue #1900).\n *\n * The MaintenanceScheduler used to inline the arm / re-arm / teardown wiring\n * for the periodic activity sync, which made the lifecycle invariants easy to\n * break one edit at a time. This module centralizes them in one testable place:\n *\n * - Re-arm and teardown BOTH stop any prior scheduler first, so a repeated\n * registration never orphans a running interval.\n * - An aborted deferred-init signal means teardown began. `destroy()` aborts\n * the init signal *before* it disposes, so an aborted signal here also\n * implies `dispose()` may already have latched teardown. `register()` never\n * arms nor clears the latch while aborted — even when the abort lands\n * mid-drain — so a late re-init cannot resurrect a torn-down instance.\n * - The teardown latch gates the reindex-failure retry, so a draining tick's\n * `onRun` cannot queue QMD maintenance after teardown.\n * - Activity digests + SQLite are written in the clear, so the registrar\n * refuses to arm under a secure store rather than silently bypass opted-in\n * at-rest encryption.\n *\n * Host-agnostic: the scheduler factory is injectable, so tests drive the whole\n * lifecycle against a fake scheduler without real timers.\n */\n\nimport { refreshActivityIndex, type ActivityIndexRefresher } from \"./reindex.js\";\nimport type { ActivitySyncRunSummary } from \"./runner.js\";\nimport { ActivitySyncScheduler, type ActivitySyncSchedulerOptions } from \"./scheduler.js\";\nimport type { ActivityConfig } from \"./types.js\";\nimport { log } from \"../logger.js\";\n\n/** The scheduler surface the registrar drives (injectable for tests). */\nexport interface ActivitySyncSchedulerLike {\n start(): unknown;\n stop(): Promise<void>;\n}\n\nexport interface ActivitySyncRegistrarDeps {\n /** Activity sync settings; the master default-off flag lives here. */\n readonly config: ActivityConfig;\n /** Memory root the digests + SQLite store live under. */\n readonly memoryDir: string;\n /** QMD collection the day digest is (strictly) reindexed into. */\n readonly qmdCollection: string;\n /**\n * True when at-rest encryption is configured. Activity writes are not yet\n * encrypted, so the registrar refuses to arm rather than leak plaintext.\n */\n readonly secureStoreEnabled: boolean;\n /** Live QMD accessor — the host may swap backends after construction. */\n readonly getQmd: () => ActivityIndexRefresher;\n /** Debounced/singleflighted QMD retry used when a tick's reindex fails. */\n readonly requestReindexRetry: () => void;\n /**\n * Tail step (issue #1900): observe a completed sync tick so a dependent\n * subsystem (meeting building) can rebuild the affected day(s). Fired only\n * after a durable tick and never after teardown. Optional; omitted by hosts\n * that do not build meetings.\n */\n readonly onSynced?: (summary: ActivitySyncRunSummary) => void;\n /** Scheduler factory (injectable for tests); defaults to the real scheduler. */\n readonly createScheduler?: (options: ActivitySyncSchedulerOptions) => ActivitySyncSchedulerLike;\n}\n\nexport class ActivitySyncRegistrar {\n private scheduler: ActivitySyncSchedulerLike | null = null;\n /** Latched by dispose() so a draining tick's onRun cannot re-arm maintenance. */\n private disposed = false;\n private readonly createScheduler: (options: ActivitySyncSchedulerOptions) => ActivitySyncSchedulerLike;\n\n constructor(private readonly deps: ActivitySyncRegistrarDeps) {\n this.createScheduler = deps.createScheduler ?? ((options) => new ActivitySyncScheduler(options));\n }\n\n /** True while a scheduler is armed (test/telemetry observability). */\n get armed(): boolean {\n return this.scheduler !== null;\n }\n\n /**\n * Arm (or re-arm) the periodic sync for the current lifecycle. Idempotent and\n * safe to call repeatedly; always stops a prior scheduler first.\n */\n async register(signal: AbortSignal): Promise<void> {\n // Re-arm or teardown: stop any prior scheduler before anything else, so a\n // repeated registration never orphans a running interval.\n const prior = this.scheduler;\n this.scheduler = null;\n await prior?.stop();\n // Teardown began (possibly during the drain above). Never arm nor clear the\n // latch: dispose() may already have set it, and it must stay set.\n if (signal.aborted) return;\n // Opted-in at-rest encryption must not be silently bypassed: activity\n // digests + SQLite are written in the clear, so refuse to run under it.\n if (this.deps.secureStoreEnabled && this.deps.config.enabled) {\n log.warn(\n \"activity sync disabled: secure store is enabled but activity digests/SQLite are not encrypted at rest\",\n );\n return;\n }\n // Committed to a fresh lifecycle: clear the latch so this scheduler's\n // reindex retries fire again.\n this.disposed = false;\n try {\n const scheduler = this.createScheduler({\n config: this.deps.config,\n memoryDir: this.deps.memoryDir,\n intervalMs: this.deps.config.autoSyncIntervalMinutes * 60_000,\n // Strict refresh after each digest write (rule 31): bypasses the\n // min-interval gate and throws on a real failure.\n reindexSearch: (sig) => refreshActivityIndex(this.deps.getQmd(), this.deps.qmdCollection, sig),\n // A failed reindex (QMD down) leaves the next unchanged-digest tick\n // skipping afterWrites, so route the retry through the durable,\n // throttled QMD path — but never after teardown.\n onRun: (summary: ActivitySyncRunSummary) => {\n if (summary.reindexErrorCount > 0 && !this.disposed) {\n log.warn(\n `activity sync: ${summary.reindexErrorCount} source(s) had a failed reindex; queuing a maintenance retry`,\n );\n this.deps.requestReindexRetry();\n }\n // Tail step: a durable tick that ingested snapshots may have changed a\n // meeting's screen context; notify the dependent builder. Never after\n // teardown.\n if (summary.enabled && summary.totalInserted > 0 && !this.disposed) {\n this.deps.onSynced?.(summary);\n }\n },\n });\n this.scheduler = scheduler;\n scheduler.start();\n // Close the race where abort fires between the guard above and start().\n if (signal.aborted) {\n await scheduler.stop();\n this.scheduler = null;\n }\n } catch (err) {\n log.debug(`activity sync scheduler start failed (non-fatal): ${err}`);\n }\n }\n\n /**\n * Latch teardown and drain the scheduler. Latching first ensures a draining\n * tick's onRun cannot queue a retry; the drain is awaited so no in-flight\n * tick keeps writing after teardown.\n */\n async dispose(): Promise<void> {\n this.disposed = true;\n const drain = this.scheduler?.stop();\n this.scheduler = null;\n await drain;\n }\n}\n"],"mappings":"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","names":["mkdir","readFile","writeFile","path","id","mkdir","path","mkdir","path","isRecord","path","path","mkdir","writeFile","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/orchestration/maintenance.ts","../src/maintenance/day-summary-cron.ts","../src/orchestration/projection-rebuild-schedule.ts","../src/activity/reindex.ts","../src/activity/store.ts","../src/activity/pipeline.ts","../src/activity/source-client.ts","../src/activity/runner.ts","../src/activity/scheduler.ts","../src/activity/sync-registration.ts"],"sourcesContent":["/**\n * Maintenance scheduler — extracted from the orchestrator (issue #1526 PR1).\n *\n * Owns the WHEN of memory maintenance:\n * - cron auto-registration (writes jobs.json entries the OpenClaw cron\n * daemon picks up)\n * - debounced + singleflight QMD index maintenance (re-index + embed\n * after writes)\n * - consolidation scheduling trigger (count + interval gated)\n *\n * Does NOT own the WHAT — the actual job runners (runSemanticConsolidation,\n * runPatternReinforcement, runDeepSleepGovernanceNow, runConsolidation)\n * remain on the orchestrator because they need the full LLM + extraction\n * dependency surface. The scheduler invokes consolidation through a\n * callback so the orchestrator stays the source of truth for that work.\n *\n * Behavior-preserving move from orchestrator.ts. No logic changes — the\n * orchestrator keeps thin delegating methods so existing tests that stub\n * `orchestrator.requestQmdMaintenance` etc. continue to work.\n */\n\nimport { resolveNamespaceCapabilities,\n resolveQmdCapabilities,resolveConsolidationCapabilities, resolveRecallAuxiliaryCapabilities } from \"../capabilities.js\";\nimport { existsSync, type Dirent } from \"node:fs\";\nimport { appendFile, lstat, mkdir, readFile, readdir, realpath, stat, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport type { PluginConfig } from \"../types.js\";\nimport type { SearchBackend } from \"../search/port.js\";\nimport type { StorageManager } from \"../index.js\";\nimport {\n NamespaceSearchRouter,\n type NamespaceUpdateResult,\n} from \"../namespaces/search.js\";\nimport type { NamespaceCatalog, NamespaceRecord } from \"../namespaces/catalog.js\";\nimport { namespaceIdentityFromToken } from \"../namespaces/identity.js\";\nimport { isSafeRouteNamespace } from \"../routing/engine.js\";\nimport {\n planNamespaceMaintenance,\n runNamespaceMaintenanceBatchPlan,\n type NamespaceMaintenancePlan,\n type NamespaceMaintenanceSkipReason,\n} from \"../maintenance/namespace-planner.js\";\nimport {\n ensureContradictionScanCron,\n ensureDaySummaryCron,\n ensureGraphEdgeDecayCron,\n ensureNightlyGovernanceCron,\n ensurePatternReinforcementCron,\n ensureProceduralMiningCron,\n graphEdgeDecayCadenceToCronExpr,\n} from \"../maintenance/memory-governance-cron.js\";\nimport { rebuildMemoryLifecycleLedger } from \"../maintenance/rebuild-memory-lifecycle-ledger.js\";\nimport { resolveDaySummaryCronModel } from \"../maintenance/day-summary-cron.js\";\nimport {\n createProjectionRebuildScheduleState,\n maybeRebuildMemoryProjectionScheduled,\n} from \"./projection-rebuild-schedule.js\";\nimport {\n drainPendingLifecycleLedgerIfAny,\n pendingLifecycleLedgerDir,\n type LifecyclePendingIo,\n} from \"../storage/memory-lifecycle-ledger-access.js\";\nimport { STATE_FILE_MAX_DECRYPT_BYTES } from \"../storage/secure-line-reader.js\";\nimport { startLocationAutoSync, type LocationAutoSyncHandle } from \"../location/scheduler.js\";\nimport { ActivitySyncRegistrar } from \"../activity/sync-registration.js\";\nimport type { ActivitySyncRunSummary } from \"../activity/runner.js\";\nimport { resolveHomeDir } from \"../runtime/env.js\";\nimport { log } from \"../logger.js\";\nimport { isErrnoCode } from \"../utils/errno.js\";\nimport { assertPathInsideRoot, listContainedSpillFiles } from \"../utils/path-containment.js\";\nimport {\n probeEncryptedRegularFileHeader,\n SECURE_STORE_ENVELOPE_OVERHEAD_BYTES,\n} from \"../secure-store/secure-fs.js\";\n\n/** Reason a QMD maintenance pass was skipped, used for status recording. */\nfunction qmdMaintenanceSkipReasonForError(\n error: unknown,\n): NamespaceMaintenanceSkipReason | null {\n const message = error instanceof Error ? error.message : String(error);\n return /^QMD (?:update|embed) skipped by .*min-interval gate$/.test(message)\n ? \"throttled\"\n : null;\n}\n\n/** Dependencies injected by the orchestrator. All stable references. */\nexport interface MaintenanceSchedulerDeps {\n config: PluginConfig;\n /** Live accessor — the orchestrator can reassign its qmd backend after\n * construction (e.g. swap to NoopSearchBackend when the collection is\n * missing), and the scheduler must always observe the current backend. */\n getQmd: () => SearchBackend;\n namespaceSearchRouter: NamespaceSearchRouter;\n namespaceCatalog: NamespaceCatalog;\n /**\n * Root storage (secure-store configured). Threaded so lifecycle-ledger\n * auto-compaction (#1910) rebuilds through the live secure context: encrypted\n * memories stay decryptable and the rewritten ledger stays encrypted at rest.\n * Omitted in focused tests, where compaction falls back to a fresh plaintext\n * StorageManager (the CLI-equivalent path).\n */\n getStorage?: () => StorageManager;\n /**\n * Resolve a namespace's (secure-store aware) storage. Threaded so\n * auto-compaction also bounds per-namespace lifecycle ledgers under\n * `memoryDir/namespaces/<token>/state/`, not just the root state path\n * (#1910). Consulted only when namespaces are enabled.\n */\n storageForNamespace?: (namespace: string) => Promise<StorageManager>;\n /**\n * Tail step (issue #1900): observe a durable activity sync tick so the\n * meetings builder can rebuild the affected day(s). Optional; omitted by\n * hosts that do not build meetings.\n */\n onActivitySynced?: (summary: ActivitySyncRunSummary) => void;\n}\n\n/**\n * Schedules and debounces memory maintenance work. Owns the cadence +\n * singleflight state that previously lived as private orchestrator fields.\n *\n * The orchestrator constructs one instance in its constructor and delegates\n * the public scheduling entrypoints to it. Job execution stays on the\n * orchestrator (LLM-heavy) and is invoked through callbacks.\n */\nexport class MaintenanceScheduler {\n // ── Consolidation scheduling state ──\n private nonZeroExtractionsSinceConsolidation = 0;\n private lastConsolidationRunAtMs = 0;\n private consolidationRunPromise: Promise<unknown> | null = null;\n private disposed = false;\n\n // ── QMD maintenance scheduling state ──\n private qmdMaintenanceTimer: NodeJS.Timeout | null = null;\n private qmdMaintenancePending = false;\n private qmdMaintenanceInFlight = false;\n private lastQmdEmbedAtMs = 0;\n private lastQmdEmbedAtMsByNamespace = new Map<string, number>();\n\n // ── Lifecycle-ledger auto-compaction state (issue #1910) ──\n private lifecycleCompactionInFlight = false;\n private lastLifecycleCompactionAtMs = 0;\n // Upper bound on a rewritten ledger; a preserving compaction is bounded to\n // this so it can never leave the ledger over the whole-file read/decrypt cap\n // (#2033). Defaults to the decrypt cap; overridable in focused tests to drive\n // the bounding + post-write verification deterministically without a 400MB\n // fixture.\n private lifecycleLedgerMaxBytes = STATE_FILE_MAX_DECRYPT_BYTES;\n\n // ── Memory-projection scheduled-rebuild state (issue #2119) ──\n private readonly projectionRebuild = createProjectionRebuildScheduleState();\n\n // ── Activity (screen-capture) sync scheduler (issue #1900) ──\n // The full arm/re-arm/teardown lifecycle lives in ActivitySyncRegistrar.\n private readonly activityRegistrar: ActivitySyncRegistrar;\n // ── Location day-sync scheduler (issue #2047) — master default-off; the\n // timer + lifecycle contract lives in location/scheduler.ts.\n private locationAutoSync: LocationAutoSyncHandle | null = null;\n\n\n constructor(private readonly deps: MaintenanceSchedulerDeps) {\n this.activityRegistrar = new ActivitySyncRegistrar({\n config: deps.config.activity,\n memoryDir: deps.config.memoryDir,\n qmdCollection: deps.config.qmdCollection,\n secureStoreEnabled: resolveRecallAuxiliaryCapabilities(deps.config).secureStore,\n getQmd: () => deps.getQmd(),\n requestReindexRetry: () => this.requestQmdMaintenanceForTool(\"activity-reindex-retry\"),\n ...(deps.onActivitySynced ? { onSynced: deps.onActivitySynced } : {}),\n });\n }\n\n // ───────────────────────────────────────────────────────────────────────\n // Cron auto-registration\n // ───────────────────────────────────────────────────────────────────────\n\n /**\n * Register every cron job the daemon should run, gated by its config flag.\n * Each registration is independent and non-fatal — a failure in one does\n * not block the others (mirrors the orchestrator's prior sequential try/\n * catch behavior exactly).\n *\n * Returns when all eligible registrations have completed so callers that\n * `await deferredReady` can rely on jobs.json being current.\n */\n async autoRegisterCrons(signal: AbortSignal): Promise<void> {\n if (resolveRecallAuxiliaryCapabilities(this.deps.config).daySummary) {\n try {\n await this.autoRegisterDaySummaryCron();\n } catch (err) {\n log.debug(`day-summary cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (this.deps.config.nightlyGovernanceCronAutoRegister) {\n try {\n await this.autoRegisterNightlyGovernanceCron();\n } catch (err) {\n log.debug(`nightly governance cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (this.deps.config.procedural?.proceduralMiningCronAutoRegister) {\n try {\n await this.autoRegisterProceduralMiningCron();\n } catch (err) {\n log.debug(`procedural mining cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (this.deps.config.contradictionScan?.enabled) {\n try {\n await this.autoRegisterContradictionScanCron();\n } catch (err) {\n log.debug(`contradiction scan cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (resolveConsolidationCapabilities(this.deps.config).patternReinforcement) {\n try {\n await this.autoRegisterPatternReinforcementCron();\n } catch (err) {\n log.debug(`pattern reinforcement cron auto-register failed (non-fatal): ${err}`);\n }\n }\n if (resolveConsolidationCapabilities(this.deps.config).graphEdgeDecay) {\n try {\n await this.autoRegisterGraphEdgeDecayCron();\n } catch (err) {\n log.debug(`graph edge decay cron auto-register failed (non-fatal): ${err}`);\n }\n }\n\n // Activity (screen-capture) in-process sync scheduler (issue #1900),\n // master default-off. The registrar owns the arm/re-arm/teardown contract\n // (stop-prior, abort/teardown guards, secure-store refusal, retry latch).\n await this.activityRegistrar.register(signal);\n // Location day sync (issue #2047): same deferred lifecycle as activity;\n // ticks run the SAME shared runner as the CLI/MCP/HTTP surfaces.\n if (\n !signal.aborted &&\n this.deps.config.location.enabled &&\n this.deps.config.location.sources.some((source) => source.enabled)\n ) {\n this.locationAutoSync = startLocationAutoSync({\n config: this.deps.config.location,\n memoryDir: this.deps.config.memoryDir,\n });\n }\n }\n\n async autoRegisterDaySummaryCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"day-summary cron: jobs.json not found, skipping auto-register\");\n return;\n }\n\n const { model, fallbacks } = resolveDaySummaryCronModel(this.deps.config);\n\n const timezone =\n this.deps.config.daySummaryTimezone ||\n Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n const result = await ensureDaySummaryCron(jobsPath, {\n timezone,\n ...(model ? { model } : {}),\n ...(fallbacks.length > 0 ? { fallbacks } : {}),\n });\n if (result.created) {\n log.info(\n `day-summary cron auto-registered (${result.jobId}, 23:47 ${timezone}${model ? `, model: ${model}` : \"\"})`,\n );\n } else if (result.updated) {\n log.info(\n `day-summary cron reconciled (${result.jobId}, timezone: ${timezone}${model ? `, model: ${model}` : \"\"})`,\n );\n } else {\n log.debug(\"day-summary cron already up to date\");\n }\n } catch (err) {\n log.debug(`day-summary cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterNightlyGovernanceCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"nightly governance cron: jobs.json not found, skipping auto-register\");\n return;\n }\n\n const created = await ensureNightlyGovernanceCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(\n `nightly governance cron auto-registered (${created.jobId}, 02:23 ${Intl.DateTimeFormat().resolvedOptions().timeZone})`,\n );\n } else {\n log.debug(\"nightly governance cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`nightly governance cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterProceduralMiningCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"procedural mining cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const created = await ensureProceduralMiningCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(`procedural mining cron auto-registered (${created.jobId})`);\n } else {\n log.debug(\"procedural mining cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`procedural mining cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterContradictionScanCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"contradiction scan cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const created = await ensureContradictionScanCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(`contradiction scan cron auto-registered (${created.jobId})`);\n } else {\n log.debug(\"contradiction scan cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`contradiction scan cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterPatternReinforcementCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"pattern reinforcement cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const created = await ensurePatternReinforcementCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n });\n if (created.created) {\n log.info(`pattern reinforcement cron auto-registered (${created.jobId})`);\n } else {\n log.debug(\"pattern reinforcement cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`pattern reinforcement cron auto-register error: ${err}`);\n }\n }\n\n async autoRegisterGraphEdgeDecayCron(): Promise<void> {\n const home = resolveHomeDir();\n const jobsPath = path.join(home, \".openclaw\", \"cron\", \"jobs.json\");\n try {\n if (!existsSync(jobsPath)) {\n log.debug(\"graph edge decay cron: jobs.json not found, skipping auto-register\");\n return;\n }\n const scheduleExpr = graphEdgeDecayCadenceToCronExpr(\n this.deps.config.graphEdgeDecayCadenceMs,\n );\n const created = await ensureGraphEdgeDecayCron(jobsPath, {\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n scheduleExpr,\n });\n if (created.created) {\n log.info(`graph edge decay cron auto-registered (${created.jobId}, ${scheduleExpr})`);\n } else {\n log.debug(\"graph edge decay cron already exists, skipping auto-register\");\n }\n } catch (err) {\n log.debug(`graph edge decay cron auto-register error: ${err}`);\n }\n }\n\n // ───────────────────────────────────────────────────────────────────────\n // Consolidation scheduling\n // ───────────────────────────────────────────────────────────────────────\n\n /**\n * Maybe trigger a background consolidation pass after an extraction.\n * Counted + interval gated so a burst of extractions does not fan into a\n * burst of consolidation passes. The consolidation runner itself is\n * passed in as a callback because it lives on the orchestrator (LLM +\n * storage dependency surface) and is not owned by this module.\n */\n maybeScheduleConsolidation(\n nonZeroExtraction: boolean,\n runConsolidation: () => Promise<unknown>,\n ): void {\n if (nonZeroExtraction) this.nonZeroExtractionsSinceConsolidation += 1;\n if (\n this.deps.config.consolidationRequireNonZeroExtraction &&\n !nonZeroExtraction\n ) {\n return;\n }\n if (\n this.nonZeroExtractionsSinceConsolidation <\n this.deps.config.consolidateEveryN\n ) {\n return;\n }\n\n const now = Date.now();\n if (\n now - this.lastConsolidationRunAtMs <\n this.deps.config.consolidationMinIntervalMs\n ) {\n return;\n }\n if (this.disposed || this.consolidationRunPromise) return;\n\n this.lastConsolidationRunAtMs = now;\n this.nonZeroExtractionsSinceConsolidation = 0;\n this.consolidationRunPromise = Promise.resolve()\n .then(runConsolidation)\n .catch((err) => {\n log.error(\"background consolidation failed\", err);\n })\n .finally(() => {\n this.consolidationRunPromise = null;\n });\n }\n\n /** Whether a background consolidation pass is currently running. Exposed so\n * the orchestrator's waitForConsolidationIdle can poll idle state without\n * owning the cadence state (issue #1526 PR1). */\n isConsolidationInFlight(): boolean {\n return this.consolidationRunPromise !== null;\n }\n\n /** Whether a debounced QMD maintenance pass is armed and waiting for the\n * debounce timer. Exposed so drain/teardown diagnostics (e.g. the bench\n * adapter) can observe scheduler idle state after the #1526 PR1 extraction\n * moved this flag off the orchestrator. */\n isQmdMaintenancePending(): boolean {\n return this.qmdMaintenancePending;\n }\n\n /** Whether a QMD maintenance pass is currently running (singleflight guard).\n * Exposed for drain/teardown diagnostics (see isQmdMaintenancePending). */\n isQmdMaintenanceInFlight(): boolean {\n return this.qmdMaintenanceInFlight;\n }\n // ───────────────────────────────────────────────────────────────────────\n // QMD maintenance scheduling (debounced + singleflight)\n // ───────────────────────────────────────────────────────────────────────\n\n /** Internal: queue a debounced QMD maintenance pass. */\n private requestQmdMaintenance(): void {\n if (!this.deps.getQmd().isAvailable()) return;\n if (!resolveQmdCapabilities(this.deps.config).qmdMaintenance) return;\n\n this.qmdMaintenancePending = true;\n if (this.qmdMaintenanceTimer) return;\n\n this.qmdMaintenanceTimer = setTimeout(() => {\n this.qmdMaintenanceTimer = null;\n this.runQmdMaintenance().catch((err) =>\n log.debug(`background qmd maintenance failed: ${err}`),\n );\n }, this.deps.config.qmdMaintenanceDebounceMs);\n }\n\n /**\n * Public entrypoint for tool-driven QMD maintenance requests.\n * Routes through existing debounced/singleflight maintenance controls.\n */\n requestQmdMaintenanceForTool(reason: string): void {\n try {\n this.requestQmdMaintenance();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n log.warn(`qmd maintenance request failed (${reason}): ${msg}`);\n }\n // Lifecycle-ledger auto-compaction (#1910) must run independently of QMD.\n // When QMD is unavailable or its maintenance capability is off,\n // requestQmdMaintenance() short-circuits and runQmdMaintenance() never\n // fires — so a compaction check living only in that path would never run.\n // Fire it here on every maintenance request (throttled, size-gated,\n // single-flighted, never awaited, never throws) so a QMD-off deployment\n // still bounds the lifecycle ledger.\n void this.maybeCompactMemoryLifecycleLedger().catch((err) =>\n log.debug(`lifecycle ledger auto-compaction check failed (non-fatal): ${err}`),\n );\n // Memory-projection scheduled rebuild (#2119): interval-throttled,\n // single-flighted, never awaited, never throws — see the module docblock.\n void maybeRebuildMemoryProjectionScheduled({\n config: this.deps.config,\n getStorage: this.deps.getStorage,\n state: this.projectionRebuild,\n }).catch((err) =>\n log.debug(`memory projection scheduled rebuild check failed (non-fatal): ${err}`),\n );\n }\n\n /** Internal: run a single QMD maintenance pass under singleflight guard. */\n async runQmdMaintenance(): Promise<void> {\n if (this.qmdMaintenanceInFlight) return;\n if (!this.qmdMaintenancePending) return;\n this.qmdMaintenanceInFlight = true;\n this.qmdMaintenancePending = false;\n\n try {\n if (resolveNamespaceCapabilities(this.deps.config).namespaces) {\n // Include cataloged dynamic namespaces, not just the configured set\n // (NGnei), but run through the namespace-aware maintenance planner so\n // each namespace is budgeted, lock-protected, and status-recorded\n // independently (issue #1500).\n const plan = await this.namespaceMaintenancePlan(\"qmd\");\n const now = Date.now();\n const lastEmbedAtByNamespace = this.lastQmdEmbedAtMsByNamespace;\n const dueEmbedNamespaces = (namespaces: string[]): string[] => {\n if (!resolveQmdCapabilities(this.deps.config).qmdAutoEmbed) return [];\n return namespaces.filter(\n (namespace) =>\n now - (lastEmbedAtByNamespace.get(namespace) ?? 0) >=\n this.deps.config.qmdEmbedMinIntervalMs,\n );\n };\n const markEmbedded = (namespaces: string[]): void => {\n if (namespaces.length === 0) return;\n for (const namespace of namespaces) {\n lastEmbedAtByNamespace.set(namespace, now);\n }\n this.lastQmdEmbedAtMs = now;\n };\n await runNamespaceMaintenanceBatchPlan(\n this.deps.config,\n plan,\n async (candidates) => {\n const namespaces = candidates.map((candidate) => candidate.namespace);\n const embedNamespaces = dueEmbedNamespaces(namespaces);\n let result: NamespaceUpdateResult;\n try {\n result = await this.deps.namespaceSearchRouter.updateNamespacesDetailed(\n namespaces,\n undefined,\n { strict: true },\n );\n } catch (error) {\n if (\n embedNamespaces.length > 0 &&\n qmdMaintenanceSkipReasonForError(error) === \"throttled\"\n ) {\n await this.deps.namespaceSearchRouter.embedNamespaces(embedNamespaces, {\n strict: true,\n });\n markEmbedded(embedNamespaces);\n }\n throw error;\n }\n if (result.backendCount <= 0) {\n throw new Error(\"no eligible QMD backend for selected namespaces\");\n }\n if (result.eligibleNamespaces.length !== namespaces.length) {\n const eligible = new Set(result.eligibleNamespaces);\n const missing = namespaces.filter(\n (namespace) => !eligible.has(namespace),\n );\n throw new Error(\n `QMD backend ineligible for selected namespaces (${missing.length})`,\n );\n }\n if (embedNamespaces.length > 0) {\n await this.deps.namespaceSearchRouter.embedNamespaces(embedNamespaces, {\n strict: true,\n });\n markEmbedded(embedNamespaces);\n }\n return { itemCount: result.backendCount };\n },\n this.deps.namespaceCatalog,\n {\n skipReasonForError: qmdMaintenanceSkipReasonForError,\n },\n );\n } else {\n await this.deps.getQmd().update();\n const now = Date.now();\n if (\n resolveQmdCapabilities(this.deps.config).qmdAutoEmbed &&\n now - this.lastQmdEmbedAtMs >= this.deps.config.qmdEmbedMinIntervalMs\n ) {\n await this.deps.getQmd().embed();\n this.lastQmdEmbedAtMs = now;\n }\n }\n // Note: a successful QMD update/embed clears the global QMD result caches\n // itself (QmdClient.runUpdateForCollection / runEmbedForCollection), so\n // this scheduler does not invalidate them here — every refresh path\n // (direct update, namespace router, wearable/OpenClaw sync) funnels through\n // the backend and clears centrally. A no-op pass (throttle/unavailable/\n // backoff) does not advance lastUpdateRanAtMs, so it leaves caches warm\n // (#1904, Codex/Cursor).\n } finally {\n this.qmdMaintenanceInFlight = false;\n if (this.qmdMaintenancePending) {\n this.requestQmdMaintenance();\n }\n // Lifecycle-ledger auto-compaction is triggered from\n // requestQmdMaintenanceForTool (every maintenance request, QMD or not),\n // so it does not need to be re-fired here (issue #1910).\n }\n }\n\n /**\n * Size-gated, throttled, single-flighted auto-compaction of the lifecycle\n * ledger (issue #1910). Compacts the root ledger AND every namespace ledger\n * (`memoryDir/namespaces/<token>/state/`) when namespaces are enabled, each\n * through the existing `rebuildMemoryLifecycleLedger` so the\n * archive-then-atomic-write discipline is reused verbatim. `0` disables.\n *\n * Before compacting, drain EVERY target's durable pending-append spill (the\n * root ledger AND each namespace ledger) so a lifecycle event that spilled\n * while a prior long rewrite held the lock is folded back into its ledger and\n * cannot be silently lost (issue #2033). Draining is independent of both the\n * compaction threshold and the min-interval throttle: targets are enumerated\n * through the same safe (symlink/containment-checked) resolver and every\n * eligible queue is drained BEFORE any throttle return, so a namespace spill\n * created after the previous pass never waits out the whole interval.\n * Encrypted-store safety is preserved — a keyed target drains through its\n * secure StorageManager, a filesystem-fallback target only through a plaintext\n * context that refuses encrypted ledgers/spills. Work stays bounded: one\n * target enumeration plus a bounded per-queue drain.\n *\n * The min-interval throttle timestamp advances ONLY after real, fully\n * successful compaction work: a failed OR deferred target leaves the throttle\n * un-advanced so it stays eligible on the next maintenance pass, while\n * `lifecycleCompactionInFlight` still prevents overlapping runs. A \"deferred\"\n * target is oversized but could not be compacted because its encrypted ledger\n * has no unlocked secure store — arming the throttle for it would suppress the\n * retry of that still-oversized ledger for the whole interval (#2033).\n *\n * The min-interval throttle is bypassed for one case: an encrypted ledger\n * already at/over the whole-file decrypt cap is unreadable, so it must be\n * clamped on the next pass rather than wait out the interval. Ordinary\n * below-cap (or plaintext) targets stay throttled (#2033).\n */\n private async maybeCompactMemoryLifecycleLedger(): Promise<void> {\n // Enumerate every lifecycle target (root + namespaces) through the safe,\n // containment-checked resolver, and fold each one's durable pending spill\n // back into its ledger BEFORE any threshold or throttle return (#2033). A\n // spill created after the previous pass must land promptly even while\n // compaction is throttled or disabled; draining is bounded per queue and\n // never bypasses encrypted-store safety.\n const targets = await this.resolveLifecycleCompactionTargets();\n for (const target of targets) {\n await this.drainPendingForTarget(target);\n }\n\n const threshold = this.deps.config.memoryLifecycleLedgerCompactBytes;\n if (!(threshold > 0)) return; // 0 / negative / non-numeric disables compaction.\n if (this.lifecycleCompactionInFlight) return;\n const now = Date.now();\n if (\n now - this.lastLifecycleCompactionAtMs <\n this.deps.config.memoryLifecycleLedgerCompactMinIntervalMs &&\n !(await this.hasOverCapEncryptedLifecycleLedger(targets))\n ) {\n // Throttled: no ordinary target is due yet. The exception is an encrypted\n // ledger already at/over the whole-file decrypt cap — it is unreadable, so\n // the global min-interval must not defer clamping it for a whole interval.\n // The guard above lets such a target through immediately (compaction then\n // clamps it via the cap-aware effective threshold) while ordinary\n // below-cap targets stay throttled (#2033).\n return;\n }\n // The over-cap probe above awaits, so a second maintenance request can pass\n // the early `lifecycleCompactionInFlight` guard and reach here during that\n // await. Recheck the guard immediately before claiming it — otherwise two\n // racing requests both see an over-cap ledger and run duplicate 400MB-class\n // compactions/backups back-to-back (#2033). The set below is synchronous\n // with this recheck (no await between), so exactly one caller wins.\n if (this.lifecycleCompactionInFlight) return;\n this.lifecycleCompactionInFlight = true;\n try {\n let compacted = 0;\n let failed = 0;\n let deferred = 0;\n for (const target of targets) {\n const outcome = await this.compactLifecycleLedgerTarget(target, threshold);\n if (outcome === \"compacted\") compacted += 1;\n else if (outcome === \"failed\") failed += 1;\n else if (outcome === \"deferred\") deferred += 1;\n }\n // Arm the throttle only when at least one ledger actually compacted AND\n // nothing failed AND nothing was deferred. A below-threshold no-op\n // (compacted === 0), any failure, or a deferred oversized ledger (locked\n // encrypted store, still pending) leaves the throttle where it was so the\n // next pass retries the untouched targets (#2033).\n if (compacted > 0 && failed === 0 && deferred === 0) {\n this.lastLifecycleCompactionAtMs = now;\n }\n } finally {\n this.lifecycleCompactionInFlight = false;\n }\n }\n\n /**\n * True when any lifecycle target's ledger is encrypted at rest AND its\n * on-disk size is at/over the whole-file decrypt cap. Such a ledger is already\n * unreadable (the secure reader refuses at/over the cap), so it must reach the\n * bounded compaction path on the next maintenance pass regardless of the\n * global min-interval throttle — otherwise the throttle could keep an\n * unreadable ledger un-clamped for a whole interval (#2033). Ordinary\n * below-cap or plaintext targets never match, so the throttle still governs\n * them. A stat/probe failure yields \"no forced target\" and is left to the\n * normal compaction path, which classifies it as failed and retries.\n */\n private async hasOverCapEncryptedLifecycleLedger(\n targets: Array<{ memoryDir: string; storage?: StorageManager }>,\n ): Promise<boolean> {\n for (const target of targets) {\n const ledgerPath = path.join(target.memoryDir, \"state\", \"memory-lifecycle-ledger.jsonl\");\n let size: number;\n try {\n size = (await stat(ledgerPath)).size;\n } catch {\n continue; // absent/unstattable — nothing to force here.\n }\n if (size < this.lifecycleLedgerMaxBytes) continue;\n try {\n if (await probeEncryptedRegularFileHeader(ledgerPath)) return true;\n } catch {\n continue; // probe failure — the normal compaction path reports it.\n }\n }\n return false;\n }\n\n /**\n * Drain one compaction target's durable pending spill. A catalog-backed\n * target folds through its secure StorageManager; a filesystem-fallback\n * target (no storage) folds through a path-scoped plaintext context that\n * refuses encrypted ledgers/spills so a keyless drain never downgrades an\n * encrypted ledger (issue #2033).\n */\n private async drainPendingForTarget(\n target: { memoryDir: string; storage?: StorageManager },\n ): Promise<void> {\n if (target.storage) {\n await this.drainPendingLifecycleAppends(target.storage);\n } else {\n await this.drainPendingLifecycleAppendsForPath(target.memoryDir);\n }\n }\n\n /** Drain a target's durable pending lifecycle-append spill into its ledger\n * (issue #2033); non-fatal and a fast no-op when nothing is pending. */\n private async drainPendingLifecycleAppends(storage: StorageManager | undefined): Promise<void> {\n if (!storage) return;\n try {\n await storage.drainPendingMemoryLifecycleEvents();\n } catch (err) {\n log.debug(`lifecycle pending drain failed (non-fatal): ${err}`);\n }\n }\n\n /**\n * Drain a filesystem-fallback target's pending lifecycle-append spill when no\n * secure StorageManager is available (catalog-disabled namespace; issue\n * #2033). Uses the safe path-scoped PLAINTEXT context this fallback already\n * operates in: a keyless drain can only fold plaintext spills into a plaintext\n * ledger, so it refuses when the ledger or ANY spill is encrypted at rest,\n * leaving those pending for a keyed (catalog-path) drain — exactly mirroring\n * the fallback compaction's encrypted-ledger deferral. Non-fatal; a fast\n * no-op when nothing is pending. The shared spill lister enforces the symlink/\n * containment guard on every spill file it reads or deletes.\n */\n private async drainPendingLifecycleAppendsForPath(memoryDir: string): Promise<void> {\n const ledgerPath = path.join(memoryDir, \"state\", \"memory-lifecycle-ledger.jsonl\");\n const spillDir = pendingLifecycleLedgerDir(ledgerPath);\n // Enumerate spills through the SAME guarded lister the actual drain uses\n // (listContainedSpillFiles): it rejects a symlinked/non-directory spill dir\n // and skips any symlink/FIFO/device/escaping entry, returning only regular\n // files contained in the dir. Never raw-readdir + open() an untrusted name:\n // opening a FIFO would block until a writer appears and a symlink would\n // redirect the probe outside the dir (#2033). Enumerate BOTH live `*.jsonl`\n // spills and crash-orphaned `*.jsonl.claimed` files: a drain that died\n // between claiming a spill (rename to `.claimed`) and committing it leaves\n // the ONLY copy of those rows as a claimed orphan. Gating solely on live\n // spills would early-return here whenever the pending dir holds nothing but\n // orphans, stranding those rows forever; proceeding lets\n // drainPendingLifecycleLedgerIfAny -> recoverOrphanedClaims restore and\n // re-commit them under the ledger lock (#2033).\n let spillFiles: string[];\n let claimedOrphans: string[];\n try {\n spillFiles = await listContainedSpillFiles(spillDir);\n claimedOrphans = await listContainedSpillFiles(spillDir, \".jsonl.claimed\");\n } catch (err) {\n log.debug(`fallback lifecycle pending scan failed (non-fatal) for ${memoryDir}: ${err}`);\n return;\n }\n if (spillFiles.length === 0 && claimedOrphans.length === 0) return;\n // The pending encryption state is authoritative only UNDER the ledger lock:\n // drainPendingLifecycleLedgerIfAny waits for the lock and re-enumerates the\n // pending dir, so a secure-store writer that spills an encrypted file — or a\n // keyed compaction that rewrites the ledger encrypted — while this fallback\n // waits would defeat any pre-lock probe (#2033). Probe each file at the\n // moment of use inside the held lock instead: readSecure refuses an\n // encrypted spill (the fold reads each spill lazily and propagates the throw\n // BEFORE claiming that spill, leaving it intact) and the append refuses a\n // ledger that became encrypted (the fold rolls the claims back). Either way\n // the plaintext IO\n // never reads or writes ciphertext; the keyed catalog-path drain handles the\n // deferred rows.\n const io: LifecyclePendingIo = {\n writeSecure: async (filePath, payload) => {\n await mkdir(path.dirname(filePath), { recursive: true });\n await writeFile(filePath, payload, \"utf-8\");\n },\n readSecure: async (filePath) => {\n if (await probeEncryptedRegularFileHeader(filePath)) {\n throw new Error(`fallback lifecycle drain deferred: encrypted spill at ${filePath}`);\n }\n return readFile(filePath, \"utf-8\");\n },\n };\n try {\n await drainPendingLifecycleLedgerIfAny(\n ledgerPath,\n io,\n async (payload) => {\n if (await probeEncryptedRegularFileHeader(ledgerPath)) {\n throw new Error(`fallback lifecycle drain deferred: ledger became encrypted at ${ledgerPath}`);\n }\n await appendFile(ledgerPath, payload, \"utf-8\");\n },\n async () => {\n await mkdir(path.dirname(ledgerPath), { recursive: true });\n },\n );\n } catch (err) {\n log.debug(`fallback lifecycle pending drain failed (non-fatal) for ${memoryDir}: ${err}`);\n }\n }\n\n /**\n * Enumerate the lifecycle ledgers to consider for compaction: the root\n * (through the secure-configured root storage when available) plus every\n * cataloged namespace ledger when namespaces are enabled. Deduplicated by\n * resolved storage dir so a namespace that collapses onto the root is not\n * compacted twice. Namespace resolution failures are non-fatal.\n */\n private async resolveLifecycleCompactionTargets(): Promise<\n Array<{ memoryDir: string; storage?: StorageManager }>\n > {\n const targets: Array<{ memoryDir: string; storage?: StorageManager }> = [];\n const seen = new Set<string>();\n const addTarget = (memoryDir: string, storage?: StorageManager): void => {\n const key = path.resolve(memoryDir);\n if (seen.has(key)) return;\n seen.add(key);\n targets.push({ memoryDir, storage });\n };\n\n const rootStorage = this.deps.getStorage?.();\n addTarget(rootStorage ? rootStorage.dir : this.deps.config.memoryDir, rootStorage);\n\n const resolveNamespaceStorage = this.deps.storageForNamespace;\n if (resolveNamespaceStorage && this.deps.namespaceCatalog?.enabled) {\n let records: NamespaceRecord[] = [];\n try {\n records = await this.deps.namespaceCatalog.listNamespaces();\n } catch (err) {\n log.debug(`lifecycle compaction: namespace enumeration failed (non-fatal): ${err}`);\n }\n for (const record of records) {\n const namespace = record.namespace.trim();\n if (!namespace) continue;\n try {\n const storage = await resolveNamespaceStorage(namespace);\n addTarget(storage.dir, storage);\n } catch (err) {\n log.debug(\n `lifecycle compaction: storage resolve failed for namespace ${namespace} (non-fatal): ${err}`,\n );\n }\n }\n }\n\n // Filesystem fallback (codex P2): when namespaces are enabled but the\n // catalog is disabled (namespaceCatalogEnabled=false) — or a namespace is\n // not yet cataloged — the catalog walk above finds nothing, yet per-namespace\n // ledgers still grow under <memoryDir>/namespaces/<token>/state/. Scan that\n // base directly so those ledgers are still bounded. Deduped by resolved dir,\n // so a namespace already added via the catalog is not compacted twice.\n if (resolveNamespaceCapabilities(this.deps.config).namespaces) {\n const namespacesBase = path.join(this.deps.config.memoryDir, \"namespaces\");\n // Symlink/traversal containment (issue #2033 codex P2): a symlinked\n // <memoryDir>/namespaces (or a symlinked child) must not redirect the scan\n // — and later backup/ledger rewrites — outside memoryDir. Resolve the\n // memory root and the scan base through realpath, reject a symlinked or\n // escaping base, and skip any symlinked/escaping child, mirroring the\n // memory-store walkers' hardening (utils/path-containment).\n let memoryDirReal: string;\n try {\n memoryDirReal = await realpath(this.deps.config.memoryDir);\n const baseStat = await lstat(namespacesBase);\n if (baseStat.isSymbolicLink() || !baseStat.isDirectory()) {\n throw new Error(\"namespaces base is a symlink or not a directory\");\n }\n assertPathInsideRoot(memoryDirReal, await realpath(namespacesBase), namespacesBase);\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) {\n log.debug(`lifecycle compaction: namespaces base rejected (non-fatal): ${err}`);\n }\n return targets;\n }\n let entries: Dirent[] = [];\n try {\n entries = await readdir(namespacesBase, { withFileTypes: true });\n } catch (err) {\n if (!isErrnoCode(err, \"ENOENT\")) {\n log.debug(`lifecycle compaction: namespaces dir scan failed (non-fatal): ${err}`);\n }\n }\n for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {\n if (!entry.isDirectory() || entry.isSymbolicLink()) continue;\n const childPath = path.join(namespacesBase, entry.name);\n try {\n assertPathInsideRoot(memoryDirReal, await realpath(childPath), childPath);\n } catch (err) {\n log.debug(`lifecycle compaction: skipping out-of-root namespace dir ${childPath}: ${err}`);\n continue;\n }\n // Resolve the namespace's secure-store-aware storage so an encrypted\n // fallback ledger compacts through the correct root key/context instead\n // of deferring forever behind a keyless plaintext StorageManager (#2033\n // thread PRRT_kwDORJXyws6SE5fv). The on-disk dir name is EITHER a\n // namespace identity TOKEN (ns-<hex>) OR a legacy raw namespace dir name\n // written before tokenization (namespaces/<name>/). Decode a token back\n // to its canonical namespace; treat a non-token dir as a raw namespace\n // only when it is a safe route namespace (isSafeRouteNamespace), so a\n // garbage/traversal dir name is never handed to the resolver. Passing the\n // raw token to the resolver would re-tokenize an already-tokenized value\n // and (for namespaces longer than 30 bytes) exceed the router's 64-char\n // safe-route limit, so the resolver throws and the encrypted over-cap\n // ledger defers forever — hence decode first. Resolve the route namespace\n // and accept the keyed storage only when it roots at THIS dir: the secure\n // store keys one master key per memory ROOT (not per namespace), the\n // router routes a token to namespaces/<token>/ and an existing legacy raw\n // name to its namespaces/<name>/ root, so a correct resolve returns THIS\n // childPath; anything else is an unexpected re-route we refuse (keeping\n // namespace isolation and never compacting the wrong ledger). A dir that\n // neither decodes nor is a safe raw namespace, or no resolver wired\n // (focused tests / plaintext deployments), leaves the keyless target\n // standing: the rebuild chokepoint still refuses to downgrade an\n // encrypted ledger, so plaintext behavior holds.\n const decodedNamespace = namespaceIdentityFromToken(entry.name);\n const routeNamespace =\n decodedNamespace !== null\n ? decodedNamespace\n : isSafeRouteNamespace(entry.name)\n ? entry.name\n : null;\n if (resolveNamespaceStorage && routeNamespace !== null) {\n try {\n const storage = await resolveNamespaceStorage(routeNamespace);\n if (path.resolve(storage.dir) === path.resolve(childPath)) {\n addTarget(storage.dir, storage);\n continue;\n }\n log.debug(\n `lifecycle compaction: fallback resolver routed ${childPath} to ${storage.dir}; `\n + \"using keyless target\",\n );\n } catch (err) {\n log.debug(\n `lifecycle compaction: fallback storage resolve failed for ${childPath} (non-fatal): ${err}`,\n );\n }\n }\n addTarget(childPath);\n }\n }\n return targets;\n }\n\n /**\n * Compact one lifecycle ledger when it is at/over `threshold`. Returns\n * `\"skipped\"` (absent or below threshold — nothing to do), `\"compacted\"`\n * (rewritten and verified under the read/decrypt cap), `\"failed\"`\n * (rebuild/probe threw, or the rewritten ledger is STILL over the cap — an\n * ineffective compaction — non-fatal, retried next pass), or `\"deferred\"`\n * (oversized but its encrypted ledger cannot be rewritten because no unlocked\n * secure store is available — real work still pending, so the caller must NOT\n * arm the throttle). #2033: a deferred/failed target is distinct from a genuine\n * no-op so one namespace compacting cannot suppress retries for another, and\n * an over-cap rewrite never arms the throttle.\n */\n private async compactLifecycleLedgerTarget(\n target: { memoryDir: string; storage?: StorageManager },\n threshold: number,\n ): Promise<\"skipped\" | \"compacted\" | \"failed\" | \"deferred\"> {\n const ledgerPath = path.join(target.memoryDir, \"state\", \"memory-lifecycle-ledger.jsonl\");\n let size = 0;\n try {\n size = (await stat(ledgerPath)).size;\n } catch (err) {\n if (isErrnoCode(err, \"ENOENT\")) return \"skipped\"; // absent — nothing to compact yet.\n log.warn(\n `lifecycle ledger size check failed (non-fatal) for ${target.memoryDir}: ${err}`,\n );\n return \"failed\";\n }\n // #2033 finding (1): an encrypted ledger becomes unreadable once its on-disk\n // size reaches the whole-file decrypt cap (the reader refuses at/over it), so\n // the effective compaction trigger for an encrypted target is clamped to that\n // cap even when the configured threshold is LARGER — otherwise a ledger that\n // grows past the cap but stays below a bigger threshold would never compact\n // and stay permanently unreadable. Plaintext streaming is unaffected at any\n // size, so a plaintext target keeps the configured threshold. The cheap\n // pre-check skips anything below the smaller of the two before paying for the\n // extra lstat/open the encryption probe costs.\n if (size < Math.min(threshold, this.lifecycleLedgerMaxBytes)) return \"skipped\";\n let encrypted: boolean;\n try {\n encrypted = await probeEncryptedRegularFileHeader(ledgerPath);\n } catch (err) {\n log.warn(`lifecycle ledger encryption probe failed (non-fatal) for ${ledgerPath}: ${err}`);\n return \"failed\";\n }\n const effectiveThreshold = encrypted\n ? Math.min(threshold, this.lifecycleLedgerMaxBytes)\n : threshold;\n if (size < effectiveThreshold) return \"skipped\";\n // No plaintext rewrite of an encrypted-at-rest ledger (#2033): rebuilding an\n // encrypted ledger without an unlocked secure StorageManager would either\n // fail the preserve-read (locked store) or downgrade the ledger to plaintext.\n // Report \"deferred\" (NOT \"skipped\"): the ledger is over threshold and still\n // needs compaction, so the throttle must stay un-armed and retry once a key\n // is available — a \"skipped\" here would let another target's success arm the\n // throttle and suppress this pending work for the whole interval (#2033).\n if (encrypted && !(target.storage?.isSecureStoreUnlocked() ?? false)) {\n log.warn(\n `lifecycle ledger at ${ledgerPath} is encrypted at rest but no unlocked secure `\n + `storage is available; deferring auto-compaction to avoid a plaintext rewrite. `\n + `Run 'remnic rebuild-memory-lifecycle-ledger --write' after unlocking.`,\n );\n return \"deferred\";\n }\n // #2033 findings (2)+(4): the rewrite budget bounds the PLAINTEXT payload, but\n // an encrypted-at-rest target adds a fixed secure-store envelope on disk.\n // Reserve that envelope (plus one byte) from the plaintext budget so the\n // encrypted file lands STRICTLY below the reader's refusal cap and the\n // post-write check — which stats the on-disk (encrypted) size against the cap\n // — cannot fail forever on a plaintext budget that equals the cap (the\n // endless-retry bug). Base this on whether the REPLACEMENT will be encrypted\n // (the storage write mode), NOT only on the current file header: a\n // plaintext ledger rewritten under `secureStoreEncryptOnWrite` with the key\n // set becomes encrypted, so it needs the reserve even though its current\n // header is plaintext (#2033 write-mode finding).\n const replacementEncrypted =\n encrypted || (target.storage?.willEncryptStateWrites() ?? false);\n const plaintextBudget = replacementEncrypted\n ? this.lifecycleLedgerMaxBytes - SECURE_STORE_ENVELOPE_OVERHEAD_BYTES - 1\n : this.lifecycleLedgerMaxBytes - 1;\n try {\n const result = await rebuildMemoryLifecycleLedger({\n memoryDir: target.memoryDir,\n dryRun: false,\n storage: target.storage,\n // Background compaction must not lose append-only history that\n // frontmatter cannot reconstruct (issue #1910); the manual CLI repair\n // rebuild now preserves and bounds identically (#2033).\n preserveExistingEvents: true,\n // Bound the PLAINTEXT rewrite to a budget that reserves the secure-store\n // envelope, so the on-disk ledger (plaintext, or plaintext+envelope when\n // encrypted) is strictly below the read/decrypt cap; overflow lands in\n // the verbatim backup (#2033 findings 2+4).\n maxLedgerBytes: plaintextBudget,\n });\n // Verify the rewritten ledger is actually bounded. A compaction that left\n // the ledger at/over the read/decrypt cap is ineffective (the ledger would\n // be unreadable) — report \"failed\" so the throttle stays un-armed and the\n // next pass retries rather than declaring success (#2033).\n let rewrittenSize = 0;\n try {\n rewrittenSize = (await stat(ledgerPath)).size;\n } catch (err) {\n log.warn(`lifecycle ledger post-compaction size check failed (non-fatal) for ${target.memoryDir}: ${err}`);\n return \"failed\";\n }\n if (rewrittenSize >= this.lifecycleLedgerMaxBytes) {\n log.warn(\n `lifecycle ledger auto-compaction ineffective for ${target.memoryDir}: rewritten `\n + `ledger is ${rewrittenSize}B, still at/over the ${this.lifecycleLedgerMaxBytes}-byte `\n + `read/decrypt cap; leaving the throttle un-armed to retry.`,\n );\n return \"failed\";\n }\n if (result.rewritten === false) {\n // No-op pass (#2033 thread PRRT_kwDORJXyws6SExst): a preserving rebuild\n // would reproduce the current ledger byte-for-byte, so it skipped the\n // backup+rewrite. The ledger stays over the trigger but under the cap;\n // report \"compacted\" so the throttle still arms (nothing more can be\n // done until the ledger grows), which stops the periodic re-archive/\n // rewrite churn the review flagged — the archive no longer grows.\n log.debug(\n `lifecycle ledger already compact for ${target.memoryDir}: ${rewrittenSize}B, `\n + `no rewrite needed (throttle armed to avoid re-archiving an unchanged ledger).`,\n );\n } else {\n log.info(\n `lifecycle ledger auto-compacted (${target.memoryDir}): ${size}B -> `\n + `${rewrittenSize}B, ${result.rebuiltRows} rows `\n + `(${result.preservedAppendOnlyRows ?? 0} append-only preserved, `\n + `${result.archivedOverflowRows ?? 0} overflow archived), `\n + `backup=${result.backupPath ?? \"none\"}`,\n );\n }\n return \"compacted\";\n } catch (err) {\n log.warn(\n `lifecycle ledger auto-compaction failed (non-fatal) for ${target.memoryDir}: ${err}`,\n );\n return \"failed\";\n }\n }\n\n /** Build the namespace-aware maintenance plan for one job. */\n private async namespaceMaintenancePlan(\n jobName: string,\n ): Promise<NamespaceMaintenancePlan> {\n return planNamespaceMaintenance(this.deps.config, {\n jobName,\n catalog: this.deps.namespaceCatalog,\n });\n }\n\n // ───────────────────────────────────────────────────────────────────────\n // Lifecycle\n // ───────────────────────────────────────────────────────────────────────\n\n /**\n * Clear any pending debounce timer and drain the activity sync scheduler.\n * Called from the orchestrator's destroy() so a late tick does not fire on a\n * torn-down instance and no in-flight activity tick keeps writing after\n * teardown. Async: awaits the scheduler's abort+drain before resolving.\n */\n async dispose(): Promise<void> {\n this.disposed = true;\n const consolidationDrain = this.consolidationRunPromise;\n // The registrar latches teardown first (so a draining tick's onRun cannot\n // re-arm QMD maintenance) and aborts+drains the sync. Capture its drain and\n // await it last so the rest of teardown runs while the aborted tick unwinds.\n const activityDrain = this.activityRegistrar.dispose();\n const locationDrain = this.locationAutoSync?.stop() ?? Promise.resolve();\n this.locationAutoSync = null;\n if (this.qmdMaintenanceTimer) {\n clearTimeout(this.qmdMaintenanceTimer);\n this.qmdMaintenanceTimer = null;\n }\n this.qmdMaintenancePending = false;\n await activityDrain;\n await locationDrain;\n // Belt-and-suspenders: clear anything a late tick armed before the latch.\n if (this.qmdMaintenanceTimer) {\n clearTimeout(this.qmdMaintenanceTimer);\n this.qmdMaintenanceTimer = null;\n }\n this.qmdMaintenancePending = false;\n if (consolidationDrain) await consolidationDrain;\n }\n}\n","/**\n * Day-summary cron model resolution (extracted from\n * orchestration/maintenance.ts in #2047 so that capped file keeps only\n * scheduling wiring). Pure function of config — no I/O.\n */\n\nimport type { PluginConfig } from \"../types.js\";\n\n/**\n * Resolve the cron-routing model + fallback chain. Only in gateway mode is\n * summaryModel routable as an OpenClaw agentTurn model; in plugin mode it is\n * a direct-client model id for Remnic's own LLM calls. Task-chain fallbacks\n * attach only when the model matches the task-chain primary (a distinct\n * override's fallbacks would be unrelated); gateway default models are\n * appended as tail fallbacks (de-duped) so a task-chain outage doesn't stop\n * the cron before reaching the gateway default chain.\n */\nexport function resolveDaySummaryCronModel(config: PluginConfig): {\n model?: string;\n fallbacks: string[];\n} {\n const rawSummaryModel = config.summaryModel;\n const taskPrimary = config.taskModelChain?.primary;\n const isGateway = config.modelSource === \"gateway\";\n const model = isGateway ? rawSummaryModel || taskPrimary || undefined : undefined;\n const fallbacks: string[] = [];\n if (model && taskPrimary && model === taskPrimary) {\n const seen = new Set<string>(model ? [model] : []);\n const addUnique = (value: string | undefined) => {\n if (typeof value !== \"string\") return;\n const trimmed = value.trim();\n if (trimmed.length > 0 && !seen.has(trimmed)) {\n seen.add(trimmed);\n fallbacks.push(trimmed);\n }\n };\n for (const fb of config.taskModelChain?.fallbacks ?? []) addUnique(fb);\n const gwDefaults = config.gatewayConfig?.agents?.defaults?.model;\n addUnique(gwDefaults?.primary);\n if (Array.isArray(gwDefaults?.fallbacks)) {\n for (const fb of gwDefaults.fallbacks) addUnique(fb);\n }\n }\n return { ...(model ? { model } : {}), fallbacks };\n}\n","/**\n * Scheduled rebuild of the root memory projection (issue #2119).\n *\n * The projection (`state/memory-projection.sqlite`) is otherwise rebuild-only\n * (manual CLI / tests), so it goes stale as the lifecycle ledger grows and\n * timeline/browse consumers silently fall back to full-corpus scans. The\n * MaintenanceScheduler fires this alongside ledger compaction on every\n * maintenance request — interval-throttled, single-flighted, never awaited,\n * never throws.\n *\n * `projectionRebuildEnabled` gates it (via the lifecycle capabilities\n * resolver, default true); `projectionRebuildIntervalMs` is the cadence.\n *\n * Skipped when the projection was rebuilt within the interval. The freshness\n * check reads the on-disk `rebuiltAt` meta (not just an in-process timestamp)\n * so a daemon restart or an operator's cron `remnic rebuild-memory-projection`\n * both suppress a redundant rebuild. On a real rebuild it logs the lag it just\n * cured (age of the projection before the rebuild) plus the fresh row counts,\n * so operators can see the projection was drifting. Failures are non-fatal\n * and leave the throttle un-advanced to retry.\n *\n * Scope: the root memoryDir projection — the one `getMemoryTimeline` /\n * browse / current-state serve from `baseDir`. Per-namespace projections are\n * not yet scheduled here (they are still served by their own rebuild paths);\n * the fallback WARN surfaces staleness for any namespace that lags.\n */\nimport { log } from \"../logger.js\";\nimport type { PluginConfig } from \"../types.js\";\nimport { resolveMemoryLifecycleCapabilities } from \"../capabilities.js\";\nimport {\n rebuildMemoryProjection,\n type RebuildMemoryProjectionOptions,\n} from \"../maintenance/rebuild-memory-projection.js\";\nimport { readProjectionRebuiltAt } from \"../maintenance/projection-support.js\";\n\ntype InjectedStorage = NonNullable<RebuildMemoryProjectionOptions[\"storage\"]>;\n\nexport interface ProjectionRebuildScheduleState {\n inFlight: boolean;\n lastRebuildAtMs: number;\n /** Timestamp of the most recent FAILURE; gates a short retry backoff that is\n * separate from the success interval so a transient cause (e.g. a locked\n * secure store) retries promptly once cleared instead of waiting a full\n * interval. */\n lastFailureAtMs: number;\n}\n\n/** Bounded retry cadence after a failure — short enough to self-heal promptly\n * once a transient cause clears, long enough to keep a persistently-failing\n * rebuild off every maintenance tick. */\nconst FAILURE_RETRY_BACKOFF_MS = 5 * 60 * 1000;\n\nexport function createProjectionRebuildScheduleState(): ProjectionRebuildScheduleState {\n return { inFlight: false, lastRebuildAtMs: 0, lastFailureAtMs: 0 };\n}\n\nexport async function maybeRebuildMemoryProjectionScheduled(opts: {\n config: PluginConfig;\n /** The daemon's live root storage, when available (secure-store unlock). */\n getStorage?: () => InjectedStorage | undefined;\n state: ProjectionRebuildScheduleState;\n}): Promise<void> {\n const { config, state } = opts;\n if (!resolveMemoryLifecycleCapabilities(config).projectionRebuild) return;\n if (state.inFlight) return;\n const intervalMs = config.projectionRebuildIntervalMs;\n const now = Date.now();\n if (now - state.lastRebuildAtMs < intervalMs) return;\n if (now - state.lastFailureAtMs < FAILURE_RETRY_BACKOFF_MS) return;\n\n const rootStorage = opts.getStorage?.();\n const memoryDir = rootStorage ? rootStorage.dir : config.memoryDir;\n\n // On-disk freshness check: survives restarts and cross-process CLI rebuilds.\n const rebuiltAt = readProjectionRebuiltAt(memoryDir);\n const rebuiltAtMs = rebuiltAt ? Date.parse(rebuiltAt) : Number.NaN;\n if (Number.isFinite(rebuiltAtMs) && now - rebuiltAtMs < intervalMs) {\n // Fresh already — adopt its timestamp so the next pass short-circuits on\n // the cheap in-process guard without re-opening the projection each time.\n state.lastRebuildAtMs = rebuiltAtMs;\n return;\n }\n\n state.inFlight = true;\n try {\n const result = await rebuildMemoryProjection({\n memoryDir,\n // Reuse the daemon's live storage so secure-store deployments rebuild\n // through the unlocked instance instead of a fresh locked one.\n ...(rootStorage ? { storage: rootStorage } : {}),\n defaultNamespace: config.defaultNamespace,\n dryRun: false,\n now: new Date(now),\n });\n state.lastRebuildAtMs = Date.now();\n const lag = Number.isFinite(rebuiltAtMs)\n ? `${Math.max(0, Math.round((now - rebuiltAtMs) / 60_000))}m stale`\n : \"never previously built\";\n log.info(\n `memory projection rebuilt (scheduled) for ${memoryDir}: was ${lag}; `\n + `${result.currentRows} current, ${result.timelineRows} timeline, `\n + `${result.entityMentionRows} entity-mention rows`,\n );\n } catch (err) {\n // Non-fatal. Arm a SHORT backoff (not the full success interval) so a\n // transient cause (locked secure store, momentarily-unavailable storage)\n // retries promptly once it clears, while a persistently-failing rebuild\n // still stays off every maintenance tick. lastRebuildAtMs is left as-is so\n // the success interval is not falsely advanced.\n state.lastFailureAtMs = Date.now();\n log.warn(`memory projection scheduled rebuild failed (non-fatal) for ${memoryDir}: ${err}`);\n } finally {\n state.inFlight = false;\n }\n}\n","/**\n * Forced, non-fake search-index refresh for direct activity digest writes\n * (issue #1899; AGENTS.md rule 31). The digest is written straight to the QMD\n * collection root, bypassing the extraction->persist->index pipeline, so it must\n * explicitly trigger a refresh — and that refresh must actually run.\n *\n * Ordinary `SearchBackend.update()` is fail-open: it silently returns when the\n * backend is unavailable, in failure backoff, or inside its min-interval gate.\n * A freshly written digest hit by that gate would stay unsearchable until an\n * unrelated reindex, and the per-day digest-unchanged guard means the next tick\n * won't retry. So prefer the forced/strict contracts: `updateCollectionStrict`\n * bypasses the min-interval gate (force) AND throws on a genuine failure instead\n * of reporting a fake success, then `updateStrict`, then plain `update`.\n */\n\nimport type { SearchExecutionOptions } from \"../search/port.js\";\n\n/** The subset of the search backend the activity reindex needs. */\nexport interface ActivityIndexRefresher {\n update(execution?: SearchExecutionOptions): Promise<void>;\n updateStrict?(execution?: SearchExecutionOptions): Promise<void>;\n updateCollectionStrict?(collection: string, execution?: SearchExecutionOptions): Promise<void>;\n}\n\nexport async function refreshActivityIndex(\n qmd: ActivityIndexRefresher,\n collection: string,\n signal?: AbortSignal,\n): Promise<void> {\n const execution = signal === undefined ? undefined : { signal };\n if (typeof qmd.updateCollectionStrict === \"function\" && collection.trim().length > 0) {\n await qmd.updateCollectionStrict(collection, execution);\n return;\n }\n if (typeof qmd.updateStrict === \"function\") {\n await qmd.updateStrict(execution);\n return;\n }\n await qmd.update(execution);\n}\n","/**\n * Screen-activity SQLite store (issue #1899).\n *\n * Durable, capture-machine-agnostic store for on-screen text snapshots plus a\n * per-machine sync cursor. Mirrors the LCM store conventions\n * (packages/remnic-core/src/lcm/schema.ts): WAL, a `<name>_meta` schema-version\n * row, `CREATE TABLE IF NOT EXISTS`, and an FTS5 virtual table created\n * separately. better-sqlite3 is synchronous.\n *\n * Snapshots dedup on `(machine, content_hash)` so a re-sync is idempotent, and\n * day queries use half-open [start, end) UTC bounds (AGENTS.md §23).\n */\n\nimport { mkdirSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { openBetterSqlite3, type BetterSqlite3Database } from \"../runtime/better-sqlite.js\";\nimport type { ActivitySnapshot } from \"./types.js\";\n\nconst ACTIVITY_SCHEMA_VERSION = 1;\nconst MAX_SEARCH_RESULTS = 100;\n\nexport function activityDatabasePath(memoryDir: string): string {\n return path.join(memoryDir, \"state\", \"activity.sqlite\");\n}\n\nexport async function ensureActivityStateDir(memoryDir: string): Promise<void> {\n await mkdir(path.join(memoryDir, \"state\"), { recursive: true });\n}\n\nexport function openActivityDatabase(memoryDir: string): BetterSqlite3Database {\n // Create the state/ dir synchronously first: better-sqlite3 can't open a file\n // in a missing directory, and open() is the sync public entry point (callers\n // don't await ensureActivityStateDir). Idempotent.\n mkdirSync(path.join(memoryDir, \"state\"), { recursive: true });\n const db = openBetterSqlite3(activityDatabasePath(memoryDir));\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"busy_timeout = 5000\");\n db.pragma(\"synchronous = NORMAL\");\n applySchema(db);\n return db;\n}\n\n/** Apply the activity schema on an already-open handle (test/in-memory use). */\nexport function applyActivitySchema(db: BetterSqlite3Database): void {\n applySchema(db);\n}\n\nfunction applySchema(db: BetterSqlite3Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS activity_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS activity_snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n machine TEXT NOT NULL,\n captured_at_utc TEXT NOT NULL,\n app_name TEXT NOT NULL,\n window_title TEXT NOT NULL,\n browser_url TEXT,\n text TEXT NOT NULL,\n text_source TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n simhash TEXT\n );\n CREATE UNIQUE INDEX IF NOT EXISTS idx_activity_snapshots_dedup\n ON activity_snapshots(machine, captured_at_utc, content_hash);\n CREATE INDEX IF NOT EXISTS idx_activity_snapshots_time\n ON activity_snapshots(captured_at_utc, id);\n\n CREATE TABLE IF NOT EXISTS activity_sync_state (\n machine TEXT PRIMARY KEY,\n cursor TEXT,\n updated_at_utc TEXT NOT NULL\n );\n `);\n\n const hasFts = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='activity_snapshots_fts'\")\n .get();\n if (!hasFts) {\n // Standalone FTS5 (not external-content): populated explicitly on insert so\n // it never drifts from the base table.\n db.exec(`\n CREATE VIRTUAL TABLE activity_snapshots_fts USING fts5(\n text, app_name, window_title, browser_url\n );\n `);\n }\n\n db.prepare(\"INSERT OR REPLACE INTO activity_meta (key, value) VALUES ('schema_version', ?)\").run(\n String(ACTIVITY_SCHEMA_VERSION),\n );\n}\n\n/**\n * Canonicalize a UTC timestamp to `YYYY-MM-DDTHH:MM:SS.sssZ` so day-window\n * range filtering (a TEXT comparison against activityDayWindow bounds) is valid\n * regardless of the input ISO form (trailing `Z` vs `+00:00`, missing millis).\n * A non-parseable value is stored verbatim (an outlier the day filter skips).\n */\nfunction canonicalizeUtc(iso: string): string {\n const parsed = Date.parse(iso);\n return Number.isFinite(parsed) ? new Date(parsed).toISOString() : iso;\n}\n\nfunction assertRequiredSnapshotFields(s: ActivitySnapshot): void {\n const required: ReadonlyArray<readonly [string, unknown]> = [\n [\"machine\", s.machine],\n [\"app\", s.app],\n [\"windowTitle\", s.windowTitle],\n [\"text\", s.text],\n [\"textSource\", s.textSource],\n [\"contentHash\", s.contentHash],\n ];\n for (const [field, value] of required) {\n if (typeof value !== \"string\") {\n // INSERT OR IGNORE would otherwise swallow a NOT NULL failure as if it\n // were a dedup conflict, silently dropping a malformed capture.\n throw new RangeError(`activity: snapshot field \"${field}\" is required (got ${value === null ? \"null\" : typeof value}).`);\n }\n }\n if (s.machine.length === 0 || s.contentHash.length === 0) {\n throw new RangeError('activity: snapshot \"machine\" and \"contentHash\" must be non-empty.');\n }\n if (s.textSource !== \"ax\" && s.textSource !== \"ocr\") {\n // rowToSnapshot maps any non-\"ocr\" value to \"ax\"; reject unknown sources at\n // the boundary instead of silently coercing them on read.\n throw new RangeError(`activity: snapshot \"textSource\" must be \"ax\" or \"ocr\" (got \"${s.textSource}\").`);\n }\n}\n\n/**\n * Reject an impossible capture timestamp instead of letting Date.parse silently\n * roll it over (e.g. 2026-02-30 → Mar 2, in either `Z` or explicit-offset form),\n * which would file the snapshot under the wrong day. Validates the wall-clock\n * calendar fields in the string directly, independent of the zone designator.\n */\nfunction assertValidUtcInstant(iso: string, what: string): void {\n const m = iso.match(\n /^(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.\\d+)?(?:Z|[+-](?:0\\d:[0-5]\\d|1[0-3]:[0-5]\\d|14:00))$/,\n );\n if (m === null || !Number.isFinite(Date.parse(iso))) {\n throw new RangeError(`activity: invalid ${what} \"${iso}\".`);\n }\n const [year, month, day, hour, minute, second] = m.slice(1).map(Number);\n const daysInMonth = month >= 1 && month <= 12 ? new Date(Date.UTC(year, month, 0)).getUTCDate() : 0;\n if (day < 1 || day > daysInMonth || hour > 23 || minute > 59 || second > 59) {\n throw new RangeError(`activity: ${what} \"${iso}\" is not a real calendar instant.`);\n }\n}\n\n/** Narrow a sqlite row object so field reads are checked, not asserted. */\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction str(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction optStr(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\n/**\n * Build a safe FTS5 MATCH expression from free text: extract alphanumeric\n * tokens and quote each as a phrase, so punctuation common in captured\n * activity (URLs like github.com/x/pull/412, quotes, bare boolean operators)\n * can never be parsed as FTS5 syntax and make SQLite throw. Tokens are AND-ed\n * (implicit). Returns null when there is nothing to match.\n */\nfunction ftsMatchFor(query: string): string | null {\n if (typeof query !== \"string\") return null;\n const tokens = query.match(/[\\p{L}\\p{N}_]+/gu);\n if (!tokens || tokens.length === 0) return null;\n return tokens.map((token) => `\"${token}\"`).join(\" \");\n}\n\nfunction rowToSnapshot(row: unknown): ActivitySnapshot {\n if (!isRecord(row)) {\n throw new Error(\"activity store: unexpected non-object row\");\n }\n const textSource = row.text_source === \"ocr\" ? \"ocr\" : \"ax\";\n return {\n id: typeof row.id === \"number\" ? row.id : undefined,\n machine: str(row.machine),\n capturedAtUtc: str(row.captured_at_utc),\n app: str(row.app_name),\n windowTitle: str(row.window_title),\n ...(optStr(row.browser_url) !== undefined ? { browserUrl: optStr(row.browser_url) } : {}),\n text: str(row.text),\n textSource,\n contentHash: str(row.content_hash),\n ...(optStr(row.simhash) !== undefined ? { simhash: optStr(row.simhash) } : {}),\n };\n}\n\nexport class ActivityStore {\n private readonly db: BetterSqlite3Database;\n\n constructor(db: BetterSqlite3Database) {\n this.db = db;\n }\n\n static open(memoryDir: string): ActivityStore {\n return new ActivityStore(openActivityDatabase(memoryDir));\n }\n\n /**\n * Insert a snapshot, idempotent on (machine, captured_at_utc, content_hash):\n * the same screen content recurring at a *different* time is kept; only an\n * exact re-ingestion of the same capture dedups. Returns `inserted: false`\n * for a duplicate. The FTS row is written only on a real insert (atomically),\n * so it never drifts from the base table.\n */\n insertSnapshot(snapshot: ActivitySnapshot): { inserted: boolean; id: number } {\n // Base row + FTS row must be atomic. Without a transaction, a crash (or an\n // FTS throw) between the two statements leaves a snapshot in the base table\n // with no matching FTS row — silently unsearchable. The transaction rolls\n // back both on any throw.\n const runInsert = this.db.transaction((s: ActivitySnapshot): { inserted: boolean; id: number } => {\n assertValidUtcInstant(s.capturedAtUtc, \"capture timestamp\");\n assertRequiredSnapshotFields(s);\n const capturedAtUtc = canonicalizeUtc(s.capturedAtUtc);\n const info = this.db\n .prepare(\n `INSERT OR IGNORE INTO activity_snapshots\n (machine, captured_at_utc, app_name, window_title, browser_url, text, text_source, content_hash, simhash)\n VALUES (@machine, @captured_at_utc, @app_name, @window_title, @browser_url, @text, @text_source, @content_hash, @simhash)`,\n )\n .run({\n machine: s.machine,\n captured_at_utc: capturedAtUtc,\n app_name: s.app,\n window_title: s.windowTitle,\n browser_url: s.browserUrl ?? null,\n text: s.text,\n text_source: s.textSource,\n content_hash: s.contentHash,\n simhash: s.simhash ?? null,\n });\n if (info.changes === 0) {\n const existing = this.db\n .prepare(\"SELECT id FROM activity_snapshots WHERE machine = ? AND captured_at_utc = ? AND content_hash = ?\")\n .get(s.machine, capturedAtUtc, s.contentHash);\n const id = isRecord(existing) && typeof existing.id === \"number\" ? existing.id : -1;\n return { inserted: false, id };\n }\n const id = Number(info.lastInsertRowid);\n this.db\n .prepare(\n `INSERT INTO activity_snapshots_fts (rowid, text, app_name, window_title, browser_url)\n VALUES (?, ?, ?, ?, ?)`,\n )\n .run(id, s.text, s.app, s.windowTitle, s.browserUrl ?? \"\");\n return { inserted: true, id };\n });\n return runInsert(snapshot);\n }\n\n /** Snapshots whose capture instant is in the half-open [start, end) window. */\n listSnapshotsForDay(\n machine: string | null,\n startUtcInclusive: string,\n endUtcExclusive: string,\n ): ActivitySnapshot[] {\n assertValidUtcInstant(startUtcInclusive, \"range start\");\n assertValidUtcInstant(endUtcExclusive, \"range end\");\n const start = canonicalizeUtc(startUtcInclusive);\n const end = canonicalizeUtc(endUtcExclusive);\n const rows =\n machine === null\n ? this.db\n .prepare(\n `SELECT * FROM activity_snapshots\n WHERE captured_at_utc >= ? AND captured_at_utc < ?\n ORDER BY captured_at_utc ASC, id ASC`,\n )\n .all(start, end)\n : this.db\n .prepare(\n `SELECT * FROM activity_snapshots\n WHERE machine = ? AND captured_at_utc >= ? AND captured_at_utc < ?\n ORDER BY captured_at_utc ASC, id ASC`,\n )\n .all(machine, start, end);\n return rows.map(rowToSnapshot);\n }\n\n getCursor(machine: string): string | null {\n const row = this.db.prepare(\"SELECT cursor FROM activity_sync_state WHERE machine = ?\").get(machine);\n return isRecord(row) && typeof row.cursor === \"string\" ? row.cursor : null;\n }\n\n setCursor(machine: string, cursor: string | null, updatedAtUtc: string = new Date().toISOString()): void {\n this.db\n .prepare(\n `INSERT INTO activity_sync_state (machine, cursor, updated_at_utc)\n VALUES (?, ?, ?)\n ON CONFLICT(machine) DO UPDATE SET cursor = excluded.cursor, updated_at_utc = excluded.updated_at_utc`,\n )\n .run(machine, cursor, updatedAtUtc);\n }\n\n /** Full-text search over snapshot text/app/window/url; newest first. */\n searchSnapshots(query: string, limit: number): ActivitySnapshot[] {\n const capped = Number.isInteger(limit) && limit > 0 ? Math.min(limit, MAX_SEARCH_RESULTS) : 20;\n const match = ftsMatchFor(query);\n if (match === null) return [];\n const rows = this.db\n .prepare(\n `SELECT s.* FROM activity_snapshots_fts f\n JOIN activity_snapshots s ON s.id = f.rowid\n WHERE activity_snapshots_fts MATCH ?\n ORDER BY s.captured_at_utc DESC, s.id DESC\n LIMIT ?`,\n )\n .all(match, capped);\n // ftsMatchFor sanitizes the query to quoted alphanumeric phrases, so there\n // is no path to an FTS5 syntax error; a real backend failure (closed handle,\n // missing/corrupt table, disk I/O) propagates rather than reading as empty.\n return rows.map(rowToSnapshot);\n }\n\n /** Retention: drop snapshots captured strictly before `cutoffUtc`. */\n pruneOlderThan(cutoffUtc: string): number {\n assertValidUtcInstant(cutoffUtc, \"prune cutoff\");\n const ids = this.db\n .prepare(\"SELECT id FROM activity_snapshots WHERE captured_at_utc < ?\")\n .all(canonicalizeUtc(cutoffUtc))\n .map((row: unknown) => (isRecord(row) && typeof row.id === \"number\" ? row.id : -1))\n .filter((id: number) => id >= 0);\n const deleteFts = this.db.prepare(\"DELETE FROM activity_snapshots_fts WHERE rowid = ?\");\n const deleteRow = this.db.prepare(\"DELETE FROM activity_snapshots WHERE id = ?\");\n const tx = this.db.transaction((rowIds: number[]) => {\n for (const id of rowIds) {\n deleteFts.run(id);\n deleteRow.run(id);\n }\n });\n tx(ids);\n return ids.length;\n }\n\n close(): void {\n this.db.close();\n }\n}\n","import { mkdir, readFile, rename, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\n\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\n\nimport {\n activityDayWindow,\n activityDigestPath,\n composeActivityDigestBody,\n composeActivityDigestMeta,\n isValidActivityDate,\n serializeActivityDigest,\n} from \"./digest.js\";\nimport type { ActivityStore } from \"./store.js\";\nimport type { ActivitySnapshot, ActivitySourceClient } from \"./types.js\";\n\nconst MAX_SYNC_PAGES = 10_000;\n\n/**\n * Sync cursors are scoped per (machine, local day): each date resumes its own\n * pagination independently, so a multi-day window never inherits an earlier\n * day's cursor (which could skip backfill). Encoded into the store's single\n * key column with a NUL separator that cannot occur in a machine label.\n */\nexport function activityCursorKey(machine: string, date: string): string {\n return `${machine}\\u0000${date}`;\n}\n\n// Serializes the list -> compose -> write digest section per (memoryDir, date)\n// so two sources syncing the same day cannot lose-update each other's digest:\n// the last writer in the chain lists every row present and wins.\nconst digestLocks = new Map<string, Promise<unknown>>();\n\nfunction withDigestLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const prior = digestLocks.get(key) ?? Promise.resolve();\n const next = prior.then(fn, fn);\n const tail = next.then(\n () => undefined,\n () => undefined,\n );\n digestLocks.set(key, tail);\n void tail.then(() => {\n if (digestLocks.get(key) === tail) digestLocks.delete(key);\n });\n return next;\n}\n\nexport interface ActivitySyncOptions {\n date: string;\n timezone: string;\n memoryDir: string;\n store: ActivityStore;\n signal?: AbortSignal;\n /** Runaway-pagination guard; defaults to MAX_SYNC_PAGES. */\n maxPages?: number;\n /**\n * Search-index refresh, run after a digest is (re)written so the durable\n * markdown becomes discoverable (AGENTS.md rule 31; the digest lives in the\n * QMD collection root but bypasses the extraction->persist->index pipeline).\n * Host-agnostic: the caller injects the core index seam (SearchBackend.update).\n * Best-effort — a failure never fails the sync (rows/digest are durable and\n * index on the next update); it surfaces as `reindexError`.\n */\n afterWrites?: (signal?: AbortSignal) => Promise<void>;\n}\n\nexport interface ActivitySyncResult {\n machine: string;\n fetched: number;\n inserted: number;\n duplicates: number;\n cursor: string | null;\n digestWritten: boolean;\n /** Set when the post-write reindex threw; the sync still succeeded. */\n reindexError?: string;\n}\n\nfunction snapshotForMachine(snapshot: ActivitySnapshot, machine: string): ActivitySnapshot {\n return {\n machine,\n capturedAtUtc: snapshot.capturedAtUtc,\n app: snapshot.app,\n windowTitle: snapshot.windowTitle,\n ...(snapshot.browserUrl === undefined ? {} : { browserUrl: snapshot.browserUrl }),\n text: snapshot.text,\n textSource: snapshot.textSource,\n contentHash: snapshot.contentHash,\n ...(snapshot.simhash === undefined ? {} : { simhash: snapshot.simhash }),\n };\n}\n\nasync function writeDigestIfChanged(memoryDir: string, date: string, serialized: string): Promise<boolean> {\n const target = activityDigestPath(memoryDir, date);\n try {\n if ((await readFile(target, \"utf8\")) === serialized) return false;\n } catch (error: unknown) {\n if (!(error instanceof Error) || !(\"code\" in error) || error.code !== \"ENOENT\") throw error;\n }\n\n await mkdir(path.dirname(target), { recursive: true, mode: 0o700 });\n const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;\n await writeFile(temporary, serialized, { encoding: \"utf8\", mode: 0o600 });\n await rename(temporary, target);\n return true;\n}\n\n/**\n * Pull one source's local-day snapshots and persist its cursor only after every\n * page and the derived digest are durable. Replays after a partial failure are\n * safe because ActivityStore deduplicates an exact captured snapshot.\n */\nexport async function syncActivitySource(\n source: ActivitySourceClient,\n options: ActivitySyncOptions,\n): Promise<ActivitySyncResult> {\n if (!isValidActivityDate(options.date)) {\n throw new RangeError(`Invalid activity date \"${options.date}\"; expected a real YYYY-MM-DD day.`);\n }\n if (source.machineLabel.trim().length === 0) {\n throw new RangeError(\"activity source machine label must not be empty\");\n }\n const maxPages = options.maxPages ?? MAX_SYNC_PAGES;\n if (!Number.isInteger(maxPages) || maxPages < 1) {\n throw new RangeError(\"activity sync maxPages must be a positive integer\");\n }\n\n const cursorKey = activityCursorKey(source.machineLabel, options.date);\n let cursor = options.store.getCursor(cursorKey);\n let pages = 0;\n let fetched = 0;\n let inserted = 0;\n let duplicates = 0;\n const seenCursors = new Set<string>();\n let completed = false;\n\n while (pages < maxPages) {\n options.signal?.throwIfAborted();\n const page = await source.fetchSnapshots({\n date: options.date,\n timezone: options.timezone,\n cursor,\n signal: options.signal,\n });\n pages += 1;\n fetched += page.snapshots.length;\n\n for (const snapshot of page.snapshots) {\n // Persist every row keyed by its own timestamp: an out-of-window snapshot\n // (replay file, timezone-boundary bug) is durable under its own day and\n // never lost, while the day digest below is a window query so it never\n // pollutes options.date. A malformed instant is rejected loudly here.\n const result = options.store.insertSnapshot(snapshotForMachine(snapshot, source.machineLabel));\n if (result.inserted) inserted += 1;\n else duplicates += 1;\n }\n if (page.nextCursor === null) {\n completed = true;\n break;\n }\n if (typeof page.nextCursor !== \"string\" || page.nextCursor.length === 0) {\n throw new TypeError(\"activity source returned an invalid cursor\");\n }\n if (seenCursors.has(page.nextCursor)) {\n throw new Error(\"activity source returned a repeated cursor\");\n }\n seenCursors.add(page.nextCursor);\n cursor = page.nextCursor;\n }\n\n // Only a loop that ran out of its page budget without seeing a terminal\n // null cursor is a runaway; completing on the final allowed page is normal.\n if (!completed) {\n throw new Error(`activity source exceeded ${maxPages} pages`);\n }\n\n // Re-check abort before the durable write section: stop() may have fired after\n // the last page returned, or while this date waited behind digestLocks. An\n // aborted tick must not compose/write a digest or advance the cursor.\n options.signal?.throwIfAborted();\n const digestWritten = await withDigestLock(`${options.memoryDir}\\u0000${options.date}`, async () => {\n options.signal?.throwIfAborted();\n const { startUtc, endUtc } = activityDayWindow(options.date, options.timezone);\n const snapshots = options.store.listSnapshotsForDay(null, startUtc, endUtc);\n const body = composeActivityDigestBody(options.date, options.timezone, snapshots);\n const machines = [...new Set(snapshots.map((snapshot) => snapshot.machine))].sort((a, b) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n const serialized = serializeActivityDigest(composeActivityDigestMeta(options.date, machines, snapshots, body), body);\n return writeDigestIfChanged(options.memoryDir, options.date, serialized);\n });\n\n options.signal?.throwIfAborted();\n options.store.setCursor(cursorKey, cursor);\n\n // Rows and digest are durable and the cursor has advanced; refresh the search\n // index so the new digest is discoverable (rule 31). Best-effort: a failure\n // is reported, not thrown — the data indexes on the next update.\n let reindexError: string | undefined;\n if (digestWritten && options.afterWrites !== undefined) {\n try {\n await options.afterWrites(options.signal);\n } catch (error: unknown) {\n // reindexError is part of the exported ActivitySyncResult. Keep our\n // controlled QMD strict messages, but reduce opaque backend errors (which\n // can embed absolute paths / loader stacks) to a sanitized name+code.\n reindexError =\n error instanceof Error && error.message.startsWith(\"QMD \")\n ? error.message\n : displayErrorDetail(error) || \"reindex failed\";\n }\n }\n\n return {\n machine: source.machineLabel,\n fetched,\n inserted,\n duplicates,\n cursor,\n digestWritten,\n ...(reindexError === undefined ? {} : { reindexError }),\n };\n}\n","import { validateActivityBaseUrl } from \"./config.js\";\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\nimport type { ActivitySnapshot, ActivitySnapshotPage, ActivitySourceCheck, ActivitySourceClient } from \"./types.js\";\n\n/** Abort a stalled capture-daemon request rather than hang the whole sync. */\nconst DEFAULT_REQUEST_TIMEOUT_MS = 30_000;\n\nexport interface ActivityHttpSourceClientOptions {\n machineLabel: string;\n baseUrl: string;\n token?: string;\n /** Per-request timeout in ms; defaults to DEFAULT_REQUEST_TIMEOUT_MS. */\n timeoutMs?: number;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction stringField(value: Record<string, unknown>, field: string): string {\n const result = value[field];\n if (typeof result !== \"string\" || result.length === 0) {\n throw new TypeError(`activity source response has no valid ${field}`);\n }\n return result;\n}\n\n/**\n * Content fields (app/windowTitle/text) may be legitimately empty: a foreground\n * window can be untitled or have no extractable text. Require the string TYPE\n * (a missing/non-string field still fails loudly) but tolerate \"\", matching the\n * durable store's contract — else a blank window poisons the day cursor by\n * failing every replay of the same page.\n */\nfunction contentStringField(value: Record<string, unknown>, field: string): string {\n const result = value[field];\n if (typeof result !== \"string\") {\n throw new TypeError(`activity source response has invalid ${field}`);\n }\n return result;\n}\n\nfunction optionalStringField(value: Record<string, unknown>, field: string): string | undefined {\n const result = value[field];\n if (result === undefined || result === null) return undefined;\n if (typeof result !== \"string\") throw new TypeError(`activity source response has invalid ${field}`);\n return result;\n}\n\nfunction snapshotFromWire(value: unknown, machine: string): ActivitySnapshot {\n if (!isRecord(value)) throw new TypeError(\"activity source returned a non-object snapshot\");\n const textSource = stringField(value, \"textSource\");\n if (textSource !== \"ax\" && textSource !== \"ocr\") {\n throw new TypeError(\"activity source response has invalid textSource\");\n }\n const browserUrl = optionalStringField(value, \"browserUrl\");\n const simhash = optionalStringField(value, \"simhash\");\n return {\n machine,\n capturedAtUtc: stringField(value, \"capturedAtUtc\"),\n app: contentStringField(value, \"app\"),\n windowTitle: contentStringField(value, \"windowTitle\"),\n ...(browserUrl === undefined ? {} : { browserUrl }),\n text: contentStringField(value, \"text\"),\n textSource,\n contentHash: stringField(value, \"contentHash\"),\n ...(simhash === undefined ? {} : { simhash }),\n };\n}\n\nfunction responsePage(value: unknown, machine: string): ActivitySnapshotPage {\n if (!isRecord(value) || !Array.isArray(value.snapshots)) {\n throw new TypeError(\"activity source response has no snapshots array\");\n }\n // A missing/undefined nextCursor (field omitted, not JSON null) is normal\n // end-of-pagination — only a present non-string value is invalid.\n const nextCursor = value.nextCursor === undefined ? null : value.nextCursor;\n if (nextCursor !== null && typeof nextCursor !== \"string\") {\n throw new TypeError(\"activity source response has invalid nextCursor\");\n }\n return {\n snapshots: value.snapshots.map((snapshot) => snapshotFromWire(snapshot, machine)),\n nextCursor,\n };\n}\n\nfunction requestUrl(baseUrl: string, path: string, query: Record<string, string | undefined>): string {\n const url = new URL(path, baseUrl);\n const parts = Object.entries(query)\n .filter((entry): entry is [string, string] => entry[1] !== undefined)\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n url.search = parts.join(\"&\");\n return url.toString();\n}\n\nexport class ActivityHttpSourceClient implements ActivitySourceClient {\n readonly machineLabel: string;\n private readonly baseUrl: string;\n private readonly token: string | undefined;\n private readonly timeoutMs: number;\n\n constructor(options: ActivityHttpSourceClientOptions) {\n if (options.machineLabel.trim().length === 0) throw new RangeError(\"activity source machine label must not be empty\");\n if (options.timeoutMs !== undefined && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) {\n throw new RangeError(\"activity source timeoutMs must be a positive number\");\n }\n this.machineLabel = options.machineLabel;\n this.baseUrl = validateActivityBaseUrl(options.baseUrl).toString();\n this.token = options.token;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;\n }\n\n private async request(url: string, signal?: AbortSignal): Promise<Response> {\n // Bound every request so a stalled daemon aborts instead of hanging the\n // sync forever; honor the caller's signal too when one is supplied.\n const timeout = AbortSignal.timeout(this.timeoutMs);\n const composed = signal === undefined ? timeout : AbortSignal.any([signal, timeout]);\n const response = await fetch(url, {\n headers: this.token === undefined ? undefined : { authorization: `Bearer ${this.token}` },\n signal: composed,\n });\n if (!response.ok) throw new Error(`activity source HTTP ${response.status}`);\n return response;\n }\n\n async verify(signal?: AbortSignal): Promise<ActivitySourceCheck> {\n try {\n await this.request(requestUrl(this.baseUrl, \"/v1/health\", {}), signal);\n return { ok: true };\n } catch (error: unknown) {\n // The controlled `HTTP <status>` message is safe to surface verbatim;\n // anything else (network/runtime fetch errors that can embed hostnames or\n // absolute paths) is reduced to a sanitized name+code via\n // displayErrorDetail, matching the wearables operator-facing path.\n const detail =\n error instanceof Error && error.message.startsWith(\"activity source HTTP \")\n ? error.message.slice(\"activity source \".length)\n : displayErrorDetail(error) || \"request failed\";\n return { ok: false, detail };\n }\n }\n\n async fetchSnapshots(opts: {\n date: string;\n timezone: string;\n cursor?: string | null;\n signal?: AbortSignal;\n }): Promise<ActivitySnapshotPage> {\n const response = await this.request(\n requestUrl(this.baseUrl, \"/v1/snapshots\", {\n date: opts.date,\n timezone: opts.timezone,\n cursor: opts.cursor ?? undefined,\n }),\n opts.signal,\n );\n return responsePage(await response.json(), this.machineLabel);\n }\n}\n","/**\n * Host-agnostic activity sync runner (issue #1900).\n *\n * Bridges parsed `config.activity` to the durable sync pipeline: the parser\n * (config.ts) validates settings but never contacts a daemon, and\n * `syncActivitySource` (pipeline.ts) syncs one source for one day. This runner\n * is the missing middle — it instantiates one `ActivityHttpSourceClient` per\n * configured source from the parsed config and drives a sync across the\n * `syncDays` local-day window, but ONLY when the feature is enabled.\n *\n * The feature is master default-off: source config alone never starts a sync.\n * When `config.enabled` is false the runner opens no store, builds no client,\n * and makes no HTTP call or write — it returns a disabled summary. This mirrors\n * `runLiveConnectorsOnce`'s disabled-vs-result reporting so a host scheduler\n * (CLI, cron) can trigger it through the runtime with no OpenClaw imports.\n *\n * Cursor semantics are delegated wholesale to `syncActivitySource`: the\n * per-machine cursor advances only after every page and the digest are durable,\n * so a runtime failure (or an aborted stop) leaves the cursor unadvanced and a\n * restart resumes from the persisted position. A per-source failure is captured\n * as an error item and never aborts the other sources or the host flow.\n */\n\nimport { ActivityStore } from \"./store.js\";\nimport { syncActivitySource } from \"./pipeline.js\";\nimport { ActivityHttpSourceClient } from \"./source-client.js\";\nimport { activityDateInTimezone } from \"./digest.js\";\nimport { displayErrorDetail } from \"../runtime/better-sqlite.js\";\nimport type { ActivityConfig, ActivitySourceClient, ActivitySourceConfig } from \"./types.js\";\n\ntype ActivityNow = Date | (() => Date);\n\n/** One configured source's aggregate result across the synced day window. */\nexport interface ActivitySourceRunItem {\n machineLabel: string;\n /** True when at least one day synced durably (no error). */\n ran: boolean;\n fetched: number;\n inserted: number;\n duplicates: number;\n digestsWritten: number;\n /** Persisted cursor after the run; unadvanced from prior on failure. */\n cursor: string | null;\n /** Set when the source failed; distinguishes a fault from an empty page. */\n error?: string;\n /**\n * Set when a durable day synced but its post-digest search reindex failed.\n * The rows/digest/cursor are committed; the index is stale until the next\n * refresh, so this is a signal (not a sync failure) for the scheduler.\n */\n reindexError?: string;\n}\n\nexport interface ActivitySyncRunSummary {\n ranAt: string;\n /** False means the feature is off: no client built, no HTTP, no write. */\n enabled: boolean;\n ranCount: number;\n errorCount: number;\n /** Sources whose durable sync succeeded but whose search reindex failed. */\n reindexErrorCount: number;\n totalInserted: number;\n results: ActivitySourceRunItem[];\n /**\n * Local dates (YYYY-MM-DD, in the sync timezone) that gained new snapshots\n * this run — a backfill/rolling window can touch several. Meetings rebuild\n * uses this so a change to an OLDER day rebuilds that day, not just today.\n */\n touchedDates: string[];\n}\n\nexport interface ActivitySyncRunOptions {\n config: ActivityConfig;\n memoryDir: string;\n /** Injectable store; opened from memoryDir when omitted (and closed here). */\n store?: ActivityStore;\n /** Clock injection for deterministic day-window resolution in tests. */\n now?: ActivityNow;\n /** Abort a run in flight (stop); an aborted source leaves its cursor intact. */\n signal?: AbortSignal;\n /** Injectable client factory; builds an ActivityHttpSourceClient by default. */\n createSourceClient?: (source: ActivitySourceConfig) => ActivitySourceClient;\n /**\n * Search-index refresh forwarded to each source sync as `afterWrites`, run\n * after a digest is (re)written so it becomes discoverable (rule 31).\n * Host wires the core index seam (SearchBackend.update); best-effort.\n */\n reindexSearch?: (signal?: AbortSignal) => Promise<void>;\n}\n\n/** Previous calendar date via pure date arithmetic (no DST wall-clock drift). */\nfunction previousIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() - 1);\n return parsed.toISOString().slice(0, 10);\n}\n\n/**\n * The local-day window to sync, oldest first: today back `syncDays` calendar\n * days in the configured timezone. Walking by calendar days (not fixed 24h\n * intervals) keeps the window correct across DST transitions.\n */\nexport function resolveActivitySyncDates(syncDays: number, timezone: string, now: Date): string[] {\n const dates: string[] = [];\n let cursor = activityDateInTimezone(now, timezone);\n for (let count = 0; count < syncDays; count++) {\n dates.unshift(cursor);\n cursor = previousIsoDate(cursor);\n }\n return dates;\n}\n\n/**\n * Operator-facing detail for a per-source/per-day sync fault. Our own\n * controlled messages (activity.../Invalid activity...) are informative and\n * safe to surface; opaque network/runtime errors can embed hostnames or paths,\n * so they are reduced to a sanitized name+code (matches ActivityHttpSourceClient.verify).\n */\nfunction sanitizedSyncError(error: unknown): string {\n return error instanceof Error && /^(activity|Invalid activity)/.test(error.message)\n ? error.message\n : displayErrorDetail(error) || \"activity sync failed\";\n}\n\n/**\n * Run one activity sync pass across every configured source. Disabled config\n * short-circuits before any store/client/HTTP work; an enabled config syncs\n * each source across the `syncDays` window and isolates per-source faults.\n */\nexport async function runActivitySyncOnce(options: ActivitySyncRunOptions): Promise<ActivitySyncRunSummary> {\n const ranAt = typeof options.now === \"function\" ? options.now() : options.now ?? new Date();\n\n if (!options.config.enabled) {\n return {\n ranAt: ranAt.toISOString(),\n enabled: false,\n ranCount: 0,\n errorCount: 0,\n reindexErrorCount: 0,\n totalInserted: 0,\n results: [],\n touchedDates: [],\n };\n }\n\n const dates = resolveActivitySyncDates(options.config.syncDays, options.config.timezone, ranAt);\n const createSourceClient =\n options.createSourceClient ??\n ((source: ActivitySourceConfig): ActivitySourceClient =>\n new ActivityHttpSourceClient({\n machineLabel: source.machineLabel,\n baseUrl: source.baseUrl,\n ...(source.token === undefined ? {} : { token: source.token }),\n }));\n const ownStore = options.store === undefined;\n const store = options.store ?? ActivityStore.open(options.memoryDir);\n const results: ActivitySourceRunItem[] = [];\n // Local dates that gained new snapshots this run (any source). Drives the\n // meetings rebuild fan-out so a backfilled OLDER day is rebuilt too.\n const touchedDates = new Set<string>();\n\n try {\n for (const sourceConfig of options.config.sources) {\n const item: ActivitySourceRunItem = {\n machineLabel: sourceConfig.machineLabel,\n ran: false,\n fetched: 0,\n inserted: 0,\n duplicates: 0,\n digestsWritten: 0,\n // Cursors are now per (machine, date); report the last synced day's\n // cursor. Null until a day advances it.\n cursor: null,\n };\n try {\n const client = createSourceClient(sourceConfig);\n for (const date of dates) {\n try {\n const result = await syncActivitySource(client, {\n date,\n timezone: options.config.timezone,\n memoryDir: options.memoryDir,\n store,\n signal: options.signal,\n ...(options.reindexSearch === undefined ? {} : { afterWrites: options.reindexSearch }),\n });\n item.fetched += result.fetched;\n item.inserted += result.inserted;\n if (result.inserted > 0) touchedDates.add(date);\n item.duplicates += result.duplicates;\n if (result.digestWritten) item.digestsWritten += 1;\n item.cursor = result.cursor;\n // A day that synced durably counts as ran, even if a later day in\n // the window fails (ran = at least one durable day).\n item.ran = true;\n // The day's rows/digest/cursor are durable; a failed post-digest\n // reindex is a separate, already-sanitized signal (not a sync\n // failure) so the scheduler can observe a stale index.\n if (result.reindexError !== undefined) item.reindexError = result.reindexError;\n } catch (dateError) {\n // One bad day (a malformed historical snapshot, a per-day daemon\n // error) must not starve the remaining dates — record it and move\n // on. Dates are processed oldest-first, so continuing keeps today's\n // sync alive when an old backfill day is broken. An abort, though,\n // halts the whole run.\n item.error = sanitizedSyncError(dateError);\n if (options.signal?.aborted) break;\n }\n }\n } catch (error) {\n // Client construction failed: no date could run for this source.\n item.error = sanitizedSyncError(error);\n }\n results.push(item);\n // An abort halts the WHOLE run: the in-flight source above is recorded\n // (its fast-failing attempt reports an error), but no later source builds\n // a client or contacts a daemon — teardown settles promptly.\n if (options.signal?.aborted) break;\n }\n } finally {\n if (ownStore) store.close();\n }\n\n return {\n ranAt: ranAt.toISOString(),\n enabled: true,\n ranCount: results.filter((item) => item.ran).length,\n errorCount: results.filter((item) => item.error !== undefined).length,\n reindexErrorCount: results.filter((item) => item.reindexError !== undefined).length,\n totalInserted: results.reduce((sum, item) => sum + item.inserted, 0),\n results,\n touchedDates: [...touchedDates].sort(),\n };\n}\n","/**\n * In-process periodic driver for activity (screen-capture) sync (issue #1900).\n *\n * The parser (config.ts) validates `config.activity` and the runner\n * (runner.ts) performs one durable sync pass, but nothing drove that pass on a\n * cadence — parsed config alone never synced. This scheduler is the missing\n * link: parser -> scheduler -> durable sync. It registers a repeating timer\n * that invokes the runner, and cancels it on stop.\n *\n * Master default-off: when `config.activity.enabled` is false, `start()`\n * registers no timer and never invokes — source config alone starts nothing.\n * `stop()` clears the timer and latches so no later tick invokes, which is what\n * a runtime teardown (or a config disable + restart) needs. Ticks are\n * overlap-guarded (a slow sync never stacks) and never throw out of the timer.\n *\n * Host-agnostic: the timer and clock are injectable, so a host schedules\n * through this without any OpenClaw import, and tests drive it deterministically.\n */\n\nimport { runActivitySyncOnce, type ActivitySyncRunSummary } from \"./runner.js\";\nimport type { ActivityConfig } from \"./types.js\";\n\n/**\n * Default sync cadence: 15 minutes. Issue #1899 contracts\n * `activity.autoSyncIntervalMinutes` with a default of 15, so the effective\n * default interval is 15 * 60_000 = 900_000 ms. Overridable via `intervalMs`.\n */\nexport const ACTIVITY_SYNC_DEFAULT_INTERVAL_MS = 900_000;\n\n/** An opaque timer handle from whichever timer implementation is injected. */\ntype TimerHandle = unknown;\n\nexport interface ActivitySyncSchedulerOptions {\n config: ActivityConfig;\n memoryDir: string;\n /** Cadence in ms; defaults to ACTIVITY_SYNC_DEFAULT_INTERVAL_MS. */\n intervalMs?: number;\n /** The sync pass to run each tick; defaults to the durable runner. */\n invoke?: (signal?: AbortSignal) => Promise<ActivitySyncRunSummary>;\n /** Search-index refresh forwarded to the default runner as `reindexSearch`. */\n reindexSearch?: (signal?: AbortSignal) => Promise<void>;\n /** Timer factory (injectable for tests); defaults to an unref'd setInterval. */\n setTimer?: (fn: () => void, ms: number) => TimerHandle;\n /** Timer canceller matching setTimer; defaults to clearInterval. */\n clearTimer?: (handle: TimerHandle) => void;\n /** Called with each successful run's summary (telemetry/tests). */\n onRun?: (summary: ActivitySyncRunSummary) => void;\n /** Called when a tick's sync rejects; defaults to swallowing (best-effort). */\n onError?: (error: unknown) => void;\n}\n\nexport interface ActivitySyncRegistration {\n /** True when a periodic timer was armed (feature enabled). */\n registered: boolean;\n /** The cadence the timer runs at (reported even when not registered). */\n intervalMs: number;\n}\n\n/**\n * Registers/cancels a periodic activity sync. One instance per memory dir;\n * `start()` and `stop()` are both idempotent.\n */\nexport class ActivitySyncScheduler {\n private readonly config: ActivityConfig;\n private readonly memoryDir: string;\n private readonly intervalMs: number;\n private readonly invoke: (signal?: AbortSignal) => Promise<ActivitySyncRunSummary>;\n private readonly setTimer: (fn: () => void, ms: number) => TimerHandle;\n private readonly clearTimer: (handle: TimerHandle) => void;\n private readonly onRun: ((summary: ActivitySyncRunSummary) => void) | undefined;\n private readonly onError: ((error: unknown) => void) | undefined;\n\n private handle: TimerHandle = null;\n private stopped = false;\n private inFlight = false;\n private inFlightPromise: Promise<void> | null = null;\n private readonly abortController = new AbortController();\n\n constructor(options: ActivitySyncSchedulerOptions) {\n this.config = options.config;\n this.memoryDir = options.memoryDir;\n this.intervalMs = options.intervalMs ?? ACTIVITY_SYNC_DEFAULT_INTERVAL_MS;\n this.invoke =\n options.invoke ??\n ((signal) =>\n runActivitySyncOnce({\n config: this.config,\n memoryDir: this.memoryDir,\n signal,\n ...(options.reindexSearch === undefined ? {} : { reindexSearch: options.reindexSearch }),\n }));\n this.setTimer =\n options.setTimer ??\n ((fn, ms) => {\n const timer = setInterval(fn, ms);\n // Never keep the host process alive solely for the sync cadence.\n timer.unref?.();\n return timer;\n });\n this.clearTimer =\n options.clearTimer ?? ((handle) => clearInterval(handle as NodeJS.Timeout));\n this.onRun = options.onRun;\n this.onError = options.onError;\n }\n\n /**\n * Arm the periodic sync. Disabled config (default-off) registers nothing.\n * Idempotent: a second call while running (or after stop) re-arms nothing.\n */\n start(): ActivitySyncRegistration {\n if (this.handle !== null || this.stopped || !this.config.enabled) {\n return { registered: this.handle !== null, intervalMs: this.intervalMs };\n }\n this.handle = this.setTimer(() => this.tick(), this.intervalMs);\n return { registered: true, intervalMs: this.intervalMs };\n }\n\n /**\n * Cancel the periodic sync and abort any in-flight tick, then resolve once it\n * has unwound. The abort + timer-clear + stop-latch happen synchronously, so\n * even a caller that cannot await (a sync teardown) still signals the running\n * sync to stop; async callers can await the returned drain. Idempotent.\n */\n async stop(): Promise<void> {\n this.stopped = true;\n this.abortController.abort();\n if (this.handle !== null) {\n this.clearTimer(this.handle);\n this.handle = null;\n }\n await this.inFlightPromise?.catch(() => undefined);\n }\n\n private tick(): void {\n // Latched-stop and overlap guards: a callback that fires after stop(), or\n // while a prior slow sync is still running, must not invoke.\n if (this.stopped || this.inFlight) return;\n this.inFlight = true;\n this.inFlightPromise = this.invoke(this.abortController.signal)\n .then((summary) => this.onRun?.(summary))\n .catch((error) => this.onError?.(error))\n .finally(() => {\n this.inFlight = false;\n this.inFlightPromise = null;\n });\n }\n}\n","/**\n * Activity (screen-capture) sync lifecycle owner (issue #1900).\n *\n * The MaintenanceScheduler used to inline the arm / re-arm / teardown wiring\n * for the periodic activity sync, which made the lifecycle invariants easy to\n * break one edit at a time. This module centralizes them in one testable place:\n *\n * - Re-arm and teardown BOTH stop any prior scheduler first, so a repeated\n * registration never orphans a running interval.\n * - An aborted deferred-init signal means teardown began. `destroy()` aborts\n * the init signal *before* it disposes, so an aborted signal here also\n * implies `dispose()` may already have latched teardown. `register()` never\n * arms nor clears the latch while aborted — even when the abort lands\n * mid-drain — so a late re-init cannot resurrect a torn-down instance.\n * - The teardown latch gates the reindex-failure retry, so a draining tick's\n * `onRun` cannot queue QMD maintenance after teardown.\n * - Activity digests + SQLite are written in the clear, so the registrar\n * refuses to arm under a secure store rather than silently bypass opted-in\n * at-rest encryption.\n *\n * Host-agnostic: the scheduler factory is injectable, so tests drive the whole\n * lifecycle against a fake scheduler without real timers.\n */\n\nimport { refreshActivityIndex, type ActivityIndexRefresher } from \"./reindex.js\";\nimport type { ActivitySyncRunSummary } from \"./runner.js\";\nimport { ActivitySyncScheduler, type ActivitySyncSchedulerOptions } from \"./scheduler.js\";\nimport type { ActivityConfig } from \"./types.js\";\nimport { log } from \"../logger.js\";\n\n/** The scheduler surface the registrar drives (injectable for tests). */\nexport interface ActivitySyncSchedulerLike {\n start(): unknown;\n stop(): Promise<void>;\n}\n\nexport interface ActivitySyncRegistrarDeps {\n /** Activity sync settings; the master default-off flag lives here. */\n readonly config: ActivityConfig;\n /** Memory root the digests + SQLite store live under. */\n readonly memoryDir: string;\n /** QMD collection the day digest is (strictly) reindexed into. */\n readonly qmdCollection: string;\n /**\n * True when at-rest encryption is configured. Activity writes are not yet\n * encrypted, so the registrar refuses to arm rather than leak plaintext.\n */\n readonly secureStoreEnabled: boolean;\n /** Live QMD accessor — the host may swap backends after construction. */\n readonly getQmd: () => ActivityIndexRefresher;\n /** Debounced/singleflighted QMD retry used when a tick's reindex fails. */\n readonly requestReindexRetry: () => void;\n /**\n * Tail step (issue #1900): observe a completed sync tick so a dependent\n * subsystem (meeting building) can rebuild the affected day(s). Fired only\n * after a durable tick and never after teardown. Optional; omitted by hosts\n * that do not build meetings.\n */\n readonly onSynced?: (summary: ActivitySyncRunSummary) => void;\n /** Scheduler factory (injectable for tests); defaults to the real scheduler. */\n readonly createScheduler?: (options: ActivitySyncSchedulerOptions) => ActivitySyncSchedulerLike;\n}\n\nexport class ActivitySyncRegistrar {\n private scheduler: ActivitySyncSchedulerLike | null = null;\n /** Latched by dispose() so a draining tick's onRun cannot re-arm maintenance. */\n private disposed = false;\n private readonly createScheduler: (options: ActivitySyncSchedulerOptions) => ActivitySyncSchedulerLike;\n\n constructor(private readonly deps: ActivitySyncRegistrarDeps) {\n this.createScheduler = deps.createScheduler ?? ((options) => new ActivitySyncScheduler(options));\n }\n\n /** True while a scheduler is armed (test/telemetry observability). */\n get armed(): boolean {\n return this.scheduler !== null;\n }\n\n /**\n * Arm (or re-arm) the periodic sync for the current lifecycle. Idempotent and\n * safe to call repeatedly; always stops a prior scheduler first.\n */\n async register(signal: AbortSignal): Promise<void> {\n // Re-arm or teardown: stop any prior scheduler before anything else, so a\n // repeated registration never orphans a running interval.\n const prior = this.scheduler;\n this.scheduler = null;\n await prior?.stop();\n // Teardown began (possibly during the drain above). Never arm nor clear the\n // latch: dispose() may already have set it, and it must stay set.\n if (signal.aborted) return;\n // Opted-in at-rest encryption must not be silently bypassed: activity\n // digests + SQLite are written in the clear, so refuse to run under it.\n if (this.deps.secureStoreEnabled && this.deps.config.enabled) {\n log.warn(\n \"activity sync disabled: secure store is enabled but activity digests/SQLite are not encrypted at rest\",\n );\n return;\n }\n // Committed to a fresh lifecycle: clear the latch so this scheduler's\n // reindex retries fire again.\n this.disposed = false;\n try {\n const scheduler = this.createScheduler({\n config: this.deps.config,\n memoryDir: this.deps.memoryDir,\n intervalMs: this.deps.config.autoSyncIntervalMinutes * 60_000,\n // Strict refresh after each digest write (rule 31): bypasses the\n // min-interval gate and throws on a real failure.\n reindexSearch: (sig) => refreshActivityIndex(this.deps.getQmd(), this.deps.qmdCollection, sig),\n // A failed reindex (QMD down) leaves the next unchanged-digest tick\n // skipping afterWrites, so route the retry through the durable,\n // throttled QMD path — but never after teardown.\n onRun: (summary: ActivitySyncRunSummary) => {\n if (summary.reindexErrorCount > 0 && !this.disposed) {\n log.warn(\n `activity sync: ${summary.reindexErrorCount} source(s) had a failed reindex; queuing a maintenance retry`,\n );\n this.deps.requestReindexRetry();\n }\n // Tail step: a durable tick that ingested snapshots may have changed a\n // meeting's screen context; notify the dependent builder. Never after\n // teardown.\n if (summary.enabled && summary.totalInserted > 0 && !this.disposed) {\n this.deps.onSynced?.(summary);\n }\n },\n });\n this.scheduler = scheduler;\n scheduler.start();\n // Close the race where abort fires between the guard above and start().\n if (signal.aborted) {\n await scheduler.stop();\n this.scheduler = null;\n }\n } catch (err) {\n log.debug(`activity sync scheduler start failed (non-fatal): ${err}`);\n }\n }\n\n /**\n * Latch teardown and drain the scheduler. Latching first ensures a draining\n * tick's onRun cannot queue a retry; the drain is awaited so no in-flight\n * tick keeps writing after teardown.\n */\n async dispose(): Promise<void> {\n this.disposed = true;\n const drain = this.scheduler?.stop();\n this.scheduler = null;\n await drain;\n }\n}\n"],"mappings":"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","names":["mkdir","readFile","writeFile","path","id","mkdir","path","mkdir","path","isRecord","path","path","mkdir","writeFile","readFile"]}
|