/** * Agent Storage Module * * Persistent storage for agent state, conversations, and results. * Uses filesystem for persistence. */ import { writeFileSync, readFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from 'fs'; import { join } from 'path'; const STORAGE_DIR = join(process.cwd(), '.agent-storage'); export interface StoredConversation { id: string; agentId: string; messages: Array<{ role: 'user' | 'assistant' | 'system'; content: string; timestamp: string; }>; metadata?: Record; createdAt: string; updatedAt: string; } export interface StoredTask { id: string; type: string; status: string; input: unknown; output?: unknown; error?: string; createdAt: string; completedAt?: string; } export interface StoredAgentState { id: string; name: string; type: string; status: string; memory: Record; createdAt: string; updatedAt: string; } /** * AgentStorage - Persistent storage for agents */ export class AgentStorage { private baseDir: string; constructor(baseDir: string = STORAGE_DIR) { this.baseDir = baseDir; this.ensureDirectory(); } /** * Ensure storage directory exists */ private ensureDirectory(): void { if (!existsSync(this.baseDir)) { mkdirSync(this.baseDir, { recursive: true }); } const subdirs = ['conversations', 'tasks', 'agents']; for (const subdir of subdirs) { const dir = join(this.baseDir, subdir); if (!existsSync(dir)) { mkdirSync(dir, { recursive: true }); } } } /** * Save a conversation */ saveConversation(conversation: StoredConversation): void { const path = join(this.baseDir, 'conversations', `${conversation.id}.json`); writeFileSync(path, JSON.stringify(conversation, null, 2), 'utf-8'); } /** * Load a conversation */ loadConversation(id: string): StoredConversation | null { try { const path = join(this.baseDir, 'conversations', `${id}.json`); if (!existsSync(path)) return null; return JSON.parse(readFileSync(path, 'utf-8')); } catch { return null; } } /** * List all conversations */ listConversations(agentId?: string): StoredConversation[] { const dir = join(this.baseDir, 'conversations'); if (!existsSync(dir)) return []; const files = readdirSync(dir).filter(f => f.endsWith('.json')); return files.map(file => { const content = readFileSync(join(dir, file), 'utf-8'); return JSON.parse(content) as StoredConversation; }).filter(conv => !agentId || conv.agentId === agentId); } /** * Delete a conversation */ deleteConversation(id: string): boolean { try { const path = join(this.baseDir, 'conversations', `${id}.json`); if (existsSync(path)) { unlinkSync(path); return true; } return false; } catch { return false; } } /** * Save a task */ saveTask(task: StoredTask): void { const path = join(this.baseDir, 'tasks', `${task.id}.json`); writeFileSync(path, JSON.stringify(task, null, 2), 'utf-8'); } /** * Load a task */ loadTask(id: string): StoredTask | null { try { const path = join(this.baseDir, 'tasks', `${id}.json`); if (!existsSync(path)) return null; return JSON.parse(readFileSync(path, 'utf-8')); } catch { return null; } } /** * List all tasks */ listTasks(status?: string): StoredTask[] { const dir = join(this.baseDir, 'tasks'); if (!existsSync(dir)) return []; const files = readdirSync(dir).filter(f => f.endsWith('.json')); return files.map(file => { const content = readFileSync(join(dir, file), 'utf-8'); return JSON.parse(content) as StoredTask; }).filter(task => !status || task.status === status); } /** * Save agent state */ saveAgentState(state: StoredAgentState): void { const path = join(this.baseDir, 'agents', `${state.id}.json`); writeFileSync(path, JSON.stringify(state, null, 2), 'utf-8'); } /** * Load agent state */ loadAgentState(id: string): StoredAgentState | null { try { const path = join(this.baseDir, 'agents', `${id}.json`); if (!existsSync(path)) return null; return JSON.parse(readFileSync(path, 'utf-8')); } catch { return null; } } /** * List all agent states */ listAgentStates(): StoredAgentState[] { const dir = join(this.baseDir, 'agents'); if (!existsSync(dir)) return []; const files = readdirSync(dir).filter(f => f.endsWith('.json')); return files.map(file => { const content = readFileSync(join(dir, file), 'utf-8'); return JSON.parse(content) as StoredAgentState; }); } /** * Clear all storage */ clearAll(): void { const subdirs = ['conversations', 'tasks', 'agents']; for (const subdir of subdirs) { const dir = join(this.baseDir, subdir); if (existsSync(dir)) { const files = readdirSync(dir).filter(f => f.endsWith('.json')); for (const file of files) { unlinkSync(join(dir, file)); } } } } /** * Get storage stats */ getStats(): { conversations: number; tasks: number; agents: number; } { return { conversations: this.listConversations().length, tasks: this.listTasks().length, agents: this.listAgentStates().length }; } } // Default storage instance export const defaultStorage = new AgentStorage();