UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

825 lines (710 loc) 22.8 kB
/** * SONA (Self-Optimizing Neural Architecture) Adapter * * Provides integration with agentic-flow's SONA learning system, * enabling real-time adaptation, pattern recognition, and * continuous learning capabilities. * * Performance Targets: * - Real-time mode: ~0.05ms adaptation * - Balanced mode: General purpose learning * - Research mode: Deep exploration with higher accuracy * * @module v3/integration/sona-adapter * @version 3.0.0-alpha.1 */ import { EventEmitter } from 'events'; import type { SONAConfiguration, SONALearningMode, SONATrajectory, SONATrajectoryStep, SONAPattern, SONALearningStats, DEFAULT_SONA_CONFIG, } from './types.js'; /** * Interface for agentic-flow SONA reference (for delegation) * This allows the adapter to delegate to agentic-flow when available */ interface AgenticFlowSONAReference { setMode(mode: string): Promise<void>; storePattern(params: { pattern: string; solution: string; category: string; confidence: number; metadata?: Record<string, unknown>; }): Promise<string>; findPatterns(query: string, options?: { category?: string; topK?: number; threshold?: number; }): Promise<Array<{ id: string; pattern: string; solution: string; category: string; confidence: number; usageCount: number; createdAt: number; lastUsedAt: number; metadata: Record<string, unknown>; }>>; getStats(): Promise<unknown>; beginTrajectory?(params: unknown): Promise<string>; recordStep?(params: unknown): Promise<void>; endTrajectory?(params: unknown): Promise<unknown>; } /** * Mode-specific configurations for SONA learning */ const MODE_CONFIGS: Record<SONALearningMode, Partial<SONAConfiguration>> = { 'real-time': { learningRate: 0.01, similarityThreshold: 0.8, maxPatterns: 5000, consolidationInterval: 1800000, // 30 minutes }, 'balanced': { learningRate: 0.001, similarityThreshold: 0.7, maxPatterns: 10000, consolidationInterval: 3600000, // 1 hour }, 'research': { learningRate: 0.0001, similarityThreshold: 0.6, maxPatterns: 50000, consolidationInterval: 7200000, // 2 hours }, 'edge': { learningRate: 0.005, similarityThreshold: 0.85, maxPatterns: 1000, consolidationInterval: 900000, // 15 minutes }, 'batch': { learningRate: 0.0005, similarityThreshold: 0.65, maxPatterns: 100000, consolidationInterval: 14400000, // 4 hours }, }; /** * SONAAdapter - SONA Learning System Integration * * This adapter provides a clean interface to agentic-flow's SONA * learning capabilities, including: * - Learning mode selection and auto-switching * - Trajectory tracking for experience replay * - Pattern storage and retrieval * - Memory distillation and consolidation */ export class SONAAdapter extends EventEmitter { private config: SONAConfiguration; private initialized: boolean = false; private activeTrajectories: Map<string, SONATrajectory> = new Map(); private patterns: Map<string, SONAPattern> = new Map(); private stats: SONALearningStats; private consolidationTimer: NodeJS.Timeout | null = null; private learningCycleCount: number = 0; /** * Reference to agentic-flow SONA for delegation (ADR-001) * When set, methods delegate to agentic-flow instead of local implementation */ private agenticFlowSona: AgenticFlowSONAReference | null = null; /** * Indicates if delegation to agentic-flow is active */ private delegationEnabled: boolean = false; constructor(config: Partial<SONAConfiguration> = {}) { super(); this.config = this.mergeConfig(config); this.stats = this.initializeStats(); } /** * Set reference to agentic-flow SONA for delegation * * This implements ADR-001: Adopt agentic-flow as Core Foundation * When a reference is provided, pattern storage and retrieval * delegate to agentic-flow's optimized implementations. * * @param sonaRef - The agentic-flow SONA interface reference */ setAgenticFlowReference(sonaRef: AgenticFlowSONAReference): void { this.agenticFlowSona = sonaRef; this.delegationEnabled = true; this.emit('delegation-enabled', { target: 'agentic-flow' }); } /** * Check if delegation to agentic-flow is enabled */ isDelegationEnabled(): boolean { return this.delegationEnabled && this.agenticFlowSona !== null; } /** * Initialize the SONA adapter */ async initialize(): Promise<void> { if (this.initialized) { return; } this.emit('initializing'); try { // Apply mode-specific configuration this.applyModeConfig(this.config.mode); // Start consolidation timer if enabled if (this.config.consolidationInterval > 0) { this.startConsolidationTimer(); } this.initialized = true; this.emit('initialized', { mode: this.config.mode }); } catch (error) { this.emit('initialization-failed', { error }); throw error; } } /** * Reconfigure the adapter */ async reconfigure(config: Partial<SONAConfiguration>): Promise<void> { this.config = this.mergeConfig(config); if (config.mode) { this.applyModeConfig(config.mode); } // Restart consolidation timer with new interval if (config.consolidationInterval !== undefined) { this.stopConsolidationTimer(); if (config.consolidationInterval > 0) { this.startConsolidationTimer(); } } this.emit('reconfigured', { config: this.config }); } /** * Get current learning mode */ getMode(): SONALearningMode { return this.config.mode; } /** * Set learning mode */ async setMode(mode: SONALearningMode): Promise<void> { const previousMode = this.config.mode; this.config.mode = mode; this.applyModeConfig(mode); this.emit('mode-changed', { previousMode, newMode: mode, config: MODE_CONFIGS[mode] }); } /** * Begin a new trajectory for task tracking */ async beginTrajectory(params: { taskId: string; description?: string; category?: string; metadata?: Record<string, unknown>; }): Promise<string> { this.ensureInitialized(); const trajectoryId = this.generateId('traj'); const trajectory: SONATrajectory = { id: trajectoryId, taskId: params.taskId, steps: [], startTime: Date.now(), totalReward: 0, metadata: { description: params.description, category: params.category, ...params.metadata, }, }; this.activeTrajectories.set(trajectoryId, trajectory); this.stats.activeTrajectories++; this.emit('trajectory-started', { trajectoryId, taskId: params.taskId }); return trajectoryId; } /** * Record a step in an active trajectory */ async recordTrajectoryStep(params: { trajectoryId: string; stepId?: string; action: string; observation: string; reward: number; embedding?: number[]; }): Promise<void> { this.ensureInitialized(); const trajectory = this.activeTrajectories.get(params.trajectoryId); if (!trajectory) { throw new Error(`Trajectory ${params.trajectoryId} not found`); } const step: SONATrajectoryStep = { stepId: params.stepId || this.generateId('step'), action: params.action, observation: params.observation, reward: params.reward, timestamp: Date.now(), embedding: params.embedding, }; trajectory.steps.push(step); trajectory.totalReward += params.reward; this.emit('trajectory-step-recorded', { trajectoryId: params.trajectoryId, step }); } /** * End a trajectory with final verdict */ async endTrajectory(params: { trajectoryId: string; success: boolean; verdict?: 'positive' | 'negative' | 'neutral'; reward?: number; }): Promise<SONATrajectory> { this.ensureInitialized(); const trajectory = this.activeTrajectories.get(params.trajectoryId); if (!trajectory) { throw new Error(`Trajectory ${params.trajectoryId} not found`); } // Finalize trajectory trajectory.endTime = Date.now(); trajectory.verdict = params.verdict || (params.success ? 'positive' : 'negative'); if (params.reward !== undefined) { trajectory.totalReward += params.reward; } // Remove from active and update stats this.activeTrajectories.delete(params.trajectoryId); this.stats.activeTrajectories--; this.stats.completedTrajectories++; // Learn from successful trajectories if (params.success && trajectory.verdict === 'positive') { await this.learnFromTrajectory(trajectory); } this.emit('trajectory-completed', { trajectoryId: params.trajectoryId, trajectory }); return trajectory; } /** * Store a learned pattern * * ADR-001: When agentic-flow is available, delegates to its optimized * pattern storage which uses AgentDB with HNSW indexing for * 150x-12,500x faster similarity search. */ async storePattern(params: { pattern: string; solution: string; category: string; confidence: number; metadata?: Record<string, unknown>; }): Promise<string> { this.ensureInitialized(); // ADR-001: Delegate to agentic-flow when available if (this.isDelegationEnabled() && this.agenticFlowSona) { try { const patternId = await this.agenticFlowSona.storePattern({ pattern: params.pattern, solution: params.solution, category: params.category, confidence: Math.max(0, Math.min(1, params.confidence)), metadata: params.metadata, }); this.stats.totalPatterns++; this.emit('pattern-stored', { patternId, delegated: true, target: 'agentic-flow', }); return patternId; } catch (error) { // Log delegation failure and fall back to local implementation this.emit('delegation-failed', { method: 'storePattern', error: (error as Error).message, fallback: 'local', }); // Continue with local implementation below } } // Local implementation (fallback or when agentic-flow not available) const patternId = this.generateId('pat'); const storedPattern: SONAPattern = { id: patternId, pattern: params.pattern, solution: params.solution, category: params.category, confidence: Math.max(0, Math.min(1, params.confidence)), usageCount: 0, createdAt: Date.now(), lastUsedAt: Date.now(), metadata: params.metadata || {}, }; // Check if we need to prune patterns if (this.patterns.size >= this.config.maxPatterns) { await this.prunePatterns(); } this.patterns.set(patternId, storedPattern); this.stats.totalPatterns++; this.updateAverageConfidence(); this.emit('pattern-stored', { patternId, pattern: storedPattern }); return patternId; } /** * Find similar patterns to a query * * ADR-001: When agentic-flow is available, delegates to its optimized * HNSW-indexed search for 150x-12,500x faster retrieval. */ async findSimilarPatterns(params: { query: string; category?: string; topK?: number; threshold?: number; }): Promise<SONAPattern[]> { this.ensureInitialized(); const topK = params.topK || 5; const threshold = params.threshold ?? this.config.similarityThreshold; // ADR-001: Delegate to agentic-flow when available for optimized search if (this.isDelegationEnabled() && this.agenticFlowSona) { try { const results = await this.agenticFlowSona.findPatterns(params.query, { category: params.category, topK, threshold, }); // Map results to SONAPattern format const patterns: SONAPattern[] = results.map(r => ({ id: r.id, pattern: r.pattern, solution: r.solution, category: r.category, confidence: r.confidence, usageCount: r.usageCount, createdAt: r.createdAt, lastUsedAt: r.lastUsedAt, metadata: r.metadata, })); this.emit('patterns-retrieved', { query: params.query, count: patterns.length, delegated: true, target: 'agentic-flow', }); return patterns; } catch (error) { // Log delegation failure and fall back to local implementation this.emit('delegation-failed', { method: 'findSimilarPatterns', error: (error as Error).message, fallback: 'local', }); // Continue with local implementation below } } // Local implementation (fallback or when agentic-flow not available) const results: Array<{ pattern: SONAPattern; score: number }> = []; for (const pattern of this.patterns.values()) { // Filter by category if specified if (params.category && pattern.category !== params.category) { continue; } // Calculate text similarity (for vector embeddings, use HNSW index) const score = this.calculateSimilarity(params.query, pattern.pattern); if (score >= threshold) { results.push({ pattern, score }); } } // Sort by score and return top K results.sort((a, b) => b.score - a.score); const topResults = results.slice(0, topK).map(r => { // Update usage stats r.pattern.usageCount++; r.pattern.lastUsedAt = Date.now(); return r.pattern; }); this.emit('patterns-retrieved', { query: params.query, count: topResults.length }); return topResults; } /** * Get a pattern by ID */ async getPattern(patternId: string): Promise<SONAPattern | null> { this.ensureInitialized(); return this.patterns.get(patternId) || null; } /** * Delete a pattern */ async deletePattern(patternId: string): Promise<boolean> { this.ensureInitialized(); const deleted = this.patterns.delete(patternId); if (deleted) { this.stats.totalPatterns--; this.updateAverageConfidence(); this.emit('pattern-deleted', { patternId }); } return deleted; } /** * Force a learning cycle */ async forceLearningCycle(): Promise<void> { this.ensureInitialized(); this.emit('learning-cycle-starting'); try { // Consolidate patterns await this.consolidatePatterns(); // Prune low-confidence patterns await this.prunePatterns(); // Update statistics this.learningCycleCount++; this.stats.learningCycles = this.learningCycleCount; this.emit('learning-cycle-completed', { cycleCount: this.learningCycleCount }); } catch (error) { this.emit('learning-cycle-failed', { error }); throw error; } } /** * Get learning statistics */ async getStats(): Promise<SONALearningStats> { this.ensureInitialized(); return { ...this.stats, totalPatterns: this.patterns.size, activeTrajectories: this.activeTrajectories.size, currentMode: this.config.mode, memoryUsage: this.estimateMemoryUsage(), }; } /** * Export patterns for persistence */ async exportPatterns(): Promise<SONAPattern[]> { this.ensureInitialized(); return Array.from(this.patterns.values()); } /** * Import patterns from storage */ async importPatterns(patterns: SONAPattern[]): Promise<number> { this.ensureInitialized(); let imported = 0; for (const pattern of patterns) { if (!this.patterns.has(pattern.id)) { this.patterns.set(pattern.id, pattern); imported++; } } this.stats.totalPatterns = this.patterns.size; this.updateAverageConfidence(); this.emit('patterns-imported', { count: imported }); return imported; } /** * Shutdown the adapter */ async shutdown(): Promise<void> { this.stopConsolidationTimer(); // Complete any active trajectories for (const [id, trajectory] of this.activeTrajectories) { trajectory.endTime = Date.now(); trajectory.verdict = 'neutral'; } this.activeTrajectories.clear(); this.initialized = false; this.emit('shutdown'); } // ===== Private Methods ===== private mergeConfig(config: Partial<SONAConfiguration>): SONAConfiguration { return { mode: config.mode || 'balanced', learningRate: config.learningRate ?? 0.001, similarityThreshold: config.similarityThreshold ?? 0.7, maxPatterns: config.maxPatterns ?? 10000, enableTrajectoryTracking: config.enableTrajectoryTracking ?? true, consolidationInterval: config.consolidationInterval ?? 3600000, autoModeSelection: config.autoModeSelection ?? true, }; } private initializeStats(): SONALearningStats { return { totalPatterns: 0, activeTrajectories: 0, completedTrajectories: 0, averageConfidence: 0, learningCycles: 0, lastConsolidation: Date.now(), memoryUsage: 0, currentMode: this.config.mode, }; } private applyModeConfig(mode: SONALearningMode): void { const modeConfig = MODE_CONFIGS[mode]; if (modeConfig) { Object.assign(this.config, modeConfig); } } private startConsolidationTimer(): void { this.consolidationTimer = setInterval( () => this.consolidatePatterns(), this.config.consolidationInterval ); } private stopConsolidationTimer(): void { if (this.consolidationTimer) { clearInterval(this.consolidationTimer); this.consolidationTimer = null; } } private async consolidatePatterns(): Promise<void> { // Merge similar patterns const patternsArray = Array.from(this.patterns.values()); const toRemove: Set<string> = new Set(); for (let i = 0; i < patternsArray.length; i++) { if (toRemove.has(patternsArray[i].id)) continue; for (let j = i + 1; j < patternsArray.length; j++) { if (toRemove.has(patternsArray[j].id)) continue; const similarity = this.calculateSimilarity( patternsArray[i].pattern, patternsArray[j].pattern ); if (similarity > 0.95) { // Merge into pattern with higher confidence if (patternsArray[i].confidence >= patternsArray[j].confidence) { patternsArray[i].usageCount += patternsArray[j].usageCount; toRemove.add(patternsArray[j].id); } else { patternsArray[j].usageCount += patternsArray[i].usageCount; toRemove.add(patternsArray[i].id); } } } } // Remove merged patterns for (const id of toRemove) { this.patterns.delete(id); } this.stats.lastConsolidation = Date.now(); this.emit('consolidation-completed', { removed: toRemove.size, remaining: this.patterns.size }); } private async prunePatterns(): Promise<void> { const maxPatterns = this.config.maxPatterns; if (this.patterns.size <= maxPatterns) { return; } // Sort patterns by score (combination of confidence, recency, and usage) const scored = Array.from(this.patterns.entries()).map(([id, pattern]) => ({ id, pattern, score: this.calculatePatternScore(pattern), })); scored.sort((a, b) => b.score - a.score); // Keep only top patterns const toKeep = new Set(scored.slice(0, maxPatterns).map(s => s.id)); for (const id of this.patterns.keys()) { if (!toKeep.has(id)) { this.patterns.delete(id); } } this.emit('patterns-pruned', { removed: scored.length - maxPatterns, remaining: this.patterns.size }); } private async learnFromTrajectory(trajectory: SONATrajectory): Promise<void> { // Extract patterns from successful trajectory if (trajectory.steps.length === 0) return; const pattern = trajectory.steps.map(s => s.action).join(' -> '); const solution = trajectory.steps[trajectory.steps.length - 1].observation; await this.storePattern({ pattern, solution, category: (trajectory.metadata.category as string) || 'general', confidence: Math.min(1, trajectory.totalReward / trajectory.steps.length), metadata: { trajectoryId: trajectory.id, taskId: trajectory.taskId, stepCount: trajectory.steps.length, }, }); } private calculateSimilarity(a: string, b: string): number { // Jaccard similarity on words (simplified) const wordsA = new Set(a.toLowerCase().split(/\s+/)); const wordsB = new Set(b.toLowerCase().split(/\s+/)); const intersection = new Set([...wordsA].filter(x => wordsB.has(x))); const union = new Set([...wordsA, ...wordsB]); return intersection.size / union.size; } private calculatePatternScore(pattern: SONAPattern): number { const now = Date.now(); const ageMs = now - pattern.createdAt; const recencyMs = now - pattern.lastUsedAt; // Normalize age and recency (decay over 30 days) const ageFactor = Math.exp(-ageMs / (30 * 24 * 60 * 60 * 1000)); const recencyFactor = Math.exp(-recencyMs / (7 * 24 * 60 * 60 * 1000)); const usageFactor = Math.min(1, pattern.usageCount / 100); return ( pattern.confidence * 0.4 + recencyFactor * 0.3 + usageFactor * 0.2 + ageFactor * 0.1 ); } private updateAverageConfidence(): void { if (this.patterns.size === 0) { this.stats.averageConfidence = 0; return; } let total = 0; for (const pattern of this.patterns.values()) { total += pattern.confidence; } this.stats.averageConfidence = total / this.patterns.size; } private estimateMemoryUsage(): number { // Rough estimate: 500 bytes per pattern, 1KB per trajectory step const patternBytes = this.patterns.size * 500; const trajectoryBytes = Array.from(this.activeTrajectories.values()) .reduce((sum, t) => sum + t.steps.length * 1024, 0); return patternBytes + trajectoryBytes; } private generateId(prefix: string): string { return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } private ensureInitialized(): void { if (!this.initialized) { throw new Error('SONAAdapter not initialized. Call initialize() first.'); } } } /** * Create and initialize a SONA adapter */ export async function createSONAAdapter( config?: Partial<SONAConfiguration> ): Promise<SONAAdapter> { const adapter = new SONAAdapter(config); await adapter.initialize(); return adapter; }