Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions src/system/user/server/modules/cognitive/memory/Hippocampus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -45,13 +46,28 @@ 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';
import { DataList } from '../../../../../../commands/data/list/shared/DataListTypes';
import { DataCreate } from '../../../../../../commands/data/create/shared/DataCreateTypes';
import type { CorpusMemory } from '../../../../../../workers/continuum-core/bindings/CorpusMemory';

function selectDefaultConsolidationAdapter(
persona: PersonaUser,
logger: NonNullable<ConstructorParameters<typeof SemanticCompressionAdapter>[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
Expand Down Expand Up @@ -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'
});

Expand All @@ -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`);

Expand Down Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const ENABLE_LLM_MEMORY_SYNTHESIS_ENV = 'CONTINUUM_ENABLE_LLM_MEMORY_SYNTHESIS';
type Env = Readonly<Record<string, string | undefined>>;
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';
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
29 changes: 29 additions & 0 deletions src/tests/unit/memory/HippocampusConsolidationPolicy.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
Loading