thoughtmcp
Version:
AI that thinks more like humans do - MCP server with human-like cognitive architecture for enhanced reasoning, memory, and self-monitoring
532 lines • 19.4 kB
JavaScript
/**
* Integrated Memory System
*
* Coordinates episodic memory, semantic memory, and consolidation engine
* to provide a unified memory interface for the cognitive architecture.
*/
import { PersistenceManager, } from "../utils/persistence/index.js";
import { ConsolidationEngine, } from "./ConsolidationEngine.js";
import { EpisodicMemory } from "./EpisodicMemory.js";
import { SemanticMemory } from "./SemanticMemory.js";
export class MemorySystem {
episodicMemory;
semanticMemory;
consolidationEngine;
persistenceManager;
config;
initialized = false;
lastActivity = 0;
consolidationTimer;
decayTimer;
constructor(config) {
this.config = {
episodic: {},
semantic: {},
consolidation: {},
persistence: {},
consolidation_interval_ms: 60000, // 1 minute
auto_consolidation: true,
memory_decay_interval_ms: 300000, // 5 minutes
auto_decay: true,
persistence_enabled: true,
auto_save_enabled: true,
auto_recovery_enabled: true,
...config,
};
this.episodicMemory = new EpisodicMemory(this.config.episodic);
this.semanticMemory = new SemanticMemory(this.config.semantic);
this.consolidationEngine = new ConsolidationEngine(this.config.consolidation);
// Initialize persistence manager if enabled
if (this.config.persistence_enabled) {
this.persistenceManager = new PersistenceManager(this.config.persistence);
}
}
async initialize(config) {
if (config) {
this.config = { ...this.config, ...config };
}
// Initialize all memory components
await this.episodicMemory.initialize(this.config.episodic);
await this.semanticMemory.initialize(this.config.semantic);
await this.consolidationEngine.initialize(this.config.consolidation);
// Initialize persistence manager if enabled
if (this.config.persistence_enabled && this.persistenceManager) {
await this.persistenceManager.initialize();
// Attempt recovery if enabled
if (this.config.auto_recovery_enabled) {
await this.attemptRecovery();
}
// Start auto-save if enabled
if (this.config.auto_save_enabled) {
this.persistenceManager.startAutoSave(async () => {
await this.saveToStorage();
});
}
}
// Start automatic processes
if (this.config.auto_consolidation) {
this.startConsolidationProcess();
}
if (this.config.auto_decay) {
this.startDecayProcess();
}
this.initialized = true;
this.lastActivity = Date.now();
}
async process(input) {
// Generic process method - route to appropriate memory operation
const inputObj = input;
if (inputObj?.operation === "store") {
return this.storeExperience(inputObj.experience);
}
else if (inputObj?.operation === "retrieve") {
return this.retrieveMemories(inputObj.cue, inputObj.threshold);
}
else if (inputObj?.operation === "consolidate") {
return this.runConsolidation();
}
throw new Error("Invalid operation for MemorySystem.process()");
}
reset() {
this.episodicMemory.reset();
this.semanticMemory.reset();
this.consolidationEngine.reset();
if (this.consolidationTimer) {
clearInterval(this.consolidationTimer);
}
if (this.decayTimer) {
clearInterval(this.decayTimer);
}
this.lastActivity = Date.now();
}
getStatus() {
const episodicStatus = this.episodicMemory.getStatus();
const semanticStatus = this.semanticMemory.getStatus();
// Note: consolidationStatus available but not used in current status logic
return {
name: "MemorySystem",
initialized: this.initialized &&
episodicStatus.initialized &&
semanticStatus.initialized,
active: episodicStatus.active || semanticStatus.active,
last_activity: Math.max(this.lastActivity, Math.max(episodicStatus.last_activity, semanticStatus.last_activity)),
};
}
/**
* Store an experience in both episodic and semantic memory
*/
async storeExperience(experience) {
const startTime = Date.now();
this.lastActivity = startTime;
try {
// Create episode for episodic memory
const episode = {
content: experience.content,
context: experience.context,
timestamp: startTime,
emotional_tags: experience.emotional_tags || [],
importance: experience.importance,
decay_factor: 1.0,
};
// Store in episodic memory
const episodicId = this.episodicMemory.store(episode);
// Extract concepts for semantic memory
const concepts = this.extractConceptsFromExperience(experience);
let semanticId;
if (concepts.length > 0) {
// Store the primary concept
semanticId = this.semanticMemory.store(concepts[0]);
// Store additional concepts and create relations
for (let i = 1; i < concepts.length; i++) {
const conceptId = this.semanticMemory.store(concepts[i]);
this.semanticMemory.addRelation(semanticId, conceptId, "related_to", 0.5 + experience.importance * 0.3);
}
}
const result = {
episodic_id: episodicId,
storage_time_ms: Date.now() - startTime,
success: true,
};
if (semanticId) {
result.semantic_id = semanticId;
}
return result;
}
catch {
return {
episodic_id: "",
storage_time_ms: Date.now() - startTime,
success: false,
};
}
}
/**
* Retrieve memories from both episodic and semantic systems
*/
async retrieveMemories(cue, threshold = 0.3) {
const startTime = Date.now();
this.lastActivity = startTime;
// Retrieve from both memory systems in parallel
const [episodicMemories, semanticConcepts] = await Promise.all([
Promise.resolve(this.episodicMemory.retrieve(cue, threshold)),
Promise.resolve(this.semanticMemory.retrieve(cue, threshold)),
]);
// Compute combined relevance score
const combinedRelevance = this.computeCombinedRelevance(episodicMemories, semanticConcepts, cue);
return {
episodic_memories: episodicMemories,
semantic_concepts: semanticConcepts,
combined_relevance: combinedRelevance,
retrieval_time_ms: Date.now() - startTime,
};
}
/**
* Store episodic memory directly
*/
storeEpisode(episode) {
this.lastActivity = Date.now();
return this.episodicMemory.store(episode);
}
/**
* Store semantic concept directly
*/
storeConcept(concept) {
this.lastActivity = Date.now();
return this.semanticMemory.store(concept);
}
/**
* Get episodes by time range
*/
getEpisodesByTimeRange(startTime, endTime) {
return this.episodicMemory.getEpisodesByTimeRange(startTime, endTime);
}
/**
* Get episodes by context
*/
getEpisodesByContext(contextKey, contextValue) {
return this.episodicMemory.getEpisodesByContext(contextKey, contextValue);
}
/**
* Get related concepts
*/
getRelatedConcepts(conceptId) {
return this.semanticMemory.getRelated(conceptId);
}
/**
* Find similar concepts
*/
findSimilarConcepts(conceptId, maxResults = 10) {
return this.semanticMemory.findSimilarConcepts(conceptId, maxResults);
}
/**
* Run consolidation process manually
*/
async runConsolidation() {
this.lastActivity = Date.now();
// Get episodes ready for consolidation
const episodesToConsolidate = this.episodicMemory.consolidate();
if (episodesToConsolidate.length === 0) {
return {
patterns_extracted: 0,
concepts_created: 0,
relations_strengthened: 0,
episodes_processed: 0,
pruned_memories: 0,
};
}
// Run consolidation
const newConcepts = this.consolidationEngine.consolidate(episodesToConsolidate);
// Store new concepts in semantic memory
for (const concept of newConcepts) {
this.semanticMemory.store(concept);
}
// Apply decay to both memory systems
this.episodicMemory.decay();
this.semanticMemory.applyDecay();
return (this.consolidationEngine.getLastConsolidationResult() || {
patterns_extracted: 0,
concepts_created: newConcepts.length,
relations_strengthened: 0,
episodes_processed: episodesToConsolidate.length,
pruned_memories: 0,
});
}
/**
* Get memory statistics
*/
getMemoryStats() {
return {
episodic_count: this.episodicMemory.getSize(),
semantic_count: this.semanticMemory.getActiveConcepts().length,
consolidation_history: this.consolidationEngine.getConsolidationStats(),
last_consolidation: this.consolidationEngine.getLastConsolidationResult(),
};
}
/**
* Save current memory state to persistent storage
*/
async saveToStorage() {
if (!this.persistenceManager) {
throw new Error("Persistence not enabled");
}
const episodes = Array.from(this.episodicMemory["episodes"].values());
const concepts = Array.from(this.semanticMemory["concepts"].values());
const relations = Array.from(this.semanticMemory["relations"].values());
const lastConsolidation = this.consolidationEngine.getLastConsolidationResult()
?.patterns_extracted || 0;
await this.persistenceManager.saveMemorySystem(episodes, concepts, relations, lastConsolidation);
}
/**
* Load memory state from persistent storage
*/
async loadFromStorage() {
if (!this.persistenceManager) {
throw new Error("Persistence not enabled");
}
const data = await this.persistenceManager.loadMemorySystem();
if (!data) {
return false;
}
// Clear current memory
this.episodicMemory.reset();
this.semanticMemory.reset();
// Load episodes
for (const episode of data.episodicMemories) {
this.episodicMemory.store(episode);
}
// Load concepts
for (const concept of data.semanticConcepts) {
this.semanticMemory.store(concept);
}
// Load relations
for (const relation of data.semanticRelations) {
this.semanticMemory.addRelation(relation.from, relation.to, relation.type, relation.strength);
}
return true;
}
/**
* Create a backup of current memory state
*/
async createBackup(backupId) {
if (!this.persistenceManager) {
throw new Error("Persistence not enabled");
}
// Save current state first
await this.saveToStorage();
// Create backup
return this.persistenceManager.createBackup(backupId);
}
/**
* Restore memory state from a backup
*/
async restoreFromBackup(backupId) {
if (!this.persistenceManager) {
throw new Error("Persistence not enabled");
}
const data = await this.persistenceManager.restoreFromBackup(backupId);
// Clear current memory
this.episodicMemory.reset();
this.semanticMemory.reset();
// Load episodes
for (const episode of data.episodicMemories) {
this.episodicMemory.store(episode);
}
// Load concepts
for (const concept of data.semanticConcepts) {
this.semanticMemory.store(concept);
}
// Load relations
for (const relation of data.semanticRelations) {
this.semanticMemory.addRelation(relation.from, relation.to, relation.type, relation.strength);
}
}
/**
* List available backups
*/
async listBackups() {
if (!this.persistenceManager) {
throw new Error("Persistence not enabled");
}
return this.persistenceManager.listBackups();
}
/**
* Simulate time passage for memory decay testing
*/
async simulateTimePassage(milliseconds) {
// Apply decay to episodic memories
if (this.episodicMemory.simulateDecay) {
await this.episodicMemory.simulateDecay(milliseconds);
}
// Apply decay to semantic memories if applicable
if (this.semanticMemory.simulateDecay) {
await this.semanticMemory.simulateDecay(milliseconds);
}
}
/**
* Delete a backup
*/
async deleteBackup(backupId) {
if (!this.persistenceManager) {
throw new Error("Persistence not enabled");
}
await this.persistenceManager.deleteBackup(backupId);
}
/**
* Get persistence status
*/
getPersistenceStatus() {
if (!this.persistenceManager) {
return { enabled: false };
}
return {
enabled: true,
...this.persistenceManager.getStatus(),
};
}
/**
* Attempt to recover from the most recent backup
*/
async attemptRecovery() {
if (!this.persistenceManager) {
return;
}
try {
// First try to load from normal storage
const loaded = await this.loadFromStorage();
if (loaded) {
return;
}
// If that fails, try recovery from backup
const recoveredData = await this.persistenceManager.attemptRecovery();
if (recoveredData) {
// Clear current memory
this.episodicMemory.reset();
this.semanticMemory.reset();
// Load recovered data
for (const episode of recoveredData.episodicMemories) {
this.episodicMemory.store(episode);
}
for (const concept of recoveredData.semanticConcepts) {
this.semanticMemory.store(concept);
}
for (const relation of recoveredData.semanticRelations) {
this.semanticMemory.addRelation(relation.from, relation.to, relation.type, relation.strength);
}
console.log("Memory system recovered from backup");
}
}
catch (error) {
console.error("Recovery attempt failed:", error);
}
}
/**
* Shutdown the memory system
*/
async shutdown() {
// Save current state before shutdown if persistence is enabled
if (this.persistenceManager && this.config.auto_save_enabled) {
try {
await this.saveToStorage();
}
catch (error) {
console.error("Failed to save memory state during shutdown:", error);
}
}
if (this.consolidationTimer) {
clearInterval(this.consolidationTimer);
this.consolidationTimer = undefined;
}
if (this.decayTimer) {
clearInterval(this.decayTimer);
this.decayTimer = undefined;
}
if (this.persistenceManager) {
await this.persistenceManager.shutdown();
}
}
// Private helper methods
startConsolidationProcess() {
this.consolidationTimer = setInterval(async () => {
try {
await this.runConsolidation();
}
catch (error) {
console.error("Consolidation process error:", error);
}
}, this.config.consolidation_interval_ms);
}
startDecayProcess() {
this.decayTimer = setInterval(() => {
try {
this.episodicMemory.decay();
this.semanticMemory.applyDecay();
}
catch (error) {
console.error("Decay process error:", error);
}
}, this.config.memory_decay_interval_ms);
}
extractConceptsFromExperience(experience) {
const concepts = [];
// Extract main concept from content
const mainConcept = {
id: this.generateConceptId(experience.content),
content: experience.content,
relations: [],
activation: experience.importance,
last_accessed: Date.now(),
};
concepts.push(mainConcept);
// Extract context-based concepts
if (experience.context.domain) {
const domainConcept = {
id: `domain_${experience.context.domain}`,
content: { type: "domain", value: experience.context.domain },
relations: [],
activation: experience.importance * 0.5,
last_accessed: Date.now(),
};
concepts.push(domainConcept);
}
// Extract emotional concepts
if (experience.emotional_tags && experience.emotional_tags.length > 0) {
for (const tag of experience.emotional_tags) {
const emotionalConcept = {
id: `emotion_${tag}`,
content: { type: "emotion", value: tag },
relations: [],
activation: experience.importance * 0.3,
last_accessed: Date.now(),
};
concepts.push(emotionalConcept);
}
}
return concepts;
}
generateConceptId(content) {
const contentStr = JSON.stringify(content);
let hash = 0;
for (let i = 0; i < contentStr.length; i++) {
const char = contentStr.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return `concept_${Math.abs(hash).toString(16)}`;
}
computeCombinedRelevance(episodes, concepts, _cue) {
let relevance = 0;
// Episodic relevance
if (episodes.length > 0) {
const episodicRelevance = episodes.reduce((sum, ep) => sum + ep.importance, 0) / episodes.length;
relevance += episodicRelevance * 0.6;
}
// Semantic relevance
if (concepts.length > 0) {
const semanticRelevance = concepts.reduce((sum, concept) => sum + concept.activation, 0) /
concepts.length;
relevance += semanticRelevance * 0.4;
}
return Math.min(relevance, 1.0);
}
}
//# sourceMappingURL=MemorySystem.js.map