UNPKG

@sethdouglasford/claude-flow

Version:

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

609 lines 23.3 kB
/** * Task Coordination Layer - Integrates with TodoWrite/TodoRead and Memory for orchestration * Provides seamless coordination between task management and Claude Code batch tools */ import { EventEmitter } from "events"; import { generateId } from "../utils/helpers.js"; export class TaskCoordinator extends EventEmitter { taskEngine; memoryManager; todoItems = new Map(); memoryStore = new Map(); coordinationSessions = new Map(); batchOperations = new Map(); agentCoordination = new Map(); constructor(taskEngine, memoryManager) { super(); this.taskEngine = taskEngine; this.memoryManager = memoryManager; this.setupCoordinationHandlers(); } setupCoordinationHandlers() { this.taskEngine.on("task:created", this.handleTaskCreated.bind(this)); this.taskEngine.on("task:started", this.handleTaskStarted.bind(this)); this.taskEngine.on("task:completed", this.handleTaskCompleted.bind(this)); this.taskEngine.on("task:failed", this.handleTaskFailed.bind(this)); this.taskEngine.on("task:cancelled", this.handleTaskCancelled.bind(this)); } /** * Create TodoWrite-style task breakdown for complex operations */ async createTaskTodos(objective, context, options = {}) { const { sessionId } = context; this.coordinationSessions.set(sessionId, context); // AI-powered task breakdown based on objective and strategy const todos = await this.generateTaskBreakdown(objective, options); // Store todos in coordination system for (const todo of todos) { this.todoItems.set(todo.id, todo); // Store in memory for cross-agent coordination if (options.memoryCoordination && this.memoryManager) { await this.storeInMemory(`todo:${todo.id}`, todo, { namespace: "task_coordination", tags: ["todo", "task_breakdown", sessionId], }); } } // Emit coordination event this.emit("todos:created", { sessionId, todos, context }); return todos; } /** * Update TodoRead-style progress tracking */ async updateTodoProgress(todoId, status, metadata) { const todo = this.todoItems.get(todoId); if (!todo) { throw new Error(`Todo ${todoId} not found`); } const previousStatus = todo.status; todo.status = status; todo.metadata = { ...todo.metadata, ...metadata, updatedAt: new Date() }; // Update in memory for coordination if (this.memoryManager) { await this.storeInMemory(`todo:${todoId}`, todo, { namespace: "task_coordination", tags: ["todo", "progress_update"], }); } // Create corresponding task if moving to in_progress if (status === "in_progress" && previousStatus === "pending") { await this.createTaskFromTodo(todo); } this.emit("todo:updated", { todoId, status, previousStatus, todo }); } /** * Read all todos for coordination (TodoRead equivalent) */ async readTodos(sessionId, filter) { let todos = Array.from(this.todoItems.values()); // Filter by session if provided if (sessionId) { const sessionTodos = await this.getSessionTodos(sessionId); todos = todos.filter(todo => sessionTodos.some(st => st.id === todo.id)); } // Apply filters if (filter) { if (filter.status) { todos = todos.filter(todo => filter.status.includes(todo.status)); } if (filter.priority) { todos = todos.filter(todo => filter.priority.includes(todo.priority)); } if (filter.assignedAgent) { todos = todos.filter(todo => todo.assignedAgent === filter.assignedAgent); } if (filter.tags) { todos = todos.filter(todo => todo.tags?.some(tag => filter.tags.includes(tag))); } if (filter.batchOptimized !== undefined) { todos = todos.filter(todo => todo.batchOptimized === filter.batchOptimized); } } return todos; } /** * Store data in Memory for cross-agent coordination */ async storeInMemory(key, value, options = {}) { const entry = { key, value, timestamp: new Date(), namespace: options.namespace, tags: options.tags, expiresAt: options.expiresAt, }; this.memoryStore.set(key, entry); // Store in external memory manager if available if (this.memoryManager) { const memoryKey = options.namespace ? `${options.namespace}:${key}` : key; await this.memoryManager.store(memoryKey, value, { tags: options.tags, expiresAt: options.expiresAt, }); } this.emit("memory:stored", { key, entry }); } /** * Retrieve data from Memory for coordination */ async retrieveFromMemory(key, namespace) { const memoryKey = namespace ? `${namespace}:${key}` : key; // Try external memory manager first if (this.memoryManager) { try { const value = await this.memoryManager.retrieve(memoryKey); if (value !== null) return value; } catch (error) { // Fall back to local store } } // Use local store const entry = this.memoryStore.get(key); if (!entry) return null; // Check expiration if (entry.expiresAt && entry.expiresAt < new Date()) { this.memoryStore.delete(key); return null; } return entry.value; } /** * Query Memory with filters for coordination */ async queryMemory(query) { let entries = Array.from(this.memoryStore.values()); // Apply filters if (query.namespace) { entries = entries.filter(entry => entry.namespace === query.namespace); } if (query.tags) { entries = entries.filter(entry => entry.tags?.some(tag => query.tags.includes(tag))); } if (query.keyPattern) { const pattern = new RegExp(query.keyPattern); entries = entries.filter(entry => pattern.test(entry.key)); } if (query.since) { entries = entries.filter(entry => entry.timestamp >= query.since); } // Sort by timestamp (newest first) entries.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // Apply limit if (query.limit) { entries = entries.slice(0, query.limit); } return entries; } /** * Launch parallel agents using Task tool pattern */ async launchParallelAgents(tasks, coordinationContext) { const batchId = generateId("batch"); const agentIds = []; const batchOperation = { id: batchId, type: "parallel_agents", tasks, startedAt: new Date(), status: "running", results: new Map(), errors: new Map(), }; this.batchOperations.set(batchId, batchOperation); // Store batch operation in memory for coordination await this.storeInMemory(`batch:${batchId}`, batchOperation, { namespace: "coordination", tags: ["batch_operation", "parallel_agents"], }); // Launch each agent for (const task of tasks) { try { const agentId = await this.launchAgent(task, coordinationContext, batchId); agentIds.push(agentId); // Store agent coordination state this.agentCoordination.set(agentId, { agentId, batchId, objective: task.objective, status: "running", startedAt: new Date(), memoryKey: task.memoryKey, coordinationContext, }); } catch (error) { batchOperation.errors.set(task.agentType, error); } } this.emit("agents:launched", { batchId, agentIds, tasks }); return agentIds; } /** * Coordinate batch operations for maximum efficiency */ async coordinateBatchOperations(operations, context) { const batchId = generateId("batch_ops"); const results = new Map(); // Group operations by type for maximum efficiency const groupedOps = new Map(); for (const op of operations) { if (!groupedOps.has(op.type)) { groupedOps.set(op.type, []); } const opGroup = groupedOps.get(op.type); if (opGroup) { opGroup.push(op); } } // Store batch coordination info await this.storeInMemory(`batch_ops:${batchId}`, { operations, groupedOps: Object.fromEntries(groupedOps), context, startedAt: new Date(), }, { namespace: "coordination", tags: ["batch_operations", "efficiency"], }); // Execute operations in parallel by type const promises = []; for (const [type, ops] of groupedOps) { promises.push(this.executeBatchOperationType(type, ops, batchId, results)); } await Promise.all(promises); this.emit("batch:completed", { batchId, results, context }); return results; } /** * Swarm coordination patterns based on mode */ async coordinateSwarm(objective, context, agents) { const swarmId = generateId("swarm"); // Store swarm configuration await this.storeInMemory(`swarm:${swarmId}`, { objective, context, agents, startedAt: new Date(), coordinationPattern: context.coordinationMode, }, { namespace: "swarm_coordination", tags: ["swarm", context.coordinationMode], }); switch (context.coordinationMode) { case "centralized": await this.coordinateCentralizedSwarm(swarmId, objective, agents); break; case "distributed": await this.coordinateDistributedSwarm(swarmId, objective, agents); break; case "hierarchical": await this.coordinateHierarchicalSwarm(swarmId, objective, agents); break; case "mesh": await this.coordinateMeshSwarm(swarmId, objective, agents); break; case "hybrid": await this.coordinateHybridSwarm(swarmId, objective, agents); break; } } // Private helper methods async generateTaskBreakdown(objective, options) { // AI-powered task breakdown based on strategy const strategy = options.strategy || "development"; const todos = []; // Strategy-specific task patterns switch (strategy) { case "research": todos.push({ id: generateId("todo"), content: "Gather initial information and sources", status: "pending", priority: "high", batchOptimized: true, parallelExecution: true, memoryKey: "research_sources", tags: ["research", "information_gathering"], }, { id: generateId("todo"), content: "Analyze and synthesize findings", status: "pending", priority: "medium", dependencies: ["research_sources"], batchOptimized: true, memoryKey: "research_analysis", tags: ["research", "analysis"], }); break; case "development": todos.push({ id: generateId("todo"), content: "Design system architecture", status: "pending", priority: "high", memoryKey: "system_architecture", tags: ["development", "architecture"], }, { id: generateId("todo"), content: "Implement core functionality", status: "pending", priority: "high", dependencies: ["system_architecture"], batchOptimized: true, parallelExecution: true, memoryKey: "core_implementation", tags: ["development", "implementation"], }, { id: generateId("todo"), content: "Write comprehensive tests", status: "pending", priority: "medium", dependencies: ["core_implementation"], batchOptimized: true, memoryKey: "test_suite", tags: ["development", "testing"], }); break; case "analysis": todos.push({ id: generateId("todo"), content: "Collect and preprocess data", status: "pending", priority: "high", batchOptimized: true, parallelExecution: true, memoryKey: "analysis_data", tags: ["analysis", "data_collection"], }, { id: generateId("todo"), content: "Perform statistical analysis", status: "pending", priority: "high", dependencies: ["analysis_data"], batchOptimized: true, memoryKey: "statistical_results", tags: ["analysis", "statistics"], }, { id: generateId("todo"), content: "Generate insights and reports", status: "pending", priority: "medium", dependencies: ["statistical_results"], memoryKey: "analysis_insights", tags: ["analysis", "reporting"], }); break; default: // Generic breakdown todos.push({ id: generateId("todo"), content: `Analyze requirements for: ${objective}`, status: "pending", priority: "high", memoryKey: "requirements_analysis", tags: ["generic", "requirements"], }, { id: generateId("todo"), content: `Execute main tasks for: ${objective}`, status: "pending", priority: "high", dependencies: ["requirements_analysis"], batchOptimized: true, parallelExecution: true, memoryKey: "main_execution", tags: ["generic", "execution"], }, { id: generateId("todo"), content: "Validate and finalize results", status: "pending", priority: "medium", dependencies: ["main_execution"], memoryKey: "validation_results", tags: ["generic", "validation"], }); } return todos; } async createTaskFromTodo(todo) { const taskData = { type: todo.tags?.[0] || "general", description: todo.content, priority: this.priorityToNumber(todo.priority), assignedAgent: todo.assignedAgent, tags: todo.tags || [], metadata: { todoId: todo.id, batchOptimized: todo.batchOptimized, parallelExecution: todo.parallelExecution, memoryKey: todo.memoryKey, }, }; return await this.taskEngine.createTask(taskData); } priorityToNumber(priority) { switch (priority) { case "high": return 80; case "medium": return 50; case "low": return 20; default: return 50; } } async launchAgent(task, context, batchId) { const agentId = generateId("agent"); // Store agent launch info in memory await this.storeInMemory(`agent:${agentId}`, { ...task, agentId, batchId, context, launchedAt: new Date(), }, { namespace: "agent_coordination", tags: ["agent_launch", task.agentType], }); return agentId; } async executeBatchOperationType(type, operations, batchId, results) { // Simulate batch operation execution // In real implementation, this would use actual tools for (const op of operations) { try { const result = await this.simulateBatchOperation(type, op); results.set(`${type}_${op.targets.join("_")}`, result); } catch (error) { results.set(`${type}_${op.targets.join("_")}_error`, error); } } } async simulateBatchOperation(type, operation) { // Simulate operation based on type await new Promise(resolve => setTimeout(resolve, 100)); return { type, targets: operation.targets, result: `Simulated ${type} operation completed`, timestamp: new Date(), }; } // Swarm coordination patterns async coordinateCentralizedSwarm(swarmId, objective, agents) { // Single coordinator manages all agents await this.storeInMemory(`swarm:${swarmId}:pattern`, { type: "centralized", coordinator: "main", agentAssignments: agents.map(agent => ({ agentId: agent.type, role: agent.role, coordinator: "main", })), }); } async coordinateDistributedSwarm(swarmId, objective, agents) { // Multiple coordinators for different aspects const coordinators = ["research_coord", "impl_coord", "test_coord"]; await this.storeInMemory(`swarm:${swarmId}:pattern`, { type: "distributed", coordinators, agentAssignments: agents.map((agent, index) => ({ agentId: agent.type, role: agent.role, coordinator: coordinators[index % coordinators.length], })), }); } async coordinateHierarchicalSwarm(swarmId, objective, agents) { // Tree structure with team leads await this.storeInMemory(`swarm:${swarmId}:pattern`, { type: "hierarchical", hierarchy: { master: "main_coordinator", teamLeads: ["frontend_lead", "backend_lead", "devops_lead"], teams: { frontend_lead: agents.filter(a => a.type.includes("frontend")), backend_lead: agents.filter(a => a.type.includes("backend")), devops_lead: agents.filter(a => a.type.includes("devops")), }, }, }); } async coordinateMeshSwarm(swarmId, objective, agents) { // Peer-to-peer coordination await this.storeInMemory(`swarm:${swarmId}:pattern`, { type: "mesh", peerConnections: agents.map(agent => ({ agentId: agent.type, peers: agents.filter(a => a.type !== agent.type).map(a => a.type), })), }); } async coordinateHybridSwarm(swarmId, objective, agents) { // Mixed patterns based on requirements await this.storeInMemory(`swarm:${swarmId}:pattern`, { type: "hybrid", phases: [ { phase: "planning", pattern: "centralized" }, { phase: "execution", pattern: "distributed" }, { phase: "integration", pattern: "hierarchical" }, ], }); } async getSessionTodos(sessionId) { const entries = await this.queryMemory({ namespace: "task_coordination", tags: ["todo", sessionId], }); return entries.map(entry => entry.value); } // Event handlers async handleTaskCreated(data) { // Update corresponding todo if exists const todoId = data.task.metadata?.todoId; if (todoId) { await this.updateTodoProgress(todoId, "in_progress", { taskId: data.task.id, createdAt: data.task.createdAt, }); } } async handleTaskStarted(data) { // Store task start in memory for coordination await this.storeInMemory(`task_execution:${data.taskId}`, { status: "started", agentId: data.agentId, startedAt: new Date(), }, { namespace: "task_execution", tags: ["task_start", data.agentId], }); } async handleTaskCompleted(data) { // Update todo and store results const task = (await this.taskEngine.getTaskStatus(data.taskId))?.task; const todoId = task?.metadata?.todoId; if (todoId) { await this.updateTodoProgress(todoId, "completed", { completedAt: new Date(), result: data.result, }); } // Store completion in memory await this.storeInMemory(`task_execution:${data.taskId}`, { status: "completed", result: data.result, completedAt: new Date(), }, { namespace: "task_execution", tags: ["task_completion"], }); } async handleTaskFailed(data) { // Store failure info await this.storeInMemory(`task_execution:${data.taskId}`, { status: "failed", error: data.error.message, failedAt: new Date(), }, { namespace: "task_execution", tags: ["task_failure"], }); } async handleTaskCancelled(data) { // Store cancellation info await this.storeInMemory(`task_execution:${data.taskId}`, { status: "cancelled", reason: data.reason, cancelledAt: new Date(), }, { namespace: "task_execution", tags: ["task_cancellation"], }); } } //# sourceMappingURL=coordination.js.map