@mastra/inngest 0.0.0-add-save-score-validation-on-stores-20250911031242 → 0.0.0-agent-error-handling-20251023180025

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/index.js CHANGED
@@ -1,17 +1,25 @@
1
1
  import { randomUUID } from 'crypto';
2
+ import { ReadableStream } from 'stream/web';
2
3
  import { subscribe } from '@inngest/realtime';
3
4
  import { wrapMastra, AISpanType } from '@mastra/core/ai-tracing';
4
5
  import { RuntimeContext } from '@mastra/core/di';
6
+ import { WorkflowRunOutput } from '@mastra/core/stream';
5
7
  import { ToolStream, Tool } from '@mastra/core/tools';
6
- import { Run, Workflow, DefaultExecutionEngine } from '@mastra/core/workflows';
8
+ import { Run, Workflow, DefaultExecutionEngine, getStepResult, validateStepInput } from '@mastra/core/workflows';
7
9
  import { EMITTER_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
10
+ import { NonRetriableError, RetryAfterError } from 'inngest';
8
11
  import { serve as serve$1 } from 'inngest/hono';
9
12
  import { z } from 'zod';
10
13
 
11
14
  // src/index.ts
12
- function serve({ mastra, inngest }) {
15
+ function serve({
16
+ mastra,
17
+ inngest,
18
+ functions: userFunctions = [],
19
+ registerOptions
20
+ }) {
13
21
  const wfs = mastra.getWorkflows();
14
- const functions = Array.from(
22
+ const workflowFunctions = Array.from(
15
23
  new Set(
16
24
  Object.values(wfs).flatMap((wf) => {
17
25
  if (wf instanceof InngestWorkflow) {
@@ -23,8 +31,9 @@ function serve({ mastra, inngest }) {
23
31
  )
24
32
  );
25
33
  return serve$1({
34
+ ...registerOptions,
26
35
  client: inngest,
27
- functions
36
+ functions: [...workflowFunctions, ...userFunctions]
28
37
  });
29
38
  }
30
39
  var InngestRun = class extends Run {
@@ -52,9 +61,15 @@ var InngestRun = class extends Run {
52
61
  await new Promise((resolve) => setTimeout(resolve, 1e3));
53
62
  runs = await this.getRuns(eventId);
54
63
  if (runs?.[0]?.status === "Failed") {
55
- console.log("run", runs?.[0]);
56
- throw new Error(`Function run ${runs?.[0]?.status}`);
57
- } else if (runs?.[0]?.status === "Cancelled") {
64
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
65
+ workflowName: this.workflowId,
66
+ runId: this.runId
67
+ });
68
+ return {
69
+ output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
70
+ };
71
+ }
72
+ if (runs?.[0]?.status === "Cancelled") {
58
73
  const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
59
74
  workflowName: this.workflowId,
60
75
  runId: this.runId
@@ -85,6 +100,7 @@ var InngestRun = class extends Run {
85
100
  await this.#mastra?.storage?.persistWorkflowSnapshot({
86
101
  workflowName: this.workflowId,
87
102
  runId: this.runId,
103
+ resourceId: this.resourceId,
88
104
  snapshot: {
89
105
  ...snapshot,
90
106
  status: "canceled"
@@ -93,11 +109,13 @@ var InngestRun = class extends Run {
93
109
  }
94
110
  }
95
111
  async start({
96
- inputData
112
+ inputData,
113
+ initialState
97
114
  }) {
98
115
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
99
116
  workflowName: this.workflowId,
100
117
  runId: this.runId,
118
+ resourceId: this.resourceId,
101
119
  snapshot: {
102
120
  runId: this.runId,
103
121
  serializedStepGraph: this.serializedStepGraph,
@@ -105,16 +123,21 @@ var InngestRun = class extends Run {
105
123
  context: {},
106
124
  activePaths: [],
107
125
  suspendedPaths: {},
126
+ resumeLabels: {},
108
127
  waitingPaths: {},
109
128
  timestamp: Date.now(),
110
129
  status: "running"
111
130
  }
112
131
  });
132
+ const inputDataToUse = await this._validateInput(inputData);
133
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
113
134
  const eventOutput = await this.inngest.send({
114
135
  name: `workflow.${this.workflowId}`,
115
136
  data: {
116
- inputData,
117
- runId: this.runId
137
+ inputData: inputDataToUse,
138
+ initialState: initialStateToUse,
139
+ runId: this.runId,
140
+ resourceId: this.resourceId
118
141
  }
119
142
  });
120
143
  const eventId = eventOutput.ids[0];
@@ -150,17 +173,20 @@ var InngestRun = class extends Run {
150
173
  workflowName: this.workflowId,
151
174
  runId: this.runId
152
175
  });
176
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
177
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
153
178
  const eventOutput = await this.inngest.send({
154
179
  name: `workflow.${this.workflowId}`,
155
180
  data: {
156
- inputData: params.resumeData,
181
+ inputData: resumeDataToUse,
182
+ initialState: snapshot?.value ?? {},
157
183
  runId: this.runId,
158
184
  workflowId: this.workflowId,
159
185
  stepResults: snapshot?.context,
160
186
  resume: {
161
187
  steps,
162
188
  stepResults: snapshot?.context,
163
- resumePayload: params.resumeData,
189
+ resumePayload: resumeDataToUse,
164
190
  // @ts-ignore
165
191
  resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
166
192
  }
@@ -200,35 +226,11 @@ var InngestRun = class extends Run {
200
226
  });
201
227
  };
202
228
  }
203
- stream({ inputData, runtimeContext } = {}) {
229
+ streamLegacy({ inputData, runtimeContext } = {}) {
204
230
  const { readable, writable } = new TransformStream();
205
- let currentToolData = void 0;
206
231
  const writer = writable.getWriter();
207
232
  const unwatch = this.watch(async (event) => {
208
- if (event.type === "workflow-agent-call-start") {
209
- currentToolData = {
210
- name: event.payload.name,
211
- args: event.payload.args
212
- };
213
- await writer.write({
214
- ...event.payload,
215
- type: "tool-call-streaming-start"
216
- });
217
- return;
218
- }
219
233
  try {
220
- if (event.type === "workflow-agent-call-finish") {
221
- return;
222
- } else if (!event.type.startsWith("workflow-")) {
223
- if (event.type === "text-delta") {
224
- await writer.write({
225
- type: "tool-call-delta",
226
- ...currentToolData ?? {},
227
- argsTextDelta: event.textDelta
228
- });
229
- }
230
- return;
231
- }
232
234
  const e = {
233
235
  ...event,
234
236
  type: event.type.replace("workflow-", "")
@@ -259,6 +261,58 @@ var InngestRun = class extends Run {
259
261
  getWorkflowState: () => this.executionResults
260
262
  };
261
263
  }
264
+ stream({
265
+ inputData,
266
+ runtimeContext,
267
+ closeOnSuspend = true
268
+ } = {}) {
269
+ const self = this;
270
+ let streamOutput;
271
+ const stream = new ReadableStream({
272
+ async start(controller) {
273
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
274
+ controller.enqueue({
275
+ type,
276
+ runId: self.runId,
277
+ from,
278
+ payload: {
279
+ stepName: payload.id,
280
+ ...payload
281
+ }
282
+ });
283
+ }, "watch-v2");
284
+ self.closeStreamAction = async () => {
285
+ unwatch();
286
+ try {
287
+ await controller.close();
288
+ } catch (err) {
289
+ console.error("Error closing stream:", err);
290
+ }
291
+ };
292
+ const executionResultsPromise = self.start({
293
+ inputData,
294
+ runtimeContext
295
+ });
296
+ const executionResults = await executionResultsPromise;
297
+ if (closeOnSuspend) {
298
+ self.closeStreamAction?.().catch(() => {
299
+ });
300
+ } else if (executionResults.status !== "suspended") {
301
+ self.closeStreamAction?.().catch(() => {
302
+ });
303
+ }
304
+ if (streamOutput) {
305
+ streamOutput.updateResults(executionResults);
306
+ }
307
+ }
308
+ });
309
+ streamOutput = new WorkflowRunOutput({
310
+ runId: this.runId,
311
+ workflowId: this.workflowId,
312
+ stream
313
+ });
314
+ return streamOutput;
315
+ }
262
316
  };
263
317
  var InngestWorkflow = class _InngestWorkflow extends Workflow {
264
318
  #mastra;
@@ -310,23 +364,14 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
310
364
  }
311
365
  }
312
366
  }
313
- createRun(options) {
314
- const runIdToUse = options?.runId || randomUUID();
315
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
316
- {
317
- workflowId: this.id,
318
- runId: runIdToUse,
319
- executionEngine: this.executionEngine,
320
- executionGraph: this.executionGraph,
321
- serializedStepGraph: this.serializedStepGraph,
322
- mastra: this.#mastra,
323
- retryConfig: this.retryConfig,
324
- cleanup: () => this.runs.delete(runIdToUse)
325
- },
326
- this.inngest
367
+ /**
368
+ * @deprecated Use createRunAsync() instead.
369
+ * @throws {Error} Always throws an error directing users to use createRunAsync()
370
+ */
371
+ createRun(_options) {
372
+ throw new Error(
373
+ "createRun() has been deprecated. Please use createRunAsync() instead.\n\nMigration guide:\n Before: const run = workflow.createRun();\n After: const run = await workflow.createRunAsync();\n\nNote: createRunAsync() is an async method, so make sure your calling function is async."
327
374
  );
328
- this.runs.set(runIdToUse, run);
329
- return run;
330
375
  }
331
376
  async createRunAsync(options) {
332
377
  const runIdToUse = options?.runId || randomUUID();
@@ -334,21 +379,28 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
334
379
  {
335
380
  workflowId: this.id,
336
381
  runId: runIdToUse,
382
+ resourceId: options?.resourceId,
337
383
  executionEngine: this.executionEngine,
338
384
  executionGraph: this.executionGraph,
339
385
  serializedStepGraph: this.serializedStepGraph,
340
386
  mastra: this.#mastra,
341
387
  retryConfig: this.retryConfig,
342
- cleanup: () => this.runs.delete(runIdToUse)
388
+ cleanup: () => this.runs.delete(runIdToUse),
389
+ workflowSteps: this.steps
343
390
  },
344
391
  this.inngest
345
392
  );
346
393
  this.runs.set(runIdToUse, run);
394
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
395
+ workflowStatus: run.workflowRunStatus,
396
+ stepResults: {}
397
+ });
347
398
  const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
348
- if (!workflowSnapshotInStorage) {
399
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
349
400
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
350
401
  workflowName: this.id,
351
402
  runId: runIdToUse,
403
+ resourceId: options?.resourceId,
352
404
  snapshot: {
353
405
  runId: runIdToUse,
354
406
  status: "pending",
@@ -358,6 +410,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
358
410
  waitingPaths: {},
359
411
  serializedStepGraph: this.serializedStepGraph,
360
412
  suspendedPaths: {},
413
+ resumeLabels: {},
361
414
  result: void 0,
362
415
  error: void 0,
363
416
  // @ts-ignore
@@ -382,7 +435,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
382
435
  },
383
436
  { event: `workflow.${this.id}` },
384
437
  async ({ event, step, attempt, publish }) => {
385
- let { inputData, runId, resume } = event.data;
438
+ let { inputData, initialState, runId, resourceId, resume, outputOptions } = event.data;
386
439
  if (!runId) {
387
440
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
388
441
  return randomUUID();
@@ -410,21 +463,32 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
410
463
  once: (_event, _callback) => {
411
464
  }
412
465
  };
413
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
466
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
414
467
  const result = await engine.execute({
415
468
  workflowId: this.id,
416
469
  runId,
470
+ resourceId,
417
471
  graph: this.executionGraph,
418
472
  serializedStepGraph: this.serializedStepGraph,
419
473
  input: inputData,
474
+ initialState,
420
475
  emitter,
421
476
  retryConfig: this.retryConfig,
422
477
  runtimeContext: new RuntimeContext(),
423
478
  // TODO
424
479
  resume,
425
480
  abortController: new AbortController(),
426
- currentSpan: void 0
481
+ currentSpan: void 0,
427
482
  // TODO: Pass actual parent AI span from workflow execution context
483
+ outputOptions
484
+ });
485
+ await step.run(`workflow.${this.id}.finalize`, async () => {
486
+ if (result.status === "failed") {
487
+ throw new NonRetriableError(`Workflow failed`, {
488
+ cause: result
489
+ });
490
+ }
491
+ return result;
428
492
  });
429
493
  return { result, runId };
430
494
  }
@@ -458,11 +522,10 @@ function createStep(params) {
458
522
  if (isAgent(params)) {
459
523
  return {
460
524
  id: params.name,
525
+ description: params.getDescription(),
461
526
  // @ts-ignore
462
527
  inputSchema: z.object({
463
528
  prompt: z.string()
464
- // resourceId: z.string().optional(),
465
- // threadId: z.string().optional(),
466
529
  }),
467
530
  // @ts-ignore
468
531
  outputSchema: z.object({
@@ -478,34 +541,66 @@ function createStep(params) {
478
541
  name: params.name,
479
542
  args: inputData
480
543
  };
481
- await emitter.emit("watch-v2", {
482
- type: "workflow-agent-call-start",
483
- payload: toolData
484
- });
485
- const { fullStream } = await params.stream(inputData.prompt, {
486
- // resourceId: inputData.resourceId,
487
- // threadId: inputData.threadId,
488
- runtimeContext,
489
- tracingContext,
490
- onFinish: (result) => {
491
- streamPromise.resolve(result.text);
492
- },
493
- abortSignal
494
- });
495
- if (abortSignal.aborted) {
496
- return abort();
497
- }
498
- for await (const chunk of fullStream) {
499
- await emitter.emit("watch-v2", chunk);
544
+ if ((await params.getLLM()).getModel().specificationVersion === `v2`) {
545
+ const { fullStream } = await params.stream(inputData.prompt, {
546
+ runtimeContext,
547
+ tracingContext,
548
+ onFinish: (result) => {
549
+ streamPromise.resolve(result.text);
550
+ },
551
+ abortSignal
552
+ });
553
+ if (abortSignal.aborted) {
554
+ return abort();
555
+ }
556
+ await emitter.emit("watch-v2", {
557
+ type: "tool-call-streaming-start",
558
+ ...toolData ?? {}
559
+ });
560
+ for await (const chunk of fullStream) {
561
+ if (chunk.type === "text-delta") {
562
+ await emitter.emit("watch-v2", {
563
+ type: "tool-call-delta",
564
+ ...toolData ?? {},
565
+ argsTextDelta: chunk.payload.text
566
+ });
567
+ }
568
+ }
569
+ } else {
570
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
571
+ runtimeContext,
572
+ tracingContext,
573
+ onFinish: (result) => {
574
+ streamPromise.resolve(result.text);
575
+ },
576
+ abortSignal
577
+ });
578
+ if (abortSignal.aborted) {
579
+ return abort();
580
+ }
581
+ await emitter.emit("watch-v2", {
582
+ type: "tool-call-streaming-start",
583
+ ...toolData ?? {}
584
+ });
585
+ for await (const chunk of fullStream) {
586
+ if (chunk.type === "text-delta") {
587
+ await emitter.emit("watch-v2", {
588
+ type: "tool-call-delta",
589
+ ...toolData ?? {},
590
+ argsTextDelta: chunk.textDelta
591
+ });
592
+ }
593
+ }
500
594
  }
501
595
  await emitter.emit("watch-v2", {
502
- type: "workflow-agent-call-finish",
503
- payload: toolData
596
+ type: "tool-call-streaming-finish",
597
+ ...toolData ?? {}
504
598
  });
505
599
  return {
506
600
  text: await streamPromise.promise
507
601
  };
508
- }
602
+ },
603
+ component: params.component
509
604
  };
510
605
  }
511
606
  if (isTool(params)) {
@@ -516,16 +611,20 @@ function createStep(params) {
516
611
  // TODO: tool probably should have strong id type
517
612
  // @ts-ignore
518
613
  id: params.id,
614
+ description: params.description,
519
615
  inputSchema: params.inputSchema,
520
616
  outputSchema: params.outputSchema,
521
- execute: async ({ inputData, mastra, runtimeContext, tracingContext }) => {
617
+ execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
522
618
  return params.execute({
523
619
  context: inputData,
524
620
  mastra: wrapMastra(mastra, tracingContext),
525
621
  runtimeContext,
526
- tracingContext
622
+ tracingContext,
623
+ suspend,
624
+ resumeData
527
625
  });
528
- }
626
+ },
627
+ component: "TOOL"
529
628
  };
530
629
  }
531
630
  return {
@@ -541,7 +640,10 @@ function createStep(params) {
541
640
  function init(inngest) {
542
641
  return {
543
642
  createWorkflow(params) {
544
- return new InngestWorkflow(params, inngest);
643
+ return new InngestWorkflow(
644
+ params,
645
+ inngest
646
+ );
545
647
  },
546
648
  createStep,
547
649
  cloneStep(step, opts) {
@@ -550,7 +652,11 @@ function init(inngest) {
550
652
  description: step.description,
551
653
  inputSchema: step.inputSchema,
552
654
  outputSchema: step.outputSchema,
553
- execute: step.execute
655
+ resumeSchema: step.resumeSchema,
656
+ suspendSchema: step.suspendSchema,
657
+ stateSchema: step.stateSchema,
658
+ execute: step.execute,
659
+ component: step.component
554
660
  };
555
661
  },
556
662
  cloneWorkflow(workflow, opts) {
@@ -570,8 +676,8 @@ function init(inngest) {
570
676
  var InngestExecutionEngine = class extends DefaultExecutionEngine {
571
677
  inngestStep;
572
678
  inngestAttempts;
573
- constructor(mastra, inngestStep, inngestAttempts = 0) {
574
- super({ mastra });
679
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
680
+ super({ mastra, options });
575
681
  this.inngestStep = inngestStep;
576
682
  this.inngestAttempts = inngestAttempts;
577
683
  }
@@ -634,7 +740,7 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
634
740
  });
635
741
  const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
636
742
  if (stepResult?.status === "suspended") {
637
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
743
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
638
744
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
639
745
  }
640
746
  return [];
@@ -667,7 +773,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
667
773
  attributes: {
668
774
  durationMs: duration,
669
775
  sleepType: fn ? "dynamic" : "fixed"
670
- }
776
+ },
777
+ tracingPolicy: this.options?.tracingPolicy
671
778
  });
672
779
  if (fn) {
673
780
  const stepCallId = randomUUID();
@@ -678,21 +785,16 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
678
785
  mastra: this.mastra,
679
786
  runtimeContext,
680
787
  inputData: prevOutput,
788
+ state: executionContext.state,
789
+ setState: (state) => {
790
+ executionContext.state = state;
791
+ },
681
792
  runCount: -1,
682
793
  tracingContext: {
683
794
  currentSpan: sleepSpan
684
795
  },
685
796
  getInitData: () => stepResults?.input,
686
- getStepResult: (step) => {
687
- if (!step?.id) {
688
- return null;
689
- }
690
- const result = stepResults[step.id];
691
- if (result?.status === "success") {
692
- return result.output;
693
- }
694
- return null;
695
- },
797
+ getStepResult: getStepResult.bind(this, stepResults),
696
798
  // TODO: this function shouldn't have suspend probably?
697
799
  suspend: async (_suspendPayload) => {
698
800
  },
@@ -752,7 +854,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
752
854
  untilDate: date,
753
855
  durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
754
856
  sleepType: fn ? "dynamic" : "fixed"
755
- }
857
+ },
858
+ tracingPolicy: this.options?.tracingPolicy
756
859
  });
757
860
  if (fn) {
758
861
  date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
@@ -763,21 +866,16 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
763
866
  mastra: this.mastra,
764
867
  runtimeContext,
765
868
  inputData: prevOutput,
869
+ state: executionContext.state,
870
+ setState: (state) => {
871
+ executionContext.state = state;
872
+ },
766
873
  runCount: -1,
767
874
  tracingContext: {
768
875
  currentSpan: sleepUntilSpan
769
876
  },
770
877
  getInitData: () => stepResults?.input,
771
- getStepResult: (step) => {
772
- if (!step?.id) {
773
- return null;
774
- }
775
- const result = stepResults[step.id];
776
- if (result?.status === "success") {
777
- return result.output;
778
- }
779
- return null;
780
- },
878
+ getStepResult: getStepResult.bind(this, stepResults),
781
879
  // TODO: this function shouldn't have suspend probably?
782
880
  suspend: async (_suspendPayload) => {
783
881
  },
@@ -802,6 +900,9 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
802
900
  )
803
901
  });
804
902
  });
903
+ if (date && !(date instanceof Date)) {
904
+ date = new Date(date);
905
+ }
805
906
  const time = !date ? 0 : date.getTime() - Date.now();
806
907
  sleepUntilSpan?.update({
807
908
  attributes: {
@@ -850,7 +951,13 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
850
951
  input: prevOutput,
851
952
  attributes: {
852
953
  stepId: step.id
853
- }
954
+ },
955
+ tracingPolicy: this.options?.tracingPolicy
956
+ });
957
+ const { inputData, validationError } = await validateStepInput({
958
+ prevOutput,
959
+ step,
960
+ validateInputs: this.options?.validateInputs ?? false
854
961
  });
855
962
  const startedAt = await this.inngestStep.run(
856
963
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
@@ -882,7 +989,7 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
882
989
  payload: {
883
990
  id: step.id,
884
991
  status: "running",
885
- payload: prevOutput,
992
+ payload: inputData,
886
993
  startedAt: startedAt2
887
994
  }
888
995
  });
@@ -893,38 +1000,60 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
893
1000
  const isResume = !!resume?.steps?.length;
894
1001
  let result;
895
1002
  let runId;
896
- if (isResume) {
897
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
898
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
899
- workflowName: step.id,
900
- runId
901
- });
902
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
903
- function: step.getFunction(),
904
- data: {
905
- inputData: prevOutput,
906
- runId,
907
- resume: {
1003
+ try {
1004
+ if (isResume) {
1005
+ runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
1006
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1007
+ workflowName: step.id,
1008
+ runId
1009
+ });
1010
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1011
+ function: step.getFunction(),
1012
+ data: {
1013
+ inputData,
1014
+ initialState: executionContext.state ?? snapshot?.value ?? {},
908
1015
  runId,
909
- steps: resume.steps.slice(1),
910
- stepResults: snapshot?.context,
911
- resumePayload: resume.resumePayload,
912
- // @ts-ignore
913
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1016
+ resume: {
1017
+ runId,
1018
+ steps: resume.steps.slice(1),
1019
+ stepResults: snapshot?.context,
1020
+ resumePayload: resume.resumePayload,
1021
+ // @ts-ignore
1022
+ resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1023
+ },
1024
+ outputOptions: { includeState: true }
914
1025
  }
915
- }
916
- });
917
- result = invokeResp.result;
918
- runId = invokeResp.runId;
919
- } else {
920
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
921
- function: step.getFunction(),
922
- data: {
923
- inputData: prevOutput
924
- }
925
- });
926
- result = invokeResp.result;
927
- runId = invokeResp.runId;
1026
+ });
1027
+ result = invokeResp.result;
1028
+ runId = invokeResp.runId;
1029
+ executionContext.state = invokeResp.result.state;
1030
+ } else {
1031
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1032
+ function: step.getFunction(),
1033
+ data: {
1034
+ inputData,
1035
+ initialState: executionContext.state ?? {},
1036
+ outputOptions: { includeState: true }
1037
+ }
1038
+ });
1039
+ result = invokeResp.result;
1040
+ runId = invokeResp.runId;
1041
+ executionContext.state = invokeResp.result.state;
1042
+ }
1043
+ } catch (e) {
1044
+ const errorCause = e?.cause;
1045
+ if (errorCause && typeof errorCause === "object") {
1046
+ result = errorCause;
1047
+ runId = errorCause.runId || randomUUID();
1048
+ } else {
1049
+ runId = randomUUID();
1050
+ result = {
1051
+ status: "failed",
1052
+ error: e instanceof Error ? e : new Error(String(e)),
1053
+ steps: {},
1054
+ input: inputData
1055
+ };
1056
+ }
928
1057
  }
