@sethdouglasford/claude-flow
Version:
Claude Code Flow - Advanced AI-powered development workflows with SPARC methodology
355 lines • 13.8 kB
JavaScript
/**
* Coordination manager for task scheduling and resource management
*/
import { SystemEvents } from "../utils/types.js";
import { CoordinationError, DeadlockError } from "../utils/errors.js";
import { TaskScheduler } from "./scheduler.js";
import { ResourceManager } from "./resources.js";
import { MessageRouter } from "./messaging.js";
import { AdvancedTaskScheduler } from "./advanced-scheduler.js";
import { ConflictResolver } from "./conflict-resolution.js";
import { CoordinationMetricsCollector } from "./metrics.js";
/**
* Coordination manager implementation
*/
export class CoordinationManager {
config;
eventBus;
logger;
scheduler;
resourceManager;
messageRouter;
conflictResolver;
metricsCollector;
initialized = false;
deadlockCheckInterval;
advancedSchedulingEnabled = false;
constructor(config, eventBus, logger) {
this.config = config;
this.eventBus = eventBus;
this.logger = logger;
this.scheduler = new TaskScheduler(config, eventBus, logger);
this.resourceManager = new ResourceManager(config, eventBus, logger);
this.messageRouter = new MessageRouter(config, eventBus, logger);
this.conflictResolver = new ConflictResolver(logger, eventBus);
this.metricsCollector = new CoordinationMetricsCollector(logger, eventBus);
}
async initialize() {
if (this.initialized) {
return;
}
this.logger.info("Initializing coordination manager...");
try {
// Initialize components
await this.scheduler.initialize();
await this.resourceManager.initialize();
await this.messageRouter.initialize();
// Start metrics collection
this.metricsCollector.start();
// Start deadlock detection if enabled
if (this.config.deadlockDetection) {
this.startDeadlockDetection();
}
// Set up event handlers
this.setupEventHandlers();
this.initialized = true;
this.logger.info("Coordination manager initialized");
}
catch (error) {
this.logger.error("Failed to initialize coordination manager", error);
throw new CoordinationError("Coordination manager initialization failed", { error });
}
}
async shutdown() {
if (!this.initialized) {
return;
}
this.logger.info("Shutting down coordination manager...");
try {
// Stop deadlock detection
if (this.deadlockCheckInterval) {
clearInterval(this.deadlockCheckInterval);
}
// Stop metrics collection
this.metricsCollector.stop();
// Shutdown components
await Promise.all([
this.scheduler.shutdown(),
this.resourceManager.shutdown(),
this.messageRouter.shutdown(),
]);
this.initialized = false;
this.logger.info("Coordination manager shutdown complete");
}
catch (error) {
this.logger.error("Error during coordination manager shutdown", error);
throw error;
}
}
async assignTask(task, agentId) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
await this.scheduler.assignTask(task, agentId);
}
async getAgentTaskCount(agentId) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
return await this.scheduler.getAgentTaskCount(agentId);
}
async acquireResource(resourceId, agentId) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
await this.resourceManager.acquire(resourceId, agentId);
}
async releaseResource(resourceId, agentId) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
await this.resourceManager.release(resourceId, agentId);
}
async sendMessage(from, to, message) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
await this.messageRouter.send(from, to, message);
}
async getHealthStatus() {
try {
const [schedulerHealth, resourceHealth, messageHealth] = await Promise.all([
this.scheduler.getHealthStatus(),
this.resourceManager.getHealthStatus(),
this.messageRouter.getHealthStatus(),
]);
const metrics = {
...schedulerHealth.metrics,
...resourceHealth.metrics,
...messageHealth.metrics,
};
const healthy = schedulerHealth.healthy &&
resourceHealth.healthy &&
messageHealth.healthy;
const errors = [
schedulerHealth.error,
resourceHealth.error,
messageHealth.error,
].filter(Boolean);
const status = {
healthy,
metrics,
};
if (errors.length > 0) {
status.error = errors.join("; ");
}
return status;
}
catch (error) {
return {
healthy: false,
error: error instanceof Error ? error.message : "Unknown error",
};
}
}
setupEventHandlers() {
// Handle task events
this.eventBus.on(SystemEvents.TASK_COMPLETED, (data) => {
const { taskId, result } = data;
this.scheduler.completeTask(taskId, result)
.catch((error) => {
this.logger.error("Error handling task completion", { taskId, error });
});
});
this.eventBus.on(SystemEvents.TASK_FAILED, (data) => {
const { taskId, error } = data;
this.scheduler.failTask(taskId, error)
.catch((err) => {
this.logger.error("Error handling task failure", { taskId, error: err });
});
});
// Handle agent termination
this.eventBus.on(SystemEvents.AGENT_TERMINATED, (data) => {
const { agentId } = data;
Promise.all([
// Release all resources held by the agent
this.resourceManager.releaseAllForAgent(agentId),
// Cancel all tasks assigned to the agent
this.scheduler.cancelAgentTasks(agentId),
]).catch((error) => {
this.logger.error("Error handling agent termination", { agentId, error });
});
});
}
startDeadlockDetection() {
this.deadlockCheckInterval = setInterval(() => {
const deadlock = this.detectDeadlock();
if (deadlock) {
this.logger.error("Deadlock detected", deadlock);
// Emit deadlock event
this.eventBus.emit(SystemEvents.DEADLOCK_DETECTED, deadlock);
// Attempt to resolve deadlock
this.resolveDeadlock(deadlock)
.catch((error) => {
this.logger.error("Error resolving deadlock", error);
});
}
}, 10000); // Check every 10 seconds
}
detectDeadlock() {
// Get resource allocation graph
const allocations = this.resourceManager.getAllocations();
const waitingFor = this.resourceManager.getWaitingRequests();
// Build dependency graph
const graph = new Map();
// Add edges for resources agents are waiting for
for (const [agentId, resources] of waitingFor) {
let agentDependencies = graph.get(agentId);
if (!agentDependencies) {
agentDependencies = new Set();
graph.set(agentId, agentDependencies);
}
// Find who owns these resources
for (const resource of resources) {
const owner = allocations.get(resource);
if (owner && owner !== agentId) {
agentDependencies.add(owner);
}
}
}
// Detect cycles using DFS
const visited = new Set();
const recursionStack = new Set();
const cycle = [];
const hasCycle = (node) => {
visited.add(node);
recursionStack.add(node);
const neighbors = graph.get(node) ?? new Set();
for (const neighbor of neighbors) {
if (!visited.has(neighbor)) {
if (hasCycle(neighbor)) {
cycle.unshift(node);
return true;
}
}
else if (recursionStack.has(neighbor)) {
cycle.unshift(node);
cycle.unshift(neighbor);
return true;
}
}
recursionStack.delete(node);
return false;
};
// Check for cycles
for (const node of graph.keys()) {
if (!visited.has(node) && hasCycle(node)) {
// Extract unique agents in cycle
const agents = Array.from(new Set(cycle));
// Find resources involved
const resources = [];
for (const agent of agents) {
const waiting = waitingFor.get(agent) ?? [];
resources.push(...waiting);
}
return {
agents,
resources: Array.from(new Set(resources)),
};
}
}
return null;
}
async resolveDeadlock(deadlock) {
this.logger.warn("Attempting to resolve deadlock", deadlock);
// Simple resolution: release resources from the lowest priority agent
// In a real implementation, use more sophisticated strategies
try {
// Find the agent with the lowest priority or least work done
const agentToPreempt = deadlock.agents[0]; // Simplified
// Release all resources held by this agent
await this.resourceManager.releaseAllForAgent(agentToPreempt);
// Reschedule the agent's tasks
await this.scheduler.rescheduleAgentTasks(agentToPreempt);
this.logger.info("Deadlock resolved by preempting agent", {
agentId: agentToPreempt,
});
}
catch (_error) {
throw new DeadlockError("Failed to resolve deadlock", deadlock.agents, deadlock.resources);
}
}
async getAgentTasks(agentId) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
return this.scheduler.getAgentTasks(agentId);
}
async cancelTask(taskId, reason) {
if (!this.initialized) {
throw new CoordinationError("Coordination manager not initialized");
}
await this.scheduler.cancelTask(taskId, reason ?? "User requested cancellation");
}
async performMaintenance() {
if (!this.initialized) {
return;
}
this.logger.debug("Performing coordination manager maintenance");
try {
await Promise.all([
this.scheduler.performMaintenance(),
this.resourceManager.performMaintenance(),
this.messageRouter.performMaintenance(),
]);
// Clean up old conflicts
this.conflictResolver.cleanupOldConflicts(24 * 60 * 60 * 1000); // 24 hours
}
catch (error) {
this.logger.error("Error during coordination manager maintenance", error);
}
}
async getCoordinationMetrics() {
const baseMetrics = await this.getHealthStatus();
const coordinationMetrics = this.metricsCollector.getCurrentMetrics();
const conflictStats = this.conflictResolver.getStats();
return {
...baseMetrics.metrics,
coordination: coordinationMetrics,
conflicts: conflictStats,
advancedScheduling: this.advancedSchedulingEnabled,
};
}
enableAdvancedScheduling() {
if (this.advancedSchedulingEnabled) {
return;
}
this.logger.info("Enabling advanced scheduling features");
// Replace basic scheduler with advanced one
const advancedScheduler = new AdvancedTaskScheduler(this.config, this.eventBus, this.logger);
// Transfer state if needed (in a real implementation)
this.scheduler = advancedScheduler;
this.advancedSchedulingEnabled = true;
}
async reportConflict(type, id, agents) {
this.logger.warn("Conflict reported", { type, id, agents });
let conflict;
if (type === "resource") {
conflict = await this.conflictResolver.reportResourceConflict(id, agents);
}
else {
conflict = await this.conflictResolver.reportTaskConflict(id, agents, "assignment");
}
// Auto-resolve using default strategy
try {
await this.conflictResolver.autoResolve(conflict.id);
}
catch (error) {
this.logger.error("Failed to auto-resolve conflict", {
conflictId: conflict.id,
error,
});
}
}
}
//# sourceMappingURL=manager.js.map