@mastra/core 0.2.0-alpha.98 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/index.d.ts +6 -8
- package/dist/agent/index.js +15 -7
- package/dist/{telemetry-oCUM52DG.d.ts → base-BbtPAA6f.d.ts} +50 -8
- package/dist/{index-Cwb-5AzX.d.ts → base-Bpb7Dmwe.d.ts} +367 -320
- package/dist/base.d.ts +3 -43
- package/dist/base.js +3 -3
- package/dist/bundler/index.d.ts +3 -4
- package/dist/bundler/index.js +4 -4
- package/dist/{chunk-MCB4M5W4.js → chunk-22LC46YN.js} +3 -9
- package/dist/{chunk-MG3WAQV7.js → chunk-2JL6DQMZ.js} +20 -28
- package/dist/chunk-2SAHBQEF.js +3 -0
- package/dist/chunk-3HBFW3Q7.js +24 -0
- package/dist/{chunk-KNPBNSJ7.js → chunk-55GTEVHJ.js} +12 -13
- package/dist/chunk-65VPTVVP.js +218 -0
- package/dist/chunk-AWEACB2T.js +66 -0
- package/dist/chunk-C6A6W6XS.js +49 -0
- package/dist/chunk-FGZVE4CM.js +404 -0
- package/dist/{chunk-TYIBRZOY.js → chunk-J3W3IHDO.js} +120 -87
- package/dist/chunk-K36NSQWH.js +10 -0
- package/dist/{chunk-QXH6EK72.js → chunk-K4DFI76V.js} +382 -370
- package/dist/{chunk-42DYOLDV.js → chunk-MEISIZMP.js} +13 -21
- package/dist/chunk-MLWGYRJR.js +87 -0
- package/dist/{chunk-ICMEXHKD.js → chunk-O2VP5JBC.js} +48 -55
- package/dist/{chunk-ZJOMHCWE.js → chunk-OJ26F3J4.js} +98 -153
- package/dist/chunk-RG66XEJT.js +8 -0
- package/dist/chunk-SB37QG7O.js +1203 -0
- package/dist/chunk-SDBM53G4.js +32 -0
- package/dist/{chunk-4LJFWC2Q.js → chunk-SIFBBGY6.js} +59 -85
- package/dist/chunk-U6J2FOU4.js +624 -0
- package/dist/chunk-VB7CO5ND.js +31 -0
- package/dist/{chunk-C55JWGDU.js → chunk-ZJOXJFJI.js} +43 -15
- package/dist/deployer/index.d.ts +2 -4
- package/dist/deployer/index.js +5 -5
- package/dist/eval/index.d.ts +8 -13
- package/dist/eval/index.js +3 -3
- package/dist/filter/index.js +2 -2
- package/dist/hooks/index.d.ts +13 -18
- package/dist/hooks/index.js +2 -2
- package/dist/{index-CBZ2mk2H.d.ts → index-B2JCcAQt.d.ts} +1 -1
- package/dist/index.d.ts +15 -15
- package/dist/index.js +43 -69
- package/dist/integration/index.d.ts +8 -10
- package/dist/integration/index.js +6 -3
- package/dist/llm/index.d.ts +6 -8
- package/dist/llm/index.js +1 -1
- package/dist/logger/index.d.ts +1 -1
- package/dist/logger/index.js +2 -2
- package/dist/mastra/index.d.ts +10 -13
- package/dist/mastra/index.js +20 -4
- package/dist/memory/index.d.ts +8 -10
- package/dist/memory/index.js +11 -9
- package/dist/relevance/index.js +16 -8
- package/dist/storage/index.d.ts +21 -10
- package/dist/storage/index.js +8 -7
- package/dist/telemetry/index.d.ts +35 -5
- package/dist/telemetry/index.js +3 -2
- package/dist/telemetry/otel-vendor.d.ts +7 -0
- package/dist/telemetry/otel-vendor.js +8 -0
- package/dist/tools/index.d.ts +6 -8
- package/dist/tools/index.js +2 -2
- package/dist/tts/index.d.ts +2 -4
- package/dist/tts/index.js +6 -5
- package/dist/{metric-BWeQNZt6.d.ts → types-m9RryK9a.d.ts} +6 -1
- package/dist/utils.js +2 -2
- package/dist/vector/index.d.ts +4 -6
- package/dist/vector/index.js +4 -4
- package/dist/vector/libsql/index.d.ts +2 -4
- package/dist/vector/libsql/index.js +6 -6
- package/dist/{workflow-DTtv7_Eq.d.ts → workflow-Cy8UTGCt.d.ts} +3 -6
- package/dist/workflows/index.d.ts +7 -9
- package/dist/workflows/index.js +4 -4
- package/package.json +14 -10
- package/dist/chunk-4ZUSEHLH.js +0 -285
- package/dist/chunk-AJJZUHB4.js +0 -14
- package/dist/chunk-G4MCO7XF.js +0 -70
- package/dist/chunk-HBTQNIAX.js +0 -90
- package/dist/chunk-HPXWJBQK.js +0 -222
- package/dist/chunk-JJ57BXQR.js +0 -16
- package/dist/chunk-JP37ODNX.js +0 -36
- package/dist/chunk-K3N7KJHH.js +0 -52
- package/dist/chunk-MDM2JS2U.js +0 -1288
- package/dist/chunk-VOUPGVRD.js +0 -27
- package/dist/chunk-Z7JFMQZZ.js +0 -551
- /package/dist/{chunk-AE3H2QEY.js → chunk-VDOJTUYY.js} +0 -0
|
@@ -0,0 +1,1203 @@
|
|
|
1
|
+
import { MastraBase } from './chunk-AWEACB2T.js';
|
|
2
|
+
import { context, trace } from '@opentelemetry/api';
|
|
3
|
+
import { get } from 'radash';
|
|
4
|
+
import sift from 'sift';
|
|
5
|
+
import { setup, createActor, assign, fromPromise } from 'xstate';
|
|
6
|
+
import 'zod';
|
|
7
|
+
|
|
8
|
+
var Step = class {
|
|
9
|
+
id;
|
|
10
|
+
description;
|
|
11
|
+
inputSchema;
|
|
12
|
+
outputSchema;
|
|
13
|
+
payload;
|
|
14
|
+
execute;
|
|
15
|
+
retryConfig;
|
|
16
|
+
mastra;
|
|
17
|
+
constructor({
|
|
18
|
+
id,
|
|
19
|
+
description,
|
|
20
|
+
execute,
|
|
21
|
+
payload,
|
|
22
|
+
outputSchema,
|
|
23
|
+
inputSchema,
|
|
24
|
+
retryConfig
|
|
25
|
+
}) {
|
|
26
|
+
this.id = id;
|
|
27
|
+
this.description = description ?? "";
|
|
28
|
+
this.inputSchema = inputSchema;
|
|
29
|
+
this.payload = payload;
|
|
30
|
+
this.outputSchema = outputSchema;
|
|
31
|
+
this.execute = execute;
|
|
32
|
+
this.retryConfig = retryConfig;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
function createStep(opts) {
|
|
36
|
+
return new Step(opts);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// src/workflows/utils.ts
|
|
40
|
+
function isErrorEvent(stateEvent) {
|
|
41
|
+
return stateEvent.type.startsWith("xstate.error.actor.");
|
|
42
|
+
}
|
|
43
|
+
function isTransitionEvent(stateEvent) {
|
|
44
|
+
return stateEvent.type.startsWith("xstate.done.actor.");
|
|
45
|
+
}
|
|
46
|
+
function isVariableReference(value) {
|
|
47
|
+
return typeof value === "object" && "step" in value && "path" in value;
|
|
48
|
+
}
|
|
49
|
+
function getStepResult(result) {
|
|
50
|
+
if (result?.status === "success") return result.output;
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/workflows/workflow.ts
|
|
55
|
+
var Workflow = class extends MastraBase {
|
|
56
|
+
name;
|
|
57
|
+
triggerSchema;
|
|
58
|
+
/** XState machine instance that orchestrates the workflow execution */
|
|
59
|
+
#machine;
|
|
60
|
+
/** XState actor instance that manages the workflow execution */
|
|
61
|
+
#actor = null;
|
|
62
|
+
#runId;
|
|
63
|
+
#retryConfig;
|
|
64
|
+
#mastra;
|
|
65
|
+
// registers stepIds on `after` calls
|
|
66
|
+
#afterStepStack = [];
|
|
67
|
+
#lastStepStack = [];
|
|
68
|
+
#stepGraph = { initial: [] };
|
|
69
|
+
#stepSubscriberGraph = {};
|
|
70
|
+
#steps = {};
|
|
71
|
+
#onStepTransition = /* @__PURE__ */ new Set();
|
|
72
|
+
#executionSpan;
|
|
73
|
+
/**
|
|
74
|
+
* Creates a new Workflow instance
|
|
75
|
+
* @param name - Identifier for the workflow (not necessarily unique)
|
|
76
|
+
* @param logger - Optional logger instance
|
|
77
|
+
*/
|
|
78
|
+
constructor({ name, triggerSchema, retryConfig, mastra }) {
|
|
79
|
+
super({ component: "WORKFLOW", name });
|
|
80
|
+
this.name = name;
|
|
81
|
+
this.#retryConfig = retryConfig;
|
|
82
|
+
this.triggerSchema = triggerSchema;
|
|
83
|
+
this.#runId = crypto.randomUUID();
|
|
84
|
+
this.#mastra = mastra;
|
|
85
|
+
if (mastra?.logger) {
|
|
86
|
+
this.logger = mastra?.logger;
|
|
87
|
+
}
|
|
88
|
+
this.initializeMachine();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Initializes the XState machine for the workflow
|
|
92
|
+
*
|
|
93
|
+
* Registers the machine's types, actions, actors, initial context, entry actions, initial state, and states
|
|
94
|
+
* @returns The initialized machine
|
|
95
|
+
*/
|
|
96
|
+
initializeMachine() {
|
|
97
|
+
const machine = setup({
|
|
98
|
+
types: {},
|
|
99
|
+
delays: this.#makeDelayMap(),
|
|
100
|
+
actions: this.#getDefaultActions(),
|
|
101
|
+
actors: this.#getDefaultActors()
|
|
102
|
+
}).createMachine({
|
|
103
|
+
id: this.name,
|
|
104
|
+
type: "parallel",
|
|
105
|
+
context: ({ input }) => ({
|
|
106
|
+
...input
|
|
107
|
+
}),
|
|
108
|
+
states: this.#buildStateHierarchy(this.#stepGraph)
|
|
109
|
+
});
|
|
110
|
+
this.#machine = machine;
|
|
111
|
+
return machine;
|
|
112
|
+
}
|
|
113
|
+
step(step, config) {
|
|
114
|
+
const { variables = {} } = config || {};
|
|
115
|
+
const requiredData = {};
|
|
116
|
+
for (const [key, variable] of Object.entries(variables)) {
|
|
117
|
+
if (variable && isVariableReference(variable)) {
|
|
118
|
+
requiredData[key] = variable;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const stepKey = this.#makeStepKey(step);
|
|
122
|
+
const graphEntry = {
|
|
123
|
+
step,
|
|
124
|
+
config: {
|
|
125
|
+
...this.#makeStepDef(stepKey),
|
|
126
|
+
...config,
|
|
127
|
+
data: requiredData
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
this.#steps[stepKey] = step;
|
|
131
|
+
const parentStepKey = this.#afterStepStack[this.#afterStepStack.length - 1];
|
|
132
|
+
const stepGraph = this.#stepSubscriberGraph[parentStepKey || ""];
|
|
133
|
+
if (parentStepKey && stepGraph) {
|
|
134
|
+
if (!stepGraph.initial.some((step2) => step2.step.id === stepKey)) {
|
|
135
|
+
stepGraph.initial.push(graphEntry);
|
|
136
|
+
}
|
|
137
|
+
stepGraph[stepKey] = [];
|
|
138
|
+
} else {
|
|
139
|
+
if (!this.#stepGraph[stepKey]) this.#stepGraph[stepKey] = [];
|
|
140
|
+
this.#stepGraph.initial.push(graphEntry);
|
|
141
|
+
}
|
|
142
|
+
this.#lastStepStack.push(stepKey);
|
|
143
|
+
return this;
|
|
144
|
+
}
|
|
145
|
+
then(step, config) {
|
|
146
|
+
const { variables = {} } = config || {};
|
|
147
|
+
const requiredData = {};
|
|
148
|
+
for (const [key, variable] of Object.entries(variables)) {
|
|
149
|
+
if (variable && isVariableReference(variable)) {
|
|
150
|
+
requiredData[key] = variable;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const lastStepKey = this.#lastStepStack[this.#lastStepStack.length - 1];
|
|
154
|
+
const stepKey = this.#makeStepKey(step);
|
|
155
|
+
const graphEntry = {
|
|
156
|
+
step,
|
|
157
|
+
config: {
|
|
158
|
+
...this.#makeStepDef(stepKey),
|
|
159
|
+
...config,
|
|
160
|
+
data: requiredData
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
this.#steps[stepKey] = step;
|
|
164
|
+
if (!lastStepKey) return this;
|
|
165
|
+
const parentStepKey = this.#afterStepStack[this.#afterStepStack.length - 1];
|
|
166
|
+
const stepGraph = this.#stepSubscriberGraph[parentStepKey || ""];
|
|
167
|
+
if (parentStepKey && stepGraph && stepGraph[lastStepKey]) {
|
|
168
|
+
stepGraph[lastStepKey].push(graphEntry);
|
|
169
|
+
} else {
|
|
170
|
+
if (!this.#stepGraph[lastStepKey]) this.#stepGraph[lastStepKey] = [];
|
|
171
|
+
this.#stepGraph[lastStepKey].push(graphEntry);
|
|
172
|
+
}
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
after(step) {
|
|
176
|
+
const stepKey = this.#makeStepKey(step);
|
|
177
|
+
this.#afterStepStack.push(stepKey);
|
|
178
|
+
if (!this.#stepSubscriberGraph[stepKey]) {
|
|
179
|
+
this.#stepSubscriberGraph[stepKey] = { initial: [] };
|
|
180
|
+
}
|
|
181
|
+
return this;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Executes the workflow with the given trigger data
|
|
185
|
+
* @param triggerData - Initial data to start the workflow with
|
|
186
|
+
* @returns Promise resolving to workflow results or rejecting with error
|
|
187
|
+
* @throws Error if trigger schema validation fails
|
|
188
|
+
*/
|
|
189
|
+
createRun() {
|
|
190
|
+
const runId = crypto.randomUUID();
|
|
191
|
+
this.#runId = runId;
|
|
192
|
+
return {
|
|
193
|
+
runId,
|
|
194
|
+
start: async ({ triggerData } = {}) => this.execute({ triggerData })
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async execute({
|
|
198
|
+
triggerData,
|
|
199
|
+
snapshot,
|
|
200
|
+
runId,
|
|
201
|
+
stepId
|
|
202
|
+
} = {}) {
|
|
203
|
+
if (runId) {
|
|
204
|
+
this.#runId = runId;
|
|
205
|
+
this.logger.debug(`Workflow snapshot received`, { runId: this.#runId, snapshot });
|
|
206
|
+
}
|
|
207
|
+
this.#executionSpan = this.#mastra?.telemetry?.tracer.startSpan(`workflow.${this.name}.execute`, {
|
|
208
|
+
attributes: { componentName: this.name, runId: this.#runId }
|
|
209
|
+
});
|
|
210
|
+
const machineInput = snapshot ? snapshot.context : {
|
|
211
|
+
// Maintain the original step results and their output
|
|
212
|
+
steps: {},
|
|
213
|
+
triggerData: triggerData || {},
|
|
214
|
+
attempts: Object.keys(this.#steps).reduce(
|
|
215
|
+
(acc, stepKey) => {
|
|
216
|
+
acc[stepKey] = this.#steps[stepKey]?.retryConfig?.attempts || this.#retryConfig?.attempts || 3;
|
|
217
|
+
return acc;
|
|
218
|
+
},
|
|
219
|
+
{}
|
|
220
|
+
)
|
|
221
|
+
};
|
|
222
|
+
this.logger.debug(`Machine input prepared`, { runId: this.#runId, machineInput });
|
|
223
|
+
const actorSnapshot = snapshot ? {
|
|
224
|
+
...snapshot,
|
|
225
|
+
context: machineInput
|
|
226
|
+
} : undefined;
|
|
227
|
+
this.logger.debug(`Creating actor with configuration`, {
|
|
228
|
+
machineInput,
|
|
229
|
+
actorSnapshot,
|
|
230
|
+
machineStates: this.#machine.config.states,
|
|
231
|
+
runId: this.#runId
|
|
232
|
+
});
|
|
233
|
+
this.#actor = createActor(this.#machine, {
|
|
234
|
+
inspect: (inspectionEvent) => {
|
|
235
|
+
this.logger.debug("XState inspection event", {
|
|
236
|
+
type: inspectionEvent.type,
|
|
237
|
+
event: inspectionEvent.event,
|
|
238
|
+
runId: this.#runId
|
|
239
|
+
});
|
|
240
|
+
},
|
|
241
|
+
input: machineInput,
|
|
242
|
+
snapshot: actorSnapshot
|
|
243
|
+
});
|
|
244
|
+
this.#actor.start();
|
|
245
|
+
if (stepId) {
|
|
246
|
+
this.#actor.send({ type: "RESET_TO_PENDING", stepId });
|
|
247
|
+
}
|
|
248
|
+
this.logger.debug("Actor started", { runId: this.#runId });
|
|
249
|
+
return new Promise((resolve, reject) => {
|
|
250
|
+
if (!this.#actor) {
|
|
251
|
+
const e = new Error("Actor not initialized");
|
|
252
|
+
this.#executionSpan?.recordException(e);
|
|
253
|
+
this.#executionSpan?.end();
|
|
254
|
+
reject(e);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
const suspendedPaths = /* @__PURE__ */ new Set();
|
|
258
|
+
this.#actor.subscribe(async (state) => {
|
|
259
|
+
if (this.#onStepTransition) {
|
|
260
|
+
this.#onStepTransition.forEach((onTransition) => {
|
|
261
|
+
onTransition({
|
|
262
|
+
runId: this.#runId,
|
|
263
|
+
value: state.value,
|
|
264
|
+
context: state.context,
|
|
265
|
+
activePaths: this.#getActivePathsAndStatus(state.value),
|
|
266
|
+
timestamp: Date.now()
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
this.#getSuspendedPaths({
|
|
271
|
+
value: state.value,
|
|
272
|
+
path: "",
|
|
273
|
+
suspendedPaths
|
|
274
|
+
});
|
|
275
|
+
const allStatesValue = state.value;
|
|
276
|
+
const allStatesComplete = this.#recursivelyCheckForFinalState({
|
|
277
|
+
value: allStatesValue,
|
|
278
|
+
suspendedPaths,
|
|
279
|
+
path: ""
|
|
280
|
+
});
|
|
281
|
+
this.logger.debug("State completion check", {
|
|
282
|
+
allStatesComplete,
|
|
283
|
+
suspendedPaths: Array.from(suspendedPaths),
|
|
284
|
+
runId: this.#runId
|
|
285
|
+
});
|
|
286
|
+
if (!allStatesComplete) return;
|
|
287
|
+
try {
|
|
288
|
+
await this.#persistWorkflowSnapshot();
|
|
289
|
+
this.#cleanup();
|
|
290
|
+
this.#executionSpan?.end();
|
|
291
|
+
resolve({
|
|
292
|
+
triggerData,
|
|
293
|
+
results: state.context.steps,
|
|
294
|
+
runId: this.#runId
|
|
295
|
+
});
|
|
296
|
+
} catch (error) {
|
|
297
|
+
this.logger.debug("Failed to persist final snapshot", { error });
|
|
298
|
+
this.#cleanup();
|
|
299
|
+
this.#executionSpan?.end();
|
|
300
|
+
resolve({
|
|
301
|
+
triggerData,
|
|
302
|
+
results: state.context.steps,
|
|
303
|
+
runId: this.#runId
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Rebuilds the machine with the current steps configuration and validates the workflow
|
|
311
|
+
*
|
|
312
|
+
* This is the last step of a workflow builder method chain
|
|
313
|
+
* @throws Error if validation fails
|
|
314
|
+
*
|
|
315
|
+
* @returns this instance for method chaining
|
|
316
|
+
*/
|
|
317
|
+
commit() {
|
|
318
|
+
this.initializeMachine();
|
|
319
|
+
return this;
|
|
320
|
+
}
|
|
321
|
+
// record all object paths that leads to a suspended state
|
|
322
|
+
#getSuspendedPaths({
|
|
323
|
+
value,
|
|
324
|
+
path,
|
|
325
|
+
suspendedPaths
|
|
326
|
+
}) {
|
|
327
|
+
if (typeof value === "string") {
|
|
328
|
+
if (value === "suspended") {
|
|
329
|
+
suspendedPaths.add(path);
|
|
330
|
+
}
|
|
331
|
+
} else {
|
|
332
|
+
Object.keys(value).forEach(
|
|
333
|
+
(key) => this.#getSuspendedPaths({ value: value[key], path: path ? `${path}.${key}` : key, suspendedPaths })
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
#isFinalState(status) {
|
|
338
|
+
return ["completed", "failed"].includes(status);
|
|
339
|
+
}
|
|
340
|
+
#recursivelyCheckForFinalState({
|
|
341
|
+
value,
|
|
342
|
+
suspendedPaths,
|
|
343
|
+
path
|
|
344
|
+
}) {
|
|
345
|
+
if (typeof value === "string") {
|
|
346
|
+
return this.#isFinalState(value) || suspendedPaths.has(path);
|
|
347
|
+
}
|
|
348
|
+
return Object.keys(value).every(
|
|
349
|
+
(key) => this.#recursivelyCheckForFinalState({ value: value[key], suspendedPaths, path: path ? `${path}.${key}` : key })
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
#buildBaseState(stepNode, nextSteps = []) {
|
|
353
|
+
const nextStep = nextSteps.shift();
|
|
354
|
+
return {
|
|
355
|
+
initial: "pending",
|
|
356
|
+
on: {
|
|
357
|
+
RESET_TO_PENDING: {
|
|
358
|
+
target: ".pending"
|
|
359
|
+
// Note the dot to target child state
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
states: {
|
|
363
|
+
pending: {
|
|
364
|
+
entry: () => {
|
|
365
|
+
this.logger.debug(`Step ${stepNode.step.id} pending`, {
|
|
366
|
+
stepId: stepNode.step.id,
|
|
367
|
+
runId: this.#runId
|
|
368
|
+
});
|
|
369
|
+
},
|
|
370
|
+
exit: () => {
|
|
371
|
+
this.logger.debug(`Step ${stepNode.step.id} finished pending`, {
|
|
372
|
+
stepId: stepNode.step.id,
|
|
373
|
+
runId: this.#runId
|
|
374
|
+
});
|
|
375
|
+
},
|
|
376
|
+
invoke: {
|
|
377
|
+
src: "conditionCheck",
|
|
378
|
+
input: ({ context }) => {
|
|
379
|
+
return {
|
|
380
|
+
context,
|
|
381
|
+
stepNode
|
|
382
|
+
};
|
|
383
|
+
},
|
|
384
|
+
onDone: [
|
|
385
|
+
{
|
|
386
|
+
guard: ({ event }) => {
|
|
387
|
+
return event.output.type === "SUSPENDED";
|
|
388
|
+
},
|
|
389
|
+
target: "suspended",
|
|
390
|
+
actions: [
|
|
391
|
+
assign({
|
|
392
|
+
steps: ({ context, event }) => {
|
|
393
|
+
if (event.output.type !== "SUSPENDED") return context.steps;
|
|
394
|
+
return {
|
|
395
|
+
...context.steps,
|
|
396
|
+
[stepNode.step.id]: {
|
|
397
|
+
status: "suspended",
|
|
398
|
+
...context.steps?.[stepNode.step.id] || {}
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
},
|
|
402
|
+
attempts: ({ context, event }) => {
|
|
403
|
+
if (event.output.type !== "SUSPENDED") return context.attempts;
|
|
404
|
+
return { ...context.attempts, [stepNode.step.id]: stepNode.step.retryConfig?.attempts || 3 };
|
|
405
|
+
}
|
|
406
|
+
})
|
|
407
|
+
]
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
guard: ({ event }) => {
|
|
411
|
+
return event.output.type === "WAITING";
|
|
412
|
+
},
|
|
413
|
+
target: "waiting",
|
|
414
|
+
actions: [
|
|
415
|
+
{ type: "decrementAttemptCount", params: { stepId: stepNode.step.id } },
|
|
416
|
+
assign({
|
|
417
|
+
steps: ({ context, event }) => {
|
|
418
|
+
if (event.output.type !== "WAITING") return context.steps;
|
|
419
|
+
return {
|
|
420
|
+
...context.steps,
|
|
421
|
+
[stepNode.step.id]: {
|
|
422
|
+
status: "waiting"
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
})
|
|
427
|
+
]
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
guard: ({ event }) => {
|
|
431
|
+
return event.output.type === "CONDITIONS_MET";
|
|
432
|
+
},
|
|
433
|
+
target: "executing"
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
guard: ({ event }) => {
|
|
437
|
+
return event.output.type === "CONDITION_FAILED";
|
|
438
|
+
},
|
|
439
|
+
target: "failed",
|
|
440
|
+
actions: assign({
|
|
441
|
+
steps: ({ context, event }) => {
|
|
442
|
+
if (event.output.type !== "CONDITION_FAILED") return context.steps;
|
|
443
|
+
this.logger.debug(`Workflow condition check failed`, {
|
|
444
|
+
error: event.output.error,
|
|
445
|
+
stepId: stepNode.step.id
|
|
446
|
+
});
|
|
447
|
+
return {
|
|
448
|
+
...context.steps,
|
|
449
|
+
[stepNode.step.id]: {
|
|
450
|
+
status: "failed",
|
|
451
|
+
error: event.output.error
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
})
|
|
456
|
+
}
|
|
457
|
+
]
|
|
458
|
+
}
|
|
459
|
+
},
|
|
460
|
+
waiting: {
|
|
461
|
+
entry: () => {
|
|
462
|
+
this.logger.debug(`Step ${stepNode.step.id} waiting`, {
|
|
463
|
+
stepId: stepNode.step.id,
|
|
464
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
465
|
+
runId: this.#runId
|
|
466
|
+
});
|
|
467
|
+
},
|
|
468
|
+
exit: () => {
|
|
469
|
+
this.logger.debug(`Step ${stepNode.step.id} finished waiting`, {
|
|
470
|
+
stepId: stepNode.step.id,
|
|
471
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
472
|
+
runId: this.#runId
|
|
473
|
+
});
|
|
474
|
+
},
|
|
475
|
+
after: {
|
|
476
|
+
[stepNode.step.id]: {
|
|
477
|
+
target: "pending"
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
suspended: {
|
|
482
|
+
type: "final",
|
|
483
|
+
entry: [
|
|
484
|
+
() => {
|
|
485
|
+
this.logger.debug(`Step ${stepNode.step.id} suspended`, {
|
|
486
|
+
stepId: stepNode.step.id,
|
|
487
|
+
runId: this.#runId
|
|
488
|
+
});
|
|
489
|
+
},
|
|
490
|
+
assign({
|
|
491
|
+
steps: ({ context }) => ({
|
|
492
|
+
...context.steps,
|
|
493
|
+
[stepNode.step.id]: {
|
|
494
|
+
...context?.steps?.[stepNode.step.id] || {},
|
|
495
|
+
status: "suspended"
|
|
496
|
+
}
|
|
497
|
+
})
|
|
498
|
+
})
|
|
499
|
+
]
|
|
500
|
+
// after: {
|
|
501
|
+
// [stepNode.step.id]: {
|
|
502
|
+
// target: 'pending',
|
|
503
|
+
// actions: [
|
|
504
|
+
// assign({
|
|
505
|
+
// attempts: ({ context }: { context: WorkflowContext }) => ({
|
|
506
|
+
// ...context.attempts,
|
|
507
|
+
// [stepNode.step.id]: this.#steps[stepNode.step.id]?.retryConfig?.attempts || 3,
|
|
508
|
+
// }),
|
|
509
|
+
// }),
|
|
510
|
+
// ],
|
|
511
|
+
// }
|
|
512
|
+
// },
|
|
513
|
+
// entry: () => {
|
|
514
|
+
// this.logger.debug(`Step ${stepNode.step.id} suspended ${new Date().toISOString()}`);
|
|
515
|
+
// },
|
|
516
|
+
// exit: () => {
|
|
517
|
+
// this.logger.debug(`Step ${stepNode.step.id} finished suspended ${new Date().toISOString()}`);
|
|
518
|
+
// },
|
|
519
|
+
// after: {
|
|
520
|
+
// [stepNode.step.id]: {
|
|
521
|
+
// target: 'suspended',
|
|
522
|
+
// },
|
|
523
|
+
// },
|
|
524
|
+
},
|
|
525
|
+
executing: {
|
|
526
|
+
entry: () => {
|
|
527
|
+
this.logger.debug(`Step ${stepNode.step.id} executing`, {
|
|
528
|
+
stepId: stepNode.step.id,
|
|
529
|
+
runId: this.#runId
|
|
530
|
+
});
|
|
531
|
+
},
|
|
532
|
+
on: {
|
|
533
|
+
SUSPENDED: {
|
|
534
|
+
target: "suspended",
|
|
535
|
+
actions: [
|
|
536
|
+
assign({
|
|
537
|
+
steps: ({ context }) => ({
|
|
538
|
+
...context.steps,
|
|
539
|
+
[stepNode.step.id]: {
|
|
540
|
+
status: "suspended"
|
|
541
|
+
}
|
|
542
|
+
})
|
|
543
|
+
})
|
|
544
|
+
]
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
invoke: {
|
|
548
|
+
src: "resolverFunction",
|
|
549
|
+
input: ({ context }) => ({
|
|
550
|
+
context,
|
|
551
|
+
stepNode
|
|
552
|
+
}),
|
|
553
|
+
onDone: {
|
|
554
|
+
target: "runningSubscribers",
|
|
555
|
+
actions: [
|
|
556
|
+
({ event }) => this.logger.debug(`Step ${stepNode.step.id} finished executing`, {
|
|
557
|
+
stepId: stepNode.step.id,
|
|
558
|
+
output: event.output,
|
|
559
|
+
runId: this.#runId
|
|
560
|
+
}),
|
|
561
|
+
{ type: "updateStepResult", params: { stepId: stepNode.step.id } },
|
|
562
|
+
{ type: "spawnSubscribers", params: { stepId: stepNode.step.id } }
|
|
563
|
+
]
|
|
564
|
+
},
|
|
565
|
+
onError: {
|
|
566
|
+
target: "failed",
|
|
567
|
+
actions: [{ type: "setStepError", params: { stepId: stepNode.step.id } }]
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
runningSubscribers: {
|
|
572
|
+
entry: () => {
|
|
573
|
+
this.logger.debug(`Step ${stepNode.step.id} running subscribers`, {
|
|
574
|
+
stepId: stepNode.step.id,
|
|
575
|
+
runId: this.#runId
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
exit: () => {
|
|
579
|
+
this.logger.debug(`Step ${stepNode.step.id} finished running subscribers`, {
|
|
580
|
+
stepId: stepNode.step.id,
|
|
581
|
+
runId: this.#runId
|
|
582
|
+
});
|
|
583
|
+
},
|
|
584
|
+
invoke: {
|
|
585
|
+
src: "spawnSubscriberFunction",
|
|
586
|
+
input: ({ context }) => ({
|
|
587
|
+
parentStepId: stepNode.step.id,
|
|
588
|
+
context
|
|
589
|
+
}),
|
|
590
|
+
onDone: {
|
|
591
|
+
target: nextStep ? nextStep.step.id : "completed",
|
|
592
|
+
actions: [
|
|
593
|
+
assign({
|
|
594
|
+
steps: ({ context, event }) => ({
|
|
595
|
+
...context.steps,
|
|
596
|
+
...event.output.steps
|
|
597
|
+
})
|
|
598
|
+
}),
|
|
599
|
+
() => this.logger.debug(`Subscriber execution completed`, { stepId: stepNode.step.id })
|
|
600
|
+
]
|
|
601
|
+
},
|
|
602
|
+
onError: {
|
|
603
|
+
target: nextStep ? nextStep.step.id : "completed",
|
|
604
|
+
actions: ({ event }) => {
|
|
605
|
+
this.logger.debug(`Subscriber execution failed`, {
|
|
606
|
+
error: event.error,
|
|
607
|
+
stepId: stepNode.step.id
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
completed: {
|
|
614
|
+
type: "final",
|
|
615
|
+
entry: [
|
|
616
|
+
{ type: "notifyStepCompletion", params: { stepId: stepNode.step.id } },
|
|
617
|
+
{ type: "snapshotStep", params: { stepId: stepNode.step.id } },
|
|
618
|
+
{ type: "persistSnapshot" }
|
|
619
|
+
]
|
|
620
|
+
},
|
|
621
|
+
failed: {
|
|
622
|
+
type: "final",
|
|
623
|
+
entry: [
|
|
624
|
+
{ type: "notifyStepCompletion", params: { stepId: stepNode.step.id } },
|
|
625
|
+
{ type: "snapshotStep", params: { stepId: stepNode.step.id } },
|
|
626
|
+
{ type: "persistSnapshot" }
|
|
627
|
+
]
|
|
628
|
+
},
|
|
629
|
+
// build chain of next steps recursively
|
|
630
|
+
...nextStep ? { [nextStep.step.id]: { ...this.#buildBaseState(nextStep, nextSteps) } } : {}
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
#makeStepKey(step) {
|
|
635
|
+
return `${step.id}`;
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Builds the state hierarchy for the workflow
|
|
639
|
+
* @returns Object representing the state hierarchy
|
|
640
|
+
*/
|
|
641
|
+
#buildStateHierarchy(stepGraph) {
|
|
642
|
+
const states = {};
|
|
643
|
+
stepGraph.initial.forEach((stepNode) => {
|
|
644
|
+
const nextSteps = [...stepGraph[stepNode.step.id] || []];
|
|
645
|
+
states[stepNode.step.id] = {
|
|
646
|
+
...this.#buildBaseState(stepNode, nextSteps)
|
|
647
|
+
};
|
|
648
|
+
});
|
|
649
|
+
return states;
|
|
650
|
+
}
|
|
651
|
+
#getDefaultActions() {
|
|
652
|
+
return {
|
|
653
|
+
updateStepResult: assign({
|
|
654
|
+
steps: ({ context, event }) => {
|
|
655
|
+
if (!isTransitionEvent(event)) return context.steps;
|
|
656
|
+
const { stepId, result } = event.output;
|
|
657
|
+
return {
|
|
658
|
+
...context.steps,
|
|
659
|
+
[stepId]: {
|
|
660
|
+
status: "success",
|
|
661
|
+
output: result
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
}),
|
|
666
|
+
setStepError: assign({
|
|
667
|
+
steps: ({ context, event }, params) => {
|
|
668
|
+
if (!isErrorEvent(event)) return context.steps;
|
|
669
|
+
const { stepId } = params;
|
|
670
|
+
if (!stepId) return context.steps;
|
|
671
|
+
return {
|
|
672
|
+
...context.steps,
|
|
673
|
+
[stepId]: {
|
|
674
|
+
status: "failed",
|
|
675
|
+
error: event.error.message
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
}),
|
|
680
|
+
notifyStepCompletion: async (_, params) => {
|
|
681
|
+
const { stepId } = params;
|
|
682
|
+
this.logger.debug(`Step ${stepId} completed`);
|
|
683
|
+
},
|
|
684
|
+
snapshotStep: assign({
|
|
685
|
+
_snapshot: ({}, params) => {
|
|
686
|
+
const { stepId } = params;
|
|
687
|
+
return { stepId };
|
|
688
|
+
}
|
|
689
|
+
}),
|
|
690
|
+
persistSnapshot: async ({ context }) => {
|
|
691
|
+
if (context._snapshot) {
|
|
692
|
+
return await this.#persistWorkflowSnapshot();
|
|
693
|
+
}
|
|
694
|
+
return;
|
|
695
|
+
},
|
|
696
|
+
decrementAttemptCount: assign({
|
|
697
|
+
attempts: ({ context, event }, params) => {
|
|
698
|
+
if (!isTransitionEvent(event)) return context.attempts;
|
|
699
|
+
const { stepId } = params;
|
|
700
|
+
const attemptCount = context.attempts[stepId];
|
|
701
|
+
if (attemptCount === undefined) return context.attempts;
|
|
702
|
+
return { ...context.attempts, [stepId]: attemptCount - 1 };
|
|
703
|
+
}
|
|
704
|
+
})
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
#getInjectables() {
|
|
708
|
+
return {
|
|
709
|
+
runId: this.#runId,
|
|
710
|
+
mastra: this.#mastra
|
|
711
|
+
};
|
|
712
|
+
}
|
|
713
|
+
#getDefaultActors() {
|
|
714
|
+
return {
|
|
715
|
+
resolverFunction: fromPromise(async ({ input }) => {
|
|
716
|
+
const { stepNode, context } = input;
|
|
717
|
+
const injectables = this.#getInjectables();
|
|
718
|
+
const resolvedData = this.#resolveVariables({
|
|
719
|
+
stepConfig: stepNode.config,
|
|
720
|
+
context,
|
|
721
|
+
stepId: stepNode.step.id
|
|
722
|
+
});
|
|
723
|
+
this.logger.debug(`Resolved variables for ${stepNode.step.id}`, {
|
|
724
|
+
resolvedData,
|
|
725
|
+
runId: this.#runId
|
|
726
|
+
});
|
|
727
|
+
const result = await stepNode.config.handler({
|
|
728
|
+
context: resolvedData,
|
|
729
|
+
suspend: async () => {
|
|
730
|
+
if (this.#actor) {
|
|
731
|
+
context.steps[stepNode.step.id] = {
|
|
732
|
+
status: "suspended"
|
|
733
|
+
};
|
|
734
|
+
await this.#persistWorkflowSnapshot();
|
|
735
|
+
this.logger.debug(`Sending SUSPENDED event for step ${stepNode.step.id}`);
|
|
736
|
+
this.#actor?.send({ type: "SUSPENDED", stepId: stepNode.step.id });
|
|
737
|
+
} else {
|
|
738
|
+
this.logger.debug(`Actor not available for step ${stepNode.step.id}`);
|
|
739
|
+
}
|
|
740
|
+
},
|
|
741
|
+
...injectables
|
|
742
|
+
});
|
|
743
|
+
this.logger.debug(`Step ${stepNode.step.id} result`, {
|
|
744
|
+
stepId: stepNode.step.id,
|
|
745
|
+
result,
|
|
746
|
+
runId: this.#runId
|
|
747
|
+
});
|
|
748
|
+
return {
|
|
749
|
+
stepId: stepNode.step.id,
|
|
750
|
+
result
|
|
751
|
+
};
|
|
752
|
+
}),
|
|
753
|
+
conditionCheck: fromPromise(async ({ input }) => {
|
|
754
|
+
const { context, stepNode } = input;
|
|
755
|
+
const stepConfig = stepNode.config;
|
|
756
|
+
const attemptCount = context.attempts[stepNode.step.id];
|
|
757
|
+
this.logger.debug(`Checking conditions for step ${stepNode.step.id}`, {
|
|
758
|
+
stepId: stepNode.step.id,
|
|
759
|
+
runId: this.#runId
|
|
760
|
+
});
|
|
761
|
+
this.logger.debug(`Attempt count for step ${stepNode.step.id}`, {
|
|
762
|
+
attemptCount,
|
|
763
|
+
attempts: context.attempts,
|
|
764
|
+
runId: this.#runId,
|
|
765
|
+
stepId: stepNode.step.id
|
|
766
|
+
});
|
|
767
|
+
if (!attemptCount || attemptCount < 0) {
|
|
768
|
+
if (stepConfig?.snapshotOnTimeout) {
|
|
769
|
+
return { type: "SUSPENDED", stepId: stepNode.step.id };
|
|
770
|
+
}
|
|
771
|
+
return { type: "CONDITION_FAILED", error: `Step:${stepNode.step.id} condition check failed` };
|
|
772
|
+
}
|
|
773
|
+
if (!stepConfig?.when) {
|
|
774
|
+
return { type: "CONDITIONS_MET" };
|
|
775
|
+
}
|
|
776
|
+
this.logger.debug(`Checking conditions for step ${stepNode.step.id}`, {
|
|
777
|
+
stepId: stepNode.step.id,
|
|
778
|
+
runId: this.#runId
|
|
779
|
+
});
|
|
780
|
+
if (typeof stepConfig?.when === "function") {
|
|
781
|
+
const conditionMet = await stepConfig.when({ context });
|
|
782
|
+
if (conditionMet) {
|
|
783
|
+
this.logger.debug(`Condition met for step ${stepNode.step.id}`, {
|
|
784
|
+
stepId: stepNode.step.id,
|
|
785
|
+
runId: this.#runId
|
|
786
|
+
});
|
|
787
|
+
return { type: "CONDITIONS_MET" };
|
|
788
|
+
}
|
|
789
|
+
if (!attemptCount || attemptCount < 0) {
|
|
790
|
+
return { type: "CONDITION_FAILED", error: `Step:${stepNode.step.id} condition check failed` };
|
|
791
|
+
}
|
|
792
|
+
return { type: "WAITING", stepId: stepNode.step.id };
|
|
793
|
+
} else {
|
|
794
|
+
const conditionMet = this.#evaluateCondition(stepConfig.when, context);
|
|
795
|
+
if (!conditionMet) {
|
|
796
|
+
return {
|
|
797
|
+
type: "CONDITION_FAILED",
|
|
798
|
+
error: `Step:${stepNode.step.id} condition check failed`
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return { type: "CONDITIONS_MET" };
|
|
803
|
+
}),
|
|
804
|
+
spawnSubscriberFunction: fromPromise(
|
|
805
|
+
async ({
|
|
806
|
+
input
|
|
807
|
+
}) => {
|
|
808
|
+
const { parentStepId, context } = input;
|
|
809
|
+
const stepGraph = this.#stepSubscriberGraph[parentStepId];
|
|
810
|
+
if (!stepGraph) {
|
|
811
|
+
return {
|
|
812
|
+
steps: {}
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
const subscriberMachine = setup({
|
|
816
|
+
types: {},
|
|
817
|
+
delays: this.#makeDelayMap(),
|
|
818
|
+
actions: this.#getDefaultActions(),
|
|
819
|
+
actors: this.#getDefaultActors()
|
|
820
|
+
}).createMachine({
|
|
821
|
+
id: `${this.name}-subscriber-${parentStepId}`,
|
|
822
|
+
context,
|
|
823
|
+
type: "parallel",
|
|
824
|
+
states: this.#buildStateHierarchy(stepGraph)
|
|
825
|
+
});
|
|
826
|
+
const actor = createActor(subscriberMachine, { input: context });
|
|
827
|
+
actor.start();
|
|
828
|
+
return new Promise((resolve) => {
|
|
829
|
+
const suspendedPaths = /* @__PURE__ */ new Set();
|
|
830
|
+
actor.subscribe((state) => {
|
|
831
|
+
this.#getSuspendedPaths({
|
|
832
|
+
value: state.value,
|
|
833
|
+
path: "",
|
|
834
|
+
suspendedPaths
|
|
835
|
+
});
|
|
836
|
+
const allStatesValue = state.value;
|
|
837
|
+
const allStatesComplete = this.#recursivelyCheckForFinalState({
|
|
838
|
+
value: allStatesValue,
|
|
839
|
+
suspendedPaths,
|
|
840
|
+
path: ""
|
|
841
|
+
});
|
|
842
|
+
if (allStatesComplete) {
|
|
843
|
+
actor.stop();
|
|
844
|
+
resolve({
|
|
845
|
+
steps: state.context.steps
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
)
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Persists the workflow state to the database
|
|
856
|
+
*/
|
|
857
|
+
async #persistWorkflowSnapshot() {
|
|
858
|
+
const snapshotFromActor = this.#actor?.getPersistedSnapshot();
|
|
859
|
+
if (!this.#mastra?.storage) {
|
|
860
|
+
this.logger.debug("Snapshot cannot be persisted. Mastra engine is not initialized", {
|
|
861
|
+
runId: this.#runId
|
|
862
|
+
});
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
if (!snapshotFromActor) {
|
|
866
|
+
this.logger.debug("Snapshot cannot be persisted. No snapshot received.", { runId: this.#runId });
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
if (this.#mastra?.storage) {
|
|
870
|
+
await this.#mastra.storage.persistWorkflowSnapshot({
|
|
871
|
+
workflowName: this.name,
|
|
872
|
+
runId: this.#runId,
|
|
873
|
+
snapshot: snapshotFromActor
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
return this.#runId;
|
|
877
|
+
}
|
|
878
|
+
async #loadWorkflowSnapshot(runId) {
|
|
879
|
+
if (!this.#mastra?.storage) {
|
|
880
|
+
this.logger.debug("Snapshot cannot be loaded. Mastra engine is not initialized", { runId });
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
return this.#mastra.storage.loadWorkflowSnapshot({ runId, workflowName: this.name });
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* Resolves variables for a step from trigger data or previous step results
|
|
887
|
+
* @param stepConfig - Configuration of the step needing variable resolution
|
|
888
|
+
* @param context - Current workflow context containing results and trigger data
|
|
889
|
+
* @returns Object containing resolved variable values
|
|
890
|
+
*/
|
|
891
|
+
#resolveVariables({
|
|
892
|
+
stepConfig,
|
|
893
|
+
context,
|
|
894
|
+
stepId
|
|
895
|
+
}) {
|
|
896
|
+
this.logger.debug(`Resolving variables for step ${stepId}`, {
|
|
897
|
+
stepId,
|
|
898
|
+
runId: this.#runId
|
|
899
|
+
});
|
|
900
|
+
const resolvedData = {
|
|
901
|
+
...context,
|
|
902
|
+
getStepPayload: (stepId2) => {
|
|
903
|
+
if (stepId2 === "trigger") {
|
|
904
|
+
return context.triggerData;
|
|
905
|
+
}
|
|
906
|
+
const result = context.steps[stepId2];
|
|
907
|
+
if (result && result.status === "success") {
|
|
908
|
+
return result.output;
|
|
909
|
+
}
|
|
910
|
+
return undefined;
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
for (const [key, variable] of Object.entries(stepConfig.data)) {
|
|
914
|
+
const sourceData = variable.step === "trigger" ? context.triggerData : getStepResult(context.steps[variable.step.id]);
|
|
915
|
+
this.logger.debug(
|
|
916
|
+
`Got source data for ${key} variable from ${variable.step === "trigger" ? "trigger" : variable.step.id}`,
|
|
917
|
+
{
|
|
918
|
+
sourceData,
|
|
919
|
+
path: variable.path,
|
|
920
|
+
runId: this.#runId
|
|
921
|
+
}
|
|
922
|
+
);
|
|
923
|
+
if (!sourceData && variable.step !== "trigger") {
|
|
924
|
+
resolvedData[key] = undefined;
|
|
925
|
+
continue;
|
|
926
|
+
}
|
|
927
|
+
const value = variable.path === "" || variable.path === "." ? sourceData : get(sourceData, variable.path);
|
|
928
|
+
this.logger.debug(`Resolved variable ${key}`, {
|
|
929
|
+
value,
|
|
930
|
+
runId: this.#runId
|
|
931
|
+
});
|
|
932
|
+
resolvedData[key] = value;
|
|
933
|
+
}
|
|
934
|
+
return resolvedData;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* Evaluates a single condition against workflow context
|
|
938
|
+
*/
|
|
939
|
+
#evaluateCondition(condition, context) {
|
|
940
|
+
let andBranchResult = true;
|
|
941
|
+
let baseResult = true;
|
|
942
|
+
let orBranchResult = true;
|
|
943
|
+
const simpleCondition = Object.entries(condition).find(([key]) => key.includes("."));
|
|
944
|
+
if (simpleCondition) {
|
|
945
|
+
const [key, queryValue] = simpleCondition;
|
|
946
|
+
const [stepId, ...pathParts] = key.split(".");
|
|
947
|
+
const path = pathParts.join(".");
|
|
948
|
+
const sourceData = stepId === "trigger" ? context.triggerData : getStepResult(context.steps[stepId]);
|
|
949
|
+
this.logger.debug(`Got condition data from step ${stepId}`, {
|
|
950
|
+
stepId,
|
|
951
|
+
sourceData,
|
|
952
|
+
runId: this.#runId
|
|
953
|
+
});
|
|
954
|
+
if (!sourceData) {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
let value = get(sourceData, path);
|
|
958
|
+
if (stepId !== "trigger" && path === "status" && !value) {
|
|
959
|
+
value = "success";
|
|
960
|
+
}
|
|
961
|
+
if (typeof queryValue === "object" && queryValue !== null) {
|
|
962
|
+
baseResult = sift(queryValue)(value);
|
|
963
|
+
} else {
|
|
964
|
+
baseResult = value === queryValue;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if ("ref" in condition) {
|
|
968
|
+
const { ref, query } = condition;
|
|
969
|
+
const sourceData = ref.step === "trigger" ? context.triggerData : getStepResult(context.steps[ref.step.id]);
|
|
970
|
+
this.logger.debug(`Got condition data from ${ref.step === "trigger" ? "trigger" : ref.step.id}`, {
|
|
971
|
+
sourceData,
|
|
972
|
+
runId: this.#runId
|
|
973
|
+
});
|
|
974
|
+
if (!sourceData) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
let value = get(sourceData, ref.path);
|
|
978
|
+
if (ref.step !== "trigger" && ref.path === "status" && !value) {
|
|
979
|
+
value = "success";
|
|
980
|
+
}
|
|
981
|
+
baseResult = sift(query)(value);
|
|
982
|
+
}
|
|
983
|
+
if ("and" in condition) {
|
|
984
|
+
andBranchResult = condition.and.every((cond) => this.#evaluateCondition(cond, context));
|
|
985
|
+
this.logger.debug(`Evaluated AND condition`, {
|
|
986
|
+
andBranchResult,
|
|
987
|
+
runId: this.#runId
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
if ("or" in condition) {
|
|
991
|
+
orBranchResult = condition.or.some((cond) => this.#evaluateCondition(cond, context));
|
|
992
|
+
this.logger.debug(`Evaluated OR condition`, {
|
|
993
|
+
orBranchResult,
|
|
994
|
+
runId: this.#runId
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
const finalResult = baseResult && andBranchResult && orBranchResult;
|
|
998
|
+
this.logger.debug(`Evaluated condition`, {
|
|
999
|
+
finalResult,
|
|
1000
|
+
runId: this.#runId
|
|
1001
|
+
});
|
|
1002
|
+
return finalResult;
|
|
1003
|
+
}
|
|
1004
|
+
#makeStepDef(stepId) {
|
|
1005
|
+
const executeStep = (handler2, spanName, attributes) => {
|
|
1006
|
+
return async (data) => {
|
|
1007
|
+
return await context.with(trace.setSpan(context.active(), this.#executionSpan), async () => {
|
|
1008
|
+
return this.#mastra.telemetry.traceMethod(handler2, {
|
|
1009
|
+
spanName,
|
|
1010
|
+
attributes
|
|
1011
|
+
})(data);
|
|
1012
|
+
});
|
|
1013
|
+
};
|
|
1014
|
+
};
|
|
1015
|
+
const handler = async ({ context, ...rest }) => {
|
|
1016
|
+
const targetStep = this.#steps[stepId];
|
|
1017
|
+
if (!targetStep) throw new Error(`Step not found`);
|
|
1018
|
+
const { payload = {}, execute } = targetStep;
|
|
1019
|
+
const mergedData = {
|
|
1020
|
+
...payload,
|
|
1021
|
+
...context
|
|
1022
|
+
};
|
|
1023
|
+
const finalAction = this.#mastra?.telemetry ? executeStep(execute, `workflow.${this.name}.action.${stepId}`, {
|
|
1024
|
+
componentName: this.name,
|
|
1025
|
+
runId: context.runId ?? this.#runId
|
|
1026
|
+
}) : execute;
|
|
1027
|
+
return finalAction ? await finalAction({ context: mergedData, ...rest }) : {};
|
|
1028
|
+
};
|
|
1029
|
+
const finalHandler = ({ context, ...rest }) => {
|
|
1030
|
+
if (this.#executionSpan) {
|
|
1031
|
+
return executeStep(handler, `workflow.${this.name}.step.${stepId}`, {
|
|
1032
|
+
componentName: this.name,
|
|
1033
|
+
runId: context.runId ?? this.#runId
|
|
1034
|
+
})({ context, ...rest });
|
|
1035
|
+
}
|
|
1036
|
+
return handler({ context, ...rest });
|
|
1037
|
+
};
|
|
1038
|
+
return {
|
|
1039
|
+
handler: finalHandler,
|
|
1040
|
+
data: {}
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Creates a map of step IDs to their respective delay values
|
|
1045
|
+
* @returns Object mapping step IDs to delay values
|
|
1046
|
+
*/
|
|
1047
|
+
#makeDelayMap() {
|
|
1048
|
+
const delayMap = {};
|
|
1049
|
+
Object.keys(this.#steps).forEach((stepId) => {
|
|
1050
|
+
delayMap[stepId] = this.#steps[stepId]?.retryConfig?.delay || this.#retryConfig?.delay || 1e3;
|
|
1051
|
+
});
|
|
1052
|
+
return delayMap;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Cleans up the actor instance
|
|
1056
|
+
*/
|
|
1057
|
+
#cleanup() {
|
|
1058
|
+
if (this.#actor) {
|
|
1059
|
+
this.#actor.stop();
|
|
1060
|
+
this.#actor = null;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
#getActivePathsAndStatus(value) {
|
|
1064
|
+
const paths = [];
|
|
1065
|
+
const traverse = (current, path = []) => {
|
|
1066
|
+
for (const [key, value2] of Object.entries(current)) {
|
|
1067
|
+
const currentPath = [...path, key];
|
|
1068
|
+
if (typeof value2 === "string") {
|
|
1069
|
+
paths.push({
|
|
1070
|
+
stepPath: currentPath,
|
|
1071
|
+
stepId: key,
|
|
1072
|
+
status: value2
|
|
1073
|
+
});
|
|
1074
|
+
} else if (typeof value2 === "object" && value2 !== null) {
|
|
1075
|
+
traverse(value2, currentPath);
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
};
|
|
1079
|
+
traverse(value);
|
|
1080
|
+
return paths;
|
|
1081
|
+
}
|
|
1082
|
+
async getState(runId) {
|
|
1083
|
+
if (this.#runId === runId && this.#actor) {
|
|
1084
|
+
const snapshot = this.#actor.getSnapshot();
|
|
1085
|
+
const m = this.#getActivePathsAndStatus(snapshot.value);
|
|
1086
|
+
return {
|
|
1087
|
+
runId,
|
|
1088
|
+
value: snapshot.value,
|
|
1089
|
+
context: snapshot.context,
|
|
1090
|
+
activePaths: m,
|
|
1091
|
+
timestamp: Date.now()
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const storedSnapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
|
|
1095
|
+
runId,
|
|
1096
|
+
workflowName: this.name
|
|
1097
|
+
});
|
|
1098
|
+
if (storedSnapshot) {
|
|
1099
|
+
const parsed = storedSnapshot;
|
|
1100
|
+
const m = this.#getActivePathsAndStatus(parsed.value);
|
|
1101
|
+
return {
|
|
1102
|
+
runId,
|
|
1103
|
+
value: parsed.value,
|
|
1104
|
+
context: parsed.context,
|
|
1105
|
+
activePaths: m,
|
|
1106
|
+
timestamp: Date.now()
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
return null;
|
|
1110
|
+
}
|
|
1111
|
+
watch(onTransition) {
|
|
1112
|
+
this.#onStepTransition.add(onTransition);
|
|
1113
|
+
return () => {
|
|
1114
|
+
this.#onStepTransition.delete(onTransition);
|
|
1115
|
+
};
|
|
1116
|
+
}
|
|
1117
|
+
async resume({
|
|
1118
|
+
runId,
|
|
1119
|
+
stepId,
|
|
1120
|
+
context: resumeContext
|
|
1121
|
+
}) {
|
|
1122
|
+
const snapshot = await this.#loadWorkflowSnapshot(runId);
|
|
1123
|
+
if (!snapshot) {
|
|
1124
|
+
throw new Error(`No snapshot found for workflow run ${runId}`);
|
|
1125
|
+
}
|
|
1126
|
+
let parsedSnapshot;
|
|
1127
|
+
try {
|
|
1128
|
+
parsedSnapshot = typeof snapshot === "string" ? JSON.parse(snapshot) : snapshot;
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
this.logger.debug("Failed to parse workflow snapshot for resume", { error, runId });
|
|
1131
|
+
throw new Error("Failed to parse workflow snapshot");
|
|
1132
|
+
}
|
|
1133
|
+
if (resumeContext) {
|
|
1134
|
+
parsedSnapshot.context.steps[stepId] = {
|
|
1135
|
+
status: "success",
|
|
1136
|
+
output: {
|
|
1137
|
+
...parsedSnapshot?.context?.steps?.[stepId]?.output || {},
|
|
1138
|
+
...resumeContext
|
|
1139
|
+
}
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
if (parsedSnapshot.children) {
|
|
1143
|
+
Object.entries(parsedSnapshot.children).forEach(([_childId, child]) => {
|
|
1144
|
+
if (child.snapshot?.input?.stepNode) {
|
|
1145
|
+
const stepDef = this.#makeStepDef(child.snapshot.input.stepNode.step.id);
|
|
1146
|
+
child.snapshot.input.stepNode.config = {
|
|
1147
|
+
...child.snapshot.input.stepNode.config,
|
|
1148
|
+
...stepDef
|
|
1149
|
+
};
|
|
1150
|
+
child.snapshot.input.context = parsedSnapshot.context;
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
const updateStepInHierarchy = (value, targetStepId) => {
|
|
1155
|
+
const result = {};
|
|
1156
|
+
for (const key of Object.keys(value)) {
|
|
1157
|
+
const currentValue = value[key];
|
|
1158
|
+
if (key === targetStepId) {
|
|
1159
|
+
result[key] = "pending";
|
|
1160
|
+
} else if (typeof currentValue === "object" && currentValue !== null) {
|
|
1161
|
+
result[key] = updateStepInHierarchy(currentValue, targetStepId);
|
|
1162
|
+
} else {
|
|
1163
|
+
result[key] = currentValue;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
return result;
|
|
1167
|
+
};
|
|
1168
|
+
parsedSnapshot.value = updateStepInHierarchy(parsedSnapshot.value, stepId);
|
|
1169
|
+
if (parsedSnapshot.context?.attempts) {
|
|
1170
|
+
parsedSnapshot.context.attempts[stepId] = this.#steps[stepId]?.retryConfig?.attempts || this.#retryConfig?.attempts || 3;
|
|
1171
|
+
}
|
|
1172
|
+
this.logger.debug("Resuming workflow with updated snapshot", {
|
|
1173
|
+
updatedSnapshot: parsedSnapshot,
|
|
1174
|
+
runId,
|
|
1175
|
+
stepId
|
|
1176
|
+
});
|
|
1177
|
+
return this.execute({
|
|
1178
|
+
snapshot: parsedSnapshot,
|
|
1179
|
+
runId,
|
|
1180
|
+
stepId
|
|
1181
|
+
});
|
|
1182
|
+
}
|
|
1183
|
+
__registerPrimitives(p) {
|
|
1184
|
+
if (p.telemetry) {
|
|
1185
|
+
this.__setTelemetry(p.telemetry);
|
|
1186
|
+
}
|
|
1187
|
+
if (p.logger) {
|
|
1188
|
+
this.__setLogger(p.logger);
|
|
1189
|
+
}
|
|
1190
|
+
this.#mastra = p;
|
|
1191
|
+
}
|
|
1192
|
+
get stepGraph() {
|
|
1193
|
+
return this.#stepGraph;
|
|
1194
|
+
}
|
|
1195
|
+
get stepSubscriberGraph() {
|
|
1196
|
+
return this.#stepSubscriberGraph;
|
|
1197
|
+
}
|
|
1198
|
+
get steps() {
|
|
1199
|
+
return this.#steps;
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
export { Step, Workflow, createStep, getStepResult, isErrorEvent, isTransitionEvent, isVariableReference };
|