UNPKG

@every-env/cli

Version:

Multi-agent orchestrator for AI-powered development workflows

142 lines 5.36 kB
import pLimit from 'p-limit'; import { EventEmitter } from 'events'; import { ClaudeAgent } from '../agents/claude-agent.js'; import { GenericAgent } from '../agents/generic-agent.js'; import { LiquidEngine } from '../templates/liquid-engine.js'; import { logger } from '../utils/logger.js'; export class AgentManager extends EventEmitter { maxConcurrent; limiter; results = []; runningAgents = new Map(); liquidEngine = new LiquidEngine(); constructor(maxConcurrent = 5) { super(); this.maxConcurrent = maxConcurrent; this.limiter = pLimit(maxConcurrent); } async runBatch(tasks) { logger.info(`Running ${tasks.length} agents (max ${this.maxConcurrent} concurrent)`); logger.debug('Tasks to run:', tasks.map(t => ({ agentId: t.agent.id, pattern: t.pattern.name, outputPath: t.outputPath }))); this.emit('batch:start', { total: tasks.length }); const promises = tasks.map(task => this.limiter(() => this.runAgent(task))); const results = await Promise.allSettled(promises); this.emit('batch:complete', { total: tasks.length, successful: results.filter(r => r.status === 'fulfilled').length, failed: results.filter(r => r.status === 'rejected').length, }); return this.results; } async runAgent(task) { const { agent, variables, outputPath } = task; logger.debug(`Starting agent ${agent.id}`, { pattern: task.pattern.name, outputPath, variableKeys: Object.keys(variables) }); this.emit('agent:start', { agentId: agent.id, outputPath }); try { // Create agent instance logger.debug(`Creating agent instance for ${agent.id}`, { command: agent.command, promptMode: agent.promptMode }); const agentInstance = this.createAgent(agent); this.runningAgents.set(agent.id, agentInstance); // Subscribe to agent events agentInstance.on('stdout', (data) => { this.emit('agent:output', { agentId: agent.id, type: 'stdout', data }); }); agentInstance.on('stderr', (data) => { this.emit('agent:output', { agentId: agent.id, type: 'stderr', data }); }); // Load and process prompt logger.debug(`Loading prompt for ${agent.id}`); const prompt = await this.loadPrompt(agent.promptFile, variables); // Run the agent logger.debug(`Running agent ${agent.id}`); const result = await agentInstance.run(prompt, outputPath); logger.debug(`Agent ${agent.id} completed`, { status: result.status, duration: result.duration }); this.results.push(result); this.runningAgents.delete(agent.id); this.emit('agent:complete', { agentId: agent.id, status: result.status, duration: result.duration, }); return result; } catch (error) { const errorResult = { agentId: agent.id, status: 'failed', duration: 0, stdout: [], stderr: [], error: error instanceof Error ? error.message : 'Unknown error', }; this.results.push(errorResult); this.runningAgents.delete(agent.id); this.emit('agent:failed', { agentId: agent.id, error: errorResult.error, }); return errorResult; } } createAgent(config) { // Factory pattern for different agent types const options = { workingDir: config.workingDir, env: config.env, stdio: config.stdio, timeout: config.timeout, }; logger.debug(`Creating agent with command: ${config.command}`, { agentId: config.id, options }); switch (config.command) { case 'claude': return new ClaudeAgent(config, options); default: // For other tools, use generic agent return new GenericAgent(config, options); } } async loadPrompt(promptFile, variables) { logger.debug(`Loading prompt template: ${promptFile}`); logger.debug(`Template variables:`, variables); const rendered = await this.liquidEngine.renderFile(promptFile, variables); logger.debug(`Rendered prompt length: ${rendered.length} characters`); return rendered; } async stopAll() { const stopPromises = Array.from(this.runningAgents.entries()).map(async ([agentId, agent]) => { logger.info(`Stopping agent: ${agentId}`); await agent.stop(); }); await Promise.all(stopPromises); this.runningAgents.clear(); } getResults() { return [...this.results]; } } //# sourceMappingURL=agent-manager.js.map