← back to Designer Wallcoverings
DW-Agents/dw-agents/agent-todays-highlights/shared-memory-system.ts
216 lines
/**
* Shared Memory System for DW-Agents
*
* Provides persistent memory storage for all agents.
* Each agent can store and retrieve memories that persist across restarts.
*/
import * as fs from 'fs';
import * as path from 'path';
export interface Memory {
id: string;
timestamp: Date;
type: 'note' | 'reminder' | 'preference' | 'learning' | 'conversation' | 'task';
content: string;
metadata?: Record<string, any>;
}
export class AgentMemory {
private memoryFile: string;
private memories: Memory[] = [];
private agentName: string;
constructor(agentName: string) {
this.agentName = agentName;
this.memoryFile = path.join(__dirname, `agent-${agentName}`, 'memory.json');
this.loadMemories();
}
/**
* Load memories from disk
*/
private loadMemories(): void {
try {
if (fs.existsSync(this.memoryFile)) {
const data = fs.readFileSync(this.memoryFile, 'utf8');
const parsed = JSON.parse(data);
// Ensure parsed data is an array
if (Array.isArray(parsed)) {
this.memories = parsed;
console.log(`📚 Loaded ${this.memories.length} memories for ${this.agentName}`);
} else {
console.warn(`⚠️ Invalid memory data format for ${this.agentName}, expected array but got ${typeof parsed}`);
this.memories = [];
}
} else {
console.log(`📝 No existing memories for ${this.agentName}, starting fresh`);
this.memories = [];
}
} catch (error) {
console.error(`❌ Error loading memories for ${this.agentName}:`, error);
this.memories = [];
}
}
/**
* Save memories to disk
*/
private saveMemories(): void {
try {
const dir = path.dirname(this.memoryFile);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(this.memoryFile, JSON.stringify(this.memories, null, 2));
console.log(`💾 Saved ${this.memories.length} memories for ${this.agentName}`);
} catch (error) {
console.error(`❌ Error saving memories for ${this.agentName}:`, error);
}
}
/**
* Add a new memory
*/
add(content: string, type: Memory['type'] = 'note', metadata?: Record<string, any>): Memory {
const memory: Memory = {
id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date(),
type,
content,
metadata
};
this.memories.push(memory);
this.saveMemories();
return memory;
}
/**
* Get all memories
*/
getAll(): Memory[] {
return this.memories;
}
/**
* Get memories by type
*/
getByType(type: Memory['type']): Memory[] {
// Defensive check to ensure memories is an array
if (!Array.isArray(this.memories)) {
console.error('❌ memories is not an array, resetting to empty array');
this.memories = [];
}
return this.memories.filter(m => m.type === type);
}
/**
* Search memories by content
*/
search(query: string): Memory[] {
// Defensive check to ensure memories is an array
if (!Array.isArray(this.memories)) {
console.error('❌ memories is not an array, resetting to empty array');
this.memories = [];
}
const lowerQuery = query.toLowerCase();
return this.memories.filter(m =>
m.content.toLowerCase().includes(lowerQuery)
);
}
/**
* Delete a memory by ID
*/
delete(id: string): boolean {
const index = this.memories.findIndex(m => m.id === id);
if (index !== -1) {
this.memories.splice(index, 1);
this.saveMemories();
return true;
}
return false;
}
/**
* Clear all memories
*/
clear(): void {
this.memories = [];
this.saveMemories();
}
/**
* Get recent memories (last N)
*/
getRecent(count: number = 10): Memory[] {
return this.memories.slice(-count).reverse();
}
/**
* Get formatted summary of all memories for AI context
*/
getContextSummary(): string {
if (this.memories.length === 0) {
return 'No stored memories yet.';
}
const byType: Record<string, Memory[]> = {};
this.memories.forEach(m => {
if (!byType[m.type]) byType[m.type] = [];
byType[m.type].push(m);
});
let summary = `Agent Memory (${this.memories.length} total):\n\n`;
Object.entries(byType).forEach(([type, mems]) => {
summary += `${type.toUpperCase()} (${mems.length}):\n`;
mems.slice(-5).forEach(m => {
summary += `- ${m.content}\n`;
});
summary += '\n';
});
return summary;
}
}
/**
* Example usage in an agent:
*
* import { AgentMemory } from '../shared-memory-system';
*
* const memory = new AgentMemory('marketing');
*
* // Add memories
* memory.add('Always use purple for primary buttons', 'preference');
* memory.add('Check inventory every Monday', 'reminder');
* memory.add('Customer John prefers email contact', 'note');
*
* // Retrieve in AI prompts
* const context = memory.getContextSummary();
* const systemPrompt = `You are the Marketing Agent. ${context}`;
*
* // Search
* const buttonPrefs = memory.search('button');
*
* // Use in chat endpoint
* app.post('/api/chat', async (req, res) => {
* const { message } = req.body;
*
* // Auto-detect memory commands
* if (message.toLowerCase().includes('remember:') || message.toLowerCase().includes('note:')) {
* const content = message.split(/remember:|note:/i)[1].trim();
* memory.add(content, 'note');
* return res.json({ response: 'Memory saved!', success: true });
* }
*
* // Include memories in AI context
* const systemPrompt = `You are the Agent. ${memory.getContextSummary()}`;
* // ... rest of AI call
* });
*/