UNPKG

vibe-coder-mcp

Version:

Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.

1,131 lines (1,130 loc) 64.4 kB
import { EnhancedError, ConfigurationError, TaskExecutionError, ValidationError, createErrorContext } from '../utils/enhanced-errors.js'; import logger from '../../../logger.js'; export const DEFAULT_SCHEDULING_CONFIG = { algorithm: 'hybrid_optimal', resources: { maxConcurrentTasks: 10, maxMemoryMB: 4096, maxCpuUtilization: 0.8, availableAgents: 3, taskTypeResources: new Map([ ['development', { memoryMB: 512, cpuWeight: 0.7, agentCount: 1 }], ['testing', { memoryMB: 256, cpuWeight: 0.5, agentCount: 1 }], ['documentation', { memoryMB: 128, cpuWeight: 0.3, agentCount: 1 }], ['research', { memoryMB: 256, cpuWeight: 0.4, agentCount: 1 }], ['deployment', { memoryMB: 1024, cpuWeight: 0.9, agentCount: 1 }], ['review', { memoryMB: 128, cpuWeight: 0.2, agentCount: 1 }] ]) }, weights: { dependencies: 0.35, deadline: 0.25, systemLoad: 0.20, complexity: 0.10, businessImpact: 0.05, agentAvailability: 0.05, priority: 0.0, resources: 0.0, duration: 0.0 }, deadlineBuffer: 2, rescheduleSensitivity: 'medium', enableDynamicOptimization: true, optimizationInterval: 30 }; export class TaskScheduler { config; currentSchedule = null; optimizationTimer = null; scheduleVersion = 0; static currentInstance = null; constructor(config = {}) { this.config = { ...DEFAULT_SCHEDULING_CONFIG, ...config }; if (this.config.enableDynamicOptimization) { this.startOptimizationTimer(); } TaskScheduler.currentInstance = this; logger.info('TaskScheduler initialized', { algorithm: this.config.algorithm, maxConcurrentTasks: this.config.resources.maxConcurrentTasks, enableDynamicOptimization: this.config.enableDynamicOptimization }); } static getCurrentInstance() { return TaskScheduler.currentInstance; } static resetCurrentInstance() { TaskScheduler.currentInstance = null; } static hasCurrentInstance() { return TaskScheduler.currentInstance !== null; } async generateSchedule(tasks, dependencyGraph, projectId) { const startTime = Date.now(); try { logger.info('Generating execution schedule', { taskCount: tasks.length, algorithm: this.config.algorithm, projectId }); this.validateSchedulingInputs(tasks, dependencyGraph); const taskScores = this.calculateTaskScores(tasks, dependencyGraph); const parallelBatches = dependencyGraph.getParallelBatches(); const scheduledTasks = await this.applySchedulingAlgorithm(tasks, taskScores, parallelBatches, dependencyGraph); const timeline = this.calculateTimeline(scheduledTasks, parallelBatches); const resourceUtilization = this.calculateResourceUtilization(scheduledTasks); const schedule = { id: `schedule_${projectId}_${Date.now()}`, projectId, scheduledTasks, executionBatches: parallelBatches, timeline, resourceUtilization, metadata: { algorithm: this.config.algorithm, config: { ...this.config }, generatedAt: new Date(), optimizedAt: new Date(), version: ++this.scheduleVersion, isOptimal: false } }; const optimizedSchedule = await this.optimizeSchedule(schedule); this.currentSchedule = optimizedSchedule; const generationTime = Date.now() - startTime; logger.info('Schedule generated successfully', { scheduleId: schedule.id, taskCount: tasks.length, batchCount: parallelBatches.length, generationTime, algorithm: this.config.algorithm }); return optimizedSchedule; } catch (error) { const context = createErrorContext('TaskScheduler', 'generateSchedule') .projectId(projectId) .metadata({ taskCount: tasks.length, algorithm: this.config.algorithm, generationTime: Date.now() - startTime }) .build(); if (error instanceof EnhancedError) { throw error; } if (error instanceof Error) { if (error.message.includes('validation') || error.message.includes('invalid')) { throw new ValidationError(`Schedule generation validation failed: ${error.message}`, context, { cause: error, field: 'tasks', expectedFormat: 'Array of valid AtomicTask objects' }); } if (error.message.includes('algorithm') || error.message.includes('config')) { throw new ConfigurationError(`Schedule generation configuration error: ${error.message}`, context, { cause: error, configKey: 'algorithm', actualValue: this.config.algorithm }); } throw new TaskExecutionError(`Schedule generation failed: ${error.message}`, context, { cause: error, retryable: true }); } throw new TaskExecutionError(`Schedule generation failed with unknown error: ${String(error)}`, context, { retryable: false }); } } async updateSchedule(updatedTasks, dependencyGraph) { const context = createErrorContext('TaskScheduler', 'updateSchedule') .metadata({ updatedTaskCount: updatedTasks.length, hasCurrentSchedule: !!this.currentSchedule }) .build(); if (!this.currentSchedule) { throw new ValidationError('No current schedule exists to update. Generate a schedule first.', context, { field: 'currentSchedule', expectedFormat: 'Valid ExecutionSchedule object' }); } try { logger.info('Updating existing schedule', { scheduleId: this.currentSchedule.id, updatedTaskCount: updatedTasks.length }); if (!Array.isArray(updatedTasks) || updatedTasks.length === 0) { throw new ValidationError('Updated tasks must be a non-empty array of AtomicTask objects', context, { field: 'updatedTasks', expectedFormat: 'Array<AtomicTask>', actualValue: updatedTasks }); } const needsReschedule = this.shouldReschedule(updatedTasks); if (needsReschedule) { return this.generateSchedule(updatedTasks, dependencyGraph, this.currentSchedule.projectId); } else { return this.incrementalUpdate(updatedTasks, dependencyGraph); } } catch (error) { if (error instanceof EnhancedError) { throw error; } throw new TaskExecutionError(`Schedule update failed: ${error instanceof Error ? error.message : String(error)}`, context, { cause: error instanceof Error ? error : undefined, retryable: true }); } } getCurrentSchedule() { return this.currentSchedule; } getReadyTasks() { if (!this.currentSchedule) { return []; } const now = new Date(); const readyTasks = []; for (const scheduledTask of this.currentSchedule.scheduledTasks.values()) { if (scheduledTask.scheduledStart <= now && scheduledTask.task.status === 'pending' && this.arePrerequisitesComplete(scheduledTask)) { readyTasks.push(scheduledTask); } } return readyTasks.sort((a, b) => b.metadata.priorityScore - a.metadata.priorityScore); } getNextExecutionBatch() { if (!this.currentSchedule) { return null; } const readyTasks = this.getReadyTasks(); if (readyTasks.length === 0) { return null; } for (const batch of this.currentSchedule.executionBatches) { const batchTasks = batch.taskIds.map(id => this.currentSchedule.scheduledTasks.get(id)).filter(task => task !== undefined); const allReady = batchTasks.every(task => readyTasks.some(ready => ready.task.id === task.task.id)); if (allReady) { return batch; } } return null; } async markTaskCompleted(taskId) { if (!this.currentSchedule) { return; } const scheduledTask = this.currentSchedule.scheduledTasks.get(taskId); if (!scheduledTask) { logger.warn('Task not found in current schedule', { taskId }); return; } scheduledTask.task.status = 'completed'; scheduledTask.task.actualHours = this.calculateActualHours(scheduledTask); logger.info('Task marked as completed', { taskId, actualHours: scheduledTask.task.actualHours, estimatedHours: scheduledTask.task.estimatedHours }); if (this.config.enableDynamicOptimization) { await this.checkForOptimization(); } } async executeScheduledTasks() { if (!this.currentSchedule) { return { success: false, executedTasks: [], queuedTasks: [], errors: [{ taskId: 'N/A', error: 'No current schedule available' }] }; } await this.waitForExecutionDependencies(); const executedTasks = []; const queuedTasks = []; const errors = []; try { const readyTasks = this.getReadyTasks(); if (readyTasks.length === 0) { logger.debug('No ready tasks for execution'); return { success: true, executedTasks, queuedTasks, errors }; } const { AgentOrchestrator } = await import('./agent-orchestrator.js'); const orchestrator = AgentOrchestrator.getInstance(); logger.info(`Executing ${readyTasks.length} ready tasks`); for (const scheduledTask of readyTasks) { try { const projectContext = { projectId: scheduledTask.task.projectId, projectPath: process.cwd(), projectName: scheduledTask.task.projectId, description: `Scheduled task execution for ${scheduledTask.task.title}`, languages: ['typescript', 'javascript'], frameworks: [], buildTools: ['npm'], tools: [], configFiles: [], entryPoints: [], architecturalPatterns: [], existingTasks: [], codebaseSize: 'medium', teamSize: 1, complexity: 'medium', codebaseContext: { relevantFiles: [], contextSummary: `Scheduled task execution for ${scheduledTask.task.title}`, gatheringMetrics: { searchTime: 0, readTime: 0, scoringTime: 0, totalTime: 0, cacheHitRate: 0 }, totalContextSize: 0, averageRelevance: 0 }, structure: { sourceDirectories: ['src'], testDirectories: ['test', 'tests'], docDirectories: ['docs'], buildDirectories: ['build', 'dist'] }, dependencies: { production: [], development: [], external: [] }, metadata: { createdAt: new Date(), updatedAt: new Date(), version: '1.0.0', source: 'auto-detected' } }; const result = await orchestrator.executeTask(scheduledTask.task, projectContext, { priority: this.mapTaskPriorityToExecutionPriority(scheduledTask.task.priority), timeout: scheduledTask.assignedResources.memoryMB * 1000, enableMonitoring: true }); if (result.success) { executedTasks.push(scheduledTask.task.id); logger.info(`Task ${scheduledTask.task.id} executed successfully`); } else if (result.queued) { queuedTasks.push(scheduledTask.task.id); logger.info(`Task ${scheduledTask.task.id} queued for later execution`); } else { errors.push({ taskId: scheduledTask.task.id, error: result.error || result.message }); logger.warn(`Task ${scheduledTask.task.id} execution failed: ${result.error || result.message}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; errors.push({ taskId: scheduledTask.task.id, error: errorMessage }); logger.error({ err: error, taskId: scheduledTask.task.id }, 'Task execution failed with exception'); } } logger.info({ executed: executedTasks.length, queued: queuedTasks.length, errors: errors.length }, 'Scheduled task execution completed'); return { success: errors.length === 0, executedTasks, queuedTasks, errors }; } catch (error) { logger.error({ err: error }, 'Failed to execute scheduled tasks'); return { success: false, executedTasks, queuedTasks, errors: [{ taskId: 'N/A', error: error instanceof Error ? error.message : 'Unknown error' }] }; } } mapTaskPriorityToExecutionPriority(taskPriority) { return taskPriority; } dispose() { if (this.isDisposed) { return; } this.isDisposed = true; if (this.optimizationTimer) { clearInterval(this.optimizationTimer); this.optimizationTimer = null; } this.currentSchedule = null; if (TaskScheduler.currentInstance === this) { TaskScheduler.currentInstance = null; } logger.info('TaskScheduler disposed'); } isDisposed = false; async waitForExecutionDependencies() { const maxWaitTime = 15000; const checkInterval = 500; const startTime = Date.now(); while (Date.now() - startTime < maxWaitTime) { try { try { const { ExecutionCoordinator } = await import('./execution-coordinator.js'); const coordinator = await ExecutionCoordinator.getInstance(); if (!coordinator.getRunningStatus()) { throw new Error('ExecutionCoordinator not running'); } } catch { logger.debug('ExecutionCoordinator not available, continuing without it'); } try { const { TransportManager } = await import('../../../services/transport-manager/index.js'); const transportManager = TransportManager.getInstance(); const status = transportManager.getStatus(); if (status.isConfigured && !status.isStarted) { throw new Error('Transport Manager not ready'); } } catch { logger.debug('Transport Manager not available, continuing without it'); } logger.debug('All execution dependencies ready for TaskScheduler'); return; } catch { await new Promise(resolve => setTimeout(resolve, checkInterval)); } } logger.warn('Timeout waiting for execution dependencies, proceeding anyway'); } validateSchedulingInputs(tasks, dependencyGraph) { if (tasks.length === 0) { throw new Error('Cannot schedule empty task list'); } for (const task of tasks) { if (!task.id || !task.title || task.estimatedHours === undefined || task.estimatedHours < 0) { throw new Error(`Invalid task: ${task.id} - missing required fields`); } } const graphNodes = dependencyGraph.getNodes(); const taskIds = new Set(tasks.map(t => t.id)); for (const taskId of taskIds) { if (!graphNodes.has(taskId)) { logger.warn('Task not found in dependency graph', { taskId }); } } } calculateTaskScores(tasks, dependencyGraph) { const scores = new Map(); const criticalPath = dependencyGraph.getCriticalPath(); const criticalPathSet = new Set(criticalPath); for (const task of tasks) { const priorityScore = this.calculatePriorityScore(task.priority); const deadlineScore = this.calculateDeadlineScore(task); const dependencyScore = this.calculateDependencyScore(task, dependencyGraph, criticalPathSet); const resourceScore = this.calculateResourceScore(task); const durationScore = this.calculateDurationScore(task); const systemLoadScore = this.calculateSystemLoadScore(task); const complexityScore = this.calculateComplexityScore(task); const businessImpactScore = this.calculateBusinessImpactScore(task); const agentAvailabilityScore = this.calculateAgentAvailabilityScore(task); const totalScore = priorityScore * this.config.weights.priority + deadlineScore * this.config.weights.deadline + dependencyScore * this.config.weights.dependencies + resourceScore * this.config.weights.resources + durationScore * this.config.weights.duration + systemLoadScore * this.config.weights.systemLoad + complexityScore * this.config.weights.complexity + businessImpactScore * this.config.weights.businessImpact + agentAvailabilityScore * this.config.weights.agentAvailability; scores.set(task.id, { priorityScore, deadlineScore, dependencyScore, resourceScore, durationScore, systemLoadScore, complexityScore, businessImpactScore, agentAvailabilityScore, totalScore }); } return scores; } async applySchedulingAlgorithm(tasks, taskScores, parallelBatches, dependencyGraph) { switch (this.config.algorithm) { case 'priority_first': return this.priorityFirstScheduling(tasks, taskScores, parallelBatches); case 'earliest_deadline': return this.earliestDeadlineScheduling(tasks, taskScores, parallelBatches); case 'critical_path': return this.criticalPathScheduling(tasks, taskScores, parallelBatches, dependencyGraph); case 'resource_balanced': return this.resourceBalancedScheduling(tasks, taskScores, parallelBatches); case 'shortest_job': return this.shortestJobScheduling(tasks, taskScores, parallelBatches); case 'hybrid_optimal': return this.hybridOptimalScheduling(tasks, taskScores, parallelBatches, dependencyGraph); default: throw new Error(`Unknown scheduling algorithm: ${this.config.algorithm}`); } } priorityFirstScheduling(tasks, taskScores, parallelBatches) { const scheduledTasks = new Map(); const sortedTasks = tasks.sort((a, b) => { const scoreA = taskScores.get(a.id)?.priorityScore || 0; const scoreB = taskScores.get(b.id)?.priorityScore || 0; return scoreB - scoreA; }); let currentTime = new Date(); let batchId = 0; for (const batch of parallelBatches) { const batchTasks = batch.taskIds .map(id => sortedTasks.find(t => t.id === id)) .filter(task => task !== undefined); for (const task of batchTasks) { const scores = taskScores.get(task.id); const resources = this.allocateResources(task); const scheduledTask = { task, scheduledStart: new Date(currentTime), scheduledEnd: new Date(currentTime.getTime() + task.estimatedHours * 60 * 60 * 1000), assignedResources: resources, batchId, prerequisiteTasks: task.dependencies, dependentTasks: task.dependents, metadata: { algorithm: 'priority_first', priorityScore: scores?.priorityScore || 0, resourceScore: scores?.resourceScore || 0, deadlineScore: scores?.deadlineScore || 0, dependencyScore: scores?.dependencyScore || 0, durationScore: scores?.durationScore || 0, systemLoadScore: scores?.systemLoadScore || 0, complexityScore: scores?.complexityScore || 0, businessImpactScore: scores?.businessImpactScore || 0, agentAvailabilityScore: scores?.agentAvailabilityScore || 0, totalScore: scores?.totalScore || 0, scheduledAt: new Date(), lastOptimized: new Date() } }; scheduledTasks.set(task.id, scheduledTask); } currentTime = new Date(currentTime.getTime() + batch.estimatedDuration * 60 * 60 * 1000); batchId++; } return scheduledTasks; } hybridOptimalScheduling(tasks, taskScores, parallelBatches, dependencyGraph) { const scheduledTasks = new Map(); const criticalPath = dependencyGraph.getCriticalPath(); const criticalPathSet = new Set(criticalPath); const sortedTasks = tasks.sort((a, b) => { const scoreA = taskScores.get(a.id); const scoreB = taskScores.get(b.id); const aCritical = criticalPathSet.has(a.id); const bCritical = criticalPathSet.has(b.id); if (aCritical !== bCritical) { return bCritical ? 1 : -1; } const totalA = scoreA?.totalScore || 0; const totalB = scoreB?.totalScore || 0; if (Math.abs(totalA - totalB) > 0.1) { return totalB - totalA; } const priorityOrder = { 'critical': 4, 'high': 3, 'medium': 2, 'low': 1 }; const priorityA = priorityOrder[a.priority] || 0; const priorityB = priorityOrder[b.priority] || 0; if (priorityA !== priorityB) { return priorityB - priorityA; } return a.estimatedHours - b.estimatedHours; }); let currentTime = new Date(); let batchId = 0; for (const batch of parallelBatches) { const batchTasks = this.optimizeBatchOrder(batch, sortedTasks, taskScores); const batchStartTime = new Date(currentTime); for (const task of batchTasks) { const scores = taskScores.get(task.id); const resources = this.allocateOptimalResources(task, batchTasks); const scheduledTask = { task, scheduledStart: batchStartTime, scheduledEnd: new Date(batchStartTime.getTime() + task.estimatedHours * 60 * 60 * 1000), assignedResources: resources, batchId, prerequisiteTasks: task.dependencies, dependentTasks: task.dependents, metadata: { algorithm: 'hybrid_optimal', priorityScore: scores?.priorityScore || 0, resourceScore: scores?.resourceScore || 0, deadlineScore: scores?.deadlineScore || 0, dependencyScore: scores?.dependencyScore || 0, durationScore: scores?.durationScore || 0, systemLoadScore: scores?.systemLoadScore || 0, complexityScore: scores?.complexityScore || 0, businessImpactScore: scores?.businessImpactScore || 0, agentAvailabilityScore: scores?.agentAvailabilityScore || 0, totalScore: scores?.totalScore || 0, scheduledAt: new Date(), lastOptimized: new Date() } }; scheduledTasks.set(task.id, scheduledTask); } const maxTaskDuration = Math.max(...batchTasks.map(t => t.estimatedHours)); const bufferTime = maxTaskDuration * 0.1; currentTime = new Date(currentTime.getTime() + (maxTaskDuration + bufferTime) * 60 * 60 * 1000); batchId++; } return scheduledTasks; } optimizeBatchOrder(batch, sortedTasks, taskScores) { const batchTasks = batch.taskIds .map(id => sortedTasks.find(t => t.id === id)) .filter(task => task !== undefined); return batchTasks.sort((a, b) => { const scoreA = taskScores.get(a.id); const scoreB = taskScores.get(b.id); const resourceA = scoreA?.resourceScore || 0; const resourceB = scoreB?.resourceScore || 0; if (Math.abs(resourceA - resourceB) > 0.1) { return resourceB - resourceA; } const totalA = scoreA?.totalScore || 0; const totalB = scoreB?.totalScore || 0; return totalB - totalA; }); } initializeResourceTracker() { return { memoryUsed: 0, cpuUsed: 0, agentsAssigned: new Set() }; } updateResourceTracker(tracker, resources) { tracker.memoryUsed += resources.memoryMB; tracker.cpuUsed += resources.cpuWeight; if (resources.agentId) { tracker.agentsAssigned.add(resources.agentId); } } calculateOptimalBatchStartTime(batchTasks, scheduledTasks, defaultStartTime) { let latestPrerequisiteEnd = defaultStartTime; for (const task of batchTasks) { for (const depId of task.dependencies) { const depTask = scheduledTasks.get(depId); if (depTask && depTask.scheduledEnd > latestPrerequisiteEnd) { latestPrerequisiteEnd = depTask.scheduledEnd; } } } return latestPrerequisiteEnd; } calculateTimeline(scheduledTasks, _parallelBatches) { const tasks = Array.from(scheduledTasks.values()); if (tasks.length === 0) { const now = new Date(); return { startTime: now, endTime: now, totalDuration: 0, criticalPath: [], parallelismFactor: 1 }; } const startTime = new Date(Math.min(...tasks.map(t => t.scheduledStart.getTime()))); const endTime = new Date(Math.max(...tasks.map(t => t.scheduledEnd.getTime()))); const totalDuration = (endTime.getTime() - startTime.getTime()) / (1000 * 60 * 60); const criticalPath = this.findCriticalPath(scheduledTasks); const totalTaskHours = tasks.reduce((sum, t) => sum + t.task.estimatedHours, 0); const parallelismFactor = totalTaskHours / totalDuration; return { startTime, endTime, totalDuration, criticalPath, parallelismFactor }; } calculateResourceUtilization(scheduledTasks) { const tasks = Array.from(scheduledTasks.values()); if (tasks.length === 0) { return { peakMemoryMB: 0, averageCpuUtilization: 0, agentUtilization: 0, resourceEfficiency: 0 }; } const peakMemoryMB = Math.max(...tasks.map(t => t.assignedResources.memoryMB)); const totalCpuWeight = tasks.reduce((sum, t) => sum + t.assignedResources.cpuWeight, 0); const averageCpuUtilization = totalCpuWeight / tasks.length; const assignedAgents = new Set(tasks.map(t => t.assignedResources.agentId).filter(id => id !== undefined)).size; const agentUtilization = assignedAgents / this.config.resources.availableAgents; const memoryEfficiency = peakMemoryMB / this.config.resources.maxMemoryMB; const cpuEfficiency = averageCpuUtilization / this.config.resources.maxCpuUtilization; const resourceEfficiency = (memoryEfficiency + cpuEfficiency + agentUtilization) / 3; return { peakMemoryMB, averageCpuUtilization, agentUtilization, resourceEfficiency }; } async optimizeSchedule(schedule) { let optimizedSchedule = { ...schedule }; optimizedSchedule = this.optimizeResourceAllocation(optimizedSchedule); optimizedSchedule = this.optimizeParallelExecution(optimizedSchedule); optimizedSchedule = this.optimizeDeadlineCompliance(optimizedSchedule); optimizedSchedule.metadata.isOptimal = true; optimizedSchedule.metadata.optimizedAt = new Date(); return optimizedSchedule; } calculatePriorityScore(priority) { const priorityMap = { 'critical': 1.0, 'high': 0.8, 'medium': 0.6, 'low': 0.4 }; return priorityMap[priority] || 0.5; } calculateDeadlineScore(task) { const now = new Date(); const priorityMultipliers = { 'critical': 1.0, 'high': 2.0, 'medium': 4.0, 'low': 8.0 }; const multiplier = priorityMultipliers[task.priority] || 4.0; const impliedDeadlineHours = task.estimatedHours * multiplier; const impliedDeadline = new Date(now.getTime() + impliedDeadlineHours * 60 * 60 * 1000); const timeToDeadline = impliedDeadline.getTime() - now.getTime(); const maxTimeWindow = 7 * 24 * 60 * 60 * 1000; const urgencyScore = Math.max(0, 1 - (timeToDeadline / maxTimeWindow)); if (task.priority === 'critical') { return Math.min(1.0, urgencyScore * 1.5); } return Math.min(1.0, urgencyScore); } calculateDependencyScore(task, dependencyGraph, criticalPathSet) { let score = 0.5; if (criticalPathSet.has(task.id)) { score += 0.3; } const dependentCount = task.dependents.length; score += Math.min(dependentCount * 0.1, 0.2); return Math.min(score, 1.0); } calculateResourceScore(task) { const taskTypeResources = this.config.resources.taskTypeResources.get(task.type); if (!taskTypeResources) { return 0.5; } const memoryRatio = taskTypeResources.memoryMB / this.config.resources.maxMemoryMB; const cpuRatio = taskTypeResources.cpuWeight / this.config.resources.maxCpuUtilization; return 1.0 - Math.min((memoryRatio + cpuRatio) / 2, 0.5); } calculateDurationScore(task) { const maxHours = 8; return 1.0 - Math.min(task.estimatedHours / maxHours, 0.8); } calculateSystemLoadScore(task) { const currentMemoryUtilization = this.getCurrentMemoryUtilization(); const currentCpuUtilization = this.getCurrentCpuUtilization(); const currentTaskLoad = this.getCurrentTaskLoad(); const taskTypeResources = this.config.resources.taskTypeResources.get(task.type); const taskMemoryRatio = taskTypeResources ? taskTypeResources.memoryMB / this.config.resources.maxMemoryMB : 0.1; const taskCpuRatio = taskTypeResources ? taskTypeResources.cpuWeight / this.config.resources.maxCpuUtilization : 0.1; const memoryAvailability = Math.max(0, 1.0 - currentMemoryUtilization - taskMemoryRatio); const cpuAvailability = Math.max(0, 1.0 - currentCpuUtilization - taskCpuRatio); const taskSlotAvailability = Math.max(0, (this.config.resources.maxConcurrentTasks - currentTaskLoad) / this.config.resources.maxConcurrentTasks); return (memoryAvailability * 0.4 + cpuAvailability * 0.4 + taskSlotAvailability * 0.2); } calculateComplexityScore(task) { let complexityFactor = 0; complexityFactor += Math.min(task.filePaths.length * 0.1, 0.3); const testingComplexity = task.testingRequirements.unitTests.length * 0.05 + task.testingRequirements.integrationTests.length * 0.1 + task.testingRequirements.performanceTests.length * 0.15; complexityFactor += Math.min(testingComplexity, 0.2); complexityFactor += Math.min(task.acceptanceCriteria.length * 0.05, 0.2); complexityFactor += Math.min(task.dependencies.length * 0.1, 0.2); const typeComplexity = { 'development': 0.6, 'research': 0.8, 'deployment': 0.7, 'testing': 0.4, 'documentation': 0.3, 'review': 0.2 }; complexityFactor += typeComplexity[task.type] || 0.5; return Math.max(0, 1.0 - Math.min(complexityFactor, 1.0)); } calculateBusinessImpactScore(task) { let impactScore = 0.5; const priorityImpact = { 'critical': 1.0, 'high': 0.8, 'medium': 0.6, 'low': 0.4 }; impactScore = priorityImpact[task.priority] || 0.5; const typeImpact = { 'deployment': 0.3, 'development': 0.2, 'testing': 0.1, 'research': 0.1, 'documentation': 0.05, 'review': 0.05 }; impactScore += typeImpact[task.type] || 0.1; const businessCriticalTags = ['critical-path', 'customer-facing', 'revenue-impact', 'security']; const hasBusinessCriticalTag = task.tags?.some(tag => businessCriticalTags.some(criticalTag => tag.toLowerCase().includes(criticalTag))); if (hasBusinessCriticalTag) { impactScore += 0.2; } return Math.min(impactScore, 1.0); } calculateAgentAvailabilityScore(task) { const totalAgents = this.config.resources.availableAgents; const busyAgents = this.getCurrentBusyAgents(); const availableAgents = Math.max(0, totalAgents - busyAgents); const availabilityRatio = totalAgents > 0 ? availableAgents / totalAgents : 0; const taskTypeResources = this.config.resources.taskTypeResources.get(task.type); const requiredAgents = taskTypeResources?.agentCount || 1; if (availableAgents >= requiredAgents) { return Math.min(availabilityRatio + 0.2, 1.0); } else { return availabilityRatio * 0.5; } } getCurrentMemoryUtilization() { if (this.currentSchedule) { return Math.min(this.currentSchedule.resourceUtilization.peakMemoryMB / this.config.resources.maxMemoryMB, 1.0); } return 0.3; } getCurrentCpuUtilization() { if (this.currentSchedule) { return Math.min(this.currentSchedule.resourceUtilization.averageCpuUtilization, 1.0); } return 0.4; } getCurrentTaskLoad() { if (this.currentSchedule) { const now = new Date(); let runningTasks = 0; for (const [, scheduledTask] of this.currentSchedule.scheduledTasks) { if (scheduledTask.scheduledStart <= now && scheduledTask.scheduledEnd > now) { runningTasks++; } } return runningTasks; } return 0; } getCurrentBusyAgents() { if (this.currentSchedule) { const busyAgents = new Set(); const now = new Date(); for (const [, scheduledTask] of this.currentSchedule.scheduledTasks) { if (scheduledTask.assignedResources.agentId && scheduledTask.scheduledStart <= now && scheduledTask.scheduledEnd > now) { busyAgents.add(scheduledTask.assignedResources.agentId); } } return busyAgents.size; } return 0; } calculateTaskDeadline(task) { const now = new Date(); const priorityMultipliers = { 'critical': 0.5, 'high': 1.0, 'medium': 2.0, 'low': 3.0 }; const multiplier = priorityMultipliers[task.priority] || 2.0; const deadlineHours = task.estimatedHours * multiplier + 24; return new Date(now.getTime() + deadlineHours * 60 * 60 * 1000); } allocateResources(task) { const taskTypeResources = this.config.resources.taskTypeResources.get(task.type); let defaultMemory = 256; let defaultCpu = 0.5; if (task.type === 'deployment') { defaultMemory = 1024; defaultCpu = 0.9; } else if (task.type === 'development') { defaultMemory = 512; defaultCpu = 0.7; } return { memoryMB: taskTypeResources?.memoryMB || defaultMemory, cpuWeight: taskTypeResources?.cpuWeight || defaultCpu, agentId: this.assignAgent(task) }; } allocateOptimalResources(task, batchTasks) { const baseResources = this.allocateResources(task); const batchMemoryTotal = batchTasks.reduce((sum, t) => { const taskRes = this.config.resources.taskTypeResources.get(t.type); return sum + (taskRes?.memoryMB || 256); }, 0); if (batchMemoryTotal > this.config.resources.maxMemoryMB) { const scaleFactor = this.config.resources.maxMemoryMB / batchMemoryTotal; baseResources.memoryMB = Math.floor(baseResources.memoryMB * scaleFactor); } baseResources.agentId = this.assignAgent(task); return baseResources; } assignAgent(task) { const agentCount = this.config.resources.availableAgents; if (agentCount === 0) return undefined; if (!task) { const agentId = `agent_${(this.scheduleVersion % agentCount) + 1}`; return agentId; } const agentCapabilities = new Map([ ['agent_1', ['development', 'testing', 'review']], ['agent_2', ['deployment', 'documentation', 'research']], ['agent_3', ['development', 'testing', 'deployment']] ]); const availableAgents = Array.from({ length: agentCount }, (_, i) => `agent_${i + 1}`); const capableAgents = availableAgents.filter(agentId => { const capabilities = agentCapabilities.get(agentId) || ['development', 'testing']; return capabilities.includes(task.type); }); if (capableAgents.length === 0) { const agentId = `agent_${(this.scheduleVersion % agentCount) + 1}`; return agentId; } const agentLoads = new Map(availableAgents.map(agentId => [agentId, Math.floor(Math.random() * 5)])); const selectedAgent = capableAgents.reduce((best, current) => { const currentLoad = agentLoads.get(current) || 0; const bestLoad = agentLoads.get(best) || 0; return currentLoad < bestLoad ? current : best; }); return selectedAgent; } findCriticalPath(scheduledTasks) { const tasks = Array.from(scheduledTasks.values()); let longestPath = []; let maxDuration = 0; for (const task of tasks) { const path = this.findPathFromTask(task, scheduledTasks); const pathDuration = path.reduce((sum, taskId) => { const t = scheduledTasks.get(taskId); return sum + (t?.task.estimatedHours || 0); }, 0); if (pathDuration > maxDuration) { maxDuration = pathDuration; longestPath = path; } } return longestPath; } findPathFromTask(startTask, scheduledTasks) { const path = [startTask.task.id]; const visited = new Set([startTask.task.id]); let currentTask = startTask; while (currentTask.dependentTasks.length > 0) { let nextTask; let maxDuration = 0; for (const dependentId of currentTask.dependentTasks) { if (visited.has(dependentId)) continue; const dependent = scheduledTasks.get(dependentId); if (dependent && dependent.task.estimatedHours > maxDuration) { maxDuration = dependent.task.estimatedHours; nextTask = dependent; } } if (!nextTask) break; path.push(nextTask.task.id); visited.add(nextTask.task.id); currentTask = nextTask; } return path; } optimizeResourceAllocation(schedule) { return schedule; } optimizeParallelExecution(schedule) { return schedule; } optimizeDeadlineCompliance(schedule) { return schedule; } shouldReschedule(updatedTasks) { if (!this.currentSchedule) return true; const sensitivity = this.config.rescheduleSensitivity; const thresholds = { 'low': 0.3, 'medium': 0.2, 'high': 0.1 }; const changeRatio = updatedTasks.length / this.currentSchedule.scheduledTasks.size; return changeRatio > thresholds[sensitivity]; } async incrementalUpdate(_updatedTasks, _dependencyGraph) { return this.currentSchedule; } arePrerequisitesComplete(scheduledTask) { if (!this.currentSchedule) return false; return scheduledTask.prerequisiteTasks.every(prereqId => { const prereq = this.currentSchedule.scheduledTasks.get(prereqId); return prereq?.task.status === 'completed'; }); } calculateActualHours(scheduledTask) { const now = new Date(); const startTime = scheduledTask.scheduledStart; const elapsedMs = now.getTime() - startTime.getTime(); const elapsedHours = elapsedMs / (1000 * 60 * 60); return Math.max(elapsedHours, 0.1); } async checkForOptimization() { if (!this.currentSchedule || !this.config.enableDynamicOptimization) { return; } const resourceEfficiency = this.currentSchedule.resourceUtilization.resourceEfficiency; if (resourceEfficiency < 0.7) { logger.info('Triggering schedule optimization due to low resource utilization'); setTimeout(() => this.optimizeCurrentSchedule(), 1000); } } async optimizeCurrentSchedule() { if (!this.currentSchedule) return; try { const optimized = await this.optimizeSchedule(this.currentSchedule); this.currentSchedule = optimized; logger.info('Schedule optimized successfully'); } catch (error) { logger.error('Failed to optimize schedule', { error }); } } startOptimizationTimer() { if (this.optimizationTimer) { clearInterval(this.optimizationTimer); } this.optimizationTimer = setInterval(() => this.checkForOptimization(), this.config.optimizationInterval * 60 * 1000); } earliestDeadlineScheduling(tasks, taskScores, parallelBatches) { const scheduledTasks = new Map(); const sortedTasks = tasks.sort((a, b) => { const deadlineA = this.calculateTaskDeadline(a); const deadlineB = this.calculateTaskDeadline(b); return deadlineA.getTime() - deadlineB.getTime(); }); let currentTime = new Date(); let batchId = 0; for (const batch of parallelBatches) { const batchTasks = batch.taskIds .map(id => sortedTasks.find(t => t.id === id)) .filter(task => task !== undefined); for (const task of batchTasks) { const scores = taskScores.get(task.id); const resources = this.allocateResources(task); const scheduledTask = { task, scheduledStart: new Date(currentTime), scheduledEnd: new Date(currentTime.getTime() + task.estimatedHours * 60 * 60 * 1000), assignedResources: resources, batchId, prerequisiteTasks: task.dependencies, dependentTasks: task.dependents, metadata: { algorithm: 'earliest_deadline', priorityScore: scores?.priorityScore || 0, resourceScore: scores?.resourceScore || 0, deadlineScore: scores?.deadlineScore || 0, dependencyScore: scores?.dependencyScore || 0, durationScore: scores?.durationScore || 0, systemLoadScore: scores?.systemLoadScore || 0, complexityScore: scores?.complexityScore || 0, businessImpactScore: scores?.businessImpactScore || 0, agentAvailabilityScore: scores?.agentAvailabilityScore || 0, totalScore: scores?.totalScore || 0, scheduledAt: new Date(), lastOptimized: new Date() } }; scheduledTasks.set(task.id, scheduledTask); } currentTime = new Date(currentTime.getTime() + batch.estimatedDuration * 60 * 60 * 1000); batchId++; } return scheduledTasks; } criticalPathScheduling(tasks, taskScores, parallelBatches, dependencyGraph) { const scheduledTasks = new Map(); const criticalPath = dependencyGraph.getCriticalPath(); const criticalPathSet = new Set(criticalPath); const sortedTasks = tasks.sort((a, b) => { const aOnCriticalPath = criticalPathSet.has(a.id); const bOnCriticalPath = criticalPathSet.has(b.id); if (aOnCriticalPath && !bOnCriticalPath) return -1; if (!aOnCriticalPath && bOnCriticalPath) return 1; const scoreA = taskScores.get(a.id)?.totalScore || 0; const scoreB = taskScores.get(b.id)?.totalScore || 0; return scoreB - scoreA; }); let currentTime = new Date(); let batchId = 0; for (const batch of parallelBatches) { const batchTasks = this.optimizeBatchOrder(batch, sortedTasks, taskScores); const batchStartTime = new Date(currentTime); for (const task of batchTasks) { const scores = taskScores.get(task.id); const resources = this.allocateOptimalResources(task, batchTasks); const scheduledTask = { task, scheduledStart: batchStartTime, scheduledEnd: new Date(batchStartTime.getTime() + task.estimatedHours * 60 * 60 * 1000), assignedResources: resources, batchId,