@mastra/core
Version:
284 lines (283 loc) • 13.9 kB
JavaScript
import { standardSchemaToJSONSchema, toStandardSchema } from "../schema/index.js";
import { $t as createWorkflow, Qt as createEventedWorkflow, Sn as ExecutionEngine, Zt as cloneWorkflow, _n as DefaultExecutionEngine, bn as createStepFromTool, cn as Workflow, dn as isProcessor, fn as mapVariable, gn as predicateSchema, hn as evaluatePredicate, ln as cloneStep, mn as derivePredicateLabel, pn as predicateToCondition, sn as Run, un as createStep, vn as createMappingStep, yn as createStepFromAgent } from "../agent-Dj30gJa3.js";
import { n as validateCron, t as computeNextFireAt } from "../cron-B2j814dd.js";
import { A as getEntryId, C as runCountDeprecationMessage, D as validateStepStateData, E as validateStepResumeData, M as getStepResult, O as validateStepSuspendData, S as resolveForeachConcurrency, T as validateStepRequestContext, _ as getResumeLabelsByStepId, b as hydrateSerializedStepErrors, g as createTimeTravelExecutionParams, h as createRestartExecutionParams, j as getEntryWorkflow, k as waitForSuspendedSnapshot, m as createDeprecationProxy, p as cleanStepResult, v as getSingleStepEntryId, w as validateStepInput, x as isSingleStepEntry, y as getStepIds } from "../workflow-event-processor-BbED1LMn.js";
import { n as WorkflowScheduler, t as Scheduler } from "../scheduler-CY8m1fr_.js";
import { a as inferGraphSchemas, c as forEachSingleStepEntry, d as analyzeMapConfig, f as parseMapConfig, g as validateStorableJsonSchema, h as jsonSchemaToZod, i as validateWorkflowSchemas, l as forEachSingleStepEntryWithPath, m as toJsonSchemaOrUndefined, n as validateStoredWorkflow, o as validateWorkflowRefs, p as schemaCompatibility, r as validateWorkflowStructure, s as collectNestedWorkflowIds, t as assertValidStoredWorkflow, u as rehydrateWorkflow } from "../validate-BWdo4oEz.js";
//#region src/workflows/stored/serialize.ts
/**
* Live → Storable half of the workflow round-trip: walk a live `stepFlow`
* (runtime references, closures) and emit the JSON-safe storable form
* (ids + serialized mapping configs, no closures).
*
* The static subset that round-trips:
* - agent / tool by id
* - mapping with `value`, `step`, `initData`, `requestContextPath`, `template`,
* `state` sources (no `fn` source — closures don't round-trip)
* - sleep / sleepUntil with literal duration/date
* - parallel (inner entries must themselves be static)
* - foreach with literal concurrency
* - conditional / loop with declarative predicates (closure predicates throw)
* - generic `.then(step)` falls back to a minimal step descriptor — usable
* only when the step's id resolves on the live Mastra at load time
*
* Anything outside the subset throws at `toStorableGraph` time: silent loss
* would ship broken workflows unnoticed.
*/
/**
* Walk a live `stepFlow` and emit a JSON-safe `SerializedStepFlowEntry[]` with
* full (un-truncated) mapping configs and all step/agent/tool references stored
* as ids. Throws on entries that can't round-trip (closures, closure predicates).
*/
function toStorableGraph(stepFlow) {
return stepFlow.map((entry) => serializeEntry(entry));
}
function serializeEntry(entry) {
switch (entry.type) {
case "step":
case "agent":
case "tool":
case "mapping": return serializeSingleEntry(entry);
case "sleep":
if (typeof entry.duration !== "number") throw new Error(`Sleep step "${entry.id}" cannot be stored: dynamic duration (function) is not supported.`);
return {
type: "sleep",
id: entry.id,
duration: entry.duration
};
case "sleepUntil":
if (!(entry.date instanceof Date)) throw new Error(`SleepUntil step "${entry.id}" cannot be stored: dynamic date (function) is not supported.`);
return {
type: "sleepUntil",
id: entry.id,
date: entry.date
};
case "parallel": return {
type: "parallel",
steps: entry.steps.map((s) => serializeSingleEntry(s))
};
case "foreach":
if (entry.step.type === "mapping") throw new Error(`Foreach step cannot iterate a mapping: mappings project data, they don't execute per item. Use an agent, tool, or plain step as the foreach body.`);
return {
type: "foreach",
step: serializeSingleEntry(entry.step),
opts: typeof entry.opts.concurrency === "function" ? { fn: entry.opts.concurrency.toString() } : { concurrency: entry.opts.concurrency }
};
case "conditional": {
const predicates = entry.predicates;
if (!predicates || predicates.some((p) => !p || typeof p !== "object")) throw new Error(`Conditional (branch) step cannot be stored: closure predicates do not round-trip. Use the declarative form ({ predicate: {...} }) for each branch.`);
return {
type: "conditional",
steps: entry.steps.map((s) => serializeSingleEntry(s)),
serializedConditions: entry.serializedConditions,
predicates
};
}
case "loop": {
const predicate = entry.predicate;
if (!predicate || typeof predicate !== "object") throw new Error(`Loop step "${getSingleStepEntryId(entry.step)}" cannot be stored: closure predicates do not round-trip. Use the declarative form ({ predicate: {...} }).`);
return {
type: "loop",
step: serializeSingleEntry(entry.step),
serializedCondition: entry.serializedCondition,
loopType: entry.loopType,
predicate
};
}
default: throw new Error(`Unknown step entry type: ${JSON.stringify(entry)}`);
}
}
function serializeSingleEntry(entry) {
if (entry.type === "agent") {
const options = pickSerializableStepOptions(entry.options, entry.id, "agent");
const outputSchema = extractStructuredOutputJsonSchema(entry.options, entry.id);
return {
type: "agent",
id: entry.id,
agentId: entry.agentId,
description: entry.agent?.description,
...outputSchema ? { outputSchema } : {},
...options ? { options } : {}
};
}
if (entry.type === "tool") {
const options = pickSerializableStepOptions(entry.options, entry.id, "tool");
return {
type: "tool",
id: entry.id,
toolId: entry.toolId,
description: entry.tool?.description,
...options ? { options } : {}
};
}
if (entry.type === "mapping") {
if (typeof entry.mapConfig === "function") throw new Error(`Mapping step "${entry.id}" cannot be stored: the function form does not round-trip. Use the declarative form (template / step / initData / value).`);
const serialized = {};
for (const [key, mapping] of Object.entries(entry.mapConfig)) {
const m = mapping;
if (m.fn !== void 0) throw new Error(`Mapping step "${entry.id}" key "${key}" cannot be stored: source is a function.`);
if (m.value !== void 0) serialized[key] = { value: m.value };
else if (m.requestContextPath) serialized[key] = { requestContextPath: m.requestContextPath };
else if (typeof m.template === "string") serialized[key] = { template: m.template };
else if (m.initData) serialized[key] = {
initData: m.initData?.id,
path: m.path
};
else if (m.step) serialized[key] = {
step: Array.isArray(m.step) ? m.step.map((s) => s?.id) : m.step?.id,
path: m.path
};
else serialized[key] = m;
}
return {
type: "mapping",
id: entry.id,
mapConfig: JSON.stringify(serialized)
};
}
if (entry.step?.component === "WORKFLOW") {
const nestedFlow = entry.step.serializedStepGraph ?? entry.step.serializedStepFlow;
return {
type: "workflow",
id: entry.step.id,
workflowId: entry.step.id,
...entry.step.description ? { description: entry.step.description } : {},
...nestedFlow ? { serializedStepFlow: nestedFlow } : {}
};
}
return {
type: "step",
step: stepDescriptor(entry.step)
};
}
function stepDescriptor(step) {
return {
id: step.id,
description: step.description,
metadata: step.metadata,
component: step.component,
canSuspend: Boolean(step.suspendSchema || step.resumeSchema)
};
}
/**
* Pull the JSON-safe fields (`retries`, `metadata`) out of the options bag
* carried on a live agent/tool `SingleStepEntry`. Closure-valued fields must
* hard-crash here rather than silently vanish through storage.
*/
function pickSerializableStepOptions(options, entryId, kind) {
if (!options || typeof options !== "object") return void 0;
for (const { key, hint } of [
{
key: "onFinish",
hint: "callback closure"
},
{
key: "onChunk",
hint: "callback closure"
},
{
key: "onError",
hint: "callback closure"
},
{
key: "onStepFinish",
hint: "callback closure"
},
{
key: "onAbort",
hint: "callback closure"
},
{
key: "toolChoice",
hint: "may be a function"
}
]) if (typeof options[key] === "function") throw new Error(`${kind === "agent" ? "Agent" : "Tool"} step "${entryId}" cannot be stored: option "${key}" is a ${hint} that does not round-trip. Remove it or move that logic outside the persisted workflow.`);
if (typeof options.scorers === "function") throw new Error(`${kind === "agent" ? "Agent" : "Tool"} step "${entryId}" cannot be stored: "scorers" is a function; only the static array form round-trips.`);
const out = {};
if (typeof options.retries === "number") out.retries = options.retries;
if (options.metadata && typeof options.metadata === "object") out.metadata = options.metadata;
return Object.keys(out).length > 0 ? out : void 0;
}
/**
* If the agent-step options carry `structuredOutput.schema`, that schema IS
* the step's output shape (see `createStepFromAgent`). Emit it as JSON Schema
* so rehydration can wire the same structured output back in.
*/
function extractStructuredOutputJsonSchema(options, entryId) {
const raw = options?.structuredOutput?.schema;
if (raw === void 0 || raw === null) return void 0;
try {
return standardSchemaToJSONSchema(toStandardSchema(raw));
} catch (e) {
throw new Error(`Agent step "${entryId}" cannot be stored: structuredOutput.schema is not convertible to JSON Schema (${e.message}).`);
}
}
//#endregion
//#region src/workflows/state-reader.ts
const getStep = (state, stepId) => state.steps?.[stepId];
const getFirstStepResult = (step) => {
return Array.isArray(step) ? step.find((result) => result?.status === "suspended") ?? step[0] : step;
};
const getNestedSuspendPath = (step) => {
const path = getFirstStepResult(step)?.suspendPayload?.__workflow_meta?.path;
return Array.isArray(path) ? path.filter((part) => typeof part === "string") : [];
};
function getWorkflowStepOutput(state, stepId) {
const step = getStep(state, stepId);
return Array.isArray(step) ? step.map((result) => result?.output) : step?.output;
}
function getWorkflowStepPayload(state, stepId) {
const step = getStep(state, stepId);
return Array.isArray(step) ? step.map((result) => result?.payload) : step?.payload;
}
function getWorkflowResumeLabel(state, label) {
const resumeLabel = state.resumeLabels?.[label];
return resumeLabel ? { ...resumeLabel } : void 0;
}
function getWorkflowResumeLabels(state) {
return Object.entries(state.resumeLabels ?? {}).reduce((labels, [label, value]) => {
labels[label] = { ...value };
return labels;
}, {});
}
function getWorkflowSuspendedSteps(state) {
return Object.entries(state.suspendedPaths ?? {}).map(([stepId, executionPath]) => {
const step = getStep(state, stepId);
const firstStepResult = getFirstStepResult(step);
const nestedPath = getNestedSuspendPath(step);
const path = nestedPath.length > 0 ? nestedPath[0] === stepId ? nestedPath : [stepId, ...nestedPath] : [stepId];
const resumeLabels = Object.entries(state.resumeLabels ?? {}).reduce((labels, [label, value]) => {
if (value.stepId === stepId) labels[label] = { ...value };
return labels;
}, {});
return {
stepId,
path,
executionPath,
step,
payload: Array.isArray(step) ? step.map((result) => result?.payload) : step?.payload,
suspendPayload: firstStepResult?.suspendPayload,
suspendOutput: firstStepResult?.suspendOutput,
resumeLabels
};
});
}
function getWorkflowSuspendedStep(state) {
return getWorkflowSuspendedSteps(state)[0];
}
function createWorkflowStateReader(state) {
return {
getStatus: () => state.status,
getResult: () => state.result,
getError: () => state.error,
getStepOutput: (stepId) => getWorkflowStepOutput(state, stepId),
getStepPayload: (stepId) => getWorkflowStepPayload(state, stepId),
getSuspendedStep: () => getWorkflowSuspendedStep(state),
getSuspendedSteps: () => getWorkflowSuspendedSteps(state),
getResumeLabel: (label) => getWorkflowResumeLabel(state, label),
getResumeLabels: () => getWorkflowResumeLabels(state)
};
}
//#endregion
export { DefaultExecutionEngine, ExecutionEngine, Run, Scheduler, Workflow, WorkflowScheduler, analyzeMapConfig, assertValidStoredWorkflow, cleanStepResult, cloneStep, cloneWorkflow, collectNestedWorkflowIds, computeNextFireAt, createDeprecationProxy, createEventedWorkflow, createMappingStep, createRestartExecutionParams, createStep, createStepFromAgent, createStepFromTool, createTimeTravelExecutionParams, createWorkflow, createWorkflowStateReader, derivePredicateLabel, evaluatePredicate, forEachSingleStepEntry, forEachSingleStepEntryWithPath, getEntryId, getEntryWorkflow, getResumeLabelsByStepId, getSingleStepEntryId, getStepIds, getStepResult, getWorkflowResumeLabel, getWorkflowResumeLabels, getWorkflowStepOutput, getWorkflowStepPayload, getWorkflowSuspendedStep, getWorkflowSuspendedSteps, hydrateSerializedStepErrors, inferGraphSchemas, isProcessor, isSingleStepEntry, jsonSchemaToZod, mapVariable, parseMapConfig, predicateSchema, predicateToCondition, rehydrateWorkflow, resolveForeachConcurrency, runCountDeprecationMessage, schemaCompatibility, toJsonSchemaOrUndefined, toStorableGraph, validateCron, validateStepInput, validateStepRequestContext, validateStepResumeData, validateStepStateData, validateStepSuspendData, validateStorableJsonSchema, validateStoredWorkflow, validateWorkflowRefs, validateWorkflowSchemas, validateWorkflowStructure, waitForSuspendedSnapshot };
//# sourceMappingURL=index.js.map