devflow-ai
Version:
Enterprise-grade AI agent orchestration with swarm management UI dashboard
675 lines (572 loc) • 17.5 kB
text/typescript
/**
* Base Agent Class
*
* Foundation for all agent types in the Hive Mind swarm.
* Provides core functionality for task execution, communication, and coordination.
*/
import { EventEmitter } from 'events';
import { v4 as uuidv4 } from 'uuid';
import { DatabaseManager } from './DatabaseManager.js';
import { MCPToolWrapper } from '../integration/MCPToolWrapper.js';
import {
AgentType,
AgentStatus,
AgentCapability,
Task,
Message,
AgentConfig,
ExecutionResult,
} from '../types.js';
export class Agent extends EventEmitter {
public readonly id: string;
public readonly name: string;
public readonly type: AgentType;
public readonly swarmId: string;
public readonly capabilities: AgentCapability[];
public readonly createdAt: Date;
public status: AgentStatus = 'idle';
public currentTask: string | null = null;
public messageCount: number = 0;
private db: DatabaseManager;
private mcpWrapper: MCPToolWrapper;
private memory: Map<string, any>;
private communicationBuffer: Message[];
private lastHeartbeat: number;
private isActive: boolean = false;
constructor(config: AgentConfig) {
super();
this.id = config.id || uuidv4();
this.name = config.name;
this.type = config.type;
this.swarmId = config.swarmId;
this.capabilities = config.capabilities || [];
this.createdAt = new Date();
this.memory = new Map();
this.communicationBuffer = [];
this.lastHeartbeat = Date.now();
}
/**
* Initialize the agent
*/
async initialize(): Promise<void> {
this.db = await DatabaseManager.getInstance();
this.mcpWrapper = new MCPToolWrapper();
// Load agent state from database if exists
const existingAgent = await this.db.getAgent(this.id);
if (existingAgent) {
this.status = existingAgent.status as AgentStatus;
this.currentTask = existingAgent.current_task_id;
this.messageCount = existingAgent.message_count;
}
// Start agent loops
this.startHeartbeatLoop();
this.startCommunicationLoop();
this.startLearningLoop();
this.isActive = true;
this.emit('initialized');
}
/**
* Assign a task to this agent
*/
async assignTask(taskId: string, executionPlan: any): Promise<void> {
if (this.currentTask) {
throw new Error('Agent already has an active task');
}
this.currentTask = taskId;
this.status = 'busy';
// Update database
await this.db.updateAgent(this.id, {
status: 'busy',
current_task_id: taskId,
});
// Store task in memory
this.memory.set('current_task', { taskId, executionPlan, startTime: Date.now() });
// Start task execution
this.executeTask(taskId, executionPlan).catch((error) => {
this.emit('taskError', { taskId, error });
});
this.emit('taskAssigned', { taskId });
}
/**
* Execute assigned task
*/
private async executeTask(taskId: string, executionPlan: any): Promise<void> {
try {
// Load task details
const task = await this.db.getTask(taskId);
if (!task) {
throw new Error('Task not found');
}
// Update task status
await this.db.updateTaskStatus(taskId, 'in_progress');
// Execute based on agent type
const result = await this.executeByType(task, executionPlan);
// Store result
await this.db.updateTask(taskId, {
status: 'completed',
result: JSON.stringify(result),
progress: 100,
completed_at: new Date(),
});
// Learn from execution
await this.learnFromExecution(task, result);
// Clear task
this.currentTask = null;
this.status = 'idle';
await this.db.updateAgent(this.id, {
status: 'idle',
current_task_id: null,
success_count: this.db.raw('success_count + 1'),
});
this.emit('taskCompleted', { taskId, result });
} catch (error) {
// Handle task failure
await this.handleTaskFailure(taskId, error);
}
}
/**
* Execute task based on agent type
*/
protected async executeByType(task: any, executionPlan: any): Promise<ExecutionResult> {
// Base implementation - override in specialized agents
const startTime = Date.now();
// Simulate task execution phases
const phases = executionPlan.phases || ['analysis', 'execution', 'validation'];
const results: any[] = [];
for (const phase of phases) {
const phaseResult = await this.executePhase(phase, task, executionPlan);
results.push(phaseResult);
// Update progress
const progress = Math.round(((phases.indexOf(phase) + 1) / phases.length) * 100);
await this.updateTaskProgress(task.id, progress);
// Communicate progress
await this.communicateProgress(task.id, phase, progress);
}
return {
success: true,
data: results,
executionTime: Date.now() - startTime,
agentId: this.id,
metadata: {
phases: phases,
plan: executionPlan,
},
};
}
/**
* Execute a specific phase of the task
*/
protected async executePhase(phase: string, task: any, plan: any): Promise<any> {
// Use MCP tools based on phase and agent capabilities
switch (phase) {
case 'analysis':
return this.performAnalysis(task);
case 'execution':
return this.performExecution(task, plan);
case 'validation':
return this.performValidation(task);
default:
return { phase, status: 'completed' };
}
}
/**
* Perform analysis phase
*/
protected async performAnalysis(task: any): Promise<any> {
// Use neural analysis for task understanding
const analysis = await this.mcpWrapper.analyzePattern({
action: 'analyze',
operation: `${this.type}_analysis`,
metadata: {
task: task.description,
agentType: this.type,
capabilities: this.capabilities,
},
});
// Store analysis in memory
await this.storeInMemory('task_analysis', analysis);
return {
phase: 'analysis',
complexity: analysis.complexity || 'medium',
estimatedTime: analysis.estimatedTime || 3600000,
requirements: analysis.requirements || [],
};
}
/**
* Perform execution phase
*/
protected async performExecution(task: any, plan: any): Promise<any> {
// Base execution - specialized agents override this
const actions =
plan.agentAssignments?.find((a: any) => a.agentId === this.id)?.responsibilities || [];
const results = [];
for (const action of actions) {
const actionResult = await this.executeAction(action, task);
results.push(actionResult);
}
return {
phase: 'execution',
actions: actions,
results: results,
};
}
/**
* Perform validation phase
*/
protected async performValidation(task: any): Promise<any> {
// Validate execution results
const validation = {
phase: 'validation',
checks: [],
passed: true,
};
// Basic validation checks
const checks = [
{ name: 'completeness', passed: true },
{ name: 'quality', passed: true },
{ name: 'performance', passed: true },
];
validation.checks = checks;
validation.passed = checks.every((c) => c.passed);
return validation;
}
/**
* Execute a specific action
*/
protected async executeAction(action: string, task: any): Promise<any> {
// Base action execution
return {
action: action,
status: 'completed',
timestamp: new Date(),
};
}
/**
* Send a message to another agent or broadcast
*/
async sendMessage(toAgentId: string | null, messageType: string, content: any): Promise<void> {
const message: Message = {
id: uuidv4(),
fromAgentId: this.id,
toAgentId,
swarmId: this.swarmId,
type: messageType,
content,
timestamp: new Date(),
requiresResponse: false,
};
// Store in database
await this.db.createCommunication({
from_agent_id: this.id,
to_agent_id: toAgentId,
swarm_id: this.swarmId,
message_type: messageType,
content: JSON.stringify(content),
priority: 'normal',
});
this.messageCount++;
this.emit('messageSent', message);
}
/**
* Receive and process a message
*/
async receiveMessage(message: Message): Promise<void> {
this.communicationBuffer.push(message);
this.emit('messageReceived', message);
}
/**
* Vote on a consensus proposal
*/
async voteOnProposal(proposalId: string, vote: boolean, reason?: string): Promise<void> {
await this.db.submitConsensusVote(proposalId, this.id, vote, reason);
this.emit('voteCast', { proposalId, vote, reason });
}
/**
* Update task progress
*/
protected async updateTaskProgress(taskId: string, progress: number): Promise<void> {
await this.db.updateTask(taskId, {
progress,
last_progress_update: new Date(),
});
}
/**
* Communicate progress to other agents
*/
protected async communicateProgress(
taskId: string,
phase: string,
progress: number,
): Promise<void> {
await this.sendMessage(null, 'progress_update', {
taskId,
agentId: this.id,
phase,
progress,
timestamp: new Date(),
});
}
/**
* Store data in agent memory
*/
protected async storeInMemory(key: string, value: any): Promise<void> {
this.memory.set(key, value);
// Also store in persistent memory
await this.mcpWrapper.storeMemory({
action: 'store',
key: `agent/${this.id}/${key}`,
value: JSON.stringify(value),
namespace: 'agent-memory',
ttl: 3600, // 1 hour
});
}
/**
* Retrieve from agent memory
*/
protected async retrieveFromMemory(key: string): Promise<any> {
// Check local memory first
if (this.memory.has(key)) {
return this.memory.get(key);
}
// Check persistent memory
const result = await this.mcpWrapper.retrieveMemory({
action: 'retrieve',
key: `agent/${this.id}/${key}`,
namespace: 'agent-memory',
});
return result ? JSON.parse(result) : null;
}
/**
* Learn from task execution
*/
protected async learnFromExecution(task: any, result: ExecutionResult): Promise<void> {
const learningData = {
taskType: this.detectTaskType(task.description),
agentType: this.type,
success: result.success,
executionTime: result.executionTime,
patterns: this.extractPatterns(task, result),
};
// Train neural patterns
await this.mcpWrapper.trainNeural({
pattern_type: 'optimization',
training_data: JSON.stringify(learningData),
epochs: 10,
});
}
/**
* Handle task failure
*/
protected async handleTaskFailure(taskId: string, error: any): Promise<void> {
// Update task status
await this.db.updateTask(taskId, {
status: 'failed',
error: error.message,
completed_at: new Date(),
});
// Update agent stats
await this.db.updateAgent(this.id, {
status: 'idle',
current_task_id: null,
error_count: this.db.raw('error_count + 1'),
});
// Clear current task
this.currentTask = null;
this.status = 'idle';
// Notify swarm of failure
await this.sendMessage(null, 'task_failed', {
taskId,
agentId: this.id,
error: error.message,
timestamp: new Date(),
});
this.emit('taskFailed', { taskId, error });
}
/**
* Start heartbeat loop
*/
private startHeartbeatLoop(): void {
setInterval(async () => {
if (!this.isActive) return;
this.lastHeartbeat = Date.now();
// Update last active timestamp
await this.db.updateAgent(this.id, {
last_active_at: new Date(),
});
this.emit('heartbeat');
}, 30000); // Every 30 seconds
}
/**
* Start communication processing loop
*/
private startCommunicationLoop(): void {
setInterval(async () => {
if (!this.isActive || this.communicationBuffer.length === 0) return;
// Process buffered messages
const messages = [...this.communicationBuffer];
this.communicationBuffer = [];
for (const message of messages) {
await this.processMessage(message);
}
}, 1000); // Every second
}
/**
* Start learning loop
*/
private startLearningLoop(): void {
setInterval(async () => {
if (!this.isActive) return;
try {
// Analyze recent patterns
const patterns = await this.analyzeRecentPatterns();
// Update capabilities if needed
await this.updateCapabilities(patterns);
} catch (error) {
this.emit('learningError', error);
}
}, 300000); // Every 5 minutes
}
/**
* Process incoming message
*/
protected async processMessage(message: Message): Promise<void> {
switch (message.type) {
case 'task_assignment':
await this.handleTaskAssignment(message.content);
break;
case 'consensus':
await this.handleConsensusRequest(message.content);
break;
case 'query':
await this.handleQuery(message);
break;
case 'coordination':
await this.handleCoordination(message.content);
break;
default:
this.emit('unknownMessage', message);
}
}
/**
* Check if agent is responsive
*/
isResponsive(): boolean {
const timeout = 60000; // 1 minute
return Date.now() - this.lastHeartbeat < timeout;
}
/**
* Get agent state
*/
getState(): any {
return {
id: this.id,
name: this.name,
type: this.type,
status: this.status,
currentTask: this.currentTask,
capabilities: this.capabilities,
messageCount: this.messageCount,
isResponsive: this.isResponsive(),
memory: Object.fromEntries(this.memory),
};
}
/**
* Shutdown the agent
*/
async shutdown(): Promise<void> {
this.isActive = false;
// Update status in database
await this.db.updateAgent(this.id, {
status: 'offline',
});
// Clear memory
this.memory.clear();
this.communicationBuffer = [];
this.emit('shutdown');
}
// Helper methods
private detectTaskType(description: string): string {
const lower = description.toLowerCase();
if (lower.includes('research') || lower.includes('investigate')) return 'research';
if (lower.includes('develop') || lower.includes('implement')) return 'development';
if (lower.includes('analyze') || lower.includes('review')) return 'analysis';
if (lower.includes('test') || lower.includes('validate')) return 'testing';
if (lower.includes('optimize') || lower.includes('improve')) return 'optimization';
return 'general';
}
private extractPatterns(task: any, result: ExecutionResult): any {
return {
taskComplexity: task.priority,
executionStrategy: task.strategy,
phasesCompleted: result.metadata?.phases?.length || 0,
timePerPhase: result.executionTime / (result.metadata?.phases?.length || 1),
};
}
private async analyzeRecentPatterns(): Promise<any> {
return this.mcpWrapper.analyzePattern({
action: 'analyze',
operation: 'agent_patterns',
metadata: {
agentId: this.id,
agentType: this.type,
timeframe: '1h',
},
});
}
private async updateCapabilities(patterns: any): Promise<void> {
if (patterns.suggestedCapabilities) {
// Update capabilities based on learning
const newCapabilities = patterns.suggestedCapabilities.filter(
(cap: string) => !this.capabilities.includes(cap),
);
if (newCapabilities.length > 0) {
this.capabilities.push(...newCapabilities);
await this.db.updateAgent(this.id, {
capabilities: JSON.stringify(this.capabilities),
});
this.emit('capabilitiesUpdated', newCapabilities);
}
}
}
private async handleTaskAssignment(content: any): Promise<void> {
// Handle incoming task assignment
if (!this.currentTask && content.taskId) {
await this.assignTask(content.taskId, content.executionPlan || {});
}
}
private async handleConsensusRequest(content: any): Promise<void> {
// Analyze proposal and vote
const analysis = await this.analyzeProposal(content);
await this.voteOnProposal(content.proposalId, analysis.vote, analysis.reason);
}
private async handleQuery(message: Message): Promise<void> {
// Respond to query
const response = await this.processQuery(message.content);
if (message.fromAgentId) {
await this.sendMessage(message.fromAgentId, 'response', {
queryId: message.id,
response,
});
}
}
private async handleCoordination(content: any): Promise<void> {
// Handle coordination messages
this.emit('coordinationReceived', content);
}
private async analyzeProposal(proposal: any): Promise<any> {
// Simple analysis - can be overridden by specialized agents
return {
vote: Math.random() > 0.3, // 70% approval rate
reason: 'Based on agent analysis',
};
}
private async processQuery(query: any): Promise<any> {
// Process and respond to queries
return {
agentId: this.id,
agentType: this.type,
status: this.status,
response: 'Query processed',
};
}
}