Skip to main content
Glama

documcp

by tosin2013
multi-agent-sharing.ts22.6 kB
/** * Multi-Agent Memory Sharing System for DocuMCP * Implements Issue #50: Multi-Agent Memory Sharing * * Enables multiple DocuMCP instances to share and synchronize memory, * creating a collaborative knowledge network for enhanced learning and recommendations. */ import { MemoryManager } from './manager.js'; import { MemoryEntry } from './storage.js'; import { EventEmitter } from 'events'; import * as crypto from 'crypto'; export interface AgentIdentity { id: string; name: string; version: string; capabilities: string[]; lastSeen: string; trustLevel: 'untrusted' | 'low' | 'medium' | 'high' | 'trusted'; specializations: string[]; } export interface SharedMemory { originalEntry: MemoryEntry; sharingMetadata: { sourceAgent: string; sharedAt: string; accessCount: number; trustScore: number; validatedBy: string[]; conflicts: string[]; }; transformations: Array<{ agentId: string; transformationType: 'anonymization' | 'aggregation' | 'enrichment' | 'validation'; appliedAt: string; details: Record<string, any>; }>; } export interface SyncRequest { id: string; fromAgent: string; toAgent: string; requestType: 'full_sync' | 'incremental' | 'selective' | 'validation'; criteria?: { types?: string[]; tags?: string[]; timeRange?: { start: string; end: string }; minTrustLevel?: number; }; requestedAt: string; status: 'pending' | 'in_progress' | 'completed' | 'failed'; } export interface ConflictResolution { conflictId: string; conflictType: 'duplicate' | 'contradiction' | 'version_mismatch' | 'trust_dispute'; involvedEntries: string[]; involvedAgents: string[]; resolutionStrategy: 'merge' | 'prioritize_trusted' | 'manual_review' | 'temporal_precedence'; resolvedAt?: string; resolution?: { action: string; resultingEntry?: MemoryEntry; metadata: Record<string, any>; }; } export interface CollaborativeInsight { id: string; type: 'trend' | 'pattern' | 'anomaly' | 'consensus' | 'disagreement'; description: string; evidence: string[]; contributingAgents: string[]; confidence: number; generatedAt: string; actionable: boolean; recommendations?: string[]; } export class MultiAgentMemorySharing extends EventEmitter { private memoryManager: MemoryManager; private agentId: string; private knownAgents: Map<string, AgentIdentity>; private sharedMemories: Map<string, SharedMemory>; private syncRequests: Map<string, SyncRequest>; private conflicts: Map<string, ConflictResolution>; private collaborativeInsights: Map<string, CollaborativeInsight>; private syncInterval: NodeJS.Timeout | null = null; constructor(memoryManager: MemoryManager, agentIdentity: Partial<AgentIdentity>) { super(); this.memoryManager = memoryManager; this.agentId = agentIdentity.id || this.generateAgentId(); this.knownAgents = new Map(); this.sharedMemories = new Map(); this.syncRequests = new Map(); this.conflicts = new Map(); this.collaborativeInsights = new Map(); // Register self this.registerAgent({ id: this.agentId, name: agentIdentity.name || 'DocuMCP Agent', version: agentIdentity.version || '1.0.0', capabilities: agentIdentity.capabilities || ['analysis', 'recommendation', 'deployment'], lastSeen: new Date().toISOString(), trustLevel: 'trusted', specializations: agentIdentity.specializations || [], }); } /** * Initialize multi-agent sharing */ async initialize(): Promise<void> { await this.loadSharedMemories(); await this.loadKnownAgents(); await this.loadPendingSyncRequests(); // Start periodic sync this.startPeriodicSync(); this.emit('initialized', { agentId: this.agentId }); } /** * Register a new agent in the network */ async registerAgent(agent: AgentIdentity): Promise<void> { this.knownAgents.set(agent.id, { ...agent, lastSeen: new Date().toISOString(), }); await this.persistAgentRegistry(); this.emit('agent-registered', agent); } /** * Share a memory entry with other agents */ async shareMemory( memoryId: string, targetAgents?: string[], options?: { anonymize?: boolean; requireValidation?: boolean; trustLevel?: number; }, ): Promise<SharedMemory> { const memory = await this.memoryManager.recall(memoryId); if (!memory) { throw new Error(`Memory ${memoryId} not found`); } // Create shared memory wrapper const sharedMemory: SharedMemory = { originalEntry: this.anonymizeIfRequired(memory, options?.anonymize), sharingMetadata: { sourceAgent: this.agentId, sharedAt: new Date().toISOString(), accessCount: 0, trustScore: this.calculateInitialTrustScore(memory), validatedBy: [], conflicts: [], }, transformations: [], }; // Apply anonymization transformation if required if (options?.anonymize) { sharedMemory.transformations.push({ agentId: this.agentId, transformationType: 'anonymization', appliedAt: new Date().toISOString(), details: { level: 'standard', preserveStructure: true }, }); } this.sharedMemories.set(memoryId, sharedMemory); // Create sync requests for target agents if (targetAgents) { for (const targetAgent of targetAgents) { await this.createSyncRequest(targetAgent, 'selective', { memoryIds: [memoryId], }); } } else { // Broadcast to all trusted agents await this.broadcastToTrustedAgents(sharedMemory); } await this.persistSharedMemories(); this.emit('memory-shared', { memoryId, sharedMemory }); return sharedMemory; } /** * Receive shared memory from another agent */ async receiveSharedMemory( sharedMemory: SharedMemory, sourceAgent: string, ): Promise<{ accepted: boolean; conflicts?: ConflictResolution[]; integrationResult?: string; }> { // Validate source agent trust level const sourceAgentInfo = this.knownAgents.get(sourceAgent); if (!sourceAgentInfo || sourceAgentInfo.trustLevel === 'untrusted') { return { accepted: false }; } // Check for conflicts with existing memories const conflicts = await this.detectConflicts(sharedMemory); if (conflicts.length > 0) { // Store conflicts for resolution for (const conflict of conflicts) { this.conflicts.set(conflict.conflictId, conflict); await this.resolveConflict(conflict); } this.emit('conflict-detected', { conflicts, sharedMemory }); } // Integrate the shared memory const integrationResult = await this.integrateSharedMemory(sharedMemory, sourceAgent); // Update sharing metadata sharedMemory.sharingMetadata.accessCount++; this.emit('memory-received', { sharedMemory, sourceAgent, integrationResult }); return { accepted: true, conflicts: conflicts.length > 0 ? conflicts : undefined, integrationResult, }; } /** * Request synchronization with another agent */ async requestSync( targetAgent: string, syncType: SyncRequest['requestType'] = 'incremental', criteria?: SyncRequest['criteria'], ): Promise<SyncRequest> { const syncRequest: SyncRequest = { id: this.generateSyncId(), fromAgent: this.agentId, toAgent: targetAgent, requestType: syncType, criteria, requestedAt: new Date().toISOString(), status: 'pending', }; this.syncRequests.set(syncRequest.id, syncRequest); await this.persistSyncRequests(); this.emit('sync-requested', syncRequest); return syncRequest; } /** * Process incoming sync request */ async processSyncRequest(syncRequest: SyncRequest): Promise<{ approved: boolean; memories?: SharedMemory[]; reason?: string; }> { // Validate requesting agent const requestingAgent = this.knownAgents.get(syncRequest.fromAgent); if (!requestingAgent || requestingAgent.trustLevel === 'untrusted') { return { approved: false, reason: 'Untrusted agent' }; } // Update request status syncRequest.status = 'in_progress'; this.syncRequests.set(syncRequest.id, syncRequest); try { // Get memories based on sync type and criteria const memories = await this.getMemoriesForSync(syncRequest); syncRequest.status = 'completed'; this.emit('sync-completed', { syncRequest, memoriesCount: memories.length }); return { approved: true, memories }; } catch (error) { syncRequest.status = 'failed'; this.emit('sync-failed', { syncRequest, error }); return { approved: false, reason: error instanceof Error ? error.message : 'Unknown error' }; } } /** * Generate collaborative insights from shared memories */ async generateCollaborativeInsights(): Promise<CollaborativeInsight[]> { const insights: CollaborativeInsight[] = []; // Analyze trends across agents const trends = await this.analyzeTrends(); insights.push(...trends); // Find consensus patterns const consensus = await this.findConsensusPatterns(); insights.push(...consensus); // Identify disagreements that need attention const disagreements = await this.identifyDisagreements(); insights.push(...disagreements); // Detect anomalies const anomalies = await this.detectAnomalies(); insights.push(...anomalies); // Store insights for (const insight of insights) { this.collaborativeInsights.set(insight.id, insight); } await this.persistCollaborativeInsights(); this.emit('insights-generated', { count: insights.length }); return insights; } /** * Validate shared memory against local knowledge */ async validateSharedMemory(sharedMemory: SharedMemory): Promise<{ isValid: boolean; confidence: number; issues: string[]; recommendations: string[]; }> { const issues: string[] = []; const recommendations: string[] = []; let confidence = 1.0; // Check data consistency if (!this.validateDataStructure(sharedMemory.originalEntry)) { issues.push('Invalid data structure'); confidence *= 0.7; } // Cross-validate with local memories const similarMemories = await this.findSimilarLocalMemories(sharedMemory.originalEntry); if (similarMemories.length > 0) { const consistencyScore = this.calculateConsistency( sharedMemory.originalEntry, similarMemories, ); if (consistencyScore < 0.8) { issues.push('Inconsistent with local knowledge'); confidence *= consistencyScore; } } // Check source agent reliability const sourceAgent = this.knownAgents.get(sharedMemory.sharingMetadata.sourceAgent); if (sourceAgent) { if (sourceAgent.trustLevel === 'low') { confidence *= 0.8; recommendations.push('Verify with additional sources'); } else if (sourceAgent.trustLevel === 'high' || sourceAgent.trustLevel === 'trusted') { confidence *= 1.1; } } // Validate transformations for (const transformation of sharedMemory.transformations) { if (transformation.transformationType === 'anonymization') { // Check if anonymization preserved essential information if (!this.validateAnonymization(transformation)) { issues.push('Anonymization may have removed critical information'); confidence *= 0.9; } } } return { isValid: issues.length === 0 || confidence > 0.6, confidence: Math.min(confidence, 1.0), issues, recommendations, }; } /** * Get network statistics */ getNetworkStatistics(): { connectedAgents: number; sharedMemories: number; activeSyncs: number; resolvedConflicts: number; trustDistribution: Record<string, number>; collaborativeInsights: number; networkHealth: number; } { const trustDistribution: Record<string, number> = {}; for (const agent of this.knownAgents.values()) { trustDistribution[agent.trustLevel] = (trustDistribution[agent.trustLevel] || 0) + 1; } const activeSyncs = Array.from(this.syncRequests.values()).filter( (req) => req.status === 'pending' || req.status === 'in_progress', ).length; const resolvedConflicts = Array.from(this.conflicts.values()).filter( (conflict) => conflict.resolvedAt, ).length; // Calculate network health (0-1) const trustedAgents = Array.from(this.knownAgents.values()).filter( (agent) => agent.trustLevel === 'high' || agent.trustLevel === 'trusted', ).length; const totalAgents = this.knownAgents.size; const networkHealth = totalAgents > 0 ? trustedAgents / totalAgents : 0; return { connectedAgents: this.knownAgents.size, sharedMemories: this.sharedMemories.size, activeSyncs, resolvedConflicts, trustDistribution, collaborativeInsights: this.collaborativeInsights.size, networkHealth, }; } // Private helper methods private generateAgentId(): string { return `agent_${crypto.randomBytes(8).toString('hex')}`; } private generateSyncId(): string { return `sync_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`; } private anonymizeIfRequired(memory: MemoryEntry, anonymize?: boolean): MemoryEntry { if (!anonymize) return memory; // Create anonymized copy const anonymized = JSON.parse(JSON.stringify(memory)); // Remove/hash sensitive information if (anonymized.metadata.repository) { anonymized.metadata.repository = this.hashSensitiveData(anonymized.metadata.repository); } if (anonymized.metadata.projectId) { anonymized.metadata.projectId = this.hashSensitiveData(anonymized.metadata.projectId); } // Remove file paths and specific identifiers if (anonymized.data.files) { delete anonymized.data.files; } return anonymized; } private hashSensitiveData(data: string): string { return crypto.createHash('sha256').update(data).digest('hex').substring(0, 16); } private calculateInitialTrustScore(memory: MemoryEntry): number { let score = 0.5; // Base score // Boost for successful outcomes if (memory.data.status === 'success') score += 0.2; // Boost for rich metadata if (memory.metadata.tags && memory.metadata.tags.length > 2) score += 0.1; // Boost for recent data const daysSince = (Date.now() - new Date(memory.timestamp).getTime()) / (1000 * 60 * 60 * 24); if (daysSince <= 30) score += 0.1; // Boost for complete data if (memory.checksum) score += 0.1; return Math.min(score, 1.0); } private async detectConflicts(sharedMemory: SharedMemory): Promise<ConflictResolution[]> { const conflicts: ConflictResolution[] = []; // Check for duplicates const similarLocal = await this.findSimilarLocalMemories(sharedMemory.originalEntry); for (const similar of similarLocal) { if (this.isLikelyDuplicate(sharedMemory.originalEntry, similar)) { conflicts.push({ conflictId: `conflict_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`, conflictType: 'duplicate', involvedEntries: [sharedMemory.originalEntry.id, similar.id], involvedAgents: [sharedMemory.sharingMetadata.sourceAgent, this.agentId], resolutionStrategy: 'merge', }); } } return conflicts; } private async resolveConflict(conflict: ConflictResolution): Promise<void> { // Implement conflict resolution based on strategy switch (conflict.resolutionStrategy) { case 'merge': await this.mergeConflictingEntries(conflict); break; case 'prioritize_trusted': await this.prioritizeTrustedSource(conflict); break; case 'temporal_precedence': await this.useTemporalPrecedence(conflict); break; default: // Mark for manual review conflict.resolutionStrategy = 'manual_review'; } conflict.resolvedAt = new Date().toISOString(); this.conflicts.set(conflict.conflictId, conflict); } private async integrateSharedMemory( sharedMemory: SharedMemory, sourceAgent: string, ): Promise<string> { // Add transformation for integration sharedMemory.transformations.push({ agentId: this.agentId, transformationType: 'enrichment', appliedAt: new Date().toISOString(), details: { integratedFrom: sourceAgent }, }); // Store in local memory with special metadata const enrichedEntry = { ...sharedMemory.originalEntry, metadata: { ...sharedMemory.originalEntry.metadata, sharedFrom: sourceAgent, integratedAt: new Date().toISOString(), tags: [...(sharedMemory.originalEntry.metadata.tags || []), 'shared', 'collaborative'], }, }; await this.memoryManager.remember( enrichedEntry.type, enrichedEntry.data, enrichedEntry.metadata, ); return 'integrated_successfully'; } private async getMemoriesForSync(syncRequest: SyncRequest): Promise<SharedMemory[]> { const allMemories = await this.memoryManager.search('', { sortBy: 'timestamp' }); let filteredMemories = allMemories; // Apply criteria filtering if (syncRequest.criteria) { if (syncRequest.criteria.types) { filteredMemories = filteredMemories.filter((m) => syncRequest.criteria!.types!.includes(m.type), ); } if (syncRequest.criteria.tags) { filteredMemories = filteredMemories.filter( (m) => m.metadata.tags?.some((tag) => syncRequest.criteria!.tags!.includes(tag)), ); } if (syncRequest.criteria.timeRange) { const start = new Date(syncRequest.criteria.timeRange.start); const end = new Date(syncRequest.criteria.timeRange.end); filteredMemories = filteredMemories.filter((m) => { const memTime = new Date(m.timestamp); return memTime >= start && memTime <= end; }); } } // Convert to shared memories return filteredMemories.map((memory) => ({ originalEntry: memory, sharingMetadata: { sourceAgent: this.agentId, sharedAt: new Date().toISOString(), accessCount: 0, trustScore: this.calculateInitialTrustScore(memory), validatedBy: [], conflicts: [], }, transformations: [], })); } private async analyzeTrends(): Promise<CollaborativeInsight[]> { // Analyze shared memories to identify trends return []; // Placeholder implementation } private async findConsensusPatterns(): Promise<CollaborativeInsight[]> { // Find patterns where multiple agents agree return []; // Placeholder implementation } private async identifyDisagreements(): Promise<CollaborativeInsight[]> { // Find areas where agents disagree return []; // Placeholder implementation } private async detectAnomalies(): Promise<CollaborativeInsight[]> { // Detect unusual patterns in shared data return []; // Placeholder implementation } private validateDataStructure(entry: MemoryEntry): boolean { return Boolean(entry.id && entry.timestamp && entry.type && entry.data); } private async findSimilarLocalMemories(entry: MemoryEntry): Promise<MemoryEntry[]> { // Find similar memories in local storage return this.memoryManager.search(entry.metadata.projectId || '', { sortBy: 'timestamp' }); } private calculateConsistency(_entry: MemoryEntry, _similar: MemoryEntry[]): number { // Calculate consistency score (placeholder) return 0.8; } private validateAnonymization(_transformation: any): boolean { // Validate anonymization transformation (placeholder) return true; } private isLikelyDuplicate(entry1: MemoryEntry, entry2: MemoryEntry): boolean { // Simple duplicate detection return ( entry1.type === entry2.type && entry1.metadata.projectId === entry2.metadata.projectId && Math.abs(new Date(entry1.timestamp).getTime() - new Date(entry2.timestamp).getTime()) < 60000 ); // 1 minute } private async mergeConflictingEntries(_conflict: ConflictResolution): Promise<void> { // Merge conflicting entries (placeholder) } private async prioritizeTrustedSource(_conflict: ConflictResolution): Promise<void> { // Prioritize trusted source (placeholder) } private async useTemporalPrecedence(_conflict: ConflictResolution): Promise<void> { // Use temporal precedence (placeholder) } private async broadcastToTrustedAgents(_sharedMemory: SharedMemory): Promise<void> { // Broadcast to trusted agents (placeholder) } private startPeriodicSync(): void { this.syncInterval = setInterval( async () => { await this.performPeriodicSync(); }, 5 * 60 * 1000, ); // Every 5 minutes } private async performPeriodicSync(): Promise<void> { // Perform periodic synchronization with trusted agents } private async loadSharedMemories(): Promise<void> { // Load shared memories from persistence } private async loadKnownAgents(): Promise<void> { // Load known agents from persistence } private async loadPendingSyncRequests(): Promise<void> { // Load pending sync requests from persistence } private async persistSharedMemories(): Promise<void> { // Persist shared memories } private async persistAgentRegistry(): Promise<void> { // Persist agent registry } private async persistSyncRequests(): Promise<void> { // Persist sync requests } private async persistCollaborativeInsights(): Promise<void> { // Persist collaborative insights } private async createSyncRequest( _targetAgent: string, _type: SyncRequest['requestType'], _options: any, ): Promise<void> { // Create sync request (placeholder) } /** * Cleanup and shutdown */ async shutdown(): Promise<void> { if (this.syncInterval) { clearInterval(this.syncInterval); } await this.persistSharedMemories(); await this.persistAgentRegistry(); await this.persistSyncRequests(); await this.persistCollaborativeInsights(); this.emit('shutdown', { agentId: this.agentId }); } } export default MultiAgentMemorySharing;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/documcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server