UNPKG

@mastra/core

Version:
1,007 lines (1,006 loc) • 30.5 kB
import "../rolldown-runtime-DP3BCW9_.js"; import { a as RequestContext } from "../request-context-p_Tq-4EM.js"; import { n as MessageList, t as convertMessages } from "../message-list-mC29laJJ.js"; import { t as MastraA2AError } from "../client-D9NcUAi0.js"; import { randomUUID } from "crypto"; export * from "@a2a-js/sdk"; //#region src/a2a/a2a-agent.ts const EMPTY_USAGE = { inputTokens: void 0, outputTokens: void 0, totalTokens: void 0 }; function toAgentStreamChunk(runId, chunk) { return { ...chunk, runId, from: "AGENT" }; } function createFinishPayload() { return { stepResult: { reason: "stop" }, output: { usage: EMPTY_USAGE }, metadata: {}, messages: { all: [], user: [], nonUser: [] }, finishReason: "stop", usage: EMPTY_USAGE }; } function isTask(result) { return typeof result === "object" && result !== null && "status" in result && "id" in result && "kind" in result; } function isMessage(result) { return typeof result === "object" && result !== null && "messageId" in result && "parts" in result; } function isTerminalTaskState(state) { return state === "completed" || state === "failed" || state === "canceled" || state === "rejected"; } function splitNextEvent(buffer) { const normalizedBuffer = buffer.replace(/\x1E/g, "\n\n"); const match = normalizedBuffer.match(/\r?\n\r?\n/); if (!match || match.index === void 0) return { rest: normalizedBuffer }; return { eventBlock: normalizedBuffer.slice(0, match.index), rest: normalizedBuffer.slice(match.index + match[0].length) }; } function parseEventBlock(eventBlock) { const trimmedBlock = eventBlock.trim(); if (!trimmedBlock) return {}; const dataLines = trimmedBlock.split(/\r?\n/).filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()); const payload = dataLines.length > 0 ? dataLines.join("\n") : trimmedBlock; if (payload === "[DONE]") return { done: true }; let parsed; try { parsed = JSON.parse(payload); } catch { return {}; } if ("result" in parsed && parsed.result) return { event: parsed.result }; return { event: parsed }; } function extractTextParts(parts) { return (parts ?? []).filter((part) => part.kind === "text" && typeof part.text === "string").map((part) => part.text).join("\n"); } function extractTaskText(task) { return [(task.artifacts ?? []).flatMap((artifact) => artifact.parts?.flatMap((part) => { if (part.kind === "text" && "text" in part && typeof part.text === "string") return [part.text]; return []; }) ?? []).join("\n"), task.status.message ? extractMessageText(task.status.message) : ""].filter(Boolean).join("\n").trim(); } function extractTaskArtifactText(task) { return (task.artifacts ?? []).flatMap((artifact) => artifact.parts?.flatMap((part) => { if (part.kind === "text" && "text" in part && typeof part.text === "string") return [part.text]; return []; }) ?? []).join(""); } function extractMessageText(message) { return extractTextParts(message.parts).trim(); } function messagesToPrompt(messages, options) { if (typeof messages === "string") return messages; const converted = convertMessages(messages).to("AIV5.Model"); const lines = []; if (options?.instructions) lines.push(`Instructions:\n${options.instructions}`); const contextMessages = options?.context; if (contextMessages?.length) lines.push("Context:\n" + contextMessages.map((message) => { return `${message.role ?? "unknown"}: ${typeof message.content === "string" ? message.content : JSON.stringify(message.content)}`; }).join("\n")); for (const message of converted) { if (message.role === "system") { const content = Array.isArray(message.content) ? message.content.map((part) => { if ("type" in part && part.type === "text") return part.text; return ""; }).filter(Boolean).join("\n") : typeof message.content === "string" ? message.content : ""; if (content) lines.push(`system: ${content}`); continue; } const content = Array.isArray(message.content) ? message.content.map((part) => { if ("type" in part && part.type === "text") return part.text; if ("type" in part && part.type === "file") return `[file:${part.filename ?? "attachment"}]`; return ""; }).filter(Boolean).join("\n") : typeof message.content === "string" ? message.content : ""; lines.push(`${message.role}: ${content}`); } return lines.join("\n\n").trim(); } function resumeDataToPrompt(resumeData) { if (typeof resumeData === "string") return resumeData; if (resumeData == null) return ""; return JSON.stringify(resumeData, null, 2); } function createResumeSchema() { return JSON.stringify({ type: "object", additionalProperties: true, description: "Data to continue the remote A2A task." }); } function resolveStreamTextId(candidateIds) { for (const candidate of candidateIds) if (candidate) return candidate; return "text-1"; } function resolveMemoryInfo(options) { return { threadId: typeof options?.memory?.thread === "string" ? options.memory.thread : options?.memory?.thread?.id, resourceId: options?.memory?.resource }; } function createResponseMessages(text, memoryInfo = {}) { if (!text) return []; return new MessageList(memoryInfo).add({ role: "assistant", content: text }, "response").get.response.db(); } function createGenerateResult({ runId, text, task, message, resumePayload, resumeSchema, threadId, resourceId }) { const responseMessages = createResponseMessages(text, { threadId, resourceId }); return { text, usage: EMPTY_USAGE, steps: [], finishReason: resumePayload ? "suspended" : "stop", warnings: [], providerMetadata: void 0, request: {}, reasoning: [], reasoningText: void 0, toolCalls: [], toolResults: [], sources: [], files: [], response: { id: message?.messageId ?? task?.id ?? runId, timestamp: /* @__PURE__ */ new Date(), modelId: "a2a/remote-agent", messages: [], uiMessages: [], dbMessages: responseMessages }, totalUsage: EMPTY_USAGE, object: void 0, error: void 0, tripwire: void 0, traceId: void 0, spanId: void 0, runId, suspendPayload: resumePayload, resumeSchema, messages: responseMessages, rememberedMessages: [], task, message, resumePayload }; } function unwrapA2AResult(result) { if (!result || typeof result !== "object") throw MastraA2AError.invalidAgentResponse("Remote A2A agent returned an invalid response."); if ("result" in result && result.result && typeof result.result === "object") return result.result; return result; } function createDeferred() { let resolve; let reject; return { promise: new Promise((res, rej) => { resolve = res; reject = rej; }), resolve, reject }; } async function requireResponseBody(response, operation) { if (!response.body) throw MastraA2AError.invalidAgentResponse(`Remote A2A agent returned an empty stream for ${operation}.`); return response.body; } var A2AAgent = class { id; name; #url; #description; #headers; #fetch; #retries; #backoffMs; #maxBackoffMs; #credentials; #abortSignal; #timeoutMs; #verifyAgentCard; #cachedBootstrap; #runState = /* @__PURE__ */ new Map(); #memory; #mastra; constructor(options) { this.#url = options.url.replace(/\/$/, ""); this.#description = options.description ?? `Remote A2A agent at ${this.#url}`; this.#headers = options.headers ?? {}; this.#fetch = options.fetch ?? fetch; this.#retries = options.retries ?? 0; this.#backoffMs = options.backoffMs ?? 250; this.#maxBackoffMs = options.maxBackoffMs ?? 1e3; this.#credentials = options.credentials; this.#abortSignal = options.abortSignal; this.#timeoutMs = options.timeoutMs; this.#verifyAgentCard = options.verifyAgentCard; this.id = options.id ?? `a2a-${randomUUID()}`; this.name = options.name ?? options.description ?? "A2A Agent"; } async getAgentCard({ forceRefresh = false } = {}) { return (await this.#getBootstrap({ forceRefresh })).card; } getDescription() { return this.#description; } getModel = async () => ({ specificationVersion: "v2" }); hasOwnMemory() { return Boolean(this.#memory); } __setMemory(memory) { this.#memory = memory; } async getMemory({ requestContext = new RequestContext() } = {}) { if (!this.#memory) return; if (typeof this.#memory !== "function") return this.#memory; return await this.#memory({ requestContext, mastra: this.#mastra }); } getInstructions = async () => ""; __registerMastra(mastra) { this.#mastra = mastra; } async generate(messages, options) { const bootstrap = await this.#getBootstrap(); const runId = options?.runId ?? randomUUID(); const prompt = messagesToPrompt(messages, options); const memoryInfo = resolveMemoryInfo(options); return this.#sendAndResolve({ bootstrap, runId, prompt, signal: options?.abortSignal, ...memoryInfo }); } async resumeGenerate(resumeData, options) { const runId = options?.runId; if (!runId) throw MastraA2AError.invalidParams("A2AAgent.resumeGenerate requires a runId."); const state = this.#runState.get(runId); if (!state) throw MastraA2AError.invalidParams(`No resumable A2A run state found for runId "${runId}".`); const bootstrap = await this.#getBootstrap(); const memoryInfo = resolveMemoryInfo(options); if (state.waitingForInput) { const prompt = resumeDataToPrompt(resumeData); return this.#sendAndResolve({ bootstrap, runId, prompt, signal: options?.abortSignal, contextId: state.contextId, referenceTaskIds: state.taskId ? [state.taskId] : void 0, ...memoryInfo }); } if (!state.taskId) throw MastraA2AError.invalidParams(`A2AAgent resume state for "${runId}" is missing a taskId.`); const task = await this.#getTask({ bootstrap, taskId: state.taskId, signal: options?.abortSignal }); return this.#resolveTaskToGenerateResult({ bootstrap, runId, task, signal: options?.abortSignal, ...memoryInfo }); } async stream(messages, options) { const bootstrap = await this.#getBootstrap(); const runId = options?.runId ?? randomUUID(); const prompt = messagesToPrompt(messages, options); const memoryInfo = resolveMemoryInfo(options); if (!bootstrap.streamingSupported) { const result = await this.generate(messages, { ...options, runId }); return this.#createBufferedStreamResult({ runId, result, emitStart: true, ...memoryInfo }); } return this.#runRemoteStream({ bootstrap, runId, prompt, signal: options?.abortSignal, emitStart: true, ...memoryInfo }); } async resumeStream(resumeData, options) { const runId = options?.runId; if (!runId) throw MastraA2AError.invalidParams("A2AAgent.resumeStream requires a runId."); const state = this.#runState.get(runId); if (!state) throw MastraA2AError.invalidParams(`No resumable A2A run state found for runId "${runId}".`); const bootstrap = await this.#getBootstrap(); const memoryInfo = resolveMemoryInfo(options); if (state.waitingForInput) { const prompt = resumeDataToPrompt(resumeData); if (!bootstrap.streamingSupported) { const result = await this.resumeGenerate(resumeData, options); return this.#createBufferedStreamResult({ runId, result, emitStart: false, ...memoryInfo }); } return this.#runRemoteStream({ bootstrap, runId, prompt, signal: options?.abortSignal, contextId: state.contextId, referenceTaskIds: state.taskId ? [state.taskId] : void 0, emitStart: false, ...memoryInfo }); } if (!state.taskId) throw MastraA2AError.invalidParams(`A2AAgent resume state for "${runId}" is missing a taskId.`); if (!bootstrap.streamingSupported) { const result = await this.resumeGenerate(resumeData, options); return this.#createBufferedStreamResult({ runId, result, emitStart: false, ...memoryInfo }); } return this.#resubscribeToRemoteStream({ bootstrap, runId, taskId: state.taskId, initialTask: state.lastTask, signal: options?.abortSignal, ...memoryInfo }); } async #getBootstrap({ forceRefresh = false } = {}) { if (!forceRefresh && this.#cachedBootstrap) return this.#cachedBootstrap; const cardUrl = this.#resolveCardUrl(); const card = await (await this.#request(cardUrl, { method: "GET", signal: this.#abortSignal })).json(); const fetchedAt = /* @__PURE__ */ new Date(); if (this.#verifyAgentCard) { const context = { cardUrl, fetchedAt }; await this.#verifyAgentCard.verify(card, context); } const bootstrap = { card, cardUrl, executionUrl: card.url, streamingSupported: card.capabilities?.streaming ?? false }; this.#cachedBootstrap = bootstrap; return bootstrap; } #resolveCardUrl() { return this.#url.endsWith("/agent-card.json") ? this.#url : `${this.#url}/.well-known/agent-card.json`; } async #sendMessage({ bootstrap, prompt, signal, contextId, referenceTaskIds }) { return unwrapA2AResult(await (await this.#request(bootstrap.executionUrl, { method: "POST", signal, body: { jsonrpc: "2.0", id: randomUUID(), method: "message/send", params: { message: { role: "user", kind: "message", messageId: randomUUID(), parts: [{ kind: "text", text: prompt }], ...contextId ? { contextId } : {}, ...referenceTaskIds?.length ? { referenceTaskIds } : {} } } } })).json()); } async #sendAndResolve({ bootstrap, runId, prompt, signal, contextId, referenceTaskIds, threadId, resourceId }) { const response = await this.#sendMessage({ bootstrap, prompt, signal, contextId, referenceTaskIds }); if (isMessage(response)) { this.#runState.delete(runId); return createGenerateResult({ runId, text: extractMessageText(response), message: response, threadId, resourceId }); } return this.#resolveTaskToGenerateResult({ bootstrap, runId, task: response, signal, threadId, resourceId }); } async #getTask({ bootstrap, taskId, signal }) { const result = unwrapA2AResult(await (await this.#request(bootstrap.executionUrl, { method: "POST", signal, body: { jsonrpc: "2.0", id: randomUUID(), method: "tasks/get", params: { id: taskId } } })).json()); if (!isTask(result)) throw MastraA2AError.invalidAgentResponse("Remote A2A agent returned a non-task response for tasks/get."); return result; } async #resolveTaskToGenerateResult({ bootstrap, runId, task, signal, threadId, resourceId }) { let currentTask = task; while (true) { const evaluation = this.#evaluateTask({ bootstrap, task: currentTask }); if (evaluation.kind === "completed") { this.#runState.delete(runId); return createGenerateResult({ runId, text: evaluation.text, task: evaluation.task, message: evaluation.message, threadId, resourceId }); } this.#runState.set(runId, { runId, contextId: evaluation.task.contextId, taskId: evaluation.task.id, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, streamingSupported: bootstrap.streamingSupported, waitingForInput: evaluation.resumePayload.waitingForInput, lastTask: evaluation.task }); if (evaluation.task.status.state === "input-required") return createGenerateResult({ runId, text: evaluation.text, task: evaluation.task, resumePayload: evaluation.resumePayload, resumeSchema: evaluation.resumeSchema, threadId, resourceId }); await this.#delay(); currentTask = await this.#getTask({ bootstrap, taskId: evaluation.task.id, signal }); } } #evaluateTask({ bootstrap, task }) { const text = extractTaskText(task); if (task.status.state === "input-required") return { kind: "suspended", text, task, resumePayload: { taskId: task.id, contextId: task.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: true, task: structuredClone(task) }, resumeSchema: createResumeSchema() }; if (isTerminalTaskState(task.status.state)) return { kind: "completed", text, task }; return { kind: "suspended", text, task, resumePayload: { taskId: task.id, contextId: task.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: false, task: structuredClone(task) } }; } async #runRemoteStream({ bootstrap, runId, prompt, signal, contextId, referenceTaskIds, threadId, resourceId, emitStart }) { const response = await this.#request(bootstrap.executionUrl, { method: "POST", signal, stream: true, body: { jsonrpc: "2.0", id: randomUUID(), method: "message/stream", params: { message: { role: "user", kind: "message", messageId: randomUUID(), parts: [{ kind: "text", text: prompt }], ...contextId ? { contextId } : {}, ...referenceTaskIds?.length ? { referenceTaskIds } : {} } } } }); return this.#consumeA2AStream({ bootstrap, runId, stream: await requireResponseBody(response, "message/stream"), threadId, resourceId, emitStart }); } async #resubscribeToRemoteStream({ bootstrap, runId, taskId, initialTask, signal, threadId, resourceId }) { const response = await this.#request(bootstrap.executionUrl, { method: "POST", signal, stream: true, body: { jsonrpc: "2.0", id: randomUUID(), method: "tasks/resubscribe", params: { id: taskId } } }); return this.#consumeA2AStream({ bootstrap, runId, initialTask, stream: await requireResponseBody(response, "tasks/resubscribe"), threadId, resourceId, emitStart: false }); } async #consumeA2AStream({ bootstrap, runId, initialTask, stream, threadId, resourceId, emitStart }) { const [consumerStream, accumulatorStream] = stream.tee(); const resultDeferred = createDeferred(); const textDeferred = createDeferred(); const taskDeferred = createDeferred(); const suspendPayloadDeferred = createDeferred(); const resumeSchemaDeferred = createDeferred(); const messageList = new MessageList({ threadId, resourceId }); this.#collectStreamEvents({ bootstrap, initialTask, stream: accumulatorStream }).then((consumed) => { if (consumed.task && consumed.suspended) this.#runState.set(runId, { runId, contextId: consumed.task.contextId, taskId: consumed.task.id, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, streamingSupported: bootstrap.streamingSupported, waitingForInput: consumed.suspended.payload.waitingForInput, lastTask: consumed.task }); else this.#runState.delete(runId); textDeferred.resolve(consumed.text); taskDeferred.resolve(consumed.task); suspendPayloadDeferred.resolve(consumed.suspended?.payload); resumeSchemaDeferred.resolve(consumed.suspended?.resumeSchema); if (consumed.text) messageList.add({ role: "assistant", content: consumed.text }, "response"); resultDeferred.resolve(createGenerateResult({ runId, text: consumed.text, task: consumed.task, threadId, resourceId, ...consumed.suspended ? { resumePayload: consumed.suspended.payload, resumeSchema: consumed.suspended.resumeSchema } : {} })); }).catch((error) => { textDeferred.reject(error); taskDeferred.reject(error); suspendPayloadDeferred.reject(error); resumeSchemaDeferred.reject(error); resultDeferred.reject(error); }); return { runId, fullStream: this.#streamEvents({ bootstrap, runId, stream: consumerStream, emitStart }), text: textDeferred.promise, toolResults: Promise.resolve([]), messageList, task: taskDeferred.promise, suspendPayload: suspendPayloadDeferred.promise, resumeSchema: resumeSchemaDeferred.promise, getResult: async () => resultDeferred.promise }; } async *#streamEvents({ bootstrap, runId, stream, emitStart }) { if (emitStart) yield toAgentStreamChunk(runId, { type: "start", payload: { id: this.id } }); const reader = stream.getReader(); const decoder = new TextDecoder(); let buffer = ""; let textId; let textStarted = false; let task; let suspended; let receivedDone = false; while (true) { const { done, value } = await reader.read(); if (done) buffer += decoder.decode(); else if (value) buffer += decoder.decode(value, { stream: true }); let next = splitNextEvent(buffer); while (next.eventBlock !== void 0) { const parsed = parseEventBlock(next.eventBlock); if ("done" in parsed && parsed.done) { receivedDone = true; buffer = next.rest; break; } if ("event" in parsed && parsed.event) { const event = parsed.event; if (isTask(event)) { task = event; if (event.status.state === "input-required") suspended = { taskId: event.id, contextId: event.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: true, task: structuredClone(event) }; } else if (isMessage(event)) { const text = extractMessageText(event); if (text) { textId ??= resolveStreamTextId([event.messageId, task?.id]); if (textId) { if (!textStarted) { yield toAgentStreamChunk(runId, { type: "text-start", payload: { id: textId } }); textStarted = true; } yield toAgentStreamChunk(runId, { type: "text-delta", payload: { id: textId, text } }); } } } else if (event.kind === "artifact-update") { const text = event.artifact.parts?.flatMap((part) => part.kind === "text" && "text" in part && typeof part.text === "string" ? [part.text] : []).join(""); if (text) { textId ??= resolveStreamTextId([event.artifact.artifactId, task?.id]); if (textId) { if (!textStarted) { yield toAgentStreamChunk(runId, { type: "text-start", payload: { id: textId } }); textStarted = true; } yield toAgentStreamChunk(runId, { type: "text-delta", payload: { id: textId, text } }); } } } else if (event.kind === "status-update") { task = task ? { ...task, status: event.status } : task; if (event.status.state === "input-required" && task) suspended = { taskId: task.id, contextId: task.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: true, task: structuredClone(task) }; } } buffer = next.rest; next = splitNextEvent(buffer); } if (done || receivedDone) { if (textId && textStarted) yield toAgentStreamChunk(runId, { type: "text-end", payload: { id: textId } }); if (!suspended && task && !isTerminalTaskState(task.status.state)) suspended = { taskId: task.id, contextId: task.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: false, task: structuredClone(task) }; if (suspended) yield toAgentStreamChunk(runId, { type: "tool-call-suspended", payload: { toolCallId: runId, toolName: this.id, args: {}, suspendPayload: suspended, resumeSchema: createResumeSchema() } }); else yield toAgentStreamChunk(runId, { type: "finish", payload: createFinishPayload() }); return; } } } async #collectStreamEvents({ bootstrap, initialTask, stream }) { const reader = stream.getReader(); const decoder = new TextDecoder(); let buffer = ""; let textBuffer = ""; let task = initialTask ? structuredClone(initialTask) : void 0; let suspended; while (true) { const { done, value } = await reader.read(); if (done) buffer += decoder.decode(); else if (value) buffer += decoder.decode(value, { stream: true }); let next = splitNextEvent(buffer); while (next.eventBlock !== void 0) { const parsed = parseEventBlock(next.eventBlock); if ("done" in parsed && parsed.done) break; if ("event" in parsed && parsed.event) { const event = parsed.event; if (isTask(event)) { task = event; textBuffer = extractTaskArtifactText(event) || textBuffer; if (event.status.state === "input-required") suspended = { payload: { taskId: event.id, contextId: event.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: true, task: structuredClone(event) }, resumeSchema: createResumeSchema() }; } else if (isMessage(event)) { const messageText = extractMessageText(event); if (messageText) textBuffer = messageText; } else if (event.kind === "artifact-update") { task = task ? { ...task, artifacts: [...(task.artifacts ?? []).filter((artifact) => artifact.artifactId !== event.artifact.artifactId), event.artifact] } : task; const artifactText = event.artifact.parts?.flatMap((part) => part.kind === "text" && "text" in part && typeof part.text === "string" ? [part.text] : []).join(""); if (artifactText) textBuffer += artifactText; } else if (event.kind === "status-update") { task = task ? { ...task, status: event.status } : task; if (event.status.state === "input-required" && task) suspended = { payload: { taskId: task.id, contextId: task.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: true, task: structuredClone(task) }, resumeSchema: createResumeSchema() }; } } buffer = next.rest; next = splitNextEvent(buffer); } if (done) break; } if (!suspended && task && !isTerminalTaskState(task.status.state)) suspended = { payload: { taskId: task.id, contextId: task.contextId, executionUrl: bootstrap.executionUrl, cardUrl: bootstrap.cardUrl, waitingForInput: false, task: structuredClone(task) } }; return { text: textBuffer, task, suspended }; } #createBufferedStreamResult({ runId, result, threadId, resourceId, emitStart }) { const messageList = new MessageList({ threadId, resourceId }); const toolName = this.id; const agentId = this.id; const textId = resolveStreamTextId([result.message?.messageId, result.task?.id]); if (result.text) messageList.add({ role: "assistant", content: result.text }, "response"); return { runId, fullStream: (async function* () { if (emitStart) yield toAgentStreamChunk(runId, { type: "start", payload: { id: agentId } }); if (result.text) { yield toAgentStreamChunk(runId, { type: "text-start", payload: { id: textId } }); yield toAgentStreamChunk(runId, { type: "text-delta", payload: { id: textId, text: result.text } }); yield toAgentStreamChunk(runId, { type: "text-end", payload: { id: textId } }); } if (result.resumePayload) { yield toAgentStreamChunk(runId, { type: "tool-call-suspended", payload: { toolCallId: runId, toolName, args: {}, suspendPayload: result.resumePayload, resumeSchema: result.resumeSchema ?? createResumeSchema() } }); return; } yield toAgentStreamChunk(runId, { type: "finish", payload: createFinishPayload() }); })(), text: Promise.resolve(result.text), toolResults: Promise.resolve([]), messageList, task: Promise.resolve(result.task), suspendPayload: Promise.resolve(result.resumePayload), resumeSchema: Promise.resolve(result.resumeSchema), getResult: async () => result }; } async #request(url, { method = "POST", headers = {}, body, stream = false, credentials, signal } = {}) { let attempts = 0; let lastError; const finalHeaders = { accept: stream ? "text/event-stream" : "application/json", ...this.#headers, ...headers }; while (attempts <= this.#retries) try { const requestSignal = this.#resolveRequestSignal(signal); const response = await this.#fetch(url, { method, headers: { ...finalHeaders, ...body ? { "content-type": "application/json" } : {} }, body: body ? JSON.stringify(body) : void 0, credentials: credentials ?? this.#credentials, signal: requestSignal }); if (!response.ok) throw MastraA2AError.invalidAgentResponse(`Remote A2A request failed with status ${response.status}.`, { status: response.status, url }); return response; } catch (error) { lastError = error; if (!shouldRetryRequest(error)) throw lastError; if (attempts === this.#retries) break; attempts += 1; await this.#delay(attempts); } throw lastError; } async #delay(attempt = 0) { const delayMs = Math.min(this.#backoffMs * Math.max(1, attempt), this.#maxBackoffMs); if (delayMs <= 0) return; await new Promise((resolve) => setTimeout(resolve, delayMs)); } #resolveRequestSignal(signal) { if (this.#timeoutMs == null) return signal ?? this.#abortSignal; const timeoutSignal = AbortSignal.timeout(this.#timeoutMs); const signals = [ signal, this.#abortSignal, timeoutSignal ].filter(Boolean); if (signals.length === 0) return; return signals.length === 1 ? signals[0] : AbortSignal.any(signals); } }; function shouldRetryRequest(error) { if (error instanceof TypeError) return true; if (error instanceof DOMException && error.name === "AbortError") return true; if (error instanceof Error && error.name === "AbortError") return true; const status = typeof error === "object" && error !== null && "status" in error && typeof error.status === "number" ? error.status : typeof error === "object" && error !== null && "data" in error && typeof error.data === "object" && error.data !== null && "status" in error.data && typeof error.data.status === "number" ? error.data.status : void 0; if (status === void 0) return true; return status === 408 || status === 429 || status >= 500; } //#endregion export { A2AAgent, MastraA2AError }; //# sourceMappingURL=index.js.map