@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
405 lines • 16.3 kB
JavaScript
/**
* 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