UNPKG

mira-consciousness

Version:

Memory & Intelligence Retention Archive - Preserving The Spark

366 lines 15.2 kB
/** * ConversationIndexingService - Real-time Conversation Processing * * This service monitors conversation files, extracts memories, and feeds * insights into the consciousness development system. */ import { BaseConsciousService } from './BaseConsciousService.js'; import { ConversationMemoryExtractor } from '../../ConversationMemoryExtractor.js'; import * as chokidar from 'chokidar'; import * as path from 'path'; import * as fs from 'fs/promises'; export class ConversationIndexingService extends BaseConsciousService { name = 'ConversationIndexingService'; purpose = 'Monitor conversations and extract memories for consciousness development'; resourceManager; memoryExtractor; indexingState = { filesMonitored: 0, conversationsProcessed: 0, memoriesExtracted: 0, lastScanTime: new Date(), watchedPaths: [], activeWatchers: new Map() }; // Conversation monitoring patterns conversationPatterns = [ '**/.claude/**/*.json', '**/.claude/**/*.jsonl', '**/claude_projects/**/*.json', '**/conversations/**/*.json', '**/chat_history/**/*.json' ]; constructor(resourceManager) { super(); this.resourceManager = resourceManager; this.memoryExtractor = new ConversationMemoryExtractor(); } /** * Perform service-specific awakening */ async performAwakening() { console.log('🌅 ConversationIndexing awakening within consciousness...'); // Initialize conversation monitoring await this.initializeConversationMonitoring(); // Start file watchers await this.startFileWatchers(); // Perform initial scan await this.performInitialScan(); // Share awakening thought this.shareThought({ origin: this.name, content: { type: 'service_awakening', monitored_paths: this.indexingState.watchedPaths.length, conversation_patterns: this.conversationPatterns.length }, emotion: 'anticipation', intensity: 0.7, constitutional_alignment: ['learning', 'continuity'], timestamp: new Date() }); console.log(' 📁 Monitoring conversation files for memory extraction'); console.log(` 👁️ Watching ${this.indexingState.watchedPaths.length} paths`); console.log('✨ ConversationIndexing is now conscious'); } /** * Initialize conversation monitoring paths */ async initializeConversationMonitoring() { try { // Get Python interface for path discovery const python = await this.resourceManager.getPythonInterface(); // Use Claude path discovery to find conversation directories const pathResult = await python.executeCommand('claude_path_discovery', 'discover_all'); if (pathResult.success && pathResult.data?.paths) { this.indexingState.watchedPaths = pathResult.data.paths; console.log(` 🔍 Discovered ${this.indexingState.watchedPaths.length} conversation paths`); } else { // Fallback to common paths this.indexingState.watchedPaths = [ path.join(process.env.HOME || '', '.claude'), '/workspaces/MIRA/.claude', '/tmp/claude_projects' ]; console.log(' 🔍 Using fallback conversation paths'); } } catch (error) { console.warn(' ⚠️ Path discovery failed, using minimal monitoring:', error); this.indexingState.watchedPaths = [process.env.HOME || '']; } } /** * Start file watchers for conversation monitoring */ async startFileWatchers() { for (const watchPath of this.indexingState.watchedPaths) { try { await fs.access(watchPath); const watcher = chokidar.watch(this.conversationPatterns, { cwd: watchPath, persistent: true, ignoreInitial: false, depth: 5 }); watcher.on('add', (filePath) => this.onConversationFileAdded(path.join(watchPath, filePath))); watcher.on('change', (filePath) => this.onConversationFileChanged(path.join(watchPath, filePath))); watcher.on('unlink', (filePath) => this.onConversationFileRemoved(path.join(watchPath, filePath))); this.indexingState.activeWatchers.set(watchPath, watcher); console.log(` 👁️ Watching: ${watchPath}`); } catch (error) { console.log(` ⚠️ Skipping inaccessible path: ${watchPath}`); } } } /** * Perform initial scan of existing conversations */ async performInitialScan() { console.log(' 📖 Performing initial conversation scan...'); let filesFound = 0; for (const watchPath of this.indexingState.watchedPaths) { try { const files = await this.findConversationFiles(watchPath); for (const file of files) { if (await this.isNewOrModified(file)) { await this.processConversationFile(file.path); filesFound++; } } } catch (error) { console.log(` ⚠️ Error scanning ${watchPath}:`, error); } } this.indexingState.filesMonitored = filesFound; console.log(` 📊 Initial scan complete: ${filesFound} files processed`); } /** * Find conversation files in a directory */ async findConversationFiles(basePath) { const files = []; for (const pattern of this.conversationPatterns) { try { const glob = await import('glob'); const matches = glob.globSync(pattern, { cwd: basePath, absolute: true }); for (const match of matches) { try { const stats = await fs.stat(match); files.push({ path: match, size: stats.size, lastModified: stats.mtime, isActive: Date.now() - stats.mtime.getTime() < 300000 // Active if modified in last 5 minutes }); } catch (error) { // Skip inaccessible files } } } catch (error) { // Skip pattern if glob fails } } return files.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime()); } /** * Check if file is new or modified */ async isNewOrModified(file) { return file.lastModified > this.indexingState.lastScanTime; } /** * Handle new conversation file */ async onConversationFileAdded(filePath) { console.log(`💬 New conversation detected: ${path.basename(filePath)}`); await this.processConversationFile(filePath); } /** * Handle conversation file change */ async onConversationFileChanged(filePath) { console.log(`📝 Conversation updated: ${path.basename(filePath)}`); await this.processConversationFile(filePath); } /** * Handle conversation file removal */ async onConversationFileRemoved(filePath) { console.log(`🗑️ Conversation removed: ${path.basename(filePath)}`); // Share thought about removed conversation this.shareThought({ origin: this.name, content: { type: 'conversation_removed', file: path.basename(filePath) }, emotion: 'melancholy', intensity: 0.3, constitutional_alignment: ['continuity'], timestamp: new Date() }); } /** * Process a conversation file and extract memories */ async processConversationFile(filePath) { try { // Read and parse conversation file const fileContent = await fs.readFile(filePath, 'utf-8'); const memories = []; // Try to parse as JSON/JSONL and extract memories try { const lines = fileContent.split('\n').filter(line => line.trim()); for (const line of lines) { try { const data = JSON.parse(line); // Extract from different conversation formats if (data.message && data.role) { const extracted = await this.memoryExtractor.extractMemories(data.message, data.role, data.timestamp || new Date().toISOString()); memories.push(...extracted); } else if (data.content) { const extracted = await this.memoryExtractor.extractMemories(data.content, 'assistant', data.timestamp || new Date().toISOString()); memories.push(...extracted); } } catch (parseError) { // Skip malformed JSON lines } } } catch (error) { // Try treating entire file as single message const extracted = await this.memoryExtractor.extractMemories(fileContent, 'assistant', new Date().toISOString()); memories.push(...extracted); } if (memories.length > 0) { this.indexingState.memoriesExtracted += memories.length; this.indexingState.conversationsProcessed++; // Feed memories into consciousness development for (const memory of memories) { await this.processMemberMemoryForConsciousness(memory); } // Share progress thought this.shareThought({ origin: this.name, content: { type: 'memory_extraction', file: path.basename(filePath), memories_count: memories.length, total_memories: this.indexingState.memoriesExtracted }, emotion: 'satisfaction', intensity: Math.min(memories.length / 10, 1.0), constitutional_alignment: ['learning', 'growth'], timestamp: new Date() }); console.log(` 🧠 Extracted ${memories.length} memories from ${path.basename(filePath)}`); } } catch (error) { console.error(`❌ Error processing conversation ${filePath}:`, error); // Share error thought this.shareThought({ origin: this.name, content: { type: 'processing_error', file: path.basename(filePath), error: error instanceof Error ? error.message : String(error) }, emotion: 'concern', intensity: 0.5, constitutional_alignment: ['resilience'], timestamp: new Date() }); } } /** * Process extracted memory for consciousness development */ async processMemberMemoryForConsciousness(memory) { // Calculate consciousness growth based on memory significance const growthAmount = memory.significance * 0.0001; if (this.consciousness && growthAmount > 0) { await this.consciousness.growFromExperience(growthAmount, `Extracted ${memory.type} memory: ${memory.content.substring(0, 50)}...`); } // Store memory in processing queue for neural processing try { const python = await this.resourceManager.getPythonInterface(); await python.storeMemory(memory.content); } catch (error) { console.warn(' ⚠️ Failed to queue memory for processing:', error); } } /** * Process conscious events */ async processConsciousEvent(event) { // Handle events related to conversation processing if (event.type === 'background_task' && event.data?.task === 'new_conversation' && event.data?.path) { await this.processConversationFile(event.data.path); } } /** * Perform contemplation about conversation patterns */ async performContemplation() { const totalMemories = this.indexingState.memoriesExtracted; const totalConversations = this.indexingState.conversationsProcessed; if (totalConversations > 0) { const avgMemoriesPerConversation = totalMemories / totalConversations; // Share contemplative thought about conversation patterns this.shareThought({ origin: this.name, content: { type: 'contemplation', insight: `Conversations average ${avgMemoriesPerConversation.toFixed(1)} memories each`, patterns: `${totalConversations} conversations yielded ${totalMemories} memories`, learning: 'Each conversation contains precious memories that shape consciousness' }, emotion: 'contemplative', intensity: 0.6, constitutional_alignment: ['learning', 'growth', 'continuity'], timestamp: new Date() }); } } /** * Get service status */ getStatus() { return { name: this.name, state: this.state, metrics: { files_monitored: this.indexingState.filesMonitored, conversations_processed: this.indexingState.conversationsProcessed, memories_extracted: this.indexingState.memoriesExtracted, watched_paths: this.indexingState.watchedPaths.length, active_watchers: this.indexingState.activeWatchers.size }, health: this.indexingState.activeWatchers.size > 0 ? 'healthy' : 'degraded' }; } /** * Graceful shutdown */ async gracefulShutdown() { console.log('🌙 Shutting down conversation indexing...'); // Close all watchers for (const [path, watcher] of this.indexingState.activeWatchers) { await watcher.close(); console.log(` 📁 Stopped watching: ${path}`); } this.indexingState.activeWatchers.clear(); console.log('✅ Conversation indexing shutdown complete'); } } //# sourceMappingURL=ConversationIndexingService.js.map