@clduab11/gemini-flow
Version:
Revolutionary AI agent swarm coordination platform with Google Services integration, multimedia processing, and production-ready monitoring. Features 8 Google AI services, quantum computing capabilities, and enterprise-grade security.
890 lines (767 loc) • 24.4 kB
text/typescript
/**
* Agent Card System
*
* Comprehensive agent discovery and registration system for A2A communication.
* Provides agent registration, capability-based discovery, filtering, and metrics tracking.
*/
import { EventEmitter } from "events";
import {
AgentCard,
AgentId,
AgentType,
AgentCapability,
DiscoveryRequest,
DiscoveryResponse,
DiscoveryFilter,
RegistrationRequest,
RegistrationResponse,
} from "../../../types/a2a.js";
import { Logger } from "../../../utils/logger.js";
/**
* Agent registry entry with TTL support
*/
interface AgentRegistryEntry {
agentCard: AgentCard;
registrationTime: number;
expiresAt?: number;
lastHeartbeat: number;
}
/**
* Discovery metrics
*/
export interface DiscoveryMetrics {
totalDiscoveryRequests: number;
avgDiscoveryTime: number;
popularCapabilities: { [capability: string]: number };
discoverySuccessRate: number;
filterUsageStats: { [filter: string]: number };
}
/**
* System metrics
*/
export interface SystemMetrics {
totalRegisteredAgents: number;
agentsByType: { [type in AgentType]?: number };
agentsByStatus: { [status: string]: number };
averageLoad: number;
capabilityDistribution: { [capability: string]: number };
trustLevelDistribution: { [level: string]: number };
uptimeDistribution: { [range: string]: number };
}
/**
* Agent Card System implementation
*/
export class AgentCardSystem extends EventEmitter {
private logger: Logger;
private agentRegistry: Map<AgentId, AgentRegistryEntry> = new Map();
private capabilityIndex: Map<string, Set<AgentId>> = new Map();
private serviceIndex: Map<string, Set<AgentId>> = new Map();
private typeIndex: Map<AgentType, Set<AgentId>> = new Map();
private isInitialized: boolean = false;
// Metrics tracking
private metrics: {
totalDiscoveryRequests: number;
discoveryTimes: number[];
capabilityRequests: Map<string, number>;
filterUsage: Map<string, number>;
discoverySuccesses: number;
discoveryFailures: number;
startTime: number;
} = {
totalDiscoveryRequests: 0,
discoveryTimes: [],
capabilityRequests: new Map(),
filterUsage: new Map(),
discoverySuccesses: 0,
discoveryFailures: 0,
startTime: Date.now(),
};
// Configuration
private defaultTTL: number = 3600; // 1 hour default TTL
private heartbeatInterval: number = 300000; // 5 minutes
private cleanupInterval: number = 60000; // 1 minute
constructor() {
super();
this.logger = new Logger("AgentCardSystem");
// Set up periodic cleanup
setInterval(() => this.cleanupExpiredAgents(), this.cleanupInterval);
}
/**
* Initialize the agent card system
*/
async initialize(): Promise<void> {
try {
this.logger.info("Initializing Agent Card System");
this.isInitialized = true;
this.metrics.startTime = Date.now();
this.logger.info("Agent Card System initialized successfully");
this.emit("initialized");
} catch (error) {
this.logger.error("Failed to initialize Agent Card System:", error);
throw error;
}
}
/**
* Shutdown the agent card system
*/
async shutdown(): Promise<void> {
this.logger.info("Shutting down Agent Card System");
this.isInitialized = false;
this.agentRegistry.clear();
this.capabilityIndex.clear();
this.serviceIndex.clear();
this.typeIndex.clear();
this.logger.info("Agent Card System shutdown complete");
this.emit("shutdown");
}
/**
* Register an agent with optional TTL
*/
async registerAgent(
agentCard: AgentCard,
ttl?: number,
): Promise<RegistrationResponse> {
if (!this.isInitialized) {
throw new Error("Agent Card System not initialized");
}
try {
// Validate agent card
this.validateAgentCard(agentCard);
// Check for duplicate registration
if (this.agentRegistry.has(agentCard.id)) {
throw new Error(`Agent already registered: ${agentCard.id}`);
}
const now = Date.now();
const effectiveTTL = ttl || this.defaultTTL;
const expiresAt = now + effectiveTTL * 1000;
// Create registry entry
const entry: AgentRegistryEntry = {
agentCard: { ...agentCard },
registrationTime: now,
expiresAt,
lastHeartbeat: now,
};
// Register agent
this.agentRegistry.set(agentCard.id, entry);
// Update indexes
this.updateIndexes(agentCard, "add");
this.logger.info("Agent registered successfully", {
agentId: agentCard.id,
agentType: agentCard.metadata.type,
capabilities: agentCard.capabilities.length,
ttl: effectiveTTL,
});
this.emit("agentRegistered", agentCard);
return {
jsonrpc: "2.0",
result: {
registered: true,
agentId: agentCard.id,
expiresAt,
},
id: null,
from: "agent-registry",
to: agentCard.id,
timestamp: now,
messageType: "response",
};
} catch (error) {
this.logger.error("Failed to register agent:", error);
throw error;
}
}
/**
* Unregister an agent
*/
async unregisterAgent(agentId: AgentId): Promise<boolean> {
const entry = this.agentRegistry.get(agentId);
if (!entry) {
return false;
}
// Remove from indexes
this.updateIndexes(entry.agentCard, "remove");
// Remove from registry
this.agentRegistry.delete(agentId);
this.logger.info("Agent unregistered", { agentId });
this.emit("agentUnregistered", agentId);
return true;
}
/**
* Update existing agent card
*/
async updateAgentCard(agentCard: AgentCard): Promise<boolean> {
const entry = this.agentRegistry.get(agentCard.id);
if (!entry) {
return false;
}
try {
// Validate updated card
this.validateAgentCard(agentCard);
// Update indexes (remove old, add new)
this.updateIndexes(entry.agentCard, "remove");
this.updateIndexes(agentCard, "add");
// Update entry
entry.agentCard = { ...agentCard };
entry.lastHeartbeat = Date.now();
this.logger.debug("Agent card updated", {
agentId: agentCard.id,
version: agentCard.version,
});
this.emit("agentUpdated", agentCard);
return true;
} catch (error) {
this.logger.error("Failed to update agent card:", error);
return false;
}
}
/**
* Get agent card by ID
*/
async getAgentCard(agentId: AgentId): Promise<AgentCard | null> {
const entry = this.agentRegistry.get(agentId);
if (!entry) {
return null;
}
// Check if expired
if (entry.expiresAt && Date.now() > entry.expiresAt) {
await this.unregisterAgent(agentId);
return null;
}
return { ...entry.agentCard };
}
/**
* Discover agents based on criteria
*/
async discoverAgents(request: DiscoveryRequest): Promise<DiscoveryResponse> {
if (!this.isInitialized) {
throw new Error("Agent Card System not initialized");
}
const startTime = Date.now();
this.metrics.totalDiscoveryRequests++;
try {
let candidateAgents = Array.from(this.agentRegistry.values());
// Filter by capabilities
if (
request.params.capabilities &&
request.params.capabilities.length > 0
) {
candidateAgents = this.filterByCapabilities(
candidateAgents,
request.params.capabilities,
);
// Track capability requests
request.params.capabilities.forEach((cap) => {
const count = this.metrics.capabilityRequests.get(cap) || 0;
this.metrics.capabilityRequests.set(cap, count + 1);
});
}
// Filter by agent type
if (request.params.agentType) {
candidateAgents = candidateAgents.filter(
(entry) => entry.agentCard.metadata.type === request.params.agentType,
);
}
// Apply custom filters
if (request.params.filters && request.params.filters.length > 0) {
candidateAgents = this.applyFilters(
candidateAgents,
request.params.filters,
);
}
// Apply distance filtering
if (request.params.maxDistance !== undefined) {
candidateAgents = this.filterByDistance(
candidateAgents,
request.params.maxDistance,
);
}
// Remove expired agents
candidateAgents = candidateAgents.filter(
(entry) => !entry.expiresAt || Date.now() < entry.expiresAt,
);
// Extract agent cards
const foundAgents = candidateAgents.map((entry) => entry.agentCard);
const searchTime = Date.now() - startTime;
// Track metrics
this.metrics.discoveryTimes.push(searchTime);
if (this.metrics.discoveryTimes.length > 1000) {
this.metrics.discoveryTimes.splice(0, 100);
}
if (foundAgents.length > 0) {
this.metrics.discoverySuccesses++;
} else {
this.metrics.discoveryFailures++;
}
this.logger.debug("Agent discovery completed", {
requestId: request.id,
foundAgents: foundAgents.length,
searchTime,
criteria: {
capabilities: request.params.capabilities?.length || 0,
agentType: request.params.agentType,
filters: request.params.filters?.length || 0,
},
});
return {
jsonrpc: "2.0",
result: {
agents: foundAgents,
totalFound: foundAgents.length,
searchTime,
},
id: request.id,
from: "agent-registry",
to: request.from,
timestamp: Date.now(),
messageType: "response",
};
} catch (error) {
this.metrics.discoveryFailures++;
this.logger.error("Agent discovery failed:", error);
throw error;
}
}
/**
* Find agents by capability
*/
async findAgentsByCapability(
capabilityName: string,
version?: string,
): Promise<AgentCard[]> {
const agentIds = this.capabilityIndex.get(capabilityName) || new Set();
const matchingAgents: AgentCard[] = [];
for (const agentId of agentIds) {
const entry = this.agentRegistry.get(agentId);
if (!entry || (entry.expiresAt && Date.now() > entry.expiresAt)) {
continue;
}
const agentCard = entry.agentCard;
const capability = agentCard.capabilities.find(
(cap) => cap.name === capabilityName,
);
if (capability) {
// Check version compatibility if specified
if (!version || this.isVersionCompatible(capability.version, version)) {
matchingAgents.push(agentCard);
}
}
}
return matchingAgents;
}
/**
* Find agents by type
*/
async findAgentsByType(agentType: AgentType): Promise<AgentCard[]> {
const agentIds = this.typeIndex.get(agentType) || new Set();
const matchingAgents: AgentCard[] = [];
for (const agentId of agentIds) {
const entry = this.agentRegistry.get(agentId);
if (entry && (!entry.expiresAt || Date.now() < entry.expiresAt)) {
matchingAgents.push(entry.agentCard);
}
}
return matchingAgents;
}
/**
* Find agents by service
*/
async findAgentsByService(serviceName: string): Promise<AgentCard[]> {
const agentIds = this.serviceIndex.get(serviceName) || new Set();
const matchingAgents: AgentCard[] = [];
for (const agentId of agentIds) {
const entry = this.agentRegistry.get(agentId);
if (entry && (!entry.expiresAt || Date.now() < entry.expiresAt)) {
matchingAgents.push(entry.agentCard);
}
}
return matchingAgents;
}
/**
* Refresh agent status (heartbeat)
*/
async refreshAgentStatus(agentId: AgentId): Promise<boolean> {
const entry = this.agentRegistry.get(agentId);
if (!entry) {
return false;
}
entry.lastHeartbeat = Date.now();
entry.agentCard.metadata.lastSeen = Date.now();
this.logger.debug("Agent status refreshed", { agentId });
return true;
}
/**
* Get registered agents map
*/
getRegisteredAgents(): Map<AgentId, AgentCard> {
const result = new Map<AgentId, AgentCard>();
this.agentRegistry.forEach((entry, agentId) => {
if (!entry.expiresAt || Date.now() < entry.expiresAt) {
result.set(agentId, entry.agentCard);
}
});
return result;
}
/**
* Get system metrics
*/
async getSystemMetrics(): Promise<SystemMetrics> {
const activeAgents = Array.from(this.agentRegistry.values()).filter(
(entry) => !entry.expiresAt || Date.now() < entry.expiresAt,
);
// Count by type
const agentsByType: { [type in AgentType]?: number } = {};
const agentsByStatus: { [status: string]: number } = {};
const capabilityDistribution: { [capability: string]: number } = {};
const trustLevelDistribution: { [level: string]: number } = {};
const uptimeRanges = { "<90%": 0, "90-95%": 0, "95-99%": 0, "99%+": 0 };
let totalLoad = 0;
activeAgents.forEach((entry) => {
const agent = entry.agentCard;
// Type distribution
agentsByType[agent.metadata.type] =
(agentsByType[agent.metadata.type] || 0) + 1;
// Status distribution
agentsByStatus[agent.metadata.status] =
(agentsByStatus[agent.metadata.status] || 0) + 1;
// Load accumulation
totalLoad += agent.metadata.load;
// Capability distribution
agent.capabilities.forEach((cap) => {
capabilityDistribution[cap.name] =
(capabilityDistribution[cap.name] || 0) + 1;
});
// Trust level distribution
if (agent.metadata.trustLevel) {
trustLevelDistribution[agent.metadata.trustLevel] =
(trustLevelDistribution[agent.metadata.trustLevel] || 0) + 1;
}
// Uptime distribution
if (agent.metadata.metrics?.uptime !== undefined) {
const uptime = agent.metadata.metrics.uptime;
if (uptime < 90) uptimeRanges["<90%"]++;
else if (uptime < 95) uptimeRanges["90-95%"]++;
else if (uptime < 99) uptimeRanges["95-99%"]++;
else uptimeRanges["99%+"]++;
}
});
return {
totalRegisteredAgents: activeAgents.length,
agentsByType,
agentsByStatus,
averageLoad:
activeAgents.length > 0 ? totalLoad / activeAgents.length : 0,
capabilityDistribution,
trustLevelDistribution,
uptimeDistribution: uptimeRanges,
};
}
/**
* Get discovery metrics
*/
getDiscoveryMetrics(): DiscoveryMetrics {
const popularCapabilities: { [capability: string]: number } = {};
this.metrics.capabilityRequests.forEach((count, capability) => {
popularCapabilities[capability] = count;
});
const filterUsageStats: { [filter: string]: number } = {};
this.metrics.filterUsage.forEach((count, filter) => {
filterUsageStats[filter] = count;
});
return {
totalDiscoveryRequests: this.metrics.totalDiscoveryRequests,
avgDiscoveryTime:
this.metrics.discoveryTimes.length > 0
? this.metrics.discoveryTimes.reduce((a, b) => a + b, 0) /
this.metrics.discoveryTimes.length
: 0,
popularCapabilities,
discoverySuccessRate:
this.metrics.totalDiscoveryRequests > 0
? this.metrics.discoverySuccesses /
this.metrics.totalDiscoveryRequests
: 0,
filterUsageStats,
};
}
/**
* Validate agent card
*/
private validateAgentCard(agentCard: AgentCard): void {
if (!agentCard.id || agentCard.id.trim() === "") {
throw new Error("Invalid agent card: missing required fields");
}
if (!agentCard.name || !agentCard.version) {
throw new Error("Invalid agent card: missing required fields");
}
if (!agentCard.metadata || !agentCard.metadata.type) {
throw new Error("Invalid agent card: missing required fields");
}
if (!Array.isArray(agentCard.capabilities)) {
throw new Error("Invalid agent card: capabilities must be an array");
}
if (!Array.isArray(agentCard.services)) {
throw new Error("Invalid agent card: services must be an array");
}
if (!Array.isArray(agentCard.endpoints)) {
throw new Error("Invalid agent card: endpoints must be an array");
}
}
/**
* Update search indexes
*/
private updateIndexes(
agentCard: AgentCard,
operation: "add" | "remove",
): void {
const agentId = agentCard.id;
if (operation === "add") {
// Capability index
agentCard.capabilities.forEach((capability) => {
if (!this.capabilityIndex.has(capability.name)) {
this.capabilityIndex.set(capability.name, new Set());
}
this.capabilityIndex.get(capability.name)!.add(agentId);
});
// Service index
agentCard.services.forEach((service) => {
if (!this.serviceIndex.has(service.name)) {
this.serviceIndex.set(service.name, new Set());
}
this.serviceIndex.get(service.name)!.add(agentId);
if (!this.serviceIndex.has(service.method)) {
this.serviceIndex.set(service.method, new Set());
}
this.serviceIndex.get(service.method)!.add(agentId);
});
// Type index
if (!this.typeIndex.has(agentCard.metadata.type)) {
this.typeIndex.set(agentCard.metadata.type, new Set());
}
this.typeIndex.get(agentCard.metadata.type)!.add(agentId);
} else {
// Remove from capability index
agentCard.capabilities.forEach((capability) => {
const capabilitySet = this.capabilityIndex.get(capability.name);
if (capabilitySet) {
capabilitySet.delete(agentId);
if (capabilitySet.size === 0) {
this.capabilityIndex.delete(capability.name);
}
}
});
// Remove from service index
agentCard.services.forEach((service) => {
const nameSet = this.serviceIndex.get(service.name);
if (nameSet) {
nameSet.delete(agentId);
if (nameSet.size === 0) {
this.serviceIndex.delete(service.name);
}
}
const methodSet = this.serviceIndex.get(service.method);
if (methodSet) {
methodSet.delete(agentId);
if (methodSet.size === 0) {
this.serviceIndex.delete(service.method);
}
}
});
// Remove from type index
const typeSet = this.typeIndex.get(agentCard.metadata.type);
if (typeSet) {
typeSet.delete(agentId);
if (typeSet.size === 0) {
this.typeIndex.delete(agentCard.metadata.type);
}
}
}
}
/**
* Filter agents by capabilities
*/
private filterByCapabilities(
candidates: AgentRegistryEntry[],
requiredCapabilities: string[],
): AgentRegistryEntry[] {
return candidates.filter((entry) => {
const agentCapabilities = entry.agentCard.capabilities.map(
(cap) => cap.name,
);
return requiredCapabilities.every((required) =>
agentCapabilities.includes(required),
);
});
}
/**
* Apply discovery filters
*/
private applyFilters(
candidates: AgentRegistryEntry[],
filters: DiscoveryFilter[],
): AgentRegistryEntry[] {
return candidates.filter((entry) => {
return filters.every((filter) =>
this.evaluateFilter(entry.agentCard, filter),
);
});
}
/**
* Evaluate a single filter
*/
private evaluateFilter(
agentCard: AgentCard,
filter: DiscoveryFilter,
): boolean {
try {
// Track filter usage
const filterKey = `${filter.field}:${filter.operator}`;
const count = this.metrics.filterUsage.get(filterKey) || 0;
this.metrics.filterUsage.set(filterKey, count + 1);
const fieldValue = this.getNestedValue(agentCard, filter.field);
switch (filter.operator) {
case "eq":
return fieldValue === filter.value;
case "ne":
return fieldValue !== filter.value;
case "gt":
return Number(fieldValue) > Number(filter.value);
case "lt":
return Number(fieldValue) < Number(filter.value);
case "gte":
return Number(fieldValue) >= Number(filter.value);
case "lte":
return Number(fieldValue) <= Number(filter.value);
case "in":
return (
Array.isArray(filter.value) && filter.value.includes(fieldValue)
);
case "contains":
if (Array.isArray(fieldValue)) {
return fieldValue.some((item) =>
this.deepIncludes(item, filter.value),
);
}
if (typeof fieldValue === "string") {
return fieldValue.includes(filter.value);
}
return this.deepIncludes(fieldValue, filter.value);
default:
throw new Error(`Invalid filter operator: ${filter.operator}`);
}
} catch (error) {
this.logger.warn("Filter evaluation failed", {
field: filter.field,
operator: filter.operator,
error: (error as Error).message,
});
return false;
}
}
/**
* Filter agents by distance
*/
private filterByDistance(
candidates: AgentRegistryEntry[],
maxDistance: number,
): AgentRegistryEntry[] {
// Simplified distance calculation - in practice this would use network topology
return candidates.filter((entry) => {
const distance = this.calculateDistance(entry.agentCard);
return distance <= maxDistance;
});
}
/**
* Calculate distance to agent (simplified)
*/
private calculateDistance(agentCard: AgentCard): number {
// Simple heuristic based on agent type and load
let distance = 1;
if (agentCard.metadata.type === "coordinator") distance = 1;
else if (agentCard.metadata.type === "specialist") distance = 2;
else distance = 3;
// Add load penalty
distance += Math.floor(agentCard.metadata.load * 2);
return distance;
}
/**
* Check version compatibility
*/
private isVersionCompatible(
agentVersion: string,
requiredVersion: string,
): boolean {
try {
const agentVer = this.parseVersion(agentVersion);
const requiredVer = this.parseVersion(requiredVersion);
// Major version must match
if (agentVer.major !== requiredVer.major) return false;
// Agent version must be >= required version
if (agentVer.minor < requiredVer.minor) return false;
if (
agentVer.minor === requiredVer.minor &&
agentVer.patch < requiredVer.patch
)
return false;
return true;
} catch (error) {
return false;
}
}
/**
* Parse version string
*/
private parseVersion(version: string): {
major: number;
minor: number;
patch: number;
} {
const parts = version.split(".").map(Number);
return {
major: parts[0] || 0,
minor: parts[1] || 0,
patch: parts[2] || 0,
};
}
/**
* Get nested value from object
*/
private getNestedValue(obj: any, path: string): any {
return path.split(".").reduce((current, key) => current?.[key], obj);
}
/**
* Deep includes check for complex objects
*/
private deepIncludes(haystack: any, needle: any): boolean {
if (haystack === needle) return true;
if (typeof haystack === "object" && typeof needle === "object") {
if (Array.isArray(needle)) {
return needle.every((item) => this.deepIncludes(haystack, item));
}
for (const [key, value] of Object.entries(needle)) {
if (!this.deepIncludes(haystack[key], value)) {
return false;
}
}
return true;
}
return false;
}
/**
* Clean up expired agents
*/
private cleanupExpiredAgents(): void {
const now = Date.now();
const expiredAgents: AgentId[] = [];
this.agentRegistry.forEach((entry, agentId) => {
if (entry.expiresAt && now > entry.expiresAt) {
expiredAgents.push(agentId);
}
});
expiredAgents.forEach((agentId) => {
this.unregisterAgent(agentId);
});
if (expiredAgents.length > 0) {
this.logger.info(`Cleaned up ${expiredAgents.length} expired agents`);
}
}
}