UNPKG

ai-debug-local-mcp

Version:

๐ŸŽฏ ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh

276 lines โ€ข 10.4 kB
/** * Advanced Memory Manager for AI-Debug MCP Server * Implements comprehensive memory optimization strategies */ import { AbortSignalManager } from './abort-signal-manager.js'; export class AdvancedMemoryManager { static instance; memoryHistory = []; sessionMemoryMap = new Map(); MAX_HISTORY = 100; SESSION_TTL = 30 * 60 * 1000; // 30 minutes MAX_SESSIONS = 50; // Maximum concurrent sessions MEMORY_PRESSURE_THRESHOLD = 0.98; // 98% of heap - Node.js naturally uses high heap % GC_INTERVAL = 5 * 60 * 1000; // 5 minutes constructor() { this.startMemoryMonitoring(); } static getInstance() { if (!AdvancedMemoryManager.instance) { AdvancedMemoryManager.instance = new AdvancedMemoryManager(); } return AdvancedMemoryManager.instance; } /** * Start continuous memory monitoring */ startMemoryMonitoring() { setInterval(() => { this.recordMemoryProfile(); this.checkMemoryPressure(); this.cleanupExpiredSessions(); }, this.GC_INTERVAL); } /** * Record current memory profile */ recordMemoryProfile() { const usage = process.memoryUsage(); const profile = { heapUsed: usage.heapUsed, heapTotal: usage.heapTotal, external: usage.external, rss: usage.rss, timestamp: Date.now() }; this.memoryHistory.push(profile); if (this.memoryHistory.length > this.MAX_HISTORY) { this.memoryHistory.shift(); } } /** * Check for memory pressure and take action */ checkMemoryPressure() { const current = process.memoryUsage(); const heapUsageRatio = current.heapUsed / current.heapTotal; const heapUsedMB = current.heapUsed / 1024 / 1024; if (heapUsageRatio > this.MEMORY_PRESSURE_THRESHOLD || heapUsedMB > 2048) { console.warn(`๐Ÿšจ Memory pressure detected: ${Math.round(heapUsageRatio * 100)}% heap usage (${Math.round(heapUsedMB)} MB)`); this.performEmergencyCleanup(); } } /** * Perform emergency memory cleanup */ async performEmergencyCleanup() { console.log('๐Ÿงน Performing emergency memory cleanup...'); // 1. Clean up expired sessions await this.cleanupExpiredSessions(); // 2. Limit active sessions to most recent await this.limitActiveSessions(Math.floor(this.MAX_SESSIONS * 0.7)); // Keep 70% of max // 3. Force garbage collection if available if (global.gc) { global.gc(); console.log('โ™ป๏ธ Forced garbage collection completed'); } // 4. Clear memory history to free space this.memoryHistory = this.memoryHistory.slice(-20); // Keep only last 20 entries // 5. Emergency AbortSignal cleanup to prevent EventTarget memory leaks AbortSignalManager.emergencyCleanup(); console.log('โœ… Emergency cleanup completed'); this.logMemoryUsage('post-cleanup'); } /** * Register a new session */ registerSession(sessionId, estimatedMemoryMB = 10) { const now = Date.now(); this.sessionMemoryMap.set(sessionId, { sessionId, createdAt: now, lastAccessed: now, resourceCount: 0, memoryFootprint: estimatedMemoryMB * 1024 * 1024 }); // Check if we're at session limit if (this.sessionMemoryMap.size > this.MAX_SESSIONS) { console.warn(`โš ๏ธ Session limit exceeded (${this.sessionMemoryMap.size}/${this.MAX_SESSIONS})`); this.cleanupOldestSessions(5); // Remove 5 oldest sessions } } /** * Update session access time */ touchSession(sessionId) { const session = this.sessionMemoryMap.get(sessionId); if (session) { session.lastAccessed = Date.now(); } } /** * Add resource to session tracking */ addResource(sessionId, resourceMemoryMB = 5) { const session = this.sessionMemoryMap.get(sessionId); if (session) { session.resourceCount++; session.memoryFootprint += resourceMemoryMB * 1024 * 1024; session.lastAccessed = Date.now(); } } /** * Remove session and free memory tracking */ removeSession(sessionId) { this.sessionMemoryMap.delete(sessionId); } /** * Clean up expired sessions */ async cleanupExpiredSessions() { const now = Date.now(); const expiredSessions = []; for (const [sessionId, session] of this.sessionMemoryMap.entries()) { if (now - session.lastAccessed > this.SESSION_TTL) { expiredSessions.push(sessionId); } } // Remove expired sessions for (const sessionId of expiredSessions) { this.sessionMemoryMap.delete(sessionId); } if (expiredSessions.length > 0) { console.log(`๐Ÿงน Cleaned up ${expiredSessions.length} expired sessions`); } return expiredSessions; } /** * Limit active sessions to most recent N sessions */ async limitActiveSessions(maxSessions) { if (this.sessionMemoryMap.size <= maxSessions) { return; } // Sort sessions by last accessed time (oldest first) const sessions = Array.from(this.sessionMemoryMap.entries()) .sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed); const sessionsToRemove = sessions.slice(0, this.sessionMemoryMap.size - maxSessions); for (const [sessionId] of sessionsToRemove) { this.sessionMemoryMap.delete(sessionId); } console.log(`๐Ÿ“‰ Limited sessions to ${maxSessions} (removed ${sessionsToRemove.length} oldest)`); } /** * Clean up N oldest sessions */ cleanupOldestSessions(count) { const sessions = Array.from(this.sessionMemoryMap.entries()) .sort(([, a], [, b]) => a.lastAccessed - b.lastAccessed) .slice(0, count); for (const [sessionId] of sessions) { this.sessionMemoryMap.delete(sessionId); } console.log(`๐Ÿงน Removed ${sessions.length} oldest sessions to free memory`); } /** * Get memory usage statistics */ getMemoryStats() { const usage = process.memoryUsage(); const current = { heapUsed: usage.heapUsed, heapTotal: usage.heapTotal, external: usage.external, rss: usage.rss, timestamp: Date.now() }; const sessions = Array.from(this.sessionMemoryMap.values()); const totalMemory = sessions.reduce((sum, s) => sum + s.memoryFootprint, 0); const averageMemory = sessions.length > 0 ? totalMemory / sessions.length : 0; const oldestSession = sessions.reduce((oldest, s) => !oldest || s.lastAccessed < oldest.lastAccessed ? s : oldest, undefined); const heapUsagePercent = (current.heapUsed / current.heapTotal) * 100; const heapUsedMB = current.heapUsed / 1024 / 1024; return { current, sessions: { total: sessions.length, totalMemoryMB: Math.round(totalMemory / 1024 / 1024), averageMemoryMB: Math.round(averageMemory / 1024 / 1024), oldestSessionAge: oldestSession ? Date.now() - oldestSession.lastAccessed : 0 }, pressure: { isUnderPressure: heapUsagePercent > (this.MEMORY_PRESSURE_THRESHOLD * 100) || heapUsedMB > 2048, heapUsagePercent: Math.round(heapUsagePercent), heapUsedMB: Math.round(heapUsedMB) }, abortSignals: AbortSignalManager.getStats() }; } /** * Log comprehensive memory usage */ logMemoryUsage(context = 'general') { const stats = this.getMemoryStats(); console.log(`๐Ÿ“Š Memory Statistics (${context}):`); console.log(` Heap: ${stats.pressure.heapUsedMB} MB (${stats.pressure.heapUsagePercent}%)`); console.log(` Sessions: ${stats.sessions.total} active (${stats.sessions.totalMemoryMB} MB)`); console.log(` Average per session: ${stats.sessions.averageMemoryMB} MB`); if (stats.pressure.isUnderPressure) { console.log(` โš ๏ธ Memory pressure detected!`); } else { console.log(` โœ… Memory usage healthy`); } } /** * Get tool loading strategy based on memory availability */ getToolLoadingStrategy() { const stats = this.getMemoryStats(); if (stats.pressure.heapUsedMB > 1500 || stats.sessions.total > 40) { return { strategy: 'minimal', batchSize: 25, reason: 'High memory pressure - loading essential tools only' }; } else if (stats.pressure.heapUsedMB > 800 || stats.sessions.total > 20) { return { strategy: 'batch', batchSize: 100, reason: 'Moderate memory usage - loading tools in batches' }; } else { return { strategy: 'all', batchSize: 300, reason: 'Low memory usage - loading all tools' }; } } /** * Optimize tool loading based on memory constraints */ shouldLoadToolCategory(category) { const strategy = this.getToolLoadingStrategy(); // Essential categories that should always load const essentialCategories = ['core', 'debug', 'session']; if (essentialCategories.includes(category)) { return true; } // Load all categories if memory allows if (strategy.strategy === 'all') { return true; } // In minimal mode, only load essentials if (strategy.strategy === 'minimal') { return false; } // In batch mode, load common categories const commonCategories = ['flutter', 'performance', 'audit']; return commonCategories.includes(category); } } //# sourceMappingURL=advanced-memory-manager.js.map