abyss-ai
Version:
Autonomous AI coding agent - enhanced OpenCode with autonomous capabilities
923 lines (755 loc) • 31.3 kB
text/typescript
import { Log } from "../../util/log"
import {
IAgent,
AgentTask,
AgentResult,
SharedContext,
AgentType,
ReasoningMode
} from "../types/agent"
import { QuestionGenerator, GeneratedQuestion } from "../question-generation/question-generator"
export class AgentCoordinator {
private log = Log.create({ service: "agent-coordinator" })
private agents: Map<string, IAgent> = new Map()
private taskQueue: AgentTask[] = []
private activeTasks: Map<string, AgentTask> = new Map()
private taskHistory: Map<string, AgentResult[]> = new Map()
private questionGenerator: QuestionGenerator
// Performance monitoring
private performanceMetrics = {
totalTasksProcessed: 0,
averageProcessingTime: 0,
successRate: 0,
agentUtilization: new Map<string, number>()
}
constructor() {
this.questionGenerator = new QuestionGenerator()
this.log.info("AgentCoordinator initialized with dynamic question generation")
}
// Initialize specialized agents
async initializeAgents(): Promise<void> {
this.log.info("Initializing specialized agents")
try {
// We'll register agents as they're implemented
// For now, just log that the coordinator is ready
this.log.info("Agent coordinator ready for agent registration")
} catch (error) {
this.log.error("Failed to initialize agents", { error })
throw error
}
}
// Register an agent with the coordinator
registerAgent(agent: IAgent): void {
this.log.info("Registering agent", {
agentId: agent.id,
agentType: agent.type,
capabilities: agent.capabilities
})
this.agents.set(agent.id, agent)
this.performanceMetrics.agentUtilization.set(agent.id, 0)
}
// Unregister an agent
async unregisterAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId)
if (agent) {
await agent.dispose()
this.agents.delete(agentId)
this.performanceMetrics.agentUtilization.delete(agentId)
this.log.info("Agent unregistered", { agentId })
}
}
// Get list of registered agents
getRegisteredAgents(): { id: string; type: AgentType; capabilities: string[] }[] {
return Array.from(this.agents.values()).map(agent => ({
id: agent.id,
type: agent.type,
capabilities: agent.capabilities
}))
}
// Main task processing method with enhanced question-driven approach
async processTask(task: AgentTask): Promise<AgentResult[]> {
this.log.info("Processing task with question-driven approach", {
taskId: task.id,
taskType: task.type,
reasoningModes: task.reasoningModes
})
const startTime = Date.now()
try {
// Add to active tasks
this.activeTasks.set(task.id, task)
// Generate specialized questions for this task (make-it-heavy approach)
const questionResult = await this.questionGenerator.generateQuestions(task)
this.log.debug("Generated specialized questions", {
taskId: task.id,
questionCount: questionResult.questions.length,
perspectives: questionResult.questions.map(q => q.perspective),
complexity: questionResult.context.complexity
})
// Process using question-driven agent selection
const results = await this.processWithQuestionDrivenApproach(task, questionResult.questions)
// Perform collaborative analysis with question context
await this.collaborativeAnalysisWithQuestions(results, task.context, questionResult.questions)
// Store results in history
this.taskHistory.set(task.id, results)
// Update performance metrics
this.updatePerformanceMetrics(results, Date.now() - startTime)
this.log.info("Question-driven task completed successfully", {
taskId: task.id,
resultCount: results.length,
questionCount: questionResult.questions.length,
processingTime: Date.now() - startTime
})
return results
} catch (error) {
this.log.error("Question-driven task processing failed", { taskId: task.id, error })
throw error
} finally {
// Remove from active tasks
this.activeTasks.delete(task.id)
}
}
// Legacy method for backward compatibility
async processTaskLegacy(task: AgentTask): Promise<AgentResult[]> {
this.log.info("Processing task (legacy mode)", {
taskId: task.id,
taskType: task.type,
reasoningModes: task.reasoningModes
})
const startTime = Date.now()
try {
// Add to active tasks
this.activeTasks.set(task.id, task)
// Select appropriate agents for the task
const selectedAgents = this.selectAgentsForTask(task)
if (selectedAgents.length === 0) {
throw new Error(`No suitable agents found for task type: ${task.type}`)
}
this.log.debug("Selected agents for task", {
taskId: task.id,
agentIds: selectedAgents.map(a => a.id)
})
// Process task with selected agents in parallel
const results = await this.processWithAgents(selectedAgents, task)
// Perform collaborative analysis
await this.collaborativeAnalysis(selectedAgents, results, task.context)
// Store results in history
this.taskHistory.set(task.id, results)
// Update performance metrics
this.updatePerformanceMetrics(results, Date.now() - startTime)
this.log.info("Task completed successfully", {
taskId: task.id,
resultCount: results.length,
processingTime: Date.now() - startTime
})
return results
} catch (error) {
this.log.error("Task processing failed", { taskId: task.id, error })
throw error
} finally {
// Remove from active tasks
this.activeTasks.delete(task.id)
}
}
// Question-driven processing method inspired by make-it-heavy
private async processWithQuestionDrivenApproach(
task: AgentTask,
questions: GeneratedQuestion[]
): Promise<AgentResult[]> {
this.log.debug("Processing with question-driven approach", {
taskId: task.id,
questionCount: questions.length
})
const results: AgentResult[] = []
// Process questions in parallel (like make-it-heavy's 4 agents approach)
const questionProcessingPromises = questions.map(async (question) => {
try {
// Find the best agent for this specific question
const agent = this.selectAgentForQuestion(question)
if (!agent) {
this.log.warn("No suitable agent found for question", {
questionId: question.id,
perspective: question.perspective
})
return null
}
this.log.debug("Processing question with agent", {
questionId: question.id,
agentId: agent.id,
perspective: question.perspective
})
// Create a specialized task for this question
const questionTask: AgentTask = {
...task,
id: `${task.id}-q-${question.id}`,
type: `question-driven-${task.type}`,
reasoningModes: [question.reasoningMode],
context: {
...task.context,
generatedQuestion: question.question,
questionPerspective: question.perspective,
originalTaskId: task.id
}
}
// Process the question with the selected agent
const result = await agent.process(questionTask)
// Enhance result with question context
const enhancedResult = {
...result,
metadata: {
...result.metadata,
questionId: question.id,
questionPerspective: question.perspective,
originalQuestion: question.question
}
}
return enhancedResult
} catch (error) {
this.log.error("Question processing failed", {
questionId: question.id,
error
})
return null
}
})
// Wait for all questions to be processed
const questionResults = await Promise.all(questionProcessingPromises)
// Filter out null results and add to main results
results.push(...questionResults.filter(result => result !== null) as AgentResult[])
this.log.info("Question-driven processing completed", {
taskId: task.id,
questionsProcessed: questions.length,
successfulResults: results.length
})
return results
}
// Select the best agent for a specific generated question
private selectAgentForQuestion(question: GeneratedQuestion): IAgent | null {
// First, try to find an agent of the target type
const targetTypeAgents = Array.from(this.agents.values())
.filter(agent => agent.type === question.targetAgentType)
if (targetTypeAgents.length > 0) {
// Find the best match based on capabilities and reasoning mode
const bestMatch = targetTypeAgents.find(agent =>
agent.reasoningModes.includes(question.reasoningMode) &&
this.agentHasCapabilities(agent, question.expectedCapabilities)
)
if (bestMatch) return bestMatch
// Fallback to any agent of target type with compatible reasoning mode
const reasoningMatch = targetTypeAgents.find(agent =>
agent.reasoningModes.includes(question.reasoningMode)
)
if (reasoningMatch) return reasoningMatch
}
// Fallback: find any agent with compatible reasoning mode and capabilities
const compatibleAgents = Array.from(this.agents.values())
.filter(agent =>
agent.reasoningModes.includes(question.reasoningMode) &&
this.agentHasCapabilities(agent, question.expectedCapabilities)
)
if (compatibleAgents.length > 0) {
// Return the agent with the highest capability match score
return compatibleAgents.reduce((best, current) => {
const bestScore = this.calculateCapabilityMatchScore(best, question.expectedCapabilities)
const currentScore = this.calculateCapabilityMatchScore(current, question.expectedCapabilities)
return currentScore > bestScore ? current : best
})
}
return null
}
// Check if agent has required capabilities
private agentHasCapabilities(agent: IAgent, requiredCapabilities: string[]): boolean {
return requiredCapabilities.some(cap =>
agent.capabilities.some(agentCap =>
agentCap.toLowerCase().includes(cap.toLowerCase())
)
)
}
// Calculate capability match score for agent selection
private calculateCapabilityMatchScore(agent: IAgent, requiredCapabilities: string[]): number {
let matchCount = 0
for (const requiredCap of requiredCapabilities) {
for (const agentCap of agent.capabilities) {
if (agentCap.toLowerCase().includes(requiredCap.toLowerCase())) {
matchCount++
break
}
}
}
return requiredCapabilities.length > 0 ? matchCount / requiredCapabilities.length : 0
}
// Enhanced collaborative analysis with question context
private async collaborativeAnalysisWithQuestions(
results: AgentResult[],
context: Record<string, any>,
questions: GeneratedQuestion[]
): Promise<void> {
this.log.debug("Performing collaborative analysis with question context", {
resultCount: results.length,
questionCount: questions.length
})
try {
// Create enhanced shared context with question information
const sharedContext: SharedContext = {
sessionId: context.sessionId || `session-${Date.now()}`,
projectPath: context.projectPath,
language: context.language,
frameworks: context.frameworks,
previousResults: results,
// Add question context
codeContext: context.codeContext
}
// Group results by question perspective for better synthesis
const resultsByPerspective = this.groupResultsByPerspective(results)
// Synthesize insights from each perspective
const perspectiveInsights = await this.synthesizePerspectiveInsights(
resultsByPerspective,
questions,
sharedContext
)
this.log.info("Question-driven collaborative analysis completed", {
perspectives: Object.keys(resultsByPerspective).length,
insights: perspectiveInsights.length
})
} catch (error) {
this.log.error("Question-driven collaborative analysis failed", { error })
}
}
// Group results by their question perspective
private groupResultsByPerspective(results: AgentResult[]): { [perspective: string]: AgentResult[] } {
const grouped: { [perspective: string]: AgentResult[] } = {}
results.forEach(result => {
const perspective = result.metadata?.questionPerspective || 'general'
if (!grouped[perspective]) {
grouped[perspective] = []
}
grouped[perspective].push(result)
})
return grouped
}
// Synthesize insights from different perspectives
private async synthesizePerspectiveInsights(
resultsByPerspective: { [perspective: string]: AgentResult[] },
questions: GeneratedQuestion[],
context: SharedContext
): Promise<any[]> {
const insights = []
for (const [perspective, perspectiveResults] of Object.entries(resultsByPerspective)) {
const relatedQuestion = questions.find(q => q.perspective === perspective)
const insight = {
perspective,
originalQuestion: relatedQuestion?.question,
resultCount: perspectiveResults.length,
averageConfidence: perspectiveResults.reduce((sum, r) => sum + r.confidence, 0) / perspectiveResults.length,
keyFindings: this.extractKeyFindings(perspectiveResults),
recommendations: this.generatePerspectiveRecommendations(perspectiveResults)
}
insights.push(insight)
}
return insights
}
// Extract key findings from perspective results
private extractKeyFindings(results: AgentResult[]): any[] {
const findings = []
results.forEach(result => {
if (result.result && typeof result.result === 'object') {
// Extract key information from the result
if (result.result.issues) {
findings.push(...result.result.issues)
}
if (result.result.insights) {
findings.push(...result.result.insights)
}
if (result.result.recommendations) {
findings.push(...result.result.recommendations)
}
}
})
return findings
}
// Generate recommendations based on perspective analysis
private generatePerspectiveRecommendations(results: AgentResult[]): string[] {
const recommendations = []
const highConfidenceResults = results.filter(r => r.confidence > 0.8)
const hasWarnings = results.some(r => r.warnings && r.warnings.length > 0)
const hasErrors = results.some(r => r.errors && r.errors.length > 0)
if (highConfidenceResults.length > 0) {
recommendations.push(`Found ${highConfidenceResults.length} high-confidence findings that should be prioritized`)
}
if (hasWarnings) {
recommendations.push('Review warnings identified in this perspective')
}
if (hasErrors) {
recommendations.push('Address errors found in this analysis perspective')
}
return recommendations
}
// Process multiple tasks in batch
async processBatch(tasks: AgentTask[]): Promise<Map<string, AgentResult[]>> {
this.log.info("Processing task batch", { taskCount: tasks.length })
const results = new Map<string, AgentResult[]>()
// Process tasks in parallel with concurrency limit
const concurrencyLimit = Math.min(5, this.agents.size)
const chunks = this.chunkArray(tasks, concurrencyLimit)
for (const chunk of chunks) {
const chunkPromises = chunk.map(async (task) => {
try {
const taskResults = await this.processTask(task)
results.set(task.id, taskResults)
} catch (error) {
this.log.error("Batch task failed", { taskId: task.id, error })
results.set(task.id, [])
}
})
await Promise.all(chunkPromises)
}
return results
}
// Select appropriate agents for a task
private selectAgentsForTask(task: AgentTask): IAgent[] {
const selectedAgents: IAgent[] = []
// Agent selection strategy based on task type and requirements
for (const agent of this.agents.values()) {
if (this.isAgentSuitableForTask(agent, task)) {
selectedAgents.push(agent)
}
}
// Sort agents by suitability score
selectedAgents.sort((a, b) => {
const scoreA = this.calculateAgentSuitabilityScore(a, task)
const scoreB = this.calculateAgentSuitabilityScore(b, task)
return scoreB - scoreA
})
// Limit to top agents to avoid overwhelming
return selectedAgents.slice(0, Math.min(3, selectedAgents.length))
}
private isAgentSuitableForTask(agent: IAgent, task: AgentTask): boolean {
// Check if agent supports any of the required reasoning modes
const hasCompatibleReasoningMode = task.reasoningModes.some(mode =>
agent.reasoningModes.includes(mode)
)
if (!hasCompatibleReasoningMode) return false
// Check if agent has relevant capabilities
const hasRelevantCapability = this.checkCapabilityMatch(agent, task)
return hasRelevantCapability
}
private checkCapabilityMatch(agent: IAgent, task: AgentTask): boolean {
// Simple capability matching - can be made more sophisticated
const taskTypeToCapabilities: { [key: string]: string[] } = {
'code-analysis': ['analysis', 'static-analysis', 'code-review'],
'error-detection': ['error-detection', 'debugging', 'validation'],
'refactoring': ['refactoring', 'code-improvement', 'optimization'],
'documentation': ['documentation', 'code-explanation', 'comments'],
'performance': ['performance', 'optimization', 'profiling'],
'security': ['security', 'vulnerability-detection', 'audit'],
'file-processing': ['file-handling', 'large-files', 'chunking']
}
const requiredCapabilities = taskTypeToCapabilities[task.type] || [task.type]
return requiredCapabilities.some(cap =>
agent.capabilities.some(agentCap =>
agentCap.toLowerCase().includes(cap.toLowerCase())
)
)
}
private calculateAgentSuitabilityScore(agent: IAgent, task: AgentTask): number {
let score = 0
// Score based on reasoning mode compatibility
const compatibleModes = task.reasoningModes.filter(mode =>
agent.reasoningModes.includes(mode)
)
score += compatibleModes.length * 0.3
// Score based on capability match
const capabilityScore = this.calculateCapabilityScore(agent, task)
score += capabilityScore * 0.4
// Score based on agent performance history
const utilizationScore = this.performanceMetrics.agentUtilization.get(agent.id) || 0
score += (1 - utilizationScore) * 0.2 // Prefer less utilized agents
// Score based on agent type
const typeScore = this.calculateTypeScore(agent, task)
score += typeScore * 0.1
return score
}
private calculateCapabilityScore(agent: IAgent, task: AgentTask): number {
const taskCapabilities = this.getTaskCapabilities(task)
let matchCount = 0
for (const taskCap of taskCapabilities) {
for (const agentCap of agent.capabilities) {
if (agentCap.toLowerCase().includes(taskCap.toLowerCase())) {
matchCount++
break
}
}
}
return taskCapabilities.length > 0 ? matchCount / taskCapabilities.length : 0
}
private getTaskCapabilities(task: AgentTask): string[] {
// Extract implied capabilities from task
const capabilities = []
if (task.type.includes('analysis')) capabilities.push('analysis')
if (task.type.includes('error')) capabilities.push('error-detection')
if (task.type.includes('performance')) capabilities.push('performance')
if (task.type.includes('security')) capabilities.push('security')
if (task.type.includes('refactor')) capabilities.push('refactoring')
return capabilities.length > 0 ? capabilities : [task.type]
}
private calculateTypeScore(agent: IAgent, task: AgentTask): number {
// Simple type matching score
const typeMatches: { [key: string]: AgentType[] } = {
'code-analysis': [AgentType.CODE_ANALYZER],
'error-detection': [AgentType.ERROR_DETECTOR],
'refactoring': [AgentType.REFACTORING_AGENT],
'documentation': [AgentType.DOCUMENTATION_AGENT],
'performance': [AgentType.PERFORMANCE_AGENT],
'security': [AgentType.SECURITY_AGENT],
'file-processing': [AgentType.FILE_PROCESSOR]
}
const suitableTypes = typeMatches[task.type] || []
return suitableTypes.includes(agent.type) ? 1 : 0.5
}
// Process task with selected agents
private async processWithAgents(agents: IAgent[], task: AgentTask): Promise<AgentResult[]> {
const results: AgentResult[] = []
// Create shared context
const sharedContext: SharedContext = {
sessionId: task.context.sessionId || `session-${Date.now()}`,
projectPath: task.context.projectPath,
language: task.context.language,
frameworks: task.context.frameworks,
codeContext: task.data?.toString(),
userPreferences: task.context.userPreferences,
previousResults: this.taskHistory.get(task.id) || []
}
// Process with each reasoning mode
for (const reasoningMode of task.reasoningModes) {
const compatibleAgents = agents.filter(agent =>
agent.reasoningModes.includes(reasoningMode)
)
if (compatibleAgents.length === 0) {
this.log.warn("No agents available for reasoning mode", { reasoningMode })
continue
}
// Use the most suitable agent for this reasoning mode
const selectedAgent = compatibleAgents[0]
try {
this.log.debug("Processing with agent", {
agentId: selectedAgent.id,
reasoningMode,
taskId: task.id
})
const result = await selectedAgent.process(task)
results.push(result)
// Update agent utilization
const currentUtilization = this.performanceMetrics.agentUtilization.get(selectedAgent.id) || 0
this.performanceMetrics.agentUtilization.set(selectedAgent.id, currentUtilization + 0.1)
} catch (error) {
this.log.error("Agent processing failed", {
agentId: selectedAgent.id,
reasoningMode,
error
})
// Continue with other agents/modes even if one fails
continue
}
}
return results
}
// Perform collaborative analysis between agents
private async collaborativeAnalysis(
agents: IAgent[],
results: AgentResult[],
context: Record<string, any>
): Promise<void> {
this.log.debug("Performing collaborative analysis", {
agentCount: agents.length,
resultCount: results.length
})
try {
const sharedContext: SharedContext = {
sessionId: context.sessionId || `session-${Date.now()}`,
projectPath: context.projectPath,
language: context.language,
frameworks: context.frameworks,
previousResults: results
}
// Allow agents to collaborate and update their understanding
const collaborationPromises = agents.map(agent =>
agent.collaborate(agents, sharedContext).catch(error => {
this.log.warn("Agent collaboration failed", { agentId: agent.id, error })
})
)
await Promise.all(collaborationPromises)
// Synthesize insights from collaboration
await this.synthesizeCollaborativeInsights(results, sharedContext)
} catch (error) {
this.log.error("Collaborative analysis failed", { error })
}
}
private async synthesizeCollaborativeInsights(
results: AgentResult[],
context: SharedContext
): Promise<void> {
// Analyze patterns across results
const insights = {
commonFindings: this.findCommonFindings(results),
conflictingResults: this.findConflictingResults(results),
confidenceDistribution: this.analyzeConfidenceDistribution(results),
recommendedActions: this.generateRecommendedActions(results)
}
this.log.info("Collaborative insights generated", {
commonFindings: insights.commonFindings.length,
conflicts: insights.conflictingResults.length,
avgConfidence: insights.confidenceDistribution.average
})
// Store insights for future reference
if (context.sessionId) {
// Could store in a session-based cache or database
this.log.debug("Insights stored for session", { sessionId: context.sessionId })
}
}
private findCommonFindings(results: AgentResult[]): any[] {
const findings: { [key: string]: number } = {}
results.forEach(result => {
if (result.result && typeof result.result === 'object') {
// Extract key findings from result
const keys = Object.keys(result.result)
keys.forEach(key => {
findings[key] = (findings[key] || 0) + 1
})
}
})
// Return findings that appear in multiple results
return Object.entries(findings)
.filter(([_, count]) => count > 1)
.map(([finding, count]) => ({ finding, occurrences: count }))
}
private findConflictingResults(results: AgentResult[]): any[] {
const conflicts = []
// Simple conflict detection based on confidence scores
const lowConfidenceResults = results.filter(r => r.confidence < 0.5)
const highConfidenceResults = results.filter(r => r.confidence > 0.8)
if (lowConfidenceResults.length > 0 && highConfidenceResults.length > 0) {
conflicts.push({
type: 'confidence_conflict',
description: 'Some agents have high confidence while others have low confidence',
lowConfidenceAgents: lowConfidenceResults.map(r => r.agentId),
highConfidenceAgents: highConfidenceResults.map(r => r.agentId)
})
}
return conflicts
}
private analyzeConfidenceDistribution(results: AgentResult[]): any {
const confidences = results.map(r => r.confidence)
return {
average: confidences.reduce((sum, c) => sum + c, 0) / confidences.length,
min: Math.min(...confidences),
max: Math.max(...confidences),
standardDeviation: this.calculateStandardDeviation(confidences)
}
}
private calculateStandardDeviation(values: number[]): number {
const avg = values.reduce((sum, val) => sum + val, 0) / values.length
const squaredDiffs = values.map(val => Math.pow(val - avg, 2))
const avgSquaredDiff = squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length
return Math.sqrt(avgSquaredDiff)
}
private generateRecommendedActions(results: AgentResult[]): string[] {
const actions = []
const avgConfidence = results.reduce((sum, r) => sum + r.confidence, 0) / results.length
if (avgConfidence < 0.6) {
actions.push('Consider running additional analysis with different reasoning modes')
}
const hasWarnings = results.some(r => r.warnings && r.warnings.length > 0)
if (hasWarnings) {
actions.push('Review and address warnings identified by agents')
}
const hasErrors = results.some(r => r.errors && r.errors.length > 0)
if (hasErrors) {
actions.push('Address errors identified during analysis')
}
return actions
}
// Utility methods
private chunkArray<T>(array: T[], chunkSize: number): T[][] {
const chunks = []
for (let i = 0; i < array.length; i += chunkSize) {
chunks.push(array.slice(i, i + chunkSize))
}
return chunks
}
private updatePerformanceMetrics(results: AgentResult[], processingTime: number): void {
this.performanceMetrics.totalTasksProcessed++
// Update average processing time
const currentAvg = this.performanceMetrics.averageProcessingTime
const taskCount = this.performanceMetrics.totalTasksProcessed
this.performanceMetrics.averageProcessingTime =
(currentAvg * (taskCount - 1) + processingTime) / taskCount
// Update success rate
const successfulResults = results.filter(r => !r.errors || r.errors.length === 0).length
const currentSuccessRate = this.performanceMetrics.successRate
this.performanceMetrics.successRate =
(currentSuccessRate * (taskCount - 1) + (successfulResults / Math.max(1, results.length))) / taskCount
}
// Queue management
addToQueue(task: AgentTask): void {
this.taskQueue.push(task)
this.log.debug("Task added to queue", { taskId: task.id, queueSize: this.taskQueue.length })
}
async processQueue(): Promise<void> {
if (this.taskQueue.length === 0) return
this.log.info("Processing task queue", { queueSize: this.taskQueue.length })
while (this.taskQueue.length > 0) {
const task = this.taskQueue.shift()!
try {
await this.processTask(task)
} catch (error) {
this.log.error("Queue task processing failed", { taskId: task.id, error })
}
}
}
// Status and monitoring
getStatus(): any {
return {
registeredAgents: this.agents.size,
activeTasks: this.activeTasks.size,
queuedTasks: this.taskQueue.length,
totalTasksProcessed: this.performanceMetrics.totalTasksProcessed,
averageProcessingTime: this.performanceMetrics.averageProcessingTime,
successRate: this.performanceMetrics.successRate,
agentUtilization: Object.fromEntries(this.performanceMetrics.agentUtilization)
}
}
async healthCheck(): Promise<boolean> {
try {
// Check if all agents are healthy
const healthChecks = Array.from(this.agents.values()).map(agent =>
agent.isHealthy().catch(() => false)
)
const results = await Promise.all(healthChecks)
const healthyCount = results.filter(Boolean).length
this.log.debug("Health check completed", {
totalAgents: this.agents.size,
healthyAgents: healthyCount
})
return healthyCount === this.agents.size
} catch (error) {
this.log.error("Health check failed", { error })
return false
}
}
// Cleanup
async dispose(): Promise<void> {
this.log.info("Disposing AgentCoordinator")
// Dispose all agents
const disposePromises = Array.from(this.agents.values()).map(agent =>
agent.dispose().catch(error =>
this.log.warn("Agent disposal failed", { agentId: agent.id, error })
)
)
await Promise.all(disposePromises)
// Clear collections
this.agents.clear()
this.taskQueue.length = 0
this.activeTasks.clear()
this.taskHistory.clear()
this.log.info("AgentCoordinator disposed")
}
}