@mastra/core
Version:
1,264 lines (1,263 loc) • 1.38 MB
JavaScript
const require_rolldown_runtime = require("./rolldown-runtime-uwYp4b74.cjs");
const require_logger = require("./logger-BPclhj7J.cjs");
const require_base = require("./base-B6soWsYg.cjs");
const require_error = require("./error-B-e62x-A.cjs");
const require_event_emitter = require("./event-emitter-80LrFIBS.cjs");
const require_observability = require("./observability-BzV5axz0.cjs");
const require_utils = require("./utils-CNiGU0Uf.cjs");
require("./tracing-BUrUJwCM.cjs");
const require_request_context = require("./request-context-ByoZMp-j.cjs");
const require_llm = require("./llm-CmflaXHA.cjs");
const require_tool = require("./tool-d85xHVkl.cjs");
const require_ee_DXvSoTl7 = require("./ee-DXvSoTl7-C7miIhtq.cjs");
const require_background_tasks = require("./background-tasks-lifNqs9M.cjs");
const require_toolchecks = require("./toolchecks-Rfz17G5p.cjs");
const require_types = require("./types-CepZ83u8.cjs");
const require_utils$1 = require("./utils-Bw7FoAI3.cjs");
const require_deep_equal = require("./deep-equal-BvQBG8wE.cjs");
const require_utils_safe_stringify = require("./utils/safe-stringify.cjs");
const require_trip_wire = require("./trip-wire-dxd_uCHj.cjs");
const require_payload_transform = require("./payload-transform-DJzDdfpE.cjs");
const require_signals = require("./signals-D2CulJo3.cjs");
const require_message_list = require("./message-list-BM7m-E-v.cjs");
const require_dist = require("./dist-BGdEcgoh.cjs");
const require_stream = require("./stream-CGwJ76we.cjs");
const require_types$1 = require("./types-ftn4_F2h.cjs");
const require_provider_registry = require("./provider-registry-Bv8eMxuW.cjs");
const require_model = require("./model-PhgPlg24.cjs");
const require_signal_provider = require("./signal-provider-xZOwL9Eb.cjs");
const require_cron = require("./cron-CrmzJKFJ.cjs");
const require_task_state_processor = require("./task-state-processor-BB7omHO3.cjs");
const require_constants = require("./constants-CHm1eNBE.cjs");
const require_logger_index = require("./logger/index.cjs");
const require_workflows_constants = require("./workflows/constants.cjs");
const require_workflow_event_processor = require("./workflow-event-processor-CkjVcesJ.cjs");
const require_hooks = require("./hooks-JAto1d0E.cjs");
const require_workspace = require("./workspace-SsEU6Si6.cjs");
const require_tools = require("./tools-B-JXNZhs.cjs");
const require_storage = require("./storage-DBpVHkrA.cjs");
const require_storage$1 = require("./storage-C4FD5U8Z.cjs");
const require_output_helpers = require("./output-helpers-BxsqfJ0U.cjs");
const require_agent_skills_resolver = require("./agent-skills-resolver-BfwRVTvX.cjs");
const require_voice = require("./voice-BxFaQWuy.cjs");
let crypto$1 = require("crypto");
crypto$1 = require_rolldown_runtime.__toESM(crypto$1, 1);
let zod_v4 = require("zod/v4");
let _mastra_schema_compat = require("@mastra/schema-compat");
let stream_web = require("stream/web");
let zod = require("zod");
let _ai_sdk_provider_utils_v5 = require("@ai-sdk/provider-utils-v5");
let fs = require("fs");
let path = require("path");
let fastq = require("fastq");
fastq = require_rolldown_runtime.__toESM(fastq, 1);
let tokenx = require("tokenx");
let xxhash_wasm = require("xxhash-wasm");
xxhash_wasm = require_rolldown_runtime.__toESM(xxhash_wasm, 1);
let lru_cache = require("lru-cache");
let _mastra_schema_compat_schema = require("@mastra/schema-compat/schema");
//#region src/processors/processors/unicode-normalizer.ts
var UnicodeNormalizer = class {
id = "unicode-normalizer";
name = "Unicode Normalizer";
options;
constructor(options = {}) {
this.options = {
stripControlChars: options.stripControlChars ?? false,
preserveEmojis: options.preserveEmojis ?? true,
collapseWhitespace: options.collapseWhitespace ?? true,
trim: options.trim ?? true
};
}
processInput(args) {
try {
return args.messages.map((message) => ({
...message,
content: {
...message.content,
parts: message.content.parts?.map((part) => {
if (part.type === "text" && "text" in part && typeof part.text === "string") return {
...part,
text: this.normalizeText(part.text)
};
return part;
}),
content: typeof message.content.content === "string" ? this.normalizeText(message.content.content) : message.content.content
}
}));
} catch {
return args.messages;
}
}
normalizeText(text) {
let normalized = text;
normalized = normalized.normalize("NFKC");
if (this.options.stripControlChars) if (this.options.preserveEmojis) normalized = normalized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, "");
else normalized = normalized.replace(/[^\x09\x0A\x0D\x20-\x7E\u00A0-\uFFFF]/g, "");
if (this.options.collapseWhitespace) {
normalized = normalized.replace(/\r\n/g, "\n");
normalized = normalized.replace(/\r/g, "\n");
normalized = normalized.replace(/\n+/g, "\n");
normalized = normalized.replace(/[ \t]+/g, " ");
}
if (this.options.trim) normalized = normalized.trim();
return normalized;
}
};
//#endregion
//#region src/stream/aisdk/v5/compat/prepare-tools.ts
/**
* Recursively fixes JSON Schema properties that lack a 'type' key.
* Zod v4's toJSONSchema serializes z.any() to just { description: "..." } with no 'type',
* which providers like OpenAI reject. This converts such schemas to a permissive type union.
*/
function fixTypelessProperties(schema) {
if (typeof schema !== "object" || schema === null) return schema;
const result = { ...schema };
if (result.properties && typeof result.properties === "object" && !Array.isArray(result.properties)) result.properties = Object.fromEntries(Object.entries(result.properties).map(([key, value]) => {
if (typeof value !== "object" || value === null || Array.isArray(value)) return [key, value];
const propSchema = value;
const hasType = "type" in propSchema;
const hasRef = "$ref" in propSchema;
const hasAnyOf = "anyOf" in propSchema;
const hasOneOf = "oneOf" in propSchema;
const hasAllOf = "allOf" in propSchema;
if (!hasType && !hasRef && !hasAnyOf && !hasOneOf && !hasAllOf) {
const { items: _items, ...rest } = propSchema;
return [key, {
...rest,
type: [
"string",
"number",
"integer",
"boolean",
"object",
"null"
]
}];
}
return [key, fixTypelessProperties(propSchema)];
}));
if (result.items) {
if (Array.isArray(result.items)) result.items = result.items.map((item) => fixTypelessProperties(item));
else if (typeof result.items === "object") result.items = fixTypelessProperties(result.items);
}
return result;
}
function prepareToolsAndToolChoice({ tools, toolChoice, activeTools, targetVersion = "v2" }) {
if (toolChoice === "none") return {
tools: void 0,
toolChoice: { type: "none" }
};
if (Object.keys(tools || {}).length === 0) return {
tools: void 0,
toolChoice: void 0
};
const filteredTools = activeTools != null ? Object.entries(tools || {}).filter(([name]) => activeTools.includes(name)) : Object.entries(tools || {});
const providerToolType = targetVersion === "v2" ? "provider-defined" : "provider";
return {
tools: filteredTools.map(([name, tool$1]) => {
try {
if (require_toolchecks.isProviderDefinedTool(tool$1)) {
const toolName = tool$1.name ?? name;
return {
type: providerToolType,
name: toolName,
id: tool$1.id,
args: tool$1.args ?? {}
};
}
let inputSchema;
if ("inputSchema" in tool$1) inputSchema = tool$1.inputSchema;
else if ("parameters" in tool$1) inputSchema = tool$1.parameters;
const sdkTool = require_dist.tool({
type: "function",
...tool$1,
inputSchema
});
const strict = "strict" in tool$1 ? tool$1.strict : void 0;
const toolType = sdkTool?.type ?? "function";
switch (toolType) {
case void 0:
case "dynamic":
case "function":
let parameters;
if (sdkTool.inputSchema) {
if ("$schema" in sdkTool.inputSchema && typeof sdkTool.inputSchema.$schema === "string" && sdkTool.inputSchema.$schema.startsWith("http://json-schema.org/")) parameters = sdkTool.inputSchema;
else if ((0, _mastra_schema_compat_schema.isStandardSchemaWithJSON)(sdkTool.inputSchema)) parameters = (0, _mastra_schema_compat_schema.standardSchemaToJSONSchema)(sdkTool.inputSchema, {
io: "input",
target: "draft-07"
});
else parameters = require_dist.asSchema(sdkTool.inputSchema).jsonSchema;
if (parameters && typeof parameters === "object" && "$schema" in parameters && parameters.$schema !== "http://json-schema.org/draft-07/schema#") parameters.$schema = "http://json-schema.org/draft-07/schema#";
} else parameters = {
type: "object",
properties: {},
additionalProperties: false
};
return {
type: "function",
name,
description: sdkTool.description,
inputSchema: fixTypelessProperties(parameters),
...strict != null ? { strict } : {},
providerOptions: sdkTool.providerOptions
};
case "provider-defined": {
const providerId = sdkTool.id;
const providerName = sdkTool.name ?? name;
return {
type: providerToolType,
name: providerName,
id: providerId,
args: sdkTool.args
};
}
default: throw new Error(`Unsupported tool type: ${toolType}`);
}
} catch (e) {
console.error("Error preparing tool", e);
return null;
}
}).filter((tool) => tool !== null),
toolChoice: toolChoice == null ? { type: "auto" } : typeof toolChoice === "string" ? { type: toolChoice } : {
type: "tool",
toolName: toolChoice.toolName
}
};
}
/**
* Serialize a tool set into `ModelToolDefinition[]` for the `tools` attribute
* on MODEL_GENERATION spans, reusing the same conversion the provider request
* goes through so exporters see the schemas the model actually received.
*
* Never throws — tracing must not break model execution. Returns undefined
* when there are no tools or serialization fails.
*/
function getToolDefinitionsForTracing({ tools, toolChoice, activeTools }) {
try {
const { tools: prepared } = prepareToolsAndToolChoice({
tools,
toolChoice,
activeTools
});
if (!prepared?.length) return void 0;
return prepared.map((tool) => tool.type === "function" ? {
type: "function",
name: tool.name,
...tool.description !== void 0 ? { description: tool.description } : {},
parameters: tool.inputSchema
} : {
type: tool.type,
name: tool.name,
id: tool.id
});
} catch {
return;
}
}
//#endregion
//#region src/agent/types.ts
/**
* Type guard to check if an object is a DurableAgentLike wrapper.
*/
function isDurableAgentLike(obj) {
if (!obj) return false;
return typeof obj.id === "string" && typeof obj.name === "string" && "agent" in obj && obj.agent !== null && typeof obj.agent === "object" && typeof obj.agent.id === "string" && typeof obj.stream === "function" && typeof obj.recover === "function" && typeof obj.recoverActiveRuns === "function";
}
//#endregion
//#region src/agent/goal/activity.ts
const activeSegments = /* @__PURE__ */ new Map();
const checkpointedDurations = /* @__PURE__ */ new Map();
const writeQueues = /* @__PURE__ */ new WeakMap();
function objectiveScopeKey(agentId, threadId) {
return `${agentId}:${threadId}`;
}
function segmentKey(agentId, runId) {
return `${agentId}:${runId}`;
}
function normalizeDuration(value) {
return value !== void 0 && Number.isFinite(value) && value >= 0 ? value : 0;
}
function debugFailure(mastra, message, context) {
try {
mastra?.getLogger()?.debug(message, context);
} catch {}
}
function enqueueThreadWrite(store, threadId, write) {
let storeQueues = writeQueues.get(store);
if (!storeQueues) {
storeQueues = /* @__PURE__ */ new Map();
writeQueues.set(store, storeQueues);
}
const next = (storeQueues.get(threadId) ?? Promise.resolve()).catch(() => {}).then(write);
storeQueues.set(threadId, next);
return next.finally(() => {
if (storeQueues.get(threadId) === next) storeQueues.delete(threadId);
});
}
/** Begin an in-process active-pursuit segment for an active thread objective. */
async function beginGoalActivity({ mastra, agentId, threadId, runId, requestContext, now = Date.now }) {
if (!threadId) return;
const key = segmentKey(agentId, runId);
if (activeSegments.has(key)) return;
require_task_state_processor.clearCachedGoalObjective(requestContext);
let store;
let objective;
try {
store = await require_task_state_processor.resolveGoalStore(mastra);
objective = await require_task_state_processor.readObjective(store, threadId);
require_task_state_processor.cacheGoalObjective(requestContext, threadId, objective);
} catch (error) {
debugFailure(mastra, "Failed to begin goal activity tracking", {
error,
agentId,
threadId,
runId
});
return;
}
if (!store || objective?.status !== "active") return;
const objectiveId = objective.id ?? objective.objective;
checkpointedDurations.set(objectiveScopeKey(agentId, threadId), {
objectiveId,
durationMs: normalizeDuration(objective.activeDurationMs)
});
activeSegments.set(key, {
mastra,
agentId,
threadId,
objectiveId,
startedAt: now(),
store
});
}
/**
* Stop and durably checkpoint an active-pursuit segment. Calling this for an
* already-stopped run is a no-op.
*/
async function stopGoalActivity({ agentId, runId, now = Date.now }) {
const key = segmentKey(agentId, runId);
const segment = activeSegments.get(key);
if (!segment) return;
activeSegments.delete(key);
const stoppedAt = now();
const elapsedMs = Math.max(0, stoppedAt - segment.startedAt);
try {
await enqueueThreadWrite(segment.store, segment.threadId, async () => {
const objective = await require_task_state_processor.readObjective(segment.store, segment.threadId);
if (!objective || (objective.id ?? objective.objective) !== segment.objectiveId) return;
const activeDurationMs = normalizeDuration(objective.activeDurationMs) + elapsedMs;
const updated = {
...objective,
activeDurationMs,
updatedAt: Math.max(objective.updatedAt, stoppedAt)
};
await require_task_state_processor.writeObjective(segment.store, segment.threadId, updated);
checkpointedDurations.set(objectiveScopeKey(segment.agentId, segment.threadId), {
objectiveId: segment.objectiveId,
durationMs: activeDurationMs
});
});
} catch (error) {
debugFailure(segment.mastra, "Failed to persist goal activity duration", {
error,
agentId: segment.agentId,
threadId: segment.threadId,
runId
});
}
}
/** Read the persisted duration plus all live core-owned segments for display. */
function getGoalActivityDurationMs({ agentId, threadId, objectiveId, activeDurationMs, now = Date.now }) {
let durationMs = normalizeDuration(activeDurationMs);
if (!threadId || !objectiveId) return durationMs;
const checkpoint = checkpointedDurations.get(objectiveScopeKey(agentId, threadId));
if (checkpoint?.objectiveId === objectiveId) durationMs = Math.max(durationMs, checkpoint.durationMs);
for (const segment of activeSegments.values()) if (segment.agentId === agentId && segment.threadId === threadId && segment.objectiveId === objectiveId) durationMs += Math.max(0, now() - segment.startedAt);
return durationMs;
}
//#endregion
//#region src/agent/subagent.ts
function isAgentCompatible(input) {
if (typeof input !== "object" || input === null) return false;
const candidate = input;
return typeof candidate.id === "string" && candidate.id.length > 0 && typeof candidate.generate === "function" && typeof candidate.stream === "function" && typeof candidate.getDescription === "function" && typeof candidate.getModel === "function" && typeof candidate.hasOwnMemory === "function" && typeof candidate.__setMemory === "function" && typeof candidate.getMemory === "function" && typeof candidate.getInstructions === "function" && typeof candidate.resumeGenerate === "function" && typeof candidate.resumeStream === "function";
}
//#endregion
//#region src/processors/step-schema.ts
/**
* Text part in a message
*/
const TextPartSchema = zod_v4.z.object({
type: zod_v4.z.literal("text"),
text: zod_v4.z.string()
}).passthrough();
/**
* Image part in a message
*/
const ImagePartSchema = zod_v4.z.object({
type: zod_v4.z.literal("image"),
image: zod_v4.z.union([
zod_v4.z.string(),
zod_v4.z.instanceof(URL),
zod_v4.z.instanceof(Uint8Array)
]),
mimeType: zod_v4.z.string().optional()
}).passthrough();
/**
* File part in a message
*/
const FilePartSchema = zod_v4.z.object({
type: zod_v4.z.literal("file"),
data: zod_v4.z.union([
zod_v4.z.string(),
zod_v4.z.instanceof(URL),
zod_v4.z.instanceof(Uint8Array)
]),
mimeType: zod_v4.z.string()
}).passthrough();
/**
* Tool invocation part in a message (covers tool-call states)
*/
const ToolInvocationPartSchema = zod_v4.z.object({
type: zod_v4.z.literal("tool-invocation"),
toolInvocation: zod_v4.z.object({
toolCallId: zod_v4.z.string(),
toolName: zod_v4.z.string(),
args: zod_v4.z.unknown(),
state: zod_v4.z.enum([
"partial-call",
"call",
"result"
]),
result: zod_v4.z.unknown().optional()
})
}).passthrough();
/**
* Reasoning part in a message (for models that support reasoning)
*/
const ReasoningPartSchema = zod_v4.z.object({
type: zod_v4.z.literal("reasoning"),
reasoning: zod_v4.z.string(),
details: zod_v4.z.array(zod_v4.z.object({
type: zod_v4.z.enum(["text", "redacted"]),
text: zod_v4.z.string().optional(),
data: zod_v4.z.string().optional()
}))
}).passthrough();
/**
* Source part in a message (for citations/references)
*/
const SourcePartSchema = zod_v4.z.object({
type: zod_v4.z.literal("source"),
source: zod_v4.z.object({
sourceType: zod_v4.z.string(),
id: zod_v4.z.string(),
url: zod_v4.z.string().optional(),
title: zod_v4.z.string().optional()
})
}).passthrough();
/**
* Step start part (marks the beginning of a step in multi-step responses)
*/
const StepStartPartSchema = zod_v4.z.object({ type: zod_v4.z.literal("step-start") }).passthrough();
/**
* Custom data part (for data-* custom parts from AI SDK writer.custom())
* This uses a regex to match any type starting with "data-"
*/
const DataPartSchema = zod_v4.z.object({
type: zod_v4.z.string().refine((t) => t.startsWith("data-"), { message: "Type must start with \"data-\"" }),
id: zod_v4.z.string().optional(),
data: zod_v4.z.unknown().optional()
}).passthrough();
/**
* Union of all message part types.
* Uses passthrough to allow additional fields from the AI SDK.
* Note: We can't use discriminatedUnion here because DataPartSchema uses a regex pattern.
*/
const MessagePartSchema = zod_v4.z.union([
TextPartSchema,
ImagePartSchema,
FilePartSchema,
ToolInvocationPartSchema,
ReasoningPartSchema,
SourcePartSchema,
StepStartPartSchema,
DataPartSchema
]);
/**
* Message content structure (MastraMessageContentV2 format)
* This is a documentation-friendly schema with properly typed parts.
*/
const MessageContentSchema = zod_v4.z.object({
/** Format version - 2 corresponds to AI SDK v4 UIMessage format */
format: zod_v4.z.literal(2),
/** Array of message parts (text, images, tool calls, etc.) */
parts: zod_v4.z.array(MessagePartSchema),
/** Legacy content field for backwards compatibility */
content: zod_v4.z.string().optional(),
/** Additional metadata */
metadata: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional(),
/** Provider-specific metadata */
providerMetadata: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional()
});
/**
* Schema for message content in processor workflows.
* Uses the MessagePartSchema discriminated union for proper UI rendering.
*/
const ProcessorMessageContentSchema = zod_v4.z.object({
/** Format version - 2 corresponds to AI SDK v4 UIMessage format */
format: zod_v4.z.literal(2),
/** Array of message parts (text, images, tool calls, etc.) */
parts: zod_v4.z.array(MessagePartSchema),
/** Legacy content field for backwards compatibility */
content: zod_v4.z.string().optional(),
/** Additional metadata */
metadata: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional(),
/** Provider-specific metadata */
providerMetadata: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional()
}).passthrough();
/**
* Schema for a message in the processor workflow.
* This represents MastraDBMessage with properly typed fields for UI usage.
*
* Key fields:
* - id: string - Unique message identifier
* - role: 'user' | 'assistant' | 'system' - Message role
* - createdAt: Date - When the message was created
* - threadId?: string - Thread identifier for conversation grouping
* - resourceId?: string - Resource identifier
* - type?: string - Message type
* - content: Message content with parts array
*/
const ProcessorMessageSchema = zod_v4.z.object({
/** Unique message identifier */
id: zod_v4.z.string(),
/** Message role */
role: zod_v4.z.enum([
"user",
"assistant",
"system",
"tool",
"signal"
]),
/** When the message was created */
createdAt: zod_v4.z.coerce.date(),
/** Thread identifier for conversation grouping */
threadId: zod_v4.z.string().optional(),
/** Resource identifier */
resourceId: zod_v4.z.string().optional(),
/** Message type */
type: zod_v4.z.string().optional(),
/** Message content with parts */
content: ProcessorMessageContentSchema
}).passthrough();
/**
* MessageList instance for managing message sources.
* Required for processors that need to mutate the message list.
*/
const messageListSchema = zod_v4.z.custom().describe("MessageList instance for managing message sources");
/**
* The messages to be processed.
* Format is MastraDBMessage[] - use ProcessorMessage type for TypeScript.
*/
const messagesSchema = zod_v4.z.array(ProcessorMessageSchema);
/**
* Schema for system message content parts (CoreSystemMessage format)
* System messages can have text parts or experimental provider extensions
*/
const SystemMessageTextPartSchema = zod_v4.z.object({
type: zod_v4.z.literal("text"),
text: zod_v4.z.string()
}).passthrough();
zod_v4.z.object({
role: zod_v4.z.literal("system"),
content: zod_v4.z.union([zod_v4.z.string(), zod_v4.z.array(SystemMessageTextPartSchema)]),
/** Optional experimental provider-specific extensions */
experimental_providerMetadata: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional()
}).passthrough();
/**
* Schema for CoreMessage (any message type from AI SDK)
* This is a more permissive schema for runtime flexibility.
*/
const CoreMessageSchema = zod_v4.z.object({
role: zod_v4.z.enum([
"system",
"user",
"assistant",
"tool"
]),
content: zod_v4.z.unknown()
}).passthrough();
/**
* System messages for context.
* These are CoreMessage types from the AI SDK, typically system messages
* but may include other message types in some contexts.
*/
const systemMessagesSchema = zod_v4.z.array(CoreMessageSchema);
/**
* Tool call schema for processOutputStep
*/
const toolCallSchema = zod_v4.z.object({
toolName: zod_v4.z.string(),
toolCallId: zod_v4.z.string(),
args: zod_v4.z.unknown()
});
/**
* Number of times processors have triggered retry for this generation.
*/
const retryCountSchema = zod_v4.z.number().optional();
/**
* Schema for 'input' phase - processInput
* Processes input messages before they are sent to the LLM (once at the start)
*/
const ProcessorInputPhaseSchema = zod_v4.z.object({
phase: zod_v4.z.literal("input"),
messages: messagesSchema,
messageList: messageListSchema,
systemMessages: systemMessagesSchema.optional(),
retryCount: retryCountSchema
});
/**
* Schema for 'inputStep' phase - processInputStep
* Processes input messages at each step of the agentic loop.
* Includes model/tools configuration that can be modified per-step.
*/
const ProcessorInputStepPhaseSchema = zod_v4.z.object({
phase: zod_v4.z.literal("inputStep"),
messages: messagesSchema,
messageList: messageListSchema,
stepNumber: zod_v4.z.number().describe("The current step number (0-indexed)"),
systemMessages: systemMessagesSchema.optional(),
retryCount: retryCountSchema,
messageId: zod_v4.z.string().optional().describe("The active assistant response message ID for this step"),
rotateResponseMessageId: zod_v4.z.custom().optional().describe("Rotate the active assistant response message ID when supported by the caller"),
model: zod_v4.z.custom().optional().describe("Current model for this step"),
tools: zod_v4.z.custom().optional().describe("Current tools available for this step"),
toolChoice: zod_v4.z.custom().optional().describe("Current tool choice setting"),
activeTools: zod_v4.z.array(zod_v4.z.string()).optional().describe("Currently active tools"),
providerOptions: zod_v4.z.custom().optional().describe("Provider-specific options"),
modelSettings: zod_v4.z.custom().optional().describe("Model settings (temperature, etc.)"),
structuredOutput: zod_v4.z.custom().optional().describe("Structured output configuration"),
steps: zod_v4.z.custom().optional().describe("Results from previous steps")
});
/**
* Schema for 'outputStream' phase - processOutputStream
* Processes output stream chunks with built-in state management
*/
const ProcessorOutputStreamPhaseSchema = zod_v4.z.object({
phase: zod_v4.z.literal("outputStream"),
part: zod_v4.z.unknown().nullable().describe("The current chunk being processed. Can be null to skip."),
streamParts: zod_v4.z.array(zod_v4.z.unknown()).describe("All chunks seen so far"),
state: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).describe("Mutable state object that persists across chunks"),
messageList: messageListSchema.optional(),
retryCount: retryCountSchema
});
/**
* Schema for 'outputResult' phase - processOutputResult
* Processes the complete output result after streaming/generate is finished
*/
const outputResultSchema = zod_v4.z.object({
text: zod_v4.z.string().describe("The accumulated text from all steps"),
usage: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).describe("Token usage (cumulative across all steps)"),
finishReason: zod_v4.z.string().describe("Why the generation finished"),
steps: zod_v4.z.array(zod_v4.z.unknown()).describe("All LLM step results")
});
const ProcessorOutputResultPhaseSchema = zod_v4.z.object({
phase: zod_v4.z.literal("outputResult"),
messages: messagesSchema,
messageList: messageListSchema,
retryCount: retryCountSchema,
result: outputResultSchema.optional()
});
/**
* Schema for 'outputStep' phase - processOutputStep
* Processes output after each LLM response in the agentic loop, before tool execution
*/
const ProcessorOutputStepPhaseSchema = zod_v4.z.object({
phase: zod_v4.z.literal("outputStep"),
messages: messagesSchema,
messageList: messageListSchema,
stepNumber: zod_v4.z.number().describe("The current step number (0-indexed)"),
finishReason: zod_v4.z.string().optional().describe("The finish reason from the LLM (stop, tool-use, length, etc.)"),
providerMetadata: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional().describe("Provider-specific metadata for the step (e.g. Bedrock guardrail trace under bedrock.trace.guardrail)"),
toolCalls: zod_v4.z.array(toolCallSchema).optional().describe("Tool calls made in this step (if any)"),
text: zod_v4.z.string().optional().describe("Generated text from this step"),
usage: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional().describe("Token usage for the current step (inputTokens, outputTokens, totalTokens, etc.)"),
systemMessages: systemMessagesSchema.optional(),
retryCount: retryCountSchema
});
/**
* Schema for 'toolResult' phase - processToolResult
* Processes a tool's result after tool.execute() returns successfully and
* before the result is added to the message list / fed to the next LLM call.
*/
const ProcessorToolResultPhaseSchema = zod_v4.z.object({
phase: zod_v4.z.literal("toolResult"),
messages: messagesSchema,
messageList: messageListSchema,
stepNumber: zod_v4.z.number().describe("The current step number (0-indexed)"),
toolName: zod_v4.z.string().describe("Name of the tool that was executed"),
toolCallId: zod_v4.z.string().describe("Unique identifier for this specific tool call"),
args: zod_v4.z.unknown().optional().describe("Arguments the LLM passed to the tool"),
result: zod_v4.z.unknown().optional().describe("Raw value returned by tool.execute() (already serialized)"),
providerExecuted: zod_v4.z.boolean().optional().describe("Whether this result came from a provider-executed tool (e.g. Anthropic web_search)"),
systemMessages: systemMessagesSchema.optional(),
steps: zod_v4.z.custom().optional().describe("Results from previous steps"),
retryCount: retryCountSchema
});
/**
* Discriminated union schema for processor step input in workflows.
*
* This schema uses a discriminated union based on the `phase` field,
* which determines what other fields are required/available.
* This makes it much clearer what data is needed for each phase
* and provides better UX in the playground UI.
*
* Phases:
* - 'input': Process input messages before LLM (once at start)
* - 'inputStep': Process input messages at each agentic loop step
* - 'outputStream': Process streaming chunks
* - 'outputResult': Process complete output after streaming
* - 'outputStep': Process output after each LLM response (before tools)
* - 'toolResult': Process a tool's result after tool.execute() (before next LLM call)
*/
const ProcessorStepInputSchema = zod_v4.z.discriminatedUnion("phase", [
ProcessorInputPhaseSchema,
ProcessorInputStepPhaseSchema,
ProcessorOutputStreamPhaseSchema,
ProcessorOutputResultPhaseSchema,
ProcessorOutputStepPhaseSchema,
ProcessorToolResultPhaseSchema
]);
/**
* Output schema for processor step data in workflows.
*
* This is a more flexible schema that allows all fields to be optional
* since the output from one phase may need to be passed to another.
* The workflow engine handles the type narrowing internally.
*/
const ProcessorStepOutputSchema = zod_v4.z.object({
phase: zod_v4.z.enum([
"input",
"inputStep",
"outputStream",
"outputResult",
"outputStep",
"toolResult"
]),
messages: messagesSchema.optional(),
messageList: messageListSchema.optional(),
systemMessages: systemMessagesSchema.optional(),
stepNumber: zod_v4.z.number().optional(),
part: zod_v4.z.unknown().nullable().optional(),
streamParts: zod_v4.z.array(zod_v4.z.unknown()).optional(),
state: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional(),
result: outputResultSchema.optional(),
finishReason: zod_v4.z.string().optional(),
toolCalls: zod_v4.z.array(toolCallSchema).optional(),
text: zod_v4.z.string().optional(),
usage: zod_v4.z.record(zod_v4.z.string(), zod_v4.z.unknown()).optional(),
toolName: zod_v4.z.string().optional(),
toolCallId: zod_v4.z.string().optional(),
args: zod_v4.z.unknown().optional(),
toolResultValue: zod_v4.z.unknown().optional(),
providerExecuted: zod_v4.z.boolean().optional(),
retryCount: zod_v4.z.number().optional(),
model: zod_v4.z.custom().optional(),
tools: zod_v4.z.custom().optional(),
toolChoice: zod_v4.z.custom().optional(),
activeTools: zod_v4.z.array(zod_v4.z.string()).optional(),
providerOptions: zod_v4.z.custom().optional(),
modelSettings: zod_v4.z.custom().optional(),
structuredOutput: zod_v4.z.custom().optional(),
steps: zod_v4.z.custom().optional(),
messageId: zod_v4.z.string().optional(),
rotateResponseMessageId: zod_v4.z.custom().optional()
});
/**
* Combined schema that works for both input and output.
* Uses the discriminated union for better type inference.
*/
const ProcessorStepSchema = ProcessorStepInputSchema;
//#endregion
//#region src/workflows/execution-engine.ts
/**
* Execution engine abstract class for building and executing workflow graphs
* Providers will implement this class to provide their own execution logic
*/
var ExecutionEngine = class extends require_base.MastraBase {
mastra;
options;
constructor({ mastra, options }) {
super({
name: "ExecutionEngine",
component: require_logger.RegisteredLogger.WORKFLOW
});
this.mastra = mastra;
this.options = options;
}
__registerMastra(mastra) {
this.mastra = mastra;
const logger = mastra?.getLogger();
if (logger) this.__setLogger(logger);
}
getLogger() {
return this.logger;
}
/**
* Invokes the onFinish and onError lifecycle callbacks if they are defined.
* Errors in callbacks are caught and logged, not propagated.
* @param result The workflow result containing status, result, error, steps, tripwire info, and context
*/
async invokeLifecycleCallbacks(result) {
const { onFinish, onError } = this.options;
const commonContext = {
runId: result.runId,
workflowId: result.workflowId,
resourceId: result.resourceId,
getInitData: () => result.input,
mastra: this.mastra,
requestContext: result.requestContext,
logger: this.logger,
state: result.state,
stepExecutionPath: result.stepExecutionPath
};
if (onFinish) try {
await Promise.resolve(onFinish({
status: result.status,
result: result.result,
error: result.error,
steps: result.steps,
tripwire: result.tripwire,
...commonContext
}));
} catch (err) {
this.logger.error("Error in onFinish callback", { error: err });
}
if (onError && (result.status === "failed" || result.status === "tripwire")) try {
await Promise.resolve(onError({
status: result.status,
error: result.error,
steps: result.steps,
tripwire: result.tripwire,
...commonContext
}));
} catch (err) {
this.logger.error("Error in onError callback", { error: err });
}
}
};
//#endregion
//#region src/workflows/handlers/control-flow.ts
/**
* Runs one child of a parallel/conditional block by dispatching on its step type
* to the matching engine execute method - the same per-type dispatch the engine
* uses for top-level entries.
*/
function executeChildEntry(engine, child, params) {
switch (child.type) {
case "step": return engine.executeStep({
...params,
step: child.step
});
case "agent": return engine.executeAgent({
...params,
entry: child
});
case "tool": return engine.executeTool({
...params,
entry: child
});
case "mapping": return engine.executeMapping({
...params,
entry: child
});
}
}
async function executeParallel(engine, params) {
const { workflowId, runId, resourceId, entry, prevStep, serializedStepGraph, stepResults, resume, restart, timeTravel, executionContext, pubsub, abortController, requestContext, actor, outputWriter, disableScorers, perStep, ...rest } = params;
const observabilityContext = require_observability.resolveObservabilityContext(rest);
const steps = entry.steps;
const parallelSpan = await engine.createChildSpan({
parentSpan: observabilityContext.tracingContext.currentSpan,
operationId: `workflow.${workflowId}.run.${runId}.parallel.${executionContext.executionPath.join("-")}.span.start`,
options: {
type: "workflow_parallel",
name: `parallel: '${steps.length} branches'`,
input: engine.getStepOutput(stepResults, prevStep),
attributes: {
branchCount: steps.length,
parallelSteps: steps.map((s) => require_workflow_event_processor.getSingleStepEntryId(s))
},
tracingPolicy: engine.options?.tracingPolicy
},
executionContext
});
const prevOutput = engine.getStepOutput(stepResults, prevStep);
for (const [stepIndex, step] of steps.entries()) {
const stepId = require_workflow_event_processor.getSingleStepEntryId(step);
let makeStepRunning = true;
if (restart) makeStepRunning = !!restart.activeStepsPath[stepId];
if (timeTravel && timeTravel.executionPath.length > 0) makeStepRunning = timeTravel.steps[0] === stepId;
if (!makeStepRunning) break;
const startTime = resume?.steps[0] === stepId ? void 0 : Date.now();
const resumeTime = resume?.steps[0] === stepId ? Date.now() : void 0;
stepResults[stepId] = {
...stepResults[stepId],
status: "running",
...resumeTime ? { resumePayload: resume?.resumePayload } : { payload: prevOutput },
...startTime ? { startedAt: startTime } : {},
...resumeTime ? { resumedAt: resumeTime } : {}
};
executionContext.activeStepsPath[stepId] = [...executionContext.executionPath, stepIndex];
if (perStep) break;
}
if (timeTravel && timeTravel.executionPath.length > 0) timeTravel.executionPath.shift();
let execResults;
const results = await Promise.all(steps.map(async (step, i) => {
const stepId = require_workflow_event_processor.getSingleStepEntryId(step);
const currStepResult = stepResults[stepId];
if (currStepResult && currStepResult.status !== "running") return currStepResult;
if (!currStepResult && (perStep || timeTravel)) return {};
const stepExecResult = await executeChildEntry(engine, step, {
workflowId,
runId,
resourceId,
prevOutput,
stepResults,
serializedStepGraph,
restart,
timeTravel,
resume,
executionContext: {
activeStepsPath: executionContext.activeStepsPath,
workflowId,
runId,
executionPath: [...executionContext.executionPath, i],
stepExecutionPath: executionContext.stepExecutionPath,
suspendedPaths: executionContext.suspendedPaths,
resumeLabels: executionContext.resumeLabels,
retryConfig: executionContext.retryConfig,
state: executionContext.state,
tracingIds: executionContext.tracingIds
},
...require_observability.createObservabilityContext({ currentSpan: parallelSpan }),
pubsub,
abortController,
requestContext,
actor,
outputWriter,
disableScorers,
perStep
});
engine.applyMutableContext(executionContext, stepExecResult.mutableContext);
Object.assign(stepResults, stepExecResult.stepResults);
return stepExecResult.result;
}));
const hasFailed = results.find((result) => result.status === "failed");
const hasSuspended = results.find((result) => result.status === "suspended");
if (hasFailed) execResults = {
status: "failed",
error: hasFailed.error,
tripwire: hasFailed.tripwire
};
else if (hasSuspended) execResults = {
status: "suspended",
suspendPayload: hasSuspended.suspendPayload,
...hasSuspended.suspendOutput ? { suspendOutput: hasSuspended.suspendOutput } : {}
};
else if (abortController?.signal?.aborted) execResults = { status: "canceled" };
else execResults = {
status: "success",
output: results.reduce((acc, result, index) => {
if (result.status === "success") acc[require_workflow_event_processor.getSingleStepEntryId(steps[index])] = result.output;
return acc;
}, {})
};
if (execResults.status === "failed") await engine.errorChildSpan({
span: parallelSpan,
operationId: `workflow.${workflowId}.run.${runId}.parallel.${executionContext.executionPath.join("-")}.span.error`,
errorOptions: { error: execResults.error }
});
else await engine.endChildSpan({
span: parallelSpan,
operationId: `workflow.${workflowId}.run.${runId}.parallel.${executionContext.executionPath.join("-")}.span.end`,
endOptions: { output: execResults.output || execResults }
});
return execResults;
}
async function executeConditional(engine, params) {
const { workflowId, runId, resourceId, entry, prevOutput, serializedStepGraph, stepResults, resume, restart, timeTravel, executionContext, pubsub, abortController, requestContext, actor, outputWriter, disableScorers, perStep, ...rest } = params;
const observabilityContext = require_observability.resolveObservabilityContext(rest);
const steps = entry.steps;
const conditionalSpan = await engine.createChildSpan({
parentSpan: observabilityContext.tracingContext.currentSpan,
operationId: `workflow.${workflowId}.run.${runId}.conditional.${executionContext.executionPath.join("-")}.span.start`,
options: {
type: "workflow_conditional",
name: `conditional: '${entry.conditions.length} conditions'`,
input: prevOutput,
attributes: { conditionCount: entry.conditions.length },
tracingPolicy: engine.options?.tracingPolicy
},
executionContext
});
let execResults;
const truthyIndexes = (await Promise.all(entry.conditions.map(async (cond, index) => {
const evalSpan = await engine.createChildSpan({
parentSpan: conditionalSpan,
operationId: `workflow.${workflowId}.run.${runId}.conditional.${executionContext.executionPath.join("-")}.eval.${index}.span.start`,
options: {
type: "workflow_conditional_eval",
name: `condition '${index}'`,
input: prevOutput,
attributes: { conditionIndex: index },
tracingPolicy: engine.options?.tracingPolicy
},
executionContext
});
const operationId = `workflow.${workflowId}.conditional.${index}`;
const context = require_workflow_event_processor.createDeprecationProxy({
runId,
workflowId,
mastra: engine.mastra,
requestContext,
actor,
inputData: prevOutput,
state: executionContext.state,
retryCount: -1,
...require_observability.createObservabilityContext({ currentSpan: evalSpan }),
getInitData: () => stepResults?.input,
getStepResult: require_workflow_event_processor.getStepResult.bind(null, stepResults),
bail: (() => {}),
abort: () => {
abortController?.abort();
},
[require_workflows_constants.PUBSUB_SYMBOL]: pubsub,
[require_workflows_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
engine: engine.getEngineContext(),
abortSignal: abortController?.signal,
writer: new require_types.ToolStream({
prefix: "workflow-step",
callId: (0, crypto$1.randomUUID)(),
name: "conditional",
runId
}, outputWriter)
}, {
paramName: "runCount",
deprecationMessage: require_workflow_event_processor.runCountDeprecationMessage,
logger: engine.getLogger()
});
try {
const result = await engine.evaluateCondition(cond, index, context, operationId);
await engine.endChildSpan({
span: evalSpan,
operationId: `workflow.${workflowId}.run.${runId}.conditional.${executionContext.executionPath.join("-")}.eval.${index}.span.end`,
endOptions: {
output: result !== null,
attributes: { result: result !== null }
}
});
return result;
} catch (e) {
const errorInstance = require_error.getErrorFromUnknown(e, { serializeStack: false });
const mastraError = new require_error.MastraError({
id: "WORKFLOW_CONDITION_EVALUATION_FAILED",
domain: require_error.ErrorDomain.MASTRA_WORKFLOW,
category: require_error.ErrorCategory.USER,
details: {
workflowId,
runId
}
}, errorInstance);
engine.getLogger()?.trackException(mastraError);
engine.getLogger()?.error("Error evaluating condition: " + errorInstance.stack);
await engine.errorChildSpan({
span: evalSpan,
operationId: `workflow.${workflowId}.run.${runId}.conditional.${executionContext.executionPath.join("-")}.eval.${index}.span.error`,
errorOptions: {
error: mastraError,
attributes: { result: false }
}
});
return null;
}
}))).filter((index) => index !== null);
let stepsToRun = steps.filter((_, index) => truthyIndexes.includes(index));
if (perStep || timeTravel && timeTravel.executionPath.length > 0) {
const possibleStepToRun = stepsToRun.filter((s) => {
const currStepResult = stepResults[require_workflow_event_processor.getSingleStepEntryId(s)];
if (timeTravel && timeTravel.executionPath.length > 0) return timeTravel.steps[0] === require_workflow_event_processor.getSingleStepEntryId(s);
return !currStepResult;
})?.[0];
stepsToRun = possibleStepToRun ? [possibleStepToRun] : stepsToRun;
}
if (timeTravel && timeTravel.executionPath.length > 0) entry.steps.forEach((armEntry, index) => {
if (truthyIndexes.includes(index)) return;
const armId = require_workflow_event_processor.getSingleStepEntryId(armEntry);
const existing = stepResults[armId];
if (existing?.status !== "running") return;
stepResults[armId] = {
status: "skipped",
payload: existing.payload ?? {},
startedAt: existing.startedAt ?? Date.now(),
endedAt: Date.now()
};
});
conditionalSpan?.update({ attributes: {
truthyIndexes,
selectedSteps: stepsToRun.map((s) => require_workflow_event_processor.getSingleStepEntryId(s))
} });
const results = await Promise.all(stepsToRun.map(async (step) => {
const stepId = require_workflow_event_processor.getSingleStepEntryId(step);
const currStepResult = stepResults[stepId];
const isRestartStep = restart ? !!restart.activeStepsPath[stepId] : void 0;
if (currStepResult && timeTravel && timeTravel.executionPath.length > 0) {
if (timeTravel.steps[0] !== stepId) return currStepResult;
}
if (currStepResult && ["success", "failed"].includes(currStepResult.status) && isRestartStep === void 0) return currStepResult;
const stepExecResult = await executeChildEntry(engine, step, {
workflowId,
runId,
resourceId,
prevOutput,
stepResults,
serializedStepGraph,
resume,
restart,
timeTravel,
executionContext: {
workflowId,
runId,
executionPath: [...executionContext.executionPath, steps.indexOf(step)],
stepExecutionPath: executionContext.stepExecutionPath,
activeStepsPath: executionContext.activeStepsPath,
suspendedPaths: executionContext.suspendedPaths,
resumeLabels: executionContext.resumeLabels,
retryConfig: executionContext.retryConfig,
state: executionContext.state,
tracingIds: executionContext.tracingIds
},
...require_observability.createObservabilityContext({ currentSpan: conditionalSpan }),
pubsub,
abortController,
requestContext,
actor,
outputWriter,
disableScorers,
perStep
});
engine.applyMutableContext(executionContext, stepExecResult.mutableContext);
Object.assign(stepResults, stepExecResult.stepResults);
return stepExecResult.result;
}));
const hasFailed = results.find((result) => result.status === "failed");
const hasSuspended = results.find((result) => result.status === "suspended");
if (hasFailed) execResults = {
status: "failed",
error: hasFailed.error,
tripwire: hasFailed.tripwire
};
else if (hasSuspended) execResults = {
status: "suspended",
suspendPayload: hasSuspended.suspendPayload,
...hasSuspended.suspendOutput ? { suspendOutput: hasSuspended.suspendOutput } : {},
suspendedAt: hasSuspended.suspendedAt
};
else if (abortController?.signal?.aborted) execResults = { status: "canceled" };
else execResults = {
status: "success",
output: results.reduce((acc, result, index) => {
if (result.status === "success") acc[require_workflow_event_processor.getSingleStepEntryId(stepsToRun[index])] = result.output;
return acc;
}, {})
};
if (execResults.status === "failed") await engine.errorChildSpan({
span: conditionalSpan,
operationId: `workflow.${workflowId}.run.${runId}.conditional.${executionContext.executionPath.join("-")}.span.error`,
errorOptions: { error: execResults.error }
});
else await engine.endChildSpan({
span: conditionalSpan,
operationId: `workflow.${workflowId}.run.${runId}.conditional.${executionContext.executionPath.join("-")}.span.end`,
endOptions: { output: execResults.output || execResults }
});
return execResults;
}
async function executeLoop(engine, params) {
const { workflowId, runId, resourceId, entry, prevOutput, stepResults, resume, restart, timeTravel, executionContext, pubsub, abortController, requestContext, actor, outputWriter, disableScorers, serializedStepGraph, perStep, ...rest } = params;
const observabilityContext = require_observability.resolveObservabilityContext(rest);
const { step, condition } = entry;
const stepId = require_workflow_event_processor.getEntryId(step);
const loopSpan = await engine.createChildSpan({
parentSpan: observabilityContext.tracingContext.currentSpan,
operationId: `workflow.${workflowId}.run.${runId}.loop.${executionContext.executionPath.join("-")}.span.start`,
options: {
type: "workflow_loop",
name: `loop: '${entry.loopType}'`,
input: prevOutput,
attributes: { loopType: entry.loopType },
tracingPolicy: engine.options?.tracingPolicy
},
executionContext
});
let isTrue = true;
const prevIterationCount = stepResults[stepId]?.metadata?.iterationCount;
let iteration = prevIterationCount ? prevIterationCount - 1 : 0;
const prevStepResult = stepResults[stepId];
let result = {
status: "success",
output: prevStepResult && Object.prototype.hasOwnProperty.call(prevStepResult, "payload") ? prevStepResult.payload : prevOutput
};
let currentResume = resume;
let currentRestart = restart;
let currentTimeTravel = timeTravel;
do {
if (abortController?.signal?.aborted) {
await engine.endChildSpan({
span: loopSpan,
operationId: `workflow.${workflowId}.run.${runId}.loop.${executionContext.executionPath.join("-")}.span.end.early`,
endOptions: { attributes: { totalIterations: iteration } }
});
return { status: "canceled" };
}
const stepExecResult = await executeChildEntry(engine, step, {
workflowId,
runId,
resourceId,
stepResults,
executionContext,
restart: currentRestart,
resume: currentResume,
timeTravel: currentTimeTravel,
prevOutput: result.output,
...require_observability.createObservabilityContext({ currentSpan: loopSpan }),
pubsub,
abortController,
requestContext,
actor,
outputWriter,
disableScorers,
serializedStepGraph,
iterationCount: iteration + 1,
perStep
});
engine.applyMutableContext(executionContext, stepExecResult.mutableContext);
Object.assign(stepResults, stepExecResult.stepResults);
result = stepExecResult.result;
currentRestart = void 0;
currentTimeTravel = void 0;
if (currentResume && result.status !== "suspended") currentResume = void 0;
if (result.status !== "success") {
await engine.endChildSpan({
span: loopSpan,
operationId: `workflow.${workflowId}.run.${runId}.loop.${executionContext.executionPath.join("-")}.span.end.early`,
endOptions: { attributes: { totalIterations: iteration } }
});
return result;
}
if (abortController?.signal?.aborted) {
await engine.endChildSpan({
span: loopSpan,