/** * Delegation Engine * * Makes intelligent decisions about when and how to delegate requests. * Routes requests to appropriate agents based on classification and pool state. */ import { DelegationDecision, DelegationStrategy, DelegationContext, DelegationResult, RequestClassification, ClassifiableRequest, AgentType, PoolAgent } from '../core/types'; import { RequestClassifier } from '../core/request-classifier'; import { AgentPoolManager } from '../pool/agent-pool-manager'; // ============================================================================ // Delegation Configuration // ============================================================================ interface DelegationConfig { // Main agent load thresholds mainAgentBusyThreshold: number; // 0-1, above this = delegate mainAgentQueueThreshold: number; // max queue before delegation // Delegation preferences preferDelegation: boolean; maxDelegationTime: number; // ms before fallback parallelThreshold: number; // complexity score for parallel delegation // Fallback settings fallbackToMain: boolean; escalationEnabled: boolean; escalationThreshold: number; // confidence below this triggers escalation } const DEFAULT_CONFIG: DelegationConfig = { mainAgentBusyThreshold: 0.7, mainAgentQueueThreshold: 5, preferDelegation: true, maxDelegationTime: 60000, parallelThreshold: 0.6, fallbackToMain: true, escalationEnabled: true, escalationThreshold: 0.7 }; // ============================================================================ // Main Agent State (simulated) // ============================================================================ interface MainAgentState { load: number; // 0-1 queueLength: number; currentTask?: string; averageResponseTime: number; } // ============================================================================ // Delegation Engine Class // ============================================================================ export class DelegationEngine { private config: DelegationConfig; private classifier: RequestClassifier; private poolManager: AgentPoolManager; private mainAgentState: MainAgentState; private activeDelegations: Map = new Map(); private delegationHistory: DelegationContext[] = []; private maxHistorySize: number = 1000; constructor( classifier: RequestClassifier, poolManager: AgentPoolManager, config: Partial = {} ) { this.config = { ...DEFAULT_CONFIG, ...config }; this.classifier = classifier; this.poolManager = poolManager; this.mainAgentState = { load: 0, queueLength: 0, averageResponseTime: 5000 }; } // ============================================================================ // Delegation Decision // ============================================================================ /** * Make a delegation decision for a request */ async makeDecision( request: ClassifiableRequest, classification?: RequestClassification ): Promise { // Classify if not provided if (!classification) { classification = await this.classifier.classify(request); } // Get current state const poolStats = this.poolManager.getPoolStats(); // Decision tree const decision = this.evaluateDelegation(request, classification, poolStats); return decision; } private evaluateDelegation( request: ClassifiableRequest, classification: RequestClassification, poolStats: ReturnType ): DelegationDecision { const mainAgentLoad = this.mainAgentState.load; const mainAgentQueue = this.mainAgentState.queueLength; // Check if can delegate if (!classification.canDelegate) { return this.noDelegation(classification, 'Request cannot be delegated'); } // Check if pool has available agents if (poolStats.idleCount === 0 && poolStats.coolingDownCount === 0) { if (this.config.fallbackToMain) { return this.noDelegation(classification, 'No agents available in pool'); } return this.queueDelegation(classification, 'Waiting for agent availability'); } // Main agent is lightly loaded - process directly if (mainAgentLoad < 0.5 && mainAgentQueue < 3) { if (!this.config.preferDelegation || classification.complexity === 'complex') { return this.noDelegation(classification, 'Main agent available'); } } // Determine delegation strategy const strategy = this.determineStrategy(classification, poolStats); const targetAgents = this.selectAgents(classification, strategy, poolStats); const estimatedCompletion = this.estimateCompletion(classification, strategy, poolStats); return { shouldDelegate: true, strategy, targetAgents, estimatedCompletion, reason: this.getDelegationReason(mainAgentLoad, poolStats), fallbackPlan: this.config.fallbackToMain ? 'main-agent' : 'queue', requiresCallback: strategy === 'hierarchical' || classification.complexity === 'complex' }; } private determineStrategy( classification: RequestClassification, poolStats: ReturnType ): DelegationStrategy { // Complex requests with high confidence -> hierarchical if (classification.complexity === 'complex' && classification.confidence > 0.8) { return 'hierarchical'; } // Multiple files or high complexity score -> parallel if (classification.contextRequirements.files > 2 || classification.score > this.config.parallelThreshold) { if (poolStats.idleCount >= 2) { return 'parallel'; } } // Quick requests -> full delegation if (classification.complexity === 'quick') { return 'full'; } // Streaming requests with progress -> hybrid if (classification.complexity === 'streaming') { return 'hybrid'; } // Default to full delegation return 'full'; } private selectAgents( classification: RequestClassification, strategy: DelegationStrategy, poolStats: ReturnType ): AgentType[] { const primary = classification.recommendedAgent; const agents: AgentType[] = []; switch (strategy) { case 'full': agents.push(primary); break; case 'parallel': // Add primary agent and complementary agents agents.push(primary); if (classification.requiredCapabilities.includes('security')) { agents.push('reviewer'); } if (classification.contextRequirements.files > 3) { agents.push('explorer'); } break; case 'hierarchical': // Planner oversees execution agents.push('planner', primary); if (classification.requiredCapabilities.includes('security')) { agents.push('reviewer'); } break; case 'hybrid': agents.push(primary); // Add support agents if available if (poolStats.idleCount > 2) { agents.push('fast-responder'); } break; } return agents; } private estimateCompletion( classification: RequestClassification, strategy: DelegationStrategy, poolStats: ReturnType ): number { let baseTime = classification.estimatedTime; // Adjust for strategy switch (strategy) { case 'parallel': // Parallel is faster but has coordination overhead baseTime = baseTime * 0.6 + 500; break; case 'hierarchical': // Hierarchical has communication overhead baseTime = baseTime * 1.3; break; case 'hybrid': baseTime = baseTime * 0.8; break; } // Adjust for pool availability const availabilityFactor = 1 + (1 - poolStats.availablePercentage) * 0.5; return Math.round(baseTime * availabilityFactor); } private getDelegationReason( mainAgentLoad: number, poolStats: ReturnType ): string { if (mainAgentLoad > 0.8) { return 'Main agent at high capacity'; } if (poolStats.busyPercentage > 0.7) { return 'Optimal for parallel processing'; } return 'Fast delegation path available'; } private noDelegation(classification: RequestClassification, reason: string): DelegationDecision { return { shouldDelegate: false, strategy: 'full', targetAgents: [classification.recommendedAgent], estimatedCompletion: classification.estimatedTime, reason, requiresCallback: false }; } private queueDelegation(classification: RequestClassification, reason: string): DelegationDecision { return { shouldDelegate: true, strategy: 'full', targetAgents: [classification.recommendedAgent], estimatedCompletion: classification.estimatedTime + 10000, // Add queue time reason, fallbackPlan: 'queue', requiresCallback: true }; } // ============================================================================ // Delegation Execution // ============================================================================ /** * Execute a delegation */ async executeDelegation( request: ClassifiableRequest, decision: DelegationDecision, executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise ): Promise { const requestId = this.generateRequestId(); const startTime = Date.now(); // Create delegation context const context: DelegationContext = { requestId, originalRequest: request, classification: await this.classifier.classify(request), delegationDecision: decision, assignedAgents: [], status: 'in-progress', startTime: new Date() }; this.activeDelegations.set(requestId, context); try { let result: any; const agentsUsed: string[] = []; switch (decision.strategy) { case 'full': result = await this.executeFull(request, decision, executor, agentsUsed); break; case 'parallel': result = await this.executeParallel(request, decision, executor, agentsUsed); break; case 'hierarchical': result = await this.executeHierarchical(request, decision, executor, agentsUsed); break; case 'hybrid': result = await this.executeHybrid(request, decision, executor, agentsUsed); break; } const delegationResult: DelegationResult = { success: true, output: result.output || result, confidence: result.confidence || 0.9, tokensUsed: result.tokensUsed || 0, duration: Date.now() - startTime, agentsUsed, needsReview: context.classification.complexity === 'complex' }; context.status = 'completed'; context.endTime = new Date(); context.result = delegationResult; this.addToHistory(context); return delegationResult; } catch (error) { const delegationResult: DelegationResult = { success: false, output: `Delegation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, confidence: 0, tokensUsed: 0, duration: Date.now() - startTime, agentsUsed: [], needsReview: true, escalationReason: error instanceof Error ? error.message : 'Unknown error' }; context.status = 'failed'; context.endTime = new Date(); context.result = delegationResult; this.addToHistory(context); return delegationResult; } finally { this.activeDelegations.delete(requestId); } } private async executeFull( request: ClassifiableRequest, decision: DelegationDecision, executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, agentsUsed: string[] ): Promise { const agentType = decision.targetAgents[0]; const agent = this.poolManager.acquireAgent(agentType, undefined, request.content.slice(0, 50)); if (!agent) { throw new Error(`No ${agentType} agent available`); } agentsUsed.push(agent.id); try { const result = await executor(agentType, agent.id, request); return result; } finally { this.poolManager.releaseAgent(agent.id); } } private async executeParallel( request: ClassifiableRequest, decision: DelegationDecision, executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, agentsUsed: string[] ): Promise { const agents = this.poolManager.acquireAgents(decision.targetAgents, request.content.slice(0, 50)); if (agents.length === 0) { throw new Error('No agents available for parallel execution'); } agents.forEach(a => agentsUsed.push(a.id)); // Split request for parallel execution const subRequests = this.splitRequest(request, agents.length); try { const results = await Promise.all( agents.map((agent, i) => executor(agent.type, agent.id, subRequests[i] || request)) ); // Merge results return this.mergeResults(results); } finally { agents.forEach(a => this.poolManager.releaseAgent(a.id)); } } private async executeHierarchical( request: ClassifiableRequest, decision: DelegationDecision, executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, agentsUsed: string[] ): Promise { // First, planner creates plan const plannerAgent = this.poolManager.acquireAgent('planner', undefined, request.content.slice(0, 50)); if (!plannerAgent) { return this.executeFull(request, decision, executor, agentsUsed); } agentsUsed.push(plannerAgent.id); try { // Get plan from planner const plan = await executor('planner', plannerAgent.id, { ...request, content: `Create execution plan for: ${request.content}` }); // Execute plan steps with primary agent const primaryType = decision.targetAgents[decision.targetAgents.indexOf('planner') + 1] || 'coder'; const primaryAgent = this.poolManager.acquireAgent(primaryType); if (primaryAgent) { agentsUsed.push(primaryAgent.id); const result = await executor(primaryType, primaryAgent.id, { ...request, content: `Execute this plan:\n${plan.output}\n\nOriginal request: ${request.content}` }); this.poolManager.releaseAgent(primaryAgent.id); return result; } return plan; } finally { this.poolManager.releaseAgent(plannerAgent.id); } } private async executeHybrid( request: ClassifiableRequest, decision: DelegationDecision, executor: (agentType: AgentType, agentId: string, request: ClassifiableRequest) => Promise, agentsUsed: string[] ): Promise { // Start with fast response, then enhance const fastAgent = this.poolManager.acquireAgent('fast-responder'); if (fastAgent) { agentsUsed.push(fastAgent.id); try { // Quick initial response const quickResult = await executor('fast-responder', fastAgent.id, request); // Enhanced processing with primary agent const primaryType = decision.targetAgents[0]; const primaryAgent = this.poolManager.acquireAgent(primaryType); if (primaryAgent) { agentsUsed.push(primaryAgent.id); const enhancedResult = await executor(primaryType, primaryAgent.id, { ...request, content: `Enhance this response:\n${quickResult.output}\n\nOriginal: ${request.content}` }); this.poolManager.releaseAgent(primaryAgent.id); return enhancedResult; } return quickResult; } finally { this.poolManager.releaseAgent(fastAgent.id); } } // Fallback to full delegation return this.executeFull(request, decision, executor, agentsUsed); } private splitRequest(request: ClassifiableRequest, parts: number): ClassifiableRequest[] { // Simple splitting by files if available if (request.files && request.files.length >= parts) { const filesPerPart = Math.ceil(request.files.length / parts); return Array.from({ length: parts }, (_, i) => ({ ...request, files: request.files?.slice(i * filesPerPart, (i + 1) * filesPerPart) })); } // Otherwise return the same request for each agent return Array(parts).fill(request); } private mergeResults(results: any[]): any { if (results.length === 1) return results[0]; return { output: results.map(r => r.output || r).join('\n\n---\n\n'), confidence: results.reduce((sum, r) => sum + (r.confidence || 0.9), 0) / results.length, tokensUsed: results.reduce((sum, r) => sum + (r.tokensUsed || 0), 0) }; } // ============================================================================ // Main Agent State Management // ============================================================================ /** * Update main agent state (called by integration layer) */ updateMainAgentState(state: Partial): void { this.mainAgentState = { ...this.mainAgentState, ...state }; } /** * Get current main agent state */ getMainAgentState(): MainAgentState { return { ...this.mainAgentState }; } // ============================================================================ // History and Monitoring // ============================================================================ private addToHistory(context: DelegationContext): void { this.delegationHistory.push(context); if (this.delegationHistory.length > this.maxHistorySize) { this.delegationHistory.shift(); } } getActiveDelegations(): DelegationContext[] { return Array.from(this.activeDelegations.values()); } getDelegationHistory(limit: number = 100): DelegationContext[] { return this.delegationHistory.slice(-limit); } getStats(): { totalDelegations: number; successfulDelegations: number; failedDelegations: number; averageDuration: number; strategyUsage: Record; } { const history = this.delegationHistory; const stats = { totalDelegations: history.length, successfulDelegations: history.filter(h => h.status === 'completed').length, failedDelegations: history.filter(h => h.status === 'failed').length, averageDuration: 0, strategyUsage: { full: 0, parallel: 0, hierarchical: 0, hybrid: 0 } as Record }; if (history.length > 0) { const durations = history .filter(h => h.result) .map(h => h.result!.duration); stats.averageDuration = durations.reduce((a, b) => a + b, 0) / durations.length || 0; history.forEach(h => { stats.strategyUsage[h.delegationDecision.strategy]++; }); } return stats; } private generateRequestId(): string { return `del-${Date.now()}-${Math.random().toString(36).substr(2, 6)}`; } } // ============================================================================ // Factory Function // ============================================================================ export function createDelegationEngine( classifier: RequestClassifier, poolManager: AgentPoolManager, config?: Partial ): DelegationEngine { return new DelegationEngine(classifier, poolManager, config); } // ============================================================================ // Export // ============================================================================ export default DelegationEngine;