UNPKG

claude-flow-multilang

Version:

Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture

483 lines (403 loc) 12.7 kB
/** * Agent Registry with Memory Integration * Provides persistent storage and coordination for agent management */ import type { DistributedMemorySystem } from '../memory/distributed-memory.js'; import type { AgentState, AgentId, AgentType, AgentStatus } from '../swarm/types.js'; import { EventEmitter } from 'node:events'; export interface AgentRegistryEntry { agent: AgentState; createdAt: Date; lastUpdated: Date; tags: string[]; metadata: Record<string, any>; } export interface AgentQuery { type?: AgentType; status?: AgentStatus; tags?: string[]; healthThreshold?: number; namePattern?: string; createdAfter?: Date; lastActiveAfter?: Date; } export interface AgentStatistics { totalAgents: number; byType: Record<AgentType, number>; byStatus: Record<AgentStatus, number>; averageHealth: number; activeAgents: number; totalUptime: number; tasksCompleted: number; successRate: number; } /** * Centralized agent registry with persistent storage */ export class AgentRegistry extends EventEmitter { private memory: DistributedMemorySystem; private namespace: string; private cache = new Map<string, AgentRegistryEntry>(); private cacheExpiry = 60000; // 1 minute private lastCacheUpdate = 0; constructor(memory: DistributedMemorySystem, namespace: string = 'agents') { super(); this.memory = memory; this.namespace = namespace; } async initialize(): Promise<void> { await this.loadFromMemory(); this.emit('registry:initialized'); } /** * Register a new agent in the registry */ async registerAgent(agent: AgentState, tags: string[] = []): Promise<void> { const entry: AgentRegistryEntry = { agent, createdAt: new Date(), lastUpdated: new Date(), tags: [...tags, agent.type, agent.status], metadata: { registeredBy: 'agent-manager', version: '1.0.0', }, }; // Store in memory const key = this.getAgentKey(agent.id.id); await this.memory.store(key, entry, { type: 'agent-registry', tags: entry.tags, partition: this.namespace, }); // Update cache this.cache.set(agent.id.id, entry); this.emit('agent:registered', { agentId: agent.id.id, agent }); } /** * Update agent information in registry */ async updateAgent(agentId: string, updates: Partial<AgentState>): Promise<void> { const entry = await this.getAgentEntry(agentId); if (!entry) { throw new Error(`Agent ${agentId} not found in registry`); } // Merge updates entry.agent = { ...entry.agent, ...updates }; entry.lastUpdated = new Date(); entry.tags = [ entry.agent.type, entry.agent.status, ...entry.tags.filter((t) => t !== entry.agent.type && t !== entry.agent.status), ]; // Store updated entry const key = this.getAgentKey(agentId); await this.memory.store(key, entry, { type: 'agent-registry', tags: entry.tags, partition: this.namespace, }); // Update cache this.cache.set(agentId, entry); this.emit('agent:updated', { agentId, agent: entry.agent }); } /** * Remove agent from registry */ async unregisterAgent(agentId: string, preserveHistory: boolean = true): Promise<void> { const entry = await this.getAgentEntry(agentId); if (!entry) { return; // Already removed } if (preserveHistory) { // Move to archived partition const archiveKey = this.getArchiveKey(agentId); await this.memory.store( archiveKey, { ...entry, archivedAt: new Date(), reason: 'agent_removed', }, { type: 'agent-archive', tags: [...entry.tags, 'archived'], partition: 'archived', }, ); } // Remove from active registry const key = this.getAgentKey(agentId); await this.memory.deleteEntry(key); // Remove from cache this.cache.delete(agentId); this.emit('agent:unregistered', { agentId, preserved: preserveHistory }); } /** * Get agent by ID */ async getAgent(agentId: string): Promise<AgentState | null> { const entry = await this.getAgentEntry(agentId); return entry?.agent || null; } /** * Get agent entry with metadata */ async getAgentEntry(agentId: string): Promise<AgentRegistryEntry | null> { // Check cache first if (this.cache.has(agentId) && this.isCacheValid()) { return this.cache.get(agentId) || null; } // Load from memory const key = this.getAgentKey(agentId); const memoryEntry = await this.memory.retrieve(key); if (memoryEntry && memoryEntry.value) { // Convert MemoryEntry to AgentRegistryEntry const registryEntry: AgentRegistryEntry = memoryEntry.value as AgentRegistryEntry; this.cache.set(agentId, registryEntry); return registryEntry; } return null; } /** * Query agents by criteria */ async queryAgents(query: AgentQuery = {}): Promise<AgentState[]> { await this.refreshCacheIfNeeded(); let agents = Array.from(this.cache.values()).map((entry) => entry.agent); // Apply filters if (query.type) { agents = agents.filter((agent) => agent.type === query.type); } if (query.status) { agents = agents.filter((agent) => agent.status === query.status); } if (query.healthThreshold !== undefined) { agents = agents.filter((agent) => agent.health >= query.healthThreshold!); } if (query.namePattern) { const pattern = new RegExp(query.namePattern, 'i'); agents = agents.filter((agent) => pattern.test(agent.name)); } if (query.tags && query.tags.length > 0) { const entries = Array.from(this.cache.values()); const matchingEntries = entries.filter((entry) => query.tags!.some((tag) => entry.tags.includes(tag)), ); agents = matchingEntries.map((entry) => entry.agent); } if (query.createdAfter) { const entries = Array.from(this.cache.values()); const matchingEntries = entries.filter((entry) => entry.createdAt >= query.createdAfter!); agents = matchingEntries.map((entry) => entry.agent); } if (query.lastActiveAfter) { agents = agents.filter((agent) => agent.metrics.lastActivity >= query.lastActiveAfter!); } return agents; } /** * Get all registered agents */ async getAllAgents(): Promise<AgentState[]> { return this.queryAgents(); } /** * Get agents by type */ async getAgentsByType(type: AgentType): Promise<AgentState[]> { return this.queryAgents({ type }); } /** * Get agents by status */ async getAgentsByStatus(status: AgentStatus): Promise<AgentState[]> { return this.queryAgents({ status }); } /** * Get healthy agents */ async getHealthyAgents(threshold: number = 0.7): Promise<AgentState[]> { return this.queryAgents({ healthThreshold: threshold }); } /** * Get registry statistics */ async getStatistics(): Promise<AgentStatistics> { const agents = await this.getAllAgents(); const stats: AgentStatistics = { totalAgents: agents.length, byType: {} as Record<AgentType, number>, byStatus: {} as Record<AgentStatus, number>, averageHealth: 0, activeAgents: 0, totalUptime: 0, tasksCompleted: 0, successRate: 0, }; if (agents.length === 0) { return stats; } // Count by type and status for (const agent of agents) { stats.byType[agent.type] = (stats.byType[agent.type] || 0) + 1; stats.byStatus[agent.status] = (stats.byStatus[agent.status] || 0) + 1; if (agent.status === 'idle' || agent.status === 'busy') { stats.activeAgents++; } stats.totalUptime += agent.metrics.totalUptime; stats.tasksCompleted += agent.metrics.tasksCompleted; } // Calculate averages stats.averageHealth = agents.reduce((sum, agent) => sum + agent.health, 0) / agents.length; const totalTasks = agents.reduce( (sum, agent) => sum + agent.metrics.tasksCompleted + agent.metrics.tasksFailed, 0, ); if (totalTasks > 0) { stats.successRate = stats.tasksCompleted / totalTasks; } return stats; } /** * Search agents by capabilities */ async searchByCapabilities(requiredCapabilities: string[]): Promise<AgentState[]> { const agents = await this.getAllAgents(); return agents.filter((agent) => { const capabilities = [ ...agent.capabilities.languages, ...agent.capabilities.frameworks, ...agent.capabilities.domains, ...agent.capabilities.tools, ]; return requiredCapabilities.every((required) => capabilities.some((cap) => cap.toLowerCase().includes(required.toLowerCase())), ); }); } /** * Find best agent for task */ async findBestAgent( taskType: string, requiredCapabilities: string[] = [], preferredAgent?: string, ): Promise<AgentState | null> { let candidates = await this.getHealthyAgents(0.5); // Filter by capabilities if specified if (requiredCapabilities.length > 0) { candidates = await this.searchByCapabilities(requiredCapabilities); } // Prefer specific agent if available and healthy if (preferredAgent) { const preferred = candidates.find( (agent) => agent.id.id === preferredAgent || agent.name === preferredAgent, ); if (preferred) return preferred; } // Filter by availability candidates = candidates.filter( (agent) => agent.status === 'idle' && agent.workload < 0.8 && agent.capabilities.maxConcurrentTasks > 0, ); if (candidates.length === 0) return null; // Score candidates const scored = candidates.map((agent) => ({ agent, score: this.calculateAgentScore(agent, taskType, requiredCapabilities), })); // Sort by score (highest first) scored.sort((a, b) => b.score - a.score); return scored[0]?.agent || null; } /** * Store agent coordination data */ async storeCoordinationData(agentId: string, data: any): Promise<void> { const key = `coordination:${agentId}`; await this.memory.store( key, { agentId, data, timestamp: new Date(), }, { type: 'agent-coordination', tags: ['coordination', agentId], partition: this.namespace, }, ); } /** * Retrieve agent coordination data */ async getCoordinationData(agentId: string): Promise<any> { const key = `coordination:${agentId}`; const result = await this.memory.retrieve(key); return result?.value || null; } // === PRIVATE METHODS === private async loadFromMemory(): Promise<void> { try { const entries = await this.memory.query({ type: 'state' as const, namespace: this.namespace, }); this.cache.clear(); for (const entry of entries) { if (entry.value && entry.value.agent) { this.cache.set(entry.value.agent.id.id, entry.value); } } this.lastCacheUpdate = Date.now(); } catch (error) { console.warn('Failed to load agent registry from memory:', error); } } private async refreshCacheIfNeeded(): Promise<void> { if (!this.isCacheValid()) { await this.loadFromMemory(); } } private isCacheValid(): boolean { return Date.now() - this.lastCacheUpdate < this.cacheExpiry; } private getAgentKey(agentId: string): string { return `agent:${agentId}`; } private getArchiveKey(agentId: string): string { return `archived:${agentId}:${Date.now()}`; } private calculateAgentScore( agent: AgentState, taskType: string, requiredCapabilities: string[], ): number { let score = 0; // Base health score (0-40 points) score += agent.health * 40; // Success rate score (0-30 points) score += agent.metrics.successRate * 30; // Availability score (0-20 points) const availability = 1 - agent.workload; score += availability * 20; // Capability match score (0-10 points) if (requiredCapabilities.length > 0) { const agentCaps = [ ...agent.capabilities.languages, ...agent.capabilities.frameworks, ...agent.capabilities.domains, ...agent.capabilities.tools, ]; const matches = requiredCapabilities.filter((required) => agentCaps.some((cap) => cap.toLowerCase().includes(required.toLowerCase())), ); score += (matches.length / requiredCapabilities.length) * 10; } return score; } }