claude-flow-multilang
Version:
Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture
1,614 lines (1,438 loc) • 53.6 kB
text/typescript
/**
* Comprehensive agent management system
*/
import { EventEmitter } from 'node:events';
import { spawn, ChildProcess } from 'node:child_process';
import type { ILogger } from '../core/logger.js';
import type { IEventBus } from '../core/event-bus.js';
import type {
AgentId,
AgentType,
AgentStatus,
AgentState,
AgentCapabilities,
AgentConfig,
AgentEnvironment,
AgentMetrics,
AgentError,
} from '../swarm/types.js';
import type { DistributedMemorySystem } from '../memory/distributed-memory.js';
import { generateId } from '../utils/helpers.js';
export interface AgentManagerConfig {
maxAgents: number;
defaultTimeout: number;
heartbeatInterval: number;
healthCheckInterval: number;
autoRestart: boolean;
resourceLimits: {
memory: number;
cpu: number;
disk: number;
};
agentDefaults: {
autonomyLevel: number;
learningEnabled: boolean;
adaptationEnabled: boolean;
};
environmentDefaults: {
runtime: 'deno' | 'node' | 'claude' | 'browser';
workingDirectory: string;
tempDirectory: string;
logDirectory: string;
};
}
export interface AgentTemplate {
name: string;
type: AgentType;
capabilities: AgentCapabilities;
config: Partial<AgentConfig>;
environment: Partial<AgentEnvironment>;
startupScript?: string;
dependencies?: string[];
}
export interface AgentCluster {
id: string;
name: string;
agents: AgentId[];
coordinator: AgentId;
strategy: 'round-robin' | 'load-based' | 'capability-based';
maxSize: number;
autoScale: boolean;
}
export interface AgentPool {
id: string;
name: string;
type: AgentType;
minSize: number;
maxSize: number;
currentSize: number;
availableAgents: AgentId[];
busyAgents: AgentId[];
template: AgentTemplate;
autoScale: boolean;
scaleUpThreshold: number;
scaleDownThreshold: number;
}
export interface ScalingPolicy {
name: string;
enabled: boolean;
rules: ScalingRule[];
cooldownPeriod: number;
maxScaleOperations: number;
}
export interface ScalingRule {
metric: string;
threshold: number;
comparison: 'gt' | 'lt' | 'eq' | 'gte' | 'lte';
action: 'scale-up' | 'scale-down';
amount: number;
conditions?: string[];
}
export interface AgentHealth {
agentId: string;
overall: number; // 0-1 health score
components: {
responsiveness: number;
performance: number;
reliability: number;
resourceUsage: number;
};
issues: HealthIssue[];
lastCheck: Date;
trend: 'improving' | 'stable' | 'degrading';
}
export interface HealthIssue {
type: 'performance' | 'reliability' | 'resource' | 'communication';
severity: 'low' | 'medium' | 'high' | 'critical';
message: string;
timestamp: Date;
resolved: boolean;
recommendedAction?: string;
}
/**
* Comprehensive agent lifecycle and resource management
*/
export class AgentManager extends EventEmitter {
private logger: ILogger;
private eventBus: IEventBus;
private memory: DistributedMemorySystem;
private config: AgentManagerConfig;
// Agent tracking
private agents = new Map<string, AgentState>();
private processes = new Map<string, ChildProcess>();
private templates = new Map<string, AgentTemplate>();
private clusters = new Map<string, AgentCluster>();
private pools = new Map<string, AgentPool>();
// Health monitoring
private healthChecks = new Map<string, AgentHealth>();
private healthInterval?: NodeJS.Timeout;
private heartbeatInterval?: NodeJS.Timeout;
// Scaling and policies
private scalingPolicies = new Map<string, ScalingPolicy>();
private scalingOperations = new Map<string, { timestamp: Date; type: string }>();
// Resource tracking
private resourceUsage = new Map<string, { cpu: number; memory: number; disk: number }>();
private performanceHistory = new Map<string, Array<{ timestamp: Date; metrics: AgentMetrics }>>();
constructor(
config: Partial<AgentManagerConfig>,
logger: ILogger,
eventBus: IEventBus,
memory: DistributedMemorySystem,
) {
super();
this.logger = logger;
this.eventBus = eventBus;
this.memory = memory;
this.config = {
maxAgents: 50,
defaultTimeout: 30000,
heartbeatInterval: 10000,
healthCheckInterval: 30000,
autoRestart: true,
resourceLimits: {
memory: 512 * 1024 * 1024, // 512MB
cpu: 1.0,
disk: 1024 * 1024 * 1024, // 1GB
},
agentDefaults: {
autonomyLevel: 0.7,
learningEnabled: true,
adaptationEnabled: true,
},
environmentDefaults: {
runtime: 'deno',
workingDirectory: './agents',
tempDirectory: './tmp',
logDirectory: './logs',
},
...config,
};
this.setupEventHandlers();
this.initializeDefaultTemplates();
}
private setupEventHandlers(): void {
this.eventBus.on('agent:heartbeat', (data: unknown) => {
const heartbeatData = data as { agentId: string; timestamp: Date; metrics?: AgentMetrics };
this.handleHeartbeat(heartbeatData);
});
this.eventBus.on('agent:error', (data: unknown) => {
const errorData = data as { agentId: string; error: AgentError };
this.handleAgentError(errorData);
});
this.eventBus.on('task:assigned', (data: unknown) => {
const taskData = data as { agentId: string };
this.updateAgentWorkload(taskData.agentId, 1);
});
this.eventBus.on('task:completed', (data: unknown) => {
const completedData = data as { agentId: string; metrics?: AgentMetrics };
this.updateAgentWorkload(completedData.agentId, -1);
if (completedData.metrics) {
this.updateAgentMetrics(completedData.agentId, completedData.metrics);
}
});
this.eventBus.on('resource:usage', (data: unknown) => {
const resourceData = data as {
agentId: string;
usage: { cpu: number; memory: number; disk: number };
};
this.updateResourceUsage(resourceData.agentId, resourceData.usage);
});
}
private initializeDefaultTemplates(): void {
// Research agent template
this.templates.set('researcher', {
name: 'Research Agent',
type: 'researcher',
capabilities: {
codeGeneration: false,
codeReview: false,
testing: false,
documentation: true,
research: true,
analysis: true,
webSearch: true,
apiIntegration: true,
fileSystem: true,
terminalAccess: false,
languages: [],
frameworks: [],
domains: ['research', 'analysis', 'information-gathering'],
tools: ['web-search', 'document-analysis', 'data-extraction'],
maxConcurrentTasks: 5,
maxMemoryUsage: 256 * 1024 * 1024,
maxExecutionTime: 600000,
reliability: 0.9,
speed: 0.8,
quality: 0.9,
},
config: {
autonomyLevel: 0.8,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 20,
maxConcurrentTasks: 5,
timeoutThreshold: 600000,
reportingInterval: 30000,
heartbeatInterval: 10000,
permissions: ['web-access', 'file-read'],
trustedAgents: [],
expertise: { research: 0.9, analysis: 0.8, documentation: 0.7 },
preferences: { verbose: true, detailed: true },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/researcher',
tempDirectory: './tmp/researcher',
logDirectory: './logs/researcher',
apiEndpoints: {},
credentials: {},
availableTools: ['web-search', 'document-reader', 'data-extractor'],
toolConfigs: {},
},
startupScript: './scripts/start-researcher.ts',
});
// Developer agent template
this.templates.set('coder', {
name: 'Developer Agent',
type: 'coder',
capabilities: {
codeGeneration: true,
codeReview: true,
testing: true,
documentation: true,
research: false,
analysis: true,
webSearch: false,
apiIntegration: true,
fileSystem: true,
terminalAccess: true,
languages: ['typescript', 'javascript', 'python', 'rust'],
frameworks: ['deno', 'node', 'react', 'svelte'],
domains: ['web-development', 'backend', 'api-design'],
tools: ['git', 'editor', 'debugger', 'linter', 'formatter'],
maxConcurrentTasks: 3,
maxMemoryUsage: 512 * 1024 * 1024,
maxExecutionTime: 1200000,
reliability: 0.95,
speed: 0.7,
quality: 0.95,
},
config: {
autonomyLevel: 0.6,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 10,
maxConcurrentTasks: 3,
timeoutThreshold: 1200000,
reportingInterval: 60000,
heartbeatInterval: 15000,
permissions: ['file-read', 'file-write', 'terminal-access', 'git-access'],
trustedAgents: [],
expertise: { coding: 0.95, testing: 0.8, debugging: 0.9 },
preferences: { codeStyle: 'functional', testFramework: 'deno-test' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/developer',
tempDirectory: './tmp/developer',
logDirectory: './logs/developer',
apiEndpoints: {},
credentials: {},
availableTools: ['git', 'deno', 'editor', 'debugger'],
toolConfigs: {},
},
startupScript: './scripts/start-developer.ts',
});
// Add more templates...
this.initializeSpecializedTemplates();
}
private initializeSpecializedTemplates(): void {
// Analyzer template
this.templates.set('analyst', {
name: 'Analyzer Agent',
type: 'analyst',
capabilities: {
codeGeneration: false,
codeReview: true,
testing: false,
documentation: true,
research: false,
analysis: true,
webSearch: false,
apiIntegration: true,
fileSystem: true,
terminalAccess: false,
languages: ['python', 'r', 'sql'],
frameworks: ['pandas', 'numpy', 'matplotlib'],
domains: ['data-analysis', 'statistics', 'visualization'],
tools: ['data-processor', 'chart-generator', 'statistical-analyzer'],
maxConcurrentTasks: 4,
maxMemoryUsage: 1024 * 1024 * 1024,
maxExecutionTime: 900000,
reliability: 0.9,
speed: 0.75,
quality: 0.9,
},
config: {
autonomyLevel: 0.7,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 15,
maxConcurrentTasks: 4,
timeoutThreshold: 900000,
reportingInterval: 45000,
heartbeatInterval: 12000,
permissions: ['file-read', 'data-access'],
trustedAgents: [],
expertise: { analysis: 0.95, visualization: 0.8, statistics: 0.85 },
preferences: { outputFormat: 'detailed', includeCharts: true },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/analyzer',
tempDirectory: './tmp/analyzer',
logDirectory: './logs/analyzer',
apiEndpoints: {},
credentials: {},
availableTools: ['data-processor', 'chart-gen', 'stats-calc'],
toolConfigs: {},
},
startupScript: './scripts/start-analyzer.ts',
});
// Requirements Engineer Agent Template
this.templates.set('requirements-engineer', {
name: 'Requirements Engineer Agent',
type: 'requirements-engineer',
capabilities: {
codeGeneration: false,
codeReview: false,
testing: false,
documentation: true,
research: true,
analysis: true,
webSearch: true,
apiIntegration: false,
fileSystem: true,
terminalAccess: false,
languages: [],
frameworks: [],
domains: ['requirements-engineering', 'user-stories', 'ears-notation'],
tools: ['document-writer', 'nlp-processor', 'web-search'],
maxConcurrentTasks: 2,
maxMemoryUsage: 256 * 1024 * 1024,
maxExecutionTime: 300000,
reliability: 0.95,
speed: 0.8,
quality: 0.95,
},
config: {
autonomyLevel: 0.8,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 10,
maxConcurrentTasks: 2,
timeoutThreshold: 300000,
reportingInterval: 30000,
heartbeatInterval: 10000,
permissions: ['file-read', 'file-write'],
trustedAgents: [],
expertise: { requirements: 0.95, documentation: 0.9, analysis: 0.8 },
preferences: { format: 'markdown', style: 'formal' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/requirements-engineer',
tempDirectory: './tmp/requirements-engineer',
logDirectory: './logs/requirements-engineer',
apiEndpoints: {},
credentials: {},
availableTools: ['document-writer', 'nlp-processor'],
toolConfigs: {},
},
startupScript: './scripts/start-requirements-engineer.ts',
});
// Design Architect Agent Template
this.templates.set('design-architect', {
name: 'Design Architect Agent',
type: 'design-architect',
capabilities: {
codeGeneration: false,
codeReview: true,
testing: false,
documentation: true,
research: true,
analysis: true,
webSearch: false,
apiIntegration: true,
fileSystem: true,
terminalAccess: false,
languages: ['typescript', 'javascript', 'python'],
frameworks: [],
domains: ['software-architecture', 'system-design', 'data-modeling'],
tools: ['diagram-generator', 'code-analyzer', 'api-designer'],
maxConcurrentTasks: 1,
maxMemoryUsage: 512 * 1024 * 1024,
maxExecutionTime: 600000,
reliability: 0.9,
speed: 0.7,
quality: 0.95,
},
config: {
autonomyLevel: 0.7,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 5,
maxConcurrentTasks: 1,
timeoutThreshold: 600000,
reportingInterval: 60000,
heartbeatInterval: 15000,
permissions: ['file-read', 'file-write'],
trustedAgents: [],
expertise: { architecture: 0.95, design: 0.9, modeling: 0.85 },
preferences: { diagramFormat: 'mermaid', detailLevel: 'high' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/design-architect',
tempDirectory: './tmp/design-architect',
logDirectory: './logs/design-architect',
apiEndpoints: {},
credentials: {},
availableTools: ['diagram-gen', 'code-analyzer'],
toolConfigs: {},
},
startupScript: './scripts/start-design-architect.ts',
});
// Task Planner Agent Template
this.templates.set('task-planner', {
name: 'Task Planner Agent',
type: 'task-planner',
capabilities: {
codeGeneration: false,
codeReview: false,
testing: false,
documentation: true,
research: false,
analysis: true,
webSearch: false,
apiIntegration: false,
fileSystem: true,
terminalAccess: false,
languages: [],
frameworks: [],
domains: ['project-management', 'task-breakdown', 'agile-planning'],
tools: ['task-scheduler', 'dependency-analyzer'],
maxConcurrentTasks: 3,
maxMemoryUsage: 256 * 1024 * 1024,
maxExecutionTime: 300000,
reliability: 0.95,
speed: 0.85,
quality: 0.9,
},
config: {
autonomyLevel: 0.8,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 20,
maxConcurrentTasks: 3,
timeoutThreshold: 300000,
reportingInterval: 30000,
heartbeatInterval: 10000,
permissions: ['file-read', 'file-write'],
trustedAgents: [],
expertise: { planning: 0.95, 'task-management': 0.9, optimization: 0.8 },
preferences: { outputFormat: 'markdown-checkbox', granularity: 'fine' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/task-planner',
tempDirectory: './tmp/task-planner',
logDirectory: './logs/task-planner',
apiEndpoints: {},
credentials: {},
availableTools: ['task-scheduler', 'dependency-analyzer'],
toolConfigs: {},
},
startupScript: './scripts/start-task-planner.ts',
});
// Developer Agent Template (already exists, but ensure it's aligned)
this.templates.set('developer', {
name: 'Developer Agent',
type: 'developer',
capabilities: {
codeGeneration: true,
codeReview: true,
testing: true,
documentation: true,
research: false,
analysis: true,
webSearch: false,
apiIntegration: true,
fileSystem: true,
terminalAccess: true,
languages: ['typescript', 'javascript', 'python', 'rust'],
frameworks: ['deno', 'node', 'react', 'svelte'],
domains: ['web-development', 'backend', 'api-design'],
tools: ['git', 'editor', 'debugger', 'linter', 'formatter'],
maxConcurrentTasks: 3,
maxMemoryUsage: 512 * 1024 * 1024,
maxExecutionTime: 1200000,
reliability: 0.95,
speed: 0.7,
quality: 0.95,
},
config: {
autonomyLevel: 0.6,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 10,
maxConcurrentTasks: 3,
timeoutThreshold: 1200000,
reportingInterval: 60000,
heartbeatInterval: 15000,
permissions: ['file-read', 'file-write', 'terminal-access', 'git-access'],
trustedAgents: [],
expertise: { coding: 0.95, testing: 0.8, debugging: 0.9 },
preferences: { codeStyle: 'functional', testFramework: 'deno-test' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/developer',
tempDirectory: './tmp/developer',
logDirectory: './logs/developer',
apiEndpoints: {},
credentials: {},
availableTools: ['git', 'deno', 'editor', 'debugger'],
toolConfigs: {},
},
startupScript: './scripts/start-developer.ts',
});
// System Architect Agent Template
this.templates.set('system-architect', {
name: 'System Architect Agent',
type: 'system-architect',
capabilities: {
codeGeneration: false,
codeReview: true,
testing: false,
documentation: true,
research: true,
analysis: true,
webSearch: false,
apiIntegration: true,
fileSystem: true,
terminalAccess: false,
languages: ['typescript', 'javascript', 'python'],
frameworks: ['microservices', 'distributed-systems'],
domains: ['system-architecture', 'scalability', 'performance', 'distributed-systems'],
tools: ['architecture-analyzer', 'system-modeler', 'performance-analyzer'],
maxConcurrentTasks: 1,
maxMemoryUsage: 512 * 1024 * 1024,
maxExecutionTime: 900000,
reliability: 0.95,
speed: 0.7,
quality: 0.95,
},
config: {
autonomyLevel: 0.8,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 5,
maxConcurrentTasks: 1,
timeoutThreshold: 900000,
reportingInterval: 60000,
heartbeatInterval: 15000,
permissions: ['file-read', 'file-write'],
trustedAgents: [],
expertise: { 'system-architecture': 0.95, scalability: 0.9, performance: 0.85 },
preferences: { scope: 'system-wide', focusArea: 'architecture' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/system-architect',
tempDirectory: './tmp/system-architect',
logDirectory: './logs/system-architect',
apiEndpoints: {},
credentials: {},
availableTools: ['architecture-analyzer', 'system-modeler'],
toolConfigs: {},
},
startupScript: './scripts/start-system-architect.ts',
});
// Tester Agent Template
this.templates.set('tester', {
name: 'Testing Agent',
type: 'tester',
capabilities: {
codeGeneration: false,
codeReview: true,
testing: true,
documentation: true,
research: false,
analysis: true,
webSearch: false,
apiIntegration: true,
fileSystem: true,
terminalAccess: true,
languages: ['typescript', 'javascript', 'python'],
frameworks: ['deno-test', 'jest', 'vitest', 'cypress'],
domains: ['testing', 'quality-assurance', 'test-automation'],
tools: ['test-runner', 'coverage-analyzer', 'test-generator'],
maxConcurrentTasks: 3,
maxMemoryUsage: 256 * 1024 * 1024,
maxExecutionTime: 600000,
reliability: 0.9,
speed: 0.8,
quality: 0.9,
},
config: {
autonomyLevel: 0.7,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 15,
maxConcurrentTasks: 3,
timeoutThreshold: 600000,
reportingInterval: 45000,
heartbeatInterval: 12000,
permissions: ['file-read', 'file-write', 'terminal-access'],
trustedAgents: [],
expertise: { testing: 0.9, 'quality-assurance': 0.85, automation: 0.8 },
preferences: { testFramework: 'deno-test', coverage: 'comprehensive' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/tester',
tempDirectory: './tmp/tester',
logDirectory: './logs/tester',
apiEndpoints: {},
credentials: {},
availableTools: ['test-runner', 'coverage-tool', 'test-gen'],
toolConfigs: {},
},
startupScript: './scripts/start-tester.ts',
});
// Code Reviewer Agent Template
this.templates.set('reviewer', {
name: 'Code Review Agent',
type: 'reviewer',
capabilities: {
codeGeneration: false,
codeReview: true,
testing: false,
documentation: true,
research: false,
analysis: true,
webSearch: false,
apiIntegration: false,
fileSystem: true,
terminalAccess: false,
languages: ['typescript', 'javascript', 'python', 'rust'],
frameworks: ['static-analysis', 'code-quality'],
domains: ['code-review', 'quality-assurance', 'best-practices'],
tools: ['static-analyzer', 'code-quality-checker', 'security-scanner'],
maxConcurrentTasks: 2,
maxMemoryUsage: 256 * 1024 * 1024,
maxExecutionTime: 450000,
reliability: 0.95,
speed: 0.8,
quality: 0.95,
},
config: {
autonomyLevel: 0.8,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 12,
maxConcurrentTasks: 2,
timeoutThreshold: 450000,
reportingInterval: 30000,
heartbeatInterval: 10000,
permissions: ['file-read'],
trustedAgents: [],
expertise: { 'code-review': 0.95, 'quality-assurance': 0.9, security: 0.8 },
preferences: { style: 'thorough', focus: 'quality-and-security' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/reviewer',
tempDirectory: './tmp/reviewer',
logDirectory: './logs/reviewer',
apiEndpoints: {},
credentials: {},
availableTools: ['static-analyzer', 'quality-checker'],
toolConfigs: {},
},
startupScript: './scripts/start-reviewer.ts',
});
// Steering Author Agent Template
this.templates.set('steering-author', {
name: 'Steering Author Agent',
type: 'steering-author',
capabilities: {
codeGeneration: false,
codeReview: true,
testing: false,
documentation: true,
research: true,
analysis: true,
webSearch: false,
apiIntegration: false,
fileSystem: true,
terminalAccess: false,
languages: [],
frameworks: [],
domains: ['documentation', 'knowledge-management', 'governance'],
tools: ['document-writer', 'content-analyzer'],
maxConcurrentTasks: 1,
maxMemoryUsage: 256 * 1024 * 1024,
maxExecutionTime: 300000,
reliability: 0.95,
speed: 0.7,
quality: 0.98,
},
config: {
autonomyLevel: 0.7,
learningEnabled: true,
adaptationEnabled: true,
maxTasksPerHour: 5,
maxConcurrentTasks: 1,
timeoutThreshold: 300000,
reportingInterval: 30000,
heartbeatInterval: 10000,
permissions: ['file-read', 'file-write'],
trustedAgents: [],
expertise: { documentation: 0.98, governance: 0.9, 'content-creation': 0.85 },
preferences: { style: 'concise', tone: 'formal' },
},
environment: {
runtime: 'deno',
version: '1.40.0',
workingDirectory: './agents/steering-author',
tempDirectory: './tmp/steering-author',
logDirectory: './logs/steering-author',
apiEndpoints: {},
credentials: {},
availableTools: ['document-writer', 'content-analyzer'],
toolConfigs: {},
},
startupScript: './scripts/start-steering-author.ts',
});
}
async initialize(): Promise<void> {
this.logger.info('Initializing agent manager', {
maxAgents: this.config.maxAgents,
templates: this.templates.size,
});
// Start health monitoring
this.startHealthMonitoring();
// Start heartbeat monitoring
this.startHeartbeatMonitoring();
// Initialize default scaling policies
this.initializeScalingPolicies();
this.emit('agent-manager:initialized');
}
async shutdown(): Promise<void> {
this.logger.info('Shutting down agent manager');
// Stop monitoring
if (this.healthInterval) clearInterval(this.healthInterval);
if (this.heartbeatInterval) clearInterval(this.heartbeatInterval);
// Gracefully shutdown all agents
const shutdownPromises = Array.from(this.agents.keys()).map((agentId) =>
this.stopAgent(agentId, 'shutdown'),
);
await Promise.all(shutdownPromises);
this.emit('agent-manager:shutdown');
}
// === AGENT LIFECYCLE ===
async createAgent(
templateName: string,
overrides: {
name?: string;
config?: Partial<AgentConfig>;
environment?: Partial<AgentEnvironment>;
} = {},
): Promise<string> {
if (this.agents.size >= this.config.maxAgents) {
throw new Error('Maximum agent limit reached');
}
const template = this.templates.get(templateName);
if (!template) {
throw new Error(`Template ${templateName} not found`);
}
const agentId = generateId('agent');
const swarmId = 'default'; // Could be parameterized
const agent: AgentState = {
id: { id: agentId, swarmId, type: template.type, instance: 1 },
name: overrides.name || `${template.name}-${agentId.slice(-8)}`,
type: template.type,
status: 'initializing',
capabilities: { ...template.capabilities },
metrics: this.createDefaultMetrics(),
workload: 0,
health: 1.0,
config: {
autonomyLevel: template.config.autonomyLevel ?? this.config.agentDefaults.autonomyLevel,
learningEnabled:
template.config.learningEnabled ?? this.config.agentDefaults.learningEnabled,
adaptationEnabled:
template.config.adaptationEnabled ?? this.config.agentDefaults.adaptationEnabled,
maxTasksPerHour: template.config.maxTasksPerHour ?? 10,
maxConcurrentTasks: template.config.maxConcurrentTasks ?? 3,
timeoutThreshold: template.config.timeoutThreshold ?? 300000,
reportingInterval: template.config.reportingInterval ?? 30000,
heartbeatInterval: template.config.heartbeatInterval ?? 10000,
permissions: template.config.permissions ?? [],
trustedAgents: template.config.trustedAgents ?? [],
expertise: template.config.expertise ?? {},
preferences: template.config.preferences ?? {},
...overrides.config,
},
environment: {
runtime: template.environment.runtime ?? this.config.environmentDefaults.runtime,
version: template.environment.version ?? '1.40.0',
workingDirectory:
template.environment.workingDirectory ?? this.config.environmentDefaults.workingDirectory,
tempDirectory:
template.environment.tempDirectory ?? this.config.environmentDefaults.tempDirectory,
logDirectory:
template.environment.logDirectory ?? this.config.environmentDefaults.logDirectory,
apiEndpoints: template.environment.apiEndpoints ?? {},
credentials: template.environment.credentials ?? {},
availableTools: template.environment.availableTools ?? [],
toolConfigs: template.environment.toolConfigs ?? {},
...overrides.environment,
},
endpoints: [],
lastHeartbeat: new Date(),
taskHistory: [],
errorHistory: [],
childAgents: [],
collaborators: [],
};
this.agents.set(agentId, agent);
this.healthChecks.set(agentId, this.createDefaultHealth(agentId));
this.logger.info('Created agent', {
agentId,
name: agent.name,
type: agent.type,
template: templateName,
});
this.emit('agent:created', { agent });
// Store in memory for persistence
await this.memory.store(`agent:${agentId}`, agent, {
type: 'agent-state',
tags: [agent.type, 'active'],
partition: 'state',
});
return agentId;
}
async startAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
throw new Error(`Agent ${agentId} not found`);
}
if (agent.status !== 'initializing' && agent.status !== 'offline') {
throw new Error(`Agent ${agentId} cannot be started from status ${agent.status}`);
}
try {
agent.status = 'initializing';
this.updateAgentStatus(agentId, 'initializing');
// Spawn agent process
const process = await this.spawnAgentProcess(agent);
this.processes.set(agentId, process);
// Wait for agent to signal ready
await this.waitForAgentReady(agentId, this.config.defaultTimeout);
agent.status = 'idle';
this.updateAgentStatus(agentId, 'idle');
this.logger.info('Started agent', { agentId, name: agent.name });
this.emit('agent:started', { agent });
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
agent.status = 'error';
this.addAgentError(agentId, {
timestamp: new Date(),
type: 'startup_failed',
message: errorMessage,
context: { agentId },
severity: 'critical',
resolved: false,
});
this.logger.error('Failed to start agent', { agentId, error });
throw error;
}
}
async stopAgent(agentId: string, reason: string = 'user_request'): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
throw new Error(`Agent ${agentId} not found`);
}
if (agent.status === 'offline' || agent.status === 'terminated') {
return; // Already stopped
}
try {
agent.status = 'terminating';
this.updateAgentStatus(agentId, 'terminating');
// Send graceful shutdown signal
const process = this.processes.get(agentId);
if (process && !process.killed) {
process.kill('SIGTERM');
// Force kill after timeout
setTimeout(() => {
if (process && !process.killed) {
process.kill('SIGKILL');
}
}, this.config.defaultTimeout);
}
// Wait for process to exit
await this.waitForProcessExit(agentId, this.config.defaultTimeout);
agent.status = 'terminated';
this.updateAgentStatus(agentId, 'terminated');
// Cleanup
this.processes.delete(agentId);
this.logger.info('Stopped agent', { agentId, reason });
this.emit('agent:stopped', { agent, reason });
} catch (error) {
this.logger.error('Failed to stop agent gracefully', { agentId, error });
// Force cleanup
this.processes.delete(agentId);
agent.status = 'terminated';
}
}
async restartAgent(agentId: string, reason: string = 'restart_requested'): Promise<void> {
this.logger.info('Restarting agent', { agentId, reason });
await this.stopAgent(agentId, `restart:${reason}`);
await this.startAgent(agentId);
this.emit('agent:restarted', { agentId, reason });
}
async removeAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) {
throw new Error(`Agent ${agentId} not found`);
}
// Stop agent if running
if (agent.status !== 'terminated' && agent.status !== 'offline') {
await this.stopAgent(agentId, 'removal');
}
// Remove from all data structures
this.agents.delete(agentId);
this.healthChecks.delete(agentId);
this.resourceUsage.delete(agentId);
this.performanceHistory.delete(agentId);
// Remove from pools and clusters
this.removeAgentFromPoolsAndClusters(agentId);
// Remove from memory
await this.memory.deleteEntry(`agent:${agentId}`);
this.logger.info('Removed agent', { agentId });
this.emit('agent:removed', { agentId });
}
// === AGENT POOLS ===
async createAgentPool(
name: string,
templateName: string,
config: {
minSize: number;
maxSize: number;
autoScale?: boolean;
scaleUpThreshold?: number;
scaleDownThreshold?: number;
},
): Promise<string> {
const template = this.templates.get(templateName);
if (!template) {
throw new Error(`Template ${templateName} not found`);
}
const poolId = generateId('pool');
const pool: AgentPool = {
id: poolId,
name,
type: template.type,
minSize: config.minSize,
maxSize: config.maxSize,
currentSize: 0,
availableAgents: [],
busyAgents: [],
template,
autoScale: config.autoScale || false,
scaleUpThreshold: config.scaleUpThreshold || 0.8,
scaleDownThreshold: config.scaleDownThreshold || 0.3,
};
this.pools.set(poolId, pool);
// Create minimum agents
for (let i = 0; i < config.minSize; i++) {
const agentId = await this.createAgent(templateName, {
name: `${name}-${i + 1}`,
});
await this.startAgent(agentId);
pool.availableAgents.push({
id: agentId,
swarmId: 'default',
type: template.type,
instance: i + 1,
});
pool.currentSize++;
}
this.logger.info('Created agent pool', { poolId, name, minSize: config.minSize });
this.emit('pool:created', { pool });
return poolId;
}
async scalePool(poolId: string, targetSize: number): Promise<void> {
const pool = this.pools.get(poolId);
if (!pool) {
throw new Error(`Pool ${poolId} not found`);
}
if (targetSize < pool.minSize || targetSize > pool.maxSize) {
throw new Error(
`Target size ${targetSize} outside pool limits [${pool.minSize}, ${pool.maxSize}]`,
);
}
const currentSize = pool.currentSize;
const delta = targetSize - currentSize;
if (delta > 0) {
// Scale up
for (let i = 0; i < delta; i++) {
const agentId = await this.createAgent(pool.template.name, {
name: `${pool.name}-${currentSize + i + 1}`,
});
await this.startAgent(agentId);
pool.availableAgents.push({
id: agentId,
swarmId: 'default',
type: pool.type,
instance: currentSize + i + 1,
});
}
} else if (delta < 0) {
// Scale down
const agentsToRemove = pool.availableAgents.slice(0, Math.abs(delta));
for (const agentId of agentsToRemove) {
await this.removeAgent(agentId.id);
pool.availableAgents = pool.availableAgents.filter((a) => a.id !== agentId.id);
}
}
pool.currentSize = targetSize;
this.logger.info('Scaled pool', { poolId, fromSize: currentSize, toSize: targetSize });
this.emit('pool:scaled', { pool, fromSize: currentSize, toSize: targetSize });
}
// === HEALTH MONITORING ===
private startHealthMonitoring(): void {
this.healthInterval = setInterval(() => {
this.performHealthChecks();
}, this.config.healthCheckInterval);
this.logger.info('Started health monitoring', {
interval: this.config.healthCheckInterval,
});
}
private startHeartbeatMonitoring(): void {
this.heartbeatInterval = setInterval(() => {
this.checkHeartbeats();
}, this.config.heartbeatInterval);
this.logger.info('Started heartbeat monitoring', {
interval: this.config.heartbeatInterval,
});
}
private async performHealthChecks(): Promise<void> {
const healthPromises = Array.from(this.agents.keys()).map((agentId) =>
this.checkAgentHealth(agentId),
);
await Promise.allSettled(healthPromises);
}
private async checkAgentHealth(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (!agent) return;
const health = this.healthChecks.get(agentId);
if (!health) return;
const now = new Date();
try {
// Check responsiveness
const responsiveness = await this.checkResponsiveness(agentId);
health.components.responsiveness = responsiveness;
// Check performance
const performance = this.calculatePerformanceScore(agentId);
health.components.performance = performance;
// Check reliability
const reliability = this.calculateReliabilityScore(agentId);
health.components.reliability = reliability;
// Check resource usage
const resourceScore = this.calculateResourceScore(agentId);
health.components.resourceUsage = resourceScore;
// Calculate overall health
const overall = (responsiveness + performance + reliability + resourceScore) / 4;
health.overall = overall;
health.lastCheck = now;
// Update agent health
agent.health = overall;
// Check for issues
this.detectHealthIssues(agentId, health);
// Auto-restart if critically unhealthy
if (overall < 0.3 && this.config.autoRestart) {
this.logger.warn('Agent critically unhealthy, restarting', { agentId, health: overall });
await this.restartAgent(agentId, 'health_critical');
}
} catch (error) {
this.logger.error('Health check failed', { agentId, error });
health.overall = 0;
health.lastCheck = now;
}
}
private async checkResponsiveness(agentId: string): Promise<number> {
// Send ping and measure response time
try {
// This would send an actual ping to the agent
// For now, simulate based on last heartbeat
const agent = this.agents.get(agentId);
if (!agent) return 0;
const timeSinceHeartbeat = Date.now() - agent.lastHeartbeat.getTime();
if (timeSinceHeartbeat > this.config.heartbeatInterval * 3) {
return 0; // Unresponsive
} else if (timeSinceHeartbeat > this.config.heartbeatInterval * 2) {
return 0.5; // Slow
} else {
return 1.0; // Responsive
}
} catch (error) {
return 0; // Failed to respond
}
}
private calculatePerformanceScore(agentId: string): number {
const history = this.performanceHistory.get(agentId) || [];
if (history.length === 0) return 1.0;
// Calculate average task completion time vs expected
const recent = history.slice(-10); // Last 10 entries
const avgTime =
recent.reduce((sum, entry) => sum + entry.metrics.averageExecutionTime, 0) / recent.length;
// Normalize based on expected performance (simplified)
const expectedTime = 60000; // 1 minute baseline
return Math.max(0, Math.min(1, expectedTime / avgTime));
}
private calculateReliabilityScore(agentId: string): number {
const agent = this.agents.get(agentId);
if (!agent) return 0;
const totalTasks = agent.metrics.tasksCompleted + agent.metrics.tasksFailed;
if (totalTasks === 0) return 1.0;
return agent.metrics.tasksCompleted / totalTasks;
}
private calculateResourceScore(agentId: string): number {
const usage = this.resourceUsage.get(agentId);
if (!usage) return 1.0;
const limits = this.config.resourceLimits;
const memoryScore = 1 - usage.memory / limits.memory;
const cpuScore = 1 - usage.cpu / limits.cpu;
const diskScore = 1 - usage.disk / limits.disk;
return Math.max(0, (memoryScore + cpuScore + diskScore) / 3);
}
private detectHealthIssues(agentId: string, health: AgentHealth): void {
const issues: HealthIssue[] = [];
if (health.components.responsiveness < 0.5) {
issues.push({
type: 'communication',
severity: health.components.responsiveness < 0.2 ? 'critical' : 'high',
message: 'Agent is not responding to heartbeats',
timestamp: new Date(),
resolved: false,
recommendedAction: 'Restart agent or check network connectivity',
});
}
if (health.components.performance < 0.6) {
issues.push({
type: 'performance',
severity: health.components.performance < 0.3 ? 'high' : 'medium',
message: 'Agent performance is below expected levels',
timestamp: new Date(),
resolved: false,
recommendedAction: 'Check resource allocation or agent configuration',
});
}
if (health.components.resourceUsage < 0.4) {
issues.push({
type: 'resource',
severity: health.components.resourceUsage < 0.2 ? 'critical' : 'high',
message: 'Agent resource usage is critically high',
timestamp: new Date(),
resolved: false,
recommendedAction: 'Increase resource limits or reduce workload',
});
}
health.issues = issues;
}
private checkHeartbeats(): void {
const now = Date.now();
const timeout = this.config.heartbeatInterval * 3;
for (const [agentId, agent] of Array.from(this.agents.entries())) {
const timeSinceHeartbeat = now - agent.lastHeartbeat.getTime();
if (
timeSinceHeartbeat > timeout &&
agent.status !== 'offline' &&
agent.status !== 'terminated'
) {
this.logger.warn('Agent heartbeat timeout', { agentId, timeSinceHeartbeat });
agent.status = 'error';
this.addAgentError(agentId, {
timestamp: new Date(),
type: 'heartbeat_timeout',
message: 'Agent failed to send heartbeat within timeout period',
context: { timeout, timeSinceHeartbeat },
severity: 'high',
resolved: false,
});
this.emit('agent:heartbeat-timeout', { agentId, timeSinceHeartbeat });
// Auto-restart if enabled
if (this.config.autoRestart) {
this.restartAgent(agentId, 'heartbeat_timeout').catch((error) => {
this.logger.error('Failed to auto-restart agent', { agentId, error });
});
}
}
}
}
// === UTILITY METHODS ===
private async spawnAgentProcess(agent: AgentState): Promise<ChildProcess> {
const env: NodeJS.ProcessEnv = {
...process.env,
AGENT_ID: agent.id.id,
AGENT_TYPE: agent.type,
AGENT_NAME: agent.name,
WORKING_DIR: agent.environment.workingDirectory,
LOG_DIR: agent.environment.logDirectory,
};
const args = [
'run',
'--allow-all',
agent.environment.availableTools[0] || './agents/generic-agent.ts',
'--config',
JSON.stringify(agent.config),
];
const childProcess = spawn(agent.environment.runtime, args, {
env,
stdio: ['pipe', 'pipe', 'pipe'],
cwd: agent.environment.workingDirectory,
});
// Handle process events
childProcess.on('exit', (code: number | null) => {
this.handleProcessExit(agent.id.id, code);
});
childProcess.on('error', (error: Error) => {
this.handleProcessError(agent.id.id, error);
});
return childProcess;
}
private async waitForAgentReady(agentId: string, timeout: number): Promise<void> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error(`Agent ${agentId} startup timeout`));
}, timeout);
const handler = (data: unknown) => {
const readyData = data as { agentId: string };
if (readyData.agentId === agentId) {
clearTimeout(timer);
this.eventBus.off('agent:ready', handler);
resolve();
}
};
this.eventBus.on('agent:ready', handler);
});
}
private async waitForProcessExit(agentId: string, timeout: number): Promise<void> {
return new Promise((resolve) => {
const process = this.processes.get(agentId);
if (!process || process.killed) {
resolve();
return;
}
const timer = setTimeout(() => {
resolve(); // Timeout, continue anyway
}, timeout);
process.on('exit', () => {
clearTimeout(timer);
resolve();
});
});
}
private handleProcessExit(agentId: string, code: number | null): void {
const agent = this.agents.get(agentId);
if (!agent) return;
this.logger.info('Agent process exited', { agentId, exitCode: code });
if (code !== 0 && code !== null) {
this.addAgentError(agentId, {
timestamp: new Date(),
type: 'process_exit',
message: `Agent process exited with code ${code}`,
context: { exitCode: code },
severity: 'high',
resolved: false,
});
}
agent.status = 'offline';
this.emit('agent:process-exit', { agentId, exitCode: code });
}
private handleProcessError(agentId: string, error: Error): void {
this.logger.error('Agent process error', { agentId, error });
this.addAgentError(agentId, {
timestamp: new Date(),
type: 'process_error',
message: error instanceof Error ? error.message : String(error),
context: { error: error.toString() },
severity: 'critical',
resolved: false,
});
this.emit('agent:process-error', { agentId, error });
}
private handleHeartbeat(data: {
agentId: string;
timestamp: Date;
metrics?: AgentMetrics;
}): void {
const agent = this.agents.get(data.agentId);
if (!agent) return;
agent.lastHeartbeat = data.timestamp;
if (data.metrics) {
this.updateAgentMetrics(data.agentId, data.metrics);
}
// Update health if agent was previously unresponsive
if (agent.status === 'error') {
agent.status = 'idle';
this.updateAgentStatus(data.agentId, 'idle');
}
}
private handleAgentError(data: { agentId: string; error: AgentError }): void {
this.addAgentError(data.agentId, data.error);
const agent = this.agents.get(data.agentId);
if (agent && data.error.severity === 'critical') {
agent.status = 'error';
this.updateAgentStatus(data.agentId, 'error');
}
}
private updateAgentStatus(agentId: string, status: AgentStatus): void {
const agent = this.agents.get(agentId);
if (!agent) return;
const oldStatus = agent.status;
agent.status = status;
this.emit('agent:status-changed', { agentId, from: oldStatus, to: status });
}
private updateAgentWorkload(agentId: string, delta: number): void {
const agent = this.agents.get(agentId);
if (!agent) return;
agent.workload = Math.max(0, agent.workload + delta);
}
private updateAgentMetrics(agentId: string, metrics: AgentMetrics): void {
const agent = this.agents.get(agentId);
if (!agent) return;
agent.metrics = { ...agent.metrics, ...metrics };
// Store performance history
const history = this.performanceHistory.get(agentId) || [];
history.push({ timestamp: new Date(), metrics: { ...metrics } });
// Keep only last 100 entries
if (history.length > 100) {
history.shift();
}
this.performanceHistory.set(agentId, history);
}
private updateResourceUsage(
agentId: string,
usage: { cpu: number; memory: number; disk: number },
): void {
this.resourceUsage.set(agentId, usage);
}
private addAgentError(agentId: string, error: AgentError): void {
const agent = this.agents.get(agentId);
if (!agent) return;
agent.errorHistory.push(error);
// Keep only last 50 errors
if (agent.errorHistory.length > 50) {
agent.errorHistory.shift();
}
}
private createDefaultMetrics(): AgentMetrics {
return {
tasksCompleted: 0,
tasksFailed: 0,
averageExecutionTime: 0,
successRate: 1.0,
cpuUsage: 0,
memoryUsage: 0,
diskUsage: 0,
networkUsage: 0,
codeQuality: 0.8,
testCoverage: 0,
bugRate: 0,
userSatisfaction: 0.8,
totalUptime: 0,
lastActivity: new Date(),
responseTime: 0,
};
}
private createDefaultHealth(agentId: string): AgentHealth {
return {
agentId,
overall: 1.0,
components: {
responsiveness: 1.0,
performance: 1.0,