929
1058
  const res = await this.inngestStep.run(
930
1059
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
@@ -963,7 +1092,7 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
963
1092
  return stepRes2?.status === "suspended";
964
1093
  });
965
1094
  for (const [stepName, stepResult] of suspendedSteps) {
966
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
1095
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
967
1096
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
968
1097
  await emitter.emit("watch", {
969
1098
  type: "watch",
@@ -971,7 +1100,11 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
971
1100
  currentStep: {
972
1101
  id: step.id,
973
1102
  status: "suspended",
974
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1103
+ payload: stepResult.payload,
1104
+ suspendPayload: {
1105
+ ...stepResult?.suspendPayload,
1106
+ __workflow_meta: { runId, path: suspendPath }
1107
+ }
975
1108
  },
976
1109
  workflowState: {
977
1110
  status: "running",
@@ -993,7 +1126,11 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
993
1126
  executionContext,
994
1127
  result: {
995
1128
  status: "suspended",
996
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1129
+ payload: stepResult.payload,
1130
+ suspendPayload: {
1131
+ ...stepResult?.suspendPayload,
1132
+ __workflow_meta: { runId, path: suspendPath }
1133
+ }
997
1134
  }
998
1135
  };
999
1136
  }
@@ -1058,141 +1195,182 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1058
1195
  }
1059
1196
  );
