@mastra/core
Version:
1,344 lines (1,343 loc) • 44.1 kB
JavaScript
import "./llm-DntEbB3j.js";
import { T as DelayedPromise } from "./trip-wire-csEv7lz7.js";
import { n as isUrlString } from "./content-DX_6irdy.js";
import { n as DefaultGeneratedFileWithType, t as DefaultGeneratedFile } from "./file-C2ghzAMN.js";
import EventEmitter$1 from "events";
import { ReadableStream as ReadableStream$1, TransformStream as TransformStream$1, WritableStream as WritableStream$1 } from "stream/web";
//#region src/stream/aisdk/v5/compat/ui-message.ts
function convertFullStreamChunkToUIMessageStream({ part, messageMetadataValue, sendReasoning, sendSources, onError, sendStart, sendFinish, responseMessageId }) {
const partType = part.type;
switch (partType) {
case "text-start": return {
type: "text-start",
id: part.id,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
case "text-delta": return {
type: "text-delta",
id: part.id,
delta: part.text,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
case "text-end": return {
type: "text-end",
id: part.id,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
case "reasoning-start": return {
type: "reasoning-start",
id: part.id,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
case "reasoning-delta":
if (sendReasoning) return {
type: "reasoning-delta",
id: part.id,
delta: part.text,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
return;
case "reasoning-end": return {
type: "reasoning-end",
id: part.id,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
case "file": {
const base64 = part.file.base64;
return {
type: "file",
mediaType: part.file.mediaType,
url: isUrlString(base64) ? base64 : `data:${part.file.mediaType};base64,${base64}`
};
}
case "source":
if (sendSources && part.sourceType === "url") return {
type: "source-url",
sourceId: part.id,
url: part.url,
title: part.title,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
if (sendSources && part.sourceType === "document") return {
type: "source-document",
sourceId: part.id,
mediaType: part.mediaType,
title: part.title,
filename: part.filename,
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {}
};
return;
case "tool-input-start": return {
type: "tool-input-start",
toolCallId: part.id,
toolName: part.toolName,
...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
...part.dynamic != null ? { dynamic: part.dynamic } : {}
};
case "tool-input-delta": return {
type: "tool-input-delta",
toolCallId: part.id,
inputTextDelta: part.delta
};
case "tool-call": return {
type: "tool-input-available",
toolCallId: part.toolCallId,
toolName: part.toolName,
input: part.input,
...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
...part.providerMetadata != null ? { providerMetadata: part.providerMetadata } : {},
...part.dynamic != null ? { dynamic: part.dynamic } : {}
};
case "tool-result": return {
type: "tool-output-available",
toolCallId: part.toolCallId,
output: part.output,
...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
...part.dynamic != null ? { dynamic: part.dynamic } : {}
};
case "tool-output": return { ...part.output };
case "tool-error": return {
type: "tool-output-error",
toolCallId: part.toolCallId,
errorText: onError(part.error),
...part.providerExecuted != null ? { providerExecuted: part.providerExecuted } : {},
...part.dynamic != null ? { dynamic: part.dynamic } : {}
};
case "error": return {
type: "error",
errorText: onError(part.error)
};
case "start-step": return { type: "start-step" };
case "finish-step": return { type: "finish-step" };
case "start":
if (sendStart) return {
type: "start",
...messageMetadataValue != null ? { messageMetadata: messageMetadataValue } : {},
...responseMessageId != null ? { messageId: responseMessageId } : {}
};
return;
case "finish":
if (sendFinish) return {
type: "finish",
...messageMetadataValue != null ? { messageMetadata: messageMetadataValue } : {}
};
return;
case "abort": return part;
case "tool-input-end": return;
case "raw": return;
default: throw new Error(`Unknown chunk type: ${partType}`);
}
}
//#endregion
//#region src/stream/base/consume-stream.ts
async function consumeStream({ stream, onError }) {
const reader = stream.getReader();
try {
while (true) {
const { done } = await reader.read();
if (done) break;
}
} catch (error) {
onError?.(error);
} finally {
reader.releaseLock();
}
}
//#endregion
//#region src/stream/RunOutput.ts
var WorkflowRunOutput = class {
#status = "running";
#tripwireData;
#usageCount = {
inputTokens: 0,
outputTokens: 0,
totalTokens: 0,
cachedInputTokens: 0,
cacheCreationInputTokens: 0,
reasoningTokens: 0
};
#consumptionStarted = false;
#baseStream;
#emitter = new EventEmitter$1();
#bufferedChunks = [];
#streamFinished = false;
#streamError;
#finalWorkflowResult;
#delayedPromises = {
usage: new DelayedPromise(),
result: new DelayedPromise()
};
/**
* Unique identifier for this workflow run
*/
runId;
/**
* Unique identifier for this workflow
*/
workflowId;
constructor({ runId, workflowId, stream }) {
const self = this;
this.runId = runId;
this.workflowId = workflowId;
this.#baseStream = stream;
stream.pipeTo(new WritableStream$1({
start() {
const chunk = {
type: "workflow-start",
runId: self.runId,
from: "WORKFLOW",
payload: { workflowId: self.workflowId }
};
self.#bufferedChunks.push(chunk);
self.#emitter.emit("chunk", chunk);
},
write(chunk) {
if (chunk.type !== "workflow-step-finish") {
self.#bufferedChunks.push(chunk);
self.#emitter.emit("chunk", chunk);
}
if (chunk.type === "workflow-step-output") {
if ("output" in chunk.payload && chunk.payload.output) {
const output = chunk.payload.output;
if (output.type === "finish") {
if (output.payload && "usage" in output.payload && output.payload.usage) self.#updateUsageCount(output.payload.usage);
else if (output.payload && "output" in output.payload && output.payload.output) {
const outputPayload = output.payload.output;
if ("usage" in outputPayload && outputPayload.usage) self.#updateUsageCount(outputPayload.usage);
}
}
}
} else if (chunk.type === "workflow-canceled") self.#status = "canceled";
else if (chunk.type === "workflow-step-suspended") self.#status = "suspended";
else if (chunk.type === "workflow-step-result" && chunk.payload.status === "failed") if (chunk.payload.tripwire) {
self.#status = "tripwire";
self.#tripwireData = chunk.payload.tripwire;
} else self.#status = "failed";
else if (chunk.type === "workflow-paused") self.#status = "paused";
},
close() {
if (self.#status === "running") self.#status = "success";
self.#emitter.emit("chunk", {
type: "workflow-finish",
runId: self.runId,
from: "WORKFLOW",
payload: {
workflowStatus: self.#status,
metadata: self.#streamError ? {
error: self.#streamError,
errorMessage: self.#streamError?.message
} : {},
output: { usage: self.#usageCount },
...self.#status === "success" && self.#finalWorkflowResult !== void 0 ? { finalWorkflowResult: self.#finalWorkflowResult } : {},
...self.#status === "tripwire" && self.#tripwireData ? { tripwire: self.#tripwireData } : {}
}
});
self.#delayedPromises.usage.resolve(self.#usageCount);
Object.entries(self.#delayedPromises).forEach(([key, promise]) => {
if (promise.status.type === "pending") promise.reject(/* @__PURE__ */ new Error(`promise '${key}' was not resolved or rejected when stream finished`));
});
self.#streamFinished = true;
self.#emitter.emit("finish");
}
})).catch((reason) => {
console.log(" something went wrong", reason);
});
}
#getDelayedPromise(promise) {
if (!this.#consumptionStarted) this.consumeStream();
return promise.promise;
}
#updateUsageCount(usage) {
let totalUsage = {
inputTokens: this.#usageCount.inputTokens ?? 0,
outputTokens: this.#usageCount.outputTokens ?? 0,
totalTokens: this.#usageCount.totalTokens ?? 0,
reasoningTokens: this.#usageCount.reasoningTokens ?? 0,
cachedInputTokens: this.#usageCount.cachedInputTokens ?? 0,
cacheCreationInputTokens: this.#usageCount.cacheCreationInputTokens ?? 0
};
if ("inputTokens" in usage) {
totalUsage.inputTokens += parseInt(usage?.inputTokens?.toString() ?? "0", 10);
totalUsage.outputTokens += parseInt(usage?.outputTokens?.toString() ?? "0", 10);
} else if ("promptTokens" in usage) {
totalUsage.inputTokens += parseInt(usage?.promptTokens?.toString() ?? "0", 10);
totalUsage.outputTokens += parseInt(usage?.completionTokens?.toString() ?? "0", 10);
}
totalUsage.totalTokens += parseInt(usage?.totalTokens?.toString() ?? "0", 10);
totalUsage.reasoningTokens += parseInt(usage?.reasoningTokens?.toString() ?? "0", 10);
totalUsage.cachedInputTokens += parseInt(usage?.cachedInputTokens?.toString() ?? "0", 10);
totalUsage.cacheCreationInputTokens += parseInt(usage?.cacheCreationInputTokens?.toString() ?? "0", 10);
this.#usageCount = totalUsage;
}
/**
* @internal
*/
updateResults(results) {
if (results.status === "success") this.#finalWorkflowResult = results.result;
this.#delayedPromises.result.resolve(results);
}
/**
* @internal
*/
rejectResults(error) {
this.#delayedPromises.result.reject(error);
this.#status = "failed";
this.#streamError = error;
}
/**
* @internal
*/
resume(stream) {
this.#baseStream = stream;
this.#streamFinished = false;
this.#consumptionStarted = false;
this.#status = "running";
this.#delayedPromises = {
usage: new DelayedPromise(),
result: new DelayedPromise()
};
const self = this;
stream.pipeTo(new WritableStream$1({
start() {
const chunk = {
type: "workflow-start",
runId: self.runId,
from: "WORKFLOW",
payload: { workflowId: self.workflowId }
};
self.#bufferedChunks.push(chunk);
self.#emitter.emit("chunk", chunk);
},
write(chunk) {
if (chunk.type !== "workflow-step-finish") {
self.#bufferedChunks.push(chunk);
self.#emitter.emit("chunk", chunk);
}
if (chunk.type === "workflow-step-output") {
if ("output" in chunk.payload && chunk.payload.output) {
const output = chunk.payload.output;
if (output.type === "finish") {
if (output.payload && "usage" in output.payload && output.payload.usage) self.#updateUsageCount(output.payload.usage);
else if (output.payload && "output" in output.payload && output.payload.output) {
const outputPayload = output.payload.output;
if ("usage" in outputPayload && outputPayload.usage) self.#updateUsageCount(outputPayload.usage);
}
}
}
} else if (chunk.type === "workflow-canceled") self.#status = "canceled";
else if (chunk.type === "workflow-step-suspended") self.#status = "suspended";
else if (chunk.type === "workflow-step-result" && chunk.payload.status === "failed") if (chunk.payload.tripwire) {
self.#status = "tripwire";
self.#tripwireData = chunk.payload.tripwire;
} else self.#status = "failed";
else if (chunk.type === "workflow-paused") self.#status = "paused";
},
close() {
if (self.#status === "running") self.#status = "success";
self.#emitter.emit("chunk", {
type: "workflow-finish",
runId: self.runId,
from: "WORKFLOW",
payload: {
workflowStatus: self.#status,
metadata: self.#streamError ? {
error: self.#streamError,
errorMessage: self.#streamError?.message
} : {},
output: { usage: self.#usageCount },
...self.#status === "success" && self.#finalWorkflowResult !== void 0 ? { finalWorkflowResult: self.#finalWorkflowResult } : {},
...self.#status === "tripwire" && self.#tripwireData ? { tripwire: self.#tripwireData } : {}
}
});
self.#streamFinished = true;
self.#emitter.emit("finish");
}
})).catch((reason) => {
console.log(" something went wrong", reason);
});
}
async consumeStream(options) {
if (this.#consumptionStarted) return;
this.#consumptionStarted = true;
try {
await consumeStream({
stream: this.#baseStream,
onError: options?.onError
});
} catch (error) {
options?.onError?.(error);
}
}
get fullStream() {
const self = this;
let detach;
return new ReadableStream$1({
start(controller) {
self.#bufferedChunks.forEach((chunk) => {
controller.enqueue(chunk);
});
if (self.#streamFinished) {
controller.close();
return;
}
const chunkHandler = (chunk) => {
controller.enqueue(chunk);
};
const finishHandler = () => {
self.#emitter.off("chunk", chunkHandler);
self.#emitter.off("finish", finishHandler);
controller.close();
};
self.#emitter.on("chunk", chunkHandler);
self.#emitter.on("finish", finishHandler);
detach = () => {
self.#emitter.off("chunk", chunkHandler);
self.#emitter.off("finish", finishHandler);
};
},
pull(_controller) {
if (!self.#consumptionStarted) self.consumeStream();
},
cancel() {
detach?.();
}
});
}
get status() {
return this.#status;
}
get result() {
return this.#getDelayedPromise(this.#delayedPromises.result);
}
get usage() {
return this.#getDelayedPromise(this.#delayedPromises.usage);
}
/**
* @deprecated Use `fullStream.locked` instead
*/
get locked() {
console.warn("WorkflowRunOutput.locked is deprecated. Use fullStream.locked instead.");
return this.fullStream.locked;
}
/**
* @deprecated Use `fullStream.cancel()` instead
*/
cancel(reason) {
console.warn("WorkflowRunOutput.cancel() is deprecated. Use fullStream.cancel() instead.");
return this.fullStream.cancel(reason);
}
/**
* @deprecated Use `fullStream.getReader()` instead
*/
getReader(options) {
console.warn("WorkflowRunOutput.getReader() is deprecated. Use fullStream.getReader() instead.");
return this.fullStream.getReader(options);
}
/**
* @deprecated Use `fullStream.pipeThrough()` instead
*/
pipeThrough(transform, options) {
console.warn("WorkflowRunOutput.pipeThrough() is deprecated. Use fullStream.pipeThrough() instead.");
return this.fullStream.pipeThrough(transform, options);
}
/**
* @deprecated Use `fullStream.pipeTo()` instead
*/
pipeTo(destination, options) {
console.warn("WorkflowRunOutput.pipeTo() is deprecated. Use fullStream.pipeTo() instead.");
return this.fullStream.pipeTo(destination, options);
}
/**
* @deprecated Use `fullStream.tee()` instead
*/
tee() {
console.warn("WorkflowRunOutput.tee() is deprecated. Use fullStream.tee() instead.");
return this.fullStream.tee();
}
/**
* @deprecated Use `fullStream[Symbol.asyncIterator]()` instead
*/
[Symbol.asyncIterator]() {
console.warn("WorkflowRunOutput[Symbol.asyncIterator]() is deprecated. Use fullStream[Symbol.asyncIterator]() instead.");
return this.fullStream[Symbol.asyncIterator]();
}
/**
* Helper method to treat this object as a ReadableStream
* @deprecated Use `fullStream` directly instead
*/
toReadableStream() {
console.warn("WorkflowRunOutput.toReadableStream() is deprecated. Use fullStream directly instead.");
return this.fullStream;
}
};
//#endregion
//#region src/stream/MastraAgentNetworkStream.ts
var MastraAgentNetworkStream = class extends ReadableStream$1 {
#usageCount = {
inputTokens: 0,
outputTokens: 0,
totalTokens: 0,
cachedInputTokens: 0,
cacheCreationInputTokens: 0,
reasoningTokens: 0
};
#streamPromise;
#objectPromise;
#objectStreamController = null;
#objectStream = null;
#run;
runId;
constructor({ createStream, run }) {
const deferredPromise = {
promise: null,
resolve: null,
reject: null
};
deferredPromise.promise = new Promise((resolve, reject) => {
deferredPromise.resolve = resolve;
deferredPromise.reject = reject;
});
const objectDeferredPromise = {
promise: null,
resolve: null,
reject: null
};
objectDeferredPromise.promise = new Promise((resolve, reject) => {
objectDeferredPromise.resolve = resolve;
objectDeferredPromise.reject = reject;
});
let objectStreamController = null;
const updateUsageCount = (usage) => {
this.#usageCount.inputTokens += parseInt(usage?.inputTokens?.toString() ?? "0", 10);
this.#usageCount.outputTokens += parseInt(usage?.outputTokens?.toString() ?? "0", 10);
this.#usageCount.totalTokens += parseInt(usage?.totalTokens?.toString() ?? "0", 10);
this.#usageCount.reasoningTokens += parseInt(usage?.reasoningTokens?.toString() ?? "0", 10);
this.#usageCount.cachedInputTokens += parseInt(usage?.cachedInputTokens?.toString() ?? "0", 10);
this.#usageCount.cacheCreationInputTokens += parseInt(usage?.cacheCreationInputTokens?.toString() ?? "0", 10);
};
super({ start: async (controller) => {
try {
const stream = await createStream(new WritableStream({ write: (chunk) => {
if (chunk.type === "step-output" && chunk.payload?.output?.from === "AGENT" && chunk.payload?.output?.type === "finish" || chunk.type === "step-output" && chunk.payload?.output?.from === "WORKFLOW" && chunk.payload?.output?.type === "finish") {
const output = chunk.payload?.output;
if (output && "payload" in output && output.payload) {
const finishPayload = output.payload;
if ("usage" in finishPayload && finishPayload.usage) updateUsageCount(finishPayload.usage);
else if ("output" in finishPayload && finishPayload.output) {
const outputPayload = finishPayload.output;
if ("usage" in outputPayload && outputPayload.usage) updateUsageCount(outputPayload.usage);
}
}
}
controller.enqueue(chunk);
} }));
const getInnerChunk = (chunk) => {
if (chunk.type === "workflow-step-output") return getInnerChunk(chunk.payload.output);
return chunk;
};
let objectResolved = false;
for await (const chunk of stream) if (chunk.type === "workflow-step-output") {
const innerChunk = getInnerChunk(chunk);
if (innerChunk.type === "routing-agent-end" || innerChunk.type === "agent-execution-end" || innerChunk.type === "workflow-execution-end") {
if (innerChunk.payload?.usage) updateUsageCount(innerChunk.payload.usage);
}
if (innerChunk.type === "network-object") {
if (objectStreamController) objectStreamController.enqueue(innerChunk.payload?.object);
controller.enqueue(innerChunk);
} else if (innerChunk.type === "network-object-result") {
if (!objectResolved) {
objectResolved = true;
objectDeferredPromise.resolve(innerChunk.payload?.object);
if (objectStreamController) objectStreamController.close();
}
controller.enqueue(innerChunk);
} else if (innerChunk.type === "network-execution-event-finish") {
const finishPayload = {
...innerChunk.payload,
usage: this.#usageCount
};
controller.enqueue({
...innerChunk,
payload: finishPayload
});
} else controller.enqueue(innerChunk);
}
if (!objectResolved) {
objectDeferredPromise.resolve(void 0);
if (objectStreamController) objectStreamController.close();
}
controller.close();
deferredPromise.resolve();
} catch (error) {
controller.error(error);
deferredPromise.reject(error);
objectDeferredPromise.reject(error);
if (objectStreamController) objectStreamController.error(error);
}
} });
this.#run = run;
this.#streamPromise = deferredPromise;
this.runId = run.runId;
this.#objectPromise = objectDeferredPromise;
this.#objectStream = new ReadableStream$1({ start: (ctrl) => {
objectStreamController = ctrl;
this.#objectStreamController = ctrl;
} });
}
get status() {
return this.#streamPromise.promise.then(() => this.#run._getExecutionResults()).then((res) => res.status);
}
get result() {
return this.#streamPromise.promise.then(() => this.#run._getExecutionResults());
}
get usage() {
return this.#streamPromise.promise.then(() => this.#usageCount);
}
/**
* Returns a promise that resolves to the structured output object.
* Only available when structuredOutput option is provided to network().
* Resolves to undefined if no structuredOutput was requested.
*/
get object() {
return this.#objectPromise.promise;
}
/**
* Returns a ReadableStream of partial objects during structured output generation.
* Useful for streaming partial results as they're being generated.
*/
get objectStream() {
return this.#objectStream;
}
};
//#endregion
//#region src/stream/aisdk/v5/transform.ts
/**
* Sanitizes tool-call input strings for safe JSON parsing.
*
* LLMs sometimes append internal tokens like `<|call|>`, `<|endoftext|>`, or `<|end|>`
* to otherwise valid JSON in streamed tool-call arguments, causing JSON.parse to fail.
*
* This function first attempts JSON.parse on the original input. If parsing succeeds,
* the original string is returned unchanged — this avoids corrupting valid JSON payloads
* that legitimately contain `<|...|>` patterns inside string values.
*
* Only when the original input is not valid JSON does the function fall back to stripping
* `<|...|>` token patterns and surrounding whitespace via regex.
*
* @see https://github.com/mastra-ai/mastra/issues/13261
* @see https://github.com/mastra-ai/mastra/issues/13185
*/
function sanitizeToolCallInput(input) {
try {
JSON.parse(input);
return input;
} catch {
return input.replace(/<\|[^|]*\|>\s*/g, "").trim();
}
}
/**
* Attempts to repair common JSON malformations produced by LLM providers.
*
* Some LLM providers (e.g., Kimi/K2) occasionally generate malformed JSON for
* tool call arguments. This function applies a sequence of targeted fixes for
* the most common errors before giving up.
*
* Repairs applied (in order):
* 1. Missing quote before property name: `{"a":"b",c":"d"}` → `{"a":"b","c":"d"}`
* 2. Unquoted property names: `{command:"value"}` → `{"command":"value"}`
* 3. Single quotes → double quotes (only outside already-double-quoted strings)
* 4. Trailing commas: `{"a":1,}` → `{"a":1}`
*
* @returns The parsed object if repair succeeds, or null if the JSON is unrecoverable.
* @see https://github.com/mastra-ai/mastra/issues/11078
*/
function tryRepairJson(input) {
let repaired = input.trim();
repaired = repaired.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)"/g, (match, prefix, name) => {
if (prefix.trimEnd().endsWith("\"")) return match;
return `${prefix}"${name}"`;
});
repaired = repaired.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, "$1\"$2\":");
repaired = repaired.replace(/'/g, "\"");
repaired = repaired.replace(/,(\s*[}\]])/g, "$1");
repaired = repaired.replace(/:\s*(\d{4}-\d{2}-\d{2}(?:T[\d:]+)?)\s*([,}])/g, ": \"$1\"$2");
try {
return JSON.parse(repaired);
} catch {
return null;
}
}
function convertFullStreamChunkToMastra(value, ctx) {
switch (value.type) {
case "response-metadata": return {
type: "response-metadata",
runId: ctx.runId,
from: "AGENT",
payload: { ...value }
};
case "text-start": return {
type: "text-start",
runId: ctx.runId,
from: "AGENT",
payload: {
id: value.id,
providerMetadata: value.providerMetadata
}
};
case "text-delta":
if (value.delta || value.providerMetadata != null) return {
type: "text-delta",
runId: ctx.runId,
from: "AGENT",
payload: {
id: value.id,
providerMetadata: value.providerMetadata,
text: value.delta
}
};
return;
case "text-end": return {
type: "text-end",
runId: ctx.runId,
from: "AGENT",
payload: value
};
case "reasoning-start": return {
type: "reasoning-start",
runId: ctx.runId,
from: "AGENT",
payload: {
id: value.id,
providerMetadata: value.providerMetadata
}
};
case "reasoning-delta": return {
type: "reasoning-delta",
runId: ctx.runId,
from: "AGENT",
payload: {
id: value.id,
providerMetadata: value.providerMetadata,
text: value.delta
}
};
case "reasoning-end": return {
type: "reasoning-end",
runId: ctx.runId,
from: "AGENT",
payload: {
id: value.id,
providerMetadata: value.providerMetadata
}
};
case "source": return {
type: "source",
runId: ctx.runId,
from: "AGENT",
payload: {
id: value.id,
sourceType: value.sourceType,
title: value.title || "",
mimeType: value.sourceType === "document" ? value.mediaType : void 0,
filename: value.sourceType === "document" ? value.filename : void 0,
url: value.sourceType === "url" ? value.url : void 0,
providerMetadata: value.providerMetadata
}
};
case "file": {
const pm = value.providerMetadata;
return {
type: "file",
runId: ctx.runId,
from: "AGENT",
payload: {
data: value.data,
base64: typeof value.data === "string" && !isUrlString(value.data) ? value.data : void 0,
mimeType: value.mediaType,
...pm != null ? { providerMetadata: pm } : {}
}
};
}
case "tool-call": {
let toolCallInput = void 0;
if (value.input) {
const sanitized = sanitizeToolCallInput(value.input);
if (sanitized) try {
toolCallInput = JSON.parse(sanitized);
} catch {
const repaired = tryRepairJson(sanitized);
if (repaired) toolCallInput = repaired;
else {
console.error("Error converting tool call input to JSON", { input: value.input });
toolCallInput = void 0;
}
}
}
return {
type: "tool-call",
runId: ctx.runId,
from: "AGENT",
payload: {
toolCallId: value.toolCallId,
toolName: value.toolName,
args: toolCallInput,
providerExecuted: value.providerExecuted,
providerMetadata: value.providerMetadata,
...value.observability ? { observability: value.observability } : {}
}
};
}
case "tool-result": return {
type: "tool-result",
runId: ctx.runId,
from: "AGENT",
payload: {
toolCallId: value.toolCallId,
toolName: value.toolName,
result: value.result,
isError: value.isError,
providerExecuted: value.providerExecuted,
providerMetadata: value.providerMetadata
}
};
case "tool-input-start": return {
type: "tool-call-input-streaming-start",
runId: ctx.runId,
from: "AGENT",
payload: {
toolCallId: value.id,
toolName: value.toolName,
providerExecuted: value.providerExecuted,
providerMetadata: value.providerMetadata,
dynamic: value.dynamic,
...value.observability ? { observability: value.observability } : {}
}
};
case "tool-input-delta":
if (value.delta) return {
type: "tool-call-delta",
runId: ctx.runId,
from: "AGENT",
payload: {
argsTextDelta: value.delta,
toolCallId: value.id,
providerMetadata: value.providerMetadata
}
};
return;
case "tool-input-end": return {
type: "tool-call-input-streaming-end",
runId: ctx.runId,
from: "AGENT",
payload: {
toolCallId: value.id,
providerMetadata: value.providerMetadata
}
};
case "finish":
const { finishReason, usage, providerMetadata, messages, ...rest } = value;
return {
type: "finish",
runId: ctx.runId,
from: "AGENT",
payload: {
providerMetadata: value.providerMetadata,
stepResult: { reason: normalizeFinishReason(value.finishReason) },
output: { usage: normalizeUsage(value.usage) },
metadata: { providerMetadata: value.providerMetadata },
messages: messages ?? {
all: [],
user: [],
nonUser: []
},
...rest
}
};
case "error": return {
type: "error",
runId: ctx.runId,
from: "AGENT",
payload: value
};
case "raw": return {
type: "raw",
runId: ctx.runId,
from: "AGENT",
payload: value.rawValue
};
}
}
function convertMastraChunkToAISDKv5({ chunk, mode = "stream" }) {
switch (chunk.type) {
case "start": return { type: "start" };
case "step-start":
const { messageId: _messageId, ...rest } = chunk.payload;
return {
type: "start-step",
request: rest.request,
warnings: rest.warnings || []
};
case "raw": return {
type: "raw",
rawValue: chunk.payload
};
case "finish": return {
type: "finish",
finishReason: chunk.payload.stepResult.reason,
totalUsage: chunk.payload.output.usage
};
case "reasoning-start": return {
type: "reasoning-start",
id: chunk.payload.id,
providerMetadata: chunk.payload.providerMetadata
};
case "reasoning-delta": return {
type: "reasoning-delta",
id: chunk.payload.id,
text: chunk.payload.text,
providerMetadata: chunk.payload.providerMetadata
};
case "reasoning-signature": throw new Error("AISDKv5 chunk type \"reasoning-signature\" not supported");
case "redacted-reasoning": throw new Error("AISDKv5 chunk type \"redacted-reasoning\" not supported");
case "reasoning-end": return {
type: "reasoning-end",
id: chunk.payload.id,
providerMetadata: chunk.payload.providerMetadata
};
case "source": if (chunk.payload.sourceType === "url") return {
type: "source",
sourceType: "url",
id: chunk.payload.id,
url: chunk.payload.url,
title: chunk.payload.title,
providerMetadata: chunk.payload.providerMetadata
};
else return {
type: "source",
sourceType: "document",
id: chunk.payload.id,
mediaType: chunk.payload.mimeType,
title: chunk.payload.title,
filename: chunk.payload.filename,
providerMetadata: chunk.payload.providerMetadata
};
case "file": {
const filePart = mode === "generate" ? {
type: "file",
file: new DefaultGeneratedFile({
data: chunk.payload.data,
mediaType: chunk.payload.mimeType
})
} : {
type: "file",
file: new DefaultGeneratedFileWithType({
data: chunk.payload.data,
mediaType: chunk.payload.mimeType
})
};
if (chunk.payload.providerMetadata) filePart.providerMetadata = chunk.payload.providerMetadata;
return filePart;
}
case "tool-call": {
const toolCallPart = {
type: "tool-call",
toolCallId: chunk.payload.toolCallId,
providerMetadata: chunk.payload.providerMetadata,
providerExecuted: chunk.payload.providerExecuted,
toolName: chunk.payload.toolName,
input: chunk.payload.args
};
if (chunk.payload.observability) toolCallPart.observability = chunk.payload.observability;
return toolCallPart;
}
case "tool-call-input-streaming-start": return {
type: "tool-input-start",
id: chunk.payload.toolCallId,
toolName: chunk.payload.toolName,
dynamic: !!chunk.payload.dynamic,
providerMetadata: chunk.payload.providerMetadata,
providerExecuted: chunk.payload.providerExecuted,
...chunk.payload.observability ? { observability: chunk.payload.observability } : {}
};
case "tool-call-input-streaming-end": return {
type: "tool-input-end",
id: chunk.payload.toolCallId,
providerMetadata: chunk.payload.providerMetadata
};
case "tool-call-delta": return {
type: "tool-input-delta",
id: chunk.payload.toolCallId,
delta: chunk.payload.argsTextDelta,
providerMetadata: chunk.payload.providerMetadata
};
case "step-finish": {
const { request: _request, providerMetadata: metadataProviderMetadata, ...rest } = chunk.payload.metadata;
return {
type: "finish-step",
response: {
id: chunk.payload.id || "",
timestamp: /* @__PURE__ */ new Date(),
modelId: rest.modelId || "",
...rest
},
usage: chunk.payload.output.usage,
finishReason: chunk.payload.stepResult.reason,
providerMetadata: metadataProviderMetadata ?? chunk.payload.providerMetadata
};
}
case "text-delta": return {
type: "text-delta",
id: chunk.payload.id,
text: chunk.payload.text,
providerMetadata: chunk.payload.providerMetadata
};
case "text-end": return {
type: "text-end",
id: chunk.payload.id,
providerMetadata: chunk.payload.providerMetadata
};
case "text-start": return {
type: "text-start",
id: chunk.payload.id,
providerMetadata: chunk.payload.providerMetadata
};
case "tool-result": return {
type: "tool-result",
input: chunk.payload.args,
toolCallId: chunk.payload.toolCallId,
providerExecuted: chunk.payload.providerExecuted,
toolName: chunk.payload.toolName,
output: chunk.payload.result
};
case "tool-error": return {
type: "tool-error",
error: chunk.payload.error,
input: chunk.payload.args,
toolCallId: chunk.payload.toolCallId,
providerExecuted: chunk.payload.providerExecuted,
toolName: chunk.payload.toolName
};
case "abort": return { type: "abort" };
case "error": return {
type: "error",
error: chunk.payload.error
};
case "object": return {
type: "object",
object: chunk.object
};
default:
if (chunk.type && "payload" in chunk && chunk.payload) return {
type: chunk.type,
...chunk.payload || {}
};
return;
}
}
/**
* Type guard to check if usage is in V3 format (nested objects)
*/
function isV3Usage(usage) {
if (!usage || typeof usage !== "object") return false;
const u = usage;
return typeof u.inputTokens === "object" && u.inputTokens !== null && "total" in u.inputTokens && typeof u.outputTokens === "object" && u.outputTokens !== null && "total" in u.outputTokens;
}
/**
* Normalizes usage from either V2 (flat) or V3 (nested) format to Mastra's flat format.
* V2 format: { inputTokens: number, outputTokens: number, totalTokens?: number }
* V3 format: { inputTokens: { total, noCache, cacheRead, cacheWrite }, outputTokens: { total, text, reasoning } }
*
* The original usage data is preserved in the `raw` field for advanced use cases.
*/
function normalizeUsage(usage) {
if (!usage) return {
inputTokens: void 0,
outputTokens: void 0,
totalTokens: void 0,
reasoningTokens: void 0,
cachedInputTokens: void 0,
cacheCreationInputTokens: void 0,
raw: void 0
};
if (isV3Usage(usage)) {
const inputTokens = usage.inputTokens.total;
const outputTokens = usage.outputTokens.total;
return {
inputTokens,
outputTokens,
totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0),
reasoningTokens: usage.outputTokens.reasoning,
cachedInputTokens: usage.inputTokens.cacheRead,
cacheCreationInputTokens: usage.inputTokens.cacheWrite,
raw: usage
};
}
const v2Usage = usage;
return {
inputTokens: v2Usage.inputTokens,
outputTokens: v2Usage.outputTokens,
totalTokens: v2Usage.totalTokens ?? (v2Usage.inputTokens ?? 0) + (v2Usage.outputTokens ?? 0),
reasoningTokens: v2Usage.reasoningTokens,
cachedInputTokens: v2Usage.cachedInputTokens,
cacheCreationInputTokens: v2Usage.cacheCreationInputTokens,
raw: usage
};
}
/**
* Type guard to check if a finish reason is V3 format (object with unified/raw properties)
*/
function isV3FinishReason(finishReason) {
return typeof finishReason === "object" && finishReason !== null && "unified" in finishReason;
}
/**
* Normalize finish reason from either V2/V5 (string) or V3/V6 (object) format to a string.
*
* V2/V5 format: 'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other' | 'unknown'
* V3/V6 format: { unified: 'stop' | 'length' | 'content-filter' | 'tool-calls' | 'error' | 'other', raw: string | undefined }
*
* We normalize to the unified string value for internal Mastra use.
* Note: V6 removed 'unknown' and merged it into 'other'.
*/
function normalizeFinishReason(finishReason) {
if (!finishReason) return "other";
if (finishReason === "tripwire" || finishReason === "retry") return finishReason;
if (isV3FinishReason(finishReason)) return finishReason.unified;
return finishReason === "unknown" ? "other" : finishReason;
}
//#endregion
//#region src/stream/caching-transform-stream.ts
/**
* Creates a TransformStream that caches all chunks passing through it.
*
* Use this for workflow streaming where you need resumable streams
* without changing to a PubSub-based architecture.
*
* @example
* ```typescript
* const cache = mastra.getServerCache();
* const { transform, getHistory } = createCachingTransformStream({
* cache,
* cacheKey: runId,
* });
*
* // Use the transform stream
* const cachedStream = sourceStream.pipeThrough(transform);
*
* // Later, get cached history for replay
* const history = await getHistory();
* ```
*/
function createCachingTransformStream(options) {
const { cache, cacheKey, serialize = (x) => x, deserialize = (x) => x } = options;
const transform = new TransformStream({ transform(chunk, controller) {
const serialized = serialize(chunk);
cache.listPush(cacheKey, serialized).catch(() => {});
controller.enqueue(chunk);
} });
const getHistory = async (offset = 0) => {
return (await cache.listFromTo(cacheKey, offset)).map((item) => deserialize(item));
};
const clearCache = async () => {
await cache.delete(cacheKey);
};
return {
transform,
getHistory,
clearCache
};
}
/**
* Creates a ReadableStream that first emits cached history, then pipes from a live source.
*
* Use this when a client reconnects and needs to receive missed chunks
* before continuing with the live stream.
*
* @example
* ```typescript
* const cache = mastra.getServerCache();
*
* // Get cached history
* const history = await cache.listFromTo(runId, 0);
*
* // Create combined stream
* const stream = createReplayStream({
* history: history as ChunkType[],
* liveSource: workflow.stream(),
* cache,
* cacheKey: runId,
* });
* ```
*/
function createReplayStream(options) {
const { history, liveSource, cache, cacheKey, serialize = (x) => x } = options;
let historyIndex = 0;
let liveReader = null;
let historyComplete = false;
return new ReadableStream({
async pull(controller) {
if (!historyComplete) {
if (historyIndex < history.length) {
controller.enqueue(history[historyIndex]);
historyIndex++;
return;
}
historyComplete = true;
liveReader = liveSource.getReader();
}
if (liveReader) try {
const { done, value } = await liveReader.read();
if (done) {
controller.close();
return;
}
if (cache && cacheKey) {
const serialized = serialize(value);
cache.listPush(cacheKey, serialized).catch(() => {});
}
controller.enqueue(value);
} catch (error) {
controller.error(error);
}
},
cancel() {
if (liveReader) liveReader.cancel();
}
});
}
/**
* Helper to create a caching transform and get history in one call.
*
* This is the recommended way to add caching to workflow streams.
*
* @example
* ```typescript
* const { pipeThrough, getHistory, clearCache } = withStreamCaching({
* cache: mastra.getServerCache(),
* cacheKey: runId,
* });
*
* // Apply caching to a stream
* const cachedStream = workflow.fullStream.pipeThrough(pipeThrough());
*
* // On reconnect, get history and create replay stream
* const history = await getHistory();
* const replayStream = createReplayStream({
* history,
* liveSource: workflow.resumeStream(),
* });
* ```
*/
function withStreamCaching(options) {
const { cache, cacheKey, serialize, deserialize } = options;
return {
pipeThrough: () => {
const { transform } = createCachingTransformStream({
cache,
cacheKey,
serialize,
deserialize
});
return transform;
},
getHistory: async (offset = 0) => {
const deserializeFn = deserialize ?? ((x) => x);
return (await cache.listFromTo(cacheKey, offset)).map((item) => deserializeFn(item));
},
clearCache: async () => {
await cache.delete(cacheKey);
}
};
}
//#endregion
//#region src/stream/smooth-stream.ts
const CHUNKING_PATTERNS = {
word: /\S+\s+/m,
line: /\n+/m
};
const wait = (delayInMs) => new Promise((resolve) => setTimeout(resolve, delayInMs));
function createChunkDetector(chunking) {
if (typeof chunking === "object" && "segment" in chunking && typeof chunking.segment === "function") return (buffer) => {
if (!buffer) return null;
return chunking.segment(buffer)[Symbol.iterator]().next().value?.segment || null;
};
if (typeof chunking === "function") return (buffer) => {
const match = chunking(buffer);
if (match == null) return null;
if (!match.length) throw new TypeError("The chunking function must return a non-empty string.");
if (!buffer.startsWith(match)) throw new TypeError("The chunking function must return a prefix of the buffered text.");
return match;
};
const pattern = typeof chunking === "string" ? CHUNKING_PATTERNS[chunking] : chunking;
if (!(pattern instanceof RegExp)) throw new TypeError("chunking must be \"word\", \"line\", a RegExp, an Intl.Segmenter, or a chunk detector function.");
return (buffer) => {
pattern.lastIndex = 0;
const match = pattern.exec(buffer);
if (!match) return null;
const detected = buffer.slice(0, match.index) + match[0];
if (!detected.length) throw new TypeError("The chunking RegExp must match a non-empty string.");
return detected;
};
}
/**
* Creates a transform stream that buffers text and reasoning deltas and emits
* them in consistent, delayed chunks. Other stream parts pass through without
* modification after any buffered content has been emitted.
*
* @experimental This API may change in a future release.
*/
function smoothStream({ delayInMs = 10, chunking = "word" } = {}) {
const detectChunk = createChunkDetector(chunking);
let buffer = "";
let bufferedChunk;
let bufferedMetadata;
let bufferedProviderMetadata;
const enqueueBufferedText = (controller, text) => {
if (!bufferedChunk || !text && !(bufferedMetadata !== void 0 || bufferedProviderMetadata !== void 0)) return;
const { metadata: _metadata, ...chunkWithoutMetadata } = bufferedChunk;
const { providerMetadata: _providerMetadata, ...payloadWithoutProviderMetadata } = bufferedChunk.payload;
controller.enqueue({
...chunkWithoutMetadata,
...bufferedMetadata !== void 0 ? { metadata: bufferedMetadata } : {},
payload: {
...payloadWithoutProviderMetadata,
...bufferedProviderMetadata !== void 0 ? { providerMetadata: bufferedProviderMetadata } : {},
text
}
});
bufferedMetadata = void 0;
bufferedProviderMetadata = void 0;
};
const flushBuffer = (controller) => {
enqueueBufferedText(controller, buffer);
buffer = "";
bufferedChunk = void 0;
bufferedMetadata = void 0;
bufferedProviderMetadata = void 0;
};
return new TransformStream$1({
async transform(chunk, controller) {
if (chunk.type !== "text-delta" && chunk.type !== "reasoning-delta") {
flushBuffer(controller);
controller.enqueue(chunk);
return;
}
if (bufferedChunk && (chunk.type !== bufferedChunk.type || chunk.payload.id !== bufferedChunk.payload.id)) flushBuffer(controller);
buffer += chunk.payload.text;
bufferedChunk = chunk;
bufferedMetadata = chunk.metadata ?? bufferedMetadata;
bufferedProviderMetadata = chunk.payload.providerMetadata ?? bufferedProviderMetadata;
let match;
while ((match = detectChunk(buffer)) != null) {
enqueueBufferedText(controller, match);
buffer = buffer.slice(match.length);
if (delayInMs !== null) await wait(delayInMs);
}
},
flush(controller) {
flushBuffer(controller);
}
});
}
//#endregion
export { convertFullStreamChunkToMastra as a, WorkflowRunOutput as c, withStreamCaching as i, convertFullStreamChunkToUIMessageStream as l, createCachingTransformStream as n, convertMastraChunkToAISDKv5 as o, createReplayStream as r, MastraAgentNetworkStream as s, smoothStream as t };
//# sourceMappingURL=stream-CRtv-q6z.js.map