localgreenchain/lib/agents/BaseAgent.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

301 lines
7.2 KiB
TypeScript

/**
* BaseAgent - Abstract base class for all LocalGreenChain agents
* Provides common functionality for autonomous task execution
*/
import {
AgentConfig,
AgentStatus,
AgentMetrics,
AgentTask,
AgentAlert,
AgentError,
AgentPriority,
BaseAgent as IBaseAgent
} from './types';
export abstract class BaseAgent implements IBaseAgent {
config: AgentConfig;
status: AgentStatus = 'idle';
metrics: AgentMetrics;
protected alerts: AgentAlert[] = [];
protected taskQueue: AgentTask[] = [];
protected runInterval: NodeJS.Timeout | null = null;
protected startTime: number = 0;
constructor(config: AgentConfig) {
this.config = config;
this.metrics = {
agentId: config.id,
tasksCompleted: 0,
tasksFailed: 0,
averageExecutionMs: 0,
lastRunAt: null,
lastSuccessAt: null,
lastErrorAt: null,
uptime: 0,
errors: []
};
}
/**
* Start the agent's execution loop
*/
async start(): Promise<void> {
if (this.status === 'running') {
console.log(`[${this.config.name}] Already running`);
return;
}
this.status = 'running';
this.startTime = Date.now();
console.log(`[${this.config.name}] Starting agent...`);
// Run immediately once
await this.executeTask();
// Set up interval
this.runInterval = setInterval(async () => {
if (this.status === 'running') {
await this.executeTask();
}
}, this.config.intervalMs);
this.emitEvent('agent_started', { config: this.config });
}
/**
* Stop the agent
*/
async stop(): Promise<void> {
if (this.runInterval) {
clearInterval(this.runInterval);
this.runInterval = null;
}
this.status = 'idle';
this.metrics.uptime += Date.now() - this.startTime;
console.log(`[${this.config.name}] Stopped`);
this.emitEvent('agent_stopped', { uptime: this.metrics.uptime });
}
/**
* Pause the agent
*/
pause(): void {
if (this.status === 'running') {
this.status = 'paused';
console.log(`[${this.config.name}] Paused`);
}
}
/**
* Resume the agent
*/
resume(): void {
if (this.status === 'paused') {
this.status = 'running';
console.log(`[${this.config.name}] Resumed`);
}
}
/**
* Execute a single task cycle
*/
protected async executeTask(): Promise<void> {
const startTime = Date.now();
this.metrics.lastRunAt = new Date().toISOString();
try {
const task = await this.runOnce();
if (task) {
const executionTime = Date.now() - startTime;
this.updateAverageExecutionTime(executionTime);
if (task.status === 'completed') {
this.metrics.tasksCompleted++;
this.metrics.lastSuccessAt = new Date().toISOString();
this.emitEvent('task_completed', { taskId: task.id, result: task.result });
} else if (task.status === 'failed') {
this.metrics.tasksFailed++;
this.metrics.lastErrorAt = new Date().toISOString();
this.emitEvent('task_failed', { taskId: task.id, error: task.error });
}
}
} catch (error: any) {
this.handleError(error);
}
}
/**
* Run a single execution cycle - must be implemented by subclasses
*/
abstract runOnce(): Promise<AgentTask | null>;
/**
* Get current metrics
*/
getMetrics(): AgentMetrics {
return {
...this.metrics,
uptime: this.status === 'running'
? this.metrics.uptime + (Date.now() - this.startTime)
: this.metrics.uptime
};
}
/**
* Get all alerts
*/
getAlerts(): AgentAlert[] {
return this.alerts;
}
/**
* Add a task to the queue
*/
protected addTask(type: string, payload: Record<string, any>, priority: AgentPriority = 'medium'): AgentTask {
const task: AgentTask = {
id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
agentId: this.config.id,
type,
payload,
priority,
createdAt: new Date().toISOString(),
status: 'pending',
retryCount: 0
};
this.taskQueue.push(task);
this.sortTaskQueue();
return task;
}
/**
* Create an alert
*/
protected createAlert(
severity: AgentAlert['severity'],
title: string,
message: string,
options?: {
actionRequired?: string;
relatedEntityId?: string;
relatedEntityType?: string;
}
): AgentAlert {
const alert: AgentAlert = {
id: `alert-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
agentId: this.config.id,
severity,
title,
message,
timestamp: new Date().toISOString(),
acknowledged: false,
...options
};
this.alerts.push(alert);
// Keep only last 100 alerts
if (this.alerts.length > 100) {
this.alerts = this.alerts.slice(-100);
}
console.log(`[${this.config.name}] Alert (${severity}): ${title}`);
return alert;
}
/**
* Handle errors
*/
protected handleError(error: any, taskId?: string): void {
const agentError: AgentError = {
timestamp: new Date().toISOString(),
message: error.message || String(error),
taskId,
stack: error.stack
};
this.metrics.errors.push(agentError);
this.metrics.lastErrorAt = agentError.timestamp;
// Keep only last 50 errors
if (this.metrics.errors.length > 50) {
this.metrics.errors = this.metrics.errors.slice(-50);
}
console.error(`[${this.config.name}] Error: ${agentError.message}`);
// Create critical alert if too many errors
const recentErrors = this.metrics.errors.filter(
e => Date.now() - new Date(e.timestamp).getTime() < 60000
);
if (recentErrors.length >= 5) {
this.createAlert('critical', 'High Error Rate',
`${recentErrors.length} errors in the last minute`,
{ actionRequired: 'Check agent configuration and dependencies' }
);
}
}
/**
* Emit an event
*/
protected emitEvent(eventType: string, data: Record<string, any>): void {
// Events can be picked up by the orchestrator
console.log(`[${this.config.name}] Event: ${eventType}`);
}
/**
* Update average execution time
*/
private updateAverageExecutionTime(newTime: number): void {
const totalTasks = this.metrics.tasksCompleted + this.metrics.tasksFailed;
if (totalTasks === 0) {
this.metrics.averageExecutionMs = newTime;
} else {
this.metrics.averageExecutionMs =
(this.metrics.averageExecutionMs * totalTasks + newTime) / (totalTasks + 1);
}
}
/**
* Sort task queue by priority
*/
private sortTaskQueue(): void {
const priorityOrder: Record<AgentPriority, number> = {
critical: 0,
high: 1,
medium: 2,
low: 3
};
this.taskQueue.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
}
/**
* Create a task result
*/
protected createTaskResult(
type: string,
status: 'completed' | 'failed',
result?: any,
error?: string
): AgentTask {
return {
id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
agentId: this.config.id,
type,
payload: {},
priority: 'medium',
createdAt: new Date().toISOString(),
status,
result,
error,
retryCount: 0
};
}
}