@remnic/core 9.3.690 → 9.3.692
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-boundary.d.ts +7 -6
- package/dist/access-boundary.js +21 -21
- package/dist/access-cli.js +49 -47
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +7 -6
- package/dist/access-http.js +26 -26
- package/dist/access-mcp.d.ts +7 -6
- package/dist/access-mcp.js +23 -23
- package/dist/access-operations.d.ts +7 -6
- package/dist/access-operations.js +22 -22
- package/dist/{access-service-Dujr3MTm.d.ts → access-service-DvApf9LZ.d.ts} +2 -2
- package/dist/access-service.d.ts +7 -6
- package/dist/access-service.js +20 -20
- package/dist/access-surface-catalog.d.ts +7 -6
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{auto-sync-NUQWSFQD.js → auto-sync-RWXT6P5M.js} +5 -5
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +6 -5
- package/dist/briefing.d.ts +1 -1
- package/dist/briefing.js +5 -5
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer.d.ts +1 -1
- package/dist/calibration.d.ts +1 -1
- package/dist/calibration.js +2 -2
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-CKPtJ114.d.ts → catalog-BEWLjdOr.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-consolidation.js +8 -8
- package/dist/{chunk-GTDH3IUH.js → chunk-2D27ODNX.js} +2 -2
- package/dist/{chunk-46URPRE6.js → chunk-2EN5WZQB.js} +2 -2
- package/dist/{chunk-3K37VPM3.js → chunk-3BRA3N5A.js} +2 -2
- package/dist/{chunk-RTFAZOIR.js → chunk-3VVH6ZQW.js} +2 -2
- package/dist/{chunk-ZU7N3S6V.js → chunk-4IQNAOP3.js} +6 -142
- package/dist/chunk-4IQNAOP3.js.map +1 -0
- package/dist/{chunk-FIVDN2SM.js → chunk-4RGWCRIJ.js} +4 -4
- package/dist/{chunk-AGJH5ISO.js → chunk-5KBNNUXS.js} +2 -2
- package/dist/{chunk-X7RLU5CR.js → chunk-66HROLW5.js} +2 -2
- package/dist/chunk-6C2BMUZA.js +444 -0
- package/dist/chunk-6C2BMUZA.js.map +1 -0
- package/dist/{chunk-TYF3D4MS.js → chunk-6ZNIUGXB.js} +11 -11
- package/dist/{chunk-UPIBE2DK.js → chunk-ADUAF5EV.js} +2 -2
- package/dist/{chunk-LLONI6PY.js → chunk-AFWEUTMS.js} +2 -2
- package/dist/{chunk-ZUDM75KG.js → chunk-DCWIQFNA.js} +4 -4
- package/dist/{chunk-OMKUJTVJ.js → chunk-DO7MDZMU.js} +4 -4
- package/dist/{chunk-NGFEWFNK.js → chunk-DQHZA7Y3.js} +1 -1
- package/dist/chunk-DQHZA7Y3.js.map +1 -0
- package/dist/{chunk-LXIEXSHU.js → chunk-E6BOD3UJ.js} +2 -2
- package/dist/{chunk-OBXTMFZQ.js → chunk-ECZPWSB5.js} +2 -2
- package/dist/{chunk-CWE74HRG.js → chunk-FM7KLRKN.js} +3 -3
- package/dist/{chunk-BFVPIKDY.js → chunk-GCOSQBQ2.js} +3 -3
- package/dist/chunk-HXNMDPHN.js +149 -0
- package/dist/chunk-HXNMDPHN.js.map +1 -0
- package/dist/{chunk-MDYG7VI7.js → chunk-IREVVFTM.js} +10 -1
- package/dist/{chunk-MDYG7VI7.js.map → chunk-IREVVFTM.js.map} +1 -1
- package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
- package/dist/{chunk-CP2NZQLT.js → chunk-KYH53EFD.js} +6 -6
- package/dist/{chunk-LQ6JI4VH.js → chunk-LD53WPMU.js} +4 -4
- package/dist/{chunk-SB6CQTKP.js → chunk-MCV2DD6F.js} +2 -2
- package/dist/{chunk-3FC6LW6T.js → chunk-MRFVMSTR.js} +2 -2
- package/dist/{chunk-WDXCNJSF.js → chunk-MXZWKOOR.js} +2 -2
- package/dist/{chunk-NSVXK7M5.js → chunk-NEILAC3H.js} +3 -3
- package/dist/{chunk-RGNEARXW.js → chunk-NUZ3N445.js} +7 -7
- package/dist/{chunk-WROKD3XC.js → chunk-O4ZIV62T.js} +35 -35
- package/dist/{chunk-PHZKALOE.js → chunk-QFG23CRE.js} +2 -2
- package/dist/{chunk-A4HH2EWA.js → chunk-QVG4LAQO.js} +2 -2
- package/dist/{chunk-54PVJDO5.js → chunk-SAC5V6JT.js} +2 -2
- package/dist/{chunk-XKUKJIOY.js → chunk-SEK4XIDN.js} +6 -6
- package/dist/{chunk-5CEJH5ZN.js → chunk-TAPSNIMA.js} +4 -4
- package/dist/{chunk-2N6UNJSX.js → chunk-U2OBZSGG.js} +2 -2
- package/dist/{chunk-BKAMHZYR.js → chunk-UOCLRM74.js} +2 -2
- package/dist/{chunk-A7UZR6RA.js → chunk-UTUYAPLY.js} +14 -1
- package/dist/chunk-UTUYAPLY.js.map +1 -0
- package/dist/{chunk-Z7KILAOU.js → chunk-V56S3A23.js} +19 -19
- package/dist/{chunk-CUNTLEJP.js → chunk-VFVG4NS2.js} +30 -9
- package/dist/chunk-VFVG4NS2.js.map +1 -0
- package/dist/{chunk-UD5OKH4J.js → chunk-W4ELOUNX.js} +2 -2
- package/dist/{chunk-CE6CBRCV.js → chunk-W4MICQIZ.js} +89 -407
- package/dist/chunk-W4MICQIZ.js.map +1 -0
- package/dist/{chunk-GR77Z2BM.js → chunk-WIKNVFBS.js} +2 -2
- package/dist/{chunk-WRFKZEO6.js → chunk-YQGHW5ML.js} +4 -4
- package/dist/{chunk-67MMWC74.js → chunk-ZLXGFAML.js} +2 -2
- package/dist/{cli-BkDp6WNi.d.ts → cli-BjsY9Le7.d.ts} +3 -3
- package/dist/cli.d.ts +8 -7
- package/dist/cli.js +45 -44
- package/dist/compounding/engine.d.ts +1 -1
- package/dist/compounding/engine.js +5 -5
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize-runner.js +5 -5
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +5 -5
- package/dist/consolidation-provenance-check.d.ts +1 -1
- package/dist/consolidation-undo.d.ts +1 -1
- package/dist/contradiction/index.d.ts +1 -1
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +1 -1
- package/dist/entity-retrieval.js +5 -5
- package/dist/entity-schema.d.ts +1 -1
- package/dist/event-time.d.ts +35 -0
- package/dist/event-time.js +325 -0
- package/dist/event-time.js.map +1 -0
- package/dist/explicit-capture.d.ts +6 -5
- 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-judge.js +3 -3
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +3 -3
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/fallback-llm.js +2 -2
- package/dist/identity-continuity.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/index.d.ts +11 -10
- package/dist/index.js +77 -75
- package/dist/index.js.map +1 -1
- 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/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/memory-governance.js +5 -5
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
- package/dist/maintenance/rebuild-memory-projection.js +6 -6
- package/dist/mcp-memory-inspector-app.d.ts +7 -6
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-worth-outcomes.d.ts +1 -1
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +2 -2
- package/dist/namespaces/migrate.js +15 -15
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/search.js +9 -9
- package/dist/namespaces/storage.d.ts +2 -2
- package/dist/namespaces/storage.js +5 -5
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +1 -1
- package/dist/operator-toolkit.js +20 -19
- package/dist/orchestration/maintenance.d.ts +112 -0
- package/dist/orchestration/maintenance.js +39 -0
- package/dist/orchestration/maintenance.js.map +1 -0
- package/dist/{orchestrator-B7ixmUkP.d.ts → orchestrator-CNJCYjZ1.d.ts} +12 -36
- package/dist/orchestrator.d.ts +5 -4
- package/dist/orchestrator.js +42 -40
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-planner-llm.js +2 -2
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.d.ts +1 -1
- package/dist/recall-xray.js +2 -2
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.d.ts +1 -1
- 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/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/factory.js +8 -8
- package/dist/search/index.d.ts +1 -1
- package/dist/search/index.js +13 -13
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +2 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +2 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +2 -2
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-CWch5uM7.d.ts → semantic-consolidation-D5U0Mrdb.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-consolidation.js +6 -6
- package/dist/semantic-rule-promotion.js +5 -5
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/semantic-rule-verifier.js +5 -5
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/storage.d.ts +3 -1
- package/dist/storage.js +4 -4
- package/dist/summarizer.d.ts +1 -1
- package/dist/summarizer.js +3 -3
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/temporal-supersession.d.ts +1 -1
- package/dist/temporal-supersession.js +2 -2
- package/dist/temporal-validity.d.ts +22 -2
- package/dist/temporal-validity.js +3 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +1 -1
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/{types-Dm5xxVrr.d.ts → types-2NoBoRBM.d.ts} +38 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/verified-recall.js +5 -5
- package/package.json +12 -2
- package/src/bitemporal-frontmatter.test.ts +177 -0
- package/src/config.ts +13 -0
- package/src/event-time.test.ts +514 -0
- package/src/event-time.ts +548 -0
- package/src/orchestration/maintenance.test.ts +738 -0
- package/src/orchestration/maintenance.ts +557 -0
- package/src/orchestrator-flush.test.ts +0 -599
- package/src/orchestrator.ts +60 -420
- package/src/storage.ts +34 -0
- package/src/temporal-validity.ts +34 -0
- package/src/types.ts +38 -0
- package/dist/chunk-A7UZR6RA.js.map +0 -1
- package/dist/chunk-CE6CBRCV.js.map +0 -1
- package/dist/chunk-CUNTLEJP.js.map +0 -1
- package/dist/chunk-NGFEWFNK.js.map +0 -1
- package/dist/chunk-ZU7N3S6V.js.map +0 -1
- /package/dist/{auto-sync-NUQWSFQD.js.map → auto-sync-RWXT6P5M.js.map} +0 -0
- /package/dist/{chunk-GTDH3IUH.js.map → chunk-2D27ODNX.js.map} +0 -0
- /package/dist/{chunk-46URPRE6.js.map → chunk-2EN5WZQB.js.map} +0 -0
- /package/dist/{chunk-3K37VPM3.js.map → chunk-3BRA3N5A.js.map} +0 -0
- /package/dist/{chunk-RTFAZOIR.js.map → chunk-3VVH6ZQW.js.map} +0 -0
- /package/dist/{chunk-FIVDN2SM.js.map → chunk-4RGWCRIJ.js.map} +0 -0
- /package/dist/{chunk-AGJH5ISO.js.map → chunk-5KBNNUXS.js.map} +0 -0
- /package/dist/{chunk-X7RLU5CR.js.map → chunk-66HROLW5.js.map} +0 -0
- /package/dist/{chunk-TYF3D4MS.js.map → chunk-6ZNIUGXB.js.map} +0 -0
- /package/dist/{chunk-UPIBE2DK.js.map → chunk-ADUAF5EV.js.map} +0 -0
- /package/dist/{chunk-LLONI6PY.js.map → chunk-AFWEUTMS.js.map} +0 -0
- /package/dist/{chunk-ZUDM75KG.js.map → chunk-DCWIQFNA.js.map} +0 -0
- /package/dist/{chunk-OMKUJTVJ.js.map → chunk-DO7MDZMU.js.map} +0 -0
- /package/dist/{chunk-LXIEXSHU.js.map → chunk-E6BOD3UJ.js.map} +0 -0
- /package/dist/{chunk-OBXTMFZQ.js.map → chunk-ECZPWSB5.js.map} +0 -0
- /package/dist/{chunk-CWE74HRG.js.map → chunk-FM7KLRKN.js.map} +0 -0
- /package/dist/{chunk-BFVPIKDY.js.map → chunk-GCOSQBQ2.js.map} +0 -0
- /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
- /package/dist/{chunk-CP2NZQLT.js.map → chunk-KYH53EFD.js.map} +0 -0
- /package/dist/{chunk-LQ6JI4VH.js.map → chunk-LD53WPMU.js.map} +0 -0
- /package/dist/{chunk-SB6CQTKP.js.map → chunk-MCV2DD6F.js.map} +0 -0
- /package/dist/{chunk-3FC6LW6T.js.map → chunk-MRFVMSTR.js.map} +0 -0
- /package/dist/{chunk-WDXCNJSF.js.map → chunk-MXZWKOOR.js.map} +0 -0
- /package/dist/{chunk-NSVXK7M5.js.map → chunk-NEILAC3H.js.map} +0 -0
- /package/dist/{chunk-RGNEARXW.js.map → chunk-NUZ3N445.js.map} +0 -0
- /package/dist/{chunk-WROKD3XC.js.map → chunk-O4ZIV62T.js.map} +0 -0
- /package/dist/{chunk-PHZKALOE.js.map → chunk-QFG23CRE.js.map} +0 -0
- /package/dist/{chunk-A4HH2EWA.js.map → chunk-QVG4LAQO.js.map} +0 -0
- /package/dist/{chunk-54PVJDO5.js.map → chunk-SAC5V6JT.js.map} +0 -0
- /package/dist/{chunk-XKUKJIOY.js.map → chunk-SEK4XIDN.js.map} +0 -0
- /package/dist/{chunk-5CEJH5ZN.js.map → chunk-TAPSNIMA.js.map} +0 -0
- /package/dist/{chunk-2N6UNJSX.js.map → chunk-U2OBZSGG.js.map} +0 -0
- /package/dist/{chunk-BKAMHZYR.js.map → chunk-UOCLRM74.js.map} +0 -0
- /package/dist/{chunk-Z7KILAOU.js.map → chunk-V56S3A23.js.map} +0 -0
- /package/dist/{chunk-UD5OKH4J.js.map → chunk-W4ELOUNX.js.map} +0 -0
- /package/dist/{chunk-GR77Z2BM.js.map → chunk-WIKNVFBS.js.map} +0 -0
- /package/dist/{chunk-WRFKZEO6.js.map → chunk-YQGHW5ML.js.map} +0 -0
- /package/dist/{chunk-67MMWC74.js.map → chunk-ZLXGFAML.js.map} +0 -0
package/src/orchestrator.ts
CHANGED
|
@@ -82,15 +82,7 @@ import {
|
|
|
82
82
|
fallbackLlmRuntimeContextFromConfig,
|
|
83
83
|
gatewayTaskChainOptions,
|
|
84
84
|
} from "./fallback-llm.js";
|
|
85
|
-
import {
|
|
86
|
-
ensureDaySummaryCron,
|
|
87
|
-
ensureNightlyGovernanceCron,
|
|
88
|
-
ensureProceduralMiningCron,
|
|
89
|
-
ensureContradictionScanCron,
|
|
90
|
-
ensurePatternReinforcementCron,
|
|
91
|
-
ensureGraphEdgeDecayCron,
|
|
92
|
-
graphEdgeDecayCadenceToCronExpr,
|
|
93
|
-
} from "./maintenance/memory-governance-cron.js";
|
|
85
|
+
import { MaintenanceScheduler } from "./orchestration/maintenance.js";
|
|
94
86
|
import {
|
|
95
87
|
runLiveConnectorsOnce,
|
|
96
88
|
type LiveConnectorsRunSummary,
|
|
@@ -122,7 +114,7 @@ import {
|
|
|
122
114
|
normalizeSupersessionKey,
|
|
123
115
|
shouldFilterSupersededFromRecall,
|
|
124
116
|
} from "./temporal-supersession.js";
|
|
125
|
-
import { isValidAsOf } from "./temporal-validity.js";
|
|
117
|
+
import { isValidAsOf, isValidityExpiredNow } from "./temporal-validity.js";
|
|
126
118
|
import { RelevanceStore } from "./relevance.js";
|
|
127
119
|
import { NegativeExampleStore } from "./negative.js";
|
|
128
120
|
import {
|
|
@@ -315,9 +307,6 @@ import {
|
|
|
315
307
|
} from "./namespaces/catalog.js";
|
|
316
308
|
import {
|
|
317
309
|
planNamespaceMaintenance,
|
|
318
|
-
runNamespaceMaintenanceBatchPlan,
|
|
319
|
-
type NamespaceMaintenancePlan,
|
|
320
|
-
type NamespaceMaintenanceSkipReason,
|
|
321
310
|
type NamespaceMaintenanceSummary,
|
|
322
311
|
} from "./maintenance/namespace-planner.js";
|
|
323
312
|
import {
|
|
@@ -1804,13 +1793,6 @@ export function resolvePersistedMemoryRelativePath(options: {
|
|
|
1804
1793
|
return path.join(subtree, `${options.memoryId}.md`);
|
|
1805
1794
|
}
|
|
1806
1795
|
|
|
1807
|
-
function qmdMaintenanceSkipReasonForError(error: unknown): NamespaceMaintenanceSkipReason | null {
|
|
1808
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1809
|
-
return /^QMD (?:update|embed) skipped by .*min-interval gate$/.test(message)
|
|
1810
|
-
? "throttled"
|
|
1811
|
-
: null;
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
1796
|
export class Orchestrator {
|
|
1815
1797
|
readonly storage: StorageManager;
|
|
1816
1798
|
private readonly storageRouter: NamespaceStorageRouter;
|
|
@@ -1820,6 +1802,12 @@ export class Orchestrator {
|
|
|
1820
1802
|
private namespaceStorageDirHintsLoaded = false;
|
|
1821
1803
|
private readonly namespaceSearchRouter: NamespaceSearchRouter;
|
|
1822
1804
|
qmd: SearchBackend;
|
|
1805
|
+
/**
|
|
1806
|
+
* Maintenance scheduler (issue #1526 PR1). Owns cron auto-registration,
|
|
1807
|
+
* debounced QMD index maintenance, and consolidation scheduling. Job
|
|
1808
|
+
* runners (consolidation/pattern-reinforcement/governance) stay here.
|
|
1809
|
+
*/
|
|
1810
|
+
readonly maintenanceScheduler: MaintenanceScheduler;
|
|
1823
1811
|
private readonly conversationQmd?: ConversationQmdRuntime;
|
|
1824
1812
|
private readonly conversationFaiss?: ReturnType<
|
|
1825
1813
|
typeof createConversationIndexRuntime
|
|
@@ -1947,19 +1935,11 @@ export class Orchestrator {
|
|
|
1947
1935
|
private queueProcessing = false;
|
|
1948
1936
|
private heartbeatObserverChains = new Map<string, Promise<void>>();
|
|
1949
1937
|
private recentExtractionFingerprints = new Map<string, number>();
|
|
1950
|
-
private nonZeroExtractionsSinceConsolidation = 0;
|
|
1951
|
-
private lastConsolidationRunAtMs = 0;
|
|
1952
|
-
private consolidationInFlight = false;
|
|
1953
1938
|
private readonly consolidationObservers = new Set<
|
|
1954
1939
|
(observation: ConsolidationObservation) => Promise<void> | void
|
|
1955
1940
|
>();
|
|
1956
|
-
private qmdMaintenanceTimer: NodeJS.Timeout | null = null;
|
|
1957
1941
|
private wearablesServiceInstance: WearablesService | null = null;
|
|
1958
1942
|
private wearablesAutoSyncHandle: { stop(): Promise<void> } | null = null;
|
|
1959
|
-
private qmdMaintenancePending = false;
|
|
1960
|
-
private qmdMaintenanceInFlight = false;
|
|
1961
|
-
private lastQmdEmbedAtMs = 0;
|
|
1962
|
-
private lastQmdEmbedAtMsByNamespace = new Map<string, number>();
|
|
1963
1943
|
private lastQmdReprobeAtMs = 0;
|
|
1964
1944
|
private tierMigrationInFlight = false;
|
|
1965
1945
|
private lastTierMigrationRunAtMs = 0;
|
|
@@ -2044,11 +2024,7 @@ export class Orchestrator {
|
|
|
2044
2024
|
await this.wearablesAutoSyncHandle.stop();
|
|
2045
2025
|
this.wearablesAutoSyncHandle = null;
|
|
2046
2026
|
}
|
|
2047
|
-
|
|
2048
|
-
clearTimeout(this.qmdMaintenanceTimer);
|
|
2049
|
-
this.qmdMaintenanceTimer = null;
|
|
2050
|
-
}
|
|
2051
|
-
this.qmdMaintenancePending = false;
|
|
2027
|
+
this.maintenanceScheduler.dispose();
|
|
2052
2028
|
await this.namespaceSearchRouter.dispose();
|
|
2053
2029
|
await this.disposeSearchBackendIfNeeded();
|
|
2054
2030
|
if (this.conversationQmd && this.conversationQmd !== this.qmd) {
|
|
@@ -2462,22 +2438,6 @@ export class Orchestrator {
|
|
|
2462
2438
|
}
|
|
2463
2439
|
}
|
|
2464
2440
|
|
|
2465
|
-
/**
|
|
2466
|
-
* Shared namespace maintenance planner (issue #1500). This extends the
|
|
2467
|
-
* #1499 catalog-union QMD helper into a reusable contract: configured
|
|
2468
|
-
* namespaces are always considered, dynamic catalog namespaces are admitted
|
|
2469
|
-
* only when their live router root still matches real memory data, and branch
|
|
2470
|
-
* namespaces are opt-in. Recurring jobs use the per-cycle budget; startup and
|
|
2471
|
-
* recovery discovery paths use the same safety filters without that cycle
|
|
2472
|
-
* budget so every live namespace is ensured/synced.
|
|
2473
|
-
*/
|
|
2474
|
-
private async namespaceMaintenancePlan(jobName: string): Promise<NamespaceMaintenancePlan> {
|
|
2475
|
-
return planNamespaceMaintenance(this.config, {
|
|
2476
|
-
jobName,
|
|
2477
|
-
catalog: this.namespaceCatalog,
|
|
2478
|
-
});
|
|
2479
|
-
}
|
|
2480
|
-
|
|
2481
2441
|
private async maintenanceNamespaces(
|
|
2482
2442
|
jobName = "qmd",
|
|
2483
2443
|
budgetMode: "cycle" | "unbounded" = "unbounded",
|
|
@@ -2760,6 +2720,16 @@ export class Orchestrator {
|
|
|
2760
2720
|
// and writes will throw SecureStoreLockedError via resolveWriteKey().
|
|
2761
2721
|
}
|
|
2762
2722
|
this.qmd = createSearchBackend(config);
|
|
2723
|
+
this.maintenanceScheduler = new MaintenanceScheduler({
|
|
2724
|
+
config,
|
|
2725
|
+
// Live accessor: the orchestrator reassigns this.qmd to NoopSearchBackend
|
|
2726
|
+
// after construction when the collection is missing (initialize /
|
|
2727
|
+
// startupSearchSync); the scheduler must read the current backend so
|
|
2728
|
+
// debounced maintenance never runs against a stale/disposed one.
|
|
2729
|
+
getQmd: () => this.qmd,
|
|
2730
|
+
namespaceSearchRouter: this.namespaceSearchRouter,
|
|
2731
|
+
namespaceCatalog: this.namespaceCatalog,
|
|
2732
|
+
});
|
|
2763
2733
|
const conversationIndexRuntime = createConversationIndexRuntime(config, {
|
|
2764
2734
|
getQmd: () => this.conversationQmd,
|
|
2765
2735
|
getFaiss: () => this.conversationFaiss,
|
|
@@ -3540,56 +3510,10 @@ export class Orchestrator {
|
|
|
3540
3510
|
// rely on cron jobs being registered when it resolves. Without this, the
|
|
3541
3511
|
// fire-and-forget pattern lets deferredReady settle while cron writes are
|
|
3542
3512
|
// still in flight. Errors are non-fatal — catch individually.
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
log.debug(`day-summary cron auto-register failed (non-fatal): ${err}`);
|
|
3548
|
-
}
|
|
3549
|
-
}
|
|
3550
|
-
if (this.config.nightlyGovernanceCronAutoRegister) {
|
|
3551
|
-
try {
|
|
3552
|
-
await this.autoRegisterNightlyGovernanceCron();
|
|
3553
|
-
} catch (err) {
|
|
3554
|
-
log.debug(`nightly governance cron auto-register failed (non-fatal): ${err}`);
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
|
-
if (this.config.procedural?.proceduralMiningCronAutoRegister) {
|
|
3558
|
-
try {
|
|
3559
|
-
await this.autoRegisterProceduralMiningCron();
|
|
3560
|
-
} catch (err) {
|
|
3561
|
-
log.debug(`procedural mining cron auto-register failed (non-fatal): ${err}`);
|
|
3562
|
-
}
|
|
3563
|
-
}
|
|
3564
|
-
|
|
3565
|
-
// Auto-register contradiction scan cron (gated by config)
|
|
3566
|
-
if (this.config.contradictionScan?.enabled) {
|
|
3567
|
-
try {
|
|
3568
|
-
await this.autoRegisterContradictionScanCron();
|
|
3569
|
-
} catch (err) {
|
|
3570
|
-
log.debug(`contradiction scan cron auto-register failed (non-fatal): ${err}`);
|
|
3571
|
-
}
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
|
-
// Auto-register pattern-reinforcement cron (issue #687 PR 2/4).
|
|
3575
|
-
// Gated on the feature flag so memory-only users without the
|
|
3576
|
-
// cron daemon installed never see a stray jobs.json mutation.
|
|
3577
|
-
if (this.config.patternReinforcementEnabled) {
|
|
3578
|
-
try {
|
|
3579
|
-
await this.autoRegisterPatternReinforcementCron();
|
|
3580
|
-
} catch (err) {
|
|
3581
|
-
log.debug(`pattern reinforcement cron auto-register failed (non-fatal): ${err}`);
|
|
3582
|
-
}
|
|
3583
|
-
}
|
|
3584
|
-
|
|
3585
|
-
// Auto-register graph-edge decay cron (gated by config — issue #681 PR 2/3).
|
|
3586
|
-
if (this.config.graphEdgeDecayEnabled) {
|
|
3587
|
-
try {
|
|
3588
|
-
await this.autoRegisterGraphEdgeDecayCron();
|
|
3589
|
-
} catch (err) {
|
|
3590
|
-
log.debug(`graph edge decay cron auto-register failed (non-fatal): ${err}`);
|
|
3591
|
-
}
|
|
3592
|
-
}
|
|
3513
|
+
// Auto-register every cron job in one pass. Each registration is gated
|
|
3514
|
+
// by its config flag inside the scheduler and individually non-fatal
|
|
3515
|
+
// (issue #1526 PR1 — moved to MaintenanceScheduler).
|
|
3516
|
+
await this.maintenanceScheduler.autoRegisterCrons(signal);
|
|
3593
3517
|
|
|
3594
3518
|
// First-start lifecycle migration (issue #686 retention-completion).
|
|
3595
3519
|
// When lifecyclePolicyEnabled is true and the memoryDir has never been
|
|
@@ -3819,168 +3743,6 @@ export class Orchestrator {
|
|
|
3819
3743
|
return true;
|
|
3820
3744
|
}
|
|
3821
3745
|
|
|
3822
|
-
/**
|
|
3823
|
-
* Auto-register the engram-day-summary cron job in OpenClaw.
|
|
3824
|
-
* Reconciles model and timezone on every startup so config changes propagate.
|
|
3825
|
-
* Fire-and-forget — never blocks init or crashes on failure.
|
|
3826
|
-
* Issues #1474, #1475.
|
|
3827
|
-
*/
|
|
3828
|
-
private async autoRegisterDaySummaryCron(): Promise<void> {
|
|
3829
|
-
const home = resolveHomeDir();
|
|
3830
|
-
const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
|
|
3831
|
-
|
|
3832
|
-
try {
|
|
3833
|
-
if (!existsSync(jobsPath)) {
|
|
3834
|
-
log.debug(
|
|
3835
|
-
"day-summary cron: jobs.json not found, skipping auto-register",
|
|
3836
|
-
);
|
|
3837
|
-
return;
|
|
3838
|
-
}
|
|
3839
|
-
|
|
3840
|
-
// Resolve an OpenClaw cron-routing model only in gateway mode. In plugin
|
|
3841
|
-
// mode, summaryModel is a direct-client model id for Remnic's own LLM
|
|
3842
|
-
// calls and may be unroutable as an OpenClaw agentTurn model.
|
|
3843
|
-
const rawSummaryModel = this.config.summaryModel;
|
|
3844
|
-
const taskPrimary = this.config.taskModelChain?.primary;
|
|
3845
|
-
const isGateway = this.config.modelSource === "gateway";
|
|
3846
|
-
const model = isGateway ? (rawSummaryModel || taskPrimary || undefined) : undefined;
|
|
3847
|
-
// Attach task-chain fallbacks only when the model matches the task-chain
|
|
3848
|
-
// primary. If summaryModel is a distinct override, its fallbacks would
|
|
3849
|
-
// be unrelated to the task chain. Also append gateway default models as
|
|
3850
|
-
// tail fallbacks (de-duped) so a task-chain outage doesn't stop the cron
|
|
3851
|
-
// before reaching the gateway default chain. Mirrors hourly cron pattern.
|
|
3852
|
-
const fallbacks: string[] = [];
|
|
3853
|
-
if (model && taskPrimary && model === taskPrimary) {
|
|
3854
|
-
const seen = new Set<string>(model ? [model] : []);
|
|
3855
|
-
const addUnique = (value: string | undefined) => {
|
|
3856
|
-
if (typeof value !== "string") return;
|
|
3857
|
-
const trimmed = value.trim();
|
|
3858
|
-
if (trimmed.length > 0 && !seen.has(trimmed)) {
|
|
3859
|
-
seen.add(trimmed);
|
|
3860
|
-
fallbacks.push(trimmed);
|
|
3861
|
-
}
|
|
3862
|
-
};
|
|
3863
|
-
for (const fb of this.config.taskModelChain?.fallbacks ?? []) addUnique(fb);
|
|
3864
|
-
const gwDefaults = this.config.gatewayConfig?.agents?.defaults?.model;
|
|
3865
|
-
addUnique(gwDefaults?.primary);
|
|
3866
|
-
if (Array.isArray(gwDefaults?.fallbacks)) {
|
|
3867
|
-
for (const fb of gwDefaults.fallbacks) addUnique(fb);
|
|
3868
|
-
}
|
|
3869
|
-
}
|
|
3870
|
-
|
|
3871
|
-
// Resolve timezone: configurable override, then server default
|
|
3872
|
-
const timezone = this.config.daySummaryTimezone
|
|
3873
|
-
|| Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
3874
|
-
|
|
3875
|
-
const result = await ensureDaySummaryCron(jobsPath, {
|
|
3876
|
-
timezone,
|
|
3877
|
-
...(model ? { model } : {}),
|
|
3878
|
-
...(fallbacks.length > 0 ? { fallbacks } : {}),
|
|
3879
|
-
});
|
|
3880
|
-
if (result.created) {
|
|
3881
|
-
log.info(
|
|
3882
|
-
`day-summary cron auto-registered (${result.jobId}, 23:47 ${timezone}${model ? `, model: ${model}` : ""})`,
|
|
3883
|
-
);
|
|
3884
|
-
} else if (result.updated) {
|
|
3885
|
-
log.info(
|
|
3886
|
-
`day-summary cron reconciled (${result.jobId}, timezone: ${timezone}${model ? `, model: ${model}` : ""})`,
|
|
3887
|
-
);
|
|
3888
|
-
} else {
|
|
3889
|
-
log.debug("day-summary cron already up to date");
|
|
3890
|
-
}
|
|
3891
|
-
} catch (err) {
|
|
3892
|
-
log.debug(`day-summary cron auto-register error: ${err}`);
|
|
3893
|
-
}
|
|
3894
|
-
}
|
|
3895
|
-
|
|
3896
|
-
private async autoRegisterNightlyGovernanceCron(): Promise<void> {
|
|
3897
|
-
const home = resolveHomeDir();
|
|
3898
|
-
const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
|
|
3899
|
-
|
|
3900
|
-
try {
|
|
3901
|
-
if (!existsSync(jobsPath)) {
|
|
3902
|
-
log.debug("nightly governance cron: jobs.json not found, skipping auto-register");
|
|
3903
|
-
return;
|
|
3904
|
-
}
|
|
3905
|
-
|
|
3906
|
-
const created = await ensureNightlyGovernanceCron(jobsPath, {
|
|
3907
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
3908
|
-
});
|
|
3909
|
-
if (created.created) {
|
|
3910
|
-
log.info(
|
|
3911
|
-
`nightly governance cron auto-registered (${created.jobId}, 02:23 ${Intl.DateTimeFormat().resolvedOptions().timeZone})`,
|
|
3912
|
-
);
|
|
3913
|
-
} else {
|
|
3914
|
-
log.debug("nightly governance cron already exists, skipping auto-register");
|
|
3915
|
-
}
|
|
3916
|
-
} catch (err) {
|
|
3917
|
-
log.debug(`nightly governance cron auto-register error: ${err}`);
|
|
3918
|
-
}
|
|
3919
|
-
}
|
|
3920
|
-
|
|
3921
|
-
private async autoRegisterProceduralMiningCron(): Promise<void> {
|
|
3922
|
-
const home = resolveHomeDir();
|
|
3923
|
-
const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
|
|
3924
|
-
try {
|
|
3925
|
-
if (!existsSync(jobsPath)) {
|
|
3926
|
-
log.debug("procedural mining cron: jobs.json not found, skipping auto-register");
|
|
3927
|
-
return;
|
|
3928
|
-
}
|
|
3929
|
-
const created = await ensureProceduralMiningCron(jobsPath, {
|
|
3930
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
3931
|
-
});
|
|
3932
|
-
if (created.created) {
|
|
3933
|
-
log.info(`procedural mining cron auto-registered (${created.jobId})`);
|
|
3934
|
-
} else {
|
|
3935
|
-
log.debug("procedural mining cron already exists, skipping auto-register");
|
|
3936
|
-
}
|
|
3937
|
-
} catch (err) {
|
|
3938
|
-
log.debug(`procedural mining cron auto-register error: ${err}`);
|
|
3939
|
-
}
|
|
3940
|
-
}
|
|
3941
|
-
|
|
3942
|
-
private async autoRegisterContradictionScanCron(): Promise<void> {
|
|
3943
|
-
const home = resolveHomeDir();
|
|
3944
|
-
const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
|
|
3945
|
-
try {
|
|
3946
|
-
if (!existsSync(jobsPath)) {
|
|
3947
|
-
log.debug("contradiction scan cron: jobs.json not found, skipping auto-register");
|
|
3948
|
-
return;
|
|
3949
|
-
}
|
|
3950
|
-
const created = await ensureContradictionScanCron(jobsPath, {
|
|
3951
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
3952
|
-
});
|
|
3953
|
-
if (created.created) {
|
|
3954
|
-
log.info(`contradiction scan cron auto-registered (${created.jobId})`);
|
|
3955
|
-
} else {
|
|
3956
|
-
log.debug("contradiction scan cron already exists, skipping auto-register");
|
|
3957
|
-
}
|
|
3958
|
-
} catch (err) {
|
|
3959
|
-
log.debug(`contradiction scan cron auto-register error: ${err}`);
|
|
3960
|
-
}
|
|
3961
|
-
}
|
|
3962
|
-
|
|
3963
|
-
private async autoRegisterPatternReinforcementCron(): Promise<void> {
|
|
3964
|
-
const home = resolveHomeDir();
|
|
3965
|
-
const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
|
|
3966
|
-
try {
|
|
3967
|
-
if (!existsSync(jobsPath)) {
|
|
3968
|
-
log.debug("pattern reinforcement cron: jobs.json not found, skipping auto-register");
|
|
3969
|
-
return;
|
|
3970
|
-
}
|
|
3971
|
-
const created = await ensurePatternReinforcementCron(jobsPath, {
|
|
3972
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
3973
|
-
});
|
|
3974
|
-
if (created.created) {
|
|
3975
|
-
log.info(`pattern reinforcement cron auto-registered (${created.jobId})`);
|
|
3976
|
-
} else {
|
|
3977
|
-
log.debug("pattern reinforcement cron already exists, skipping auto-register");
|
|
3978
|
-
}
|
|
3979
|
-
} catch (err) {
|
|
3980
|
-
log.debug(`pattern reinforcement cron auto-register error: ${err}`);
|
|
3981
|
-
}
|
|
3982
|
-
}
|
|
3983
|
-
|
|
3984
3746
|
/**
|
|
3985
3747
|
* Run the pattern-reinforcement maintenance job (issue #687 PR 2/4).
|
|
3986
3748
|
*
|
|
@@ -4142,29 +3904,6 @@ export class Orchestrator {
|
|
|
4142
3904
|
);
|
|
4143
3905
|
}
|
|
4144
3906
|
|
|
4145
|
-
private async autoRegisterGraphEdgeDecayCron(): Promise<void> {
|
|
4146
|
-
const home = resolveHomeDir();
|
|
4147
|
-
const jobsPath = path.join(home, ".openclaw", "cron", "jobs.json");
|
|
4148
|
-
try {
|
|
4149
|
-
if (!existsSync(jobsPath)) {
|
|
4150
|
-
log.debug("graph edge decay cron: jobs.json not found, skipping auto-register");
|
|
4151
|
-
return;
|
|
4152
|
-
}
|
|
4153
|
-
const scheduleExpr = graphEdgeDecayCadenceToCronExpr(this.config.graphEdgeDecayCadenceMs);
|
|
4154
|
-
const created = await ensureGraphEdgeDecayCron(jobsPath, {
|
|
4155
|
-
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
4156
|
-
scheduleExpr,
|
|
4157
|
-
});
|
|
4158
|
-
if (created.created) {
|
|
4159
|
-
log.info(`graph edge decay cron auto-registered (${created.jobId}, ${scheduleExpr})`);
|
|
4160
|
-
} else {
|
|
4161
|
-
log.debug("graph edge decay cron already exists, skipping auto-register");
|
|
4162
|
-
}
|
|
4163
|
-
} catch (err) {
|
|
4164
|
-
log.debug(`graph edge decay cron auto-register error: ${err}`);
|
|
4165
|
-
}
|
|
4166
|
-
}
|
|
4167
|
-
|
|
4168
3907
|
async runLiveConnectors(options: {
|
|
4169
3908
|
force?: boolean;
|
|
4170
3909
|
abortSignal?: AbortSignal;
|
|
@@ -4718,7 +4457,7 @@ export class Orchestrator {
|
|
|
4718
4457
|
|
|
4719
4458
|
async waitForConsolidationIdle(timeoutMs: number = 60_000): Promise<boolean> {
|
|
4720
4459
|
const started = Date.now();
|
|
4721
|
-
while (this.
|
|
4460
|
+
while (this.maintenanceScheduler.isConsolidationInFlight()) {
|
|
4722
4461
|
if (Date.now() - started > timeoutMs) {
|
|
4723
4462
|
log.warn(`waitForConsolidationIdle timed out after ${timeoutMs}ms`);
|
|
4724
4463
|
return false;
|
|
@@ -13657,7 +13396,8 @@ export class Orchestrator {
|
|
|
13657
13396
|
// Check if consolidation is needed (debounced + non-zero gated).
|
|
13658
13397
|
const nonZeroExtraction = durableOutputCount > 0;
|
|
13659
13398
|
try {
|
|
13660
|
-
|
|
13399
|
+
// The increment of nonZeroExtractionsSinceConsolidation moved into
|
|
13400
|
+
// the scheduler with the rest of the cadence state (issue #1526 PR1).
|
|
13661
13401
|
this.maybeScheduleConsolidation(nonZeroExtraction);
|
|
13662
13402
|
} catch (err) {
|
|
13663
13403
|
log.warn(
|
|
@@ -13928,148 +13668,22 @@ export class Orchestrator {
|
|
|
13928
13668
|
}
|
|
13929
13669
|
|
|
13930
13670
|
private maybeScheduleConsolidation(nonZeroExtraction: boolean): void {
|
|
13931
|
-
|
|
13932
|
-
|
|
13933
|
-
|
|
13934
|
-
|
|
13935
|
-
)
|
|
13936
|
-
return;
|
|
13937
|
-
|
|
13938
|
-
const now = Date.now();
|
|
13939
|
-
if (
|
|
13940
|
-
now - this.lastConsolidationRunAtMs <
|
|
13941
|
-
this.config.consolidationMinIntervalMs
|
|
13942
|
-
)
|
|
13943
|
-
return;
|
|
13944
|
-
if (this.consolidationInFlight) return;
|
|
13945
|
-
|
|
13946
|
-
this.consolidationInFlight = true;
|
|
13947
|
-
this.lastConsolidationRunAtMs = now;
|
|
13948
|
-
this.nonZeroExtractionsSinceConsolidation = 0;
|
|
13949
|
-
this.runConsolidation()
|
|
13950
|
-
.catch((err) => log.error("background consolidation failed", err))
|
|
13951
|
-
.finally(() => {
|
|
13952
|
-
this.consolidationInFlight = false;
|
|
13953
|
-
});
|
|
13671
|
+
this.maintenanceScheduler.maybeScheduleConsolidation(
|
|
13672
|
+
nonZeroExtraction,
|
|
13673
|
+
() => this.runConsolidation(),
|
|
13674
|
+
);
|
|
13954
13675
|
}
|
|
13955
13676
|
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
if (!this.config.qmdMaintenanceEnabled) return;
|
|
13959
|
-
|
|
13960
|
-
this.qmdMaintenancePending = true;
|
|
13961
|
-
if (this.qmdMaintenanceTimer) return;
|
|
13962
|
-
|
|
13963
|
-
this.qmdMaintenanceTimer = setTimeout(() => {
|
|
13964
|
-
this.qmdMaintenanceTimer = null;
|
|
13965
|
-
this.runQmdMaintenance().catch((err) =>
|
|
13966
|
-
log.debug(`background qmd maintenance failed: ${err}`),
|
|
13967
|
-
);
|
|
13968
|
-
}, this.config.qmdMaintenanceDebounceMs);
|
|
13677
|
+
requestQmdMaintenance(): void {
|
|
13678
|
+
this.maintenanceScheduler.requestQmdMaintenanceForTool("__internal__");
|
|
13969
13679
|
}
|
|
13970
13680
|
|
|
13971
13681
|
/**
|
|
13972
13682
|
* Public entrypoint for tool-driven QMD maintenance requests.
|
|
13973
|
-
*
|
|
13683
|
+
* Delegates to the maintenance scheduler (issue #1526 PR1).
|
|
13974
13684
|
*/
|
|
13975
13685
|
requestQmdMaintenanceForTool(reason: string): void {
|
|
13976
|
-
|
|
13977
|
-
this.requestQmdMaintenance();
|
|
13978
|
-
} catch (err) {
|
|
13979
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
13980
|
-
log.warn(`qmd maintenance request failed (${reason}): ${msg}`);
|
|
13981
|
-
}
|
|
13982
|
-
}
|
|
13983
|
-
|
|
13984
|
-
private async runQmdMaintenance(): Promise<void> {
|
|
13985
|
-
if (this.qmdMaintenanceInFlight) return;
|
|
13986
|
-
if (!this.qmdMaintenancePending) return;
|
|
13987
|
-
this.qmdMaintenanceInFlight = true;
|
|
13988
|
-
this.qmdMaintenancePending = false;
|
|
13989
|
-
|
|
13990
|
-
try {
|
|
13991
|
-
if (this.config.namespacesEnabled) {
|
|
13992
|
-
// Include cataloged dynamic namespaces, not just the configured set
|
|
13993
|
-
// (NGnei), but run through the namespace-aware maintenance planner so
|
|
13994
|
-
// each namespace is budgeted, lock-protected, and status-recorded
|
|
13995
|
-
// independently (issue #1500).
|
|
13996
|
-
const plan = await this.namespaceMaintenancePlan("qmd");
|
|
13997
|
-
const now = Date.now();
|
|
13998
|
-
const lastEmbedAtByNamespace =
|
|
13999
|
-
this.lastQmdEmbedAtMsByNamespace ?? (this.lastQmdEmbedAtMsByNamespace = new Map());
|
|
14000
|
-
const dueEmbedNamespaces = (namespaces: string[]): string[] => {
|
|
14001
|
-
if (!this.config.qmdAutoEmbedEnabled) return [];
|
|
14002
|
-
return namespaces.filter(
|
|
14003
|
-
(namespace) =>
|
|
14004
|
-
now - (lastEmbedAtByNamespace.get(namespace) ?? 0) >= this.config.qmdEmbedMinIntervalMs,
|
|
14005
|
-
);
|
|
14006
|
-
};
|
|
14007
|
-
const markEmbedded = (namespaces: string[]): void => {
|
|
14008
|
-
if (namespaces.length === 0) return;
|
|
14009
|
-
for (const namespace of namespaces) {
|
|
14010
|
-
lastEmbedAtByNamespace.set(namespace, now);
|
|
14011
|
-
}
|
|
14012
|
-
this.lastQmdEmbedAtMs = now;
|
|
14013
|
-
};
|
|
14014
|
-
await runNamespaceMaintenanceBatchPlan(
|
|
14015
|
-
this.config,
|
|
14016
|
-
plan,
|
|
14017
|
-
async (candidates) => {
|
|
14018
|
-
const namespaces = candidates.map((candidate) => candidate.namespace);
|
|
14019
|
-
const embedNamespaces = dueEmbedNamespaces(namespaces);
|
|
14020
|
-
let result: Awaited<ReturnType<NamespaceSearchRouter["updateNamespacesDetailed"]>>;
|
|
14021
|
-
try {
|
|
14022
|
-
result = await this.namespaceSearchRouter.updateNamespacesDetailed(
|
|
14023
|
-
namespaces,
|
|
14024
|
-
undefined,
|
|
14025
|
-
{ strict: true },
|
|
14026
|
-
);
|
|
14027
|
-
} catch (error) {
|
|
14028
|
-
if (
|
|
14029
|
-
embedNamespaces.length > 0 &&
|
|
14030
|
-
qmdMaintenanceSkipReasonForError(error) === "throttled"
|
|
14031
|
-
) {
|
|
14032
|
-
await this.namespaceSearchRouter.embedNamespaces(embedNamespaces, { strict: true });
|
|
14033
|
-
markEmbedded(embedNamespaces);
|
|
14034
|
-
}
|
|
14035
|
-
throw error;
|
|
14036
|
-
}
|
|
14037
|
-
if (result.backendCount <= 0) {
|
|
14038
|
-
throw new Error("no eligible QMD backend for selected namespaces");
|
|
14039
|
-
}
|
|
14040
|
-
if (result.eligibleNamespaces.length !== namespaces.length) {
|
|
14041
|
-
const eligible = new Set(result.eligibleNamespaces);
|
|
14042
|
-
const missing = namespaces.filter((namespace) => !eligible.has(namespace));
|
|
14043
|
-
throw new Error(`QMD backend ineligible for selected namespaces (${missing.length})`);
|
|
14044
|
-
}
|
|
14045
|
-
if (embedNamespaces.length > 0) {
|
|
14046
|
-
await this.namespaceSearchRouter.embedNamespaces(embedNamespaces, { strict: true });
|
|
14047
|
-
markEmbedded(embedNamespaces);
|
|
14048
|
-
}
|
|
14049
|
-
return { itemCount: result.backendCount };
|
|
14050
|
-
},
|
|
14051
|
-
this.namespaceCatalog,
|
|
14052
|
-
{
|
|
14053
|
-
skipReasonForError: qmdMaintenanceSkipReasonForError,
|
|
14054
|
-
},
|
|
14055
|
-
);
|
|
14056
|
-
} else {
|
|
14057
|
-
await this.qmd.update();
|
|
14058
|
-
const now = Date.now();
|
|
14059
|
-
if (
|
|
14060
|
-
this.config.qmdAutoEmbedEnabled &&
|
|
14061
|
-
now - this.lastQmdEmbedAtMs >= this.config.qmdEmbedMinIntervalMs
|
|
14062
|
-
) {
|
|
14063
|
-
await this.qmd.embed();
|
|
14064
|
-
this.lastQmdEmbedAtMs = now;
|
|
14065
|
-
}
|
|
14066
|
-
}
|
|
14067
|
-
} finally {
|
|
14068
|
-
this.qmdMaintenanceInFlight = false;
|
|
14069
|
-
if (this.qmdMaintenancePending) {
|
|
14070
|
-
this.requestQmdMaintenance();
|
|
14071
|
-
}
|
|
14072
|
-
}
|
|
13686
|
+
this.maintenanceScheduler.requestQmdMaintenanceForTool(reason);
|
|
14073
13687
|
}
|
|
14074
13688
|
|
|
14075
13689
|
private async persistExtraction(
|
|
@@ -19339,6 +18953,7 @@ export class Orchestrator {
|
|
|
19339
18953
|
): QmdSearchResult[] {
|
|
19340
18954
|
let lifecycleFilteredCount = 0;
|
|
19341
18955
|
let temporalSupersededFilteredCount = 0;
|
|
18956
|
+
let biTemporalExpiredFilteredCount = 0;
|
|
19342
18957
|
let dedicatedSurfaceFilteredCount = 0;
|
|
19343
18958
|
let forgottenFilteredCount = 0;
|
|
19344
18959
|
let blockedPathFilteredCount = 0;
|
|
@@ -19401,6 +19016,26 @@ export class Orchestrator {
|
|
|
19401
19016
|
temporalSupersededFilteredCount += 1;
|
|
19402
19017
|
continue;
|
|
19403
19018
|
}
|
|
19019
|
+
// Bi-temporal INJECTION filter (issue #1578): when the master gate
|
|
19020
|
+
// is on and the caller did NOT pin `as_of`, drop facts whose event-
|
|
19021
|
+
// time interval has ended before now. This lives ONLY in the recall
|
|
19022
|
+
// injection path (filterSearchResultsByRecallSafety) — explicit
|
|
19023
|
+
// search (access-service.memorySearch → searchAcrossNamespaces /
|
|
19024
|
+
// qmd.search) never routes through here, so expired-validity facts
|
|
19025
|
+
// remain findable by memory_search and `as_of` queries (escape
|
|
19026
|
+
// hatch: the as_of branch above also admits historically-valid
|
|
19027
|
+
// records). Gated off entirely when `temporalExpiredInInjection` is
|
|
19028
|
+
// set. Status-orthogonal: an `active` fact can be validity-expired;
|
|
19029
|
+
// a `superseded` one may still be within its window.
|
|
19030
|
+
if (
|
|
19031
|
+
!asOfActive &&
|
|
19032
|
+
this.config.temporalBiTemporal &&
|
|
19033
|
+
!this.config.temporalExpiredInInjection &&
|
|
19034
|
+
isValidityExpiredNow(memory.frontmatter, Date.now())
|
|
19035
|
+
) {
|
|
19036
|
+
biTemporalExpiredFilteredCount += 1;
|
|
19037
|
+
continue;
|
|
19038
|
+
}
|
|
19404
19039
|
|
|
19405
19040
|
if (
|
|
19406
19041
|
options?.allowDedicatedSurface !== true &&
|
|
@@ -19423,6 +19058,11 @@ export class Orchestrator {
|
|
|
19423
19058
|
`temporal supersession filter removed ${temporalSupersededFilteredCount} superseded candidates`,
|
|
19424
19059
|
);
|
|
19425
19060
|
}
|
|
19061
|
+
if (biTemporalExpiredFilteredCount > 0) {
|
|
19062
|
+
log.debug(
|
|
19063
|
+
`bi-temporal validity filter removed ${biTemporalExpiredFilteredCount} expired-validity candidates from injection (temporal.biTemporal on)`,
|
|
19064
|
+
);
|
|
19065
|
+
}
|
|
19426
19066
|
if (dedicatedSurfaceFilteredCount > 0) {
|
|
19427
19067
|
log.debug(
|
|
19428
19068
|
`dedicated surface filter removed ${dedicatedSurfaceFilteredCount} dream/procedural candidates from generic recall`,
|
package/src/storage.ts
CHANGED
|
@@ -321,6 +321,22 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
|
|
|
321
321
|
// memories round-trip unchanged; readers default `valid_at` to `created`.
|
|
322
322
|
if (fm.valid_at) lines.push(`validAt: ${fm.valid_at}`);
|
|
323
323
|
if (fm.invalid_at) lines.push(`invalidAt: ${fm.invalid_at}`);
|
|
324
|
+
// Issue #1578 — bi-temporal ingestion provenance. Validate on write so a
|
|
325
|
+
// corrupt `observedAt` (non-ISO / overflowed) cannot leak onto disk; reads
|
|
326
|
+
// are permissive, mirroring the `valid_at` precedent. `eventTimeSource` is
|
|
327
|
+
// a closed enum — reject anything outside it rather than persisting garbage.
|
|
328
|
+
if (fm.observedAt) {
|
|
329
|
+
const validated = normalizeMemoryWriteTimestamp("observedAt", fm.observedAt);
|
|
330
|
+
if (validated) lines.push(`observedAt: ${validated}`);
|
|
331
|
+
}
|
|
332
|
+
if (fm.eventTimeSource) {
|
|
333
|
+
if (fm.eventTimeSource !== "extracted" && fm.eventTimeSource !== "assumed") {
|
|
334
|
+
throw new Error(
|
|
335
|
+
`serializeFrontmatter: invalid eventTimeSource ${JSON.stringify(fm.eventTimeSource)} — expected "extracted" | "assumed"`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
lines.push(`eventTimeSource: ${fm.eventTimeSource}`);
|
|
339
|
+
}
|
|
324
340
|
if (fm.forgottenAt) lines.push(`forgottenAt: ${fm.forgottenAt}`);
|
|
325
341
|
if (fm.forgottenReason) lines.push(`forgottenReason: ${JSON.stringify(fm.forgottenReason)}`);
|
|
326
342
|
// Lifecycle policy fields
|
|
@@ -808,6 +824,12 @@ function parseFrontmatter(
|
|
|
808
824
|
// Issue #680 — explicit fact lifecycle round-trip.
|
|
809
825
|
valid_at: fm.validAt || undefined,
|
|
810
826
|
invalid_at: fm.invalidAt || undefined,
|
|
827
|
+
// Issue #1578 — bi-temporal ingestion provenance round-trip.
|
|
828
|
+
observedAt: fm.observedAt || undefined,
|
|
829
|
+
eventTimeSource:
|
|
830
|
+
fm.eventTimeSource === "extracted" || fm.eventTimeSource === "assumed"
|
|
831
|
+
? fm.eventTimeSource
|
|
832
|
+
: undefined,
|
|
811
833
|
forgottenAt: fm.forgottenAt || undefined,
|
|
812
834
|
forgottenReason: parseFrontmatterStringValue(fm.forgottenReason),
|
|
813
835
|
lifecycleState: (fm.lifecycleState as LifecycleState) || undefined,
|
|
@@ -3333,6 +3355,12 @@ export class StorageManager {
|
|
|
3333
3355
|
memoryKind?: MemoryFrontmatter["memoryKind"];
|
|
3334
3356
|
expiresAt?: string;
|
|
3335
3357
|
validAt?: string;
|
|
3358
|
+
// Issue #1578 — bi-temporal ingestion provenance. `observedAt` is the
|
|
3359
|
+
// ingestion time (when Remnic learned the fact); `eventTimeSource`
|
|
3360
|
+
// records whether `validAt` was resolved from an extracted expression
|
|
3361
|
+
// or assumed from the ingestion anchor. Both validate on serialize.
|
|
3362
|
+
observedAt?: string;
|
|
3363
|
+
eventTimeSource?: "extracted" | "assumed";
|
|
3336
3364
|
structuredAttributes?: Record<string, string>;
|
|
3337
3365
|
/**
|
|
3338
3366
|
* When provided, this string is used as the source for the fact-content
|
|
@@ -3365,6 +3393,10 @@ export class StorageManager {
|
|
|
3365
3393
|
const conf = options.confidence ?? 0.8;
|
|
3366
3394
|
const tier = confidenceTier(conf);
|
|
3367
3395
|
const validAt = normalizeMemoryWriteTimestamp("validAt", options.validAt);
|
|
3396
|
+
const observedAt = normalizeMemoryWriteTimestamp(
|
|
3397
|
+
"observedAt",
|
|
3398
|
+
options.observedAt,
|
|
3399
|
+
);
|
|
3368
3400
|
|
|
3369
3401
|
// Auto-set TTL for speculative memories
|
|
3370
3402
|
let expiresAt: string | undefined;
|
|
@@ -3398,6 +3430,8 @@ export class StorageManager {
|
|
|
3398
3430
|
sourceTurnId: options.sourceTurnId,
|
|
3399
3431
|
memoryKind: options.memoryKind,
|
|
3400
3432
|
valid_at: validAt,
|
|
3433
|
+
observedAt,
|
|
3434
|
+
eventTimeSource: options.eventTimeSource,
|
|
3401
3435
|
structuredAttributes: options.structuredAttributes,
|
|
3402
3436
|
};
|
|
3403
3437
|
if (options.status !== undefined) {
|