UNPKG

@mastra/core

Version:

Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.

1,154 lines (1,151 loc) • 34.8 kB
'use strict'; var chunkSKWYPONI_cjs = require('../chunk-SKWYPONI.cjs'); var chunkRQ4MB5TM_cjs = require('../chunk-RQ4MB5TM.cjs'); var chunkCAVARKYS_cjs = require('../chunk-CAVARKYS.cjs'); var crypto = require('crypto'); var EMPTY_USAGE = { inputTokens: void 0, outputTokens: void 0, totalTokens: void 0 }; function toAgentStreamChunk(runId, chunk) { return { ...chunk, runId, from: "AGENT" }; } 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 lines = trimmedBlock.split(/\r?\n/); const dataLines = lines.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) { const artifactText = (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"); const statusText = task.status.message ? extractMessageText(task.status.message) : ""; return [artifactText, statusText].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 = chunkRQ4MB5TM_cjs.convertMessages(messages).to("AIV5.Model"); const lines = []; if (options?.instructions) { lines.push(`Instructions: ${options.instructions}`); } const contextMessages = options?.context; if (contextMessages?.length) { lines.push( "Context:\n" + contextMessages.map((message) => { const role = message.role ?? "unknown"; const content = typeof message.content === "string" ? message.content : JSON.stringify(message.content); return `${role}: ${content}`; }).join("\n") ); } for (const message of converted) { if (message.role === "system") { const content2 = 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 (content2) { lines.push(`system: ${content2}`); } 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) { const threadId = typeof options?.memory?.thread === "string" ? options.memory.thread : options?.memory?.thread?.id; return { threadId, resourceId: options?.memory?.resource }; } function createResponseMessages(text, memoryInfo = {}) { if (!text) { return []; } return new chunkRQ4MB5TM_cjs.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 chunkSKWYPONI_cjs.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; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; } async function requireResponseBody(response, operation) { if (!response.body) { throw chunkSKWYPONI_cjs.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-${crypto.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 chunkCAVARKYS_cjs.RequestContext() } = {}) { if (!this.#memory) { return void 0; } 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 ?? crypto.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 chunkSKWYPONI_cjs.MastraA2AError.invalidParams("A2AAgent.resumeGenerate requires a runId."); } const state = this.#runState.get(runId); if (!state) { throw chunkSKWYPONI_cjs.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 chunkSKWYPONI_cjs.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 ?? crypto.randomUUID(); const prompt = messagesToPrompt(messages, options); const memoryInfo = resolveMemoryInfo(options); if (!bootstrap.streamingSupported) { const result = await this.generate(messages, options); return this.#createBufferedStreamResult({ runId, result, ...memoryInfo }); } return this.#runRemoteStream({ bootstrap, runId, prompt, signal: options?.abortSignal, ...memoryInfo }); } async resumeStream(resumeData, options) { const runId = options?.runId; if (!runId) { throw chunkSKWYPONI_cjs.MastraA2AError.invalidParams("A2AAgent.resumeStream requires a runId."); } const state = this.#runState.get(runId); if (!state) { throw chunkSKWYPONI_cjs.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, ...memoryInfo }); } return this.#runRemoteStream({ bootstrap, runId, prompt, signal: options?.abortSignal, contextId: state.contextId, referenceTaskIds: state.taskId ? [state.taskId] : void 0, ...memoryInfo }); } if (!state.taskId) { throw chunkSKWYPONI_cjs.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, ...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 response = await this.#request(cardUrl, { method: "GET", signal: this.#abortSignal }); const card = await response.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 }) { const response = await this.#request(bootstrap.executionUrl, { method: "POST", signal, body: { jsonrpc: "2.0", id: crypto.randomUUID(), method: "message/send", params: { message: { role: "user", kind: "message", messageId: crypto.randomUUID(), parts: [{ kind: "text", text: prompt }], ...contextId ? { contextId } : {}, ...referenceTaskIds?.length ? { referenceTaskIds } : {} } } } }); const json = await response.json(); return unwrapA2AResult(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 response = await this.#request(bootstrap.executionUrl, { method: "POST", signal, body: { jsonrpc: "2.0", id: crypto.randomUUID(), method: "tasks/get", params: { id: taskId } } }); const json = await response.json(); const result = unwrapA2AResult(json); if (!isTask(result)) { throw chunkSKWYPONI_cjs.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 }) { const response = await this.#request(bootstrap.executionUrl, { method: "POST", signal, stream: true, body: { jsonrpc: "2.0", id: crypto.randomUUID(), method: "message/stream", params: { message: { role: "user", kind: "message", messageId: crypto.randomUUID(), parts: [{ kind: "text", text: prompt }], ...contextId ? { contextId } : {}, ...referenceTaskIds?.length ? { referenceTaskIds } : {} } } } }); return this.#consumeA2AStream({ bootstrap, runId, stream: await requireResponseBody(response, "message/stream"), threadId, resourceId }); } 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: crypto.randomUUID(), method: "tasks/resubscribe", params: { id: taskId } } }); return this.#consumeA2AStream({ bootstrap, runId, initialTask, stream: await requireResponseBody(response, "tasks/resubscribe"), threadId, resourceId }); } async #consumeA2AStream({ bootstrap, runId, initialTask, stream, threadId, resourceId }) { const [consumerStream, accumulatorStream] = stream.tee(); const resultDeferred = createDeferred(); const textDeferred = createDeferred(); const taskDeferred = createDeferred(); const suspendPayloadDeferred = createDeferred(); const resumeSchemaDeferred = createDeferred(); const messageList = new chunkRQ4MB5TM_cjs.MessageList({ threadId, resourceId }); void 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); }); const streamResult = { runId, fullStream: this.#streamEvents({ bootstrap, runId, stream: consumerStream }), text: textDeferred.promise, toolResults: Promise.resolve([]), messageList, task: taskDeferred.promise, suspendPayload: suspendPayloadDeferred.promise, resumeSchema: resumeSchemaDeferred.promise, getResult: async () => resultDeferred.promise }; return streamResult; } async *#streamEvents({ bootstrap, runId, stream }) { 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: { finishReason: "stop", usage: EMPTY_USAGE } }); } 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 }) { const messageList = new chunkRQ4MB5TM_cjs.MessageList({ threadId, resourceId }); const toolName = this.id; const textId = resolveStreamTextId([result.message?.messageId, result.task?.id]); if (result.text) { messageList.add( { role: "assistant", content: result.text }, "response" ); } const fullStream = (async function* () { 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: { finishReason: "stop", usage: EMPTY_USAGE } }); })(); const streamResult = { runId, fullStream, 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 }; return streamResult; } 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 chunkSKWYPONI_cjs.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 void 0; } 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; } Object.defineProperty(exports, "MastraA2AError", { enumerable: true, get: function () { return chunkSKWYPONI_cjs.MastraA2AError; } }); exports.A2AAgent = A2AAgent; //# sourceMappingURL=index.cjs.map //# sourceMappingURL=index.cjs.map