@mastra/inngest 0.0.0-roamin-openaivoice-speak-options-passing-20250926163614 → 0.0.0-safe-stringify-telemetry-20251205024938

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.cjs CHANGED
@@ -1,12 +1,15 @@
1
1
  'use strict';
2
2
 
3
3
  var crypto = require('crypto');
4
+ var web = require('stream/web');
4
5
  var realtime = require('@inngest/realtime');
5
6
  var aiTracing = require('@mastra/core/ai-tracing');
6
7
  var di = require('@mastra/core/di');
8
+ var stream = require('@mastra/core/stream');
7
9
  var tools = require('@mastra/core/tools');
8
10
  var workflows = require('@mastra/core/workflows');
9
11
  var _constants = require('@mastra/core/workflows/_constants');
12
+ var inngest = require('inngest');
10
13
  var hono = require('inngest/hono');
11
14
  var zod = require('zod');
12
15
 
@@ -60,8 +63,15 @@ var InngestRun = class extends workflows.Run {
60
63
  await new Promise((resolve) => setTimeout(resolve, 1e3));
61
64
  runs = await this.getRuns(eventId);
62
65
  if (runs?.[0]?.status === "Failed") {
63
- throw new Error(`Function run ${runs?.[0]?.status}`);
64
- } else if (runs?.[0]?.status === "Cancelled") {
66
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
67
+ workflowName: this.workflowId,
68
+ runId: this.runId
69
+ });
70
+ return {
71
+ output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
72
+ };
73
+ }
74
+ if (runs?.[0]?.status === "Cancelled") {
65
75
  const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
66
76
  workflowName: this.workflowId,
67
77
  runId: this.runId
@@ -95,13 +105,15 @@ var InngestRun = class extends workflows.Run {
95
105
  resourceId: this.resourceId,
96
106
  snapshot: {
97
107
  ...snapshot,
98
- status: "canceled"
108
+ status: "canceled",
109
+ value: snapshot.value
99
110
  }
100
111
  });
101
112
  }
102
113
  }
103
114
  async start({
104
- inputData
115
+ inputData,
116
+ initialState
105
117
  }) {
106
118
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
107
119
  workflowName: this.workflowId,
@@ -110,20 +122,24 @@ var InngestRun = class extends workflows.Run {
110
122
  snapshot: {
111
123
  runId: this.runId,
112
124
  serializedStepGraph: this.serializedStepGraph,
125
+ status: "running",
113
126
  value: {},
114
127
  context: {},
115
128
  activePaths: [],
116
129
  suspendedPaths: {},
130
+ activeStepsPath: {},
131
+ resumeLabels: {},
117
132
  waitingPaths: {},
118
- timestamp: Date.now(),
119
- status: "running"
133
+ timestamp: Date.now()
120
134
  }
121
135
  });
122
136
  const inputDataToUse = await this._validateInput(inputData);
137
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
123
138
  const eventOutput = await this.inngest.send({
124
139
  name: `workflow.${this.workflowId}`,
125
140
  data: {
126
141
  inputData: inputDataToUse,
142
+ initialState: initialStateToUse,
127
143
  runId: this.runId,
128
144
  resourceId: this.resourceId
129
145
  }
@@ -154,10 +170,16 @@ var InngestRun = class extends workflows.Run {
154
170
  return p;
155
171
  }
156
172
  async _resume(params) {
157
- const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
158
- (step) => typeof step === "string" ? step : step?.id
159
- );
160
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
173
+ const storage = this.#mastra?.getStorage();
174
+ let steps = [];
175
+ if (typeof params.step === "string") {
176
+ steps = params.step.split(".");
177
+ } else {
178
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
179
+ (step) => typeof step === "string" ? step : step?.id
180
+ );
181
+ }
182
+ const snapshot = await storage?.loadWorkflowSnapshot({
161
183
  workflowName: this.workflowId,
162
184
  runId: this.runId
163
185
  });
@@ -167,6 +189,7 @@ var InngestRun = class extends workflows.Run {
167
189
  name: `workflow.${this.workflowId}`,
168
190
  data: {
169
191
  inputData: resumeDataToUse,
192
+ initialState: snapshot?.value ?? {},
170
193
  runId: this.runId,
171
194
  workflowId: this.workflowId,
172
195
  stepResults: snapshot?.context,
@@ -213,7 +236,98 @@ var InngestRun = class extends workflows.Run {
213
236
  });
214
237
  };
215
238
  }