1060
1197
  Object.assign(executionContext, res.executionContext);
1061
- return res.result;
1198
+ return {
1199
+ ...res.result,
1200
+ startedAt,
1201
+ endedAt: Date.now(),
1202
+ payload: inputData,
1203
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1204
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1205
+ };
1062
1206
  }
1063
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1064
- let execResults;
1065
- let suspended;
1066
- let bailed;
1067
- try {
1068
- const result = await step.execute({
1069
- runId: executionContext.runId,
1070
- mastra: this.mastra,
1071
- runtimeContext,
1072
- writableStream,
1073
- inputData: prevOutput,
1074
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1075
- tracingContext: {
1076
- currentSpan: stepAISpan
1077
- },
1078
- getInitData: () => stepResults?.input,
1079
- getStepResult: (step2) => {
1080
- const result2 = stepResults[step2.id];
1081
- if (result2?.status === "success") {
1082
- return result2.output;
1207
+ let stepRes;
1208
+ try {
1209
+ stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1210
+ let execResults;
1211
+ let suspended;
1212
+ let bailed;
1213
+ try {
1214
+ if (validationError) {
1215
+ throw validationError;
1216
+ }
1217
+ const result = await step.execute({
1218
+ runId: executionContext.runId,
1219
+ mastra: this.mastra,
1220
+ runtimeContext,
1221
+ writableStream,
1222
+ state: executionContext?.state ?? {},
1223
+ setState: (state) => {
1224
+ executionContext.state = state;
1225
+ },
1226
+ inputData,
1227
+ resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1228
+ tracingContext: {
1229
+ currentSpan: stepAISpan
1230
+ },
1231
+ getInitData: () => stepResults?.input,
1232
+ getStepResult: getStepResult.bind(this, stepResults),
1233
+ suspend: async (suspendPayload, suspendOptions) => {
1234
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1235
+ if (suspendOptions?.resumeLabel) {
1236
+ const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1237
+ for (const label of resumeLabel) {
1238
+ executionContext.resumeLabels[label] = {
1239
+ stepId: step.id,
1240
+ foreachIndex: executionContext.foreachIndex
1241
+ };
1242
+ }
1243
+ }
1244
+ suspended = { payload: suspendPayload };
1245
+ },
1246
+ bail: (result2) => {
1247
+ bailed = { payload: result2 };
1248
+ },
1249
+ resume: {
1250
+ steps: resume?.steps?.slice(1) || [],
1251
+ resumePayload: resume?.resumePayload,
1252
+ // @ts-ignore
1253
+ runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
1254
+ },
1255
+ [EMITTER_SYMBOL]: emitter,
1256
+ engine: {
1257
+ step: this.inngestStep
1258
+ },
1259
+ abortSignal: abortController.signal
1260
+ });
1261
+ const endedAt = Date.now();
1262
+ execResults = {
1263
+ status: "success",
1264
+ output: result,
1265
+ startedAt,
1266
+ endedAt,
1267
+ payload: inputData,
1268
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1269
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1270
+ };
1271
+ } catch (e) {
1272
+ const stepFailure = {
1273
+ status: "failed",
1274
+ payload: inputData,
1275
+ error: e instanceof Error ? e.message : String(e),
1276
+ endedAt: Date.now(),
1277
+ startedAt,
1278
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1279
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1280
+ };
1281
+ execResults = stepFailure;
1282
+ const fallbackErrorMessage = `Step ${step.id} failed`;
1283
+ stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1284
+ throw new RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1285
+ cause: execResults
1286
+ });
1287
+ }
1288
+ if (suspended) {
1289
+ execResults = {
1290
+ status: "suspended",
1291
+ suspendPayload: suspended.payload,
1292
+ payload: inputData,
1293
+ suspendedAt: Date.now(),
1294
+ startedAt,
1295
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1296
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1297
+ };
1298
+ } else if (bailed) {
1299
+ execResults = {
1300
+ status: "bailed",
1301
+ output: bailed.payload,
1302
+ payload: inputData,
1303
+ endedAt: Date.now(),
1304
+ startedAt
1305
+ };
1306
+ }
1307
+ await emitter.emit("watch", {
1308
+ type: "watch",
1309
+ payload: {
1310
+ currentStep: {
1311
+ id: step.id,
1312
+ ...execResults
1313
+ },
1314
+ workflowState: {
1315
+ status: "running",
1316
+ steps: { ...stepResults, [step.id]: execResults },
1317
+ result: null,
1318
+ error: null
1083
1319
  }
1084
- return null;
1085
- },
1086
- suspend: async (suspendPayload) => {
1087
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1088
- suspended = { payload: suspendPayload };
1089
- },
1090
- bail: (result2) => {
1091
- bailed = { payload: result2 };
1092
- },
1093
- resume: {
1094
- steps: resume?.steps?.slice(1) || [],
1095
- resumePayload: resume?.resumePayload,
1096
- // @ts-ignore
1097
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1098
- },
1099
- [EMITTER_SYMBOL]: emitter,
1100
- engine: {
1101
- step: this.inngestStep
1102
1320
  },
1103
- abortSignal: abortController.signal
1321
+ eventTimestamp: Date.now()
1104
1322
  });
1105
- const endedAt = Date.now();
1106
- execResults = {
1107
- status: "success",
1108
- output: result,
1109
- startedAt,
1110
- endedAt,
1111
- payload: prevOutput,
1112
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1113
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1114
- };
1115
- } catch (e) {
1116
- execResults = {
1117
- status: "failed",
1118
- payload: prevOutput,
1119
- error: e instanceof Error ? e.message : String(e),
1120
- endedAt: Date.now(),
1121
- startedAt,
1122
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1123
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1124
- };
1125
- }
1126
- if (suspended) {
1127
- execResults = {
1128
- status: "suspended",
1129
- suspendedPayload: suspended.payload,
1130
- payload: prevOutput,
1131
- suspendedAt: Date.now(),
1132
- startedAt,
1133
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1134
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1135
- };
1136
- } else if (bailed) {
1137
- execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
1138
- }
1139
- if (execResults.status === "failed") {
1140
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1141
- const error = new Error(execResults.error);
1142
- stepAISpan?.error({ error });
1143
- throw error;
1323
+ if (execResults.status === "suspended") {
1324
+ await emitter.emit("watch-v2", {
1325
+ type: "workflow-step-suspended",
1326
+ payload: {
1327
+ id: step.id,
1328
+ ...execResults
1329
+ }
1330
+ });
1331
+ } else {
1332
+ await emitter.emit("watch-v2", {
1333
+ type: "workflow-step-result",
1334
+ payload: {
1335
+ id: step.id,
1336
+ ...execResults
1337
+ }
1338
+ });
1339
+ await emitter.emit("watch-v2", {
1340
+ type: "workflow-step-finish",
1341
+ payload: {
1342
+ id: step.id,
1343
+ metadata: {}
1344
+ }
1345
+ });
1144
1346
  }
1145
- }
1146
- await emitter.emit("watch", {
1147
- type: "watch",
1148
- payload: {
1149
- currentStep: {
1150
- id: step.id,
1151
- ...execResults
1152
- },
1153
- workflowState: {
1154
- status: "running",
1155
- steps: { ...stepResults, [step.id]: execResults },
1156
- result: null,
1157
- error: null
1158
- }
1159
- },
1160
- eventTimestamp: Date.now()
1347
+ stepAISpan?.end({ output: execResults });
1348
+ return { result: execResults, executionContext, stepResults };
1161
1349
  });
1162
- if (execResults.status === "suspended") {
1163
- await emitter.emit("watch-v2", {
1164
- type: "workflow-step-suspended",
1165
- payload: {
1166
- id: step.id,
1167
- ...execResults
1168
- }
1169
- });
1170
- } else {
1171
- await emitter.emit("watch-v2", {
1172
- type: "workflow-step-result",
1173
- payload: {
1174
- id: step.id,
1175
- ...execResults
1176
- }
1177
- });
1178
- await emitter.emit("watch-v2", {
1179
- type: "workflow-step-finish",
1180
- payload: {
1181
- id: step.id,
1182
- metadata: {}
1183
- }
1184
- });
1185
- }
1186
- stepAISpan?.end({ output: execResults });
1187
- return { result: execResults, executionContext, stepResults };
1188
- });
1189
- if (disableScorers !== false) {
1350
+ } catch (e) {
1351
+ const stepFailure = e instanceof Error ? e?.cause : {
1352
+ status: "failed",
1353
+ error: e instanceof Error ? e.message : String(e),
1354
+ payload: inputData,
1355
+ startedAt,
1356
+ endedAt: Date.now()
1357
+ };
1358
+ stepRes = {
1359
+ result: stepFailure,
1360
+ executionContext,
1361
+ stepResults: {
1362
+ ...stepResults,
1363
+ [step.id]: stepFailure
1364
+ }
1365
+ };
1366
+ }
1367
+ if (disableScorers !== false && stepRes.result.status === "success") {
1190
1368
  await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1191
1369
  if (step.scorers) {
1192
1370
  await this.runScorers({
1193
1371
  scorers: step.scorers,
1194
1372
  runId: executionContext.runId,
1195
- input: prevOutput,
1373
+ input: inputData,
1196
1374
  output: stepRes.result,
1197
1375
  workflowId: executionContext.workflowId,
1198
1376
  stepId: step.id,
@@ -1205,12 +1383,14 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1205
1383
  }
1206
1384
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1207
1385
  Object.assign(stepResults, stepRes.stepResults);
1386
+ executionContext.state = stepRes.executionContext.state;
1208
1387
  return stepRes.result;
1209
1388
  }
1210
1389
  async persistStepUpdate({
1211
1390
  workflowId,
1212
1391
  runId,
1213
1392
  stepResults,
1393
+ resourceId,
1214
1394
  executionContext,
1215
1395
  serializedStepGraph,
1216
1396
  workflowStatus,
@@ -1220,15 +1400,21 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1220
1400
  await this.inngestStep.run(
1221
1401
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1222
1402
  async () => {
1403
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1404
+ if (!shouldPersistSnapshot) {
1405
+ return;
1406
+ }
1223
1407
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1224
1408
  workflowName: workflowId,
1225
1409
  runId,
1410
+ resourceId,
1226
1411
  snapshot: {
1227
1412
  runId,
1228
- value: {},
1413
+ value: executionContext.state,
1229
1414
  context: stepResults,
1230
1415
  activePaths: [],
1231
1416
  suspendedPaths: executionContext.suspendedPaths,
1417
+ resumeLabels: executionContext.resumeLabels,
1232
1418
  waitingPaths: {},
1233
1419
  serializedStepGraph,
1234
1420
  status: workflowStatus,
@@ -1264,7 +1450,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1264
1450
  input: prevOutput,
1265
1451
  attributes: {
1266
1452
  conditionCount: entry.conditions.length
1267
- }
1453
+ },
1454
+ tracingPolicy: this.options?.tracingPolicy
1268
1455
  });
1269
1456
  let execResults;
1270
1457
  const truthyIndexes = (await Promise.all(
@@ -1276,7 +1463,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1276
1463
  input: prevOutput,
1277
1464
  attributes: {
1278
1465
  conditionIndex: index
1279
- }
1466
+ },
1467
+ tracingPolicy: this.options?.tracingPolicy
1280
1468
  });
1281
1469
  try {
1282
1470
  const result = await cond({
@@ -1286,20 +1474,15 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1286
1474
  runtimeContext,
1287
1475
  runCount: -1,
1288
1476
  inputData: prevOutput,
1477
+ state: executionContext.state,
1478
+ setState: (state) => {
1479
+ executionContext.state = state;
1480
+ },
1289
1481
  tracingContext: {
1290
1482
  currentSpan: evalSpan
1291
1483
  },
1292
1484
  getInitData: () => stepResults?.input,
1293
- getStepResult: (step) => {
1294
- if (!step?.id) {
1295
- return null;
1296
- }
1297
- const result2 = stepResults[step.id];
1298
- if (result2?.status === "success") {
1299
- return result2.output;
1300
- }
1301
- return null;
1302
- },
1485
+ getStepResult: getStepResult.bind(this, stepResults),
1303
1486
  // TODO: this function shouldn't have suspend probably?
1304
1487
  suspend: async (_suspendPayload) => {
1305
1488
  },
@@ -1366,8 +1549,10 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1366
1549
  runId,
1367
1550
  executionPath: [...executionContext.executionPath, index],
1368
1551
  suspendedPaths: executionContext.suspendedPaths,
1552
+ resumeLabels: executionContext.resumeLabels,
1369
1553
  retryConfig: executionContext.retryConfig,
1370
- executionSpan: executionContext.executionSpan
1554
+ executionSpan: executionContext.executionSpan,
1555
+ state: executionContext.state
1371
1556
  },
1372
1557
  emitter,
1373
1558
  abortController,
@@ -1385,7 +1570,7 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
1385
1570
  if (hasFailed) {
1386
1571
  execResults = { status: "failed", error: hasFailed.result.error };
1387
1572
  } else if (hasSuspended) {
1388
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1573
+ execResults = { status: "suspended", suspendPayload: hasSuspended.result.suspendPayload };
1389
1574
  } else {
1390
1575
  execResults = {
1391
1576
  status: "success",