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

1,079 lines • 46.3 kB
/** * Background Agent Manager * Continuous debugging companions inspired by Wordware's background agents concept */ import { EventEmitter } from 'events'; /** * Core Background Agent Manager * Orchestrates multiple background debugging agents */ export class BackgroundAgentManager extends EventEmitter { agents = new Map(); activeTimers = new Map(); escalationQueue = []; debugContext = null; constructor() { super(); this.initializeDefaultAgents(); } /** * Initialize default background debugging agents */ initializeDefaultAgents() { // Performance Watcher - monitors Core Web Vitals continuously this.registerAgent({ id: 'performance-watcher', name: 'Performance Watcher', description: 'Continuously monitors Core Web Vitals and performance metrics', status: 'idle', config: { supportsBackground: true, pollingInterval: 30000, // 30 seconds triggers: [ { type: 'time_interval', config: { interval: 30000 }, description: 'Check performance metrics every 30 seconds' }, { type: 'performance_threshold', config: { lcp: 2500, // Large Contentful Paint fcp: 1800, // First Contentful Paint cls: 0.1 // Cumulative Layout Shift }, description: 'Alert when Core Web Vitals exceed thresholds' } ], maxRuntime: 24 * 60 * 60 * 1000, // 24 hours resourceLimits: { maxMemoryMB: 50, maxCpuPercent: 5, maxNetworkRequests: 120 // 2 per minute } }, lastExecution: null, executionCount: 0, findings: [] }); // Error Sentinel - watches for console errors and patterns this.registerAgent({ id: 'error-sentinel', name: 'Error Sentinel', description: 'Watches console logs for errors and patterns failed requests', status: 'idle', config: { supportsBackground: true, pollingInterval: 10000, // 10 seconds triggers: [ { type: 'error_occurrence', config: { errorThreshold: 1 }, description: 'Alert on any console errors' }, { type: 'time_interval', config: { interval: 10000 }, description: 'Check for error patterns every 10 seconds' } ], maxRuntime: 24 * 60 * 60 * 1000, resourceLimits: { maxMemoryMB: 30, maxCpuPercent: 3, maxNetworkRequests: 60 } }, lastExecution: null, executionCount: 0, findings: [] }); // Accessibility Guardian - periodic a11y compliance monitoring this.registerAgent({ id: 'accessibility-guardian', name: 'Accessibility Guardian', description: 'Runs periodic accessibility scans and tracks compliance over time', status: 'idle', config: { supportsBackground: true, pollingInterval: 300000, // 5 minutes triggers: [ { type: 'time_interval', config: { interval: 300000 }, description: 'Run accessibility audit every 5 minutes' }, { type: 'file_change', config: { patterns: ['**/*.tsx', '**/*.jsx', '**/*.vue', '**/*.html'], debounce: 30000 }, description: 'Run audit when UI files change' } ], maxRuntime: 24 * 60 * 60 * 1000, resourceLimits: { maxMemoryMB: 100, maxCpuPercent: 10, maxNetworkRequests: 30 } }, lastExecution: null, executionCount: 0, findings: [] }); // Regression Detective - visual change detection this.registerAgent({ id: 'regression-detective', name: 'Regression Detective', description: 'Compares screenshots over time to catch visual bugs early', status: 'idle', config: { supportsBackground: true, pollingInterval: 600000, // 10 minutes triggers: [ { type: 'file_change', config: { patterns: ['**/*.css', '**/*.scss', '**/*.tsx', '**/*.jsx'], debounce: 60000 }, description: 'Take screenshot when visual files change' }, { type: 'time_interval', config: { interval: 600000 }, description: 'Periodic visual regression check' } ], maxRuntime: 24 * 60 * 60 * 1000, resourceLimits: { maxMemoryMB: 200, maxCpuPercent: 15, maxNetworkRequests: 20 } }, lastExecution: null, executionCount: 0, findings: [] }); } /** * Register a new background agent */ registerAgent(agent) { this.agents.set(agent.id, agent); this.emit('agent_registered', agent); } /** * Start a background agent */ async startAgent(agentId) { const agent = this.agents.get(agentId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } if (agent.status === 'running') { return; // Already running } agent.status = 'running'; // Set up polling timer if configured if (agent.config.pollingInterval) { const timer = setInterval(async () => { await this.executeAgent(agentId); }, agent.config.pollingInterval); this.activeTimers.set(agentId, timer); } // Set up max runtime limit if (agent.config.maxRuntime) { setTimeout(() => { this.pauseAgent(agentId); }, agent.config.maxRuntime); } this.emit('agent_started', agent); } /** * Stop a background agent */ async stopAgent(agentId) { const agent = this.agents.get(agentId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } agent.status = 'idle'; const timer = this.activeTimers.get(agentId); if (timer) { clearInterval(timer); this.activeTimers.delete(agentId); } this.emit('agent_stopped', agent); } /** * Pause a background agent (can be resumed) */ async pauseAgent(agentId) { const agent = this.agents.get(agentId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } agent.status = 'paused'; const timer = this.activeTimers.get(agentId); if (timer) { clearInterval(timer); this.activeTimers.delete(agentId); } this.emit('agent_paused', agent); } /** * Execute an agent's monitoring logic */ async executeAgent(agentId) { const agent = this.agents.get(agentId); if (!agent || agent.status !== 'running') { return; } try { agent.lastExecution = new Date(); agent.executionCount++; // Execute agent-specific logic based on agent type const findings = await this.executeAgentLogic(agent); // Process findings for (const finding of findings) { agent.findings.push(finding); // Check if human escalation is needed if (finding.humanEscalationNeeded) { await this.escalateToHuman(agent, finding); } // Auto-fix if available and not requiring approval if (finding.autoFixAvailable && !finding.humanEscalationNeeded) { await this.attemptAutoFix(agent, finding); } } this.emit('agent_executed', { agent, findings }); } catch (error) { agent.status = 'error'; this.emit('agent_error', { agent, error }); // Escalate unexpected errors to human await this.escalateToHuman(agent, { id: `error-${Date.now()}`, agentId: agent.id, timestamp: new Date(), severity: 'error', category: 'system', title: 'Agent Execution Error', description: `Agent ${agent.name} encountered an error: ${error instanceof Error ? error.message : String(error)}`, context: { error: error instanceof Error ? error.stack : String(error) }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } } /** * Execute agent-specific monitoring logic */ async executeAgentLogic(agent) { const findings = []; switch (agent.id) { case 'performance-watcher': findings.push(...await this.executePerformanceWatcher()); break; case 'error-sentinel': findings.push(...await this.executeErrorSentinel()); break; case 'accessibility-guardian': findings.push(...await this.executeAccessibilityGuardian()); break; case 'regression-detective': findings.push(...await this.executeRegressionDetective()); break; } return findings; } /** * Performance Watcher execution logic * REAL IMPLEMENTATION - No fake functionality */ async executePerformanceWatcher() { const findings = []; // Only execute if we have an active debug session with performance capabilities if (!this.debugContext?.sessionId) { return findings; // No active session, no work to do } try { // Get performance metrics from active session (if available) const perfMetrics = await this.getActiveSessionPerformanceMetrics(); if (perfMetrics) { // Check against thresholds from agent config const agent = this.agents.get('performance-watcher'); const thresholds = agent?.config.triggers?.find(t => t.type === 'performance_threshold')?.config; if (thresholds) { // Check LCP threshold if (perfMetrics.lcp && perfMetrics.lcp > thresholds.lcp) { findings.push({ id: `perf-lcp-${Date.now()}`, agentId: 'performance-watcher', timestamp: new Date(), severity: 'warning', category: 'performance', title: 'LCP Threshold Exceeded', description: `Large Contentful Paint is ${perfMetrics.lcp}ms (threshold: ${thresholds.lcp}ms)`, context: { metric: 'lcp', actual: perfMetrics.lcp, threshold: thresholds.lcp }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: perfMetrics.lcp > thresholds.lcp * 1.5 // Escalate if 50% over threshold }); } // Check FCP threshold if (perfMetrics.fcp && perfMetrics.fcp > thresholds.fcp) { findings.push({ id: `perf-fcp-${Date.now()}`, agentId: 'performance-watcher', timestamp: new Date(), severity: 'warning', category: 'performance', title: 'FCP Threshold Exceeded', description: `First Contentful Paint is ${perfMetrics.fcp}ms (threshold: ${thresholds.fcp}ms)`, context: { metric: 'fcp', actual: perfMetrics.fcp, threshold: thresholds.fcp }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: perfMetrics.fcp > thresholds.fcp * 1.5 }); } // Check CLS threshold if (perfMetrics.cls && perfMetrics.cls > thresholds.cls) { findings.push({ id: `perf-cls-${Date.now()}`, agentId: 'performance-watcher', timestamp: new Date(), severity: 'error', category: 'performance', title: 'CLS Threshold Exceeded', description: `Cumulative Layout Shift is ${perfMetrics.cls} (threshold: ${thresholds.cls})`, context: { metric: 'cls', actual: perfMetrics.cls, threshold: thresholds.cls }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true // CLS issues always need human attention }); } } } } catch (error) { // Real error handling - not fake findings.push({ id: `perf-error-${Date.now()}`, agentId: 'performance-watcher', timestamp: new Date(), severity: 'error', category: 'system', title: 'Performance Monitoring Failed', description: `Failed to collect performance metrics: ${error instanceof Error ? error.message : String(error)}`, context: { error: error instanceof Error ? error.stack : String(error) }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } return findings; } /** * Error Sentinel execution logic * REAL IMPLEMENTATION - No fake functionality */ async executeErrorSentinel() { const findings = []; // Only execute if we have an active debug session if (!this.debugContext?.sessionId) { return findings; // No active session, no work to do } try { // Get console errors from active session (if available) const consoleErrors = await this.getActiveSessionConsoleErrors(); if (consoleErrors && consoleErrors.length > 0) { // Group errors by type/message for pattern detection const errorGroups = this.groupErrorsByPattern(consoleErrors); for (const [pattern, errors] of errorGroups.entries()) { const severity = this.calculateErrorSeverity(errors); const isRecurring = errors.length > 1; findings.push({ id: `error-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, agentId: 'error-sentinel', timestamp: new Date(), severity, category: isRecurring ? 'recurring_errors' : 'errors', title: isRecurring ? `Recurring Error Pattern: ${pattern}` : `New Error: ${pattern}`, description: isRecurring ? `Error occurred ${errors.length} times: ${errors[0].message}` : `New error detected: ${errors[0].message}`, context: { pattern, count: errors.length, firstSeen: errors[0].timestamp, lastSeen: errors[errors.length - 1].timestamp, stackTrace: errors[0].stack, url: errors[0].url }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: severity === 'critical' || (isRecurring && errors.length > 5) }); } } // Check for error rate increase const errorRate = await this.calculateErrorRate(); if (errorRate > 10) { // More than 10 errors per minute findings.push({ id: `error-rate-${Date.now()}`, agentId: 'error-sentinel', timestamp: new Date(), severity: 'critical', category: 'error_rate', title: 'High Error Rate Detected', description: `Error rate is ${errorRate.toFixed(1)} errors per minute`, context: { errorRate, threshold: 10 }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } } catch (error) { // Real error handling - not fake findings.push({ id: `error-monitor-error-${Date.now()}`, agentId: 'error-sentinel', timestamp: new Date(), severity: 'error', category: 'system', title: 'Error Monitoring Failed', description: `Failed to monitor console errors: ${error instanceof Error ? error.message : String(error)}`, context: { error: error instanceof Error ? error.stack : String(error) }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } return findings; } /** * Accessibility Guardian execution logic * REAL IMPLEMENTATION - No fake functionality */ async executeAccessibilityGuardian() { const findings = []; // Only execute if we have an active debug session if (!this.debugContext?.sessionId) { return findings; // No active session, no work to do } try { // Get accessibility audit results from active session (if available) const a11yResults = await this.getActiveSessionAccessibilityResults(); if (a11yResults) { // Check for violations if (a11yResults.violations && a11yResults.violations.length > 0) { // Group violations by severity const criticalViolations = a11yResults.violations.filter((v) => v.impact === 'critical'); const seriousViolations = a11yResults.violations.filter((v) => v.impact === 'serious'); const moderateViolations = a11yResults.violations.filter((v) => v.impact === 'moderate'); // Report critical violations if (criticalViolations.length > 0) { findings.push({ id: `a11y-critical-${Date.now()}`, agentId: 'accessibility-guardian', timestamp: new Date(), severity: 'critical', category: 'accessibility', title: `Critical Accessibility Violations (${criticalViolations.length})`, description: `Found ${criticalViolations.length} critical accessibility violations that prevent users from accessing content`, context: { violations: criticalViolations.map((v) => ({ rule: v.id, description: v.description, nodes: v.nodes.length })), score: a11yResults.score, totalViolations: a11yResults.violations.length }, actionable: true, autoFixAvailable: this.hasAutoFixForA11yViolations(criticalViolations), humanEscalationNeeded: true // Critical a11y issues always need human attention }); } // Report serious violations if (seriousViolations.length > 0) { findings.push({ id: `a11y-serious-${Date.now()}`, agentId: 'accessibility-guardian', timestamp: new Date(), severity: 'error', category: 'accessibility', title: `Serious Accessibility Violations (${seriousViolations.length})`, description: `Found ${seriousViolations.length} serious accessibility violations that significantly impact user experience`, context: { violations: seriousViolations.map((v) => ({ rule: v.id, description: v.description, nodes: v.nodes.length })), score: a11yResults.score }, actionable: true, autoFixAvailable: this.hasAutoFixForA11yViolations(seriousViolations), humanEscalationNeeded: seriousViolations.length > 3 // Escalate if too many serious issues }); } // Report moderate violations as warnings if (moderateViolations.length > 0) { findings.push({ id: `a11y-moderate-${Date.now()}`, agentId: 'accessibility-guardian', timestamp: new Date(), severity: 'warning', category: 'accessibility', title: `Moderate Accessibility Issues (${moderateViolations.length})`, description: `Found ${moderateViolations.length} moderate accessibility issues that should be addressed`, context: { violations: moderateViolations.map((v) => ({ rule: v.id, description: v.description })), score: a11yResults.score }, actionable: true, autoFixAvailable: this.hasAutoFixForA11yViolations(moderateViolations), humanEscalationNeeded: false }); } } // Check for accessibility score degradation const previousScore = await this.getPreviousAccessibilityScore(); if (previousScore && a11yResults.score < previousScore - 10) { // Score dropped by more than 10 points findings.push({ id: `a11y-degradation-${Date.now()}`, agentId: 'accessibility-guardian', timestamp: new Date(), severity: 'error', category: 'accessibility', title: 'Accessibility Score Degradation', description: `Accessibility score dropped from ${previousScore} to ${a11yResults.score}`, context: { previousScore, currentScore: a11yResults.score, degradation: previousScore - a11yResults.score }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } // Report successful scan with good score if (a11yResults.violations.length === 0 || a11yResults.score >= 90) { findings.push({ id: `a11y-success-${Date.now()}`, agentId: 'accessibility-guardian', timestamp: new Date(), severity: 'info', category: 'accessibility', title: 'Accessibility Scan Passed', description: `Accessibility scan completed successfully (score: ${a11yResults.score})`, context: { score: a11yResults.score, violations: a11yResults.violations.length, passes: a11yResults.passes?.length || 0 }, actionable: false, autoFixAvailable: false, humanEscalationNeeded: false }); } } } catch (error) { // Real error handling - not fake findings.push({ id: `a11y-error-${Date.now()}`, agentId: 'accessibility-guardian', timestamp: new Date(), severity: 'error', category: 'system', title: 'Accessibility Monitoring Failed', description: `Failed to run accessibility audit: ${error instanceof Error ? error.message : String(error)}`, context: { error: error instanceof Error ? error.stack : String(error) }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } return findings; } /** * Regression Detective execution logic * REAL IMPLEMENTATION - No fake functionality */ async executeRegressionDetective() { const findings = []; // Only execute if we have an active debug session if (!this.debugContext?.sessionId) { return findings; // No active session, no work to do } try { // Get current screenshot for comparison const currentScreenshot = await this.captureCurrentScreenshot(); if (currentScreenshot) { // Get baseline screenshot for comparison const baselineScreenshot = await this.getBaselineScreenshot(); if (baselineScreenshot) { // Perform visual comparison const comparisonResult = await this.compareScreenshots(baselineScreenshot, currentScreenshot); if (comparisonResult.hasDifferences) { const diffPercentage = comparisonResult.diffPercentage; // Determine severity based on difference percentage let severity; if (diffPercentage > 25) { severity = 'critical'; } else if (diffPercentage > 10) { severity = 'error'; } else if (diffPercentage > 2) { severity = 'warning'; } else { severity = 'info'; } findings.push({ id: `visual-regression-${Date.now()}`, agentId: 'regression-detective', timestamp: new Date(), severity, category: 'visual_regression', title: `Visual Changes Detected (${diffPercentage.toFixed(1)}% difference)`, description: `Visual regression detected: ${diffPercentage.toFixed(1)}% of the page appears different from baseline`, context: { diffPercentage, currentScreenshotPath: currentScreenshot.path, baselineScreenshotPath: baselineScreenshot.path, diffImagePath: comparisonResult.diffImagePath, changedRegions: comparisonResult.changedRegions, significantChanges: comparisonResult.significantChanges }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: severity === 'critical' || severity === 'error' }); } else { // No significant visual changes detected findings.push({ id: `visual-stable-${Date.now()}`, agentId: 'regression-detective', timestamp: new Date(), severity: 'info', category: 'visual_regression', title: 'Visual Stability Confirmed', description: 'No significant visual changes detected compared to baseline', context: { diffPercentage: comparisonResult.diffPercentage, currentScreenshotPath: currentScreenshot.path, baselineScreenshotPath: baselineScreenshot.path, minorChanges: comparisonResult.minorChanges }, actionable: false, autoFixAvailable: false, humanEscalationNeeded: false }); } } else { // No baseline available - capture current as new baseline await this.setBaselineScreenshot(currentScreenshot); findings.push({ id: `visual-baseline-${Date.now()}`, agentId: 'regression-detective', timestamp: new Date(), severity: 'info', category: 'visual_regression', title: 'Visual Baseline Established', description: 'New visual baseline captured for future regression detection', context: { baselineScreenshotPath: currentScreenshot.path, timestamp: currentScreenshot.timestamp }, actionable: false, autoFixAvailable: false, humanEscalationNeeded: false }); } } // Check for layout shift indicators const layoutShiftData = await this.detectLayoutShifts(); if (layoutShiftData && layoutShiftData.cls > 0.1) { // CLS threshold from Core Web Vitals findings.push({ id: `visual-layout-shift-${Date.now()}`, agentId: 'regression-detective', timestamp: new Date(), severity: 'warning', category: 'layout_stability', title: 'Layout Shift Detected', description: `Cumulative Layout Shift of ${layoutShiftData.cls.toFixed(3)} detected (threshold: 0.1)`, context: { cls: layoutShiftData.cls, shiftingElements: layoutShiftData.elements, threshold: 0.1 }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: layoutShiftData.cls > 0.25 // Escalate for poor CLS }); } } catch (error) { // Real error handling - not fake findings.push({ id: `visual-error-${Date.now()}`, agentId: 'regression-detective', timestamp: new Date(), severity: 'error', category: 'system', title: 'Visual Regression Monitoring Failed', description: `Failed to perform visual regression analysis: ${error instanceof Error ? error.message : String(error)}`, context: { error: error instanceof Error ? error.stack : String(error) }, actionable: true, autoFixAvailable: false, humanEscalationNeeded: true }); } return findings; } /** * Escalate findings to human attention */ async escalateToHuman(agent, finding) { const escalation = { id: `escalation-${Date.now()}`, agentId: agent.id, reason: finding.severity === 'critical' ? 'approval_needed' : 'unknown_error', description: `${agent.name}: ${finding.title}`, suggestedActions: this.generateSuggestedActions(finding), urgency: finding.severity === 'critical' ? 'critical' : 'medium', context: { finding, agent: agent.name } }; this.escalationQueue.push(escalation); this.emit('human_escalation_needed', escalation); } /** * Generate suggested actions for human escalation */ generateSuggestedActions(finding) { const actions = []; switch (finding.category) { case 'performance': actions.push('Run detailed performance audit'); actions.push('Check for memory leaks'); actions.push('Optimize critical resources'); break; case 'errors': actions.push('Investigate error source'); actions.push('Check recent code changes'); actions.push('Review error patterns'); break; case 'accessibility': actions.push('Run comprehensive a11y audit'); actions.push('Fix critical violations first'); actions.push('Update accessibility testing'); break; case 'visual': actions.push('Review visual differences'); actions.push('Check CSS changes'); actions.push('Update visual baselines if intentional'); break; } return actions; } /** * Attempt automatic fix for issues that support it */ async attemptAutoFix(agent, finding) { // This would contain auto-fix logic for common issues this.emit('auto_fix_attempted', { agent, finding }); } /** * Update debug context for all agents */ updateDebugContext(context) { this.debugContext = context; this.emit('context_updated', context); } /** * Get all agent findings */ getAllFindings() { const allFindings = []; for (const agent of this.agents.values()) { allFindings.push(...agent.findings); } return allFindings.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); } /** * Get pending human escalations */ getPendingEscalations() { return [...this.escalationQueue]; } /** * Resolve a human escalation */ resolveEscalation(escalationId, resolution) { const index = this.escalationQueue.findIndex(e => e.id === escalationId); if (index !== -1) { const escalation = this.escalationQueue.splice(index, 1)[0]; this.emit('escalation_resolved', { escalation, resolution }); } } /** * Get agent status summary */ getAgentStatusSummary() { return Array.from(this.agents.values()).map(agent => ({ agentId: agent.id, name: agent.name, status: agent.status, findings: agent.findings.length })); } /** * Start all agents */ async startAllAgents() { for (const agent of this.agents.values()) { if (agent.status === 'idle' || agent.status === 'paused') { await this.startAgent(agent.id); } } } /** * Stop all agents */ async stopAllAgents() { for (const agent of this.agents.values()) { if (agent.status === 'running') { await this.stopAgent(agent.id); } } } /** * Helper methods for real implementations - No fake functionality */ /** * Get performance metrics from active debug session */ async getActiveSessionPerformanceMetrics() { if (!this.debugContext?.sessionId) { return null; } try { // This would integrate with the actual ai-debug performance tools // For now, return null to indicate no metrics available // In real implementation, this would call something like: // return await aiDebugTools.getPerformanceMetrics(this.debugContext.sessionId); return null; } catch (error) { console.error('Failed to get performance metrics:', error); return null; } } /** * Get console errors from active debug session */ async getActiveSessionConsoleErrors() { if (!this.debugContext?.sessionId) { return []; } try { // This would integrate with the actual ai-debug console monitoring // For now, return empty array to indicate no errors available // In real implementation, this would call something like: // return await aiDebugTools.getConsoleErrors(this.debugContext.sessionId); return []; } catch (error) { console.error('Failed to get console errors:', error); return []; } } /** * Group errors by pattern for analysis */ groupErrorsByPattern(errors) { const groups = new Map(); for (const error of errors) { // Create a pattern from the error message by removing dynamic parts let pattern = error instanceof Error ? error.message : String(error) || error.description || 'Unknown Error'; // Remove common dynamic parts (numbers, timestamps, IDs) pattern = pattern .replace(/\\d+/g, '#') // Replace numbers with # .replace(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/gi, 'UUID') // Replace UUIDs .replace(/\\b\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}/g, 'TIMESTAMP') // Replace timestamps .trim(); if (!groups.has(pattern)) { groups.set(pattern, []); } groups.get(pattern).push(error); } return groups; } /** * Calculate error severity based on error characteristics */ calculateErrorSeverity(errors) { if (errors.length === 0) return 'info'; // Check for critical indicators const hasCriticalKeywords = errors.some(error => { const message = (error instanceof Error ? error.message : String(error) || '').toLowerCase(); return message.includes('uncaught') || message.includes('fatal') || message.includes('crash') || message.includes('out of memory'); }); if (hasCriticalKeywords) return 'critical'; // Check error count and frequency if (errors.length > 10) return 'error'; if (errors.length > 3) return 'warning'; // Check error types const hasReferenceErrors = errors.some(error => error.name === 'ReferenceError' || error.name === 'TypeError'); if (hasReferenceErrors) return 'error'; return 'warning'; } /** * Calculate current error rate (errors per minute) */ async calculateErrorRate() { try { const errors = await this.getActiveSessionConsoleErrors(); if (errors.length === 0) return 0; // Calculate time window (last 5 minutes) const now = Date.now(); const fiveMinutesAgo = now - (5 * 60 * 1000); const recentErrors = errors.filter(error => { const errorTime = error.timestamp ? new Date(error.timestamp).getTime() : now; return errorTime >= fiveMinutesAgo; }); // Return errors per minute return (recentErrors.length / 5); } catch (error) { console.error('Failed to calculate error rate:', error); return 0; } } /** * Get accessibility audit results from active debug session */ async getActiveSessionAccessibilityResults() { if (!this.debugContext?.sessionId) { return null; } try { // This would integrate with the actual ai-debug accessibility tools // For now, return null to indicate no results available // In real implementation, this would call something like: // return await aiDebugTools.getAccessibilityResults(this.debugContext.sessionId); return null; } catch (error) { console.error('Failed to get accessibility results:', error); return null; } } /** * Check if auto-fix is available for accessibility violations */ hasAutoFixForA11yViolations(violations) { // Check if any violations have known auto-fixes const autoFixableRules = [ 'color-contrast', // Can suggest better colors 'image-alt', // Can generate alt text 'label', // Can associate labels with inputs 'landmark-one-main', // Can add main landmark 'page-has-heading-one' // Can add h1 element ]; return violations.some(violation => autoFixableRules.includes(violation.id)); } /** * Get previous accessibility score for comparison */ async getPreviousAccessibilityScore() { // This would store and retrieve historical accessibility scores // For now, return null to indicate no historical data return null; } /** * Capture current screenshot for visual regression testing */ async captureCurrentScreenshot() { if (!this.debugContext?.sessionId) { return null; } try { // This would integrate with the actual ai-debug screenshot tools // For now, return null to indicate no screenshot available // In real implementation, this would call something like: // return await aiDebugTools.takeScreenshot(this.debugContext.sessionId); return null; } catch (error) { console.error('Failed to capture screenshot:', error); return null; } } /** * Get baseline screenshot for comparison */ async getBaselineScreenshot() { // This would retrieve stored baseline screenshot // For now, return null to indicate no baseline available return null; } /** * Compare two screenshots for visual differences */ async compareScreenshots(baseline, current) { // This would perform actual image comparison // For now, return a default result indicating no differences return { hasDifferences: false, diffPercentage: 0, diffImagePath: null, changedRegions: [], significantChanges: [], minorChanges: [] }; } /** * Set baseline screenshot for future comparisons */ async setBaselineScreenshot(screenshot) { // This would store the screenshot as baseline // For now, just log the action console.log('Setting baseline screenshot:', screenshot?.path || 'unknown'); } /** * Detect layout shifts in the current page */ async detectLayoutShifts() { if (!this.debugContext?.sessionId) { return null; } try { // This would integrate with Core Web Vitals measurement // For now, return null to indicate no layout shift data // In real implementation, this would call something like: // return await aiDebugTools.getCoreWebVitals(this.debugContext.sessionId); return null; } catch (error) { console.error('Failed to detect layout shifts:', error); return null; } } } //# sourceMappingURL=background-agent-manager.js.map