diff --git a/src/system/user/server/modules/cognitive/memory/Hippocampus.ts b/src/system/user/server/modules/cognitive/memory/Hippocampus.ts index 85b20d3ed..74a5793f0 100644 --- a/src/system/user/server/modules/cognitive/memory/Hippocampus.ts +++ b/src/system/user/server/modules/cognitive/memory/Hippocampus.ts @@ -37,6 +37,7 @@ import { AdaptiveConsolidationThreshold } from './AdaptiveConsolidationThreshold import { MemoryConsolidationAdapter } from './adapters/MemoryConsolidationAdapter'; import { SemanticCompressionAdapter } from './adapters/SemanticCompressionAdapter'; import { RawMemoryAdapter } from './adapters/RawMemoryAdapter'; +import { getDefaultConsolidationMode } from './HippocampusConsolidationPolicy'; import type { WorkingMemoryEntry } from '../../cognition/memory/InMemoryCognitionStorage'; import { DataDaemon } from '../../../../../../daemons/data-daemon/shared/DataDaemon'; import type { UniversalFilter } from '../../../../../../daemons/data-daemon/shared/DataStorageAdapter'; @@ -45,6 +46,7 @@ import type { VectorSearchParams, VectorSearchResult_CLI } from '../../../../../ import { BackpressureService } from '../../../../../core/services/BackpressureService'; import { CognitionLogger } from '../../cognition/CognitionLogger'; import { TieredMemoryCache } from '../../../../../rag/cache/TieredMemoryCache'; +import { StartupAutonomousWorkGate } from '../../StartupAutonomousWorkGate'; import { DataOpen } from '../../../../../../commands/data/open/shared/DataOpenTypes'; import { VectorSearch } from '../../../../../../commands/data/vector-search/shared/VectorSearchCommandTypes'; @@ -52,6 +54,20 @@ import { DataList } from '../../../../../../commands/data/list/shared/DataListTy import { DataCreate } from '../../../../../../commands/data/create/shared/DataCreateTypes'; import type { CorpusMemory } from '../../../../../../workers/continuum-core/bindings/CorpusMemory'; +function selectDefaultConsolidationAdapter( + persona: PersonaUser, + logger: NonNullable[1]>['logger'] +): MemoryConsolidationAdapter { + if (getDefaultConsolidationMode() === 'raw') { + return new RawMemoryAdapter(); + } + + return new SemanticCompressionAdapter( + persona, + { maxThoughtsPerGroup: 10, logger } + ); +} + /** * Snapshot of persona state at tick time * Used for logging and consolidation decisions @@ -123,7 +139,7 @@ export class Hippocampus extends PersonaContinuousSubprocess { constructor(persona: PersonaUser, adapter?: MemoryConsolidationAdapter) { super(persona, { - priority: 'low', // Low priority - don't interfere with response times + priority: 'lowest', // Background memory must not compete with visible chat turns. name: 'Hippocampus' }); @@ -137,15 +153,10 @@ export class Hippocampus extends PersonaContinuousSubprocess { // Initialize adaptive threshold (sigmoid-based, activity-responsive) this.adaptiveThreshold = new AdaptiveConsolidationThreshold(); - // Initialize consolidation adapter (default: semantic compression) - // Pass persona directly - adapter uses persona.generateText() for synthesis (same code path as chat) const hippocampusLogger = (message: string) => { this.persona.logger.enqueueLog('hippocampus.log', message); }; - this.consolidationAdapter = adapter || new SemanticCompressionAdapter( - persona, - { maxThoughtsPerGroup: 10, logger: hippocampusLogger } - ); + this.consolidationAdapter = adapter || selectDefaultConsolidationAdapter(persona, hippocampusLogger); this.log(`Initialized with ${this.consolidationAdapter.getName()} adapter`); @@ -405,6 +416,10 @@ export class Hippocampus extends PersonaContinuousSubprocess { tickCount: this.metrics.tickCount + 1 }; + if (StartupAutonomousWorkGate.isPaused()) { + return; + } + // BACKPRESSURE: Skip consolidation entirely when system is under high load // Consolidation involves LLM calls (expensive) - wait until load drops if (BackpressureService.isHighLoad()) { diff --git a/src/system/user/server/modules/cognitive/memory/HippocampusConsolidationPolicy.ts b/src/system/user/server/modules/cognitive/memory/HippocampusConsolidationPolicy.ts new file mode 100644 index 000000000..da715ad63 --- /dev/null +++ b/src/system/user/server/modules/cognitive/memory/HippocampusConsolidationPolicy.ts @@ -0,0 +1,14 @@ +const ENABLE_LLM_MEMORY_SYNTHESIS_ENV = 'CONTINUUM_ENABLE_LLM_MEMORY_SYNTHESIS'; +type Env = Readonly>; +export type MemoryConsolidationMode = 'raw' | 'semantic'; + +export function getDefaultConsolidationMode(env: Env = process.env): MemoryConsolidationMode { + const value = env[ENABLE_LLM_MEMORY_SYNTHESIS_ENV]?.toLowerCase(); + const enabled = value === '1' || value === 'true' || value === 'yes'; + return enabled ? 'semantic' : 'raw'; +} + +export function isLlmMemorySynthesisEnabled(env: Env = process.env): boolean { + const value = env[ENABLE_LLM_MEMORY_SYNTHESIS_ENV]?.toLowerCase(); + return value === '1' || value === 'true' || value === 'yes'; +} diff --git a/src/system/user/server/modules/cognitive/memory/adapters/SemanticCompressionAdapter.ts b/src/system/user/server/modules/cognitive/memory/adapters/SemanticCompressionAdapter.ts index be981b4d6..cd3401463 100644 --- a/src/system/user/server/modules/cognitive/memory/adapters/SemanticCompressionAdapter.ts +++ b/src/system/user/server/modules/cognitive/memory/adapters/SemanticCompressionAdapter.ts @@ -64,9 +64,10 @@ export class SemanticCompressionAdapter extends MemoryConsolidationAdapter { const errors: Array<{ domain: string; error: string }> = []; for (const group of groups) { - // BACKPRESSURE: Check system load before expensive LLM synthesis - // Memory synthesis is low priority - defer when system is loaded - if (!BackpressureService.shouldProceed('low')) { + // BACKPRESSURE: Check system load before expensive LLM synthesis. + // This uses the strict background lane because it shares the visible chat + // inference path until a dedicated memory-synthesis engine exists. + if (!BackpressureService.shouldProceed('background')) { skippedDueToLoad++; // Use fallback (no LLM call) when under load const fallback = this.createFallbackMemory(group, context); diff --git a/src/tests/unit/memory/HippocampusConsolidationPolicy.test.ts b/src/tests/unit/memory/HippocampusConsolidationPolicy.test.ts new file mode 100644 index 000000000..1f67660f3 --- /dev/null +++ b/src/tests/unit/memory/HippocampusConsolidationPolicy.test.ts @@ -0,0 +1,29 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { getDefaultConsolidationMode, isLlmMemorySynthesisEnabled } from '../../../system/user/server/modules/cognitive/memory/HippocampusConsolidationPolicy'; + +const ENV_NAME = 'CONTINUUM_ENABLE_LLM_MEMORY_SYNTHESIS'; +const originalValue = process.env[ENV_NAME]; + +describe('Hippocampus consolidation policy', () => { + afterEach(() => { + if (originalValue === undefined) { + delete process.env[ENV_NAME]; + } else { + process.env[ENV_NAME] = originalValue; + } + }); + + it('uses raw consolidation by default so background memory cannot steal chat inference', () => { + delete process.env[ENV_NAME]; + + expect(getDefaultConsolidationMode()).toBe('raw'); + expect(isLlmMemorySynthesisEnabled()).toBe(false); + }); + + it('uses semantic compression only when explicitly enabled', () => { + process.env[ENV_NAME] = '1'; + + expect(getDefaultConsolidationMode()).toBe('semantic'); + expect(isLlmMemorySynthesisEnabled()).toBe(true); + }); +});