localgreenchain/lib/agents/AgentOrchestrator.ts
Claude 4235e17f60
Add comprehensive 10-agent autonomous system for LocalGreenChain
Agents created:
1. PlantLineageAgent - Monitors plant ancestry and lineage integrity
2. TransportTrackerAgent - Tracks transport events and carbon footprint
3. DemandForecastAgent - Predicts consumer demand and market trends
4. VerticalFarmAgent - Manages vertical farm operations and optimization
5. EnvironmentAnalysisAgent - Analyzes growing conditions and recommendations
6. MarketMatchingAgent - Connects grower supply with consumer demand
7. SustainabilityAgent - Monitors environmental impact and sustainability
8. NetworkDiscoveryAgent - Maps geographic distribution and network analysis
9. QualityAssuranceAgent - Verifies blockchain integrity and data quality
10. GrowerAdvisoryAgent - Provides personalized growing recommendations

Also includes:
- BaseAgent abstract class for common functionality
- AgentOrchestrator for centralized agent management
- Comprehensive type definitions
- Full documentation in docs/AGENTS.md
2025-11-22 21:24:40 +00:00

568 lines
16 KiB
TypeScript

/**
* AgentOrchestrator
* Central management system for all LocalGreenChain agents
*
* Responsibilities:
* - Start/stop all agents
* - Coordinate inter-agent communication
* - Aggregate metrics and alerts
* - Handle agent failures and restarts
* - Provide unified status dashboard
*/
import { BaseAgent } from './BaseAgent';
import { AgentMetrics, AgentAlert, AgentConfig, AgentStatus } from './types';
import { getPlantLineageAgent, PlantLineageAgent } from './PlantLineageAgent';
import { getTransportTrackerAgent, TransportTrackerAgent } from './TransportTrackerAgent';
import { getDemandForecastAgent, DemandForecastAgent } from './DemandForecastAgent';
import { getVerticalFarmAgent, VerticalFarmAgent } from './VerticalFarmAgent';
import { getEnvironmentAnalysisAgent, EnvironmentAnalysisAgent } from './EnvironmentAnalysisAgent';
import { getMarketMatchingAgent, MarketMatchingAgent } from './MarketMatchingAgent';
import { getSustainabilityAgent, SustainabilityAgent } from './SustainabilityAgent';
import { getNetworkDiscoveryAgent, NetworkDiscoveryAgent } from './NetworkDiscoveryAgent';
import { getQualityAssuranceAgent, QualityAssuranceAgent } from './QualityAssuranceAgent';
import { getGrowerAdvisoryAgent, GrowerAdvisoryAgent } from './GrowerAdvisoryAgent';
interface OrchestratorConfig {
autoStart: boolean;
enabledAgents: string[];
alertAggregationIntervalMs: number;
healthCheckIntervalMs: number;
maxAlertsPerAgent: number;
}
interface AgentHealth {
agentId: string;
agentName: string;
status: AgentStatus;
lastRunAt: string | null;
tasksCompleted: number;
tasksFailed: number;
errorRate: number;
avgExecutionMs: number;
isHealthy: boolean;
}
interface OrchestratorStatus {
isRunning: boolean;
startedAt: string | null;
uptime: number;
totalAgents: number;
runningAgents: number;
healthyAgents: number;
totalTasksCompleted: number;
totalTasksFailed: number;
activeAlerts: number;
}
interface AgentSummary {
id: string;
name: string;
description: string;
status: AgentStatus;
priority: string;
intervalMs: number;
metrics: AgentMetrics;
alertCount: number;
}
export class AgentOrchestrator {
private static instance: AgentOrchestrator;
private config: OrchestratorConfig;
private agents: Map<string, BaseAgent> = new Map();
private isRunning: boolean = false;
private startedAt: string | null = null;
private healthCheckInterval: NodeJS.Timeout | null = null;
private alertAggregationInterval: NodeJS.Timeout | null = null;
private aggregatedAlerts: AgentAlert[] = [];
private eventHandlers: Map<string, ((event: any) => void)[]> = new Map();
private constructor(config?: Partial<OrchestratorConfig>) {
this.config = {
autoStart: false,
enabledAgents: [
'plant-lineage-agent',
'transport-tracker-agent',
'demand-forecast-agent',
'vertical-farm-agent',
'environment-analysis-agent',
'market-matching-agent',
'sustainability-agent',
'network-discovery-agent',
'quality-assurance-agent',
'grower-advisory-agent'
],
alertAggregationIntervalMs: 60000,
healthCheckIntervalMs: 30000,
maxAlertsPerAgent: 50,
...config
};
this.initializeAgents();
}
public static getInstance(config?: Partial<OrchestratorConfig>): AgentOrchestrator {
if (!AgentOrchestrator.instance) {
AgentOrchestrator.instance = new AgentOrchestrator(config);
}
return AgentOrchestrator.instance;
}
/**
* Initialize all agents
*/
private initializeAgents(): void {
const agentFactories: Record<string, () => BaseAgent> = {
'plant-lineage-agent': getPlantLineageAgent,
'transport-tracker-agent': getTransportTrackerAgent,
'demand-forecast-agent': getDemandForecastAgent,
'vertical-farm-agent': getVerticalFarmAgent,
'environment-analysis-agent': getEnvironmentAnalysisAgent,
'market-matching-agent': getMarketMatchingAgent,
'sustainability-agent': getSustainabilityAgent,
'network-discovery-agent': getNetworkDiscoveryAgent,
'quality-assurance-agent': getQualityAssuranceAgent,
'grower-advisory-agent': getGrowerAdvisoryAgent
};
for (const agentId of this.config.enabledAgents) {
const factory = agentFactories[agentId];
if (factory) {
const agent = factory();
this.agents.set(agentId, agent);
console.log(`[Orchestrator] Registered agent: ${agent.config.name}`);
}
}
}
/**
* Start all agents
*/
async startAll(): Promise<void> {
if (this.isRunning) {
console.log('[Orchestrator] Already running');
return;
}
console.log('[Orchestrator] Starting all agents...');
this.isRunning = true;
this.startedAt = new Date().toISOString();
// Start agents in priority order
const sortedAgents = this.getSortedAgentsByPriority();
for (const agent of sortedAgents) {
if (agent.config.enabled) {
try {
await agent.start();
console.log(`[Orchestrator] Started: ${agent.config.name}`);
} catch (error) {
console.error(`[Orchestrator] Failed to start ${agent.config.name}:`, error);
}
}
}
// Start health check interval
this.healthCheckInterval = setInterval(() => {
this.performHealthCheck();
}, this.config.healthCheckIntervalMs);
// Start alert aggregation interval
this.alertAggregationInterval = setInterval(() => {
this.aggregateAlerts();
}, this.config.alertAggregationIntervalMs);
this.emit('orchestrator_started', { agentCount: this.agents.size });
console.log(`[Orchestrator] All agents started (${this.agents.size} total)`);
}
/**
* Stop all agents
*/
async stopAll(): Promise<void> {
if (!this.isRunning) {
console.log('[Orchestrator] Not running');
return;
}
console.log('[Orchestrator] Stopping all agents...');
// Clear intervals
if (this.healthCheckInterval) {
clearInterval(this.healthCheckInterval);
this.healthCheckInterval = null;
}
if (this.alertAggregationInterval) {
clearInterval(this.alertAggregationInterval);
this.alertAggregationInterval = null;
}
// Stop all agents
for (const agent of this.agents.values()) {
try {
await agent.stop();
console.log(`[Orchestrator] Stopped: ${agent.config.name}`);
} catch (error) {
console.error(`[Orchestrator] Error stopping ${agent.config.name}:`, error);
}
}
this.isRunning = false;
this.emit('orchestrator_stopped', {});
console.log('[Orchestrator] All agents stopped');
}
/**
* Start a specific agent
*/
async startAgent(agentId: string): Promise<boolean> {
const agent = this.agents.get(agentId);
if (!agent) {
console.error(`[Orchestrator] Agent not found: ${agentId}`);
return false;
}
try {
await agent.start();
return true;
} catch (error) {
console.error(`[Orchestrator] Failed to start ${agentId}:`, error);
return false;
}
}
/**
* Stop a specific agent
*/
async stopAgent(agentId: string): Promise<boolean> {
const agent = this.agents.get(agentId);
if (!agent) {
console.error(`[Orchestrator] Agent not found: ${agentId}`);
return false;
}
try {
await agent.stop();
return true;
} catch (error) {
console.error(`[Orchestrator] Failed to stop ${agentId}:`, error);
return false;
}
}
/**
* Restart a specific agent
*/
async restartAgent(agentId: string): Promise<boolean> {
await this.stopAgent(agentId);
return this.startAgent(agentId);
}
/**
* Get sorted agents by priority
*/
private getSortedAgentsByPriority(): BaseAgent[] {
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
return Array.from(this.agents.values()).sort((a, b) =>
priorityOrder[a.config.priority] - priorityOrder[b.config.priority]
);
}
/**
* Perform health check on all agents
*/
private performHealthCheck(): void {
for (const [agentId, agent] of this.agents) {
const health = this.getAgentHealth(agentId);
if (!health.isHealthy) {
console.warn(`[Orchestrator] Unhealthy agent detected: ${agent.config.name}`);
// Auto-restart if error rate is high
if (health.errorRate > 50 && agent.status === 'running') {
console.log(`[Orchestrator] Auto-restarting: ${agent.config.name}`);
this.restartAgent(agentId);
}
}
}
}
/**
* Aggregate alerts from all agents
*/
private aggregateAlerts(): void {
this.aggregatedAlerts = [];
for (const agent of this.agents.values()) {
const alerts = agent.getAlerts()
.filter(a => !a.acknowledged)
.slice(-this.config.maxAlertsPerAgent);
this.aggregatedAlerts.push(...alerts);
}
// Sort by severity and timestamp
const severityOrder = { critical: 0, error: 1, warning: 2, info: 3 };
this.aggregatedAlerts.sort((a, b) => {
const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
if (severityDiff !== 0) return severityDiff;
return new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime();
});
// Emit event if critical alerts
const criticalAlerts = this.aggregatedAlerts.filter(a => a.severity === 'critical');
if (criticalAlerts.length > 0) {
this.emit('critical_alerts', { count: criticalAlerts.length, alerts: criticalAlerts });
}
}
/**
* Get agent health
*/
getAgentHealth(agentId: string): AgentHealth {
const agent = this.agents.get(agentId);
if (!agent) {
return {
agentId,
agentName: 'Unknown',
status: 'idle',
lastRunAt: null,
tasksCompleted: 0,
tasksFailed: 0,
errorRate: 0,
avgExecutionMs: 0,
isHealthy: false
};
}
const metrics = agent.getMetrics();
const totalTasks = metrics.tasksCompleted + metrics.tasksFailed;
const errorRate = totalTasks > 0
? (metrics.tasksFailed / totalTasks) * 100
: 0;
// Check if agent is responding (last run within 2x interval)
const lastRunTime = metrics.lastRunAt ? new Date(metrics.lastRunAt).getTime() : 0;
const maxTimeSinceRun = agent.config.intervalMs * 2;
const isResponding = agent.status !== 'running' ||
(Date.now() - lastRunTime < maxTimeSinceRun);
const isHealthy = agent.status !== 'error' &&
errorRate < 30 &&
isResponding;
return {
agentId,
agentName: agent.config.name,
status: agent.status,
lastRunAt: metrics.lastRunAt,
tasksCompleted: metrics.tasksCompleted,
tasksFailed: metrics.tasksFailed,
errorRate: Math.round(errorRate),
avgExecutionMs: Math.round(metrics.averageExecutionMs),
isHealthy
};
}
/**
* Get orchestrator status
*/
getStatus(): OrchestratorStatus {
const healthStatuses = Array.from(this.agents.keys()).map(id => this.getAgentHealth(id));
const runningAgents = healthStatuses.filter(h => h.status === 'running').length;
const healthyAgents = healthStatuses.filter(h => h.isHealthy).length;
const totalCompleted = healthStatuses.reduce((sum, h) => sum + h.tasksCompleted, 0);
const totalFailed = healthStatuses.reduce((sum, h) => sum + h.tasksFailed, 0);
return {
isRunning: this.isRunning,
startedAt: this.startedAt,
uptime: this.startedAt
? Date.now() - new Date(this.startedAt).getTime()
: 0,
totalAgents: this.agents.size,
runningAgents,
healthyAgents,
totalTasksCompleted: totalCompleted,
totalTasksFailed: totalFailed,
activeAlerts: this.aggregatedAlerts.filter(a => !a.acknowledged).length
};
}
/**
* Get all agent summaries
*/
getAgentSummaries(): AgentSummary[] {
return Array.from(this.agents.values()).map(agent => ({
id: agent.config.id,
name: agent.config.name,
description: agent.config.description,
status: agent.status,
priority: agent.config.priority,
intervalMs: agent.config.intervalMs,
metrics: agent.getMetrics(),
alertCount: agent.getAlerts().filter(a => !a.acknowledged).length
}));
}
/**
* Get all alerts
*/
getAlerts(severity?: string): AgentAlert[] {
if (severity) {
return this.aggregatedAlerts.filter(a => a.severity === severity);
}
return this.aggregatedAlerts;
}
/**
* Acknowledge an alert
*/
acknowledgeAlert(alertId: string): boolean {
const alert = this.aggregatedAlerts.find(a => a.id === alertId);
if (alert) {
alert.acknowledged = true;
return true;
}
return false;
}
/**
* Get specific agent
*/
getAgent<T extends BaseAgent>(agentId: string): T | null {
return this.agents.get(agentId) as T || null;
}
/**
* Get typed agent instances
*/
getPlantLineageAgent(): PlantLineageAgent | null {
return this.getAgent<PlantLineageAgent>('plant-lineage-agent');
}
getTransportTrackerAgent(): TransportTrackerAgent | null {
return this.getAgent<TransportTrackerAgent>('transport-tracker-agent');
}
getDemandForecastAgent(): DemandForecastAgent | null {
return this.getAgent<DemandForecastAgent>('demand-forecast-agent');
}
getVerticalFarmAgent(): VerticalFarmAgent | null {
return this.getAgent<VerticalFarmAgent>('vertical-farm-agent');
}
getEnvironmentAnalysisAgent(): EnvironmentAnalysisAgent | null {
return this.getAgent<EnvironmentAnalysisAgent>('environment-analysis-agent');
}
getMarketMatchingAgent(): MarketMatchingAgent | null {
return this.getAgent<MarketMatchingAgent>('market-matching-agent');
}
getSustainabilityAgent(): SustainabilityAgent | null {
return this.getAgent<SustainabilityAgent>('sustainability-agent');
}
getNetworkDiscoveryAgent(): NetworkDiscoveryAgent | null {
return this.getAgent<NetworkDiscoveryAgent>('network-discovery-agent');
}
getQualityAssuranceAgent(): QualityAssuranceAgent | null {
return this.getAgent<QualityAssuranceAgent>('quality-assurance-agent');
}
getGrowerAdvisoryAgent(): GrowerAdvisoryAgent | null {
return this.getAgent<GrowerAdvisoryAgent>('grower-advisory-agent');
}
/**
* Register event handler
*/
on(event: string, handler: (data: any) => void): void {
const handlers = this.eventHandlers.get(event) || [];
handlers.push(handler);
this.eventHandlers.set(event, handlers);
}
/**
* Emit event
*/
private emit(event: string, data: any): void {
const handlers = this.eventHandlers.get(event) || [];
for (const handler of handlers) {
try {
handler(data);
} catch (error) {
console.error(`[Orchestrator] Event handler error for ${event}:`, error);
}
}
}
/**
* Get dashboard data
*/
getDashboard(): {
status: OrchestratorStatus;
agents: AgentSummary[];
health: AgentHealth[];
recentAlerts: AgentAlert[];
metrics: {
totalTasksToday: number;
avgErrorRate: number;
avgExecutionTime: number;
};
} {
const status = this.getStatus();
const agents = this.getAgentSummaries();
const health = Array.from(this.agents.keys()).map(id => this.getAgentHealth(id));
const recentAlerts = this.aggregatedAlerts.slice(0, 10);
const avgErrorRate = health.length > 0
? health.reduce((sum, h) => sum + h.errorRate, 0) / health.length
: 0;
const avgExecutionTime = health.length > 0
? health.reduce((sum, h) => sum + h.avgExecutionMs, 0) / health.length
: 0;
return {
status,
agents,
health,
recentAlerts,
metrics: {
totalTasksToday: status.totalTasksCompleted,
avgErrorRate: Math.round(avgErrorRate),
avgExecutionTime: Math.round(avgExecutionTime)
}
};
}
}
/**
* Get orchestrator singleton
*/
export function getOrchestrator(config?: Partial<OrchestratorConfig>): AgentOrchestrator {
return AgentOrchestrator.getInstance(config);
}
/**
* Start all agents
*/
export async function startAllAgents(): Promise<void> {
const orchestrator = getOrchestrator();
await orchestrator.startAll();
}
/**
* Stop all agents
*/
export async function stopAllAgents(): Promise<void> {
const orchestrator = getOrchestrator();
await orchestrator.stopAll();
}