UNPKG

jay-code

Version:

Streamlined AI CLI orchestration engine with mathematical rigor and enterprise-grade reliability

1,002 lines (827 loc) 28 kB
/** * Distributed memory system with sharing capabilities */ import { EventEmitter } from 'node:events'; import type { ILogger } from '../core/logger.js'; import type { IEventBus } from '../core/event-bus.js'; import type { SwarmMemory, MemoryPartition, MemoryEntry, MemoryType, AccessLevel, ConsistencyLevel, MemoryPermissions, AgentId, } from '../swarm/types.js'; import { generateId } from '../utils/helpers.js'; export interface DistributedMemoryConfig { namespace: string; distributed: boolean; consistency: ConsistencyLevel; replicationFactor: number; syncInterval: number; maxMemorySize: number; compressionEnabled: boolean; encryptionEnabled: boolean; backupEnabled: boolean; persistenceEnabled: boolean; shardingEnabled: boolean; cacheSize: number; cacheTtl: number; backend?: string; timeout?: number; retryAttempts?: number; } export interface MemoryNode { id: string; address: string; port: number; status: 'online' | 'offline' | 'syncing' | 'failed'; lastSeen: Date; partitions: string[]; load: number; capacity: number; } export interface SyncOperation { id: string; type: 'create' | 'update' | 'delete' | 'batch'; partition: string; entry?: MemoryEntry; entries?: MemoryEntry[]; timestamp: Date; version: number; origin: string; targets: string[]; status: 'pending' | 'in_progress' | 'completed' | 'failed'; } export interface MemoryQuery { namespace?: string; partition?: string; type?: MemoryType; tags?: string[]; owner?: AgentId; accessLevel?: AccessLevel; createdAfter?: Date; updatedAfter?: Date; limit?: number; offset?: number; sortBy?: string; sortOrder?: 'asc' | 'desc'; } export interface MemoryStatistics { totalEntries: number; totalSize: number; partitionCount: number; nodeCount: number; replicationHealth: number; syncOperations: { pending: number; completed: number; failed: number; }; performance: { readLatency: number; writeLatency: number; syncLatency: number; throughput: number; }; utilization: { memoryUsage: number; diskUsage: number; networkUsage: number; }; } /** * Distributed memory system for sharing data across swarm agents */ export class DistributedMemorySystem extends EventEmitter { private logger: ILogger; private eventBus: IEventBus; private config: DistributedMemoryConfig; // Storage private partitions = new Map<string, MemoryPartition>(); private entries = new Map<string, MemoryEntry>(); private cache = new Map<string, { entry: MemoryEntry; expiry: number }>(); // Distribution private nodes = new Map<string, MemoryNode>(); private localNodeId: string; private syncQueue: SyncOperation[] = []; private replicationMap = new Map<string, string[]>(); // entryId -> nodeIds // Synchronization private syncInterval?: NodeJS.Timeout; private vectorClock = new Map<string, number>(); private conflictResolver?: (local: MemoryEntry, remote: MemoryEntry) => MemoryEntry; // Performance tracking private statistics: MemoryStatistics; private operationMetrics = new Map<string, { count: number; totalTime: number }>(); constructor(config: Partial<DistributedMemoryConfig>, logger: ILogger, eventBus: IEventBus) { super(); this.logger = logger; this.eventBus = eventBus; this.config = { namespace: 'default', distributed: true, consistency: 'eventual', replicationFactor: 3, syncInterval: 5000, maxMemorySize: 1024 * 1024 * 1024, // 1GB compressionEnabled: true, encryptionEnabled: false, backupEnabled: true, persistenceEnabled: true, shardingEnabled: true, cacheSize: 10000, cacheTtl: 300000, // 5 minutes ...config, }; this.localNodeId = generateId('memory-node'); this.statistics = this.initializeStatistics(); this.setupEventHandlers(); } private setupEventHandlers(): void { this.eventBus.on('memory:sync-request', (data) => { this.handleSyncRequest(data); }); this.eventBus.on('memory:node-joined', (data) => { this.handleNodeJoined(data); }); this.eventBus.on('memory:node-left', (data) => { this.handleNodeLeft(data); }); this.eventBus.on('memory:conflict-detected', (data) => { this.handleConflict(data); }); } async initialize(): Promise<void> { this.logger.info('Initializing distributed memory system', { nodeId: this.localNodeId, namespace: this.config.namespace, distributed: this.config.distributed, }); // Register local node const localNode: MemoryNode = { id: this.localNodeId, address: 'localhost', port: 8080, status: 'online', lastSeen: new Date(), partitions: [], load: 0, capacity: this.config.maxMemorySize, }; this.nodes.set(this.localNodeId, localNode); // Initialize default partitions await this.createPartition('knowledge', 'knowledge'); await this.createPartition('state', 'state'); await this.createPartition('cache', 'cache'); await this.createPartition('results', 'results'); // Start synchronization if distributed if (this.config.distributed) { this.startSynchronization(); } // Load persisted data if enabled if (this.config.persistenceEnabled) { await this.loadPersistedData(); } this.emit('memory:initialized', { nodeId: this.localNodeId }); } async shutdown(): Promise<void> { this.logger.info('Shutting down distributed memory system'); // Stop synchronization if (this.syncInterval) { clearInterval(this.syncInterval); } // Complete pending sync operations await this.completePendingSyncOperations(); // Persist data if enabled if (this.config.persistenceEnabled) { await this.persistData(); } // Clear caches this.cache.clear(); this.partitions.clear(); this.entries.clear(); this.emit('memory:shutdown', { nodeId: this.localNodeId }); } // === PARTITION MANAGEMENT === async createPartition( name: string, type: MemoryType, options: { maxSize?: number; ttl?: number; readOnly?: boolean; shared?: boolean; indexed?: boolean; compressed?: boolean; } = {}, ): Promise<string> { const partitionId = generateId('partition'); const partition: MemoryPartition = { id: partitionId, name, type, entries: [], maxSize: options.maxSize || this.config.maxMemorySize / 10, ttl: options.ttl, readOnly: options.readOnly || false, shared: options.shared !== false, // Default to shared indexed: options.indexed || false, compressed: options.compressed || this.config.compressionEnabled, }; this.partitions.set(partitionId, partition); // Update local node partition list const localNode = this.nodes.get(this.localNodeId)!; localNode.partitions.push(partitionId); this.logger.info('Created partition', { partitionId, name, type }); this.emit('memory:partition-created', { partition }); // Sync with other nodes if distributed if (this.config.distributed) { await this.syncPartitionCreation(partition); } return partitionId; } async deletePartition(partitionId: string): Promise<void> { const partition = this.partitions.get(partitionId); if (!partition) { throw new Error(`Partition ${partitionId} not found`); } // Delete all entries in partition const entriesToDelete = Array.from(this.entries.values()).filter( (entry) => this.getEntryPartition(entry.id) === partitionId, ); for (const entry of entriesToDelete) { await this.deleteEntry(entry.id); } // Remove partition this.partitions.delete(partitionId); // Update local node const localNode = this.nodes.get(this.localNodeId)!; localNode.partitions = localNode.partitions.filter((p) => p !== partitionId); this.logger.info('Deleted partition', { partitionId }); this.emit('memory:partition-deleted', { partitionId }); // Sync with other nodes if distributed if (this.config.distributed) { await this.syncPartitionDeletion(partitionId); } } // === ENTRY OPERATIONS === async store( key: string, value: any, options: { type?: string; tags?: string[]; owner?: AgentId; accessLevel?: AccessLevel; partition?: string; ttl?: number; replicate?: boolean; } = {}, ): Promise<string> { const startTime = Date.now(); try { const entryId = generateId('entry'); const now = new Date(); // Determine partition const partitionId = options.partition || this.selectPartition(options.type || 'knowledge'); const partition = this.partitions.get(partitionId); if (!partition) { throw new Error(`Partition ${partitionId} not found`); } if (partition.readOnly) { throw new Error('Cannot write to read-only partition'); } // Check partition capacity if (this.getPartitionSize(partitionId) >= partition.maxSize) { await this.evictOldEntries(partitionId); } // Create entry const entry: MemoryEntry = { id: entryId, key, value: await this.processValue(value, partition), type: options.type || 'data', tags: options.tags || [], owner: options.owner || { id: 'system', swarmId: '', type: 'coordinator', instance: 0 }, accessLevel: options.accessLevel || 'swarm', createdAt: now, updatedAt: now, expiresAt: options.ttl ? new Date(now.getTime() + options.ttl) : undefined, version: 1, references: [], dependencies: [], }; // Store entry this.entries.set(entryId, entry); partition.entries.push(entry); // Update cache this.updateCache(entryId, entry); // Update vector clock this.incrementVectorClock(this.localNodeId); this.logger.debug('Stored entry', { entryId, key, partition: partitionId }); this.emit('memory:entry-stored', { entry }); // Replicate if distributed and requested if (this.config.distributed && options.replicate !== false) { await this.replicateEntry(entry); } this.recordMetric('store', Date.now() - startTime); return entryId; } catch (error) { this.recordMetric('store-error', Date.now() - startTime); throw error; } } async retrieve( key: string, options: { partition?: string; consistency?: ConsistencyLevel; maxAge?: number; } = {}, ): Promise<MemoryEntry | null> { const startTime = Date.now(); try { // Check cache first const cached = this.getCachedEntry(key); if (cached && this.isCacheValid(cached)) { this.recordMetric('retrieve-cache', Date.now() - startTime); return cached.entry; } // Search in specified partition or all partitions const partitions = options.partition ? [this.partitions.get(options.partition)].filter(Boolean) : Array.from(this.partitions.values()); for (const partition of partitions) { const entry = partition!.entries.find((e) => e.key === key); if (entry) { // Check if entry is expired if (entry.expiresAt && entry.expiresAt < new Date()) { await this.deleteEntry(entry.id); continue; } // Check access permissions if (!this.checkAccess(entry, 'read')) { continue; } // Apply consistency model if (this.config.distributed && options.consistency === 'strong') { const latestEntry = await this.ensureConsistency(entry); this.updateCache(latestEntry.id, latestEntry); this.recordMetric('retrieve', Date.now() - startTime); return latestEntry; } this.updateCache(entry.id, entry); this.recordMetric('retrieve', Date.now() - startTime); return entry; } } // Not found locally, try remote nodes if distributed if (this.config.distributed) { const remoteEntry = await this.retrieveFromRemote(key, options); if (remoteEntry) { this.recordMetric('retrieve-remote', Date.now() - startTime); return remoteEntry; } } this.recordMetric('retrieve-miss', Date.now() - startTime); return null; } catch (error) { this.recordMetric('retrieve-error', Date.now() - startTime); throw error; } } async update( key: string, value: any, options: { partition?: string; merge?: boolean; version?: number; } = {}, ): Promise<boolean> { const startTime = Date.now(); try { const entry = await this.retrieve(key, { partition: options.partition }); if (!entry) { this.recordMetric('update-not-found', Date.now() - startTime); return false; } // Check access permissions if (!this.checkAccess(entry, 'write')) { throw new Error('Access denied for update operation'); } // Version check for optimistic locking if (options.version && entry.version !== options.version) { throw new Error('Version conflict: entry has been modified'); } // Update entry const partition = this.partitions.get(this.getEntryPartition(entry.id))!; entry.value = options.merge ? await this.mergeValues(entry.value, value, partition) : await this.processValue(value, partition); entry.updatedAt = new Date(); entry.version++; // Update cache this.updateCache(entry.id, entry); // Update vector clock this.incrementVectorClock(this.localNodeId); this.logger.debug('Updated entry', { entryId: entry.id, key }); this.emit('memory:entry-updated', { entry }); // Sync with other nodes if distributed if (this.config.distributed) { await this.syncEntryUpdate(entry); } this.recordMetric('update', Date.now() - startTime); return true; } catch (error) { this.recordMetric('update-error', Date.now() - startTime); throw error; } } async deleteEntry(entryId: string): Promise<boolean> { const startTime = Date.now(); try { const entry = this.entries.get(entryId); if (!entry) { this.recordMetric('delete-not-found', Date.now() - startTime); return false; } // Check access permissions if (!this.checkAccess(entry, 'delete')) { throw new Error('Access denied for delete operation'); } // Remove from partition const partitionId = this.getEntryPartition(entryId); const partition = this.partitions.get(partitionId); if (partition) { partition.entries = partition.entries.filter((e) => e.id !== entryId); } // Remove from storage this.entries.delete(entryId); // Remove from cache this.removeFromCache(entry.key); // Update vector clock this.incrementVectorClock(this.localNodeId); this.logger.debug('Deleted entry', { entryId, key: entry.key }); this.emit('memory:entry-deleted', { entryId }); // Sync with other nodes if distributed if (this.config.distributed) { await this.syncEntryDeletion(entryId); } this.recordMetric('delete', Date.now() - startTime); return true; } catch (error) { this.recordMetric('delete-error', Date.now() - startTime); throw error; } } // === QUERY OPERATIONS === async query(query: MemoryQuery): Promise<MemoryEntry[]> { const startTime = Date.now(); try { let results: MemoryEntry[] = []; // Get relevant partitions const partitions = query.partition ? [this.partitions.get(query.partition)].filter(Boolean) : Array.from(this.partitions.values()); for (const partition of partitions) { for (const entry of partition!.entries) { if (this.matchesQuery(entry, query)) { results.push(entry); } } } // Apply sorting if (query.sortBy) { results.sort((a, b) => { const aVal = this.getNestedProperty(a, query.sortBy!); const bVal = this.getNestedProperty(b, query.sortBy!); const order = query.sortOrder === 'desc' ? -1 : 1; if (aVal < bVal) return -1 * order; if (aVal > bVal) return 1 * order; return 0; }); } // Apply pagination const offset = query.offset || 0; const limit = query.limit || results.length; results = results.slice(offset, offset + limit); this.recordMetric('query', Date.now() - startTime); return results; } catch (error) { this.recordMetric('query-error', Date.now() - startTime); throw error; } } /** * Query entries by type */ async queryByType(type: string, namespace?: string): Promise<MemoryEntry[]> { return this.query({ filter: { type }, namespace, }); } // === SYNCHRONIZATION === private startSynchronization(): void { this.syncInterval = setInterval(() => { this.performSync(); }, this.config.syncInterval); this.logger.info('Started synchronization', { interval: this.config.syncInterval, consistency: this.config.consistency, }); } private async performSync(): Promise<void> { try { // Process pending sync operations await this.processSyncQueue(); // Send heartbeat to other nodes await this.sendHeartbeat(); // Check for conflicts and resolve them await this.detectAndResolveConflicts(); // Update statistics this.updateStatistics(); } catch (error) { this.logger.error('Sync error', error); } } private async processSyncQueue(): Promise<void> { const pendingOps = this.syncQueue.filter((op) => op.status === 'pending'); for (const operation of pendingOps) { try { operation.status = 'in_progress'; await this.executeSyncOperation(operation); operation.status = 'completed'; this.statistics.syncOperations.completed++; } catch (error) { operation.status = 'failed'; this.statistics.syncOperations.failed++; this.logger.error('Sync operation failed', { operation, error }); } } // Remove completed/failed operations older than 1 hour const cutoff = new Date(Date.now() - 3600000); this.syncQueue = this.syncQueue.filter( (op) => op.status === 'pending' || op.timestamp > cutoff, ); } // === UTILITY METHODS === private async processValue(value: any, partition: MemoryPartition): Promise<any> { if (partition.compressed && this.config.compressionEnabled) { return this.compressValue(value); } return value; } private async mergeValues( oldValue: any, newValue: any, partition: MemoryPartition, ): Promise<any> { // Simple merge strategy - can be enhanced if (typeof oldValue === 'object' && typeof newValue === 'object') { return { ...oldValue, ...newValue }; } return newValue; } private compressValue(value: any): any { // Placeholder for compression logic return value; } private checkAccess(entry: MemoryEntry, operation: 'read' | 'write' | 'delete'): boolean { // Simplified access control - can be enhanced return true; } private selectPartition(type: string): string { // Simple partition selection based on type for (const [id, partition] of this.partitions) { if (partition.type === type) { return id; } } // Default to first available partition return Array.from(this.partitions.keys())[0] || ''; } private getPartitionSize(partitionId: string): number { const partition = this.partitions.get(partitionId); if (!partition) return 0; return partition.entries.reduce((size, entry) => { return size + JSON.stringify(entry).length; }, 0); } private getEntryPartition(entryId: string): string { for (const [partitionId, partition] of this.partitions) { if (partition.entries.some((e) => e.id === entryId)) { return partitionId; } } return ''; } private updateCache(entryId: string, entry: MemoryEntry): void { if (this.cache.size >= this.config.cacheSize) { this.evictCache(); } this.cache.set(entry.key, { entry: { ...entry }, expiry: Date.now() + this.config.cacheTtl, }); } private getCachedEntry(key: string): { entry: MemoryEntry; expiry: number } | null { return this.cache.get(key) || null; } private isCacheValid(cached: { entry: MemoryEntry; expiry: number }): boolean { return cached.expiry > Date.now(); } private removeFromCache(key: string): void { this.cache.delete(key); } private evictCache(): void { // Simple LRU eviction - remove oldest entries const entries = Array.from(this.cache.entries()); entries.sort((a, b) => a[1].expiry - b[1].expiry); const toRemove = entries.slice(0, Math.floor(this.config.cacheSize * 0.1)); toRemove.forEach(([key]) => this.cache.delete(key)); } private async evictOldEntries(partitionId: string): Promise<void> { const partition = this.partitions.get(partitionId); if (!partition) return; // Sort by last access time and remove oldest 10% const entries = partition.entries.sort((a, b) => a.updatedAt.getTime() - b.updatedAt.getTime()); const toRemove = entries.slice(0, Math.floor(entries.length * 0.1)); for (const entry of toRemove) { await this.deleteEntry(entry.id); } } private matchesQuery(entry: MemoryEntry, query: MemoryQuery): boolean { if (query.type && entry.type !== query.type) return false; if (query.owner && entry.owner.id !== query.owner.id) return false; if (query.accessLevel && entry.accessLevel !== query.accessLevel) return false; if (query.createdAfter && entry.createdAt < query.createdAfter) return false; if (query.updatedAfter && entry.updatedAt < query.updatedAfter) return false; if (query.tags && query.tags.length > 0) { const hasAllTags = query.tags.every((tag) => entry.tags.includes(tag)); if (!hasAllTags) return false; } return true; } private getNestedProperty(obj: any, path: string): any { return path.split('.').reduce((current, key) => current?.[key], obj); } private incrementVectorClock(nodeId: string): void { const current = this.vectorClock.get(nodeId) || 0; this.vectorClock.set(nodeId, current + 1); } private recordMetric(operation: string, duration: number): void { const current = this.operationMetrics.get(operation) || { count: 0, totalTime: 0 }; current.count++; current.totalTime += duration; this.operationMetrics.set(operation, current); } private initializeStatistics(): MemoryStatistics { return { totalEntries: 0, totalSize: 0, partitionCount: 0, nodeCount: 1, replicationHealth: 1.0, syncOperations: { pending: 0, completed: 0, failed: 0, }, performance: { readLatency: 0, writeLatency: 0, syncLatency: 0, throughput: 0, }, utilization: { memoryUsage: 0, diskUsage: 0, networkUsage: 0, }, }; } private updateStatistics(): void { this.statistics.totalEntries = this.entries.size; this.statistics.partitionCount = this.partitions.size; this.statistics.nodeCount = this.nodes.size; // Calculate performance metrics const readMetrics = this.operationMetrics.get('retrieve') || { count: 0, totalTime: 0 }; const writeMetrics = this.operationMetrics.get('store') || { count: 0, totalTime: 0 }; this.statistics.performance.readLatency = readMetrics.count > 0 ? readMetrics.totalTime / readMetrics.count : 0; this.statistics.performance.writeLatency = writeMetrics.count > 0 ? writeMetrics.totalTime / writeMetrics.count : 0; } // === DISTRIBUTED OPERATIONS (Placeholders) === private async replicateEntry(entry: MemoryEntry): Promise<void> { // Implementation for replication to other nodes } private async syncPartitionCreation(partition: MemoryPartition): Promise<void> { // Implementation for syncing partition creation } private async syncPartitionDeletion(partitionId: string): Promise<void> { // Implementation for syncing partition deletion } private async syncEntryUpdate(entry: MemoryEntry): Promise<void> { // Implementation for syncing entry updates } private async syncEntryDeletion(entryId: string): Promise<void> { // Implementation for syncing entry deletion } private async retrieveFromRemote(key: string, options: any): Promise<MemoryEntry | null> { // Implementation for retrieving from remote nodes return null; } private async ensureConsistency(entry: MemoryEntry): Promise<MemoryEntry> { // Implementation for ensuring strong consistency return entry; } private async sendHeartbeat(): Promise<void> { // Implementation for sending heartbeat to other nodes } private async detectAndResolveConflicts(): Promise<void> { // Implementation for conflict detection and resolution } private async executeSyncOperation(operation: SyncOperation): Promise<void> { // Implementation for executing sync operations } private async completePendingSyncOperations(): Promise<void> { // Implementation for completing pending operations } private async loadPersistedData(): Promise<void> { // Implementation for loading persisted data } private async persistData(): Promise<void> { // Implementation for persisting data } private handleSyncRequest(data: any): void { // Handle sync requests from other nodes } private handleNodeJoined(data: any): void { // Handle new node joining } private handleNodeLeft(data: any): void { // Handle node leaving } private handleConflict(data: any): void { // Handle conflict resolution } // === PUBLIC API === getStatistics(): MemoryStatistics { this.updateStatistics(); return { ...this.statistics }; } getPartitions(): MemoryPartition[] { return Array.from(this.partitions.values()); } getNodes(): MemoryNode[] { return Array.from(this.nodes.values()); } async backup(): Promise<string> { // Create backup of all data const backup = { timestamp: new Date(), partitions: Array.from(this.partitions.values()), entries: Array.from(this.entries.values()), metadata: { version: '1.0', nodeId: this.localNodeId, config: this.config, }, }; return JSON.stringify(backup); } async restore(backupData: string): Promise<void> { // Restore from backup const backup = JSON.parse(backupData); // Clear current data this.partitions.clear(); this.entries.clear(); this.cache.clear(); // Restore partitions for (const partition of backup.partitions) { this.partitions.set(partition.id, partition); } // Restore entries for (const entry of backup.entries) { this.entries.set(entry.id, entry); } this.logger.info('Restored from backup', { partitions: backup.partitions.length, entries: backup.entries.length, }); } async clear(): Promise<void> { this.partitions.clear(); this.entries.clear(); this.cache.clear(); this.syncQueue = []; this.statistics = this.initializeStatistics(); this.logger.info('Cleared all memory data'); this.emit('memory:cleared'); } }