@remnic/core 9.3.745 → 9.3.746

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.
@@ -107,6 +107,7 @@ import { RecallSectionCoordinator } from "./orchestration/recall-section-coordin
107
107
  import { QmdResultResolver, qmdCollectionPathParts, qmdResultPathCandidates } from "./orchestration/qmd-result-resolver.js";
108
108
  import { ContradictionLinkingCoordinator } from "./orchestration/contradiction-linking-coordinator.js";
109
109
  import { ExtractionRunCoordinator, type ExtractionRunResult } from "./orchestration/extraction-run.js";
110
+ import { ConsolidationRunCoordinator } from "./orchestration/consolidation-run.js";
110
111
  import { ExtractionPersistCoordinator } from "./orchestration/extraction-persist.js";
111
112
  export { hasIdentityRecoveryIntent, resolveEffectiveIdentityInjectionMode } from "./orchestration/recall-result-formatter.js";
112
113
  import {
@@ -1773,6 +1774,43 @@ export class Orchestrator {
1773
1774
  }
1774
1775
  return this._extractionRunCoordinator;
1775
1776
  }
1777
+ /**
1778
+ * Consolidation-run coordinator (issue #1526 seam 17). Owns
1779
+ * the full consolidation maintenance pass (LLM merge/invalidate/update,
1780
+ * entity merge, commitment/TTL cleanup, lifecycle policy, compression
1781
+ * guideline learning, tier migration, fact archival, semantic consolidation,
1782
+ * identity consolidation, profile consolidation, summarization, topic
1783
+ * extraction, TMT rebuild). The orchestrator delegates and injects
1784
+ * its own methods + coordinators.
1785
+ */
1786
+ private _consolidationRunCoordinator: ConsolidationRunCoordinator | undefined;
1787
+
1788
+ private get consolidationRunCoordinator(): ConsolidationRunCoordinator {
1789
+ if (!this._consolidationRunCoordinator) {
1790
+ this._consolidationRunCoordinator = new ConsolidationRunCoordinator({
1791
+ config: this.config,
1792
+ getStorage: () => this.storage,
1793
+ getStorageRouter: () => this.storageRouter,
1794
+ getExtraction: () => this.extraction,
1795
+ embeddingFallback: this.embeddingFallback,
1796
+ tmtBuilder: this.tmtBuilder,
1797
+ consolidationObservers: this.consolidationObservers,
1798
+ getAccessTrackingBuffer: () => this.accessTrackingBuffer,
1799
+ lifecyclePolicyCoordinator: this.lifecyclePolicyCoordinator,
1800
+ compressionGuidelineCoordinator: this.compressionGuidelineCoordinator,
1801
+ semanticConsolidationCoordinator: this.semanticConsolidationCoordinator,
1802
+ entitySynthesisCoordinator: this.entitySynthesisCoordinator,
1803
+ recallSectionCoordinator: this.recallSectionCoordinator,
1804
+ tierMigrationCoordinator: this.tierMigrationCoordinator,
1805
+ flushAccessTracking: () => this.flushAccessTracking(),
1806
+ indexPersistedMemory: (storage, memoryId) => this.indexPersistedMemory(storage, memoryId),
1807
+ autoConsolidateIdentity: () => this.autoConsolidateIdentity(),
1808
+ fastChatCompletion: (messages, options) => this.fastChatCompletion(messages, options),
1809
+ });
1810
+ }
1811
+ return this._consolidationRunCoordinator;
1812
+ }
1813
+
1776
1814
  /**
1777
1815
  * Extraction-persist coordinator (issue #1526 seam 16). Owns the
1778
1816
  * `persistExtraction` pipeline. Lazy: created on first access so
@@ -12489,503 +12527,7 @@ export class Orchestrator {
12489
12527
  merged: number;
12490
12528
  invalidated: number;
12491
12529
  }> {
12492
- const lifecycleCaps = resolveMemoryLifecycleCapabilities(this.config);
12493
- log.info("running consolidation pass");
12494
- let merged = 0;
12495
- let invalidated = 0;
12496
- // Tracks whether any consolidation memory-item action (UPDATE / MERGE /
12497
- // INVALIDATE) durably rewrote memory state. A consolidation pass that only
12498
- // mutates memory items (no profile/entity updates) still changes the default
12499
- // namespace's data, so its catalog `lastWriteAt` must refresh too (NIBOi).
12500
- let memoryItemMutated = false;
12501
-
12502
- // Flush access tracking buffer first
12503
- if (this.accessTrackingBuffer.size > 0) {
12504
- await this.flushAccessTracking();
12505
- }
12506
-
12507
- let allMemories = await this.storage.readAllMemories();
12508
- if (allMemories.length < 5) {
12509
- return { memoriesProcessed: allMemories.length, merged, invalidated };
12510
- }
12511
-
12512
- const recent = allMemories
12513
- .sort(
12514
- (a, b) =>
12515
- new Date(b.frontmatter.created).getTime() -
12516
- new Date(a.frontmatter.created).getTime(),
12517
- )
12518
- .slice(0, 20);
12519
-
12520
- const older = allMemories.sort(
12521
- (a, b) =>
12522
- new Date(a.frontmatter.created).getTime() -
12523
- new Date(b.frontmatter.created).getTime(),
12524
- );
12525
-
12526
- const profile = await this.storage.readProfile();
12527
- const result = await this.extraction.consolidate(recent, older, profile);
12528
-
12529
- // Build a lookup map from the already-loaded corpus to avoid repeated
12530
- // readAllMemories() scans inside getMemoryById for pre-action deindex reads.
12531
- const memoryLookup = resolveIndexingCapabilities(this.config).queryAwareIndexing
12532
- ? new Map(allMemories.map((m) => [m.frontmatter.id, m]))
12533
- : null;
12534
-
12535
- for (const item of result.items) {
12536
- switch (item.action) {
12537
- case "INVALIDATE": {
12538
- // Capture path/frontmatter before invalidation for index cleanup
12539
- const toInvalidate = resolveIndexingCapabilities(this.config).queryAwareIndexing
12540
- ? (memoryLookup?.get(item.existingId) ?? null)
12541
- : null;
12542
- if (await this.storage.invalidateMemory(item.existingId)) {
12543
- invalidated += 1;
12544
- memoryItemMutated = true;
12545
- await this.embeddingFallback.removeFromIndex(item.existingId);
12546
- if (toInvalidate?.path && toInvalidate.frontmatter?.created) {
12547
- deindexMemory(
12548
- this.config.memoryDir,
12549
- toInvalidate.path,
12550
- toInvalidate.frontmatter.created,
12551
- toInvalidate.frontmatter.tags ?? [],
12552
- );
12553
- }
12554
- }
12555
- break;
12556
- }
12557
- case "UPDATE":
12558
- if (item.updatedContent) {
12559
- await this.storage.updateMemory(
12560
- item.existingId,
12561
- item.updatedContent,
12562
- {
12563
- lineage: [item.existingId],
12564
- },
12565
- );
12566
- memoryItemMutated = true;
12567
- await this.indexPersistedMemory(this.storage, item.existingId);
12568
- // updateMemory() only changes content/updated/lineage — path, created, and tags
12569
- // are preserved, so the temporal/tag index entry is already correct; no reindex needed.
12570
- }
12571
- break;
12572
- case "MERGE":
12573
- if (item.updatedContent && item.mergeWith) {
12574
- await this.storage.updateMemory(
12575
- item.existingId,
12576
- item.updatedContent,
12577
- {
12578
- supersedes: item.mergeWith,
12579
- lineage: [item.existingId, item.mergeWith],
12580
- },
12581
- );
12582
- memoryItemMutated = true;
12583
- await this.indexPersistedMemory(this.storage, item.existingId);
12584
- // updateMemory() only changes content/updated/supersedes/lineage — path, created, and tags
12585
- // are preserved, so the temporal/tag index entry for the survivor is already correct.
12586
- // Capture before invalidation for index cleanup
12587
- const toMergeInvalidate = resolveIndexingCapabilities(this.config).queryAwareIndexing
12588
- ? (memoryLookup?.get(item.mergeWith) ?? null)
12589
- : null;
12590
- if (await this.storage.invalidateMemory(item.mergeWith)) {
12591
- invalidated += 1;
12592
- merged += 1;
12593
- await this.embeddingFallback.removeFromIndex(item.mergeWith);
12594
- if (
12595
- toMergeInvalidate?.path &&
12596
- toMergeInvalidate.frontmatter?.created
12597
- ) {
12598
- deindexMemory(
12599
- this.config.memoryDir,
12600
- toMergeInvalidate.path,
12601
- toMergeInvalidate.frontmatter.created,
12602
- toMergeInvalidate.frontmatter.tags ?? [],
12603
- );
12604
- }
12605
- }
12606
- }
12607
- break;
12608
- }
12609
- }
12610
-
12611
- if (result.profileUpdates.length > 0) {
12612
- await this.storage.appendToProfile(result.profileUpdates);
12613
- }
12614
-
12615
- for (const entity of result.entityUpdates) {
12616
- const safeFacts = Array.isArray((entity as any)?.facts)
12617
- ? (entity as any).facts.filter((f: any) => typeof f === "string")
12618
- : [];
12619
- await this.storage.writeEntity(entity.name, entity.type, safeFacts, {
12620
- source: "consolidation",
12621
- structuredSections: Array.isArray((entity as any)?.structuredSections)
12622
- ? (entity as any).structuredSections
12623
- : undefined,
12624
- });
12625
- }
12626
-
12627
- // Catalog write touch accounting (issue #1499 sweep): consolidation persists
12628
- // durable mutations directly to the default-namespace `this.storage`, bypassing
12629
- // the extraction write path. We do NOT touch here — later maintenance steps in
12630
- // this same function (entity-file merges, expired-commitment / TTL cleanup,
12631
- // fact archival) can ALSO mutate the namespace on a run with no LLM outputs
12632
- // (NIjwl). So we accumulate every durable mutation into `memoryItemMutated` and
12633
- // record ONE consolidated touch AFTER all mutation-producing steps complete,
12634
- // just before returning (rule #25: touch after the write commits). LLM
12635
- // profile/entity updates and memory-item actions (UPDATE / MERGE / INVALIDATE)
12636
- // count here (NIBOi).
12637
- if (result.profileUpdates.length > 0 || result.entityUpdates.length > 0) {
12638
- memoryItemMutated = true;
12639
- }
12640
-
12641
- // Merge fragmented entity files
12642
- const entitiesMerged = await this.storage.mergeFragmentedEntities();
12643
- if (entitiesMerged > 0) {
12644
- memoryItemMutated = true;
12645
- log.info(`merged ${entitiesMerged} fragmented entity files`);
12646
- }
12647
-
12648
- if (resolvePresentationCapabilities(this.config).entitySummary) {
12649
- try {
12650
- const synthesized = await this.processEntitySynthesisQueue(
12651
- this.config.defaultNamespace,
12652
- 5,
12653
- );
12654
- if (synthesized > 0) {
12655
- // Entity synthesis rewrites entity files — a durable namespace mutation,
12656
- // so record it for the catalog touch even when it is the only change in
12657
- // the pass (codex). Otherwise lastWriteAt goes stale.
12658
- memoryItemMutated = true;
12659
- log.info(`refreshed ${synthesized} entity syntheses`);
12660
- }
12661
- } catch (err) {
12662
- log.debug(`entity synthesis pass failed: ${err}`);
12663
- }
12664
- }
12665
-
12666
- // Clean expired commitments
12667
- const deletedCommitments = await this.storage.cleanExpiredCommitments(
12668
- this.config.commitmentDecayDays,
12669
- );
12670
- if (deletedCommitments.length > 0) {
12671
- memoryItemMutated = true;
12672
- log.info(`cleaned ${deletedCommitments.length} expired commitments`);
12673
- if (resolveIndexingCapabilities(this.config).queryAwareIndexing) {
12674
- for (const m of deletedCommitments) {
12675
- deindexMemory(
12676
- this.config.memoryDir,
12677
- m.path,
12678
- m.frontmatter.created,
12679
- m.frontmatter.tags ?? [],
12680
- );
12681
- }
12682
- }
12683
- }
12684
-
12685
- if (
12686
- resolveCreationMemoryCapabilities(this.config).creationMemory &&
12687
- resolveCreationMemoryCapabilities(this.config).commitmentLedger &&
12688
- resolveCreationMemoryCapabilities(this.config).commitmentLifecycle
12689
- ) {
12690
- try {
12691
- const lifecycle = await applyCommitmentLedgerLifecycle({
12692
- memoryDir: this.config.memoryDir,
12693
- commitmentLedgerDir: this.config.commitmentLedgerDir,
12694
- enabled: true,
12695
- decayDays: this.config.commitmentDecayDays,
12696
- });
12697
- if (
12698
- lifecycle.transitionedToExpired.length > 0 ||
12699
- lifecycle.deletedResolved.length > 0
12700
- ) {
12701
- memoryItemMutated = true;
12702
- log.info(
12703
- `commitment ledger lifecycle: expired ${lifecycle.transitionedToExpired.length}, cleaned ${lifecycle.deletedResolved.length}`,
12704
- );
12705
- }
12706
- } catch (err) {
12707
- log.debug(`commitment ledger lifecycle pass failed: ${err}`);
12708
- }
12709
- }
12710
-
12711
- // Clean memories past their TTL (speculative memories auto-expire)
12712
- const deletedTTL = await this.storage.cleanExpiredTTL();
12713
- if (deletedTTL.length > 0) {
12714
- memoryItemMutated = true;
12715
- log.info(`cleaned ${deletedTTL.length} TTL-expired memories`);
12716
- if (resolveIndexingCapabilities(this.config).queryAwareIndexing) {
12717
- for (const m of deletedTTL) {
12718
- deindexMemory(
12719
- this.config.memoryDir,
12720
- m.path,
12721
- m.frontmatter.created,
12722
- m.frontmatter.tags ?? [],
12723
- );
12724
- }
12725
- }
12726
- }
12727
-
12728
- // v8.3 Lifecycle policy pass — deterministic promotion/decay metadata
12729
- if (lifecycleCaps.lifecyclePolicy) {
12730
- try {
12731
- const lightSleepStartedAt = new Date().toISOString();
12732
- const lifecycleCorpus = await this.storage.readAllMemories();
12733
- // Lifecycle frontmatter writes count as durable mutations for the catalog
12734
- // touch below (codex NR-tS), even when no other consolidation step set
12735
- // memoryItemMutated.
12736
- if ((await this.runLifecyclePolicyPass(lifecycleCorpus)) > 0) {
12737
- memoryItemMutated = true;
12738
- }
12739
- await this.recordScheduledDreamsPhaseRun(
12740
- "lightSleep",
12741
- lifecycleCorpus.length,
12742
- `scheduled lifecycle policy pass assessed ${lifecycleCorpus.length} memories`,
12743
- {
12744
- startedAt: lightSleepStartedAt,
12745
- completedAt: new Date().toISOString(),
12746
- },
12747
- );
12748
- } catch (err) {
12749
- log.warn(`lifecycle policy pass failed (ignored): ${err}`);
12750
- }
12751
- }
12752
-
12753
- // v8.3 Compression guideline learning pass (default off, fail-open).
12754
- await this.runCompressionGuidelineLearningPass();
12755
-
12756
- try {
12757
- const deepSleepStartedAt = new Date().toISOString();
12758
- // Tier migrations move/rewrite memory files; count them as durable
12759
- // mutations for the catalog touch below (codex NThSW).
12760
- const tierMigration = await this.runTierMigrationCycle(this.storage, "maintenance");
12761
- if (tierMigration.migrated > 0) memoryItemMutated = true;
12762
- allMemories = await this.storage.readAllMemories();
12763
-
12764
- // Fact archival pass (v6.0) — move old, low-importance, rarely-accessed facts to archive/
12765
- if (resolveRecallEnhancementCapabilities(this.config).factArchival) {
12766
- const archived = await this.runFactArchival(allMemories);
12767
- if (archived > 0) {
12768
- memoryItemMutated = true;
12769
- log.info(`archived ${archived} old low-importance facts`);
12770
- }
12771
- }
12772
- await this.recordScheduledDreamsPhaseRun(
12773
- "deepSleep",
12774
- allMemories.length,
12775
- `scheduled deep-sleep maintenance assessed ${allMemories.length} memories`,
12776
- {
12777
- startedAt: deepSleepStartedAt,
12778
- completedAt: new Date().toISOString(),
12779
- },
12780
- );
12781
- } catch (err) {
12782
- log.warn(`deep-sleep maintenance pass failed (ignored): ${err}`);
12783
- try {
12784
- allMemories = await this.storage.readAllMemories();
12785
- } catch (readErr) {
12786
- log.warn(`deep-sleep maintenance recovery read failed: ${readErr}`);
12787
- throw err;
12788
- }
12789
- }
12790
-
12791
- // Semantic consolidation pass — find similar memories, synthesize canonical versions
12792
- if (resolveConsolidationCapabilities(this.config).semanticConsolidation) {
12793
- try {
12794
- const stateFilePath = path.join(
12795
- this.config.memoryDir,
12796
- "state",
12797
- "semantic-consolidation-last-run.json",
12798
- );
12799
- let shouldRun = true;
12800
- try {
12801
- const stateRaw = await readFile(stateFilePath, "utf-8");
12802
- const stateData = JSON.parse(stateRaw) as { lastRunAt?: string };
12803
- if (stateData.lastRunAt) {
12804
- const lastRunMs = new Date(stateData.lastRunAt).getTime();
12805
- const intervalMs =
12806
- this.config.semanticConsolidationIntervalHours * 60 * 60 * 1000;
12807
- if (Date.now() - lastRunMs < intervalMs) {
12808
- shouldRun = false;
12809
- log.debug(
12810
- "[semantic-consolidation] skipping — not enough time since last run",
12811
- );
12812
- }
12813
- }
12814
- } catch {
12815
- // No state file yet — first run
12816
- }
12817
-
12818
- if (shouldRun) {
12819
- const remStartedAt = new Date().toISOString();
12820
- const semResult = await this.runSemanticConsolidation();
12821
- let remItemsProcessed = allMemories.length;
12822
- try {
12823
- allMemories = await this.storage.readAllMemories();
12824
- remItemsProcessed = allMemories.length;
12825
- } catch (err) {
12826
- log.warn(
12827
- `[semantic-consolidation] post-run telemetry refresh failed (non-fatal): ${err}`,
12828
- );
12829
- }
12830
- await this.recordScheduledDreamsPhaseRun(
12831
- "rem",
12832
- remItemsProcessed,
12833
- `scheduled REM consolidation found ${semResult.clustersFound} clusters`,
12834
- {
12835
- startedAt: remStartedAt,
12836
- completedAt: new Date().toISOString(),
12837
- },
12838
- );
12839
- if (semResult.memoriesArchived > 0) {
12840
- log.info(
12841
- `[semantic-consolidation] archived ${semResult.memoriesArchived} memories during maintenance`,
12842
- );
12843
- }
12844
- // Only persist last-run timestamp if the run succeeded (had no errors or made progress)
12845
- if (semResult.errors === 0 || semResult.memoriesArchived > 0) {
12846
- const stateDir = path.join(this.config.memoryDir, "state");
12847
- await mkdir(stateDir, { recursive: true });
12848
- await writeFile(
12849
- stateFilePath,
12850
- JSON.stringify({ lastRunAt: new Date().toISOString() }),
12851
- "utf-8",
12852
- );
12853
- }
12854
- }
12855
- } catch (err) {
12856
- log.warn(
12857
- `[semantic-consolidation] maintenance pass failed (non-fatal): ${err}`,
12858
- );
12859
- }
12860
- }
12861
-
12862
- // Auto-consolidate IDENTITY.md if it's getting large
12863
- if (resolveRecallEnhancementCapabilities(this.config).identity) {
12864
- await this.autoConsolidateIdentity();
12865
- }
12866
-
12867
- // Auto-consolidate profile.md if it exceeds max lines
12868
- const profileSection = this.getRecallSectionEntry("profile");
12869
- const profileConsolidationTriggerLines =
12870
- typeof profileSection?.consolidateTriggerLines === "number"
12871
- ? Math.max(0, Math.floor(profileSection.consolidateTriggerLines))
12872
- : undefined;
12873
- const profileConsolidationTargetLines =
12874
- typeof profileSection?.consolidateTargetLines === "number"
12875
- ? Math.max(0, Math.floor(profileSection.consolidateTargetLines))
12876
- : 50;
12877
- if (
12878
- await this.storage.profileNeedsConsolidation(
12879
- profileConsolidationTriggerLines,
12880
- )
12881
- ) {
12882
- log.info("profile.md exceeds max lines — running smart consolidation");
12883
- const currentProfile = await this.storage.readProfile();
12884
- if (currentProfile) {
12885
- const profileResult = await this.extraction.consolidateProfile(
12886
- currentProfile,
12887
- profileConsolidationTargetLines,
12888
- );
12889
- if (profileResult) {
12890
- await this.storage.writeProfile(profileResult.consolidatedProfile);
12891
- // Profile consolidation rewrites profile.md — a durable namespace
12892
- // mutation; record it for the catalog touch even when it is the only
12893
- // change in the pass (codex). Otherwise lastWriteAt goes stale.
12894
- memoryItemMutated = true;
12895
- log.info(
12896
- `profile.md consolidated: removed ${profileResult.removedCount} items — ${profileResult.summary}`,
12897
- );
12898
- }
12899
- }
12900
- }
12901
-
12902
- // Memory Summarization (Phase 4A)
12903
- if (resolvePipelineProcessingCapabilities(this.config).summarization) {
12904
- await this.runSummarization(allMemories);
12905
- }
12906
-
12907
- // Topic Extraction (Phase 4B)
12908
- if (resolvePipelineProcessingCapabilities(this.config).topicExtraction) {
12909
- await this.runTopicExtraction(allMemories);
12910
- }
12911
-
12912
- const meta = await this.storage.loadMeta();
12913
- meta.lastConsolidationAt = new Date().toISOString();
12914
- await this.storage.saveMeta(meta);
12915
-
12916
- // Temporal Memory Tree (v8.2) — rebuild nodes from all memories, fail-open
12917
- if (lifecycleCaps.temporalMemoryTree) {
12918
- try {
12919
- const tmtEntries = allMemories
12920
- .filter(
12921
- (m) =>
12922
- m.frontmatter.status !== "superseded" && m.frontmatter.status !== "archived" &&
12923
- m.frontmatter.status !== "forgotten" && m.frontmatter.status !== "pending_review", // #1576: unfaithful queue items must not feed TMT clusters
12924
- )
12925
- .map((m) => ({
12926
- path: m.path,
12927
- id: m.frontmatter.id,
12928
- created: m.frontmatter.created,
12929
- content: m.content,
12930
- }));
12931
- await this.tmtBuilder.maybeRebuildNodes(
12932
- tmtEntries,
12933
- async (texts, level) => {
12934
- const prompt = `You are a memory archivist. Summarize the following ${level}-level memories into 3–5 sentences, preserving key facts, decisions, and preferences.\n\n${texts.map((t, i) => `[${i + 1}] ${t}`).join("\n\n")}`;
12935
- const response = await this.fastChatCompletion(
12936
- [
12937
- {
12938
- role: "system",
12939
- content:
12940
- "Respond with a 3–5 sentence narrative summary. No JSON, just plain prose.",
12941
- },
12942
- { role: "user", content: prompt },
12943
- ],
12944
- {
12945
- temperature: 0.3,
12946
- maxTokens: this.config.tmtSummaryMaxTokens,
12947
- operation: "tmt_summary",
12948
- priority: "background",
12949
- },
12950
- );
12951
- return response?.content?.trim() || texts.slice(0, 3).join(" ");
12952
- },
12953
- );
12954
- } catch (err) {
12955
- log.warn(`tmt: consolidation hook failed (ignored): ${err}`);
12956
- }
12957
- }
12958
-
12959
- if (this.consolidationObservers.size > 0) {
12960
- const observation: ConsolidationObservation = {
12961
- runAt: new Date().toISOString(),
12962
- recentMemories: recent,
12963
- existingMemories: older.slice(-50),
12964
- profile,
12965
- result,
12966
- merged,
12967
- invalidated,
12968
- };
12969
- for (const observer of this.consolidationObservers) {
12970
- try {
12971
- await observer(observation);
12972
- } catch (err) {
12973
- log.warn(`consolidation observer failed (ignored): ${err}`);
12974
- }
12975
- }
12976
- }
12977
-
12978
- // Consolidated catalog write touch — belt-and-suspenders for cleanup-only
12979
- // passes that mutate the store via delete-only paths (entity merges, TTL
12980
- // cleanup) without triggering the storage chokepoint's post-write hook.
12981
- // Gated on memoryItemMutated (set by every durable mutation including cleanup-only passes)
12982
- // Best-effort and failure-tolerant.
12983
- if (memoryItemMutated) {
12984
- this.storageRouter.recordWrite(this.config.defaultNamespace, this.storage.dir);
12985
- }
12986
-
12987
- log.info("consolidation complete");
12988
- return { memoriesProcessed: allMemories.length, merged, invalidated };
12530
+ return this.consolidationRunCoordinator.run();
12989
12531
  }
12990
12532
 
12991
12533
  async optimizeCompressionGuidelines(options?: {
@@ -13004,36 +12546,10 @@ export class Orchestrator {
13004
12546
 
13005
12547
  // Issue #1526 (seam 4): compression-guideline learning moved to
13006
12548
  // CompressionGuidelineCoordinator. Thin delegation keeps the
13007
- // consolidation-loop hook + recall-section hook call sites stable.
13008
- private async runCompressionGuidelineLearningPass(): Promise<void> {
13009
- return this.compressionGuidelineCoordinator.runCompressionGuidelineLearningPass();
13010
- }
13011
-
13012
12549
  private async buildCompressionGuidelineRecallSection(): Promise<string | null> {
13013
12550
  return this.compressionGuidelineCoordinator.buildCompressionGuidelineRecallSection();
13014
12551
  }
13015
12552
 
13016
- private async recordScheduledDreamsPhaseRun(
13017
- phase: "lightSleep" | "rem" | "deepSleep",
13018
- itemsProcessed: number,
13019
- notes: string,
13020
- timing: { startedAt?: string; completedAt?: string } = {},
13021
- ): Promise<void> {
13022
- try {
13023
- const { recordDreamsPhaseRun } = await import("./maintenance/dreams-ledger.js");
13024
- await recordDreamsPhaseRun({
13025
- memoryDir: this.storage.dir,
13026
- phase,
13027
- trigger: "scheduled",
13028
- itemsProcessed,
13029
- notes,
13030
- startedAt: timing.startedAt,
13031
- completedAt: timing.completedAt,
13032
- });
13033
- } catch (error) {
13034
- log.debug(`dreams ledger scheduled ${phase} write failed (non-fatal): ${error}`);
13035
- }
13036
- }
13037
12553
 
13038
12554
  async runLifecyclePolicyNow(storage: StorageManager = this.storage): Promise<{ memoriesAssessed: number }> {
13039
12555
  const lifecycleCorpus = await storage.readAllMemories();
@@ -13049,21 +12565,6 @@ export class Orchestrator {
13049
12565
  ): Promise<number> {
13050
12566
  return this.lifecyclePolicyCoordinator.runLifecyclePolicyPass(allMemories, storage);
13051
12567
  }
13052
- private async runFactArchival(
13053
- allMemories: import("./types.js").MemoryFile[],
13054
- ): Promise<number> {
13055
- return this.lifecyclePolicyCoordinator.runFactArchival(allMemories);
13056
- }
13057
- private async runSummarization(
13058
- allMemories: import("./types.js").MemoryFile[],
13059
- ): Promise<void> {
13060
- return this.lifecyclePolicyCoordinator.runSummarization(allMemories);
13061
- }
13062
- private async runTopicExtraction(
13063
- allMemories: import("./types.js").MemoryFile[],
13064
- ): Promise<void> {
13065
- return this.lifecyclePolicyCoordinator.runTopicExtraction(allMemories);
13066
- }
13067
12568
  /** Threshold (bytes) at which IDENTITY.md reflections get auto-consolidated */
13068
12569
  private static readonly IDENTITY_CONSOLIDATE_THRESHOLD = 8_000;
13069
12570