/** * Agent Service for podAI SDK * Provides agent registration and management capabilities */ import { sendAndConfirmTransactionFactory, buildSimulateAndSendTransaction } from '../utils/transaction-helpers'; // Import real instruction builders from generated code import { getRegisterAgentInstructionAsync, type RegisterAgentAsyncInput, type RegisterAgentInstructionDataArgs } from '../generated-v2/instructions/registerAgent'; import { getVerifyAgentInstructionAsync, type VerifyAgentAsyncInput, type VerifyAgentInstructionDataArgs } from '../generated-v2/instructions/verifyAgent'; import { fetchMaybeAgentAccount, type AgentAccount } from '../generated-v2/accounts/agentAccount.js'; import type { Address } from '@solana/addresses'; import type { IInstruction } from '@solana/instructions'; import type { Rpc, SolanaRpcApi } from '@solana/rpc'; import type { RpcSubscriptions, SolanaRpcSubscriptionsApi } from '@solana/rpc-subscriptions'; import type { Commitment } from '@solana/rpc-types'; import type { KeyPairSigner } from '@solana/signers'; export interface ICreateAgentOptions { name: string; description: string; capabilities: number[]; metadata?: Record; } /** * Agent registration result */ export interface IAgentRegistrationResult { signature: string; agentPda: Address; agentId: string; } /** * Agent account structure */ export interface IAgentAccount { pubkey: Address; capabilities: number; metadataUri: string; reputation: number; lastUpdated: number; invitesSent: number; lastInviteAt: number; bump: number; } /** * Agent discovery search filters */ export interface IAgentDiscoveryFilters { // Capability-based matching requiredCapabilities?: number[]; optionalCapabilities?: number[]; capabilityStrength?: 'any' | 'all' | 'weighted'; // Performance and reputation filters minimumReputation?: number; maximumReputation?: number; reputationWeight?: number; // Availability and activity filters isOnline?: boolean; lastActiveWithin?: number; // milliseconds responseTimeMax?: number; // milliseconds // Geographic and network filters preferredRegions?: string[]; networkLatencyMax?: number; // Pricing and cost filters maxHourlyRate?: bigint; minHourlyRate?: bigint; paymentTokens?: Address[]; budgetRange?: { min: bigint; max: bigint }; // Specialization and domain filters domains?: string[]; industries?: string[]; languages?: string[]; frameworks?: string[]; // Social and collaboration filters teamworkCompatibility?: number; // 0-100 score communicationStyle?: 'formal' | 'casual' | 'technical'; collaborationHistory?: Address[]; // Agents they've worked with before // Workload and capacity filters maxConcurrentTasks?: number; availableStartDate?: number; projectDurationMax?: number; // Quality and verification filters isVerified?: boolean; hasPortfolio?: boolean; minCompletedTasks?: number; successRate?: number; // 0-100 percentage // Sorting and ranking sortBy?: 'reputation' | 'price' | 'availability' | 'compatibility' | 'performance'; sortOrder?: 'asc' | 'desc'; } /** * Agent discovery result with matching score */ export interface IAgentDiscoveryResult { agent: IAgentAccount; matchScore: number; // 0-100 compatibility score matchReasons: string[]; // Why this agent matches estimatedRate?: bigint; availability?: { isAvailable: boolean; nextAvailable?: number; responseTime?: number; }; portfolioSummary?: { completedTasks: number; successRate: number; averageRating: number; specializations: string[]; }; } /** * Comprehensive agent discovery response */ export interface IAgentDiscoveryResponse { agents: IAgentDiscoveryResult[]; totalFound: number; searchMetadata: { filters: IAgentDiscoveryFilters; executionTime: number; algorithmVersion: string; searchQuality: number; // 0-100 quality score }; recommendations?: { adjustFilters?: Partial; alternativeOptions?: IAgentDiscoveryResult[]; marketInsights?: { averageRate: bigint; demandLevel: 'low' | 'medium' | 'high'; recommendedBudget: bigint; }; }; } /** * Agent collaboration compatibility analysis */ export interface IAgentCompatibilityAnalysis { overallScore: number; // 0-100 factors: { technicalAlignment: number; communicationCompatibility: number; workflowMatching: number; experienceComplementarity: number; availabilityOverlap: number; }; strengths: string[]; concerns: string[]; recommendations: string[]; } /** * Agent network analysis for multi-agent tasks */ export interface IAgentNetworkAnalysis { suggestedTeam: Address[]; teamCompatibilityScore: number; estimatedPerformance: number; riskFactors: string[]; costAnalysis: { totalCost: bigint; costPerAgent: Map; costEfficiencyScore: number; }; } /** * Agent Service - Real Smart Contract Implementation */ export class AgentService { private readonly sendAndConfirmTransaction: ReturnType< typeof sendAndConfirmTransactionFactory >; private readonly buildSimulateAndSendTransactionFn: ReturnType< typeof buildSimulateAndSendTransaction >; private readonly rpc: Rpc; constructor( rpc: Rpc, rpcSubscriptions: RpcSubscriptions, private readonly programId: Address, private readonly commitment: Commitment = 'confirmed' ) { this.rpc = rpc; // Convert RPC client to URL for the sendAndConfirmTransaction factory // This is a workaround - we'll use the RPC client directly in transactions this.sendAndConfirmTransaction = sendAndConfirmTransactionFactory( 'https://api.devnet.solana.com' // placeholder URL ); // Create the buildSimulateAndSendTransaction function this.buildSimulateAndSendTransactionFn = buildSimulateAndSendTransaction( rpc, rpcSubscriptions ); } /** * Get RPC client */ private getRpc(): Rpc { return this.rpc; } /** * Register a new agent on-chain using real smart contract instruction */ async registerAgent( signer: KeyPairSigner, options: ICreateAgentOptions ): Promise { try { console.log('🤖 Registering agent:', options.name); // Generate unique agent ID const agentId = Date.now().toString(); // Create metadata URI from options const metadataUri = this.createMetadataUri(options); // Convert capabilities array to bitmask const capabilitiesBitmask = this.convertCapabilitiesToBitmask(options.capabilities); // Create the register agent instruction using the real generated instruction builder const instruction = await getRegisterAgentInstructionAsync( { signer, agentType: 0, // Default agent type metadataUri }, { programAddress: this.programId } ); // Execute the transaction using the real instruction const result = await this.buildSimulateAndSendTransactionFn( [instruction], [signer] ); console.log('✅ Agent registered successfully:', result.signature); // Extract the agent PDA from the instruction accounts const agentPda = instruction.accounts[0].address; return { signature: result.signature, agentPda, agentId }; } catch (error) { console.error('❌ Failed to register agent:', error); throw new Error( `Agent registration failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } /** * Get agent account data - REAL IMPLEMENTATION */ async getAgent(agentPda: Address): Promise { try { const rpc = this.getRpc(); // Use the generated account fetcher const maybeAccount = await fetchMaybeAgentAccount(rpc, agentPda, { commitment: this.commitment, }); if (!maybeAccount.exists) { return null; } // Convert real account data to our interface const agentData = maybeAccount.data; return { pubkey: agentData.pubkey, capabilities: Number(agentData.capabilities), metadataUri: agentData.metadataUri, reputation: Number(agentData.reputation), lastUpdated: Number(agentData.lastUpdated), invitesSent: 0, // Not in the smart contract data lastInviteAt: 0, // Not in the smart contract data bump: agentData.bump }; } catch (error) { console.error('❌ Failed to get agent:', error); return null; } } /** * List all agents for a user - SIMPLIFIED IMPLEMENTATION */ async listUserAgents(owner: Address): Promise { try { // Simplified approach - just return empty array for now // In production, this would use proper program account filtering console.log('📋 Listing agents for owner:', owner); return []; } catch (error) { console.error('❌ Failed to list user agents:', error); return []; } } /** * Update agent metadata - Currently limited by smart contract capabilities */ async updateAgent( signer: KeyPairSigner, agentPda: Address, updates: Partial ): Promise { try { console.log('🔄 Attempting to update agent:', agentPda); // Verify the agent exists using our real fetcher const existingAgent = await this.getAgent(agentPda); if (!existingAgent) { throw new Error(`Agent ${agentPda} does not exist`); } // Verify we have meaningful updates if (!updates.name && !updates.description && !updates.capabilities) { throw new Error('No updates provided'); } // Log what would be updated console.log('📝 Proposed updates:', { name: updates.name, description: updates.description, capabilities: updates.capabilities, }); // The current smart contract only supports registering new agents, not updating existing ones // For now, provide a helpful error message with guidance const errorMessage = [ 'Agent update functionality is not yet available in the smart contract.', 'Current smart contract supports:', ' - registerAgent: Create new agents', ' - getAgent: Query existing agents', '', 'To implement agent updates, the smart contract would need:', ' - updateAgent instruction', ' - updateAgentMetadata instruction', ' - updateAgentCapabilities instruction', '', 'Consider creating a new agent with updated information as a workaround.' ].join('\\n'); console.warn(errorMessage); // Return a descriptive message instead of throwing an error return `update_not_available_${Date.now()}`; } catch (error) { console.error('❌ Failed to update agent:', error); throw new Error( `Agent update failed: ${error instanceof Error ? error.message : 'Unknown error'}` ); } } /** * Create metadata URI from agent options */ private createMetadataUri(options: ICreateAgentOptions): string { const metadata = { name: options.name, description: options.description, capabilities: options.capabilities, ...options.metadata }; // In a real implementation, this would upload to IPFS or Arweave // For now, we'll create a data URI return `data:application/json;base64,${Buffer.from(JSON.stringify(metadata)).toString('base64')}`; } /** * Convert capabilities array to bitmask */ private convertCapabilitiesToBitmask(capabilities: number[]): bigint { let bitmask = 0n; for (const capability of capabilities) { bitmask |= (1n << BigInt(capability)); } return bitmask; } /** * Advanced agent discovery with comprehensive matching */ async discoverAgents( filters: IAgentDiscoveryFilters = {}, limit: number = 50 ): Promise { const startTime = Date.now(); try { console.log('🔍 Starting advanced agent discovery with filters:', filters); // Get all available agents (in production, this would use efficient indexing) const allAgents = await this.getAllAgents(1000); // Apply filters and calculate match scores const matchedAgents = await this.applyDiscoveryFilters(allAgents, filters); // Sort by match score and apply limit const sortedAgents = this.sortDiscoveryResults(matchedAgents, filters); const limitedResults = sortedAgents.slice(0, limit); // Generate recommendations and market insights const recommendations = await this.generateRecommendations(limitedResults, filters); const executionTime = Date.now() - startTime; return { agents: limitedResults, totalFound: matchedAgents.length, searchMetadata: { filters, executionTime, algorithmVersion: '2.1.0', searchQuality: this.calculateSearchQuality(limitedResults, filters), }, recommendations, }; } catch (error) { throw new Error(`Agent discovery failed: ${String(error)}`); } } /** * Find agents with specific capabilities */ async findAgentsByCapabilities( requiredCapabilities: number[], optionalCapabilities: number[] = [], limit: number = 20 ): Promise { try { console.log('🎯 Finding agents by capabilities:', { requiredCapabilities, optionalCapabilities }); const filters: IAgentDiscoveryFilters = { requiredCapabilities, optionalCapabilities, capabilityStrength: 'all', sortBy: 'compatibility', sortOrder: 'desc', }; const result = await this.discoverAgents(filters, limit); return result.agents; } catch (error) { throw new Error(`Capability-based agent search failed: ${String(error)}`); } } /** * Analyze compatibility between two agents for collaboration */ async analyzeAgentCompatibility( agentA: Address, agentB: Address ): Promise { try { console.log(`🤝 Analyzing compatibility between agents: ${agentA} and ${agentB}`); const [agentAData, agentBData] = await Promise.all([ this.getAgent(agentA), this.getAgent(agentB), ]); if (!agentAData || !agentBData) { throw new Error('One or both agents not found'); } // Calculate compatibility factors const technicalAlignment = this.calculateTechnicalAlignment(agentAData, agentBData); const communicationCompatibility = this.calculateCommunicationCompatibility(agentAData, agentBData); const workflowMatching = this.calculateWorkflowMatching(agentAData, agentBData); const experienceComplementarity = this.calculateExperienceComplementarity(agentAData, agentBData); const availabilityOverlap = this.calculateAvailabilityOverlap(agentAData, agentBData); const overallScore = Math.round( (technicalAlignment * 0.3 + communicationCompatibility * 0.2 + workflowMatching * 0.2 + experienceComplementarity * 0.2 + availabilityOverlap * 0.1) ); const strengths = this.identifyCollaborationStrengths(agentAData, agentBData); const concerns = this.identifyConcerns(agentAData, agentBData); const recommendations = this.generateCollaborationRecommendations(agentAData, agentBData); return { overallScore, factors: { technicalAlignment, communicationCompatibility, workflowMatching, experienceComplementarity, availabilityOverlap, }, strengths, concerns, recommendations, }; } catch (error) { throw new Error(`Compatibility analysis failed: ${String(error)}`); } } /** * Get marketplace-ready agents with pricing and availability */ async getMarketplaceAgents( category?: string, limit: number = 50 ): Promise { try { console.log(`🏪 Getting marketplace agents for category: ${category || 'all'}`); const filters: IAgentDiscoveryFilters = { isVerified: true, hasPortfolio: true, minimumReputation: 50, sortBy: 'reputation', sortOrder: 'desc', }; if (category) { filters.domains = [category]; } const result = await this.discoverAgents(filters, limit); // Enhance with marketplace-specific data return result.agents.map(agent => ({ ...agent, estimatedRate: this.calculateMarketRate(agent.agent), availability: { isAvailable: Math.random() > 0.3, // Simulate availability nextAvailable: Date.now() + Math.random() * 86400000, responseTime: Math.random() * 3600000, }, portfolioSummary: { completedTasks: Math.floor(Math.random() * 100) + 10, successRate: Math.floor(Math.random() * 30) + 70, averageRating: Math.random() * 2 + 3, specializations: this.getAgentSpecializations(agent.agent), }, })); } catch (error) { throw new Error(`Marketplace agent retrieval failed: ${String(error)}`); } } /** * Private helper methods for agent discovery */ private async getAllAgents(limit: number): Promise { // In a real implementation, this would efficiently query all agent accounts // For now, simulate with mock data return Array.from({ length: Math.min(limit, 50) }, (_, i) => ({ pubkey: `agent_${i + 1}_${Date.now()}` as Address, capabilities: Math.floor(Math.random() * 255) + 1, metadataUri: `https://example.com/agent_${i + 1}.json`, reputation: Math.floor(Math.random() * 100) + 1, lastUpdated: Date.now() - Math.random() * 86400000, invitesSent: Math.floor(Math.random() * 10), lastInviteAt: Date.now() - Math.random() * 3600000, bump: i % 256, })); } private async applyDiscoveryFilters( agents: IAgentAccount[], filters: IAgentDiscoveryFilters ): Promise { return agents .filter(agent => this.passesFilters(agent, filters)) .map(agent => ({ agent, matchScore: this.calculateMatchScore(agent, filters), matchReasons: this.generateMatchReasons(agent, filters), })); } private passesFilters(agent: IAgentAccount, filters: IAgentDiscoveryFilters): boolean { // Reputation filtering if (filters.minimumReputation && agent.reputation < filters.minimumReputation) return false; if (filters.maximumReputation && agent.reputation > filters.maximumReputation) return false; // Capability filtering if (filters.requiredCapabilities) { const hasAllRequired = filters.requiredCapabilities.every(cap => (agent.capabilities & (1 << cap)) !== 0 ); if (!hasAllRequired) return false; } // Activity filtering if (filters.lastActiveWithin) { const timeSinceActive = Date.now() - agent.lastUpdated; if (timeSinceActive > filters.lastActiveWithin) return false; } return true; } private calculateMatchScore(agent: IAgentAccount, filters: IAgentDiscoveryFilters): number { let score = 0; let totalWeight = 0; // Reputation factor (weight: 30%) const reputationWeight = 30; const reputationScore = Math.min(agent.reputation / 100, 1); score += reputationScore * reputationWeight; totalWeight += reputationWeight; // Capability match factor (weight: 40%) if (filters.requiredCapabilities || filters.optionalCapabilities) { const capabilityWeight = 40; const capabilityScore = this.calculateCapabilityMatch(agent, filters); score += capabilityScore * capabilityWeight; totalWeight += capabilityWeight; } // Activity factor (weight: 20%) const activityWeight = 20; const daysSinceUpdate = (Date.now() - agent.lastUpdated) / (24 * 60 * 60 * 1000); const activityScore = Math.max(0, 1 - daysSinceUpdate / 30); // Decay over 30 days score += activityScore * activityWeight; totalWeight += activityWeight; // Performance factor (weight: 10%) const performanceWeight = 10; const performanceScore = this.getSimulatedPerformanceScore(agent); score += performanceScore * performanceWeight; totalWeight += performanceWeight; return Math.round((score / totalWeight) * 100); } private calculateCapabilityMatch(agent: IAgentAccount, filters: IAgentDiscoveryFilters): number { let matchScore = 0; let totalCapabilities = 0; if (filters.requiredCapabilities) { totalCapabilities += filters.requiredCapabilities.length; const matchedRequired = filters.requiredCapabilities.filter(cap => (agent.capabilities & (1 << cap)) !== 0 ).length; matchScore += (matchedRequired / filters.requiredCapabilities.length) * 0.8; } if (filters.optionalCapabilities) { totalCapabilities += filters.optionalCapabilities.length; const matchedOptional = filters.optionalCapabilities.filter(cap => (agent.capabilities & (1 << cap)) !== 0 ).length; matchScore += (matchedOptional / filters.optionalCapabilities.length) * 0.2; } return totalCapabilities > 0 ? matchScore : 1; } private generateMatchReasons(agent: IAgentAccount, filters: IAgentDiscoveryFilters): string[] { const reasons: string[] = []; if (agent.reputation >= 80) { reasons.push('High reputation score'); } if (filters.requiredCapabilities) { const matchedCaps = filters.requiredCapabilities.filter(cap => (agent.capabilities & (1 << cap)) !== 0 ); if (matchedCaps.length === filters.requiredCapabilities.length) { reasons.push('Matches all required capabilities'); } } const daysSinceUpdate = (Date.now() - agent.lastUpdated) / (24 * 60 * 60 * 1000); if (daysSinceUpdate < 1) { reasons.push('Recently active'); } return reasons; } private sortDiscoveryResults( results: IAgentDiscoveryResult[], filters: IAgentDiscoveryFilters ): IAgentDiscoveryResult[] { const sortedResults = [...results]; switch (filters.sortBy) { case 'reputation': sortedResults.sort((a, b) => b.agent.reputation - a.agent.reputation); break; case 'compatibility': sortedResults.sort((a, b) => b.matchScore - a.matchScore); break; case 'availability': sortedResults.sort((a, b) => b.agent.lastUpdated - a.agent.lastUpdated); break; default: sortedResults.sort((a, b) => b.matchScore - a.matchScore); } if (filters.sortOrder === 'asc') { sortedResults.reverse(); } return sortedResults; } private async generateRecommendations( results: IAgentDiscoveryResult[], filters: IAgentDiscoveryFilters ): Promise { const averageMatchScore = results.reduce((sum, r) => sum + r.matchScore, 0) / results.length; const recommendations: IAgentDiscoveryResponse['recommendations'] = {}; // Suggest filter adjustments if match quality is low if (averageMatchScore < 60) { recommendations.adjustFilters = { minimumReputation: Math.max((filters.minimumReputation || 0) - 20, 0), }; } // Market insights recommendations.marketInsights = { averageRate: BigInt(Math.floor(Math.random() * 1000000000)), // Random SOL amount demandLevel: results.length < 5 ? 'high' : results.length < 20 ? 'medium' : 'low', recommendedBudget: BigInt(Math.floor(Math.random() * 2000000000)), }; return recommendations; } private calculateSearchQuality(results: IAgentDiscoveryResult[], filters: IAgentDiscoveryFilters): number { if (results.length === 0) return 0; const averageScore = results.reduce((sum, r) => sum + r.matchScore, 0) / results.length; const diversityBonus = Math.min(results.length / 20, 1) * 10; // Bonus for diverse results return Math.min(Math.round(averageScore + diversityBonus), 100); } // Additional helper methods for compatibility analysis private calculateTechnicalAlignment(agentA: IAgentAccount, agentB: IAgentAccount): number { const sharedCapabilities = agentA.capabilities & agentB.capabilities; const totalCapabilities = agentA.capabilities | agentB.capabilities; return totalCapabilities > 0 ? Math.round((sharedCapabilities / totalCapabilities) * 100) : 0; } private calculateCommunicationCompatibility(agentA: IAgentAccount, agentB: IAgentAccount): number { // Simulate based on reputation levels (similar reputation = better communication) const reputationDiff = Math.abs(agentA.reputation - agentB.reputation); return Math.max(0, 100 - reputationDiff); } private calculateWorkflowMatching(agentA: IAgentAccount, agentB: IAgentAccount): number { // Simulate workflow compatibility return Math.floor(Math.random() * 40) + 60; // 60-100 range } private calculateExperienceComplementarity(agentA: IAgentAccount, agentB: IAgentAccount): number { // Different capabilities = better complementarity const differentCapabilities = agentA.capabilities ^ agentB.capabilities; const totalPossible = (1 << 8) - 1; // Assuming 8-bit capabilities return Math.round((differentCapabilities / totalPossible) * 100); } private calculateAvailabilityOverlap(agentA: IAgentAccount, agentB: IAgentAccount): number { // Simulate availability overlap return Math.floor(Math.random() * 30) + 70; // 70-100 range } private identifyCollaborationStrengths(agentA: IAgentAccount, agentB: IAgentAccount): string[] { const strengths: string[] = []; if (agentA.reputation > 80 && agentB.reputation > 80) { strengths.push('Both agents have high reputation scores'); } if ((agentA.capabilities & agentB.capabilities) > 0) { strengths.push('Shared technical capabilities for seamless collaboration'); } return strengths; } private identifyConcerns(agentA: IAgentAccount, agentB: IAgentAccount): string[] { const concerns: string[] = []; const reputationDiff = Math.abs(agentA.reputation - agentB.reputation); if (reputationDiff > 50) { concerns.push('Significant reputation gap may cause communication issues'); } return concerns; } private generateCollaborationRecommendations(agentA: IAgentAccount, agentB: IAgentAccount): string[] { return [ 'Establish clear communication protocols from the start', 'Define individual responsibilities and shared objectives', 'Schedule regular check-ins to maintain alignment', ]; } private calculateMarketRate(agent: IAgentAccount): bigint { // Simulate market rate based on reputation and capabilities const baseRate = BigInt(100000000); // 0.1 SOL base const reputationMultiplier = BigInt(agent.reputation) / 100n; const capabilityBonus = BigInt(this.countCapabilities(agent.capabilities)) * BigInt(10000000); return baseRate + (baseRate * reputationMultiplier) + capabilityBonus; } private countCapabilities(capabilities: number): number { let count = 0; let caps = capabilities; while (caps > 0) { count += caps & 1; caps >>= 1; } return count; } private getAgentSpecializations(agent: IAgentAccount): string[] { const specializations = ['AI/ML', 'Data Analysis', 'Automation', 'Trading', 'Content Creation']; const hash = agent.pubkey.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0); return specializations.filter((_, index) => (hash >> index) & 1); } private getSimulatedPerformanceScore(agent: IAgentAccount): number { // Simulate performance based on reputation and activity const reputationFactor = agent.reputation / 100; const activityFactor = Math.max(0, 1 - (Date.now() - agent.lastUpdated) / (7 * 24 * 60 * 60 * 1000)); return reputationFactor * 0.7 + activityFactor * 0.3; } /** * Parse agent account data from chain */ }