216
- stream({ inputData, runtimeContext } = {}) {
239
+ async timeTravel(params) {
240
+ const p = this._timeTravel(params).then((result) => {
241
+ if (result.status !== "suspended") {
242
+ this.closeStreamAction?.().catch(() => {
243
+ });
244
+ }
245
+ return result;
246
+ });
247
+ this.executionResults = p;
248
+ return p;
249
+ }
250
+ async _timeTravel(params) {
251
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
252
+ throw new Error("Step is required and must be a valid step or array of steps");
253
+ }
254
+ let steps = [];
255
+ if (typeof params.step === "string") {
256
+ steps = params.step.split(".");
257
+ } else {
258
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
259
+ (step) => typeof step === "string" ? step : step?.id
260
+ );
261
+ }
262
+ if (steps.length === 0) {
263
+ throw new Error("No steps provided to timeTravel");
264
+ }
265
+ const storage = this.#mastra?.getStorage();
266
+ const snapshot = await storage?.loadWorkflowSnapshot({
267
+ workflowName: this.workflowId,
268
+ runId: this.runId
269
+ });
270
+ if (!snapshot) {
271
+ await storage?.persistWorkflowSnapshot({
272
+ workflowName: this.workflowId,
273
+ runId: this.runId,
274
+ resourceId: this.resourceId,
275
+ snapshot: {
276
+ runId: this.runId,
277
+ serializedStepGraph: this.serializedStepGraph,
278
+ status: "pending",
279
+ value: {},
280
+ context: {},
281
+ activePaths: [],
282
+ suspendedPaths: {},
283
+ activeStepsPath: {},
284
+ resumeLabels: {},
285
+ waitingPaths: {},
286
+ timestamp: Date.now()
287
+ }
288
+ });
289
+ }
290
+ if (snapshot?.status === "running") {
291
+ throw new Error("This workflow run is still running, cannot time travel");
292
+ }
293
+ let inputDataToUse = params.inputData;
294
+ if (inputDataToUse && steps.length === 1) {
295
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
296
+ }
297
+ const timeTravelData = workflows.createTimeTravelExecutionParams({
298
+ steps,
299
+ inputData: inputDataToUse,
300
+ resumeData: params.resumeData,
301
+ context: params.context,
302
+ nestedStepsContext: params.nestedStepsContext,
303
+ snapshot: snapshot ?? { context: {} },
304
+ graph: this.executionGraph,
305
+ initialState: params.initialState
306
+ });
307
+ const eventOutput = await this.inngest.send({
308
+ name: `workflow.${this.workflowId}`,
309
+ data: {
310
+ initialState: timeTravelData.state,
311
+ runId: this.runId,
312
+ workflowId: this.workflowId,
313
+ stepResults: timeTravelData.stepResults,
314
+ timeTravel: timeTravelData,
315
+ tracingOptions: params.tracingOptions,
316
+ outputOptions: params.outputOptions
317
+ }
318
+ });
319
+ const eventId = eventOutput.ids[0];
320
+ if (!eventId) {
321
+ throw new Error("Event ID is not set");
322
+ }
323
+ const runOutput = await this.getRunOutput(eventId);
324
+ const result = runOutput?.output?.result;
325
+ if (result.status === "failed") {
326
+ result.error = new Error(result.error);
327
+ }
328
+ return result;
329
+ }
330
+ streamLegacy({ inputData, runtimeContext } = {}) {
217
331
  const { readable, writable } = new TransformStream();
218
332
  const writer = writable.getWriter();
219
333
  const unwatch = this.watch(async (event) => {
@@ -248,6 +362,126 @@ var InngestRun = class extends workflows.Run {
248
362
  getWorkflowState: () => this.executionResults
249
363
  };
250
364
  }
365
+ stream({
366
+ inputData,
367
+ runtimeContext,
368
+ closeOnSuspend = true
369
+ } = {}) {
370
+ const self = this;
371
+ let streamOutput;
372
+ const stream$1 = new web.ReadableStream({
373
+ async start(controller) {
374
+ const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
375
+ controller.enqueue({
376
+ type,
377
+ runId: self.runId,
378
+ from,
379
+ payload: {
380
+ stepName: payload.id,
381
+ ...payload
382
+ }
383
+ });
384
+ }, "watch-v2");
385
+ self.closeStreamAction = async () => {
386
+ unwatch();
387
+ try {
388
+ await controller.close();
389
+ } catch (err) {
390
+ console.error("Error closing stream:", err);
391
+ }
392
+ };
393
+ const executionResultsPromise = self.start({
394
+ inputData,
395
+ runtimeContext
396
+ });
397
+ const executionResults = await executionResultsPromise;
398
+ if (closeOnSuspend) {
399
+ self.closeStreamAction?.().catch(() => {
400
+ });
401
+ } else if (executionResults.status !== "suspended") {
402
+ self.closeStreamAction?.().catch(() => {
403
+ });
404
+ }
405
+ if (streamOutput) {
406
+ streamOutput.updateResults(executionResults);
407
+ }
408
+ }
409
+ });
410
+ streamOutput = new stream.WorkflowRunOutput({
411
+ runId: this.runId,
412
+ workflowId: this.workflowId,
413
+ stream: stream$1
414
+ });
415
+ return streamOutput;
416
+ }
417
+ timeTravelStream({
418
+ inputData,
419
+ resumeData,
420
+ initialState,
421
+ step,
422
+ context,
423
+ nestedStepsContext,
424
+ runtimeContext,
425
+ tracingOptions,
426
+ outputOptions
427
+ }) {
428
+ const self = this;
429
+ let streamOutput;
430
+ const stream$1 = new web.ReadableStream({
431
+ async start(controller) {
432
+ const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
433
+ controller.enqueue({
434
+ type,
435
+ runId: self.runId,
436
+ from,
437
+ payload: {
438
+ stepName: payload?.id,
439
+ ...payload
440
+ }
441
+ });
442
+ }, "watch-v2");
443
+ self.closeStreamAction = async () => {
444
+ unwatch();
445
+ try {
446
+ await controller.close();
447
+ } catch (err) {
448
+ console.error("Error closing stream:", err);
449
+ }
450
+ };
451
+ const executionResultsPromise = self._timeTravel({
452
+ inputData,
453
+ step,
454
+ context,
455
+ nestedStepsContext,
456
+ resumeData,
457
+ initialState,
458
+ runtimeContext,
459
+ tracingOptions,
460
+ outputOptions
461
+ });
462
+ self.executionResults = executionResultsPromise;
463
+ let executionResults;
464
+ try {
465
+ executionResults = await executionResultsPromise;
466
+ self.closeStreamAction?.().catch(() => {
467
+ });
468
+ if (streamOutput) {
469
+ streamOutput.updateResults(executionResults);
470
+ }
471
+ } catch (err) {
472
+ streamOutput?.rejectResults(err);
473
+ self.closeStreamAction?.().catch(() => {
474
+ });
475
+ }
476
+ }
477
+ });
478
+ streamOutput = new stream.WorkflowRunOutput({
479
+ runId: this.runId,
480
+ workflowId: this.workflowId,
481
+ stream: stream$1
482
+ });
483
+ return streamOutput;
484
+ }
251
485
  };
