/** * A2A 内部客户端 * * 替代 collaboration.ts 的 invokeAgent(),提供与原接口兼容的 * agent 间调用能力,底层走 A2A Service 层。 * * 设计原则: * - 接口签名与 invokeAgent 完全兼容,便于无缝替换 * - 递归深度、超时、自调用阻断通过 A2AInvocationContext 传递 * - 未来可切换为真实 HTTP 调用本机 A2A 端点(接口不变) */ import type { AgentInvocation, AgentInvocationResult } from "../agent/types"; import { getAgentBySlug } from "../agent/agent"; import { createTask, sendTask } from "./service"; import { textToA2AMessage, a2aMessageToText } from "./converter"; import { DEFAULT_A2A_CONFIG, JSONRPC_ERROR_CODES } from "./types"; import type { A2AInvocationContext } from "./types"; import log4js from "logger"; const logger = log4js.getLogger("APP"); export async function invokeAgentViaA2A( invocation: AgentInvocation, options?: { maxRecursionDepth?: number; timeoutMs?: number; maxOutputTokens?: number; }, ): Promise { const { agentSlug, input, context } = invocation; const maxRecursionDepth = options?.maxRecursionDepth ?? DEFAULT_A2A_CONFIG.maxRecursionDepth; const timeoutMs = options?.timeoutMs ?? DEFAULT_A2A_CONFIG.timeoutMs; const maxOutputTokens = options?.maxOutputTokens ?? DEFAULT_A2A_CONFIG.maxOutputTokens; const userId = context?.userId ?? null; const callerAgentId = context?.callerAgentId ?? null; const recursionDepth = context?.recursionDepth ?? 0; const agent = await getAgentBySlug(agentSlug); if (!agent) { return { agentSlug, output: "", ok: false, error: `agent not found: ${agentSlug}` }; } if (!agent.isCallable) { return { agentSlug, output: "", ok: false, error: `agent not callable: ${agentSlug}` }; } if (callerAgentId !== null && callerAgentId === agent.id) { return { agentSlug, output: "", ok: false, error: `self-invocation blocked: agent ${agentSlug} cannot invoke itself` }; } if (recursionDepth >= maxRecursionDepth) { return { agentSlug, output: "", ok: false, error: `max recursion depth (${maxRecursionDepth}) reached`, }; } const message = textToA2AMessage(input, "user"); try { const { task } = await createTask({ calleeAgentSlug: agentSlug, message, callerAgentId, userId, }); const a2aContext: A2AInvocationContext = { callerAgentId, calleeAgentId: agent.id, calleeAgentSlug: agentSlug, userId, sessionId: task.sessionId, recursionDepth, maxRecursionDepth, timeoutMs, maxOutputTokens, }; const completedTask = await sendTask(task.id, a2aContext); const output = completedTask.message ? a2aMessageToText(completedTask.message) : ""; logger.info( "[A2A-CLIENT] invokeAgentViaA2A done agentSlug=%s taskId=%s outputLen=%d", agentSlug, task.id, output.length, ); return { agentSlug, output, ok: true }; } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); logger.error("[A2A-CLIENT] invokeAgentViaA2A error agentSlug=%s error=%s", agentSlug, errorMsg); return { agentSlug, output: "", ok: false, error: errorMsg }; } }