@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.
638 lines (564 loc) • 16.6 kB
text/typescript
/**
* A2A Tool Wrapper Base Class
*
* Provides a unified interface for wrapping MCP tools with A2A (Agent-to-Agent) capabilities.
* This base class handles the transformation of MCP tool calls into A2A-compliant messages,
* manages security contexts, and provides performance optimization hooks.
*/
import { EventEmitter } from "events";
import { Logger } from "../../../utils/logger.js";
import { CacheManager } from "../../../core/cache-manager.js";
import { PerformanceMonitor } from "../../../monitoring/performance-monitor.js";
import {
MCPToolName,
MCPToolParameters,
MCPToolReturnType,
MCPToolResult,
} from "../../../types/mcp-tools.js";
export interface A2AToolContext {
agentId: string;
agentType: string;
sessionId: string;
trustLevel: "untrusted" | "basic" | "verified" | "trusted" | "privileged";
capabilities: string[];
metadata: Record<string, any>;
timestamp: number;
}
export interface A2ACapability {
name: string;
version: string;
description: string;
parameters: {
type: "object";
properties: Record<string, any>;
required: string[];
};
security: {
minTrustLevel: string;
requiredCapabilities: string[];
rateLimits?: {
perMinute: number;
perHour: number;
perDay: number;
};
};
performance: {
avgLatency: number;
resourceUsage: "low" | "medium" | "high";
cacheable: boolean;
cacheStrategy?: "aggressive" | "conservative" | "none";
};
}
export interface A2AToolInvocation {
toolId: string;
capabilityName: string;
parameters: Record<string, any>;
context: A2AToolContext;
requestId: string;
timestamp: number;
priority: "low" | "medium" | "high" | "critical";
}
export interface A2AToolResponse {
requestId: string;
toolId: string;
success: boolean;
data?: any;
error?: {
code: string;
message: string;
details?: any;
};
metadata: {
executionTime: number;
resourceUsage: {
cpu: number;
memory: number;
network?: number;
};
cached: boolean;
trustVerified: boolean;
securityFlags: string[];
};
timestamp: number;
}
export interface A2AToolMetrics {
invocations: number;
successRate: number;
avgLatency: number;
cacheHitRate: number;
errorCounts: Record<string, number>;
resourceUtilization: {
cpu: number;
memory: number;
network: number;
};
securityEvents: number;
lastInvocation: number;
}
/**
* Abstract base class for A2A tool wrappers
*/
export abstract class A2AToolWrapper extends EventEmitter {
protected logger: Logger;
protected cache: CacheManager;
protected performanceMonitor: PerformanceMonitor;
protected metrics: A2AToolMetrics = {
invocations: 0,
successRate: 0,
avgLatency: 0,
cacheHitRate: 0,
errorCounts: {},
resourceUtilization: { cpu: 0, memory: 0, network: 0 },
securityEvents: 0,
lastInvocation: 0,
};
constructor(
protected toolId: string,
protected capability: A2ACapability,
) {
super();
this.logger = new Logger(`A2AToolWrapper:${toolId}`);
this.cache = new CacheManager();
this.performanceMonitor = new PerformanceMonitor();
this.logger.info("A2A Tool Wrapper initialized", {
toolId,
capability: capability.name,
version: capability.version,
});
}
/**
* Main entry point for A2A tool invocation
*/
async invoke(invocation: A2AToolInvocation): Promise<A2AToolResponse> {
const startTime = Date.now();
this.metrics.invocations++;
this.metrics.lastInvocation = startTime;
try {
// Validate security context
const securityValidation = await this.validateSecurity(
invocation.context,
);
if (!securityValidation.valid) {
return this.createErrorResponse(
invocation,
"SECURITY_VIOLATION",
securityValidation.reason || "Security validation failed",
startTime,
);
}
// Check rate limits
const rateLimitCheck = await this.checkRateLimits(invocation.context);
if (!rateLimitCheck.allowed) {
return this.createErrorResponse(
invocation,
"RATE_LIMIT_EXCEEDED",
rateLimitCheck.reason || "Rate limit exceeded",
startTime,
);
}
// Check cache first if cacheable
let cachedResult: A2AToolResponse | null = null;
if (this.capability.performance.cacheable) {
cachedResult = await this.getCachedResult(invocation);
if (cachedResult) {
this.metrics.cacheHitRate =
(this.metrics.cacheHitRate + 1) / this.metrics.invocations;
cachedResult.metadata.cached = true;
return cachedResult;
}
}
// Transform A2A invocation to MCP parameters
const mcpParams = await this.transformToMCP(invocation);
// Execute the underlying MCP tool
const mcpResult = await this.executeMCPTool(
mcpParams,
invocation.context,
);
// Transform MCP result back to A2A response
const a2aResponse = await this.transformFromMCP(
mcpResult,
invocation,
startTime,
);
// Cache the result if cacheable and successful
if (this.capability.performance.cacheable && a2aResponse.success) {
await this.cacheResult(invocation, a2aResponse);
}
// Update metrics
this.updateMetrics(a2aResponse, startTime);
// Emit events for monitoring
this.emit("tool_invoked", {
toolId: this.toolId,
success: a2aResponse.success,
duration: a2aResponse.metadata.executionTime,
agentId: invocation.context.agentId,
});
return a2aResponse;
} catch (error: any) {
this.logger.error("A2A tool invocation failed", {
toolId: this.toolId,
requestId: invocation.requestId,
error: error.message,
});
const errorResponse = this.createErrorResponse(
invocation,
"EXECUTION_ERROR",
error.message,
startTime,
error,
);
this.updateErrorMetrics(error);
this.emit("tool_error", { toolId: this.toolId, error, invocation });
return errorResponse;
}
}
/**
* Get the A2A capability definition for this tool
*/
getCapability(): A2ACapability {
return { ...this.capability };
}
/**
* Get current metrics for this tool
*/
getMetrics(): A2AToolMetrics {
return { ...this.metrics };
}
/**
* Reset metrics (useful for testing or monitoring cycles)
*/
resetMetrics(): void {
this.metrics = {
invocations: 0,
successRate: 0,
avgLatency: 0,
cacheHitRate: 0,
errorCounts: {},
resourceUtilization: { cpu: 0, memory: 0, network: 0 },
securityEvents: 0,
lastInvocation: 0,
};
}
/**
* Validate security context for the invocation
*/
protected async validateSecurity(context: A2AToolContext): Promise<{
valid: boolean;
reason?: string;
securityFlags: string[];
}> {
const securityFlags: string[] = [];
// Check minimum trust level
const trustLevels = [
"untrusted",
"basic",
"verified",
"trusted",
"privileged",
];
const requiredIndex = trustLevels.indexOf(
this.capability.security.minTrustLevel,
);
const actualIndex = trustLevels.indexOf(context.trustLevel);
if (actualIndex < requiredIndex) {
return {
valid: false,
reason: `Insufficient trust level: required ${this.capability.security.minTrustLevel}, got ${context.trustLevel}`,
securityFlags: ["INSUFFICIENT_TRUST_LEVEL"],
};
}
// Check required capabilities
const missingCapabilities =
this.capability.security.requiredCapabilities.filter(
(cap) => !context.capabilities.includes(cap),
);
if (missingCapabilities.length > 0) {
return {
valid: false,
reason: `Missing required capabilities: ${missingCapabilities.join(", ")}`,
securityFlags: ["MISSING_CAPABILITIES"],
};
}
// Additional security checks can be implemented by subclasses
const additionalChecks =
await this.performAdditionalSecurityChecks(context);
securityFlags.push(...additionalChecks.securityFlags);
return {
valid: additionalChecks.valid,
reason: additionalChecks.reason,
securityFlags,
};
}
/**
* Check rate limits for the agent
*/
protected async checkRateLimits(context: A2AToolContext): Promise<{
allowed: boolean;
reason?: string;
retryAfter?: number;
}> {
if (!this.capability.security.rateLimits) {
return { allowed: true };
}
const limits = this.capability.security.rateLimits;
const now = Date.now();
const agentKey = `ratelimit:${this.toolId}:${context.agentId}`;
// Check per-minute limit
const minuteKey = `${agentKey}:minute:${Math.floor(now / 60000)}`;
const minuteCount = (await this.cache.get<number>(minuteKey)) || 0;
if (minuteCount >= limits.perMinute) {
return {
allowed: false,
reason: "Per-minute rate limit exceeded",
retryAfter: 60 - (Math.floor(now / 1000) % 60),
};
}
// Check per-hour limit
const hourKey = `${agentKey}:hour:${Math.floor(now / 3600000)}`;
const hourCount = (await this.cache.get<number>(hourKey)) || 0;
if (hourCount >= limits.perHour) {
return {
allowed: false,
reason: "Per-hour rate limit exceeded",
retryAfter: 3600 - (Math.floor(now / 1000) % 3600),
};
}
// Check per-day limit
const dayKey = `${agentKey}:day:${Math.floor(now / 86400000)}`;
const dayCount = (await this.cache.get<number>(dayKey)) || 0;
if (dayCount >= limits.perDay) {
return {
allowed: false,
reason: "Per-day rate limit exceeded",
retryAfter: 86400 - (Math.floor(now / 1000) % 86400),
};
}
// Increment counters
await Promise.all([
this.cache.set(minuteKey, minuteCount + 1, 60000),
this.cache.set(hourKey, hourCount + 1, 3600000),
this.cache.set(dayKey, dayCount + 1, 86400000),
]);
return { allowed: true };
}
/**
* Get cached result if available
*/
protected async getCachedResult(
invocation: A2AToolInvocation,
): Promise<A2AToolResponse | null> {
const cacheKey = this.generateCacheKey(invocation);
const cached = await this.cache.get<A2AToolResponse>(cacheKey);
if (cached) {
this.logger.debug("Cache hit for A2A tool invocation", {
toolId: this.toolId,
requestId: invocation.requestId,
cacheKey,
});
}
return cached;
}
/**
* Cache successful result
*/
protected async cacheResult(
invocation: A2AToolInvocation,
response: A2AToolResponse,
): Promise<void> {
if (!response.success) return;
const cacheKey = this.generateCacheKey(invocation);
const strategy =
this.capability.performance.cacheStrategy || "conservative";
let ttl: number;
switch (strategy) {
case "aggressive":
ttl = 300000; // 5 minutes
break;
case "conservative":
ttl = 60000; // 1 minute
break;
default:
return; // No caching
}
await this.cache.set(cacheKey, response, ttl);
}
/**
* Generate cache key for invocation
*/
protected generateCacheKey(invocation: A2AToolInvocation): string {
const keyData = {
toolId: this.toolId,
parameters: invocation.parameters,
agentId: invocation.context.agentId,
trustLevel: invocation.context.trustLevel,
};
return `a2a_tool:${Buffer.from(JSON.stringify(keyData)).toString("base64")}`;
}
/**
* Create error response
*/
protected createErrorResponse(
invocation: A2AToolInvocation,
code: string,
message: string,
startTime: number,
details?: any,
): A2AToolResponse {
return {
requestId: invocation.requestId,
toolId: this.toolId,
success: false,
error: {
code,
message,
details,
},
metadata: {
executionTime: Date.now() - startTime,
resourceUsage: { cpu: 0, memory: 0, network: 0 },
cached: false,
trustVerified: false,
securityFlags: [],
},
timestamp: Date.now(),
};
}
/**
* Update metrics after successful invocation
*/
protected updateMetrics(response: A2AToolResponse, startTime: number): void {
const executionTime = Date.now() - startTime;
// Update average latency
this.metrics.avgLatency =
(this.metrics.avgLatency * (this.metrics.invocations - 1) +
executionTime) /
this.metrics.invocations;
// Update success rate
const successes = Math.floor(
this.metrics.successRate * (this.metrics.invocations - 1),
);
this.metrics.successRate =
(successes + (response.success ? 1 : 0)) / this.metrics.invocations;
// Update resource utilization
this.metrics.resourceUtilization.cpu =
(this.metrics.resourceUtilization.cpu +
response.metadata.resourceUsage.cpu) /
2;
this.metrics.resourceUtilization.memory =
(this.metrics.resourceUtilization.memory +
response.metadata.resourceUsage.memory) /
2;
this.metrics.resourceUtilization.network =
(this.metrics.resourceUtilization.network +
(response.metadata.resourceUsage.network || 0)) /
2;
}
/**
* Update error metrics
*/
protected updateErrorMetrics(error: Error): void {
const errorType = error.constructor.name;
this.metrics.errorCounts[errorType] =
(this.metrics.errorCounts[errorType] || 0) + 1;
}
// Abstract methods to be implemented by concrete tool wrappers
/**
* Transform A2A invocation parameters to MCP format
*/
protected abstract transformToMCP(
invocation: A2AToolInvocation,
): Promise<any>;
/**
* Execute the underlying MCP tool
*/
protected abstract executeMCPTool(
params: any,
context: A2AToolContext,
): Promise<MCPToolResult>;
/**
* Transform MCP result to A2A response format
*/
protected abstract transformFromMCP(
result: MCPToolResult,
invocation: A2AToolInvocation,
startTime: number,
): Promise<A2AToolResponse>;
/**
* Perform additional security checks (can be overridden by subclasses)
*/
protected async performAdditionalSecurityChecks(
context: A2AToolContext,
): Promise<{
valid: boolean;
reason?: string;
securityFlags: string[];
}> {
return {
valid: true,
securityFlags: [],
};
}
}
/**
* Utility functions for A2A tool management
*/
export class A2AToolUtils {
/**
* Validate A2A capability definition
*/
static validateCapability(capability: A2ACapability): {
valid: boolean;
errors: string[];
} {
const errors: string[] = [];
if (!capability.name || typeof capability.name !== "string") {
errors.push("Capability name is required and must be a string");
}
if (!capability.version || typeof capability.version !== "string") {
errors.push("Capability version is required and must be a string");
}
if (!capability.description || typeof capability.description !== "string") {
errors.push("Capability description is required and must be a string");
}
if (!capability.parameters || typeof capability.parameters !== "object") {
errors.push("Capability parameters definition is required");
}
if (!capability.security || typeof capability.security !== "object") {
errors.push("Capability security configuration is required");
}
if (!capability.performance || typeof capability.performance !== "object") {
errors.push("Capability performance configuration is required");
}
return {
valid: errors.length === 0,
errors,
};
}
/**
* Create A2A context from basic parameters
*/
static createContext(
agentId: string,
agentType: string,
sessionId: string,
trustLevel: A2AToolContext["trustLevel"] = "basic",
capabilities: string[] = [],
metadata: Record<string, any> = {},
): A2AToolContext {
return {
agentId,
agentType,
sessionId,
trustLevel,
capabilities,
metadata,
timestamp: Date.now(),
};
}
/**
* Generate unique request ID
*/
static generateRequestId(): string {
return `a2a_req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}