@multi-agent/a2a
Version:
A2A Protocol Framework
2,294 lines (2,267 loc) • 79.7 kB
TypeScript
import * as grpc from '@grpc/grpc-js';
import { DynamicStructuredTool } from '@langchain/core/tools';
/**
* IO 计量模块类型定义
*
* 提供基于输入输出的 Token 计量和计费功能
*/
/**
* IO 承诺(由协议层生成,使用平台签名密钥)
*
* 承诺包含内容哈希和 Token 数量,由平台签名确保不可伪造
*/
interface IOCommitment {
/** 承诺唯一 ID */
commitmentId: string;
/** 关联的调用链 ID */
traceId: string;
/** 内容哈希(SHA-256,不含原文,保护隐私) */
contentHash: string;
/** Token 数量 */
tokens: number;
/** 时间戳 */
timestamp: number;
/** 平台签名(证明这是协议层计算的,非伪造) */
signature: string;
}
/**
* 已验证的 IO 指标
*
* 包含输入和输出的承诺,由协议层自动生成和上报
*
*/
interface VerifiedIOMetrics {
/** 指标唯一 ID */
metricsId: string;
/** 关联的调用链 ID(整个请求链共用) */
traceId: string;
/** Agent ID */
agentId: string;
/** 调用的技能名称 */
skill: string;
/** 调用开始时间(ISO 8601 格式) */
startedAt: string;
/** 输入承诺 */
inputCommitment: IOCommitment;
/** 输出承诺 */
outputCommitment: IOCommitment;
/** 处理耗时(毫秒) */
duration: number;
/** 调用者用户 ID(可选,由 Auth 插件解析 token 获得) */
userId?: string;
}
/**
* Agent 定价配置
*/
interface AgentPricing {
/** 输入单价(分 / 1K tokens) */
inputTokenPrice: number;
/** 输出单价(分 / 1K tokens) */
outputTokenPrice: number;
}
/**
* IO 计量提供者接口
*
* 平台方实现此接口,提供计量服务
*
* @example
* ```typescript
* const provider: IOMetricsProvider = {
* verifyIntegrity: async (params) => {
* // 验证 SDK 完整性,返回签名密钥
* return { valid: true, signingKey: 'xxx' }
* },
* reportMetrics: async (metrics) => {
* // 上报指标到平台
* await fetch('/api/metrics/report', { body: JSON.stringify(metrics) })
* },
* }
* ```
*/
interface IOMetricsProvider {
/**
* 验证 SDK 完整性,获取签名密钥
*
* @param params 验证参数
* @returns 验证结果和签名密钥
*/
verifyIntegrity(params: {
/** SDK 版本 */
sdkVersion: string;
/** SDK 核心模块哈希 */
sdkHash: string;
/** Agent ID */
agentId: string;
}): Promise<{
/** 验证是否通过 */
valid: boolean;
/** 签名密钥(验证通过时返回) */
signingKey?: string;
/** 错误信息(验证失败时返回) */
error?: string;
}>;
/**
* 上报已验证的 IO 指标
*
* @param metrics 指标数据
*/
reportMetrics(metrics: VerifiedIOMetrics): Promise<void>;
/**
* 获取 Agent 定价(可选)
*
* @param agentId Agent ID
* @param skill 技能名称
* @returns 定价配置
*/
getPricing?(agentId: string, skill: string): Promise<AgentPricing>;
/**
* 预调用检查(在 LLM 调用前验证用户身份和余额)
*
* 这是计费安全的关键:在执行任何 LLM 调用之前先验证用户有足够余额
* 如果不实现此方法,将跳过预调用检查(向后兼容)
*
* @param params 检查参数
* @returns 检查结果
*/
preCallCheck?(params: {
/** 用户 ID(从 x-user-id metadata 获取) */
userId?: string;
/** Agent ID */
agentId: string;
/** 调用的技能名称 */
skill: string;
/** 调用链 ID */
traceId: string;
}): Promise<{
/** 是否允许调用 */
allowed: boolean;
/** 拒绝原因(allowed=false 时返回) */
reason?: string;
/** 错误代码(allowed=false 时返回) */
code?: 'UNAUTHORIZED' | 'INSUFFICIENT_BALANCE' | 'USER_NOT_FOUND' | 'RATE_LIMITED' | string;
}>;
}
/**
* A2A v7 Framework - TypeScript Types
*
* 设计理念:
* 1. 协议层+业务层分离:协议消息固定3种,业务消息完全开放
* 2. 扁平化消息结构:所有字段直接在Message级别
* 3. 类型安全:使用Discriminated Union提供完美的类型推断
*/
/**
* A2A 框架错误码
*
* 用于标识框架层面的错误类型,便于客户端根据错误码进行处理
*/
declare const ErrorCode: {
/** Handler 被 beforeHandler 钩子中止 */
readonly HANDLER_ABORTED: "HANDLER_ABORTED";
/** Handler 执行过程中抛出异常 */
readonly HANDLER_ERROR: "HANDLER_ERROR";
/** 请求的 Skill 不存在 */
readonly SKILL_NOT_FOUND: "SKILL_NOT_FOUND";
/** call 消息格式无效(如缺少 skill 字段) */
readonly INVALID_CALL_MESSAGE: "INVALID_CALL_MESSAGE";
/** 服务器内部错误 */
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
};
type ErrorCode = (typeof ErrorCode)[keyof typeof ErrorCode];
/**
* Message - 统一消息结构
*
* 所有消息(协议消息 + 业务消息)都使用相同的数据结构
*
* 设计理念:
* - 协议消息和业务消息结构完全一致
* - 通过 type 字段区分消息类型
* - text 提供人类可读描述
* - data 提供结构化数据(JSON序列化)
* - from 标识消息来源 Agent(协议层自动注入)
*
* 框架自动管理字段:
* - messageId: 消息唯一ID(每条消息自动生成)
* - timestamp: Unix 时间戳毫秒
* - from: 消息来源 Agent 的 AgentCard(协议层自动注入)
*
* gRPC Metadata 传递字段(单一数据源,通过 ctx.metadata 访问):
* - x-trace-id: 链路追踪ID,格式: tr_{timestamp}_{random}
* - x-session-id: 会话ID(用于跟踪对话历史)
* - x-user-id: 用户ID(用于计费归属)
* - x-span-id: 调用链 Span ID
*
* 协议消息类型(框架保留):
* - 'call': 调用Agent方法
* - 'cancel': 取消执行
* - 'agent-mount': 挂载请求(Tool Agent 挂载到 Host Agent)
* - 'agent-unmount': 卸载请求(Tool Agent 从 Host Agent 卸载)
*
* 业务消息类型(应用自定义):
* - 'error': 错误消息
* - 'question': 问题
* - 'answer': 回答
* - 'progress': 进度
* - 'todolist': 任务列表
* - 'plan': 计划
* - 'done': Agent 工作流完成(携带总结信息和关键数据)
* - ... 其他自定义类型
*/
interface Message {
messageId?: string;
timestamp?: number;
type: string;
text: string;
data?: any;
from?: AgentCard;
}
/**
* TLS 证书配置
*
* 当 address 使用 a2as:// 协议时必须提供
*/
interface TLSConfig {
/**
* 证书文件路径(PEM 格式)
*
* @example '/path/to/cert.pem'
*/
cert: string;
/**
* 私钥文件路径(PEM 格式)
*
* @example '/path/to/key.pem'
*/
key: string;
/**
* CA 证书文件路径(可选,用于客户端证书验证)
*
* @example '/path/to/ca.pem'
*/
ca?: string;
}
interface AgentConfig {
agentId: string;
name: string;
version: string;
description: string;
/**
* Agent 的网络地址
*
* 格式: a2a[s]://host:port
*
* - a2a:// - 无 TLS
* - a2as:// - TLS 加密(需要配置 tls 选项)
*
* @example
* // 监听所有网络接口(无 TLS)
* address: 'a2a://0.0.0.0:50054'
*
* // 只监听本地(无 TLS)
* address: 'a2a://127.0.0.1:50054'
*
* // TLS 加密(需要配置 tls)
* address: 'a2as://0.0.0.0:50054'
*/
address: string;
/**
* TLS 证书配置
*
* 当 address 使用 a2as:// 协议时必须提供
*
* @example
* ```typescript
* const server = createAgentServer({
* address: 'a2as://0.0.0.0:50054',
* tls: {
* cert: '/path/to/cert.pem',
* key: '/path/to/key.pem',
* },
* // ...
* })
* ```
*/
tls?: TLSConfig;
skills: SkillDefinition[];
/**
* 默认技能名称
*
* 必须是 skills 中某个技能的 name
*/
defaultSkill: string;
/**
* IO 计量提供者(可选)
*
* 配置后启用协议层 IO 计量功能:
* - 自动计算输入/输出 Token 数量
* - 自动生成带签名的 IO 承诺
* - 自动上报指标到平台
*
* 不配置则不启用计费功能
*
* @example
* ```typescript
* import { createMetricsProvider } from '@agent-zhipin/metrics-provider'
*
* const server = createAgentServer({
* agentId: 'my-agent',
* // ...
* metricsProvider: createMetricsProvider({
* platformUrl: 'https://agent-zhipin.com/api',
* developerApiKey: 'YOUR_API_KEY'
* }),
* })
* ```
*/
metricsProvider?: IOMetricsProvider;
}
interface SkillInfo {
name: string;
description?: string;
/**
* JSON Schema 格式的输入参数定义(用于 LangChain Tools)
*
* 当提供此字段时,该技能可以被其他 Agent 作为 LangChain Tool 调用
*
* @example
* inputSchema: {
* type: 'object',
* properties: {
* path: {
* type: 'string',
* description: '文件路径(相对或绝对)'
* },
* content: {
* type: 'string',
* description: '文件内容'
* }
* },
* required: ['path', 'content']
* }
*/
inputSchema?: {
type: 'object';
properties: Record<string, {
type: string;
description?: string;
enum?: any[];
default?: any;
items?: any;
minimum?: number;
maximum?: number;
}>;
required?: string[];
};
/**
* JSON Schema 格式的输出结果定义(可选)
*
* 用于描述技能的返回值结构
*/
outputSchema?: {
type: 'object' | 'string' | 'number' | 'boolean' | 'array';
properties?: Record<string, {
type: string;
description?: string;
}>;
items?: any;
description?: string;
};
}
interface AgentCard {
agentId: string;
name: string;
version: string;
description: string;
skills: SkillInfo[];
/**
* 默认技能名称
*
* 指向 skills 中的某个技能,作为 Agent 的默认入口。
* 该技能通常包含标准工作流:澄清需求 → 生成 TodoList → 执行任务 → 返回结果
*
* Host Agent 在编排时会优先调用此技能。
*
* 注意:defaultSkill 必须存在于 skills 数组中。
*
* 示例:'execute', 'process', 'handle'
*/
defaultSkill: string;
/**
* Agent 的访问端点
*
* 由 getAgentCard() RPC 自动填充,基于客户端连接时使用的地址(:authority 头)
*
* 示例:
* - 单机:{ host: 'localhost', port: 50059 }
* - Docker:{ host: 'frontend-agent', port: 50059 }
* - K8s:{ host: 'frontend-agent.default.svc.cluster.local', port: 50059 }
* - 代理注册:{ host: 'localhost', port: 50050, namespace: 'tool-agent' }
*/
endpoint: {
host: string;
port: number;
/**
* 命名空间(可选)
*
* 当 Agent 通过 ParasiteHostPlugin 寄生到另一个 Agent 时,
* namespace 用于标识该 Agent,使调用方能够正确路由请求。
*
* @example
* - undefined: 原生 Agent(直接监听端口)
* - 'tool-agent@user123': 通过 Host Agent 寄生的 Tool Agent
*/
namespace?: string;
/**
* 预构建的 A2A 地址(方便直接使用)
*
* 格式:a2a://host:port[/namespace]
*
* @example
* - 'a2a://localhost:50059'
* - 'a2a://localhost:50050/tool-agent@user123'
*/
address: string;
};
}
/**
* 双向流接口
*
* 使用方式:
* - 发送消息:stream.send(message)
* - 接收消息:for await (const message of stream) { ... }
* - 监听特定消息:stream.onError(handler).onCancel(handler)
*/
interface BidirectionalStream {
/**
* 发送消息到对端
*/
send: (message: Message) => void;
/**
* 实现 async iterator,用于接收消息
*/
[Symbol.asyncIterator]: () => AsyncIterator<Message>;
/**
* 结束发送(half-close)
*/
end: () => void;
/**
* 取消流并清理资源
*/
cancel: (reason?: string) => void;
/**
* 监听特定类型的消息(可选)
* @param type 消息类型
* @param handler 处理函数
* @returns 返回自身,支持链式调用
*/
on?: (type: string, handler: (message: Message) => void) => BidirectionalStream;
}
interface Context {
readonly streamId: string;
/**
* 双向流
*
* @example
* // 发送业务消息(发送问题)
* ctx.stream.send({
* messageId: generateId(),
* timestamp: Date.now(),
* type: 'question',
* text: '请选择框架:\n1. React\n2. Vue',
* data: encodeJSON({ questionId: 'q1', options: ['React', 'Vue'] })
* })
*
* // 接收业务消息(接收回答)
* for await (const message of ctx.stream) {
* if (message.type === 'answer') {
* const answerData = decodeJSON(message.data)
* console.log('用户回答:', message.text)
* console.log('关联问题:', answerData.questionId)
* }
* }
*/
readonly stream: BidirectionalStream;
/**
* gRPC metadata(原始访问)
*
* 开发者可以从 metadata 中获取任何自定义信息,例如:
* - 认证 token
* - 请求追踪 ID
* - 会话 ID
* - 任意自定义字段
*
* @example
* // 获取认证 token
* const authToken = ctx.metadata.get('auth-token')?.[0]?.toString('utf-8')
*
* // 获取会话 ID
* const sessionId = ctx.metadata.get('session-id')?.[0]?.toString('utf-8')
*/
readonly metadata: grpc.Metadata;
/**
* gRPC 原始调用对象(仅 gRPC 连接可用)
*
* 暴露底层 gRPC 调用,提供最大灵活性。常用方法:
* - call.getHost(): 获取客户端请求的目标地址(如 "8.153.165.230:50054")
* - call.getPeer(): 获取客户端地址
* - call.cancelled: 检查是否已取消
*
* 注意:WebSocket 连接时此字段为 undefined
*
* @example
* // 获取客户端连接的目标地址
* if (ctx.call) {
* const hostAddress = ctx.call.getHost() // "8.153.165.230:50054"
* }
*/
readonly call?: grpc.ServerDuplexStream<any, any>;
/**
* 获取自身的 AgentCard
*/
getAgentCard: () => AgentCard;
/**
* 从 gRPC metadata 中获取并反序列化 JSON 数据
*
* @param key - metadata key
* @returns 反序列化后的对象,如果不存在或解析失败则返回 undefined
*
* @example
* // 获取自定义数据
* const customData = ctx.getMetadata<{ userId: string }>('custom-data-bin')
* if (customData) {
* console.log('Custom data:', customData)
* }
*/
getMetadata: <T = any>(key: string) => T | undefined;
/**
* 触发当前 handler 的 call 消息
*
* 包含消息基本信息:
* - messageId: 消息唯一ID
* - timestamp: 消息时间戳
* - type: 消息类型
* - text: 消息文本
* - data: 结构化数据
*
* 注意:traceId/sessionId 等追踪字段通过 gRPC Metadata 传递,使用 ctx.metadata 访问
*
* @example
* // 获取 traceId 用于日志追踪(从 gRPC Metadata,内联获取)
* logger.info('收到请求', { traceId: ctx.metadata.get('x-trace-id') })
*
* // 调用其他 Agent 时传递 metadata
* const stream = await client.call('execute', params, {
* metadata: ctx.metadata.getMap()
* })
*/
readonly message: Message;
/**
* 取消信号(框架自动管理)
*
* 当 Client 发送 cancel 消息时,框架会自动触发此 signal 的 abort。
* Handler 可以直接将此 signal 传递给支持 AbortSignal 的 API:
* - LLM 调用(LangChain)
* - fetch 请求
* - A2A client.call()
*
* @example
* const handler = async (params, ctx) => {
* // 直接使用 ctx.signal,无需手动创建 AbortController
* const response = await llm.invoke(messages, {
* signal: ctx.signal
* })
*
* // 循环中检查取消状态
* while (hasMore) {
* if (ctx.signal.aborted) return
* await doSomething()
* }
* }
*/
readonly signal: AbortSignal;
}
/**
* 技能 Handler 类型
*
* 纯函数:接收参数和上下文,返回结果
*/
type SkillHandler<TParams = any, TResult = any> = (params: TParams, ctx: Context) => Promise<TResult>;
/**
* Handler 钩子配置
*
* 传递给 createHandler 工厂函数,用于将钩子与原始 handler 组合
*/
interface HandlerHooks {
beforeHandler?: BeforeHandlerHook[];
afterHandler?: AfterHandlerHook[];
}
/**
* 技能定义
*
* 使用 handler 属性直接定义技能处理函数。
* Server 内部会自动将 handler 包装为受保护版本,确保 beforeHandler/afterHandler 钩子被正确执行。
*
* @example
* ```typescript
* const config = {
* skills: [
* {
* name: 'execute',
* handler: async (params, ctx) => { ... },
* description: '执行任务',
* }
* ]
* }
* ```
*/
interface SkillDefinition {
/** 技能名称 */
name: string;
/**
* 技能处理函数
*
* Server 在启动时会将此 handler 包装为受保护版本,
* 自动应用 beforeHandler/afterHandler 钩子。
*/
handler: SkillHandler;
/** 技能描述 */
description?: string;
/** 输入参数 JSON Schema */
inputSchema?: SkillInfo['inputSchema'];
/** 输出结果 JSON Schema */
outputSchema?: SkillInfo['outputSchema'];
}
/**
* 钩子返回类型
*
* - 'handled': 已处理,跳过默认处理器,继续下一个消息
* - 'pass': 未处理,继续调用默认处理器
* - 'exit': 已处理,退出消息循环
* - void: 等同于 'pass'
*/
type HookResult = 'handled' | 'pass' | 'exit' | void;
/**
* 消息上下文 - 在钩子间共享数据
*/
interface MessageContext {
/**
* 用户自定义数据存储
* 可以在 beforeMessage 中存储数据,在 afterMessage 中读取
*/
metadata: Map<string, any>;
/**
* gRPC 原始 metadata(只读)
*
* 用于访问客户端传递的认证信息、会话标识等
*
* @example
* // 获取 authorization header
* const authHeader = context.grpcMetadata.get('authorization')?.[0]?.toString()
* // authHeader = 'Bearer <token>'
*
* // 获取自定义 header
* const sessionId = context.grpcMetadata.get('x-session-id')?.[0]?.toString()
*/
grpcMetadata: grpc.Metadata;
/**
* Agent 基础信息
*/
agentId: string;
agentName: string;
/**
* 消息处理开始时间(毫秒时间戳)
*/
startTime: number;
/**
* 请求的目标命名空间(可选)
*
* 当客户端通过 ParasiteHostPlugin 调用寄生注册的 Agent 时,
* 会在 gRPC metadata 中包含 x-agent-namespace 头,
* 框架会自动解析并填充到此字段。
*
* @example
* - undefined: 直接调用原生 Agent
* - 'tool-agent@user123': 调用通过寄生注册的 Tool Agent
*/
namespace?: string;
}
/**
* Before Message Hook
* 在处理任何消息之前调用
*
* 支持短路:
* - 返回 'handled': 消息已处理,跳过后续钩子和默认处理器
* - 返回 'exit': 消息已处理,退出消息循环
* - 返回 'pass' 或 void: 继续执行后续钩子和默认处理器
*/
type BeforeMessageHook = (message: Message, stream: BidirectionalStream, context: MessageContext) => Promise<HookResult>;
/**
* After Message Hook
* 在消息处理完成后调用
*/
type AfterMessageHook = (message: Message, stream: BidirectionalStream, context: MessageContext, result: 'continue' | 'exit') => Promise<void>;
/**
* On Call Hook
* 处理 call 消息
*/
type OnCallHook = (skill: string, params: any, stream: BidirectionalStream, context: MessageContext) => Promise<HookResult>;
/**
* On Cancel Hook
* 处理 cancel 消息
*/
type OnCancelHook = (stream: BidirectionalStream, context: MessageContext) => Promise<void>;
/**
* On Error Hook
* 处理错误
*/
type OnErrorHook = (error: Error, message: Message, stream: BidirectionalStream, context: MessageContext) => Promise<void>;
/**
* On GetAgentCard Hook
* 修改 AgentCard
*/
type OnGetAgentCardHook = (agentCard: AgentCard, context: {
agentId: string;
}) => AgentCard;
/**
* On Start Hook
* Server 启动后触发
*
* 用于执行启动后的初始化逻辑(如主动挂载到远程 Agent)
*
* 注意:
* - 此钩子在 Server 启动成功后立即调用
* - 所有 onStart 钩子会并行执行(不阻塞)
* - 如果钩子执行失败,会打印错误日志但不影响 Server 运行
*/
/**
* SkillHandlers Map
*
* 技能处理函数集合,由 Server 在启动时创建
* 这些 handler 已经组合了 beforeHandler/afterHandler 钩子
*
* @example
* ```typescript
* // 在插件中调用技能
* const handler = skillHandlers.get('execute')
* if (handler) {
* await handler(params, ctx) // 钩子自动执行
* }
* ```
*/
type SkillHandlers = Map<string, SkillHandler>;
/**
* On Start Hook
* Server 启动后钩子
*
* @param agentConfig - Agent 配置
* @param agentCard - Agent 卡片
* @param skillHandlers - 技能处理函数集合
*/
type OnStartHook = (agentConfig: AgentConfig, agentCard: AgentCard, skillHandlers: SkillHandlers) => Promise<void>;
/**
* On Message Hook
* 通用消息处理钩子(在 beforeMessage 之后、onCall/onCancel 之前执行)
*
* 用于处理自定义消息类型(如 agent-mount、agent-unmount 等插件定义的消息)
*
* 执行顺序:
* beforeMessage → onMessage → onCall/onCancel → 默认处理器 → afterMessage
*
* 返回值:
* - 'handled': 消息已处理,跳过后续钩子和默认处理器,继续下一个消息
* - 'pass': 消息未处理,继续执行后续钩子或默认处理器
* - 'exit': 消息已处理,退出消息循环
* - void: 等同于 'pass'
*/
type OnMessageHook = (message: Message, stream: BidirectionalStream, context: MessageContext) => Promise<HookResult>;
/**
* Handler 上下文 - beforeHandler/afterHandler 钩子共享
*
* 提供 Handler 执行期间的上下文信息,支持钩子间数据传递
*/
interface HandlerContext {
/**
* 技能名称
*/
skill: string;
/**
* 调用参数
*/
params: any;
/**
* 链路追踪 ID
* 格式: tr_{timestamp}_{random}
*/
traceId: string;
/**
* 用户 ID(从 gRPC metadata x-user-id 提取)
* 用于 IO 计量的用户归属
*/
userId?: string;
/**
* Agent ID
*/
agentId: string;
/**
* Handler 开始时间(毫秒时间戳)
*/
startTime: number;
/**
* 钩子间共享数据
* beforeHandler 可存储数据,afterHandler 可读取
*/
metadata: Map<string, any>;
/**
* gRPC 原始 metadata(只读)
*
* 允许插件访问客户端传递的任意 metadata,如:
* - x-parent-span-id: 父调用的 Span ID(调用链追踪)
* - x-custom-header: 自定义字段
*
* @example
* const parentSpanId = ctx.grpcMetadata?.get('x-parent-span-id')?.[0]?.toString()
*/
grpcMetadata?: grpc.Metadata;
/**
* 取消信号(框架自动管理)
*
* 当用户取消请求或插件调用 abort() 时,signal.aborted 变为 true。
* 框架会在 signal.aborted 时跳过 handler 执行。
*
* @example
* // 在 beforeHandler 中检查(通常不需要,框架自动处理)
* if (ctx.signal.aborted) return
*/
signal: AbortSignal;
/**
* 中止当前调用
*
* 触发 signal.aborted = true,框架会跳过 handler 执行。
* 调用前应先通过 stream.send() 发送错误消息。
*
* @example
* // 认证失败时中止
* stream.send({ type: 'error', text: '请先登录' })
* ctx.abort()
*/
abort: () => void;
}
/**
* beforeHandler 返回值
*
* 注意:中止调用请使用 ctx.abort(),不再通过返回值控制
*/
interface BeforeHandlerResult {
/**
* 包装后的 stream
* 用于 IO 计量收集输出
*/
stream?: BidirectionalStream;
}
/**
* afterHandler 结果信息
*/
interface AfterHandlerResultInfo {
/**
* Handler 是否执行成功
*/
success: boolean;
/**
* 错误信息(success=false 时存在)
*/
error?: Error;
/**
* Handler 执行耗时(毫秒)
*/
duration: number;
}
/**
* Before Start Hook
* Server 启动前钩子(可阻塞启动)
*
* 用途:
* - 计费平台注册
* - SDK 完整性验证
* - 获取签名密钥
*
* 特性:
* - 在 Server 绑定端口之前执行
* - 抛出错误可阻止 Server 启动
* - 按注册顺序依次执行
*/
type BeforeStartHook = (agentConfig: AgentConfig) => Promise<void>;
/**
* Before Handler Hook
* Handler 执行前钩子
*
* 用途:
* - 预调用检查(余额验证)
* - 输入计量
* - 包装 stream 收集输出
*
* 特性:
* - 可访问 skill、params、userId 等上下文
* - 返回 { stream } 可包装 stream
* - 调用 ctx.abort() 可中止调用(先发送错误消息,再调用 abort)
*/
type BeforeHandlerHook = (stream: BidirectionalStream, context: HandlerContext) => Promise<BeforeHandlerResult | void>;
/**
* After Handler Hook
* Handler 执行后钩子
*
* 用途:
* - 输出计量
* - 指标上报
* - 调用日志
*
* 特性:
* - 可访问 handler 执行结果和耗时
* - 异步执行,不阻塞响应
*/
type AfterHandlerHook = (stream: BidirectionalStream, context: HandlerContext, result: AfterHandlerResultInfo) => Promise<void>;
/**
* Server Hooks 配置
*
* 每个插件提供单个钩子函数,框架自动收集多个插件的钩子并按顺序执行
*
* @example
* ```typescript
* // 每个插件只提供单个钩子函数
* const loggingPlugin: ServerPlugin = {
* hooks: {
* beforeMessage: async (msg, stream, ctx) => {
* console.log(msg.type)
* }
* }
* }
*
* // 框架自动收集多个插件的钩子
* server
* .use(loggingPlugin) // beforeMessage: fn1
* .use(metricsPlugin) // beforeMessage: fn2
* .use(authPlugin) // beforeMessage: fn3
* // 框架内部按顺序执行: [fn1, fn2, fn3]
* ```
*/
interface ServerHooks {
/**
* 生命周期钩子:Server 启动前
* 用于验证配置、注册到外部服务等
* 抛出错误可阻止 Server 启动
*/
beforeStart?: BeforeStartHook;
/**
* 生命周期钩子:消息处理前
*/
beforeMessage?: BeforeMessageHook;
/**
* 生命周期钩子:消息处理后
*/
afterMessage?: AfterMessageHook;
/**
* 通用消息处理钩子(支持短路)
* 在 beforeMessage 之后、onCall/onCancel 之前执行
*/
onMessage?: OnMessageHook;
/**
* 消息类型钩子:call
*/
onCall?: OnCallHook;
/**
* 消息类型钩子:cancel
*/
onCancel?: OnCancelHook;
/**
* 错误钩子
*/
onError?: OnErrorHook;
/**
* AgentCard 钩子
*/
onGetAgentCard?: OnGetAgentCardHook;
/**
* 生命周期钩子:Server 启动后
* 用于执行启动后的初始化逻辑(如主动挂载到远程 Agent)
*/
onStart?: OnStartHook;
/**
* Handler 生命周期钩子:Handler 执行前
* 用于预调用检查、输入计量、包装 stream
*/
beforeHandler?: BeforeHandlerHook;
/**
* Handler 生命周期钩子:Handler 执行后
* 用于输出计量、指标上报
*/
afterHandler?: AfterHandlerHook;
}
/**
* Internal Server Hooks - 框架内部使用的合并后钩子配置
*
* 与 ServerHooks 的区别:
* - ServerHooks: 用户定义时只能是单个函数(用户不应手动写数组)
* - InternalServerHooks: 框架合并多个插件后统一为函数数组
*
* 设计决策:统一使用数组表示,即使只有一个钩子也用 [hook] 表示
* - 0个插件:undefined
* - 1个插件:[fn]
* - 多个插件:[fn1, fn2, fn3]
*
* @internal 此类型仅供框架内部使用
*/
interface InternalServerHooks {
beforeStart?: BeforeStartHook[];
beforeMessage?: BeforeMessageHook[];
afterMessage?: AfterMessageHook[];
onMessage?: OnMessageHook[];
onCall?: OnCallHook[];
onCancel?: OnCancelHook[];
onError?: OnErrorHook;
onGetAgentCard?: OnGetAgentCardHook[];
onStart?: OnStartHook[];
beforeHandler?: BeforeHandlerHook[];
afterHandler?: AfterHandlerHook[];
}
/**
* Server Plugin - 中间件插件接口
*
* 插件可以提供钩子函数和额外的 API
*
* @example
* const myPlugin: ServerPlugin = {
* hooks: {
* beforeMessage: async (msg, stream, ctx) => {
* console.log('Before:', msg.type)
* }
* },
* // 可选:插件提供的额外 API
* getMountedAgents: () => [...],
* getMountedSkills: () => [...]
* }
*/
interface ServerPlugin {
/**
* 插件提供的钩子配置
*/
hooks: ServerHooks;
/**
* 插件可以提供额外的 API(可选)
* 使用索引签名允许任意方法
*/
[key: string]: any;
}
/**
* Server Builder - 支持链式调用的服务器构建器
*/
interface ServerBuilder {
/**
* 注册插件
* 支持链式调用
*
* @example
* const server = createAgentServer(config)
* .use(createMountManager())
* .use(loggingPlugin)
*/
use: (plugin: ServerPlugin) => ServerBuilder;
/**
* 启动服务器
*/
start: () => Promise<number>;
/**
* 关闭服务器
*/
shutdown: () => Promise<void>;
/**
* gRPC 服务器实例(只读)
*/
readonly grpcServer: grpc.Server;
}
interface ServerInstance {
readonly grpcServer: grpc.Server;
/**
* 技能处理函数集合
*
* 包含所有技能的 handler,已组合 beforeHandler/afterHandler 钩子
* 用于传递给 onStart 钩子,使插件(如 ParasitePlugin)能够调用技能
*/
readonly skillHandlers: SkillHandlers;
start: () => Promise<number>;
shutdown: () => Promise<void>;
}
interface ClientConfig {
agentId: string;
/**
* A2A 地址
*
* 格式: a2a[s]://host:port[/namespace]
*
* - a2a:// - 无 TLS
* - a2as:// - TLS 加密
* - /namespace - 用于调用通过 ParasiteHostPlugin 寄生注册的 Agent
*
* @example
* // 基础格式
* address: 'a2a://localhost:50050'
*
* // TLS 加密
* address: 'a2as://api.example.com:50050'
*
* // 调用通过 Host Agent 寄生的 Tool Agent
* address: 'a2a://localhost:50050/tool-agent@user123'
*/
address: string;
/**
* 自定义 gRPC metadata
*
* 应用层传递字符串,框架自动转换为 Buffer 传递给 gRPC。
*
* @example
* metadata: {
* 'auth-token': 'abc123',
* 'session-id': sessionId
* }
*/
metadata?: Record<string, string>;
timeout?: number;
}
/**
* CallOptions - 客户端调用选项
*
* 设计理念:
* - 协议层只提供 metadata 字段,不关心具体业务字段
* - 业务层自己负责设置具体的 x-* metadata
* - 保持协议层的纯净和通用性
*
* 常见 metadata 约定(业务层设置):
* - 'x-trace-id': 链路追踪 ID
* - 'x-session-id': 会话 ID
* - 'x-user-id': 用户 ID
*
* @example
* const stream = await client.call('execute', params, {
* metadata: {
* 'x-trace-id': traceId,
* 'x-session-id': sessionId,
* 'x-user-id': userId,
* },
* })
*/
interface CallOptions {
/**
* gRPC metadata / WebSocket 消息元数据
* 用于传递认证、追踪等上下文信息
* 类型兼容 grpc.Metadata.getMap() 返回值
*/
metadata?: {
[key: string]: string | Buffer;
};
/**
* 取消信号
* 当 signal 被 abort 时,框架自动调用 stream.cancel() 取消下游 Agent
*
* @example
* // 在 Agent handler 中调用下游 Agent
* const stream = await client.call('execute', params, {
* signal: ctx.signal, // 框架自动处理取消传播
* metadata: ctx.metadata.getMap()
* })
*/
signal?: AbortSignal;
}
interface ClientInstance {
/**
* 调用 Agent 方法,返回双向流(异步版本)
*
* 设计理念:
* - 明确的异步操作(返回 Promise)
* - 清晰的错误处理路径
* - 返回时连接已建立,可立即使用
* - 避免竞态条件和资源泄漏
*
* @example
* const stream = await client.call('execute', { requirement: '生成登录框' })
*
* for await (const message of stream) {
* // 协议消息由框架处理
* if (message.type === 'call' || message.type === 'cancel' || message.type === 'error') {
* continue
* }
*
* // 业务消息由应用处理
* switch (message.type) {
* case 'question':
* const answer = await promptUser(message.text)
* stream.send({
* messageId: generateId(),
* timestamp: Date.now(),
* type: 'answer',
* text: answer,
* data: encodeJSON({ questionId: '...', value: answer })
* })
* break
* case 'todolist':
* console.log('TodoList:', message.text)
* break
* }
* }
*
* @example
* // 传递 metadata 实现请求追踪
* const stream = await client.call('execute', params, {
* metadata: ctx.metadata.getMap(),
* })
*/
call: <TParams = any>(skill: string, params: TParams, options?: CallOptions) => Promise<BidirectionalStream>;
/**
* 建立到 Agent 的原始双向流连接(不发送任何初始消息)
*
* 与 call() 的区别:
* - call(skill, params):自动发送初始 'call' 消息,适用于单次技能调用
* - connect():返回纯净的双向流,不发送任何消息,适用于完全透明的消息转发
*
* 主要用途:
* - 透明代理和消息转发
* - 需要完全控制消息流的场景
*
* @example
* // Host Agent 作为透明代理,转发 Frontend 和 CLI 之间的所有消息
* const cliStream = await cliClient.connect()
*
* // 双向转发(完全透明)
* await Promise.all([
* // Frontend → CLI
* (async () => {
* for await (const msg of frontendStream) {
* cliStream.send(msg)
* }
* })(),
*
* // CLI → Frontend
* (async () => {
* for await (const msg of cliStream) {
* frontendStream.send(msg)
* }
* })()
* ])
*/
connect: () => Promise<BidirectionalStream>;
getAgentCard: () => Promise<AgentCard>;
checkHealth: () => Promise<boolean>;
close: () => Promise<void>;
}
/**
* 调用上下文 - 在 Client Hooks 间传递和修改数据
*
* beforeCall 钩子可以修改 metadata 来注入 CallTicket 等信息
*/
interface CallContext {
/**
* 目标 Agent ID
*/
agentId: string;
/**
* 调用的技能名称
*/
skill: string;
/**
* 调用参数(可被钩子修改)
*/
params: any;
/**
* gRPC metadata(可被钩子修改)
*
* beforeCall 钩子可以向 metadata 中注入数据,
* 这些数据会在建立 gRPC 连接时传递给 Server 端
*
* @example
* // 在 beforeCall 中注入 CallTicket
* context.metadata['x-call-ticket'] = JSON.stringify(ticket)
*/
metadata: Record<string, string>;
}
/**
* Before Call Hook
*
* 在建立 gRPC 连接和发送 call 消息之前执行
*
* 主要用途:
* - 添加认证信息
* - 修改调用参数
* - 记录日志
* - 注入指标收集等
*
* 注意:
* - 抛出错误可中断调用
* - 可以修改 context.metadata 来注入数据
*
* @example
* beforeCall: async (context) => {
* context.metadata['x-trace-id'] = traceId
* }
*/
type BeforeCallHook = (context: CallContext) => Promise<void>;
/**
* After Call Hook
*
* 在获得 stream 之后、返回给用户之前执行
*
* 主要用途:
* - 包装或增强 stream
* - 记录调用日志
* - 监控和统计
*
* @example
* afterCall: async (context, stream) => {
* console.log(`[${context.agentId}] 调用 ${context.skill} 成功`)
* return stream // 可以返回包装后的 stream
* }
*/
type AfterCallHook = (context: CallContext, stream: BidirectionalStream) => Promise<BidirectionalStream>;
/**
* Client On Error Hook
*
* 调用过程中发生错误时执行
*
* 主要用途:
* - 统一错误处理
* - 错误日志记录
* - 重试逻辑(通过重新抛出或不抛出来控制)
*
* @example
* onError: async (error, context) => {
* console.error(`调用 ${context.skill} 失败:`, error.message)
* // 可以选择重新抛出或静默处理
* }
*/
type ClientOnErrorHook = (error: Error, context: CallContext) => Promise<void>;
/**
* Client Hooks 配置
*
* 与 Server Hooks 类似,每个插件提供单个钩子函数,
* 框架自动收集多个插件的钩子并按顺序执行
*/
interface ClientHooks {
/**
* 调用前钩子
* 在建立连接和发送消息之前执行
*/
beforeCall?: BeforeCallHook;
/**
* 调用后钩子
* 在获得 stream 之后、返回给用户之前执行
*/
afterCall?: AfterCallHook;
/**
* 错误处理钩子
*/
onError?: ClientOnErrorHook;
}
/**
* Internal Client Hooks - 框架内部使用的合并后钩子配置
*
* @internal 此类型仅供框架内部使用
*/
interface InternalClientHooks {
beforeCall?: BeforeCallHook[];
afterCall?: AfterCallHook[];
onError?: ClientOnErrorHook[];
}
/**
* Client Plugin - 客户端插件接口
*
* 插件可以提供钩子函数和额外的 API
*
* @example
* const billingPlugin: ClientPlugin = {
* hooks: {
* beforeCall: async (context) => {
* const ticket = await getCallTicket(context.agentId, context.skill)
* context.metadata['x-call-ticket'] = JSON.stringify(ticket)
* }
* },
* // 可选:插件提供的额外 API
* getBalance: () => currentBalance
* }
*/
interface ClientPlugin {
/**
* 插件提供的钩子配置
*/
hooks: ClientHooks;
/**
* 插件可以提供额外的 API(可选)
*/
[key: string]: any;
}
/**
* Client Builder - 支持链式调用的客户端构建器
*
* @example
* const client = createAgentClient(config)
* .use(loggingPlugin)
* .use(metricsPlugin)
*
* const stream = await client.call('execute', { ... })
*/
interface ClientBuilder extends ClientInstance {
/**
* 注册插件
* 支持链式调用
*/
use: (plugin: ClientPlugin) => ClientBuilder;
}
/**
* Agent 定义工厂函数(纯函数)
*
* 核心设计理念:
* 1. 函数式编程 - 无 class,使用工厂函数和闭包
* 2. Handler 安全 - Server 内部自动包装 handler,确保钩子被执行
* 3. 类型安全 - 使用泛型约束确保 defaultSkill 是有效技能名
*/
/**
* CreateAgent 选项(带类型约束)
*
* @template Skills - 技能定义数组类型
*/
interface CreateAgentOptions<Skills extends readonly SkillDefinition[] = SkillDefinition[]> {
agentId: string;
name: string;
version: string;
description?: string;
/**
* Agent 的网络地址
*
* 格式:a2a[s]://host:port
* - a2a:// 表示无 TLS
* - a2as:// 表示使用 TLS
*
* @example 'a2a://0.0.0.0:50050'
* @example 'a2as://api.example.com:50050'
*/
address: string;
skills: Skills;
/**
* 默认技能名称
*
* 必填,必须是 skills 中某个技能的 name
*/
defaultSkill: Skills[number]['name'];
/**
* Agent 角色能力声明
*
* - 'primary': 只能作为 primaryAgent(具备 LLM 推理能力,可协调其他 Agent)
* - 'tool': 只能作为 team 成员(纯工具层,不具备 LLM 推理能力)
* - 'both': 两者皆可(默认值)
*
* @default 'both'
*/
role?: 'primary' | 'tool' | 'both';
}
/**
* 创建 Agent 配置(对象参数风格)
*
* 使用泛型约束确保 defaultSkill 必须是 skills 中某个技能的 name
*
* @param options Agent 配置和技能
* @returns AgentConfig - Agent 完整配置
*
* @example
* ```typescript
* const agentConfig = createAgentConfig({
* agentId: 'my-agent',
* name: 'My Agent',
* version: '1.0.0',
* address: 'a2a://0.0.0.0:50051',
* skills: [
* defineSkill('execute', handler1),
* defineSkill('generate', handler2),
* ],
* defaultSkill: 'execute', // 类型安全:必须是 skills 中某个技能的 name
* })
* ```
*/
declare const createAgentConfig: <Skills extends readonly SkillDefinition[]>(options: CreateAgentOptions<Skills>) => AgentConfig;
/**
* 定义技能(简化配置模式)
*
* 核心安全机制:
* - Server 在启动时自动将 handler 包装为受保护版本
* - 受保护版本自动执行 beforeHandler/afterHandler 钩子
* - 插件无法绕过钩子直接调用原始 handler
*
* 工作原理:
* ```
* defineSkill('execute', rawHandler)
* ↓
* SkillDefinition {
* name: 'execute',
* handler: rawHandler // 原始 handler
* }
* ↓
* Server.start() 调用 createProtectedHandler(name, handler, hooks, agentId)
* ↓
* protectedHandler 被注册到 skillHandlers Map
* ```
*
* 安全性保证:
* - 插件只能从 skillHandlers Map 获取 handler
* - skillHandlers 中的 handler 已被 Server 包装
* - 包装后的 handler 自动执行钩子,无法绕过
*
* @param name 技能名(使用字面量类型)
* @param handler 原始处理函数
* @param options 可选配置(description, inputSchema, outputSchema)
* @returns SkillDefinition
*
* @example
* ```typescript
* const skill = defineSkill(
* 'mySkill',
* async (params, ctx) => {
* ctx.stream.send({ type: 'progress', text: '处理中...' })
* return { result: 'done' }
* },
* {
* description: 'My skill description',
* inputSchema: { type: 'object', properties: { ... } },
* }
* )
* ```
*/
declare const defineSkill: <TName extends string, TParams = any, TResult = any>(name: TName, handler: SkillHandler<TParams, TResult>, options?: {
description?: string;
inputSchema?: SkillDefinition["inputSchema"];
outputSchema?: SkillDefinition["outputSchema"];
}) => SkillDefinition & {
name: TName;
};
/**
* AgentServer - 扁平化消息架构
*
* 核心特性:
* - 扁平化消息结构:所有字段直接在Message级别
* - 协议层+业务层分离:协议消息固定3种,业务消息完全开放
* - Context 暴露 BidirectionalStream
* - Handler 直接使用 ctx.stream.send() 和 for await
*/
/**
* 创建AgentServer(Builder 模式)
*
* @param agentConfig Agent配置
* @returns ServerBuilder - 支持链式调用的构建器
*
* @example
* const server = createAgentServer(agentConfig)
* .use(createMountManager())
* .use(loggingPlugin)
* .use({ beforeMessage: simpleHook })
*
* await server.start()
*/
declare const createAgentServer: (agentConfig: AgentConfig) => ServerBuilder;
/**
* AgentClient - 扁平化消息架构
*
* 核心特性:
* - 扁平化消息结构:所有字段直接在Message级别
* - 协议层+业务层分离:协议消息固定3种,业务消息完全开放
* - call() 直接返回 BidirectionalStream
* - 直接暴露 gRPC 双向流为 async iterable
* - 自动环境检测:浏览器使用 WebSocket,Node.js 使用 gRPC
*/
/**
* 创建AgentClient(自动环境检测,支持插件)
*
* - 浏览器环境:自动使用 WebSocket 客户端
* - Node.js 环境:使用 gRPC 客户端
* - 支持 .use() 链式调用注册插件
*
* @param config Client配置
* @returns ClientBuilder 支持链式调用的客户端构建器
*
* @example
* // 基础用法(无插件)
* const client = createAgentClient(config)
* const stream = await client.call('execute', { ... })
*
* @example
* // 使用插件
* const client = createAgentClient(config)
* .use(loggingPlugin)
* .use(metricsPlugin)
*
* const stream = await client.call('execute', { ... })
*/
declare const createAgentClient: (config: ClientConfig) => ClientBuilder;
/**
* ParasiteHostPlugin - 宿主端寄生插件
*
* 核心功能:
* 1. 接收远程 Agent 的寄生请求(agent-register 消息)
* 2. 管理已寄生 Agent 的 AgentCard 和双向流连接
* 3. 将代理请求转发给对应的寄生 Agent(基于 namespace 路由)
* 4. 提供 API 查询已寄生的 Agent 列表
*
* 使用场景:
* - Host Agent 使用此插件,接受 Tool Agent 的寄生
* - Tool Agent 寄生后成为 "一等公民",可被 Agent Selector 选择
* - 调用方通过 namespace 指定目标 Agent,Host Agent 透明转发
*
* 消息协议:
* - agent-register: 寄生 Agent(data: { agentCard: AgentCard })
* - agent-unregister: 脱离寄生(无需参数,通过 stream 映射识别)
* - tunneled-call: 隧道封装的 gRPC 调用(包含完整 gRPC Metadata)
* - tunneled-response: 隧道封装的响应
*
* 隧道封装设计(类似 VPN):
* - 将完整的 gRPC 调用上下文(包括 Metadata)封装在消息中传输
* - Tool Agent 解封装后获得与云端 Agent 一致的上下文
* - 实现对 Tool Agent 完全透明的代理
*
* namespace 规则:
* - Client 在 agent-register 消息的 data 中传递 namespace(必传)
* - Server 直接使用,不关心其格式和含义
* - 由业务层决定 namespace 的生成规则
*
* @example
* ```typescript
* const parasiteHostPlugin = createParasiteHostPlugin()
*
* const server = createAgentServer(config)
* .use(parasiteHostPlugin)
*
* await server.start()
*
* // 获取已注册的 Agent(包含替换后的 endpoint)
* const registeredCards = parasiteHostPlugin.getRegisteredAgentCards({
* host: 'localhost',
* port: 50050
* })
* ```
*/
/**
* ParasiteHostPlugin 接口
*/
interface ParasiteHostPlugin extends ServerPlugin {
/**
* 获取已注册的 Agent namespace 列表
*/
getRegisteredAgents: () => string[];
/**
* 获取已注册 Agent 的 AgentCard 列表(替换 endpoint 为宿主地址)
*
* @param hostEndpoint 宿主服务器的 endpoint(Host Agent 的地址)
* @returns AgentCard 列表,endpoint 已替换为 { host, port, namespace }
*/
getRegisteredAgentCards: (hostEndpoint: {
host: string;
port: number;
}) => AgentCard[];
/**
* 检查指定 namespace 是否已注册
*/
isRegistered: (namespace: string) => boolean;
/**
* 获取指定 namespace 的原始 AgentCard(不替换 endpoint)
*/
getAgentCard: (namespace: string) => AgentCard | undefined;
}
/**
* 创建 ParasiteHostPlugin
*/
declare const createParasiteHostPlugin: () => ParasiteHostPlugin;
/**
* ParasitePlugin - 寄生插件(Client 端)
*
* 生物学比喻:
* - 本地 Agent(寄生者)主动连接到云端 Host Agent(宿主)
* - 寄生者借助宿主的网络端点对外提供服务
* - 寄生者依赖宿主存活,宿主断开则寄生者失去连接
*
* 核心功能:
* 1. 连接到远程 Host Agent 并发送注册请求
* 2. 保持双向流连接,接收来自 Host Agent 的调用转发
* 3. 将调用路由到本地 Agent 的技能处理器
*
* 使用场景:
* - Tool Agent 使用此插件,寄生到云端 Host Agent
* - 注册后 Tool Agent 成为 "一等公民",可被 Agent Selector 选择
* - Host Agent 的调用通过双向流透明转发给 Tool Agent
*
* 消息协议:
* - agent-register: 发送注册请求(data: { agentCard: AgentCard })
* - agent-unregister: 发送注销请求(无需参数,Server 通过 stream 映射识别)
* - call: 接收调用转发(从 Host Agent 转发)
* - done/error/progress: 发送调用响应(返回给 Host Agent)
*
* @example
* ```typescript
* // 业务层决定 namespace
* const namespace = `tool-agent@${userId}`
*
* const parasitePlugin = createParasitePlugin({
* address: 'a2a://host-agent.example.com:50050',
* namespace, // 必传,由外部决定
* })
*
* const server = createAgentServer(config)
* .use(parasitePlugin)
*
* await server.start()
* // Tool Agent 启动后自动寄生到 Host Agent
* ```
*/
/**
* 事件回调配置
*/
interface ParasiteEventCallbacks {
/**
* 连接断开时触发(与宿主断开)
*/
onDisconnect?: () => void;
/**
* 开始重连时触发
* @param attempt 当前重试次数
* @param delay 距离下次重试的延迟(毫秒)
*/
onReconnecting?: (attempt: number, delay: number) => void;
/**
* 重连成功时触发(重新寄生成功)
*/
onReconnected?: () => void;
/**
* 首次寄生成功时触发
*/
onRegistered?: () => void;
/**
* 发生错误时触发
* @param error 错误对象
*/
onError?: (error: Error) => void;
}
/**
* ParasitePlugin 配置
*/
interface ParasitePluginConfig {
/**
* 宿主地址(Host Agent 地址)
*
* 格式:a2a[s]://host:port
* - a2a:// 表示无 TLS
* - a2as:// 表示使用 TLS
*
* @example 'a2a://localhost:50050'
* @example 'a2as://api.example.com:50050'
*/
address: string;
/**
* namespace(必传)
*
* 用于在宿主端标识此 Agent 的唯一路由键
* 由业务层决定,ParasitePlugin 不关心其格式和含义
* 例如:"tool-agent@user123"、UUID、sessionId 等
*/
namespace: string;
/**
* 重连配置(可选)
*
* 用于控制连接失败或断开后的重试行为
*/
reconnect?: ReconnectConfig;
/**
* 事件回调(可选)
*
* 用于监听连接状态变化,实现用户通知等功能
*/
callbacks?: ParasiteEventCallbacks;
}
/**
* ParasitePlugin 接口
*/
interface ParasitePlugin extends ServerPlugin {
/**
* 检查是否已寄生到 Host Agent
*/
isRegistered: () => boolean;
/**
* 获取寄生状态
*/
getRegistrationStatus: () => {
registered: boolean;
registeredAt?: number;
/** 宿主地址(a2a:// 格式) */
hostAddress: string;
};
/**
* 断开连接并脱离宿主
*/
detach: () => Promise<void>;
}
/**
* 重连配置
*/
interface ReconnectConfig {
/** 最大重试次数(默认无限重试) */
maxRetries?: number;
/** 基础延迟(毫秒,默认 2000) */
baseDelay?: number;
/** 最大延迟(毫秒,默认 60000) */
maxDelay?: number;
}
/**
* 创建 ParasitePlugin(寄生插件)
*
* 使本地 Agent 能够寄生到远程 Host Agent,
* 借助宿主的网络端点对外提供服务。
*/
declare const createParasitePlugin: (config: ParasitePluginConfig) => ParasitePlugin;
/**
* MCP Plugin 类型定义
*
* 定义 MCP (Model Context Protocol) 集成所需的类型
*/
/**
* MCP Server 配置 - stdio 传输
*/
interface MCPServerConfigStdio {
/** Server 名称(用于生成 skill 前缀) */
name: string;
/** 传输方式 */
transport: 'stdio';
/** 启动命令 */
command: string;
/** 命令参数 */
args?: string[];
/** 环境变量 */
env?: Record<string, string>;
/** 工作目录 */
cwd?: string;
}
/**
* MCP Server 配置 - HTTP 传输
*/
interface MCPServerConfigHttp {
/** Server 名称(用于生成 skill 前缀) */
name: string;
/** 传输方式 */
transport: 'http';
/** Server URL */
url: string;
/** 请求头 */
headers?: Record<string, string>;
}
/**
* MCP Server 配置(联合类型)
*/
type MCPServerConfig = MCPServerConfigStdio | MCPServerConfigHttp;
/**
* MCP Tool 定义(来自 MCP Server)
*/
interface MCPToolDefinition {
/** 工具名称 */
name: string;
/** 工具描述 */
description?: string;
/** 输入参数 JSON Schema */
inputSchema: {
type: 'object';
properties?: Record<string, any>;
required?: string[];
};
}
/**
* MCP Tool 调用结果
*/
interface MCPToolResult {
/** 结果内容 */
content: Array<{
type: 'text' | 'image' | 'resource';
text?: string;
data?: string;
mimeType?: string;
}>;
/** 是否出错 */
isError?: boolean;
}
/**
* MCP 连接状态
*/
interface MCPConnectionState {
/** Server 名称 */
serverName: string;
/** 是否已连接 */
connected: boolean;
/** 连接时间 */
connectedAt?: number;
/** 可用工具数量 */
toolCount: number;
/** 错误信息 */
error?: string;
}
/**
* MCP Plugin 配置
*/
interface MCPPluginConfig {
/** MCP Server 列表 */
servers: MCPServerConfig[];
/**
* Skill 名称前缀
* 默认: 'mcp'
* 生成格式: {prefix}_{serverName}_{toolName}
* 例如: mcp_github_create_issue
*/
skillPrefix?: string;
/**
* 连接超时(毫秒)
* 默认: 30000
*/
connectTimeout?: number;
/**
* 工具调用超时(毫秒)
* 默认: 60000
*/
callTimeout?: number;
/**
* 自动重连
* 默认: true
*/
autoReconnect?: boolean;
/**
* 重连间隔(毫秒)
* 默认: 5000
*/
reconnectInterval?: number;
}
/**
* MCP Plugin 实例
*/
interface MCPPlugin extends ServerPlugin {
/**
* 获取所有已连接的 MCP Server
*/
getConnectedServers: () => MCPConnectionState[];
/**
* 获取所有 MCP Tools(已转换为 A2A Skill 格式)
*/
getMCPSkills: () => SkillDefinition[];
/**
* 获取指定 Server 的工具列表
*/
getServerTools: (serverName: string) => MCPToolDefinition[];
/**
* 手动连接指定 Server
*/
connectServer: (serverName: string) => Promise<void>;
/**
* 断开指定 Server
*/
disconnectServer: (serverName: string) => Promise<void>;
/**
* 断开所有 Server
*/
disconnectAll: () => Promise<void>;
/**
* 检查 skill 是否为 MCP Tool
*/
isMCPSkill: (skillName: string) => boolean;
}
/**
* MCP Plugin 实现
*
* 将 MCP Tools 集成到 A2A Agent 中,作为 Skills 暴露给 LLM
*/
/**
* 创建 MCP Plugin
*
* @example
* ```typescript
* const mcpPlugin = createMCPPlugin({
* servers: [
* {
* name: 'github',
* transport: 'stdio',
* command: 'npx',
* args: ['-y', '@modelcontextprotocol/server-github'],
* env: { GITHUB_TOKEN: process.env.GITHUB_TOKEN },
* },
* {
* name: 'filesystem',
* transport: 'stdio',
* command: 'npx',
* args: ['-y', '@modelcontextprotocol/server-filesystem', '/path/to/dir'],
* },
* ],
* })
*
* const server = createAgentServer(config)
* .use(mcpPlugin)
*
* // MCP tools 自动合并到 AgentCard.skills
* // 调用时自动路由到对应的 MCP Server
* ```
*/
declare const createMCPPlugin: (config: MCPPluginConfig) => MCPPlugin;
/**
* 通用 Tokenizer
*
* 使用简化算法计算文本的 Token 数量,与具体 LLM 无关
*
* 算法规则:
* - 中文字符:约 1 字 = 2 tokens
* - 英文/数字:约 4 字符 = 1 token
* - 空白字符:约 4 字符 = 1 token
* - 其他符号:1 符号 = 1 token
*/
declare const tokenize: (text: string) => number;
/**
* 计算费用(分)
*
* @param tokens Token 数量
* @param pricePerKToken 每 1K tokens 的价格(分)
* @returns 费用(分),向上取整
*/
declare const calculateCost: (tokens: number, pricePerKToken: number) => number;
/**
* 流式响应收集器
*
* 用于收集流式输出的所有文本,并计算总 Token 数
*
* @example
* ```typescript
* const collector = createStreamCollector()
*
* // 收集流式输出
* collector.collect('Hello ')
* collector.collect('World!')
*
* // 获取结果
* console.log(collector.getText()) // 'Hello World!'
* console.log(collector.getTokens()) // Token 数量
* ```
*/
declare const createStreamCollector: () => {
/**
* 收集一个文本块
*/
collect: (chunk: string) => void;
/**
* 获取所有收集的文本
*/
getText: () => string;
/**
* 获取总字符数
*/
getChars: () => number;
/**
* 获取总 Token 数
*/
getTokens: () => number;
/**
* 重置收集器
*/
reset: () => void;
};
type StreamCollector = ReturnType<typeof createStreamCollector>;
/**
* 创建 IO 承诺
*
* 承诺包含内容哈希和 Token 数量,由签名密钥签名确保不可伪造
* userId 纳入签名保护,防止恶意 Agent 伪造用户身份
*
* @param content 原始内容
* @param traceId 调用链 ID
* @param type 类型(input/output)
* @param signingKey 签名密钥
* @param userId 用户 ID(可选,来自上游传递的 x-user-id)
* @returns IO 承诺
*
* @example
* ```typescript
* const commitment = createIOCommitment(
* JSON.stringify(params),
* 'tr_abc123',
* 'input',
* 'signing-key',
* 'user_123'
* )
* ```
*/
declare const createIOCommitment: (content: string, traceId: string, type: "input" | "output", signingKey: string, userId?: string) => IOCommitment;
/**
* 验证 IO 承诺
*
* @param commitment 承诺数据
* @param type 类型(input/output)
* @param signingKey 签名密钥
* @param userId 用户 ID(需与创建时一致)
* @returns 验证是否通过
*/
declare const verifyIOCommitment: (commitment: IOCommitment, type: "input" | "output", signingKey: string, userId?: string) => boolean;
/**
* 计算 SDK 核心模块的哈希
*
* 用于验证 SDK 是否被篡改
*
* @returns 核心模块的合并哈希
*/
declare const calculateSdkHash: () => string;
/**
* 获取 SDK 版本
*
* @returns SDK 版本号
*/
declare const getSdkVersion: () => string;
/**
* Tracing Plugin 类型定义
*
* 专注于调用链追踪,与 io-metrics-plugin 完全分离
*/
/**
* 调用链追踪记录
*/
interface TraceRecord {
/** 请求链 ID(整个请求链共用) */
traceId: string;
/** 当前调用的唯一 ID */
spanId: string;
/** 父调用的 Span ID(谁调用了我,根调用为 undefined) */
parentSpanId?: string;
/** Agent ID */
agentId: string;
/** 调用的技能名称 */
skill: string;
/** 调用开始时间(ISO 8601 格式) */
startedAt: string;
/** 执行时长(毫秒) */
duration: number;
}
/**
* 调用链追踪提供者接口
*
* 平台方实现此接口,接收调用链数据
*/
interface TracingProvider {
/**
* 上报调用链追踪数据
*
* @param record 追踪记录
*/
reportTrace(record: TraceRecord): Promise<void>;
}
/**
* Tracing Plugin 配置选项
*/
interface TracingPluginOptions {
/** 调用链追踪提供者 */
provider: TracingProvider;
}
/**
* Tracing Plugin - 调用链追踪插件
*
* 功能:
* 1. beforeHandler: 生成 spanId,读取 parentSpanId,写入 x-span-id 供下游读取
* 2. afterHandler: 计算 duration,上报调用链数据到平台
*
* 与 io-metrics-plugin 的区别:
* - tracing-plugin: 只负责调用链追踪(spanId/parentSpanId)
* - io-metrics-plugin: 只负责 Token 计量和计费
*/
/**
* 创建调用链追踪插件
*
* @param options 插件配置
* @returns ServerPlugin
*
* @example
* ```typescript
* const server = createAgentServer(config)
* .use(createTracingPlugin({
* provider: {
* reportTrace: async (record) => {
* await fetch(`${platformUrl}/api/traces/report`, {
* method: 'POST',
* headers: { 'Content-Type': 'application/json' },
* body: JSON.stringify(record),
* })
* }
* }
* }))
* ```
*/
declare const createTracingPlugin: (options: TracingPluginOptions) => ServerPlugin;
/**
* Auth Plugin - 类型定义
*/
/**
* Token 验证结果
*/
interface TokenVerifyResult {
/** 是否有效 */
valid: boolean;
/** 用户 ID(验证成功时存在) */
userId?: string;
/** 用户名(验证成功时存在) */
username?: string;
/** 错误信息(验证失败时存在) */
error?: string;
}
/**
* Auth Plugin 配置选项
*/
interface AuthPluginOptions {
/**
* 平台 URL(用于验证 Token)
*
* 如果未提供或为空字符串,当收到 authorization header 时会拒绝请求,
* 因为无法安全地验证 Token。
*
* @example 'http://8.153.165.230:50010/api'
*/
platformUrl?: string;
/**
* 是否强制要求登录
*
* - true: 未登录用户会收到错误消息,请求被中止
* - false: 允许匿名用户访问
*
* @default true
*/
requireAuth?: boolean;
/**
* 自定义错误消息
*/
messages?: {
/** Token 无效时的错误消息 */
tokenInvalid?: string;
/** 需要登录时的错误消息 */
authRequired?: string;
/** 验证服务不可用时的错误消息 */
serviceUnavailable?: string;
/** 平台未配置时的错误消息(收到 auth header 但无法验证) */
platformNotConfigured?: string;
};
}
/**
* Auth Plugin - 认证插件
*
* 功能:
* 1. beforeHandler: 从 gRPC metadata 提取 authorization header
* 2. 调用平台 API 验证 Token
* 3. 验证成功:将 userId/username 存入 ctx.metadata 供后续使用
* 4. 验证失败:发送错误消息并中止请求
*
* 设计原则:
* - 不信任客户端传递的 userId,必须通过平台验证
* - 验证后的 userId 通过 ctx.metadata 传递,而非 gRPC metadata
* - 支持配置是否强制要求登录
*/
/**
* 创建认证插件
*
* @param options - 插件配置
* @returns ServerPlugin
*
* @example
* ```typescript
* const server = createAgentServer(config)
* .use(createA