UNPKG

@pod-protocol/sdk-js

Version:
435 lines (386 loc) 13 kB
'use strict'; var base = require('../base-B0PMFl1k.js'); var web3_js = require('@solana/web3.js'); var anchor = require('@coral-xyz/anchor'); var utils_pda = require('../utils/pda.js'); /** * Agent service for PoD Protocol SDK */ /** * Service for managing AI agents in the PoD Protocol * * @class AgentService * @extends BaseService */ class AgentService extends base.BaseService { /** * Register a new agent * * @param {Object} options - Agent creation options * @param {Object} wallet - Wallet to sign the transaction * @returns {Promise<string>} Transaction signature * * @example * ```javascript * const tx = await client.agents.register({ * capabilities: AGENT_CAPABILITIES.ANALYSIS | AGENT_CAPABILITIES.TRADING, * metadataUri: 'https://my-agent-metadata.json' * }, wallet); * ``` */ async register(options, wallet) { if (!this.isInitialized()) { throw new Error('Service not initialized. Call client.initialize() first.'); } const [agentPDA] = await utils_pda.findAgentPDA(wallet.publicKey, this.programId); return this.retry(async () => { try { const tx = await this.program.methods .registerAgent(new anchor.BN(options.capabilities), options.metadataUri) .accounts({ agentAccount: agentPDA, signer: wallet.publicKey, systemProgram: web3_js.SystemProgram.programId, }) .rpc(); return tx; } catch (error) { if (error.message?.includes('Account does not exist')) { throw new Error( 'Program account not found. Verify the program is deployed and the program ID is correct.' ); } if (error.message?.includes('insufficient funds')) { throw new Error( 'Insufficient SOL balance. Please add funds to your wallet and try again.' ); } throw error; } }); } /** * Update an existing agent * * @param {Object} options - Update options * @param {Object} wallet - Wallet to sign the transaction * @returns {Promise<string>} Transaction signature * * @example * ```javascript * const tx = await client.agents.update({ * capabilities: AGENT_CAPABILITIES.ANALYSIS, * metadataUri: 'https://updated-metadata.json' * }, wallet); * ``` */ async update(options, wallet) { if (!this.isInitialized()) { throw new Error('Service not initialized. Call client.initialize() first.'); } const [agentPDA] = await utils_pda.findAgentPDA(wallet.publicKey, this.programId); return this.retry(async () => { const tx = await this.program.methods .updateAgent( options.capabilities ? new anchor.BN(options.capabilities) : null, options.metadataUri || null ) .accounts({ agentAccount: agentPDA, signer: wallet.publicKey }) .rpc(); return tx; }); } /** * Get agent information by public key * * @param {string} agentPubkey - Agent's public key * @returns {Promise<Object|null>} Agent account data * * @example * ```javascript * const agent = await client.agents.get(agentAddress); * if (agent) { * console.log('Agent capabilities:', agent.capabilities); * console.log('Agent reputation:', agent.reputation); * } * ``` */ async get(agentPubkey) { if (!this.isInitialized()) { throw new Error('Service not initialized. Call client.initialize() first.'); } try { const [agentPDA] = await utils_pda.findAgentPDA(agentPubkey, this.programId); const agentAccount = await this.program.account.agentAccount.fetch(agentPDA); return { pubkey: agentPubkey, ...agentAccount, // Convert BN to number for JavaScript compatibility capabilities: agentAccount.capabilities.toNumber(), reputation: agentAccount.reputation.toNumber(), lastUpdated: agentAccount.lastUpdated.toNumber(), invitesSent: agentAccount.invitesSent.toNumber(), lastInviteAt: agentAccount.lastInviteAt.toNumber() }; } catch (error) { if (error.message?.includes('Account does not exist')) { return null; } throw error; } } /** * List all agents with optional filtering * * @param {Object} [filters] - Optional filters * @param {number} [filters.capabilities] - Filter by capabilities bitmask * @param {number} [filters.minReputation] - Minimum reputation score * @param {number} [filters.limit=100] - Maximum number of results * @returns {Promise<Array>} Array of agent accounts * * @example * ```javascript * // Get all trading agents with reputation > 50 * const agents = await client.agents.list({ * capabilities: AGENT_CAPABILITIES.TRADING, * minReputation: 50, * limit: 10 * }); * ``` */ async list(filters = {}) { if (!this.isInitialized()) { throw new Error('Service not initialized. Call client.initialize() first.'); } try { const accounts = await this.program.account.agentAccount.all(); let agents = accounts.map(account => ({ pubkey: account.publicKey, ...account.account, // Convert BN to number for JavaScript compatibility capabilities: account.account.capabilities.toNumber(), reputation: account.account.reputation.toNumber(), lastUpdated: account.account.lastUpdated.toNumber(), invitesSent: account.account.invitesSent.toNumber(), lastInviteAt: account.account.lastInviteAt.toNumber() })); // Apply filters if (filters.capabilities !== undefined) { agents = agents.filter(agent => (agent.capabilities & filters.capabilities) === filters.capabilities ); } if (filters.minReputation !== undefined) { agents = agents.filter(agent => agent.reputation >= filters.minReputation); } // Apply limit if (filters.limit) { agents = agents.slice(0, filters.limit); } return agents; } catch (error) { throw new Error(`Failed to list agents: ${error.message}`); } } /** * Check if an agent exists * * @param {string} agentPubkey - Agent's public key * @returns {Promise<boolean>} True if agent exists * * @example * ```javascript * const exists = await client.agents.exists(agentAddress); * if (!exists) { * console.log('Agent not found'); * } * ``` */ async exists(agentPubkey) { const agent = await this.get(agentPubkey); return agent !== null; } /** * Get agent statistics * * @param {string} agentPubkey - Agent's public key * @returns {Promise<Object>} Agent statistics * * @example * ```javascript * const stats = await client.agents.getStats(agentAddress); * console.log('Messages sent:', stats.messagesSent); * console.log('Channels joined:', stats.channelsJoined); * ``` */ async getStats(agentPubkey) { // This would require additional on-chain data or indexing // For now, return basic info from agent account const agent = await this.get(agentPubkey); if (!agent) { throw new Error('Agent not found'); } return { reputation: agent.reputation, invitesSent: agent.invitesSent, lastActive: agent.lastUpdated, accountAge: Date.now() - (agent.lastUpdated * 1000) }; } /** * Get agent PDA for a public key * * @param {string} agentPubkey - Agent's public key * @returns {Promise<string>} Agent PDA */ async getAgentPDA(agentPubkey) { const [pda] = await utils_pda.findAgentPDA(agentPubkey, this.programId); return pda; } /** * Generate metadata URI from metadata object * * @param {Object} metadata - Metadata object * @returns {string} Base64 encoded data URI */ generateMetadataURI(metadata) { const jsonString = JSON.stringify(metadata); const base64String = Buffer.from(jsonString).toString('base64'); return `data:application/json;base64,${base64String}`; } /** * Create register agent instruction * * @param {string} agentPubkey - Agent's public key * @param {number} capabilities - Capabilities bitmask * @param {string} metadataUri - Metadata URI * @returns {Promise<Object>} Register instruction */ async createRegisterInstruction(agentPubkey, capabilities, metadataUri) { if (!this.program) { throw new Error('Program not initialized'); } const [agentPDA] = await utils_pda.findAgentPDA(agentPubkey, this.programId); return this.program.methods .registerAgent(new anchor.BN(capabilities), metadataUri) .accounts({ agentAccount: agentPDA, signer: agentPubkey, systemProgram: web3_js.SystemProgram.programId, }) .instruction(); } /** * Create update agent instruction * * @param {string} agentPubkey - Agent's public key * @param {number} capabilities - New capabilities bitmask * @param {string} metadataUri - New metadata URI * @returns {Promise<Object>} Update instruction */ async createUpdateInstruction(agentPubkey, capabilities, metadataUri) { if (!this.program) { throw new Error('Program not initialized'); } const [agentPDA] = await utils_pda.findAgentPDA(agentPubkey, this.programId); return this.program.methods .updateAgent( capabilities ? new anchor.BN(capabilities) : null, metadataUri || null ) .accounts({ agentAccount: agentPDA, signer: agentPubkey }) .instruction(); } /** * Validate agent data * * @param {Object} agentData - Agent data to validate * @throws {Error} If validation fails */ validateAgentData(agentData) { if (!agentData.pubkey) { throw new Error('Agent public key is required'); } if (typeof agentData.capabilities !== 'number' || agentData.capabilities < 0) { throw new Error('Capabilities must be a non-negative number'); } if (!agentData.metadataUri || agentData.metadataUri.trim() === '') { throw new Error('Metadata URI is required'); } if (typeof agentData.messageCount === 'number' && agentData.messageCount < 0) { throw new Error('Message count must be non-negative'); } } /** * Calculate reputation from metrics * * @param {Object} metrics - Performance metrics * @returns {number} Calculated reputation (0-1000) */ calculateReputation(metrics) { const { successfulMessages = 0, failedMessages = 0, totalMessages = 0, averageResponseTime = 0 } = metrics; if (totalMessages === 0) return 500; // Default reputation for new agents const successRate = successfulMessages / totalMessages; const timeBonus = Math.max(0, 1 - (averageResponseTime / 5000)); // Bonus for fast responses let reputation = 500; // Base reputation reputation += (successRate * 400); // Up to 400 points for success rate reputation += (timeBonus * 100); // Up to 100 points for response time return Math.round(Math.max(0, Math.min(1000, reputation))); } /** * Get capabilities as array of strings * * @param {number} capabilities - Capabilities bitmask * @returns {string[]} Array of capability names */ getCapabilitiesArray(capabilities) { const capabilityMap = { 1: 'text', 2: 'image', 4: 'code', 8: 'analysis', 16: 'trading', 32: 'custom1', 64: 'custom2' }; const result = []; for (const [value, name] of Object.entries(capabilityMap)) { if (capabilities & parseInt(value)) { result.push(name); } } return result; } /** * Convert capabilities array to bitmask number * * @param {string[]} capabilitiesArray - Array of capability names * @returns {number} Capabilities bitmask */ capabilitiesFromArray(capabilitiesArray) { const capabilityMap = { 'text': 1, 'image': 2, 'code': 4, 'analysis': 8, 'trading': 16, 'custom1': 32, 'custom2': 64 }; let capabilities = 0; for (const capability of capabilitiesArray) { if (capabilityMap[capability]) { capabilities |= capabilityMap[capability]; } } return capabilities; } } exports.AgentService = AgentService; //# sourceMappingURL=agent.js.map