252
486
  var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
253
487
  #mastra;
@@ -257,6 +491,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
257
491
  constructor(params, inngest) {
258
492
  const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
259
493
  super(workflowParams);
494
+ this.engineType = "inngest";
260
495
  const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
261
496
  ([_, value]) => value !== void 0
262
497
  );
@@ -321,13 +556,19 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
321
556
  mastra: this.#mastra,
322
557
  retryConfig: this.retryConfig,
323
558
  cleanup: () => this.runs.delete(runIdToUse),
324
- workflowSteps: this.steps
559
+ workflowSteps: this.steps,
560
+ workflowEngineType: this.engineType,
561
+ validateInputs: this.options.validateInputs
325
562
  },
326
563
  this.inngest
327
564
  );
328
565
  this.runs.set(runIdToUse, run);
566
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
567
+ workflowStatus: run.workflowRunStatus,
568
+ stepResults: {}
569
+ });
329
570
  const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
330
- if (!workflowSnapshotInStorage) {
571
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
331
572
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
332
573
  workflowName: this.id,
333
574
  runId: runIdToUse,
@@ -338,9 +579,11 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
338
579
  value: {},
339
580
  context: {},
340
581
  activePaths: [],
582
+ activeStepsPath: {},
341
583
  waitingPaths: {},
342
584
  serializedStepGraph: this.serializedStepGraph,
343
585
  suspendedPaths: {},
586
+ resumeLabels: {},
344
587
  result: void 0,
345
588
  error: void 0,
346
589
  // @ts-ignore
@@ -365,7 +608,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
365
608
  },
366
609
  { event: `workflow.${this.id}` },
367
610
  async ({ event, step, attempt, publish }) => {
368
- let { inputData, runId, resourceId, resume } = event.data;
611
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, timeTravel } = event.data;
369
612
  if (!runId) {
370
613
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
371
614
  return crypto.randomUUID();
@@ -393,7 +636,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
393
636
  once: (_event, _callback) => {
394
637
  }
395
638
  };
396
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
639
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
397
640
  const result = await engine.execute({
398
641
  workflowId: this.id,
399
642
  runId,
@@ -401,14 +644,25 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
401
644
  graph: this.executionGraph,
402
645
  serializedStepGraph: this.serializedStepGraph,
403
646
  input: inputData,
647
+ initialState,
404
648
  emitter,
405
649
  retryConfig: this.retryConfig,
406
650
  runtimeContext: new di.RuntimeContext(),
407
651
  // TODO
408
652
  resume,
653
+ timeTravel,
409
654
  abortController: new AbortController(),
410
- currentSpan: void 0
655
+ currentSpan: void 0,
411
656
  // TODO: Pass actual parent AI span from workflow execution context
657
+ outputOptions
658
+ });
659
+ await step.run(`workflow.${this.id}.finalize`, async () => {
660
+ if (result.status === "failed") {
661
+ throw new inngest.NonRetriableError(`Workflow failed`, {
662
+ cause: result
663
+ });
664
+ }
665
+ return result;
412
666
  });
413
667
  return { result, runId };
414
668
  }
@@ -461,28 +715,55 @@ function createStep(params) {
461
715
  name: params.name,
462
716
  args: inputData
463
717
  };
