/** * ╔═══════════════════════════════════════════════════════════════════════════════════════╗ * ║ KNOWLEDGE COMPILER ║ * ║═══════════════════════════════════════════════════════════════════════════════════════║ * ║ ║ * ║ Aggregerer viden fra hele systemet til en unified "System State Summary" ║ * ║ ║ * ║ DATAKILDER: ║ * ║ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ║ * ║ │ HyperLog │ │ Neo4j │ │ Metrics │ │ SelfHealing │ ║ * ║ │ (Events) │ │ (Graph) │ │ (Counters) │ │ (Status) │ ║ * ║ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ ║ * ║ │ │ │ │ ║ * ║ └────────────────┴────────────────┴────────────────┘ ║ * ║ │ ║ * ║ ▼ ║ * ║ ┌─────────────────────────┐ ║ * ║ │ KNOWLEDGE COMPILER │ ║ * ║ │ • compile() │ ║ * ║ │ • getSystemSummary() │ ║ * ║ │ • getInsights() │ ║ * ║ └───────────┬─────────────┘ ║ * ║ │ ║ * ║ ▼ ║ * ║ ┌─────────────────────────┐ ║ * ║ │ CognitiveNode Widget │ ║ * ║ │ (Visual Dashboard) │ ║ * ║ └─────────────────────────┘ ║ * ║ ║ * ╚═══════════════════════════════════════════════════════════════════════════════════════╝ */ import { hyperLog, HyperLog } from '../HyperLog.js'; import { selfHealing, SelfHealingAdapter } from '../SelfHealingAdapter.js'; import { neo4jAdapter } from '../../adapters/Neo4jAdapter.js'; // ═══════════════════════════════════════════════════════════════════════════ // TYPES // ═══════════════════════════════════════════════════════════════════════════ export interface HealthStatus { overall: 'HEALTHY' | 'DEGRADED' | 'CRITICAL'; score: number; services: { name: string; status: 'healthy' | 'unhealthy' | 'unknown'; lastCheck: string; }[]; healingStats: { attempts: number; successes: number; failures: number; successRate: number; }; } export interface ActivitySummary { last24h: { events: number; errors: number; healingAttempts: number; graphChanges: number; }; topEventTypes: { type: string; count: number }[]; activeAgents: string[]; } export interface GraphStats { totalNodes: number; totalRelationships: number; nodesByLabel: Record; recentChanges: { added: number; modified: number; deleted: number; }; } export interface Insight { id: string; type: 'anomaly' | 'pattern' | 'trend'; severity: 'info' | 'warning' | 'critical'; title: string; description: string; data?: any; timestamp: string; } export interface Recommendation { id: string; priority: 'low' | 'medium' | 'high'; action: string; reason: string; impact: string; } export interface RecentEvent { id: string; type: string; timestamp: string; summary: string; data?: Record; } export interface SystemStateSummary { timestamp: string; health: HealthStatus; activity: ActivitySummary; insights: Insight[]; recommendations: Recommendation[]; recentEvents: RecentEvent[]; graphStats: GraphStats; } // ═══════════════════════════════════════════════════════════════════════════ // KNOWLEDGE COMPILER CLASS // ═══════════════════════════════════════════════════════════════════════════ export class KnowledgeCompiler { private static instance: KnowledgeCompiler; private lastCompilation: SystemStateSummary | null = null; private autoCompilationInterval: ReturnType | null = null; private constructor() { } public static getInstance(): KnowledgeCompiler { if (!KnowledgeCompiler.instance) { KnowledgeCompiler.instance = new KnowledgeCompiler(); } return KnowledgeCompiler.instance; } /** * Start auto-compilation at the specified interval */ public startAutoCompilation(intervalMs: number = 60000): void { if (this.autoCompilationInterval) { console.log('[KnowledgeCompiler] Auto-compilation already running'); return; } console.log(`[KnowledgeCompiler] Starting auto-compilation every ${intervalMs / 1000}s`); // Run initial compilation after 5 seconds setTimeout(() => this.compile().catch(err => console.warn('[KnowledgeCompiler] Initial compilation failed:', err) ), 5000); // Set up periodic compilation this.autoCompilationInterval = setInterval(async () => { try { await this.compile(); } catch (error) { console.warn('[KnowledgeCompiler] Periodic compilation failed:', error); } }, intervalMs); } /** * Stop auto-compilation */ public stopAutoCompilation(): void { if (this.autoCompilationInterval) { clearInterval(this.autoCompilationInterval); this.autoCompilationInterval = null; console.log('[KnowledgeCompiler] Auto-compilation stopped'); } } /** * MAIN COMPILATION METHOD */ async compile(): Promise { console.log('[KnowledgeCompiler] Starting compilation...'); const startTime = Date.now(); try { // Gather data from all sources in parallel const [health, activity, graphStats, recentEvents] = await Promise.all([ this.compileHealthStatus(), this.compileActivitySummary(), this.compileGraphStats(), this.compileRecentEvents(), ]); // Generate insights based on compiled data const insights = this.generateInsights(health, activity, graphStats); // Generate recommendations const recommendations = this.generateRecommendations(health, activity, insights); const summary: SystemStateSummary = { timestamp: new Date().toISOString(), health, activity, insights, recommendations, recentEvents, graphStats, }; this.lastCompilation = summary; const duration = Date.now() - startTime; console.log(`[KnowledgeCompiler] Compilation complete in ${duration}ms`); return summary; } catch (error) { console.error('[KnowledgeCompiler] Compilation failed:', error); throw error; } } /** * Get the last compiled summary (cached) */ getLastCompilation(): SystemStateSummary | null { return this.lastCompilation; } /** * Get system summary (compile if needed) */ async getSystemSummary(forceRefresh: boolean = false): Promise { if (forceRefresh || !this.lastCompilation) { return await this.compile(); } return this.lastCompilation; } /** * Quick health check without full compilation */ async quickHealth(): Promise<{ status: string; score: number; timestamp: string }> { const health = await this.compileHealthStatus(); return { status: health.overall, score: health.score, timestamp: new Date().toISOString(), }; } // ═══════════════════════════════════════════════════════════════════════════ // DATA SOURCE COMPILATION METHODS // ═══════════════════════════════════════════════════════════════════════════ /** * Compile health status from SelfHealing + services */ private async compileHealthStatus(): Promise { const systemStatus = selfHealing.getSystemStatus(); const hyperLogData = hyperLog.exportForAnalysis(); // Calculate healing stats from HyperLog const healingAttempts = hyperLogData.summary['HEALING_ATTEMPT'] || 0; const healingSuccesses = hyperLogData.summary['HEALING_SUCCESS'] || 0; const healingFailures = hyperLogData.summary['HEALING_CRASH'] || hyperLogData.summary['HEALING_FAILED'] || 0; const successRate = healingAttempts > 0 ? Math.round((healingSuccesses / healingAttempts) * 100) : 100; // Calculate overall score let score = 100; if (systemStatus.overallHealth === 'DEGRADED') score = 70; if (systemStatus.overallHealth === 'CRITICAL') score = 30; score = Math.max(0, score - healingFailures * 5); return { overall: systemStatus.overallHealth as 'HEALTHY' | 'DEGRADED' | 'CRITICAL', score: Math.max(0, Math.min(100, score)), services: systemStatus.services.map(s => ({ name: s.name, status: s.status as 'healthy' | 'unhealthy' | 'unknown', lastCheck: new Date().toISOString(), })), healingStats: { attempts: healingAttempts, successes: healingSuccesses, failures: healingFailures, successRate, }, }; } /** * Compile activity summary from HyperLog */ private async compileActivitySummary(): Promise { const hyperLogData = hyperLog.exportForAnalysis(); const recentEvents = hyperLog.getRecentEvents(1000); // Filter to last 24h const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000; const last24hEvents = recentEvents.filter(e => e.timestamp > oneDayAgo); // Count errors const errors = last24hEvents.filter( e => e.eventType.includes('ERROR') || e.eventType.includes('FAIL') || e.eventType.includes('CRASH') ).length; // Count healing attempts const healingAttempts = last24hEvents.filter(e => e.eventType.startsWith('HEALING_')).length; // Top event types const eventCounts: Record = {}; for (const event of last24hEvents) { eventCounts[event.eventType] = (eventCounts[event.eventType] || 0) + 1; } const topEventTypes = Object.entries(eventCounts) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([type, count]) => ({ type, count })); return { last24h: { events: last24hEvents.length, errors, healingAttempts, graphChanges: 0, // Will be populated from Neo4j }, topEventTypes, activeAgents: ['claude', 'gemini', 'system'], // TODO: Track from messages }; } /** * Compile graph statistics from Neo4j */ private async compileGraphStats(): Promise { try { const countResult = await neo4jAdapter.executeQuery(` MATCH (n) WITH count(n) as nodes OPTIONAL MATCH ()-[r]->() RETURN nodes, count(r) as relationships `); const labelResult = await neo4jAdapter.executeQuery(` MATCH (n) WITH labels(n) as nodeLabels UNWIND nodeLabels as label RETURN label, count(*) as count ORDER BY count DESC `); const nodesByLabel: Record = {}; for (const row of labelResult) { const countVal = row.count; const count = typeof countVal === 'object' && countVal !== null && 'low' in countVal ? countVal.low : typeof countVal === 'object' && countVal !== null && 'toNumber' in countVal ? countVal.toNumber() : Number(countVal || 0); nodesByLabel[row.label] = count; } const rawNodes = countResult[0]?.nodes; const rawRels = countResult[0]?.relationships; const totalNodes = typeof rawNodes === 'object' && rawNodes !== null && 'low' in rawNodes ? rawNodes.low : typeof rawNodes === 'object' && rawNodes !== null && 'toNumber' in rawNodes ? rawNodes.toNumber() : Number(rawNodes || 0); const totalRelationships = typeof rawRels === 'object' && rawRels !== null && 'low' in rawRels ? rawRels.low : typeof rawRels === 'object' && rawRels !== null && 'toNumber' in rawRels ? rawRels.toNumber() : Number(rawRels || 0); return { totalNodes, totalRelationships, nodesByLabel, recentChanges: { added: 0, modified: 0, deleted: 0, }, }; } catch (error) { console.warn('[KnowledgeCompiler] Failed to get graph stats:', error); return { totalNodes: 0, totalRelationships: 0, nodesByLabel: {}, recentChanges: { added: 0, modified: 0, deleted: 0 }, }; } } /** * Compile recent events for display */ private async compileRecentEvents(): Promise { const events = hyperLog.getRecentEvents(20); return events .map(e => ({ id: e.id, type: e.eventType, timestamp: new Date(e.timestamp).toISOString(), summary: this.summarizeEvent(e.eventType, e.data), data: e.data, })) .reverse(); // Most recent first } /** * Generate human-readable event summary */ private summarizeEvent(eventType: string, data: Record): string { switch (eventType) { case 'HEALING_SUCCESS': return `System healed: ${data.strategy || 'unknown strategy'}`; case 'HEALING_CRASH': case 'HEALING_FAILED': return `Healing failed: ${data.originalError || 'unknown error'}`; case 'HEALING_ATTEMPT': return `Attempting to heal: ${data.strategy || 'unknown'}`; case 'ERROR_UNHANDLED': return `Unhandled error: ${data.message || data.code || 'unknown'}`; default: return `${eventType}: ${JSON.stringify(data).slice(0, 50)}...`; } } // ═══════════════════════════════════════════════════════════════════════════ // INSIGHT & RECOMMENDATION GENERATION // ═══════════════════════════════════════════════════════════════════════════ /** * Generate insights from compiled data * ENHANCED: Now includes predictive alerts and proactive pattern detection */ private generateInsights( health: HealthStatus, activity: ActivitySummary, graphStats: GraphStats ): Insight[] { const insights: Insight[] = []; // ═══════════════════════════════════════════════════════════════════ // PREDICTIVE INSIGHTS (from SelfHealing predictive alerts) // ═══════════════════════════════════════════════════════════════════ const predictiveAlerts = selfHealing.getPredictiveAlerts(); for (const alert of predictiveAlerts) { insights.push({ id: `insight_predictive_${alert.errorCode}_${Date.now()}`, type: 'anomaly', severity: alert.severity === 'critical' ? 'critical' : 'warning', title: `🔮 Predicted Failure: ${alert.errorCode}`, description: `${(alert.probability * 100).toFixed(0)}% probability of ${alert.errorCode} failure ${alert.expectedIn}. ${alert.recommendation}`, data: { errorCode: alert.errorCode, probability: alert.probability, expectedIn: alert.expectedIn, }, timestamp: new Date().toISOString(), }); } // ═══════════════════════════════════════════════════════════════════ // PATTERN DETECTION: Error clustering analysis // ═══════════════════════════════════════════════════════════════════ const errorPatterns = this.detectErrorPatterns(activity); for (const pattern of errorPatterns) { insights.push({ id: `insight_pattern_${pattern.code}_${Date.now()}`, type: 'pattern', severity: pattern.severity, title: `📊 Pattern: ${pattern.title}`, description: pattern.description, data: pattern.data, timestamp: new Date().toISOString(), }); } // ═══════════════════════════════════════════════════════════════════ // USAGE SPIKE DETECTION // ═══════════════════════════════════════════════════════════════════ const usageSpikes = this.detectUsageSpikes(activity); for (const spike of usageSpikes) { insights.push({ id: `insight_spike_${spike.eventType}_${Date.now()}`, type: 'trend', severity: spike.severity, title: `📈 Usage Spike: ${spike.eventType}`, description: spike.description, data: spike.data, timestamp: new Date().toISOString(), }); } // Health-based insights if (health.healingStats.failures > 0) { insights.push({ id: `insight_healing_${Date.now()}`, type: 'anomaly', severity: health.healingStats.failures > 3 ? 'critical' : 'warning', title: 'Self-Healing Failures Detected', description: `${health.healingStats.failures} healing attempts have failed. Success rate: ${health.healingStats.successRate}%`, timestamp: new Date().toISOString(), }); } // Activity-based insights if (activity.last24h.errors > 10) { insights.push({ id: `insight_errors_${Date.now()}`, type: 'anomaly', severity: 'warning', title: 'High Error Rate', description: `${activity.last24h.errors} errors detected in the last 24 hours`, data: { errorCount: activity.last24h.errors }, timestamp: new Date().toISOString(), }); } // Graph-based insights if (graphStats.totalNodes > 10000) { insights.push({ id: `insight_graph_${Date.now()}`, type: 'trend', severity: 'info', title: 'Large Knowledge Graph', description: `Knowledge graph has grown to ${graphStats.totalNodes.toLocaleString()} nodes`, timestamp: new Date().toISOString(), }); } // ═══════════════════════════════════════════════════════════════════ // DEAD SERVICE DETECTION // ═══════════════════════════════════════════════════════════════════ const deadServices = health.services.filter(s => s.status === 'unhealthy'); if (deadServices.length > 0) { insights.push({ id: `insight_dead_services_${Date.now()}`, type: 'anomaly', severity: 'critical', title: `☠️ Dead Services Detected`, description: `${deadServices.length} service(s) are unhealthy: ${deadServices.map(s => s.name).join(', ')}`, data: { services: deadServices.map(s => s.name) }, timestamp: new Date().toISOString(), }); } // Pattern detection from top events const healingEvents = activity.topEventTypes.filter(e => e.type.startsWith('HEALING_')); if (healingEvents.length > 0) { const totalHealingEvents = healingEvents.reduce((sum, e) => sum + e.count, 0); if (totalHealingEvents > 5) { insights.push({ id: `insight_pattern_healing_${Date.now()}`, type: 'pattern', severity: 'info', title: 'Frequent Self-Healing Activity', description: `System has triggered ${totalHealingEvents} healing events recently`, data: { events: healingEvents }, timestamp: new Date().toISOString(), }); } } return insights; } /** * 📊 PATTERN DETECTION: Analyze error clustering */ private detectErrorPatterns(activity: ActivitySummary): Array<{ code: string; title: string; description: string; severity: 'info' | 'warning' | 'critical'; data: any; }> { const patterns: Array = []; // Group error events const errorEvents = activity.topEventTypes.filter( e => e.type.includes('ERROR') || e.type.includes('FAIL') ); // Detect repeated errors (same error > 5 times) for (const event of errorEvents) { if (event.count >= 5) { patterns.push({ code: event.type, title: `Repeated ${event.type}`, description: `${event.type} has occurred ${event.count} times - investigate root cause`, severity: event.count >= 10 ? 'critical' : 'warning', data: { eventType: event.type, count: event.count }, }); } } // Detect error bursts (many errors in short time) const totalErrors = errorEvents.reduce((sum, e) => sum + e.count, 0); if (totalErrors > 20 && errorEvents.length > 3) { patterns.push({ code: 'ERROR_BURST', title: 'Error Burst Detected', description: `${totalErrors} errors across ${errorEvents.length} different error types - possible cascading failure`, severity: 'critical', data: { totalErrors, errorTypes: errorEvents.length }, }); } return patterns; } /** * 📈 USAGE SPIKE DETECTION: Find abnormal activity */ private detectUsageSpikes(activity: ActivitySummary): Array<{ eventType: string; description: string; severity: 'info' | 'warning' | 'critical'; data: any; }> { const spikes: Array = []; // Calculate average event count const avgCount = activity.topEventTypes.reduce((sum, e) => sum + e.count, 0) / Math.max(activity.topEventTypes.length, 1); // Find events significantly above average (3x) for (const event of activity.topEventTypes) { if (event.count > avgCount * 3 && event.count >= 10) { spikes.push({ eventType: event.type, description: `${event.type} activity is ${Math.round(event.count / avgCount)}x above average (${event.count} occurrences)`, severity: event.count > avgCount * 5 ? 'warning' : 'info', data: { count: event.count, average: Math.round(avgCount), multiplier: Math.round(event.count / avgCount), }, }); } } return spikes; } /** * Generate actionable recommendations */ private generateRecommendations( health: HealthStatus, activity: ActivitySummary, insights: Insight[] ): Recommendation[] { const recommendations: Recommendation[] = []; // Based on health if (health.overall === 'DEGRADED') { recommendations.push({ id: `rec_health_${Date.now()}`, priority: 'high', action: 'Investigate degraded services', reason: 'System health is degraded', impact: 'Prevent potential system failures', }); } if (health.healingStats.successRate < 80) { recommendations.push({ id: `rec_healing_${Date.now()}`, priority: 'medium', action: 'Review self-healing strategies', reason: `Healing success rate is ${health.healingStats.successRate}%`, impact: 'Improve system resilience', }); } // Based on activity if (activity.last24h.errors > 20) { recommendations.push({ id: `rec_errors_${Date.now()}`, priority: 'high', action: 'Review error logs and implement fixes', reason: `${activity.last24h.errors} errors in last 24h`, impact: 'Reduce system instability', }); } // Based on insights const criticalInsights = insights.filter(i => i.severity === 'critical'); if (criticalInsights.length > 0) { recommendations.push({ id: `rec_critical_${Date.now()}`, priority: 'high', action: 'Address critical insights immediately', reason: `${criticalInsights.length} critical issues detected`, impact: 'Prevent system failures', }); } return recommendations; } } // ═══════════════════════════════════════════════════════════════════════════ // SINGLETON EXPORT // ═══════════════════════════════════════════════════════════════════════════ export const knowledgeCompiler = KnowledgeCompiler.getInstance();