/** * ============================================================ * MemoryManager — 5-Layer Cognitive Memory System (통합 진입점) * * Astra의 모든 메모리 레이어를 통합 관리하는 중앙 매니저입니다. * * ① Short-Term Memory — 현재 대화 흐름 (FIFO) * ② Long-Term Memory — 사용자 취향/규칙/결정 * ③ Project Memory — 프로젝트별 지식 * ④ Procedural Memory — 반복 작업 절차 (skill.md) * ⑤ Episodic Memory — 과거 대화/결정 흐름 * ============================================================ */ import { BrainProfile } from '../config'; import { ShortTermMemory } from './ShortTermMemory'; import { LongTermMemory } from './LongTermMemory'; import { ProjectMemory } from './ProjectMemory'; import { ProceduralMemory } from './ProceduralMemory'; import { EpisodicMemory } from './EpisodicMemory'; import { MemoryExtractor } from './MemoryExtractor'; import { MemoryContextResult, MemoryConfig } from './types'; import { distillStaleEpisodes, shouldAutoDistill, recordDistillationRun, type DistillationArchiveMode, } from './distillation'; export { ShortTermMemory } from './ShortTermMemory'; export { LongTermMemory } from './LongTermMemory'; export { ProjectMemory } from './ProjectMemory'; export { ProceduralMemory } from './ProceduralMemory'; export { EpisodicMemory } from './EpisodicMemory'; export { MemoryExtractor } from './MemoryExtractor'; export { distillStaleEpisodes, getLastDistillationRun, recordDistillationRun, shouldAutoDistill, DEFAULT_DISTILLATION_OPTIONS, type DistillationOptions, type DistillationReport, type DistillationMarker, type DistillationArchiveMode, } from './distillation'; export * from './types'; export class MemoryManager { private shortTerm: ShortTermMemory; private longTerm: LongTermMemory; private procedural: ProceduralMemory; private episodic: EpisodicMemory; private extractor: MemoryExtractor; // Project Memory는 workspace별로 lazy-init private projectMemories = new Map(); private config: MemoryConfig; constructor(brainPath: string, config?: Partial) { this.config = { enabled: true, shortTermLimit: 8, longTermMaxEntries: 100, episodicMaxEpisodes: 50, projectMemoryEnabled: true, proceduralMemoryEnabled: true, episodicMemoryEnabled: true, ...config }; this.shortTerm = new ShortTermMemory(); this.longTerm = new LongTermMemory(brainPath, this.config.longTermMaxEntries); this.procedural = new ProceduralMemory(brainPath); this.episodic = new EpisodicMemory(brainPath, this.config.episodicMaxEpisodes); this.extractor = new MemoryExtractor(); } // ─── Context Building (핵심 API) ─── /** * 프롬프트에 대해 모든 메모리 레이어에서 관련 컨텍스트를 수집합니다. * agent.ts의 buildMemoryContext()를 대체합니다. */ public buildContext( currentPrompt: string, visibleHistory: Array<{ role: string; content: string }>, summarize: (text: string, maxLen: number) => string, workspacePath?: string ): string { if (!this.config.enabled) return ''; const layers: MemoryContextResult[] = []; // ① Short-Term Memory const stm = this.shortTerm.buildContext( visibleHistory, this.config.shortTermLimit, summarize ); if (stm) layers.push(stm); // ② Long-Term Memory const ltm = this.longTerm.buildContext(currentPrompt); if (ltm) layers.push(ltm); // ③ Project Memory if (this.config.projectMemoryEnabled && workspacePath) { const pm = this.getProjectMemory(workspacePath); const pmCtx = pm.buildContext(currentPrompt); if (pmCtx) layers.push(pmCtx); } // ④ Procedural Memory if (this.config.proceduralMemoryEnabled) { const proc = this.procedural.buildContext(currentPrompt); if (proc) layers.push(proc); } // ⑤ Episodic Memory if (this.config.episodicMemoryEnabled) { const ep = this.episodic.buildContext(currentPrompt); if (ep) layers.push(ep); } if (layers.length === 0) return ''; // 관련도 순으로 정렬 layers.sort((a, b) => b.relevance - a.relevance); const sections = layers .map((layer) => `### ${layer.label}\n${layer.content}`) .join('\n\n'); return [ '', '[MEMORY CONTEXT]', 'Review this layered memory before preparing the answer. Use it only when relevant, and prefer the current user request when there is conflict.', sections ].join('\n'); } // ─── Session Lifecycle ─── /** * 세션 종료 시 호출하여 모든 메모리 레이어에 대해 추출을 수행합니다. */ public onSessionEnd( sessionId: string, messages: Array<{ role: string; content: string; timestamp?: number }>, workspacePath?: string, distillationOpts?: { enabled: boolean; ageThresholdDays: number; intervalDays: number; archiveMode: DistillationArchiveMode; brainPath: string; }, ): void { if (!this.config.enabled) return; const projectMemory = workspacePath ? this.getProjectMemory(workspacePath) : null; try { this.extractor.extractFromSession( sessionId, messages, this.longTerm, this.episodic, projectMemory, workspacePath ); } catch { /* memory extraction should never break the main flow */ } // Persist long-term memory this.longTerm.save(); // Auto-distillation — Distillation Loop 가 enabled 이고 interval 충족 시 stale // episodes 를 LongTerm digest 로 승급. 세션 종료 시점이 자연스러움 — 사용자가 // 다음 세션 시작 전 한 번 cleanup. if (distillationOpts?.enabled && shouldAutoDistill(distillationOpts.brainPath, distillationOpts.intervalDays)) { try { const report = distillStaleEpisodes(this.episodic, this.longTerm, distillationOpts.brainPath, { ageThresholdDays: distillationOpts.ageThresholdDays, archiveMode: distillationOpts.archiveMode, }); recordDistillationRun(distillationOpts.brainPath, report); } catch { /* distillation should never break session end */ } } } // ─── Direct Access (for UI & advanced features) ─── public getLongTermMemory(): LongTermMemory { return this.longTerm; } public getProceduralMemory(): ProceduralMemory { return this.procedural; } public getEpisodicMemory(): EpisodicMemory { return this.episodic; } public getProjectMemory(workspacePath: string): ProjectMemory { if (!this.projectMemories.has(workspacePath)) { this.projectMemories.set(workspacePath, new ProjectMemory(workspacePath)); } return this.projectMemories.get(workspacePath)!; } // ─── Config ─── public updateConfig(partial: Partial): void { Object.assign(this.config, partial); } public getConfig(): MemoryConfig { return { ...this.config }; } }