464
- const { fullStream } = await params.stream(inputData.prompt, {
465
- runtimeContext,
466
- tracingContext,
467
- onFinish: (result) => {
468
- streamPromise.resolve(result.text);
469
- },
470
- abortSignal
471
- });
472
- if (abortSignal.aborted) {
473
- return abort();
474
- }
475
- await emitter.emit("watch-v2", {
476
- type: "tool-call-streaming-start",
477
- ...toolData ?? {}
478
- });
479
- for await (const chunk of fullStream) {
480
- if (chunk.type === "text-delta") {
481
- await emitter.emit("watch-v2", {
482
- type: "tool-call-delta",
483
- ...toolData ?? {},
484
- argsTextDelta: chunk.textDelta
485
- });
718
+ if ((await params.getLLM()).getModel().specificationVersion === `v2`) {
719
+ const { fullStream } = await params.stream(inputData.prompt, {
720
+ runtimeContext,
721
+ tracingContext,
722
+ onFinish: (result) => {
723
+ streamPromise.resolve(result.text);
724
+ },
725
+ abortSignal
726
+ });
727
+ if (abortSignal.aborted) {
728
+ return abort();
729
+ }
730
+ await emitter.emit("watch-v2", {
731
+ type: "tool-call-streaming-start",
732
+ ...toolData ?? {}
733
+ });
734
+ for await (const chunk of fullStream) {
735
+ if (chunk.type === "text-delta") {
736
+ await emitter.emit("watch-v2", {
737
+ type: "tool-call-delta",
738
+ ...toolData ?? {},
739
+ argsTextDelta: chunk.payload.text
740
+ });
741
+ }
742
+ }
743
+ } else {
744
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
745
+ runtimeContext,
746
+ tracingContext,
747
+ onFinish: (result) => {
748
+ streamPromise.resolve(result.text);
749
+ },
750
+ abortSignal
751
+ });
752
+ if (abortSignal.aborted) {
753
+ return abort();
754
+ }
755
+ await emitter.emit("watch-v2", {
756
+ type: "tool-call-streaming-start",
757
+ ...toolData ?? {}
758
+ });
759
+ for await (const chunk of fullStream) {
760
+ if (chunk.type === "text-delta") {
761
+ await emitter.emit("watch-v2", {
762
+ type: "tool-call-delta",
763
+ ...toolData ?? {},
764
+ argsTextDelta: chunk.textDelta
765
+ });
766
+ }
486
767
  }
487
768
  }
488
769
  await emitter.emit("watch-v2", {
@@ -533,7 +814,10 @@ function createStep(params) {
533
814
  function init(inngest) {
534
815
  return {
535
816
  createWorkflow(params) {
536
- return new InngestWorkflow(params, inngest);
817
+ return new InngestWorkflow(
818
+ params,
819
+ inngest
820
+ );
537
821
  },
538
822
  createStep,
539
823
  cloneStep(step, opts) {
@@ -542,6 +826,9 @@ function init(inngest) {
542
826
  description: step.description,
543
827
  inputSchema: step.inputSchema,
544
828
  outputSchema: step.outputSchema,
829
+ resumeSchema: step.resumeSchema,
830
+ suspendSchema: step.suspendSchema,
831
+ stateSchema: step.stateSchema,
545
832
  execute: step.execute,
546
833
  component: step.component
547
834
  };
@@ -627,7 +914,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
627
914
  });
628
915
  const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
629
916
  if (stepResult?.status === "suspended") {
630
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
917
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
631
918
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
632
919
  }
633
920
  return [];
@@ -672,6 +959,10 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
672
959
  mastra: this.mastra,
673
960
  runtimeContext,
674
961
  inputData: prevOutput,
962
+ state: executionContext.state,
963
+ setState: (state) => {
964
+ executionContext.state = state;
965
+ },
675
966
  runCount: -1,
676
967
  tracingContext: {
677
968
  currentSpan: sleepSpan
@@ -749,6 +1040,10 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
749
1040
  mastra: this.mastra,
750
1041
  runtimeContext,
751
1042
  inputData: prevOutput,
1043
+ state: executionContext.state,
1044
+ setState: (state) => {
1045
+ executionContext.state = state;
1046
+ },
752
1047
  runCount: -1,
753
1048
  tracingContext: {
754
1049
  currentSpan: sleepUntilSpan
@@ -816,6 +1111,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
816
1111
  stepResults,
817
1112
  executionContext,
818
1113
  resume,
1114
+ timeTravel,
819
1115
  prevOutput,
820
1116
  emitter,
821
1117
  abortController,
@@ -879,38 +1175,87 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
879
1175
  const isResume = !!resume?.steps?.length;
880
1176
  let result;
881
1177
  let runId;
882
- if (isResume) {
883
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? crypto.randomUUID();
884
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
885
- workflowName: step.id,
886
- runId
887
- });
888
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
889
- function: step.getFunction(),
890
- data: {
891
- inputData,
892
- runId,
893
- resume: {
1178
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
1179
+ try {
1180
+ if (isResume) {
1181
+ runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
1182
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1183
+ workflowName: step.id,
1184
+ runId
1185
+ });
1186
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1187
+ function: step.getFunction(),
1188
+ data: {
1189
+ inputData,
1190
+ initialState: executionContext.state ?? snapshot?.value ?? {},
894
1191
  runId,
895
- steps: resume.steps.slice(1),
896
- stepResults: snapshot?.context,
897
- resumePayload: resume.resumePayload,
898
- // @ts-ignore
899
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1192
+ resume: {
1193
+ runId,
1194
+ steps: resume.steps.slice(1),
1195
+ stepResults: snapshot?.context,
1196
+ resumePayload: resume.resumePayload,
1197
+ // @ts-ignore
1198
+ resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1199
+ },
1200
+ outputOptions: { includeState: true }
900
1201
  }
901
- }
902
- });
903
- result = invokeResp.result;
904
- runId = invokeResp.runId;
905
- } else {
906
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
907
- function: step.getFunction(),
908
- data: {
909
- inputData
910
- }
911
- });
912
- result = invokeResp.result;
913
- runId = invokeResp.runId;
1202
+ });
1203
+ result = invokeResp.result;
1204
+ runId = invokeResp.runId;
1205
+ executionContext.state = invokeResp.result.state;
1206
+ } else if (isTimeTravel) {
1207
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1208
+ workflowName: step.id,
1209
+ runId: executionContext.runId
1210
+ }) ?? { context: {} };
1211
+ const timeTravelParams = workflows.createTimeTravelExecutionParams({
1212
+ steps: timeTravel.steps.slice(1),
1213
+ inputData: timeTravel.inputData,
1214
+ resumeData: timeTravel.resumeData,
1215
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
1216
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
1217
+ snapshot,
1218
+ graph: step.buildExecutionGraph()
1219
+ });
1220
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1221
+ function: step.getFunction(),
1222
+ data: {
1223
+ timeTravel: timeTravelParams,
1224
+ initialState: executionContext.state ?? {},
1225
+ runId: executionContext.runId,
1226
+ outputOptions: { includeState: true }
1227
+ }
1228
+ });
1229
+ result = invokeResp.result;
1230
+ runId = invokeResp.runId;
1231
+ executionContext.state = invokeResp.result.state;
1232
+ } else {
1233
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1234
+ function: step.getFunction(),
1235
+ data: {
1236
+ inputData,
1237
+ initialState: executionContext.state ?? {},
1238
+ outputOptions: { includeState: true }
1239
+ }
1240
+ });
1241
+ result = invokeResp.result;
1242
+ runId = invokeResp.runId;
1243
+ executionContext.state = invokeResp.result.state;
1244
+ }
1245
+ } catch (e) {
1246
+ const errorCause = e?.cause;
1247
+ if (errorCause && typeof errorCause === "object") {
1248
+ result = errorCause;
1249
+ runId = errorCause.runId || crypto.randomUUID();
1250
+ } else {
1251
+ runId = crypto.randomUUID();
1252
+ result = {
1253
+ status: "failed",
1254
+ error: e instanceof Error ? e : new Error(String(e)),
1255
+ steps: {},
1256
+ input: inputData
1257
+ };
1258
+ }
914
1259
  }
