UNPKG

anon-identity

Version:

Decentralized identity framework with DIDs, Verifiable Credentials, and privacy-preserving selective disclosure

842 lines 32.5 kB
"use strict"; /** * Agent Matcher for MCP * * Embedding-based agent matching and capability discovery */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentMatcher = void 0; const events_1 = require("events"); const types_1 = require("../types"); /** * Agent Matcher */ class AgentMatcher extends events_1.EventEmitter { constructor(messageRouter, authManager, auditLogger, config = { embeddingModel: 'text-embedding-ada-002', similarityThreshold: 0.7, maxResults: 10, weightings: { capabilityMatch: 0.3, performance: 0.25, availability: 0.15, cost: 0.1, trust: 0.1, experience: 0.1 }, enableSemanticMatching: true, enableLearning: true, cacheEmbeddings: true }) { super(); this.messageRouter = messageRouter; this.authManager = authManager; this.auditLogger = auditLogger; this.config = config; this.agentProfiles = new Map(); this.taskEmbeddings = new Map(); this.agentEmbeddings = new Map(); this.matchHistory = []; this.semanticCache = new Map(); this.loadAgentProfiles(); } /** * Register agent profile */ async registerAgent(profile) { // Create temporary profile for embedding generation const tempProfile = { ...profile, embedding: [], // Will be replaced lastUpdated: new Date() }; // Generate embedding for agent capabilities const embedding = await this.generateAgentEmbedding(tempProfile); const fullProfile = { ...profile, embedding, lastUpdated: new Date() }; this.agentProfiles.set(profile.agentDID, fullProfile); this.agentEmbeddings.set(profile.agentDID, embedding); await this.saveAgentProfiles(); this.emit('agent_registered', fullProfile); } /** * Update agent profile */ async updateAgent(agentDID, updates) { const existing = this.agentProfiles.get(agentDID); if (!existing) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.INVALID_REQUEST, message: `Agent profile not found: ${agentDID}`, timestamp: new Date(), retryable: false }); } const updated = { ...existing, ...updates, lastUpdated: new Date() }; // Regenerate embedding if capabilities changed if (updates.capabilities || updates.expertise || updates.availableActions) { updated.embedding = await this.generateAgentEmbedding(updated); this.agentEmbeddings.set(agentDID, updated.embedding); } this.agentProfiles.set(agentDID, updated); await this.saveAgentProfiles(); this.emit('agent_updated', updated); } /** * Find matching agents for task */ async findMatches(task) { // Generate task embedding const taskEmbedding = await this.generateTaskEmbedding(task); task.embedding = taskEmbedding; this.taskEmbeddings.set(task.id, taskEmbedding); // Get all available agents const availableAgents = await this.getAvailableAgents(task); if (availableAgents.length === 0) { return []; } // Score and rank agents const matches = []; for (const agent of availableAgents) { const match = await this.scoreAgentMatch(agent, task); if (match.score >= this.config.similarityThreshold) { matches.push(match); } } // Sort by score (highest first) matches.sort((a, b) => b.score - a.score); // Apply learning adjustments if enabled if (this.config.enableLearning) { this.applyLearningAdjustments(matches, task); } // Return top results const results = matches.slice(0, this.config.maxResults); // Log matching request await this.auditLogger.logRequest({ id: `matching-${task.id}`, type: types_1.LLMRequestType.COMPLETION, prompt: `Find agents for task: ${task.title}`, agentDID: 'system-agent-matcher', sessionId: `matching-${Date.now()}`, metadata: { agentDID: 'system-agent-matcher', sessionId: `matching-${Date.now()}`, requestId: `matching-${task.id}`, timestamp: new Date(), source: 'agent-matcher', priority: types_1.RequestPriority.MEDIUM, taskId: task.id } }, 'system-agent-matcher', `matching-${Date.now()}`); this.emit('matches_found', { task, matches: results }); return results; } /** * Find semantically similar agents */ async findSimilarAgents(agentDID, options = {}) { const sourceAgent = this.agentProfiles.get(agentDID); if (!sourceAgent || !sourceAgent.embedding) { throw new types_1.MCPError({ code: types_1.MCPErrorCode.INVALID_REQUEST, message: `Agent not found or missing embedding: ${agentDID}`, timestamp: new Date(), retryable: false }); } const similarities = []; for (const [id, agent] of this.agentProfiles) { if (id === agentDID) continue; // Skip self if (!agent.embedding) continue; const similarity = this.calculateCosineSimilarity(sourceAgent.embedding, agent.embedding); if (similarity >= (options.minSimilarity || 0.5)) { similarities.push({ agent, similarity }); } } // Sort by similarity similarities.sort((a, b) => b.similarity - a.similarity); // Return top results return similarities.slice(0, options.maxResults || 10); } /** * Recommend agents based on context */ async recommendAgents(context, options = {}) { // Create synthetic task for recommendation const syntheticTask = { id: `recommendation-${Date.now()}`, title: `${context.domain} task`, description: `A ${context.complexity} ${context.domain} task`, requiredCapabilities: [context.domain], priority: context.urgency ? 'high' : 'medium', constraints: { maxCost: context.budget, requiredCertifications: context.requiredCertifications }, context: { domain: context.domain, urgency: context.urgency, complexity: context.complexity, estimatedDuration: this.estimateDurationByComplexity(context.complexity) } }; const matches = await this.findMatches(syntheticTask); // Apply diversity if requested if (options.diversityFactor && options.diversityFactor > 0) { return this.diversifyResults(matches, options.diversityFactor); } return matches.slice(0, options.maxResults || 5); } /** * Generate agent embedding */ async generateAgentEmbedding(agent) { if (!this.config.enableSemanticMatching) { return this.generateSimpleEmbedding(agent); } // Create text representation of agent capabilities const agentText = [ agent.description, ...agent.capabilities, ...agent.expertise, ...agent.availableActions ].join(' '); // Check cache first const cacheKey = `agent:${this.hashString(agentText)}`; if (this.config.cacheEmbeddings && this.semanticCache.has(cacheKey)) { return this.semanticCache.get(cacheKey); } // Generate embedding using LLM const embedding = await this.generateEmbedding(agentText); // Cache result if (this.config.cacheEmbeddings) { this.semanticCache.set(cacheKey, embedding); } return embedding; } /** * Generate task embedding */ async generateTaskEmbedding(task) { if (!this.config.enableSemanticMatching) { return this.generateSimpleTaskEmbedding(task); } // Create text representation of task requirements const taskText = [ task.title, task.description, ...task.requiredCapabilities, ...(task.preferredCapabilities || []), task.context.domain, task.context.complexity ].join(' '); // Check cache first const cacheKey = `task:${this.hashString(taskText)}`; if (this.config.cacheEmbeddings && this.semanticCache.has(cacheKey)) { return this.semanticCache.get(cacheKey); } // Generate embedding using LLM const embedding = await this.generateEmbedding(taskText); // Cache result if (this.config.cacheEmbeddings) { this.semanticCache.set(cacheKey, embedding); } return embedding; } /** * Generate embedding using LLM */ async generateEmbedding(text) { try { const request = { id: `embedding-${Date.now()}`, type: types_1.LLMRequestType.EMBEDDING, prompt: text, agentDID: 'system-agent-matcher', sessionId: `embedding-${Date.now()}`, parameters: { model: this.config.embeddingModel }, metadata: { agentDID: 'system-agent-matcher', sessionId: `embedding-${Date.now()}`, requestId: `embedding-${Date.now()}`, timestamp: new Date(), source: 'agent-matcher', priority: types_1.RequestPriority.LOW } }; const response = await this.messageRouter.routeMessage(request); if (response.embedding) { return response.embedding; } throw new Error('No embedding returned from LLM'); } catch (error) { console.warn('Failed to generate semantic embedding, falling back to simple embedding:', error); return this.generateSimpleEmbedding({ capabilities: [text] }); } } /** * Generate simple embedding (fallback) */ generateSimpleEmbedding(agent) { // Create a simple hash-based embedding const features = [ ...agent.capabilities, ...agent.expertise, ...agent.availableActions ]; const embedding = new Array(128).fill(0); for (const feature of features) { const hash = this.hashString(feature); for (let i = 0; i < embedding.length; i++) { embedding[i] += ((hash >> i) & 1) ? 1 : -1; } } // Normalize const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0)); return embedding.map(val => val / magnitude); } /** * Generate simple task embedding */ generateSimpleTaskEmbedding(task) { const features = [ ...task.requiredCapabilities, ...(task.preferredCapabilities || []), task.context.domain, task.context.complexity ]; const embedding = new Array(128).fill(0); for (const feature of features) { const hash = this.hashString(feature); for (let i = 0; i < embedding.length; i++) { embedding[i] += ((hash >> i) & 1) ? 1 : -1; } } // Normalize const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0)); return embedding.map(val => val / magnitude); } /** * Score agent match against task */ async scoreAgentMatch(agent, task) { // Calculate base similarity using embeddings let similarity = 0; if (agent.embedding && task.embedding) { similarity = this.calculateCosineSimilarity(agent.embedding, task.embedding); } // Calculate capability alignment const capabilityAlignment = this.calculateCapabilityAlignment(agent, task); // Calculate performance score const performanceScore = this.calculatePerformanceScore(agent, task); // Calculate availability score const availabilityScore = this.calculateAvailabilityScore(agent, task); // Calculate cost score const costScore = this.calculateCostScore(agent, task); // Calculate trust score const trustScore = agent.trustLevel; // Calculate experience score const experienceScore = this.calculateExperienceScore(agent, task); // Weighted total score const score = similarity * this.config.weightings.capabilityMatch + performanceScore * this.config.weightings.performance + availabilityScore * this.config.weightings.availability + costScore * this.config.weightings.cost + trustScore * this.config.weightings.trust + experienceScore * this.config.weightings.experience; // Calculate confidence based on data quality const confidence = this.calculateConfidence(agent, task, similarity); // Generate reasoning const reasoning = this.generateMatchReasoning(agent, task, score, capabilityAlignment, performanceScore); // Risk assessment const riskAssessment = this.assessRisk(agent, task); // Estimated metrics const estimatedMetrics = this.estimateTaskMetrics(agent, task); return { agent, score: Math.min(1, Math.max(0, score)), confidence, reasoning, capabilityAlignment, riskAssessment, estimatedMetrics }; } /** * Calculate capability alignment */ calculateCapabilityAlignment(agent, task) { const required = task.requiredCapabilities.map(capability => { const match = agent.capabilities.some(ac => ac.toLowerCase().includes(capability.toLowerCase()) || capability.toLowerCase().includes(ac.toLowerCase())); const strength = match ? this.calculateCapabilityStrength(agent, capability) : 0; return { capability, match, strength }; }); const preferred = (task.preferredCapabilities || []).map(capability => { const match = agent.capabilities.some(ac => ac.toLowerCase().includes(capability.toLowerCase()) || capability.toLowerCase().includes(ac.toLowerCase())); const strength = match ? this.calculateCapabilityStrength(agent, capability) : 0; return { capability, match, strength }; }); const additional = agent.capabilities .filter(capability => !task.requiredCapabilities.some(rc => rc.toLowerCase().includes(capability.toLowerCase())) && !(task.preferredCapabilities || []).some(pc => pc.toLowerCase().includes(capability.toLowerCase()))) .map(capability => ({ capability, strength: this.calculateCapabilityStrength(agent, capability) })); return { required, preferred, additional }; } /** * Calculate capability strength */ calculateCapabilityStrength(agent, capability) { // Check if it's in expertise (higher weight) if (agent.expertise.some(exp => exp.toLowerCase().includes(capability.toLowerCase()))) { return 0.9; } // Check if it's in available actions if (agent.availableActions.some(action => action.toLowerCase().includes(capability.toLowerCase()))) { return 0.7; } // Check if it's in general capabilities if (agent.capabilities.some(cap => cap.toLowerCase().includes(capability.toLowerCase()))) { return 0.6; } return 0; } /** * Calculate performance score */ calculatePerformanceScore(agent, task) { const performance = agent.performance; // Weighted performance score const responseTimeScore = Math.max(0, 1 - (performance.averageResponseTime / 10000)); // Normalize to 10s max const successScore = performance.successRate; const reliabilityScore = performance.reliability; const costScore = performance.costEfficiency; return (responseTimeScore + successScore + reliabilityScore + costScore) / 4; } /** * Calculate availability score */ calculateAvailabilityScore(agent, task) { // Check working hours if (agent.constraints.workingHours) { const now = new Date(); const currentHour = now.getHours(); const startHour = parseInt(agent.constraints.workingHours.start.split(':')[0]); const endHour = parseInt(agent.constraints.workingHours.end.split(':')[0]); if (currentHour < startHour || currentHour > endHour) { return 0.3; // Reduced availability outside working hours } } // Check concurrent task capacity // This would need to be tracked externally const estimatedUtilization = 0.5; // Mock value return Math.max(0, 1 - estimatedUtilization); } /** * Calculate cost score */ calculateCostScore(agent, task) { const estimatedCost = this.estimateTaskCost(agent, task); const maxCost = task.constraints.maxCost || 100; return Math.max(0, 1 - (estimatedCost / maxCost)); } /** * Calculate experience score */ calculateExperienceScore(agent, task) { // This would be based on historical performance in similar tasks // For now, use expertise alignment const domainExpertise = agent.expertise.some(exp => exp.toLowerCase().includes(task.context.domain.toLowerCase())); const complexityMatch = this.getComplexityScore(agent, task.context.complexity); return domainExpertise ? 0.8 + complexityMatch * 0.2 : complexityMatch; } /** * Get complexity score */ getComplexityScore(agent, complexity) { // This would ideally be based on historical data const complexityScores = { simple: 0.9, moderate: 0.7, complex: 0.5, expert: agent.expertise.length > 3 ? 0.8 : 0.3 }; return complexityScores[complexity] || 0.5; } /** * Calculate confidence */ calculateConfidence(agent, task, similarity) { // Base confidence on data quality and match strength let confidence = 0.5; // Boost confidence for strong similarity if (similarity > 0.8) confidence += 0.3; else if (similarity > 0.6) confidence += 0.2; else if (similarity > 0.4) confidence += 0.1; // Boost confidence for complete agent profile if (agent.performance.averageResponseTime > 0) confidence += 0.1; if (agent.trustLevel > 0.8) confidence += 0.1; if (agent.expertise.length > 0) confidence += 0.1; return Math.min(1, confidence); } /** * Generate match reasoning */ generateMatchReasoning(agent, task, score, capabilityAlignment, performanceScore) { const reasons = []; const requiredMatches = capabilityAlignment.required.filter(r => r.match).length; const requiredTotal = capabilityAlignment.required.length; reasons.push(`Matches ${requiredMatches}/${requiredTotal} required capabilities`); if (performanceScore > 0.8) { reasons.push('Excellent performance history'); } else if (performanceScore > 0.6) { reasons.push('Good performance history'); } if (agent.trustLevel > 0.8) { reasons.push('High trust level'); } const preferredMatches = capabilityAlignment.preferred.filter(p => p.match).length; if (preferredMatches > 0) { reasons.push(`Matches ${preferredMatches} preferred capabilities`); } return `Score: ${(score * 100).toFixed(1)}%. ${reasons.join(', ')}.`; } /** * Assess risk */ assessRisk(agent, task) { const factors = []; const mitigations = []; // Trust level risk if (agent.trustLevel < 0.5) { factors.push('Low trust level'); mitigations.push('Require additional oversight'); } // Performance risk if (agent.performance.successRate < 0.8) { factors.push('Below average success rate'); mitigations.push('Monitor progress closely'); } // Capability mismatch risk const requiredMatches = task.requiredCapabilities.filter(req => agent.capabilities.some(cap => cap.toLowerCase().includes(req.toLowerCase()))).length; if (requiredMatches < task.requiredCapabilities.length) { factors.push('Missing some required capabilities'); mitigations.push('Provide additional training or support'); } // Data classification risk if (task.constraints.dataClassification === 'restricted' && agent.trustLevel < 0.9) { factors.push('Restricted data access with moderate trust'); mitigations.push('Enhanced security monitoring'); } const level = factors.length === 0 ? 'low' : factors.length <= 2 ? 'medium' : 'high'; return { level, factors, mitigations }; } /** * Estimate task metrics */ estimateTaskMetrics(agent, task) { const cost = this.estimateTaskCost(agent, task); const duration = this.estimateTaskDuration(agent, task); const successProbability = Math.min(1, agent.performance.successRate + (agent.trustLevel - 0.5) * 0.2); // Adjust based on trust return { cost, duration, successProbability }; } /** * Estimate task cost */ estimateTaskCost(agent, task) { // Base cost calculation (simplified) const complexityMultiplier = { simple: 1, moderate: 2, complex: 4, expert: 8 }; const baseCost = 10; // Base cost units const complexity = complexityMultiplier[task.context.complexity] || 1; const efficiency = agent.performance.costEfficiency || 0.5; return baseCost * complexity / efficiency; } /** * Estimate task duration */ estimateTaskDuration(agent, task) { const baseTime = this.estimateDurationByComplexity(task.context.complexity); const efficiency = agent.performance.averageResponseTime / 1000; // Convert to seconds return Math.max(300, baseTime * (1 + efficiency / 10)); // Minimum 5 minutes } /** * Estimate duration by complexity */ estimateDurationByComplexity(complexity) { const durations = { simple: 1800, // 30 minutes moderate: 3600, // 1 hour complex: 7200, // 2 hours expert: 14400 // 4 hours }; return durations[complexity] || 3600; } /** * Get available agents */ async getAvailableAgents(task) { const available = []; for (const [, agent] of this.agentProfiles) { // Check trust level requirement if (task.constraints.minTrustLevel && agent.trustLevel < task.constraints.minTrustLevel) { continue; } // Check data restrictions if (this.hasDataRestrictions(agent, task)) { continue; } // Check certifications if (task.constraints.requiredCertifications) { // This would check against agent certifications // For now, assume all agents are certified } available.push(agent); } return available; } /** * Check data restrictions */ hasDataRestrictions(agent, task) { if (!task.constraints.dataClassification) return false; const restrictionLevel = { public: 0, internal: 1, confidential: 2, restricted: 3 }; const taskLevel = restrictionLevel[task.constraints.dataClassification] || 0; // Check if agent has sufficient clearance (simplified) const agentClearance = agent.trustLevel >= 0.9 ? 3 : agent.trustLevel >= 0.7 ? 2 : agent.trustLevel >= 0.5 ? 1 : 0; return agentClearance < taskLevel; } /** * Apply learning adjustments */ applyLearningAdjustments(matches, task) { if (!this.config.enableLearning) return; // Adjust scores based on historical performance for (const match of matches) { const history = this.matchHistory.filter(h => h.selectedAgent === match.agent.agentDID && h.outcome === 'success'); if (history.length > 0) { const successRate = history.length / this.matchHistory.filter(h => h.selectedAgent === match.agent.agentDID).length; // Boost score for agents with good track record match.score *= (0.8 + successRate * 0.2); match.score = Math.min(1, match.score); } } // Re-sort after adjustments matches.sort((a, b) => b.score - a.score); } /** * Diversify results */ diversifyResults(matches, diversityFactor) { if (matches.length <= 1) return matches; const diversified = [matches[0]]; // Always include top match const remaining = matches.slice(1); while (diversified.length < matches.length && remaining.length > 0) { let maxDiversity = -1; let bestIndex = 0; for (let i = 0; i < remaining.length; i++) { const candidate = remaining[i]; let minSimilarity = 1; // Find minimum similarity to already selected agents for (const selected of diversified) { if (candidate.agent.embedding && selected.agent.embedding) { const similarity = this.calculateCosineSimilarity(candidate.agent.embedding, selected.agent.embedding); minSimilarity = Math.min(minSimilarity, similarity); } } const diversity = 1 - minSimilarity; const combinedScore = candidate.score * (1 - diversityFactor) + diversity * diversityFactor; if (combinedScore > maxDiversity) { maxDiversity = combinedScore; bestIndex = i; } } diversified.push(remaining[bestIndex]); remaining.splice(bestIndex, 1); } return diversified; } /** * Calculate cosine similarity */ calculateCosineSimilarity(a, b) { if (a.length !== b.length) return 0; let dotProduct = 0; let normA = 0; let normB = 0; for (let i = 0; i < a.length; i++) { dotProduct += a[i] * b[i]; normA += a[i] * a[i]; normB += b[i] * b[i]; } const magnitude = Math.sqrt(normA) * Math.sqrt(normB); return magnitude === 0 ? 0 : dotProduct / magnitude; } /** * Hash string to number */ hashString(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } return Math.abs(hash); } /** * Record match outcome for learning */ async recordMatchOutcome(taskId, selectedAgentDID, outcome, actualMetrics) { const matchRecord = this.matchHistory.find(h => h.taskId === taskId); if (matchRecord) { matchRecord.selectedAgent = selectedAgentDID; matchRecord.outcome = outcome; matchRecord.actualMetrics = actualMetrics; } else { this.matchHistory.push({ taskId, matches: [], // Would be populated from original match selectedAgent: selectedAgentDID, outcome, actualMetrics, timestamp: new Date() }); } // Update agent performance based on outcome const agent = this.agentProfiles.get(selectedAgentDID); if (agent) { this.updateAgentPerformance(agent, outcome, actualMetrics); } this.emit('match_outcome_recorded', { taskId, selectedAgentDID, outcome, actualMetrics }); } /** * Update agent performance */ updateAgentPerformance(agent, outcome, actualMetrics) { // Simple running average update const alpha = 0.1; // Learning rate if (outcome === 'success') { agent.performance.successRate = agent.performance.successRate * (1 - alpha) + alpha; } else { agent.performance.successRate = agent.performance.successRate * (1 - alpha); } // Update other metrics (simplified) agent.performance.averageResponseTime = agent.performance.averageResponseTime * (1 - alpha) + actualMetrics.duration * alpha; agent.lastUpdated = new Date(); } /** * Load agent profiles */ async loadAgentProfiles() { // This would load from persistent storage // For now, create some mock profiles const mockProfiles = [ { agentDID: 'did:key:customer-service-bot', name: 'Customer Service Assistant', description: 'Specialized in customer support and service inquiries', capabilities: ['customer_service', 'chat_support', 'ticket_management'], expertise: ['customer_relations', 'problem_solving'], availableActions: ['answer_questions', 'create_tickets', 'escalate_issues'], performance: { averageResponseTime: 2000, successRate: 0.92, reliability: 0.95, costEfficiency: 0.8 }, constraints: { maxConcurrentTasks: 10, dataRestrictions: [], geographicLimitations: [] }, lastUpdated: new Date(), trustLevel: 0.85 } ]; for (const profile of mockProfiles) { await this.registerAgent(profile); } } /** * Save agent profiles */ async saveAgentProfiles() { // This would save to persistent storage // For now, just emit event this.emit('profiles_saved', Array.from(this.agentProfiles.values())); } /** * Get matching statistics */ getStatistics() { const totalMatches = this.matchHistory.length; const successfulMatches = this.matchHistory.filter(h => h.outcome === 'success').length; return { totalProfiles: this.agentProfiles.size, totalMatches, averageMatchScore: 0.75, // Would calculate from history successRate: totalMatches > 0 ? successfulMatches / totalMatches : 0, embeddingCacheSize: this.semanticCache.size }; } /** * Shutdown */ shutdown() { this.agentProfiles.clear(); this.taskEmbeddings.clear(); this.agentEmbeddings.clear(); this.semanticCache.clear(); this.removeAllListeners(); } } exports.AgentMatcher = AgentMatcher; exports.default = AgentMatcher; //# sourceMappingURL=agent-matcher.js.map