UNPKG

stellar-cyber-mcp-agents

Version:

Model Context Protocol (MCP) server for Stellar Cyber security operations with specialized multi-agent analysis capabilities

337 lines 12.3 kB
import { EventEmitter } from 'eventemitter3'; import { AgentState, AgentHealth, AgentEventType, RequestPriority } from '../types/agent.js'; export class BaseAgent extends EventEmitter { metadata; registry; channel; logger; metrics; state = AgentState.INITIALIZING; health = AgentHealth.HEALTHY; startTime = new Date(); activeRequests = 0; totalRequests = 0; errors = 0; heartbeatInterval; healthCheckInterval; constructor(metadata, registry, channel, logger, metrics) { super(); this.metadata = metadata; this.registry = registry; this.channel = channel; this.logger = logger; this.metrics = metrics; } async initialize() { this.logger.info('Initializing agent', { agentId: this.metadata.id }); try { this.setState(AgentState.INITIALIZING); // Register with registry await this.registry.registerAgent(this.metadata); // Set up request handling this.channel.subscribeToRequests(this.metadata.id, this.handleIncomingRequest.bind(this)); // Set up event handling this.channel.subscribeToEvents(this.handleAgentEvent.bind(this)); // Perform agent-specific initialization await this.onInitialize(); this.logger.info('Agent initialized successfully', { agentId: this.metadata.id }); } catch (error) { this.logger.error('Failed to initialize agent', { agentId: this.metadata.id, error }); this.setState(AgentState.ERROR); this.setHealth(AgentHealth.CRITICAL); throw error; } } async start() { this.logger.info('Starting agent', { agentId: this.metadata.id }); try { if (this.state !== AgentState.INITIALIZING) { throw new Error(`Cannot start agent in state ${this.state}`); } this.setState(AgentState.READY); this.startTime = new Date(); // Start heartbeat this.startHeartbeat(); // Start health checks this.startHealthChecks(); // Perform agent-specific startup await this.onStart(); this.emitEvent(AgentEventType.AGENT_STARTED, { metadata: this.metadata, startTime: this.startTime }); this.logger.info('Agent started successfully', { agentId: this.metadata.id }); } catch (error) { this.logger.error('Failed to start agent', { agentId: this.metadata.id, error }); this.setState(AgentState.ERROR); this.setHealth(AgentHealth.CRITICAL); throw error; } } async stop() { this.logger.info('Stopping agent', { agentId: this.metadata.id }); try { this.setState(AgentState.STOPPING); // Stop intervals if (this.heartbeatInterval) { clearInterval(this.heartbeatInterval); } if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval); } // Wait for active requests to complete (with timeout) const timeout = 30000; // 30 seconds const start = Date.now(); while (this.activeRequests > 0 && (Date.now() - start) < timeout) { await new Promise(resolve => setTimeout(resolve, 100)); } // Perform agent-specific cleanup await this.onStop(); // Unsubscribe from channels this.channel.unsubscribeFromRequests(this.metadata.id); this.setState(AgentState.STOPPED); this.emitEvent(AgentEventType.AGENT_STOPPED, { metadata: this.metadata, uptime: Date.now() - this.startTime.getTime(), totalRequests: this.totalRequests, errors: this.errors }); this.logger.info('Agent stopped successfully', { agentId: this.metadata.id }); } catch (error) { this.logger.error('Failed to stop agent', { agentId: this.metadata.id, error }); this.setState(AgentState.ERROR); throw error; } } async destroy() { this.logger.info('Destroying agent', { agentId: this.metadata.id }); try { if (this.state !== AgentState.STOPPED) { await this.stop(); } // Unregister from registry await this.registry.unregisterAgent(this.metadata.id); // Perform agent-specific cleanup await this.onDestroy(); // Remove all listeners this.removeAllListeners(); this.logger.info('Agent destroyed successfully', { agentId: this.metadata.id }); } catch (error) { this.logger.error('Failed to destroy agent', { agentId: this.metadata.id, error }); throw error; } } async healthCheck() { try { // Perform agent-specific health check const health = await this.onHealthCheck(); this.setHealth(health); return health; } catch (error) { this.logger.error('Health check failed', { agentId: this.metadata.id, error }); this.setHealth(AgentHealth.CRITICAL); return AgentHealth.CRITICAL; } } // Getters getId() { return this.metadata.id; } getMetadata() { return this.metadata; } getState() { return this.state; } getHealth() { return this.health; } getUptime() { return Date.now() - this.startTime.getTime(); } getStatus() { return { id: this.metadata.id, state: this.state, health: this.health, lastHeartbeat: new Date(), uptime: this.getUptime(), activeRequests: this.activeRequests, totalRequests: this.totalRequests, errors: this.errors }; } // Protected utility methods setState(state) { const previousState = this.state; this.state = state; this.logger.debug('Agent state changed', { agentId: this.metadata.id, previousState, currentState: state }); this.emit('state_changed', { previousState, currentState: state }); this.metrics.recordGauge('agent.state', this.getStateValue(state)); } setHealth(health) { const previousHealth = this.health; this.health = health; this.logger.debug('Agent health changed', { agentId: this.metadata.id, previousHealth, currentHealth: health }); this.emit('health_changed', { previousHealth, currentHealth: health }); this.metrics.recordGauge('agent.health', this.getHealthValue(health)); } emitEvent(type, data) { const event = { id: crypto.randomUUID(), sourceAgentId: this.metadata.id, type, data, timestamp: new Date() }; this.channel.broadcastEvent(event).catch(error => { this.logger.error('Failed to broadcast event', { event, error }); }); } async sendRequest(targetAgentId, capability, payload, priority = RequestPriority.MEDIUM, timeout = 30000) { const request = { id: crypto.randomUUID(), sourceAgentId: this.metadata.id, targetAgentId, capability, payload, priority, timeout, timestamp: new Date() }; this.metrics.incrementCounter('agent.requests.sent'); try { const response = await this.channel.sendRequest(request); if (response.success) { this.metrics.incrementCounter('agent.requests.success'); } else { this.metrics.incrementCounter('agent.requests.failed'); } return response; } catch (error) { this.metrics.incrementCounter('agent.requests.error'); throw error; } } async handleIncomingRequest(request) { const startTime = Date.now(); this.activeRequests++; this.totalRequests++; this.metrics.incrementCounter('agent.requests.received'); this.metrics.recordGauge('agent.requests.active', this.activeRequests); try { this.logger.debug('Handling incoming request', { agentId: this.metadata.id, requestId: request.id, capability: request.capability, sourceAgent: request.sourceAgentId }); const context = { requestId: request.id, sourceAgentId: request.sourceAgentId, correlationId: request.correlationId, timestamp: request.timestamp, metadata: {} }; const result = await this.handleRequest(request, context); const response = { requestId: request.id, sourceAgentId: this.metadata.id, success: true, result, timestamp: new Date(), processingTime: Date.now() - startTime }; this.metrics.recordTimer('agent.request.processing_time', response.processingTime); this.metrics.incrementCounter('agent.requests.processed'); return response; } catch (error) { this.errors++; this.metrics.incrementCounter('agent.requests.error'); this.logger.error('Error handling request', { agentId: this.metadata.id, requestId: request.id, error }); const response = { requestId: request.id, sourceAgentId: this.metadata.id, success: false, error: { code: 'PROCESSING_ERROR', message: error instanceof Error ? error.message : 'Unknown error', details: error }, timestamp: new Date(), processingTime: Date.now() - startTime }; return response; } finally { this.activeRequests--; this.metrics.recordGauge('agent.requests.active', this.activeRequests); } } handleAgentEvent(event) { // Override in subclasses if needed this.emit('agent_event', event); } startHeartbeat() { this.heartbeatInterval = setInterval(async () => { try { await this.registry.updateAgentStatus(this.getStatus()); this.emitEvent(AgentEventType.AGENT_HEARTBEAT, this.getStatus()); } catch (error) { this.logger.error('Failed to send heartbeat', { agentId: this.metadata.id, error }); } }, 10000); // 10 seconds } startHealthChecks() { this.healthCheckInterval = setInterval(async () => { try { await this.healthCheck(); } catch (error) { this.logger.error('Health check failed', { agentId: this.metadata.id, error }); } }, 30000); // 30 seconds } getStateValue(state) { switch (state) { case AgentState.INITIALIZING: return 0; case AgentState.READY: return 1; case AgentState.BUSY: return 2; case AgentState.ERROR: return 3; case AgentState.STOPPING: return 4; case AgentState.STOPPED: return 5; default: return -1; } } getHealthValue(health) { switch (health) { case AgentHealth.HEALTHY: return 1; case AgentHealth.DEGRADED: return 2; case AgentHealth.UNHEALTHY: return 3; case AgentHealth.CRITICAL: return 4; default: return -1; } } } //# sourceMappingURL=base-agent.js.map