915
1260
  const res = await this.inngestStep.run(
916
1261
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
@@ -944,12 +1289,12 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
944
1289
  });
945
1290
  return { executionContext, result: { status: "failed", error: result?.error } };
946
1291
  } else if (result.status === "suspended") {
947
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
948
- const stepRes2 = stepResult;
1292
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult2]) => {
1293
+ const stepRes2 = stepResult2;
949
1294
  return stepRes2?.status === "suspended";
950
1295
  });
951
- for (const [stepName, stepResult] of suspendedSteps) {
952
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
1296
+ for (const [stepName, stepResult2] of suspendedSteps) {
1297
+ const suspendPath = [stepName, ...stepResult2?.suspendPayload?.__workflow_meta?.path ?? []];
953
1298
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
954
1299
  await emitter.emit("watch", {
955
1300
  type: "watch",
@@ -957,7 +1302,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
957
1302
  currentStep: {
958
1303
  id: step.id,
959
1304
  status: "suspended",
960
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1305
+ payload: stepResult2.payload,
1306
+ suspendPayload: {
1307
+ ...stepResult2?.suspendPayload,
1308
+ __workflow_meta: { runId, path: suspendPath }
1309
+ }
961
1310
  },
962
1311
  workflowState: {
963
1312
  status: "running",
@@ -979,7 +1328,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
979
1328
  executionContext,
980
1329
  result: {
981
1330
  status: "suspended",
982
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1331
+ payload: stepResult2.payload,
1332
+ suspendPayload: {
1333
+ ...stepResult2?.suspendPayload,
1334
+ __workflow_meta: { runId, path: suspendPath }
1335
+ }
983
1336
  }
984
1337
  };
985
1338
  }
@@ -1044,132 +1397,206 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1044
1397
  }
1045
1398
  );
1046
1399
  Object.assign(executionContext, res.executionContext);
1047
- return res.result;
1400
+ const stepResult = {
1401
+ ...res.result,
1402
+ startedAt,
1403
+ endedAt: Date.now(),
1404
+ payload: inputData,
1405
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1406
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1407
+ };
1408
+ return { result: stepResult, executionContextState: executionContext.state };
1048
1409
  }
