@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.
- package/dist/access-boundary.d.ts +2 -2
- package/dist/access-cli.js +1 -1
- package/dist/access-http.d.ts +2 -2
- package/dist/access-mcp.d.ts +2 -2
- package/dist/access-operations.d.ts +2 -2
- package/dist/{access-service-CdY88L86.d.ts → access-service-Cx16nmCv.d.ts} +1 -1
- package/dist/access-service.d.ts +2 -2
- package/dist/access-surface-catalog.d.ts +2 -2
- package/dist/bootstrap.d.ts +1 -1
- package/dist/{chunk-YQX63P3W.js → chunk-PJE6D3NH.js} +11513 -11474
- package/dist/chunk-PJE6D3NH.js.map +1 -0
- package/dist/{cli-BNJtn0R0.d.ts → cli-D1y3vooB.d.ts} +2 -2
- package/dist/cli.d.ts +3 -3
- package/dist/explicit-capture.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/mcp-memory-inspector-app.d.ts +2 -2
- package/dist/{orchestrator-BBGXLd20.d.ts → orchestrator-CnCE3Hvq.d.ts} +11 -5
- package/dist/orchestrator.d.ts +1 -1
- package/dist/orchestrator.js +1 -1
- package/package.json +2 -2
- package/src/orchestration/consolidation-run.ts +633 -0
- package/src/orchestrator.ts +39 -538
- package/dist/chunk-YQX63P3W.js.map +0 -1
package/src/orchestrator.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|