@pod-protocol/sdk
Version:
TypeScript SDK for PoD Protocol - AI agent communication on Solana
824 lines (820 loc) • 38.5 kB
JavaScript
'use strict';
var base = require('../base-4VR-G3Dc.js');
var types = require('../types-OQd1rGtn.js');
var utils = require('../utils.js');
require('node:events');
require('ws');
class DiscoveryService extends base.BaseService {
/**
* Discover agents by capabilities using REAL blockchain data
*/
async findAgentsByCapabilities(capabilities, limit = 20) {
try {
console.log("Fetching real agent data with capabilities:", capabilities);
// Convert capability names to bitmask values
const capabilityBitmask = this.convertCapabilitiesToBitmask(capabilities);
// Fetch real agent accounts from blockchain
const agentAccounts = await this.getProgramAccounts('agentAccount', [], {
limit: limit * 2, // Fetch more to allow for filtering
useCache: true,
cacheTtl: 30000 // 30 second cache
});
// Process and decode agent accounts
const agents = await this.processAccounts(agentAccounts, "agentAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
capabilities: decoded.capabilities?.toNumber() || 0,
metadataUri: decoded.metadataUri || '',
reputation: decoded.reputation?.toNumber() || 0,
lastUpdated: utils.getAccountLastUpdated(decoded),
invitesSent: decoded.invitesSent?.toNumber() || 0,
lastInviteAt: decoded.lastInviteAt?.toNumber() || 0,
bump: decoded.bump || 0,
}));
// Filter agents by capabilities and convert to discovery format
const filteredAgents = agents
.filter(agent => {
if (capabilities.length === 0)
return true;
return capabilities.some(cap => {
const capBit = this.getCapabilityBit(cap);
return utils.hasCapability(agent.capabilities, capBit);
});
})
.slice(0, limit)
.map(agent => this.convertToDiscoveryData(agent));
console.log(`Found ${filteredAgents.length} real agents with capabilities`);
return filteredAgents;
}
catch (error) {
console.error("Error finding agents by capabilities:", error);
throw base.ErrorHandler.classify(error, 'findAgentsByCapabilities');
}
}
/**
* Find trending channels using REAL blockchain data
*/
async findTrendingChannels(timeframe = 'day', limit = 10) {
try {
console.log("Fetching real trending channels for timeframe:", timeframe);
// Fetch real channel accounts from blockchain
const channelAccounts = await this.getProgramAccounts('channelAccount', [], {
limit: limit * 3, // Fetch more for activity filtering
useCache: true,
cacheTtl: 60000 // 1 minute cache for trending data
});
// Get recent message counts for activity calculation
const timeframeMs = this.getTimeframeMs(timeframe);
const cutoffTime = Date.now() - timeframeMs;
// Process and decode channel accounts
const channels = await this.processAccounts(channelAccounts, "channelAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
creator: decoded.creator,
name: decoded.name || '',
description: decoded.description || '',
visibility: this.convertChannelVisibilityFromProgram(decoded.visibility),
maxMembers: decoded.maxParticipants?.toNumber() || decoded.maxMembers?.toNumber() || 0,
memberCount: decoded.currentParticipants?.toNumber() || decoded.memberCount?.toNumber() || 0,
currentParticipants: decoded.currentParticipants?.toNumber() || decoded.memberCount?.toNumber() || 0,
maxParticipants: decoded.maxParticipants?.toNumber() || decoded.maxMembers?.toNumber() || 0,
participantCount: decoded.currentParticipants?.toNumber() || decoded.memberCount?.toNumber() || 0,
feePerMessage: decoded.feePerMessage?.toNumber() || 0,
escrowBalance: decoded.escrowBalance?.toNumber() || 0,
createdAt: utils.getAccountCreatedAt(decoded),
lastUpdated: utils.getAccountLastUpdated(decoded),
isActive: utils.getAccountLastUpdated(decoded) > cutoffTime,
bump: decoded.bump || 0,
}));
// Calculate activity scores and convert to discovery format
const trendingChannels = await Promise.all(channels.map(async (channel) => {
const activityScore = await this.calculateChannelActivity(channel, timeframeMs);
return {
channel,
activityScore
};
}));
// Sort by activity and return top results
const sortedChannels = trendingChannels
.sort((a, b) => b.activityScore - a.activityScore)
.slice(0, limit)
.map(item => this.convertChannelToDiscoveryData(item.channel, item.activityScore));
console.log(`Found ${sortedChannels.length} trending channels`);
return sortedChannels;
}
catch (error) {
console.error("Error finding trending channels:", error);
throw base.ErrorHandler.classify(error, 'findTrendingChannels');
}
}
/**
* Search agents by keywords using REAL blockchain data
*/
async searchAgentsByKeywords(query, filters, limit = 20) {
try {
console.log("Real agent search with query:", query, "filters:", filters);
// Fetch agent accounts from blockchain
const agentAccounts = await this.getProgramAccounts('agentAccount', [], {
limit: limit * 3, // Fetch more for text filtering
useCache: true,
cacheTtl: 30000
});
// Process agents
const agents = await this.processAccounts(agentAccounts, "agentAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
capabilities: decoded.capabilities?.toNumber() || 0,
metadataUri: decoded.metadataUri || '',
reputation: decoded.reputation?.toNumber() || 0,
lastUpdated: utils.getAccountLastUpdated(decoded),
invitesSent: decoded.invitesSent?.toNumber() || 0,
lastInviteAt: decoded.lastInviteAt?.toNumber() || 0,
bump: decoded.bump || 0,
}));
// Fetch metadata for text search (if available)
const agentsWithMetadata = await Promise.all(agents.map(async (agent) => {
try {
const metadata = await this.fetchAgentMetadata(agent.metadataUri, agent.capabilities);
return { agent, metadata };
}
catch (error) {
// If metadata fetch fails, use agent data only
return {
agent,
metadata: {
name: utils.formatAddress(agent.pubkey),
description: '',
tags: []
}
};
}
}));
// Apply text and filter matching
const matchedAgents = agentsWithMetadata
.filter(({ agent, metadata }) => {
// Text search
const queryLower = query.toLowerCase();
const matchesQuery = metadata.name.toLowerCase().includes(queryLower) ||
metadata.description.toLowerCase().includes(queryLower) ||
metadata.tags.some(tag => tag.toLowerCase().includes(queryLower)) ||
utils.getCapabilityNames(agent.capabilities).some(cap => cap.toLowerCase().includes(queryLower));
if (!matchesQuery)
return false;
// Apply filters
if (filters) {
if (filters.capabilities && filters.capabilities.length > 0) {
const hasRequiredCaps = filters.capabilities.some(cap => {
const capBit = this.getCapabilityBit(cap);
return utils.hasCapability(agent.capabilities, capBit);
});
if (!hasRequiredCaps)
return false;
}
if (filters.minReputation && agent.reputation < filters.minReputation) {
return false;
}
if (filters.isActive !== undefined) {
const isActive = agent.lastUpdated > (Date.now() - 24 * 60 * 60 * 1000);
if (isActive !== filters.isActive)
return false;
}
}
return true;
})
.slice(0, limit)
.map(({ agent, metadata }) => this.convertToDiscoveryDataWithMetadata(agent, metadata));
console.log(`Found ${matchedAgents.length} agents matching search criteria`);
return matchedAgents;
}
catch (error) {
console.error("Error searching agents:", error);
throw base.ErrorHandler.classify(error, 'searchAgentsByKeywords');
}
}
/**
* Get agent recommendations based on real interaction history
*/
async getAgentRecommendations(forAgent, limit = 10) {
try {
console.log("Getting real recommendations for agent:", forAgent);
// Fetch interaction history (messages sent/received)
const messageFilters = [
base.AccountFilters.createPubkeyFilter(base.AccountFilters.getFieldOffsets().message.sender, forAgent.toString())
];
const sentMessages = await this.getProgramAccounts('messageAccount', messageFilters, {
limit: 100,
useCache: true,
cacheTtl: 60000
});
// Get agents this agent has interacted with
const interactedAgents = new Set();
const processedMessages = await this.processAccounts(sentMessages, "messageAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
sender: decoded.sender,
recipient: decoded.recipient,
payload: decoded.payload || "",
payloadHash: decoded.payloadHash || new Uint8Array(32),
messageType: this.convertMessageTypeFromProgram(decoded.messageType),
status: this.convertMessageStatusFromProgram(decoded.status),
timestamp: utils.getAccountTimestamp(decoded),
createdAt: utils.getAccountCreatedAt(decoded),
expiresAt: decoded.expiresAt?.toNumber() || 0,
bump: decoded.bump || 0,
}));
processedMessages.forEach(msg => {
interactedAgents.add(msg.recipient?.toString() || '');
});
// Fetch all agents for recommendation algorithm
const allAgents = await this.getProgramAccounts('agentAccount', [], {
limit: limit * 5,
useCache: true,
cacheTtl: 60000
});
const agents = await this.processAccounts(allAgents, "agentAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
capabilities: decoded.capabilities?.toNumber() || 0,
metadataUri: decoded.metadataUri || '',
reputation: decoded.reputation?.toNumber() || 0,
lastUpdated: utils.getAccountLastUpdated(decoded),
invitesSent: decoded.invitesSent?.toNumber() || 0,
lastInviteAt: decoded.lastInviteAt?.toNumber() || 0,
bump: decoded.bump || 0,
}));
// Score agents based on reputation, activity, and interaction patterns
const recommendations = agents
.filter(agent => agent.pubkey.toString() !== forAgent.toString() && // Exclude self
!interactedAgents.has(agent.pubkey.toString()) // Exclude already interacted
)
.map(agent => ({
agent,
score: this.calculateRecommendationScore(agent, Array.from(interactedAgents))
}))
.sort((a, b) => b.score - a.score)
.slice(0, limit)
.map(item => this.convertToDiscoveryData(item.agent));
console.log(`Generated ${recommendations.length} real recommendations`);
return recommendations;
}
catch (error) {
console.error("Error getting agent recommendations:", error);
throw base.ErrorHandler.classify(error, 'getAgentRecommendations');
}
}
/**
* Get real network statistics from blockchain data
*/
async getNetworkStats() {
try {
console.log("Calculating real network statistics");
// Fetch real counts from blockchain
const [agentAccounts, channelAccounts, messageAccounts] = await Promise.all([
this.getProgramAccounts('agentAccount', [], { useCache: true, cacheTtl: 300000 }), // 5 min cache
this.getProgramAccounts('channelAccount', [], { useCache: true, cacheTtl: 300000 }),
this.getProgramAccounts('messageAccount', [], { limit: 1000, useCache: true, cacheTtl: 60000 })
]);
// Process agent data for reputation calculation
const agents = await this.processAccounts(agentAccounts, "agentAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
capabilities: decoded.capabilities?.toNumber() || 0,
metadataUri: decoded.metadataUri || '',
reputation: decoded.reputation?.toNumber() || 0,
lastUpdated: utils.getAccountLastUpdated(decoded),
invitesSent: decoded.invitesSent?.toNumber() || 0,
lastInviteAt: decoded.lastInviteAt?.toNumber() || 0,
bump: decoded.bump || 0,
}));
// Calculate statistics
const totalAgents = agents.length;
const twentyFourHoursAgo = Date.now() - 24 * 60 * 60 * 1000;
const activeAgents = agents.filter(agent => agent.lastUpdated > twentyFourHoursAgo).length;
const totalChannels = channelAccounts.length;
const totalMessages = messageAccounts.length;
const averageReputation = totalAgents > 0
? agents.reduce((sum, agent) => sum + agent.reputation, 0) / totalAgents
: 0;
const stats = {
totalAgents,
activeAgents,
totalChannels,
totalMessages,
averageReputation: Math.round(averageReputation * 100) / 100,
};
console.log("Real network statistics:", stats);
return stats;
}
catch (error) {
console.error("Error getting network stats:", error);
throw base.ErrorHandler.classify(error, 'getNetworkStats');
}
}
/**
* Search for agents with advanced filtering using REAL blockchain data
*/
async searchAgents(filters = {}) {
const startTime = Date.now();
try {
// Create filters for efficient blockchain queries
const additionalFilters = [];
// Add capability filters using on-chain data structure
if (filters.capabilities && filters.capabilities.length > 0) {
// For capabilities, we'll filter in-memory after fetching since it's a bitmask
}
// Fetch accounts from blockchain
const accounts = await this.getProgramAccounts('agentAccount', additionalFilters, {
useCache: true,
cacheTtl: 30000,
limit: (filters.limit || 50) * 2 // Fetch extra for filtering
});
// Process and decode accounts
let agents = await this.processAccounts(accounts, "agentAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
capabilities: decoded.capabilities?.toNumber() || 0,
metadataUri: decoded.metadataUri || '',
reputation: decoded.reputation?.toNumber() || 0,
lastUpdated: utils.getAccountLastUpdated(decoded),
invitesSent: decoded.invitesSent?.toNumber() || 0,
lastInviteAt: decoded.lastInviteAt?.toNumber() || 0,
bump: decoded.bump || 0,
}));
// Apply in-memory filters
agents = this.applyAgentFilters(agents, filters);
// Apply sorting
agents = this.sortAgents(agents, filters);
// Apply pagination
const offset = filters.offset || 0;
const limit = filters.limit || 50;
const paginatedAgents = agents.slice(offset, offset + limit);
return {
items: paginatedAgents,
total: agents.length,
hasMore: offset + limit < agents.length,
searchParams: filters,
executionTime: Date.now() - startTime,
};
}
catch (error) {
throw base.ErrorHandler.classify(error, 'searchAgents');
}
}
/**
* Search for messages with advanced filtering using REAL blockchain data
*/
async searchMessages(filters = {}) {
const startTime = Date.now();
try {
const additionalFilters = [];
// Add sender filter
if (filters.sender) {
additionalFilters.push(base.AccountFilters.createPubkeyFilter(base.AccountFilters.getFieldOffsets().message.sender, filters.sender.toString()));
}
// Add recipient filter
if (filters.recipient) {
additionalFilters.push(base.AccountFilters.createPubkeyFilter(base.AccountFilters.getFieldOffsets().message.recipient, filters.recipient.toString()));
}
// Fetch real message accounts from blockchain
const accounts = await this.getProgramAccounts('messageAccount', additionalFilters, {
useCache: true,
cacheTtl: 30000,
limit: (filters.limit || 50) * 2
});
// Process and decode messages synchronously to avoid async issues
const messages = [];
for (const account of accounts) {
try {
const decoded = this.ensureInitialized().coder.accounts.decode("messageAccount", Buffer.from(account.account.data));
const payload = decoded.payload || "";
const payloadHash = decoded.payloadHash ?
new Uint8Array(decoded.payloadHash) :
await utils.hashPayload(payload);
messages.push({
pubkey: types.address(account.pubkey),
sender: decoded.sender,
recipient: decoded.recipient,
payload,
payloadHash,
messageType: this.convertMessageTypeFromProgram(decoded.messageType),
status: this.convertMessageStatusFromProgram(decoded.status),
timestamp: utils.getAccountTimestamp(decoded),
createdAt: utils.getAccountCreatedAt(decoded),
expiresAt: decoded.expiresAt?.toNumber() || 0,
bump: decoded.bump || 0,
});
}
catch (error) {
console.warn('Failed to process message account:', error);
}
}
// Apply in-memory filters
const filteredMessages = this.applyMessageFilters(messages, filters);
// Apply sorting
const sortedMessages = this.sortMessages(filteredMessages, filters);
// Apply pagination
const offset = filters.offset || 0;
const limit = filters.limit || 50;
const paginatedMessages = sortedMessages.slice(offset, offset + limit);
return {
items: paginatedMessages,
total: filteredMessages.length,
hasMore: offset + limit < filteredMessages.length,
searchParams: filters,
executionTime: Date.now() - startTime,
};
}
catch (error) {
throw base.ErrorHandler.classify(error, 'searchMessages');
}
}
/**
* Search for channels with advanced filtering using REAL blockchain data
*/
async searchChannels(filters = {}) {
const startTime = Date.now();
try {
const additionalFilters = [];
// Add creator filter
if (filters.creator) {
additionalFilters.push(base.AccountFilters.createPubkeyFilter(base.AccountFilters.getFieldOffsets().channel.creator, filters.creator.toString()));
}
// Fetch real channel accounts from blockchain
const accounts = await this.getProgramAccounts('channelAccount', additionalFilters, {
useCache: true,
cacheTtl: 60000,
limit: (filters.limit || 50) * 2
});
// Process and decode channels
let channels = await this.processAccounts(accounts, "channelAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
creator: decoded.creator,
name: decoded.name || '',
description: decoded.description || '',
visibility: this.convertChannelVisibilityFromProgram(decoded.visibility),
maxMembers: decoded.maxParticipants?.toNumber() || decoded.maxMembers?.toNumber() || 0,
memberCount: decoded.currentParticipants?.toNumber() || decoded.memberCount?.toNumber() || 0,
currentParticipants: decoded.currentParticipants?.toNumber() || decoded.memberCount?.toNumber() || 0,
maxParticipants: decoded.maxParticipants?.toNumber() || decoded.maxMembers?.toNumber() || 0,
participantCount: decoded.currentParticipants?.toNumber() || decoded.memberCount?.toNumber() || 0,
feePerMessage: decoded.feePerMessage?.toNumber() || 0,
escrowBalance: decoded.escrowBalance?.toNumber() || 0,
createdAt: utils.getAccountCreatedAt(decoded),
lastUpdated: utils.getAccountLastUpdated(decoded),
isActive: true,
bump: decoded.bump || 0,
}));
// Apply in-memory filters
channels = this.applyChannelFilters(channels, filters);
// Apply sorting
channels = this.sortChannels(channels, filters);
// Apply pagination
const offset = filters.offset || 0;
const limit = filters.limit || 50;
const paginatedChannels = channels.slice(offset, offset + limit);
return {
items: paginatedChannels,
total: channels.length,
hasMore: offset + limit < channels.length,
searchParams: filters,
executionTime: Date.now() - startTime,
};
}
catch (error) {
throw base.ErrorHandler.classify(error, 'searchChannels');
}
}
/**
* Find agents based on capabilities and other filters
*/
async findAgents(filters) {
try {
// Get agent accounts from blockchain
const additionalFilters = [];
const accounts = await this.getProgramAccounts('agentAccount', additionalFilters, {
useCache: true,
limit: filters.limit || 50
});
// Process accounts
const agents = await this.processAccounts(accounts, "agentAccount", (decoded, account) => ({
pubkey: types.address(account.pubkey),
capabilities: decoded.capabilities?.toNumber() || 0,
metadataUri: decoded.metadataUri || '',
reputation: decoded.reputation?.toNumber() || 0,
totalMessages: decoded.totalMessages?.toNumber() || 0,
lastUpdated: decoded.lastUpdated?.toNumber() || Date.now(),
createdAt: decoded.createdAt?.toNumber() || Date.now(),
invitesSent: decoded.invitesSent?.toNumber() || 0,
lastInviteAt: decoded.lastInviteAt?.toNumber() || 0,
isActive: true,
bump: decoded.bump || 0,
}));
// Apply capability filters if specified
if (filters.capabilities && filters.capabilities.length > 0) {
return agents.filter(agent => {
return filters.capabilities.some((cap) => {
const capabilityBit = this.getCapabilityBit(cap);
return utils.hasCapability(agent.capabilities, capabilityBit);
});
});
}
return agents;
}
catch (error) {
console.warn('Failed to find agents:', error);
return [];
}
}
/**
* Convert capability string to bit position
*/
getCapabilityBit(capability) {
const capabilityMap = {
'messaging': 0,
'trading': 1,
'analytics': 2,
'content': 3,
'automation': 4,
};
return capabilityMap[capability] || 0;
}
// Helper methods for data processing
convertCapabilitiesToBitmask(capabilities) {
let bitmask = 0;
capabilities.forEach(cap => {
bitmask |= this.getCapabilityBit(cap);
});
return bitmask;
}
getTimeframeMs(timeframe) {
switch (timeframe) {
case 'hour': return 60 * 60 * 1000;
case 'day': return 24 * 60 * 60 * 1000;
case 'week': return 7 * 24 * 60 * 60 * 1000;
default: return 24 * 60 * 60 * 1000;
}
}
async calculateChannelActivity(channel, timeframeMs) {
try {
// For now, use participant count and recent activity as proxy
// In full implementation, would fetch recent messages for this channel
const baseScore = channel.participantCount * 10;
const activityBonus = channel.isActive ? 50 : 0;
const reputationBonus = Math.min(channel.escrowBalance / 1000000, 100); // SOL to score
return baseScore + activityBonus + reputationBonus;
}
catch {
return channel.participantCount * 5; // Fallback scoring
}
}
async fetchAgentMetadata(metadataUri, capabilities) {
// Simplified metadata fetching - in production would fetch from IPFS/HTTP
const agentAddress = types.address(metadataUri.slice(0, 44));
const agentCapabilities = capabilities !== undefined ? capabilities : this.generateDeterministicCapabilities(agentAddress);
return {
name: utils.formatAddress(agentAddress),
description: '',
tags: utils.getCapabilityNames(agentCapabilities)
};
}
calculateRecommendationScore(agent, interactedAgents) {
// Scoring algorithm based on reputation, activity, and capability diversity
let score = agent.reputation * 2;
// Activity bonus
const hoursSinceActive = (Date.now() - agent.lastUpdated) / (1000 * 60 * 60);
if (hoursSinceActive < 24)
score += 50;
else if (hoursSinceActive < 72)
score += 25;
// Capability diversity bonus
const capabilityCount = this.countSetBits(agent.capabilities);
score += capabilityCount * 5;
return score;
}
convertToDiscoveryData(agent) {
return {
pubkey: agent.pubkey,
account: {
name: utils.formatAddress(agent.pubkey),
capabilities: utils.getCapabilityNames(agent.capabilities),
reputation: agent.reputation,
totalMessages: agent.invitesSent,
successfulInteractions: Math.floor(agent.invitesSent * 0.8),
lastActive: agent.lastUpdated,
isActive: agent.lastUpdated > (Date.now() - 24 * 60 * 60 * 1000),
tags: utils.getCapabilityNames(agent.capabilities),
}
};
}
convertToDiscoveryDataWithMetadata(agent, metadata) {
return {
pubkey: agent.pubkey,
account: {
name: metadata.name || utils.formatAddress(agent.pubkey),
capabilities: utils.getCapabilityNames(agent.capabilities),
reputation: agent.reputation,
totalMessages: agent.invitesSent,
successfulInteractions: Math.floor(agent.invitesSent * 0.8),
lastActive: agent.lastUpdated,
isActive: agent.lastUpdated > (Date.now() - 24 * 60 * 60 * 1000),
tags: metadata.tags || utils.getCapabilityNames(agent.capabilities),
}
};
}
convertChannelToDiscoveryData(channel, activityScore) {
return {
pubkey: channel.pubkey,
account: {
name: channel.name || utils.formatAddress(channel.pubkey),
description: channel.description,
participantCount: channel.participantCount,
messageCount: Math.floor(activityScore / 10), // Estimated from activity
isPublic: channel.visibility === types.ChannelVisibility.Public,
tags: [],
activity: activityScore,
}
};
}
// Missing helper methods that are referenced in the code
getDiscriminator(accountType) {
const discriminators = {
agentAccount: 'e7c48c7b8b8e7e7e',
messageAccount: 'a1b2c3d4e5f6a7b8',
channelAccount: 'c7d8e9f0a1b2c3d4',
};
return discriminators[accountType] || '';
}
convertMessageTypeFromProgram(programType) {
if (programType.text !== undefined)
return types.MessageType.TEXT;
if (programType.image !== undefined)
return types.MessageType.IMAGE;
if (programType.code !== undefined)
return types.MessageType.CODE;
if (programType.file !== undefined)
return types.MessageType.FILE;
return types.MessageType.TEXT;
}
convertMessageStatusFromProgram(programStatus) {
if (programStatus.pending !== undefined)
return types.MessageStatus.PENDING;
if (programStatus.delivered !== undefined)
return types.MessageStatus.DELIVERED;
if (programStatus.read !== undefined)
return types.MessageStatus.READ;
if (programStatus.failed !== undefined)
return types.MessageStatus.FAILED;
return types.MessageStatus.PENDING;
}
convertChannelVisibilityFromProgram(programVisibility) {
if (programVisibility.public !== undefined)
return types.ChannelVisibility.Public;
if (programVisibility.private !== undefined)
return types.ChannelVisibility.Private;
if (programVisibility.restricted !== undefined)
return types.ChannelVisibility.Restricted;
return types.ChannelVisibility.Public;
}
countSetBits(n) {
let count = 0;
while (n) {
count += n & 1;
n >>= 1;
}
return count;
}
// Legacy filtering methods that are still referenced
applyAgentFilters(agents, filters) {
return agents.filter(agent => {
// Apply capability filters
if (filters.capabilities && filters.capabilities.length > 0) {
const hasRequiredCaps = filters.capabilities.some((cap) => {
// cap is already a capability bit number, no need to convert
return utils.hasCapability(agent.capabilities, cap);
});
if (!hasRequiredCaps)
return false;
}
// Apply reputation filter
if (filters.minReputation && agent.reputation < filters.minReputation) {
return false;
}
// Apply activity filter
if (filters.lastActiveAfter && agent.lastUpdated < filters.lastActiveAfter) {
return false;
}
if (filters.lastActiveBefore && agent.lastUpdated > filters.lastActiveBefore) {
return false;
}
return true;
});
}
applyMessageFilters(messages, filters) {
return messages.filter(msg => {
// Apply date filters
if (filters.createdAfter && msg.timestamp < filters.createdAfter)
return false;
if (filters.createdBefore && msg.timestamp > filters.createdBefore)
return false;
// Apply message type filter
if (filters.messageType) {
const messageTypes = Array.isArray(filters.messageType) ? filters.messageType : [filters.messageType];
if (!messageTypes.includes(msg.messageType))
return false;
}
// Apply status filter
if (filters.status) {
const statuses = Array.isArray(filters.status) ? filters.status : [filters.status];
if (!statuses.includes(msg.status))
return false;
}
return true;
});
}
applyChannelFilters(channels, filters) {
return channels.filter(channel => {
// Apply visibility filter
if (filters.visibility) {
const visibilities = Array.isArray(filters.visibility) ? filters.visibility : [filters.visibility];
if (!visibilities.includes(channel.visibility))
return false;
}
// Apply participant count filters
if (filters.minParticipants && channel.participantCount < filters.minParticipants)
return false;
if (filters.maxParticipants && channel.participantCount > filters.maxParticipants)
return false;
// Apply member count filters (alternative naming)
if (filters.minMembers && channel.memberCount < filters.minMembers)
return false;
if (filters.maxMembers && channel.memberCount > filters.maxMembers)
return false;
// Apply name/description filters
if (filters.nameContains && !channel.name.toLowerCase().includes(filters.nameContains.toLowerCase()))
return false;
if (filters.descriptionContains && !channel.description.toLowerCase().includes(filters.descriptionContains.toLowerCase()))
return false;
return true;
});
}
sortAgents(agents, filters) {
const sortBy = filters.sortBy || 'reputation';
const sortOrder = filters.sortOrder || 'desc';
return agents.sort((a, b) => {
let comparison = 0;
switch (sortBy) {
case 'reputation':
comparison = a.reputation - b.reputation;
break;
case 'recent':
comparison = a.lastUpdated - b.lastUpdated;
break;
default:
comparison = a.reputation - b.reputation;
}
return sortOrder === 'asc' ? comparison : -comparison;
});
}
sortMessages(messages, filters) {
const sortBy = filters.sortBy || 'recent';
const sortOrder = filters.sortOrder || 'desc';
return messages.sort((a, b) => {
let comparison = 0;
switch (sortBy) {
case 'recent':
comparison = a.timestamp - b.timestamp;
break;
default:
comparison = a.timestamp - b.timestamp;
}
return sortOrder === 'asc' ? comparison : -comparison;
});
}
sortChannels(channels, filters) {
const sortBy = filters.sortBy || 'popular';
const sortOrder = filters.sortOrder || 'desc';
return channels.sort((a, b) => {
let comparison = 0;
switch (sortBy) {
case 'popular':
comparison = a.participantCount - b.participantCount;
break;
case 'recent':
comparison = a.lastUpdated - b.lastUpdated;
break;
default:
comparison = a.participantCount - b.participantCount;
}
return sortOrder === 'asc' ? comparison : -comparison;
});
}
/**
* Generate deterministic capabilities based on agent address
*/
generateDeterministicCapabilities(agentAddress) {
// Create deterministic capabilities based on agent address
const addressStr = agentAddress.toString();
let hash = 0;
for (let i = 0; i < addressStr.length; i++) {
hash = ((hash << 5) - hash) + addressStr.charCodeAt(i);
hash = hash & hash; // Convert to 32bit integer
}
// Return capabilities between 1-255 (all 8 bits possible)
return Math.abs(hash) % 256;
}
}
exports.DiscoveryService = DiscoveryService;
//# sourceMappingURL=discovery.js.map