Skip to main content
Glama
intelligent-agent-assignment.ts35.9 kB
/** * Intelligent Agent Assignment Service * * Provides intelligent workload distribution with capability-based matching, * performance-aware assignment, and predictive workload management. */ import { EventEmitter } from 'events'; import { Agent, AgentCapability, TaskAssignment, AgentStatus } from '../types/agent.js'; import { AtomicTask } from '../types/task.js'; import logger from '../../../logger.js'; /** * Workload distribution strategies */ export type WorkloadDistributionStrategy = | 'round_robin' | 'least_loaded' | 'capability_first' | 'performance_based' | 'intelligent_hybrid'; /** * Agent assignment configuration */ export interface AgentAssignmentConfig { strategy: WorkloadDistributionStrategy; maxTasksPerAgent: number; workloadBalanceThreshold: number; // 0-1, threshold for imbalance detection capabilityMatchWeight: number; // 0-1, weight for capability matching performanceWeight: number; // 0-1, weight for performance metrics availabilityWeight: number; // 0-1, weight for availability predictiveLoadingEnabled: boolean; autoRebalanceEnabled: boolean; rebalanceInterval?: number; } /** * Agent assignment result */ export interface AgentAssignmentResult { success: boolean; assignment?: TaskAssignment; error?: string; score?: number; alternatives?: { agentId: string; score: number }[]; } /** * Agent registration result */ export interface AgentRegistrationResult { success: boolean; error?: string; } /** * Workload imbalance detection result */ export interface WorkloadImbalanceResult { isImbalanced: boolean; imbalanceRatio: number; overloadedAgents: string[]; underloadedAgents: string[]; severity: 'low' | 'medium' | 'high' | 'critical'; } /** * Task redistribution suggestion */ export interface TaskRedistributionSuggestion { fromAgent: string; toAgent: string; tasksToMove: string[]; expectedImprovement: number; reasoning: string; } /** * Performance statistics */ export interface PerformanceStats { totalAgents: number; activeAgents: number; averageSuccessRate: number; averageCompletionTime: number; totalTasksCompleted: number; taskThroughput: number; // tasks per hour } /** * Task completion prediction */ export interface TaskCompletionPrediction { estimatedCompletionTime: number; confidence: number; factors: { agentPerformance: number; taskComplexity: number; currentLoad: number; historicalData: number; }; } /** * Best agent selection result */ export interface BestAgentResult { agentId: string; score: number; reasoning: string; confidence: number; } /** * Workload trend prediction */ export interface WorkloadTrendPrediction { timeHorizon: number; agentUtilization: Record<string, number>; expectedBottlenecks: string[]; recommendations: string[]; } /** * Real-time metrics */ export interface RealTimeMetrics { totalAgents: number; activeAgents: number; averageLoad: number; taskThroughput: number; currentStrategy: WorkloadDistributionStrategy; lastUpdateTime: Date; } /** * Assignment efficiency metrics */ export interface AssignmentEfficiency { successfulAssignments: number; failedAssignments: number; averageAssignmentTime: number; capabilityMatchRate: number; performanceUtilization: number; } /** * Workload rebalancing result */ export interface WorkloadRebalanceResult { success: boolean; redistributions: number; affectedAgents: string[]; improvementScore: number; error?: string; } /** * Scaling recommendations */ export interface ScalingRecommendations { currentCapacity: number; recommendedCapacity: number; reasoning: string; urgency: 'low' | 'medium' | 'high' | 'critical'; timeframe: string; } /** * Task optimization result */ export interface TaskOptimizationResult { success: boolean; assignments: { taskId: string; agentId: string; score: number }[]; totalScore: number; error?: string; } /** * Configuration recommendations */ export interface ConfigurationRecommendations { currentPerformance: number; suggestedChanges: { parameter: string; currentValue: unknown; suggestedValue: unknown; reasoning: string; expectedImprovement: number; }[]; expectedImprovement: number; } /** * Default configuration */ const DEFAULT_CONFIG: AgentAssignmentConfig = { strategy: 'intelligent_hybrid', maxTasksPerAgent: 5, workloadBalanceThreshold: 0.8, capabilityMatchWeight: 0.4, performanceWeight: 0.3, availabilityWeight: 0.3, predictiveLoadingEnabled: true, autoRebalanceEnabled: true, rebalanceInterval: 60000 // 1 minute }; /** * Intelligent Agent Assignment Service */ export class IntelligentAgentAssignmentService extends EventEmitter { private config: AgentAssignmentConfig; private agents: Map<string, Agent> = new Map(); private assignments: Map<string, TaskAssignment> = new Map(); private performanceHistory: Map<string, unknown[]> = new Map(); private roundRobinIndex = 0; private statistics = { totalAssignments: 0, successfulAssignments: 0, failedAssignments: 0, totalAssignmentTime: 0 }; private rebalanceTimer?: NodeJS.Timeout; private disposed = false; constructor(config: Partial<AgentAssignmentConfig> = {}) { super(); // Validate configuration this.validateConfig(config); this.config = { ...DEFAULT_CONFIG, ...config }; // Start auto-rebalancing if enabled if (this.config.autoRebalanceEnabled && this.config.rebalanceInterval) { this.startAutoRebalancing(); } logger.info({ strategy: this.config.strategy, maxTasksPerAgent: this.config.maxTasksPerAgent, autoRebalanceEnabled: this.config.autoRebalanceEnabled }, 'IntelligentAgentAssignmentService initialized'); } /** * Register an agent */ registerAgent(agent: Agent): AgentRegistrationResult { try { if (this.agents.has(agent.id)) { return { success: false, error: `Agent ${agent.id} is already registered` }; } this.agents.set(agent.id, { ...agent }); this.performanceHistory.set(agent.id, []); logger.info({ agentId: agent.id, capabilities: agent.capabilities, maxTasks: agent.config.maxConcurrentTasks }, 'Agent registered'); this.emit('agent:registered', { agentId: agent.id, agent }); return { success: true }; } catch (error) { logger.error({ err: error, agentId: agent.id }, 'Failed to register agent'); return { success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Unregister an agent */ unregisterAgent(agentId: string): AgentRegistrationResult { try { if (!this.agents.has(agentId)) { return { success: false, error: `Agent ${agentId} is not registered` }; } // Remove agent and cleanup this.agents.delete(agentId); this.performanceHistory.delete(agentId); // Remove any assignments for (const [taskId, assignment] of this.assignments) { if (assignment.agentId === agentId) { this.assignments.delete(taskId); } } logger.info({ agentId }, 'Agent unregistered'); this.emit('agent:unregistered', { agentId }); return { success: true }; } catch (error) { logger.error({ err: error, agentId }, 'Failed to unregister agent'); return { success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Get an agent by ID */ getAgent(agentId: string): Agent | undefined { return this.agents.get(agentId); } /** * Update agent status */ updateAgentStatus(agentId: string, status: AgentStatus): boolean { const agent = this.agents.get(agentId); if (!agent) { return false; } const oldStatus = agent.status; agent.status = status; agent.performance.lastActiveAt = new Date(); logger.debug({ agentId, oldStatus, newStatus: status }, 'Agent status updated'); this.emit('agent:status_changed', { agentId, oldStatus, newStatus: status }); return true; } /** * Assign a task to the best available agent */ async assignTask(task: AtomicTask): Promise<AgentAssignmentResult> { const startTime = Date.now(); try { // Validate task if (!this.validateTask(task)) { return { success: false, error: 'Invalid task: missing required fields or invalid values' }; } // Check if task is already assigned if (this.assignments.has(task.id)) { return { success: false, error: `Task ${task.id} is already assigned` }; } // Find best agent using configured strategy const bestAgent = await this.findBestAgent(task); if (!bestAgent) { return { success: false, error: 'No suitable agents available for this task' }; } // Create assignment const assignment: TaskAssignment = { id: `assignment_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, taskId: task.id, agentId: bestAgent.agentId, assignedAt: new Date(), expectedCompletionAt: new Date(Date.now() + task.estimatedHours * 60 * 60 * 1000), priority: this.mapTaskPriorityToAssignmentPriority(task.priority), context: { projectId: task.projectId, epicId: task.epicId, dependencies: task.dependencies, resources: task.filePaths, constraints: task.tags || [] }, status: 'pending', progress: { percentage: 0, currentStep: 'assigned', estimatedTimeRemaining: task.estimatedHours * 60 * 60 * 1000, lastUpdateAt: new Date() } }; // Update agent state const agent = this.agents.get(bestAgent.agentId)!; agent.taskQueue.push(task.id); if (agent.status === 'idle') { agent.status = 'busy'; agent.currentTask = task.id; } // Store assignment this.assignments.set(task.id, assignment); // Update statistics this.statistics.totalAssignments++; this.statistics.successfulAssignments++; this.statistics.totalAssignmentTime += Date.now() - startTime; logger.info({ taskId: task.id, agentId: bestAgent.agentId, score: bestAgent.score, assignmentTime: Date.now() - startTime }, 'Task assigned successfully'); this.emit('task:assigned', { assignment, score: bestAgent.score }); return { success: true, assignment, score: bestAgent.score }; } catch (error) { this.statistics.failedAssignments++; logger.error({ err: error, taskId: task.id }, 'Failed to assign task'); return { success: false, error: error instanceof Error ? error.message : String(error) }; } } /** * Find the best agent for a task using configured strategy */ async findBestAgent(task: AtomicTask): Promise<BestAgentResult | null> { const candidates = Array.from(this.agents.values()).filter(agent => this.isAgentEligible(agent, task) ); if (candidates.length === 0) { return null; } let bestAgent: Agent | null = null; let bestScore = -1; switch (this.config.strategy) { case 'round_robin': bestAgent = this.selectRoundRobinAgent(candidates); bestScore = 0.5; // Default score for round robin break; case 'least_loaded': bestAgent = this.selectLeastLoadedAgent(candidates); bestScore = 0.7; // Higher score for least loaded break; case 'capability_first': { const capResult = this.selectCapabilityFirstAgent(candidates, task); bestAgent = capResult.agent; bestScore = capResult.score; break; } case 'performance_based': { const perfResult = this.selectPerformanceBasedAgent(candidates); bestAgent = perfResult.agent; bestScore = perfResult.score; break; } case 'intelligent_hybrid': default: for (const agent of candidates) { const score = await this.calculateAgentScore(agent, task); if (score > bestScore) { bestScore = score; bestAgent = agent; } } break; } if (!bestAgent) { return null; } return { agentId: bestAgent.id, score: bestScore, reasoning: this.generateAssignmentReasoning(bestAgent, task, bestScore), confidence: this.calculateConfidence(bestAgent, task, bestScore) }; } /** * Get performance statistics */ getPerformanceStats(): PerformanceStats { const agents = Array.from(this.agents.values()); const totalTasks = agents.reduce((sum, agent) => sum + agent.performance.tasksCompleted, 0); const totalTime = agents.reduce((sum, agent) => sum + agent.performance.averageCompletionTime, 0); const totalSuccessRate = agents.reduce((sum, agent) => sum + agent.performance.successRate, 0); // Calculate throughput (tasks per hour) const now = Date.now(); const oneHourAgo = now - 3600000; const recentTasks = agents.reduce((sum, agent) => { const recentActivity = agent.performance.lastActiveAt.getTime() > oneHourAgo ? 1 : 0; return sum + recentActivity; }, 0); return { totalAgents: agents.length, activeAgents: agents.filter(a => a.status !== 'offline').length, averageSuccessRate: agents.length > 0 ? totalSuccessRate / agents.length : 0, averageCompletionTime: agents.length > 0 ? totalTime / agents.length : 0, totalTasksCompleted: totalTasks, taskThroughput: recentTasks }; } /** * Predict task completion time for an agent */ predictTaskCompletion(agentId: string, task: AtomicTask): TaskCompletionPrediction { const agent = this.agents.get(agentId); if (!agent) { throw new Error(`Agent ${agentId} not found`); } const baseTime = agent.performance.averageCompletionTime; const taskComplexity = this.calculateTaskComplexity(task); const currentLoad = this.calculateAgentLoad(agent); // Adjust based on various factors let estimatedTime = baseTime * taskComplexity; estimatedTime *= (1 + currentLoad * 0.5); // Increase time based on current load estimatedTime *= task.estimatedHours; // Scale by estimated hours // Calculate confidence based on historical data const confidence = Math.min( agent.performance.tasksCompleted / 10, // More tasks = higher confidence agent.performance.successRate, // Higher success rate = higher confidence 1.0 ); return { estimatedCompletionTime: estimatedTime, confidence, factors: { agentPerformance: agent.performance.averageCompletionTime, taskComplexity, currentLoad, historicalData: agent.performance.tasksCompleted } }; } /** * Detect workload imbalances */ detectWorkloadImbalance(): WorkloadImbalanceResult { const agents = Array.from(this.agents.values()); const loads = agents.map(agent => this.calculateAgentLoad(agent)); if (loads.length === 0) { return { isImbalanced: false, imbalanceRatio: 0, overloadedAgents: [], underloadedAgents: [], severity: 'low' }; } const avgLoad = loads.reduce((sum, load) => sum + load, 0) / loads.length; const maxLoad = Math.max(...loads); const minLoad = Math.min(...loads); const imbalanceRatio = maxLoad - minLoad; const isImbalanced = imbalanceRatio > (this.config.workloadBalanceThreshold / 2); // More sensitive detection const overloadedAgents = agents .filter((agent, index) => loads[index] > avgLoad + this.config.workloadBalanceThreshold / 2) .map(agent => agent.id); const underloadedAgents = agents .filter((agent, index) => loads[index] < avgLoad - this.config.workloadBalanceThreshold / 2) .map(agent => agent.id); let severity: 'low' | 'medium' | 'high' | 'critical' = 'low'; if (imbalanceRatio > 0.8) severity = 'critical'; else if (imbalanceRatio > 0.6) severity = 'high'; else if (imbalanceRatio > 0.4) severity = 'medium'; return { isImbalanced, imbalanceRatio, overloadedAgents, underloadedAgents, severity }; } /** * Suggest task redistributions */ suggestTaskRedistribution(): TaskRedistributionSuggestion[] { const suggestions: TaskRedistributionSuggestion[] = []; const imbalance = this.detectWorkloadImbalance(); if (!imbalance.isImbalanced) { return suggestions; } for (const overloadedAgentId of imbalance.overloadedAgents) { const overloadedAgent = this.agents.get(overloadedAgentId); if (!overloadedAgent) continue; for (const underloadedAgentId of imbalance.underloadedAgents) { const underloadedAgent = this.agents.get(underloadedAgentId); if (!underloadedAgent) continue; // Find tasks that could be moved const movableTasks = overloadedAgent.taskQueue.slice(0, 2); // Move up to 2 tasks if (movableTasks.length > 0) { suggestions.push({ fromAgent: overloadedAgentId, toAgent: underloadedAgentId, tasksToMove: movableTasks, expectedImprovement: 0.3, // Simplified calculation reasoning: `Balance workload between overloaded ${overloadedAgentId} and underloaded ${underloadedAgentId}` }); } } } return suggestions; } /** * Execute workload rebalancing */ async rebalanceWorkload(): Promise<WorkloadRebalanceResult> { try { const suggestions = this.suggestTaskRedistribution(); let redistributions = 0; const affectedAgents = new Set<string>(); for (const suggestion of suggestions) { const fromAgent = this.agents.get(suggestion.fromAgent); const toAgent = this.agents.get(suggestion.toAgent); if (!fromAgent || !toAgent) continue; // Move tasks for (const taskId of suggestion.tasksToMove) { const taskIndex = fromAgent.taskQueue.indexOf(taskId); if (taskIndex !== -1) { fromAgent.taskQueue.splice(taskIndex, 1); toAgent.taskQueue.push(taskId); // Update assignment if it exists const assignment = this.assignments.get(taskId); if (assignment) { assignment.agentId = toAgent.id; assignment.assignedAt = new Date(); } redistributions++; affectedAgents.add(fromAgent.id); affectedAgents.add(toAgent.id); } } } logger.info({ redistributions, affectedAgents: Array.from(affectedAgents) }, 'Workload rebalancing completed'); this.emit('workload:rebalanced', { redistributions, affectedAgents: Array.from(affectedAgents) }); return { success: true, redistributions, affectedAgents: Array.from(affectedAgents), improvementScore: redistributions * 0.1 // Simplified calculation }; } catch (error) { logger.error({ err: error }, 'Failed to rebalance workload'); return { success: false, redistributions: 0, affectedAgents: [], improvementScore: 0, error: error instanceof Error ? error.message : String(error) }; } } /** * Predict workload trends */ predictWorkloadTrends(timeHorizon: number): WorkloadTrendPrediction { const agents = Array.from(this.agents.values()); const agentUtilization: Record<string, number> = {}; const expectedBottlenecks: string[] = []; const recommendations: string[] = []; // Calculate current utilization and project forward for (const agent of agents) { const currentLoad = this.calculateAgentLoad(agent); const projectedLoad = currentLoad * 1.2; // Simple projection agentUtilization[agent.id] = projectedLoad; if (projectedLoad > 0.9) { expectedBottlenecks.push(agent.id); recommendations.push(`Consider offloading tasks from ${agent.id}`); } } if (expectedBottlenecks.length > agents.length * 0.5) { recommendations.push('Consider adding more agents to handle increased load'); } return { timeHorizon, agentUtilization, expectedBottlenecks, recommendations }; } /** * Get scaling recommendations */ getScalingRecommendations(): ScalingRecommendations { const stats = this.getPerformanceStats(); const currentCapacity = stats.totalAgents; const utilization = stats.activeAgents / stats.totalAgents; let recommendedCapacity = currentCapacity; let urgency: 'low' | 'medium' | 'high' | 'critical' = 'low'; let reasoning = 'Current capacity is sufficient'; if (utilization > 0.9) { recommendedCapacity = Math.ceil(currentCapacity * 1.5); urgency = 'critical'; reasoning = 'High utilization detected, recommend scaling up'; } else if (utilization > 0.8) { recommendedCapacity = Math.ceil(currentCapacity * 1.3); urgency = 'high'; reasoning = 'High utilization, recommend scaling up soon'; } else if (utilization > 0.7) { recommendedCapacity = Math.ceil(currentCapacity * 1.2); urgency = 'medium'; reasoning = 'Moderate utilization, consider gradual scaling'; } else if (utilization < 0.3) { recommendedCapacity = Math.max(1, Math.floor(currentCapacity * 0.8)); urgency = 'low'; reasoning = 'Low utilization, consider scaling down'; } return { currentCapacity, recommendedCapacity, reasoning, urgency, timeframe: urgency === 'critical' ? 'immediate' : urgency === 'high' ? 'within 1 hour' : urgency === 'medium' ? 'within 1 day' : 'within 1 week' }; } /** * Optimize agent allocation for upcoming tasks */ async optimizeForUpcomingTasks(tasks: AtomicTask[]): Promise<TaskOptimizationResult> { try { const assignments: { taskId: string; agentId: string; score: number }[] = []; let totalScore = 0; // Simple greedy optimization for (const task of tasks) { const bestAgent = await this.findBestAgent(task); if (bestAgent) { assignments.push({ taskId: task.id, agentId: bestAgent.agentId, score: bestAgent.score }); totalScore += bestAgent.score; } } return { success: true, assignments, totalScore }; } catch (error) { return { success: false, assignments: [], totalScore: 0, error: error instanceof Error ? error.message : String(error) }; } } /** * Get real-time metrics */ getRealTimeMetrics(): RealTimeMetrics { const agents = Array.from(this.agents.values()); const loads = agents.map(agent => this.calculateAgentLoad(agent)); const averageLoad = loads.length > 0 ? loads.reduce((sum, load) => sum + load, 0) / loads.length : 0; return { totalAgents: agents.length, activeAgents: agents.filter(a => a.status !== 'offline').length, averageLoad, taskThroughput: this.calculateTaskThroughput(), currentStrategy: this.config.strategy, lastUpdateTime: new Date() }; } /** * Get assignment efficiency metrics */ getAssignmentEfficiency(): AssignmentEfficiency { const avgAssignmentTime = this.statistics.totalAssignments > 0 ? this.statistics.totalAssignmentTime / this.statistics.totalAssignments : 0; return { successfulAssignments: this.statistics.successfulAssignments, failedAssignments: this.statistics.failedAssignments, averageAssignmentTime: avgAssignmentTime, capabilityMatchRate: 0.85, // Simplified calculation performanceUtilization: 0.78 // Simplified calculation }; } /** * Check and emit workload events */ async checkAndEmitWorkloadEvents(): Promise<void> { const imbalance = this.detectWorkloadImbalance(); if (imbalance.isImbalanced) { this.emit('workload:imbalance', { type: 'imbalance_detected', severity: imbalance.severity, details: imbalance }); } } /** * Update assignment strategy */ updateStrategy(strategy: WorkloadDistributionStrategy): void { this.config.strategy = strategy; logger.info({ newStrategy: strategy }, 'Assignment strategy updated'); } /** * Update workload threshold */ updateWorkloadThreshold(threshold: number): void { this.config.workloadBalanceThreshold = threshold; logger.info({ newThreshold: threshold }, 'Workload threshold updated'); } /** * Get current configuration */ getConfiguration(): AgentAssignmentConfig { return { ...this.config }; } /** * Get configuration recommendations */ getConfigurationRecommendations(): ConfigurationRecommendations { const currentPerformance = this.calculateOverallPerformance(); return { currentPerformance, suggestedChanges: [ { parameter: 'capabilityMatchWeight', currentValue: this.config.capabilityMatchWeight, suggestedValue: 0.5, reasoning: 'Increase capability matching for better task-agent alignment', expectedImprovement: 0.15 } ], expectedImprovement: 0.15 }; } /** * Get active assignments */ getActiveAssignments(): TaskAssignment[] { return Array.from(this.assignments.values()); } /** * Dispose the service */ dispose(): void { if (this.disposed) { return; } // Stop auto-rebalancing if (this.rebalanceTimer) { clearInterval(this.rebalanceTimer); this.rebalanceTimer = undefined; } // Clear all data this.agents.clear(); this.assignments.clear(); this.performanceHistory.clear(); this.removeAllListeners(); this.disposed = true; logger.info('IntelligentAgentAssignmentService disposed'); } // Private helper methods private validateConfig(config: Partial<AgentAssignmentConfig>): void { if (config.maxTasksPerAgent !== undefined && config.maxTasksPerAgent < 1) { throw new Error('maxTasksPerAgent must be at least 1'); } if (config.workloadBalanceThreshold !== undefined && (config.workloadBalanceThreshold < 0 || config.workloadBalanceThreshold > 1)) { throw new Error('workloadBalanceThreshold must be between 0 and 1'); } if (config.capabilityMatchWeight !== undefined && (config.capabilityMatchWeight < 0 || config.capabilityMatchWeight > 1)) { throw new Error('capabilityMatchWeight must be between 0 and 1'); } // Validate that weights sum to 1 (only if weights are provided) const capWeight = config.capabilityMatchWeight ?? DEFAULT_CONFIG.capabilityMatchWeight; const perfWeight = config.performanceWeight ?? DEFAULT_CONFIG.performanceWeight; const availWeight = config.availabilityWeight ?? DEFAULT_CONFIG.availabilityWeight; const weightSum = capWeight + perfWeight + availWeight; if (Math.abs(weightSum - 1.0) > 0.01) { throw new Error(`Capability, performance, and availability weights must sum to 1.0. Current sum: ${weightSum}`); } } private validateTask(task: AtomicTask): boolean { return !!(task.id && task.type && task.estimatedHours > 0 && task.projectId); } private isAgentEligible(agent: Agent, task: AtomicTask): boolean { // Check if agent is available if (agent.status === 'offline' || agent.status === 'error') { return false; } // Check if agent has capacity if (agent.taskQueue.length >= this.config.maxTasksPerAgent) { return false; } // Check if agent has required capabilities (basic check) const taskCapabilities = this.mapTaskTypeToCapabilities(task.type); const hasRequiredCapability = taskCapabilities.some(cap => agent.capabilities.includes(cap) ); return hasRequiredCapability; } private async calculateAgentScore(agent: Agent, task: AtomicTask): Promise<number> { let score = 0; // Capability match score const capabilityScore = this.calculateCapabilityScore(agent, task); score += capabilityScore * this.config.capabilityMatchWeight; // Performance score const performanceScore = this.calculatePerformanceScore(agent); score += performanceScore * this.config.performanceWeight; // Availability score const availabilityScore = this.calculateAvailabilityScore(agent); score += availabilityScore * this.config.availabilityWeight; return Math.min(1.0, Math.max(0, score)); } private calculateCapabilityScore(agent: Agent, task: AtomicTask): number { const requiredCapabilities = this.mapTaskTypeToCapabilities(task.type); const matchedCapabilities = requiredCapabilities.filter(cap => agent.capabilities.includes(cap) ).length; if (requiredCapabilities.length === 0) { return 0.5; // Neutral score for tasks with no specific requirements } const baseScore = matchedCapabilities / requiredCapabilities.length; // Bonus for preferred task types const isPreferred = agent.config.preferredTaskTypes.includes(task.type); return Math.min(1.0, baseScore + (isPreferred ? 0.2 : 0)); } private calculatePerformanceScore(agent: Agent): number { // Normalize success rate (already 0-1) const successScore = agent.performance.successRate; // Normalize completion time (lower is better) const avgCompletionTime = agent.performance.averageCompletionTime; const maxReasonableTime = 8 * 60 * 60 * 1000; // 8 hours const timeScore = Math.max(0, 1 - (avgCompletionTime / maxReasonableTime)); // Normalize task count (more experience is better) const experienceScore = Math.min(1.0, agent.performance.tasksCompleted / 50); return (successScore * 0.5 + timeScore * 0.3 + experienceScore * 0.2); } private calculateAvailabilityScore(agent: Agent): number { if (agent.status === 'idle') { return 1.0; } // Calculate load-based availability const currentLoad = this.calculateAgentLoad(agent); return Math.max(0, 1 - currentLoad); } private calculateAgentLoad(agent: Agent): number { const maxTasks = agent.config.maxConcurrentTasks; const currentTasks = agent.taskQueue.length + (agent.currentTask ? 1 : 0); return Math.min(1.0, currentTasks / maxTasks); } private calculateTaskComplexity(task: AtomicTask): number { let complexity = 1.0; // Base complexity from estimated hours complexity *= Math.min(2.0, task.estimatedHours / 4); // Normalize to 4 hours // Complexity from dependencies complexity *= (1 + task.dependencies.length * 0.1); // Complexity from file paths complexity *= (1 + task.filePaths.length * 0.05); return Math.min(3.0, complexity); } private calculateTaskThroughput(): number { // Simplified calculation const now = Date.now(); const oneHour = 60 * 60 * 1000; const recentAssignments = Array.from(this.assignments.values()).filter( assignment => assignment.assignedAt.getTime() > now - oneHour ); return recentAssignments.length; } private calculateOverallPerformance(): number { const efficiency = this.getAssignmentEfficiency(); const successRate = efficiency.successfulAssignments / (efficiency.successfulAssignments + efficiency.failedAssignments || 1); return successRate * efficiency.capabilityMatchRate * efficiency.performanceUtilization; } private generateAssignmentReasoning(agent: Agent, task: AtomicTask, score: number): string { const capabilities = this.mapTaskTypeToCapabilities(task.type); const matchedCaps = capabilities.filter(cap => agent.capabilities.includes(cap)); return `Selected ${agent.name} (score: ${score.toFixed(2)}) for ${task.type} task. ` + `Matched capabilities: ${matchedCaps.join(', ')}. ` + `Performance: ${(agent.performance.successRate * 100).toFixed(1)}% success rate, ` + `${agent.performance.tasksCompleted} tasks completed.`; } private calculateConfidence(agent: Agent, task: AtomicTask, score: number): number { // Base confidence from score let confidence = score; // Adjust based on agent experience const experienceFactor = Math.min(1.0, agent.performance.tasksCompleted / 20); confidence *= (0.5 + experienceFactor * 0.5); // Adjust based on task complexity const complexity = this.calculateTaskComplexity(task); confidence *= Math.max(0.3, 1 - (complexity - 1) * 0.2); return Math.min(1.0, Math.max(0.1, confidence)); } private mapTaskTypeToCapabilities(taskType: string): AgentCapability[] { const mapping: Record<string, AgentCapability[]> = { 'development': ['code_generation', 'debugging'], 'testing': ['testing', 'debugging'], 'documentation': ['documentation'], 'research': ['research'], 'deployment': ['deployment'], 'review': ['review', 'code_generation'] }; return mapping[taskType] || ['code_generation']; // Default fallback } private mapTaskPriorityToAssignmentPriority(priority: string): 'low' | 'normal' | 'high' | 'urgent' { const mapping: Record<string, 'low' | 'normal' | 'high' | 'urgent'> = { 'low': 'low', 'medium': 'normal', 'high': 'high', 'critical': 'urgent' }; return mapping[priority] || 'normal'; } private selectRoundRobinAgent(candidates: Agent[]): Agent { const agent = candidates[this.roundRobinIndex % candidates.length]; this.roundRobinIndex++; return agent; } private selectLeastLoadedAgent(candidates: Agent[]): Agent { return candidates.reduce((leastLoaded, current) => { const currentLoad = this.calculateAgentLoad(current); const leastLoad = this.calculateAgentLoad(leastLoaded); return currentLoad < leastLoad ? current : leastLoaded; }); } private selectCapabilityFirstAgent(candidates: Agent[], task: AtomicTask): { agent: Agent; score: number } { let bestAgent = candidates[0]; let bestScore = 0; for (const agent of candidates) { const score = this.calculateCapabilityScore(agent, task); if (score > bestScore) { bestScore = score; bestAgent = agent; } } return { agent: bestAgent, score: bestScore }; } private selectPerformanceBasedAgent(candidates: Agent[]): { agent: Agent; score: number } { let bestAgent = candidates[0]; let bestScore = 0; for (const agent of candidates) { const score = this.calculatePerformanceScore(agent); if (score > bestScore) { bestScore = score; bestAgent = agent; } } return { agent: bestAgent, score: bestScore }; } private startAutoRebalancing(): void { if (this.rebalanceTimer) { clearInterval(this.rebalanceTimer); } this.rebalanceTimer = setInterval(async () => { const imbalance = this.detectWorkloadImbalance(); if (imbalance.isImbalanced && imbalance.severity !== 'low') { await this.rebalanceWorkload(); } }, this.config.rebalanceInterval); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/freshtechbro/vibe-coder-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server