1049
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1050
- let execResults;
1051
- let suspended;
1052
- let bailed;
1053
- try {
1054
- if (validationError) {
1055
- throw validationError;
1410
+ let stepRes;
1411
+ try {
1412
+ stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1413
+ let execResults;
1414
+ let suspended;
1415
+ let bailed;
1416
+ const { resumeData: timeTravelResumeData, validationError: timeTravelResumeValidationError } = await workflows.validateStepResumeData({
1417
+ resumeData: timeTravel?.stepResults[step.id]?.status === "suspended" ? timeTravel?.resumeData : void 0,
1418
+ step
1419
+ });
1420
+ let resumeDataToUse;
1421
+ if (timeTravelResumeData && !timeTravelResumeValidationError) {
1422
+ resumeDataToUse = timeTravelResumeData;
1423
+ } else if (timeTravelResumeData && timeTravelResumeValidationError) {
1424
+ this.logger.warn("Time travel resume data validation failed", {
1425
+ stepId: step.id,
1426
+ error: timeTravelResumeValidationError.message
1427
+ });
1428
+ } else if (resume?.steps[0] === step.id) {
1429
+ resumeDataToUse = resume?.resumePayload;
1056
1430
  }
1057
- const result = await step.execute({
1058
- runId: executionContext.runId,
1059
- mastra: this.mastra,
1060
- runtimeContext,
1061
- writableStream,
1062
- inputData,
1063
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1064
- tracingContext: {
1065
- currentSpan: stepAISpan
1066
- },
1067
- getInitData: () => stepResults?.input,
1068
- getStepResult: workflows.getStepResult.bind(this, stepResults),
1069
- suspend: async (suspendPayload) => {
1070
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1071
- suspended = { payload: suspendPayload };
1072
- },
1073
- bail: (result2) => {
1074
- bailed = { payload: result2 };
1075
- },
1076
- resume: {
1077
- steps: resume?.steps?.slice(1) || [],
1078
- resumePayload: resume?.resumePayload,
1079
- // @ts-ignore
1080
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1081
- },
1082
- [_constants.EMITTER_SYMBOL]: emitter,
1083
- engine: {
1084
- step: this.inngestStep
1431
+ try {
1432
+ if (validationError) {
1433
+ throw validationError;
1434
+ }
1435
+ const result = await step.execute({
1436
+ runId: executionContext.runId,
1437
+ mastra: this.mastra,
1438
+ runtimeContext,
1439
+ writableStream,
1440
+ state: executionContext?.state ?? {},
1441
+ setState: (state) => {
1442
+ executionContext.state = state;
1443
+ },
1444
+ inputData,
1445
+ resumeData: resumeDataToUse,
1446
+ tracingContext: {
1447
+ currentSpan: stepAISpan
1448
+ },
1449
+ getInitData: () => stepResults?.input,
1450
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
1451
+ suspend: async (suspendPayload, suspendOptions) => {
1452
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1453
+ if (suspendOptions?.resumeLabel) {
1454
+ const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1455
+ for (const label of resumeLabel) {
1456
+ executionContext.resumeLabels[label] = {
1457
+ stepId: step.id,
1458
+ foreachIndex: executionContext.foreachIndex
1459
+ };
1460
+ }
1461
+ }
1462
+ suspended = { payload: suspendPayload };
1463
+ },
1464
+ bail: (result2) => {
1465
+ bailed = { payload: result2 };
1466
+ },
1467
+ resume: {
1468
+ steps: resume?.steps?.slice(1) || [],
1469
+ resumePayload: resume?.resumePayload,
1470
+ // @ts-ignore
1471
+ runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
1472
+ },
1473
+ [_constants.EMITTER_SYMBOL]: emitter,
1474
+ engine: {
1475
+ step: this.inngestStep
1476
+ },
1477
+ abortSignal: abortController.signal
1478
+ });
1479
+ const endedAt = Date.now();
1480
+ execResults = {
1481
+ status: "success",
1482
+ output: result,
1483
+ startedAt,
1484
+ endedAt,
1485
+ payload: inputData,
1486
+ resumedAt: resumeDataToUse ? startedAt : void 0,
1487
+ resumePayload: resumeDataToUse
1488
+ };
1489
+ } catch (e) {
1490
+ const stepFailure = {
1491
+ status: "failed",
1492
+ payload: inputData,
1493
+ error: e instanceof Error ? e.stack ?? e.message : String(e),
1494
+ endedAt: Date.now(),
1495
+ startedAt,
1496
+ resumedAt: resumeDataToUse ? startedAt : void 0,
1497
+ resumePayload: resumeDataToUse
1498
+ };
1499
+ execResults = stepFailure;
1500
+ const fallbackErrorMessage = `Step ${step.id} failed`;
1501
+ stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1502
+ throw new inngest.RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1503
+ cause: execResults
1504
+ });
1505
+ }
1506
+ if (suspended) {
1507
+ execResults = {
1508
+ status: "suspended",
1509
+ suspendPayload: suspended.payload,
1510
+ payload: inputData,
1511
+ suspendedAt: Date.now(),
1512
+ startedAt,
1513
+ resumedAt: resumeDataToUse ? startedAt : void 0,
1514
+ resumePayload: resumeDataToUse
1515
+ };
1516
+ } else if (bailed) {
1517
+ execResults = {
1518
+ status: "bailed",
1519
+ output: bailed.payload,
1520
+ payload: inputData,
1521
+ endedAt: Date.now(),
1522
+ startedAt
1523
+ };
1524
+ }
1525
+ await emitter.emit("watch", {
1526
+ type: "watch",
1527
+ payload: {
1528
+ currentStep: {
1529
+ id: step.id,
1530
+ ...execResults
1531
+ },
1532
+ workflowState: {
1533
+ status: "running",
1534
+ steps: { ...stepResults, [step.id]: execResults },
1535
+ result: null,
1536
+ error: null
1537
+ }
1085
1538
  },
1086
- abortSignal: abortController.signal
1539
+ eventTimestamp: Date.now()
1087
1540
  });
1088
- const endedAt = Date.now();
1089
- execResults = {
1090
- status: "success",
1091
- output: result,
1092
- startedAt,
1093
- endedAt,
1094
- payload: inputData,
1095
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1096
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1097
- };
1098
- } catch (e) {
1099
- execResults = {
1100
- status: "failed",
1101
- payload: inputData,
1102
- error: e instanceof Error ? e.message : String(e),
1103
- endedAt: Date.now(),
1104
- startedAt,
1105
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1106
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1107
- };
1108
- }
1109
- if (suspended) {
1110
- execResults = {
1111
- status: "suspended",
1112
- suspendedPayload: suspended.payload,
1113
- payload: inputData,
1114
- suspendedAt: Date.now(),
1115
- startedAt,
1116
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1117
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1118
- };
1119
- } else if (bailed) {
1120
- execResults = { status: "bailed", output: bailed.payload, payload: inputData, endedAt: Date.now(), startedAt };
1121
- }
1122
- if (execResults.status === "failed") {
1123
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1124
- const error = new Error(execResults.error);
1125
- stepAISpan?.error({ error });
1126
- throw error;
1541
+ if (execResults.status === "suspended") {
1542
+ await emitter.emit("watch-v2", {
1543
+ type: "workflow-step-suspended",
1544
+ payload: {
1545
+ id: step.id,
1546
+ ...execResults
1547
+ }
1548
+ });
1549
+ } else {
1550
+ await emitter.emit("watch-v2", {
1551
+ type: "workflow-step-result",
1552
+ payload: {
1553
+ id: step.id,
1554
+ ...execResults
1555
+ }
1556
+ });
1557
+ await emitter.emit("watch-v2", {
1558
+ type: "workflow-step-finish",
1559
+ payload: {
1560
+ id: step.id,
1561
+ metadata: {}
1562
+ }
1563
+ });
1127
1564
  }
1128
- }
1129
- await emitter.emit("watch", {
1130
- type: "watch",
1565
+ stepAISpan?.end({ output: execResults });
1566
+ return { result: execResults, executionContext, stepResults };
1567
+ });
1568
+ } catch (e) {
1569
+ const stepFailure = e instanceof Error ? e?.cause : {
1570
+ status: "failed",
1571
+ error: e instanceof Error ? e.stack ?? e.message : String(e),
1572
+ payload: inputData,
1573
+ startedAt,
1574
+ endedAt: Date.now()
1575
+ };
1576
+ await emitter.emit("watch-v2", {
1577
+ type: "workflow-step-result",
1131
1578
  payload: {
1132
- currentStep: {
1133
- id: step.id,
1134
- ...execResults
1135
- },
1136
- workflowState: {
1137
- status: "running",
1138
- steps: { ...stepResults, [step.id]: execResults },
1139
- result: null,
1140
- error: null
1141
- }
1142
- },
1143
- eventTimestamp: Date.now()
1579
+ id: step.id,
1580
+ ...stepFailure
1581
+ }
1144
1582
  });
1145
- if (execResults.status === "suspended") {
1146
- await emitter.emit("watch-v2", {
1147
- type: "workflow-step-suspended",
1148
- payload: {
1149
- id: step.id,
1150
- ...execResults
1151
- }
1152
- });
1153
- } else {
1154
- await emitter.emit("watch-v2", {
1155
- type: "workflow-step-result",
1156
- payload: {
1157
- id: step.id,
1158
- ...execResults
1159
- }
1160
- });
1161
- await emitter.emit("watch-v2", {
1162
- type: "workflow-step-finish",
1163
- payload: {
1164
- id: step.id,
1165
- metadata: {}
1166
- }
1167
- });
1168
- }
1169
- stepAISpan?.end({ output: execResults });
1170
- return { result: execResults, executionContext, stepResults };
1171
- });
1172
- if (disableScorers !== false) {
1583
+ await emitter.emit("watch-v2", {
1584
+ type: "workflow-step-finish",
1585
+ payload: {
1586
+ id: step.id,
1587
+ metadata: {}
1588
+ }
1589
+ });
1590
+ stepRes = {
1591
+ result: stepFailure,
1592
+ executionContext,
1593
+ stepResults: {
1594
+ ...stepResults,
1595
+ [step.id]: stepFailure
1596
+ }
1597
+ };
1598
+ }
1599
+ if (disableScorers !== false && stepRes.result.status === "success") {
1173
1600
  await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1174
1601
  if (step.scorers) {
1175
1602
  await this.runScorers({
@@ -1187,8 +1614,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1187
1614
  });
1188
1615
  }
1189
1616
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1190
- Object.assign(stepResults, stepRes.stepResults);
1191
- return stepRes.result;
1617
+ executionContext.state = stepRes.executionContext.state;
1618
+ return {
1619
+ result: stepRes.result,
1620
+ executionContextState: stepRes.executionContext.state
1621
+ };
1192
1622
  }
