UNPKG

@sethdouglasford/claude-flow

Version:

Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology

405 lines 16.3 kB
/** * Enhanced Swarm Coordinator * Implements hierarchical model strategy with intelligent task decomposition */ import { EventEmitter } from "events"; import { logger } from "../core/logger.js"; import { CodeAgent } from "./agents/code-agent.js"; export class EnhancedSwarmCoordinator extends EventEmitter { agents = new Map(); taskQueue = []; activeTasksMap = new Map(); modelConfig; contextCache = new Map(); strategy; constructor(config) { super(); this.modelConfig = config.modelConfig; this.strategy = config.strategy; // Initialize specialized agents with hierarchical model configuration this.initializeAgents(config.maxAgents || 5); } initializeAgents(maxAgents) { // Create specialized agents with hierarchical model configuration for (let i = 0; i < maxAgents; i++) { const agentId = `enhanced-agent-${i}`; const agent = new CodeAgent(agentId, { models: { primary: this.modelConfig.primary, apply: this.modelConfig.apply, review: this.modelConfig.review, }, threshold: this.modelConfig.threshold, }); // Set up agent event listeners agent.on("taskCompleted", this.handleAgentTaskCompleted.bind(this)); agent.on("taskError", this.handleAgentTaskError.bind(this)); this.agents.set(agentId, agent); logger.info(`Initialized ${agent.getType()} agent: ${agentId}`); } } async addObjective(objective) { logger.info(`Adding objective: ${objective}`); // Decompose objective using primary model (complex reasoning) const tasks = await this.decomposeObjective(objective); // Analyze each task complexity and assign appropriate models const analyzedTasks = await Promise.all(tasks.map(task => this.analyzeTaskComplexity(task))); // Add tasks to queue with enhanced metadata for (const analysis of analyzedTasks) { const enhancedTask = { ...analysis.task, metadata: { ...analysis.task.metadata, customProperties: { complexity: analysis.complexity, suggestedModel: analysis.suggestedModel, estimatedTokens: analysis.estimatedTokens, requiredContext: analysis.requiredContext, }, }, }; this.taskQueue.push(enhancedTask); } const objectiveId = `obj-${Date.now()}`; logger.info(`Created objective ${objectiveId} with ${analyzedTasks.length} tasks`); this.emit("objectiveAdded", { objectiveId, tasks: analyzedTasks.length }); // Start processing tasks asynchronously this.processTasks().catch(error => { logger.error("Error processing tasks:", error); this.emit("error", error); }); return objectiveId; } async decomposeObjective(objective) { // Use primary model for complex objective decomposition logger.info(`Decomposing objective with primary model: ${this.modelConfig.primary}`); const timestamp = Date.now(); // This would integrate with actual LLM to decompose the objective // For now, return a simplified decomposition const tasks = [ { id: { id: `task-${timestamp}-1`, swarmId: "enhanced-swarm", sequence: 1, priority: 1, }, name: `Analyze ${objective}`, description: `Analyze the requirements for: ${objective}`, status: "created", priority: "high", type: "analysis", requirements: { capabilities: ["analysis", "planning"], tools: [], permissions: [], }, constraints: { dependencies: [], dependents: [], conflicts: [], }, input: { objective }, instructions: `Analyze the requirements for: ${objective}`, context: {}, createdAt: new Date(), updatedAt: new Date(), attempts: [], statusHistory: [], }, { id: { id: `task-${timestamp}-2`, swarmId: "enhanced-swarm", sequence: 2, priority: 1, }, name: `Implement ${objective}`, description: `Implement the solution for: ${objective}`, status: "created", priority: "high", type: "coding", requirements: { capabilities: ["coding", "implementation"], tools: [], permissions: [], }, constraints: { dependencies: [{ id: `task-${timestamp}-1`, swarmId: "enhanced-swarm", sequence: 1, priority: 1, }], dependents: [], conflicts: [], }, input: { objective }, instructions: `Implement the solution for: ${objective}`, context: {}, createdAt: new Date(), updatedAt: new Date(), attempts: [], statusHistory: [], }, ]; return tasks; } async analyzeTaskComplexity(task) { // Analyze task using intelligent complexity detection patterns const description = task.description.toLowerCase(); const name = task.name.toLowerCase(); let complexity = "simple"; let estimatedTokens = 1000; let suggestedModel = this.modelConfig.apply; // Complex task indicators const complexIndicators = [ "architecture", "system design", "integration", "security", "performance optimization", "database schema", "api design", ]; // Medium task indicators const mediumIndicators = [ "implement", "refactor", "optimize", "validate", "test", "function", "method", "class", "component", ]; if (complexIndicators.some(indicator => description.includes(indicator) || name.includes(indicator))) { complexity = "complex"; estimatedTokens = 4000; suggestedModel = this.modelConfig.primary; } else if (mediumIndicators.some(indicator => description.includes(indicator) || name.includes(indicator))) { complexity = "medium"; estimatedTokens = 2000; suggestedModel = this.modelConfig.primary; } // Extract required context files const requiredContext = this.extractContextFiles(`${task.description} ${task.instructions || ""}`); return { task, complexity, estimatedTokens, requiredContext, suggestedModel, }; } extractContextFiles(text) { // Extract file paths and imports from task description const filePatterns = [ /[\w\-\.\/]+\.(ts|js|tsx|jsx|py|java|cpp|h|css|html|md|json|yaml|yml)/g, /from\s+['"]([^'"]+)['"]/g, /import\s+['"]([^'"]+)['"]/g, ]; const files = new Set(); for (const pattern of filePatterns) { const matches = text.match(pattern); if (matches) { matches.forEach(match => files.add(match)); } } return Array.from(files); } determineChunkType(file) { const keywords = file.toLowerCase().split(/[\s\-_\.\/]/); if (keywords.includes("test") || keywords.includes("testing")) { return "test"; } if (keywords.includes("config") || keywords.includes("configuration")) { return "config"; } if (keywords.includes("type") || keywords.includes("interface")) { return "module"; } // Default to module for other files return "module"; } async processTasks() { logger.info(`Processing ${this.taskQueue.length} tasks`); while (this.taskQueue.length > 0) { const availableAgents = Array.from(this.agents.values()).filter(agent => agent.isAvailable()); if (availableAgents.length === 0) { logger.info("No available agents, waiting..."); await new Promise(resolve => setTimeout(resolve, 1000)); continue; } const nextTask = this.getNextTask(); if (!nextTask) { logger.info("No tasks ready for execution, waiting..."); await new Promise(resolve => setTimeout(resolve, 1000)); continue; } const bestAgent = this.findBestAgent(nextTask, availableAgents); if (!bestAgent) { logger.warn(`No suitable agent found for task: ${nextTask.name}`); await new Promise(resolve => setTimeout(resolve, 1000)); continue; } // Prepare context window for the task await this.prepareContextWindow(nextTask, bestAgent); // Remove task from queue and add to active tasks const taskIndex = this.taskQueue.indexOf(nextTask); this.taskQueue.splice(taskIndex, 1); this.activeTasksMap.set(nextTask.id.id, nextTask); // Assign task to agent logger.info(`Assigning task ${nextTask.id.id} to agent ${bestAgent.getId()}`); this.emit("taskStarted", { taskName: nextTask.name, taskId: nextTask.id.id, agentId: bestAgent.getId(), }); await bestAgent.assignTask(nextTask); } } getNextTask() { // Find tasks with all dependencies completed for (const task of this.taskQueue) { const dependenciesMet = task.constraints.dependencies.every(depId => { const depTask = this.activeTasksMap.get(depId.id); return depTask?.status === "completed"; }); if (dependenciesMet) { return task; } } return null; } findBestAgent(task, availableAgents) { let bestAgent = null; let bestScore = 0; for (const agent of availableAgents) { if (!agent.canHandleTask(task)) { continue; } const capabilities = agent.getCapabilities(); let score = 0; // Score based on capability match const taskCapabilities = task.requirements?.capabilities || []; const matchingCapabilities = taskCapabilities.filter(capability => capabilities.tools.includes(capability)); score += matchingCapabilities.length * 10; // Score based on agent reliability score += capabilities.reliability * 5; // Score based on agent speed score += capabilities.speed * 3; if (score > bestScore) { bestScore = score; bestAgent = agent; } } return bestAgent; } async prepareContextWindow(task, agent) { // Prepare context window based on task requirements const customProps = task.metadata?.customProperties; const requiredContext = customProps?.requiredContext || []; const estimatedTokens = customProps?.estimatedTokens || 1000; const contextWindow = { files: requiredContext, maxTokens: estimatedTokens, priority: this.determineContextPriority(task), chunks: await this.createContextChunks(requiredContext), }; this.contextCache.set(task.id.id, contextWindow); } determineContextPriority(task) { const customProps = task.metadata?.customProperties; const complexity = customProps?.complexity; switch (complexity) { case "simple": return "immediate"; case "medium": return "extended"; case "complex": return "project"; default: return "semantic"; } } async createContextChunks(files) { const chunks = []; for (const file of files) { chunks.push({ content: `// Context for ${file}`, type: this.determineChunkType(file), priority: this.calculateChunkPriority(file), tokens: 100, // Simplified token calculation }); } return chunks; } calculateChunkPriority(file) { // Higher priority for certain file types if (file.includes("test")) return 1; if (file.includes("config")) return 2; if (file.includes(".ts") || file.includes(".js")) return 3; return 4; } handleAgentTaskCompleted(event) { logger.info(`Task ${event.taskId} completed by agent ${event.agentId}`); const task = this.activeTasksMap.get(event.taskId); if (task) { task.status = "completed"; task.completedAt = new Date(); task.result = event.result; this.activeTasksMap.delete(event.taskId); const duration = task.completedAt.getTime() - task.createdAt.getTime(); this.emit("taskCompleted", { taskName: task.name, taskId: event.taskId, agentId: event.agentId, duration, result: event.result, }); } } handleAgentTaskError(event) { logger.error(`Task ${event.taskId} failed on agent ${event.agentId}:`, event.error); const task = this.activeTasksMap.get(event.taskId); if (task) { task.status = "failed"; task.error = { type: "execution_error", message: event.error.message, recoverable: true, retryable: true, context: {}, }; this.activeTasksMap.delete(event.taskId); this.emit("taskError", { taskName: task.name, taskId: event.taskId, agentId: event.agentId, error: event.error.message || event.error, }); } } getStatus() { const busyAgents = Array.from(this.agents.values()).filter(a => !a.isAvailable()).length; const totalTasks = this.taskQueue.length + this.activeTasksMap.size; const completedTasks = this.agents.size > 0 ? Array.from(this.agents.values()).reduce((sum, agent) => sum + agent.getMetrics().tasksCompleted, 0) : 0; return { status: this.taskQueue.length === 0 && this.activeTasksMap.size === 0 ? "completed" : "executing", activeAgents: busyAgents, totalAgents: this.agents.size, activeTasks: this.activeTasksMap.size, totalTasks, completedTasks, failedTasks: 0, // TODO: Track failed tasks queuedTasks: this.taskQueue.length, strategy: this.strategy, }; } async shutdown() { logger.info("Shutting down Enhanced Swarm Coordinator"); // Cleanup all agents await Promise.all(Array.from(this.agents.values()).map(agent => agent.cleanup())); this.agents.clear(); this.taskQueue.length = 0; this.activeTasksMap.clear(); this.contextCache.clear(); this.emit("shutdown"); } } //# sourceMappingURL=cursor-enhanced-coordinator.js.map