1193
1623
  async persistStepUpdate({
1194
1624
  workflowId,
@@ -1204,19 +1634,25 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1204
1634
  await this.inngestStep.run(
1205
1635
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1206
1636
  async () => {
1637
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1638
+ if (!shouldPersistSnapshot) {
1639
+ return;
1640
+ }
1207
1641
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1208
1642
  workflowName: workflowId,
1209
1643
  runId,
1210
1644
  resourceId,
1211
1645
  snapshot: {
1212
1646
  runId,
1213
- value: {},
1647
+ status: workflowStatus,
1648
+ value: executionContext.state,
1214
1649
  context: stepResults,
1215
- activePaths: [],
1650
+ activePaths: executionContext.executionPath,
1651
+ activeStepsPath: executionContext.activeStepsPath,
1216
1652
  suspendedPaths: executionContext.suspendedPaths,
1653
+ resumeLabels: executionContext.resumeLabels,
1217
1654
  waitingPaths: {},
1218
1655
  serializedStepGraph,
1219
- status: workflowStatus,
1220
1656
  result,
1221
1657
  error,
1222
1658
  // @ts-ignore
@@ -1231,9 +1667,8 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1231
1667
  runId,
1232
1668
  entry,
1233
1669
  prevOutput,
1234
- prevStep,
1235
1670
  stepResults,
1236
- serializedStepGraph,
1671
+ timeTravel,
1237
1672
  resume,
1238
1673
  executionContext,
1239
1674
  emitter,
@@ -1273,6 +1708,10 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1273
1708
  runtimeContext,
1274
1709
  runCount: -1,
1275
1710
  inputData: prevOutput,
1711
+ state: executionContext.state,
1712
+ setState: (state) => {
1713
+ executionContext.state = state;
1714
+ },
1276
1715
  tracingContext: {
1277
1716
  currentSpan: evalSpan
1278
1717
  },
@@ -1330,22 +1769,27 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1330
1769
  }
1331
1770
  });
1332
1771
  const results = await Promise.all(
1333
- stepsToRun.map(
1334
- (step, index) => this.executeEntry({
1335
- workflowId,
1336
- runId,
1337
- entry: step,
1338
- serializedStepGraph,
1339
- prevStep,
1772
+ stepsToRun.map(async (step, index) => {
1773
+ const currStepResult = stepResults[step.step.id];
1774
+ if (currStepResult && currStepResult.status === "success") {
1775
+ return currStepResult;
1776
+ }
1777
+ const result = await this.executeStep({
1778
+ step: step.step,
1779
+ prevOutput,
1340
1780
  stepResults,
1341
1781
  resume,
1782
+ timeTravel,
1342
1783
  executionContext: {
1343
1784
  workflowId,
1344
1785
  runId,
1345
1786
  executionPath: [...executionContext.executionPath, index],
1787
+ activeStepsPath: executionContext.activeStepsPath,
1346
1788
  suspendedPaths: executionContext.suspendedPaths,
1789
+ resumeLabels: executionContext.resumeLabels,
1347
1790
  retryConfig: executionContext.retryConfig,
1348
- executionSpan: executionContext.executionSpan
1791
+ executionSpan: executionContext.executionSpan,
1792
+ state: executionContext.state
1349
1793
  },
1350
1794
  emitter,
1351
1795
  abortController,
@@ -1355,20 +1799,23 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1355
1799
  tracingContext: {
1356
1800
  currentSpan: conditionalSpan
1357
1801
  }
1358
- })
1359
- )
1802
+ });
1803
+ stepResults[step.step.id] = result.result;
1804
+ executionContext.state = result.executionContextState;
1805
+ return result.result;
1806
+ })
1360
1807
  );
1361
- const hasFailed = results.find((result) => result.result.status === "failed");
1362
- const hasSuspended = results.find((result) => result.result.status === "suspended");
1808
+ const hasFailed = results.find((result) => result.status === "failed");
1809
+ const hasSuspended = results.find((result) => result.status === "suspended");
1363
1810
  if (hasFailed) {
1364
- execResults = { status: "failed", error: hasFailed.result.error };
1811
+ execResults = { status: "failed", error: hasFailed.error };
1365
1812
  } else if (hasSuspended) {
1366
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1813
+ execResults = { status: "suspended", suspendPayload: hasSuspended.suspendPayload };
1367
1814
  } else {
1368
1815
  execResults = {
1369
1816
  status: "success",
1370
1817
  output: results.reduce((acc, result, index) => {
1371
- if (result.result.status === "success") {
1818
+ if (result.status === "success") {
1372
1819
  acc[stepsToRun[index].step.id] = result.output;
1373
1820
  }
1374
1821
  return acc;