@mastra/inngest 0.0.0-toolOptionTypes-20250917085558 → 0.0.0-trace-timeline-update-20251121092347

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
- var aiTracing = require('@mastra/core/ai-tracing');
6
6
  var di = require('@mastra/core/di');
7
+ var observability = require('@mastra/core/observability');
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
 
@@ -14,9 +17,10 @@ var zod = require('zod');
14
17
  function serve({
15
18
  mastra,
16
19
  inngest,
17
- functions: userFunctions = []
20
+ functions: userFunctions = [],
21
+ registerOptions
18
22
  }) {
19
- const wfs = mastra.getWorkflows();
23
+ const wfs = mastra.listWorkflows();
20
24
  const workflowFunctions = Array.from(
21
25
  new Set(
22
26
  Object.values(wfs).flatMap((wf) => {
@@ -29,6 +33,7 @@ function serve({
29
33
  )
30
34
  );
31
35
  return hono.serve({
36
+ ...registerOptions,
32
37
  client: inngest,
33
38
  functions: [...workflowFunctions, ...userFunctions]
34
39
  });
@@ -54,13 +59,21 @@ var InngestRun = class extends workflows.Run {
54
59
  }
55
60
  async getRunOutput(eventId) {
56
61
  let runs = await this.getRuns(eventId);
62
+ const storage = this.#mastra?.getStorage();
57
63
  while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
58
64
  await new Promise((resolve) => setTimeout(resolve, 1e3));
59
65
  runs = await this.getRuns(eventId);
60
66
  if (runs?.[0]?.status === "Failed") {
61
- throw new Error(`Function run ${runs?.[0]?.status}`);
62
- } else if (runs?.[0]?.status === "Cancelled") {
63
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
67
+ const snapshot = await storage?.loadWorkflowSnapshot({
68
+ workflowName: this.workflowId,
69
+ runId: this.runId
70
+ });
71
+ return {
72
+ output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
73
+ };
74
+ }
75
+ if (runs?.[0]?.status === "Cancelled") {
76
+ const snapshot = await storage?.loadWorkflowSnapshot({
64
77
  workflowName: this.workflowId,
65
78
  runId: this.runId
66
79
  });
@@ -69,37 +82,40 @@ var InngestRun = class extends workflows.Run {
69
82
  }
70
83
  return runs?.[0];
71
84
  }
72
- async sendEvent(event, data) {
73
- await this.inngest.send({
74
- name: `user-event-${event}`,
75
- data
76
- });
77
- }
78
85
  async cancel() {
86
+ const storage = this.#mastra?.getStorage();
79
87
  await this.inngest.send({
80
88
  name: `cancel.workflow.${this.workflowId}`,
81
89
  data: {
82
90
  runId: this.runId
83
91
  }
84
92
  });
85
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
93
+ const snapshot = await storage?.loadWorkflowSnapshot({
86
94
  workflowName: this.workflowId,
87
95
  runId: this.runId
88
96
  });
89
97
  if (snapshot) {
90
- await this.#mastra?.storage?.persistWorkflowSnapshot({
98
+ await storage?.persistWorkflowSnapshot({
91
99
  workflowName: this.workflowId,
92
100
  runId: this.runId,
93
101
  resourceId: this.resourceId,
94
102
  snapshot: {
95
103
  ...snapshot,
96
- status: "canceled"
104
+ status: "canceled",
105
+ value: snapshot.value
97
106
  }
98
107
  });
99
108
  }
100
109
  }
101
- async start({
102
- inputData
110
+ async start(params) {
111
+ return this._start(params);
112
+ }
113
+ async _start({
114
+ inputData,
115
+ initialState,
116
+ outputOptions,
117
+ tracingOptions,
118
+ format
103
119
  }) {
104
120
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
105
121
  workflowName: this.workflowId,
@@ -108,21 +124,29 @@ var InngestRun = class extends workflows.Run {
108
124
  snapshot: {
109
125
  runId: this.runId,
110
126
  serializedStepGraph: this.serializedStepGraph,
127
+ status: "running",
111
128
  value: {},
112
129
  context: {},
113
130
  activePaths: [],
114
131
  suspendedPaths: {},
132
+ activeStepsPath: {},
133
+ resumeLabels: {},
115
134
  waitingPaths: {},
116
- timestamp: Date.now(),
117
- status: "running"
135
+ timestamp: Date.now()
118
136
  }
119
137
  });
138
+ const inputDataToUse = await this._validateInput(inputData);
139
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
120
140
  const eventOutput = await this.inngest.send({
121
141
  name: `workflow.${this.workflowId}`,
122
142
  data: {
123
- inputData,
143
+ inputData: inputDataToUse,
144
+ initialState: initialStateToUse,
124
145
  runId: this.runId,
125
- resourceId: this.resourceId
146
+ resourceId: this.resourceId,
147
+ outputOptions,
148
+ tracingOptions,
149
+ format
126
150
  }
127
151
  });
128
152
  const eventId = eventOutput.ids[0];
@@ -151,27 +175,126 @@ var InngestRun = class extends workflows.Run {
151
175
  return p;
152
176
  }
153
177
  async _resume(params) {
154
- const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
155
- (step) => typeof step === "string" ? step : step?.id
156
- );
157
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
178
+ const storage = this.#mastra?.getStorage();
179
+ let steps = [];
180
+ if (typeof params.step === "string") {
181
+ steps = params.step.split(".");
182
+ } else {
183
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
184
+ (step) => typeof step === "string" ? step : step?.id
185
+ );
186
+ }
187
+ const snapshot = await storage?.loadWorkflowSnapshot({
158
188
  workflowName: this.workflowId,
159
189
  runId: this.runId
160
190
  });
191
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
192
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
161
193
  const eventOutput = await this.inngest.send({
162
194
  name: `workflow.${this.workflowId}`,
163
195
  data: {
164
- inputData: params.resumeData,
196
+ inputData: resumeDataToUse,
197
+ initialState: snapshot?.value ?? {},
165
198
  runId: this.runId,
166
199
  workflowId: this.workflowId,
167
200
  stepResults: snapshot?.context,
168
201
  resume: {
169
202
  steps,
170
203
  stepResults: snapshot?.context,
171
- resumePayload: params.resumeData,
172
- // @ts-ignore
173
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
204
+ resumePayload: resumeDataToUse,
205
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
206
+ }
207
+ }
208
+ });
209
+ const eventId = eventOutput.ids[0];
210
+ if (!eventId) {
211
+ throw new Error("Event ID is not set");
212
+ }
213
+ const runOutput = await this.getRunOutput(eventId);
214
+ const result = runOutput?.output?.result;
215
+ if (result.status === "failed") {
216
+ result.error = new Error(result.error);
217
+ }
218
+ return result;
219
+ }
220
+ async timeTravel(params) {
221
+ const p = this._timeTravel(params).then((result) => {
222
+ if (result.status !== "suspended") {
223
+ this.closeStreamAction?.().catch(() => {
224
+ });
225
+ }
226
+ return result;
227
+ });
228
+ this.executionResults = p;
229
+ return p;
230
+ }
231
+ async _timeTravel(params) {
232
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
233
+ throw new Error("Step is required and must be a valid step or array of steps");
234
+ }
235
+ let steps = [];
236
+ if (typeof params.step === "string") {
237
+ steps = params.step.split(".");
238
+ } else {
239
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
240
+ (step) => typeof step === "string" ? step : step?.id
241
+ );
242
+ }
243
+ if (steps.length === 0) {
244
+ throw new Error("No steps provided to timeTravel");
245
+ }
246
+ const storage = this.#mastra?.getStorage();
247
+ const snapshot = await storage?.loadWorkflowSnapshot({
248
+ workflowName: this.workflowId,
249
+ runId: this.runId
250
+ });
251
+ if (!snapshot) {
252
+ await storage?.persistWorkflowSnapshot({
253
+ workflowName: this.workflowId,
254
+ runId: this.runId,
255
+ resourceId: this.resourceId,
256
+ snapshot: {
257
+ runId: this.runId,
258
+ serializedStepGraph: this.serializedStepGraph,
259
+ status: "pending",
260
+ value: {},
261
+ context: {},
262
+ activePaths: [],
263
+ suspendedPaths: {},
264
+ activeStepsPath: {},
265
+ resumeLabels: {},
266
+ waitingPaths: {},
267
+ timestamp: Date.now()
174
268
  }
269
+ });
270
+ }
271
+ if (snapshot?.status === "running") {
272
+ throw new Error("This workflow run is still running, cannot time travel");
273
+ }
274
+ let inputDataToUse = params.inputData;
275
+ if (inputDataToUse && steps.length === 1) {
276
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
277
+ }
278
+ const timeTravelData = workflows.createTimeTravelExecutionParams({
279
+ steps,
280
+ inputData: inputDataToUse,
281
+ resumeData: params.resumeData,
282
+ context: params.context,
283
+ nestedStepsContext: params.nestedStepsContext,
284
+ snapshot: snapshot ?? { context: {} },
285
+ graph: this.executionGraph,
286
+ initialState: params.initialState
287
+ });
288
+ const eventOutput = await this.inngest.send({
289
+ name: `workflow.${this.workflowId}`,
290
+ data: {
291
+ initialState: timeTravelData.state,
292
+ runId: this.runId,
293
+ workflowId: this.workflowId,
294
+ stepResults: timeTravelData.stepResults,
295
+ timeTravel: timeTravelData,
296
+ tracingOptions: params.tracingOptions,
297
+ outputOptions: params.outputOptions
175
298
  }
176
299
  });
177
300
  const eventId = eventOutput.ids[0];
@@ -185,12 +308,12 @@ var InngestRun = class extends workflows.Run {
185
308
  }
186
309
  return result;
187
310
  }
188
- watch(cb, type = "watch") {
311
+ watch(cb) {
189
312
  let active = true;
190
313
  const streamPromise = realtime.subscribe(
191
314
  {
192
315
  channel: `workflow:${this.workflowId}:${this.runId}`,
193
- topics: [type],
316
+ topics: ["watch"],
194
317
  app: this.inngest
195
318
  },
196
319
  (message) => {
@@ -208,20 +331,35 @@ var InngestRun = class extends workflows.Run {
208
331
  });
209
332
  };
210
333
  }
211
- stream({ inputData, runtimeContext } = {}) {
334
+ streamLegacy({ inputData, requestContext } = {}) {
212
335
  const { readable, writable } = new TransformStream();
213
336
  const writer = writable.getWriter();
214
337
  const unwatch = this.watch(async (event) => {
215
338
  try {
339
+ await writer.write({
340
+ // @ts-ignore
341
+ type: "start",
342
+ // @ts-ignore
343
+ payload: { runId: this.runId }
344
+ });
216
345
  const e = {
217
346
  ...event,
218
347
  type: event.type.replace("workflow-", "")
219
348
  };
349
+ if (e.type === "step-output") {
350
+ e.type = e.payload.output.type;
351
+ e.payload = e.payload.output.payload;
352
+ }
220
353
  await writer.write(e);
221
354
  } catch {
222
355
  }
223
- }, "watch-v2");
356
+ });
224
357
  this.closeStreamAction = async () => {
358
+ await writer.write({
359
+ type: "finish",
360
+ // @ts-ignore
361
+ payload: { runId: this.runId }
362
+ });
225
363
  unwatch();
226
364
  try {
227
365
  await writer.close();
@@ -231,7 +369,7 @@ var InngestRun = class extends workflows.Run {
231
369
  writer.releaseLock();
232
370
  }
233
371
  };
234
- this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
372
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
235
373
  if (result.status !== "suspended") {
236
374
  this.closeStreamAction?.().catch(() => {
237
375
  });
@@ -243,6 +381,151 @@ var InngestRun = class extends workflows.Run {
243
381
  getWorkflowState: () => this.executionResults
244
382
  };
245
383
  }
384
+ stream({
385
+ inputData,
386
+ requestContext,
387
+ tracingOptions,
388
+ closeOnSuspend = true,
389
+ initialState,
390
+ outputOptions
391
+ } = {}) {
392
+ if (this.closeStreamAction && this.streamOutput) {
393
+ return this.streamOutput;
394
+ }
395
+ this.closeStreamAction = async () => {
396
+ };
397
+ const self = this;
398
+ const stream$1 = new web.ReadableStream({
399
+ async start(controller) {
400
+ const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
401
+ controller.enqueue({
402
+ type,
403
+ runId: self.runId,
404
+ from,
405
+ payload: {
406
+ stepName: payload?.id,
407
+ ...payload
408
+ }
409
+ });
410
+ });
411
+ self.closeStreamAction = async () => {
412
+ unwatch();
413
+ try {
414
+ await controller.close();
415
+ } catch (err) {
416
+ console.error("Error closing stream:", err);
417
+ }
418
+ };
419
+ const executionResultsPromise = self._start({
420
+ inputData,
421
+ requestContext,
422
+ // tracingContext, // We are not able to pass a reference to a span here, what to do?
423
+ initialState,
424
+ tracingOptions,
425
+ outputOptions,
426
+ format: "vnext"
427
+ });
428
+ let executionResults;
429
+ try {
430
+ executionResults = await executionResultsPromise;
431
+ if (closeOnSuspend) {
432
+ self.closeStreamAction?.().catch(() => {
433
+ });
434
+ } else if (executionResults.status !== "suspended") {
435
+ self.closeStreamAction?.().catch(() => {
436
+ });
437
+ }
438
+ if (self.streamOutput) {
439
+ self.streamOutput.updateResults(
440
+ executionResults
441
+ );
442
+ }
443
+ } catch (err) {
444
+ self.streamOutput?.rejectResults(err);
445
+ self.closeStreamAction?.().catch(() => {
446
+ });
447
+ }
448
+ }
449
+ });
450
+ this.streamOutput = new stream.WorkflowRunOutput({
451
+ runId: this.runId,
452
+ workflowId: this.workflowId,
453
+ stream: stream$1
454
+ });
455
+ return this.streamOutput;
456
+ }
457
+ streamVNext(args = {}) {
458
+ return this.stream(args);
459
+ }
460
+ timeTravelStream({
461
+ inputData,
462
+ resumeData,
463
+ initialState,
464
+ step,
465
+ context,
466
+ nestedStepsContext,
467
+ requestContext,
468
+ tracingOptions,
469
+ outputOptions
470
+ }) {
471
+ this.closeStreamAction = async () => {
472
+ };
473
+ const self = this;
474
+ const stream$1 = new web.ReadableStream({
475
+ async start(controller) {
476
+ const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
477
+ controller.enqueue({
478
+ type,
479
+ runId: self.runId,
480
+ from,
481
+ payload: {
482
+ stepName: payload?.id,
483
+ ...payload
484
+ }
485
+ });
486
+ });
487
+ self.closeStreamAction = async () => {
488
+ unwatch();
489
+ try {
490
+ await controller.close();
491
+ } catch (err) {
492
+ console.error("Error closing stream:", err);
493
+ }
494
+ };
495
+ const executionResultsPromise = self._timeTravel({
496
+ inputData,
497
+ step,
498
+ context,
499
+ nestedStepsContext,
500
+ resumeData,
501
+ initialState,
502
+ requestContext,
503
+ tracingOptions,
504
+ outputOptions
505
+ });
506
+ self.executionResults = executionResultsPromise;
507
+ let executionResults;
508
+ try {
509
+ executionResults = await executionResultsPromise;
510
+ self.closeStreamAction?.().catch(() => {
511
+ });
512
+ if (self.streamOutput) {
513
+ self.streamOutput.updateResults(executionResults);
514
+ }
515
+ } catch (err) {
516
+ self.streamOutput?.rejectResults(err);
517
+ self.closeStreamAction?.().catch(() => {
518
+ });
519
+ }
520
+ }
521
+ });
522
+ this.streamOutput = new stream.WorkflowRunOutput({
523
+ runId: this.runId,
524
+ workflowId: this.workflowId,
525
+ stream: stream$1
526
+ });
527
+ return this.streamOutput;
528
+ }
246
529
  };
247
530
  var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
248
531
  #mastra;
@@ -252,6 +535,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
252
535
  constructor(params, inngest) {
253
536
  const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
254
537
  super(workflowParams);
538
+ this.engineType = "inngest";
255
539
  const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
256
540
  ([_, value]) => value !== void 0
257
541
  );
@@ -259,13 +543,13 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
259
543
  this.#mastra = params.mastra;
260
544
  this.inngest = inngest;
261
545
  }
262
- async getWorkflowRuns(args) {
546
+ async listWorkflowRuns(args) {
263
547
  const storage = this.#mastra?.getStorage();
264
548
  if (!storage) {
265
549
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
266
550
  return { runs: [], total: 0 };
267
551
  }
268
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
552
+ return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
269
553
  }
270
554
  async getWorkflowRunById(runId) {
271
555
  const storage = this.#mastra?.getStorage();
@@ -294,16 +578,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
294
578
  }
295
579
  }
296
580
  }
297
- /**
298
- * @deprecated Use createRunAsync() instead.
299
- * @throws {Error} Always throws an error directing users to use createRunAsync()
300
- */
301
- createRun(_options) {
302
- throw new Error(
303
- "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."
304
- );
305
- }
306
- async createRunAsync(options) {
581
+ async createRun(options) {
307
582
  const runIdToUse = options?.runId || crypto.randomUUID();
308
583
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
309
584
  {
@@ -315,13 +590,20 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
315
590
  serializedStepGraph: this.serializedStepGraph,
316
591
  mastra: this.#mastra,
317
592
  retryConfig: this.retryConfig,
318
- cleanup: () => this.runs.delete(runIdToUse)
593
+ cleanup: () => this.runs.delete(runIdToUse),
594
+ workflowSteps: this.steps,
595
+ workflowEngineType: this.engineType,
596
+ validateInputs: this.options.validateInputs
319
597
  },
320
598
  this.inngest
321
599
  );
322
600
  this.runs.set(runIdToUse, run);
601
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
602
+ workflowStatus: run.workflowRunStatus,
603
+ stepResults: {}
604
+ });
323
605
  const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
324
- if (!workflowSnapshotInStorage) {
606
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
325
607
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
326
608
  workflowName: this.id,
327
609
  runId: runIdToUse,
@@ -332,12 +614,13 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
332
614
  value: {},
333
615
  context: {},
334
616
  activePaths: [],
617
+ activeStepsPath: {},
335
618
  waitingPaths: {},
336
619
  serializedStepGraph: this.serializedStepGraph,
337
620
  suspendedPaths: {},
621
+ resumeLabels: {},
338
622
  result: void 0,
339
623
  error: void 0,
340
- // @ts-ignore
341
624
  timestamp: Date.now()
342
625
  }
343
626
  });
@@ -351,15 +634,14 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
351
634
  this.function = this.inngest.createFunction(
352
635
  {
353
636
  id: `workflow.${this.id}`,
354
- // @ts-ignore
355
- retries: this.retryConfig?.attempts ?? 0,
637
+ retries: Math.min(this.retryConfig?.attempts ?? 0, 20),
356
638
  cancelOn: [{ event: `cancel.workflow.${this.id}` }],
357
639
  // Spread flow control configuration
358
640
  ...this.flowControlConfig
359
641
  },
360
642
  { event: `workflow.${this.id}` },
361
643
  async ({ event, step, attempt, publish }) => {
362
- let { inputData, runId, resourceId, resume } = event.data;
644
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
363
645
  if (!runId) {
364
646
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
365
647
  return crypto.randomUUID();
@@ -387,7 +669,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
387
669
  once: (_event, _callback) => {
388
670
  }
389
671
  };
390
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
672
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
391
673
  const result = await engine.execute({
392
674
  workflowId: this.id,
393
675
  runId,
@@ -395,14 +677,31 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
395
677
  graph: this.executionGraph,
396
678
  serializedStepGraph: this.serializedStepGraph,
397
679
  input: inputData,
680
+ initialState,
398
681
  emitter,
399
682
  retryConfig: this.retryConfig,
400
- runtimeContext: new di.RuntimeContext(),
683
+ requestContext: new di.RequestContext(),
401
684
  // TODO
402
685
  resume,
686
+ timeTravel,
687
+ format,
403
688
  abortController: new AbortController(),
404
- currentSpan: void 0
405
- // TODO: Pass actual parent AI span from workflow execution context
689
+ // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
690
+ outputOptions,
691
+ writableStream: new web.WritableStream({
692
+ write(chunk) {
693
+ void emitter.emit("watch", chunk).catch(() => {
694
+ });
695
+ }
696
+ })
697
+ });
698
+ await step.run(`workflow.${this.id}.finalize`, async () => {
699
+ if (result.status === "failed") {
700
+ throw new inngest.NonRetriableError(`Workflow failed`, {
701
+ cause: result
702
+ });
703
+ }
704
+ return result;
406
705
  });
407
706
  return { result, runId };
408
707
  }
@@ -432,19 +731,29 @@ function isAgent(params) {
432
731
  function isTool(params) {
433
732
  return params instanceof tools.Tool;
434
733
  }
435
- function createStep(params) {
734
+ function createStep(params, agentOptions) {
436
735
  if (isAgent(params)) {
437
736
  return {
438
737
  id: params.name,
439
- // @ts-ignore
738
+ description: params.getDescription(),
440
739
  inputSchema: zod.z.object({
441
740
  prompt: zod.z.string()
741
+ // resourceId: z.string().optional(),
742
+ // threadId: z.string().optional(),
442
743
  }),
443
- // @ts-ignore
444
744
  outputSchema: zod.z.object({
445
745
  text: zod.z.string()
446
746
  }),
447
- execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
747
+ execute: async ({
748
+ inputData,
749
+ [_constants.EMITTER_SYMBOL]: emitter,
750
+ [_constants.STREAM_FORMAT_SYMBOL]: streamFormat,
751
+ requestContext,
752
+ tracingContext,
753
+ abortSignal,
754
+ abort,
755
+ writer
756
+ }) => {
448
757
  let streamPromise = {};
449
758
  streamPromise.promise = new Promise((resolve, reject) => {
450
759
  streamPromise.resolve = resolve;
@@ -454,38 +763,65 @@ function createStep(params) {
454
763
  name: params.name,
455
764
  args: inputData
456
765
  };
457
- const { fullStream } = await params.stream(inputData.prompt, {
458
- runtimeContext,
459
- tracingContext,
460
- onFinish: (result) => {
461
- streamPromise.resolve(result.text);
462
- },
463
- abortSignal
464
- });
465
- if (abortSignal.aborted) {
466
- return abort();
766
+ let stream;
767
+ if ((await params.getModel()).specificationVersion === "v1") {
768
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
769
+ ...agentOptions ?? {},
770
+ // resourceId: inputData.resourceId,
771
+ // threadId: inputData.threadId,
772
+ requestContext,
773
+ tracingContext,
774
+ onFinish: (result) => {
775
+ streamPromise.resolve(result.text);
776
+ void agentOptions?.onFinish?.(result);
777
+ },
778
+ abortSignal
779
+ });
780
+ stream = fullStream;
781
+ } else {
782
+ const modelOutput = await params.stream(inputData.prompt, {
783
+ ...agentOptions ?? {},
784
+ requestContext,
785
+ tracingContext,
786
+ onFinish: (result) => {
787
+ streamPromise.resolve(result.text);
788
+ void agentOptions?.onFinish?.(result);
789
+ },
790
+ abortSignal
791
+ });
792
+ stream = modelOutput.fullStream;
467
793
  }
468
- await emitter.emit("watch-v2", {
469
- type: "tool-call-streaming-start",
470
- ...toolData ?? {}
471
- });
472
- for await (const chunk of fullStream) {
473
- if (chunk.type === "text-delta") {
474
- await emitter.emit("watch-v2", {
475
- type: "tool-call-delta",
476
- ...toolData ?? {},
477
- argsTextDelta: chunk.textDelta
478
- });
794
+ if (streamFormat === "legacy") {
795
+ await emitter.emit("watch", {
796
+ type: "tool-call-streaming-start",
797
+ ...toolData ?? {}
798
+ });
799
+ for await (const chunk of stream) {
800
+ if (chunk.type === "text-delta") {
801
+ await emitter.emit("watch", {
802
+ type: "tool-call-delta",
803
+ ...toolData ?? {},
804
+ argsTextDelta: chunk.textDelta
805
+ });
806
+ }
807
+ }
808
+ await emitter.emit("watch", {
809
+ type: "tool-call-streaming-finish",
810
+ ...toolData ?? {}
811
+ });
812
+ } else {
813
+ for await (const chunk of stream) {
814
+ await writer.write(chunk);
479
815
  }
480
816
  }
481
- await emitter.emit("watch-v2", {
482
- type: "tool-call-streaming-finish",
483
- ...toolData ?? {}
484
- });
817
+ if (abortSignal.aborted) {
818
+ return abort();
819
+ }
485
820
  return {
486
821
  text: await streamPromise.promise
487
822
  };
488
- }
823
+ },
824
+ component: params.component
489
825
  };
490
826
  }
491
827
  if (isTool(params)) {
@@ -494,18 +830,38 @@ function createStep(params) {
494
830
  }
495
831
  return {
496
832
  // TODO: tool probably should have strong id type
497
- // @ts-ignore
498
833
  id: params.id,
834
+ description: params.description,
499
835
  inputSchema: params.inputSchema,
500
836
  outputSchema: params.outputSchema,
501
- execute: async ({ inputData, mastra, runtimeContext, tracingContext }) => {
502
- return params.execute({
503
- context: inputData,
504
- mastra: aiTracing.wrapMastra(mastra, tracingContext),
505
- runtimeContext,
506
- tracingContext
507
- });
508
- }
837
+ execute: async ({
838
+ inputData,
839
+ mastra,
840
+ requestContext,
841
+ tracingContext,
842
+ suspend,
843
+ resumeData,
844
+ runId,
845
+ workflowId,
846
+ state,
847
+ setState
848
+ }) => {
849
+ const toolContext = {
850
+ mastra,
851
+ requestContext,
852
+ tracingContext,
853
+ resumeData,
854
+ workflow: {
855
+ runId,
856
+ suspend,
857
+ workflowId,
858
+ state,
859
+ setState
860
+ }
861
+ };
862
+ return params.execute(inputData, toolContext);
863
+ },
864
+ component: "TOOL"
509
865
  };
510
866
  }
511
867
  return {
@@ -521,7 +877,10 @@ function createStep(params) {
521
877
  function init(inngest) {
522
878
  return {
523
879
  createWorkflow(params) {
524
- return new InngestWorkflow(params, inngest);
880
+ return new InngestWorkflow(
881
+ params,
882
+ inngest
883
+ );
525
884
  },
526
885
  createStep,
527
886
  cloneStep(step, opts) {
@@ -530,7 +889,13 @@ function init(inngest) {
530
889
  description: step.description,
531
890
  inputSchema: step.inputSchema,
532
891
  outputSchema: step.outputSchema,
533
- execute: step.execute
892
+ resumeSchema: step.resumeSchema,
893
+ suspendSchema: step.suspendSchema,
894
+ stateSchema: step.stateSchema,
895
+ execute: step.execute,
896
+ retries: step.retries,
897
+ scorers: step.scorers,
898
+ component: step.component
534
899
  };
535
900
  },
536
901
  cloneWorkflow(workflow, opts) {
@@ -555,73 +920,25 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
555
920
  this.inngestStep = inngestStep;
556
921
  this.inngestAttempts = inngestAttempts;
557
922
  }
558
- async execute(params) {
559
- await params.emitter.emit("watch-v2", {
560
- type: "workflow-start",
561
- payload: { runId: params.runId }
562
- });
563
- const result = await super.execute(params);
564
- await params.emitter.emit("watch-v2", {
565
- type: "workflow-finish",
566
- payload: { runId: params.runId }
567
- });
568
- return result;
569
- }
570
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
923
+ async fmtReturnValue(emitter, stepResults, lastOutput, error) {
571
924
  const base = {
572
925
  status: lastOutput.status,
573
926
  steps: stepResults
574
927
  };
575
928
  if (lastOutput.status === "success") {
576
- await emitter.emit("watch", {
577
- type: "watch",
578
- payload: {
579
- workflowState: {
580
- status: lastOutput.status,
581
- steps: stepResults,
582
- result: lastOutput.output
583
- }
584
- },
585
- eventTimestamp: Date.now()
586
- });
587
929
  base.result = lastOutput.output;
588
930
  } else if (lastOutput.status === "failed") {
589
931
  base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
590
- await emitter.emit("watch", {
591
- type: "watch",
592
- payload: {
593
- workflowState: {
594
- status: lastOutput.status,
595
- steps: stepResults,
596
- result: null,
597
- error: base.error
598
- }
599
- },
600
- eventTimestamp: Date.now()
601
- });
602
932
  } else if (lastOutput.status === "suspended") {
603
- await emitter.emit("watch", {
604
- type: "watch",
605
- payload: {
606
- workflowState: {
607
- status: lastOutput.status,
608
- steps: stepResults,
609
- result: null,
610
- error: null
611
- }
612
- },
613
- eventTimestamp: Date.now()
614
- });
615
933
  const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
616
934
  if (stepResult?.status === "suspended") {
617
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
935
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
618
936
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
619
937
  }
620
938
  return [];
621
939
  });
622
940
  base.suspended = suspendedStepIds;
623
941
  }
624
- executionSpan?.end();
625
942
  return base;
626
943
  }
627
944
  // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
@@ -635,14 +952,14 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
635
952
  stepResults,
636
953
  emitter,
637
954
  abortController,
638
- runtimeContext,
955
+ requestContext,
639
956
  executionContext,
640
957
  writableStream,
641
958
  tracingContext
642
959
  }) {
643
960
  let { duration, fn } = entry;
644
961
  const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
645
- type: aiTracing.AISpanType.WORKFLOW_SLEEP,
962
+ type: observability.SpanType.WORKFLOW_SLEEP,
646
963
  name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
647
964
  attributes: {
648
965
  durationMs: duration,
@@ -653,50 +970,53 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
653
970
  if (fn) {
654
971
  const stepCallId = crypto.randomUUID();
655
972
  duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
656
- return await fn({
657
- runId,
658
- workflowId,
659
- mastra: this.mastra,
660
- runtimeContext,
661
- inputData: prevOutput,
662
- runCount: -1,
663
- tracingContext: {
664
- currentSpan: sleepSpan
665
- },
666
- getInitData: () => stepResults?.input,
667
- getStepResult: (step) => {
668
- if (!step?.id) {
669
- return null;
670
- }
671
- const result = stepResults[step.id];
672
- if (result?.status === "success") {
673
- return result.output;
674
- }
675
- return null;
676
- },
677
- // TODO: this function shouldn't have suspend probably?
678
- suspend: async (_suspendPayload) => {
679
- },
680
- bail: () => {
681
- },
682
- abort: () => {
683
- abortController?.abort();
684
- },
685
- [_constants.EMITTER_SYMBOL]: emitter,
686
- // TODO: add streamVNext support
687
- [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
688
- engine: { step: this.inngestStep },
689
- abortSignal: abortController?.signal,
690
- writer: new tools.ToolStream(
973
+ return await fn(
974
+ workflows.createDeprecationProxy(
691
975
  {
692
- prefix: "workflow-step",
693
- callId: stepCallId,
694
- name: "sleep",
695
- runId
976
+ runId,
977
+ workflowId,
978
+ mastra: this.mastra,
979
+ requestContext,
980
+ inputData: prevOutput,
981
+ state: executionContext.state,
982
+ setState: (state) => {
983
+ executionContext.state = state;
984
+ },
985
+ retryCount: -1,
986
+ tracingContext: {
987
+ currentSpan: sleepSpan
988
+ },
989
+ getInitData: () => stepResults?.input,
990
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
991
+ // TODO: this function shouldn't have suspend probably?
992
+ suspend: async (_suspendPayload) => {
993
+ },
994
+ bail: () => {
995
+ },
996
+ abort: () => {
997
+ abortController?.abort();
998
+ },
999
+ [_constants.EMITTER_SYMBOL]: emitter,
1000
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1001
+ engine: { step: this.inngestStep },
1002
+ abortSignal: abortController?.signal,
1003
+ writer: new tools.ToolStream(
1004
+ {
1005
+ prefix: "workflow-step",
1006
+ callId: stepCallId,
1007
+ name: "sleep",
1008
+ runId
1009
+ },
1010
+ writableStream
1011
+ )
696
1012
  },
697
- writableStream
1013
+ {
1014
+ paramName: "runCount",
1015
+ deprecationMessage: workflows.runCountDeprecationMessage,
1016
+ logger: this.logger
1017
+ }
698
1018
  )
699
- });
1019
+ );
700
1020
  });
701
1021
  sleepSpan?.update({
702
1022
  attributes: {
@@ -720,14 +1040,14 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
720
1040
  stepResults,
721
1041
  emitter,
722
1042
  abortController,
723
- runtimeContext,
1043
+ requestContext,
724
1044
  executionContext,
725
1045
  writableStream,
726
1046
  tracingContext
727
1047
  }) {
728
1048
  let { date, fn } = entry;
729
1049
  const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
730
- type: aiTracing.AISpanType.WORKFLOW_SLEEP,
1050
+ type: observability.SpanType.WORKFLOW_SLEEP,
731
1051
  name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
732
1052
  attributes: {
733
1053
  untilDate: date,
@@ -739,50 +1059,53 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
739
1059
  if (fn) {
740
1060
  date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
741
1061
  const stepCallId = crypto.randomUUID();
742
- return await fn({
743
- runId,
744
- workflowId,
745
- mastra: this.mastra,
746
- runtimeContext,
747
- inputData: prevOutput,
748
- runCount: -1,
749
- tracingContext: {
750
- currentSpan: sleepUntilSpan
751
- },
752
- getInitData: () => stepResults?.input,
753
- getStepResult: (step) => {
754
- if (!step?.id) {
755
- return null;
756
- }
757
- const result = stepResults[step.id];
758
- if (result?.status === "success") {
759
- return result.output;
760
- }
761
- return null;
762
- },
763
- // TODO: this function shouldn't have suspend probably?
764
- suspend: async (_suspendPayload) => {
765
- },
766
- bail: () => {
767
- },
768
- abort: () => {
769
- abortController?.abort();
770
- },
771
- [_constants.EMITTER_SYMBOL]: emitter,
772
- [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
773
- // TODO: add streamVNext support
774
- engine: { step: this.inngestStep },
775
- abortSignal: abortController?.signal,
776
- writer: new tools.ToolStream(
1062
+ return await fn(
1063
+ workflows.createDeprecationProxy(
777
1064
  {
778
- prefix: "workflow-step",
779
- callId: stepCallId,
780
- name: "sleep",
781
- runId
1065
+ runId,
1066
+ workflowId,
1067
+ mastra: this.mastra,
1068
+ requestContext,
1069
+ inputData: prevOutput,
1070
+ state: executionContext.state,
1071
+ setState: (state) => {
1072
+ executionContext.state = state;
1073
+ },
1074
+ retryCount: -1,
1075
+ tracingContext: {
1076
+ currentSpan: sleepUntilSpan
1077
+ },
1078
+ getInitData: () => stepResults?.input,
1079
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
1080
+ // TODO: this function shouldn't have suspend probably?
1081
+ suspend: async (_suspendPayload) => {
1082
+ },
1083
+ bail: () => {
1084
+ },
1085
+ abort: () => {
1086
+ abortController?.abort();
1087
+ },
1088
+ [_constants.EMITTER_SYMBOL]: emitter,
1089
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1090
+ engine: { step: this.inngestStep },
1091
+ abortSignal: abortController?.signal,
1092
+ writer: new tools.ToolStream(
1093
+ {
1094
+ prefix: "workflow-step",
1095
+ callId: stepCallId,
1096
+ name: "sleep",
1097
+ runId
1098
+ },
1099
+ writableStream
1100
+ )
782
1101
  },
783
- writableStream
1102
+ {
1103
+ paramName: "runCount",
1104
+ deprecationMessage: workflows.runCountDeprecationMessage,
1105
+ logger: this.logger
1106
+ }
784
1107
  )
785
- });
1108
+ );
786
1109
  });
787
1110
  if (date && !(date instanceof Date)) {
788
1111
  date = new Date(date);
@@ -806,69 +1129,44 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
806
1129
  throw e;
807
1130
  }
808
1131
  }
809
- async executeWaitForEvent({ event, timeout }) {
810
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
811
- event: `user-event-${event}`,
812
- timeout: timeout ?? 5e3
813
- });
814
- if (eventData === null) {
815
- throw "Timeout waiting for event";
816
- }
817
- return eventData?.data;
818
- }
819
1132
  async executeStep({
820
1133
  step,
821
1134
  stepResults,
822
1135
  executionContext,
823
1136
  resume,
1137
+ timeTravel,
824
1138
  prevOutput,
825
1139
  emitter,
826
1140
  abortController,
827
- runtimeContext,
1141
+ requestContext,
828
1142
  tracingContext,
829
1143
  writableStream,
830
1144
  disableScorers
831
1145
  }) {
832
- const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
1146
+ const stepSpan = tracingContext?.currentSpan?.createChildSpan({
833
1147
  name: `workflow step: '${step.id}'`,
834
- type: aiTracing.AISpanType.WORKFLOW_STEP,
1148
+ type: observability.SpanType.WORKFLOW_STEP,
835
1149
  input: prevOutput,
836
1150
  attributes: {
837
1151
  stepId: step.id
838
1152
  },
839
1153
  tracingPolicy: this.options?.tracingPolicy
840
1154
  });
1155
+ const { inputData, validationError } = await workflows.validateStepInput({
1156
+ prevOutput,
1157
+ step,
1158
+ validateInputs: this.options?.validateInputs ?? true
1159
+ });
841
1160
  const startedAt = await this.inngestStep.run(
842
1161
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
843
1162
  async () => {
844
1163
  const startedAt2 = Date.now();
845
1164
  await emitter.emit("watch", {
846
- type: "watch",
847
- payload: {
848
- currentStep: {
849
- id: step.id,
850
- status: "running"
851
- },
852
- workflowState: {
853
- status: "running",
854
- steps: {
855
- ...stepResults,
856
- [step.id]: {
857
- status: "running"
858
- }
859
- },
860
- result: null,
861
- error: null
862
- }
863
- },
864
- eventTimestamp: Date.now()
865
- });
866
- await emitter.emit("watch-v2", {
867
1165
  type: "workflow-step-start",
868
1166
  payload: {
869
1167
  id: step.id,
870
1168
  status: "running",
871
- payload: prevOutput,
1169
+ payload: inputData,
872
1170
  startedAt: startedAt2
873
1171
  }
874
1172
  });
@@ -879,61 +1177,92 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
879
1177
  const isResume = !!resume?.steps?.length;
880
1178
  let result;
881
1179
  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: prevOutput,
892
- runId,
893
- resume: {
1180
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
1181
+ try {
1182
+ if (isResume) {
1183
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
1184
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1185
+ workflowName: step.id,
1186
+ runId
1187
+ });
1188
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1189
+ function: step.getFunction(),
1190
+ data: {
1191
+ inputData,
1192
+ initialState: executionContext.state ?? snapshot?.value ?? {},
894
1193
  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]]
1194
+ resume: {
1195
+ runId,
1196
+ steps: resume.steps.slice(1),
1197
+ stepResults: snapshot?.context,
1198
+ resumePayload: resume.resumePayload,
1199
+ resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
1200
+ },
1201
+ outputOptions: { includeState: true }
900
1202
  }
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: prevOutput
910
- }
911
- });
912
- result = invokeResp.result;
913
- runId = invokeResp.runId;
1203
+ });
1204
+ result = invokeResp.result;
1205
+ runId = invokeResp.runId;
1206
+ executionContext.state = invokeResp.result.state;
1207
+ } else if (isTimeTravel) {
1208
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1209
+ workflowName: step.id,
1210
+ runId: executionContext.runId
1211
+ }) ?? { context: {} };
1212
+ const timeTravelParams = workflows.createTimeTravelExecutionParams({
1213
+ steps: timeTravel.steps.slice(1),
1214
+ inputData: timeTravel.inputData,
1215
+ resumeData: timeTravel.resumeData,
1216
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
1217
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
1218
+ snapshot,
1219
+ graph: step.buildExecutionGraph()
1220
+ });
1221
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1222
+ function: step.getFunction(),
1223
+ data: {
1224
+ timeTravel: timeTravelParams,
1225
+ initialState: executionContext.state ?? {},
1226
+ runId: executionContext.runId,
1227
+ outputOptions: { includeState: true }
1228
+ }
1229
+ });
1230
+ result = invokeResp.result;
1231
+ runId = invokeResp.runId;
1232
+ executionContext.state = invokeResp.result.state;
1233
+ } else {
1234
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1235
+ function: step.getFunction(),
1236
+ data: {
1237
+ inputData,
1238
+ initialState: executionContext.state ?? {},
1239
+ outputOptions: { includeState: true }
1240
+ }
1241
+ });
1242
+ result = invokeResp.result;
1243
+ runId = invokeResp.runId;
1244
+ executionContext.state = invokeResp.result.state;
1245
+ }
1246
+ } catch (e) {
1247
+ const errorCause = e?.cause;
1248
+ if (errorCause && typeof errorCause === "object") {
1249
+ result = errorCause;
1250
+ runId = errorCause.runId || crypto.randomUUID();
1251
+ } else {
1252
+ runId = crypto.randomUUID();
1253
+ result = {
1254
+ status: "failed",
1255
+ error: e instanceof Error ? e : new Error(String(e)),
1256
+ steps: {},
1257
+ input: inputData
1258
+ };
1259
+ }
914
1260
  }
915
1261
  const res = await this.inngestStep.run(
916
1262
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
917
1263
  async () => {
918
1264
  if (result.status === "failed") {
919
1265
  await emitter.emit("watch", {
920
- type: "watch",
921
- payload: {
922
- currentStep: {
923
- id: step.id,
924
- status: "failed",
925
- error: result?.error
926
- },
927
- workflowState: {
928
- status: "running",
929
- steps: stepResults,
930
- result: null,
931
- error: null
932
- }
933
- },
934
- eventTimestamp: Date.now()
935
- });
936
- await emitter.emit("watch-v2", {
937
1266
  type: "workflow-step-result",
938
1267
  payload: {
939
1268
  id: step.id,
@@ -949,26 +1278,9 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
949
1278
  return stepRes2?.status === "suspended";
950
1279
  });
951
1280
  for (const [stepName, stepResult] of suspendedSteps) {
952
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
1281
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
953
1282
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
954
1283
  await emitter.emit("watch", {
955
- type: "watch",
956
- payload: {
957
- currentStep: {
958
- id: step.id,
959
- status: "suspended",
960
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
961
- },
962
- workflowState: {
963
- status: "running",
964
- steps: stepResults,
965
- result: null,
966
- error: null
967
- }
968
- },
969
- eventTimestamp: Date.now()
970
- });
971
- await emitter.emit("watch-v2", {
972
1284
  type: "workflow-step-suspended",
973
1285
  payload: {
974
1286
  id: step.id,
@@ -979,27 +1291,14 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
979
1291
  executionContext,
980
1292
  result: {
981
1293
  status: "suspended",
982
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1294
+ payload: stepResult.payload,
1295
+ suspendPayload: {
1296
+ ...stepResult?.suspendPayload,
1297
+ __workflow_meta: { runId, path: suspendPath }
1298
+ }
983
1299
  }
984
1300
  };
985
1301
  }
986
- await emitter.emit("watch", {
987
- type: "watch",
988
- payload: {
989
- currentStep: {
990
- id: step.id,
991
- status: "suspended",
992
- payload: {}
993
- },
994
- workflowState: {
995
- status: "running",
996
- steps: stepResults,
997
- result: null,
998
- error: null
999
- }
1000
- },
1001
- eventTimestamp: Date.now()
1002
- });
1003
1302
  return {
1004
1303
  executionContext,
1005
1304
  result: {
@@ -1009,23 +1308,6 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1009
1308
  };
1010
1309
  }
1011
1310
  await emitter.emit("watch", {
1012
- type: "watch",
1013
- payload: {
1014
- currentStep: {
1015
- id: step.id,
1016
- status: "success",
1017
- output: result?.result
1018
- },
1019
- workflowState: {
1020
- status: "running",
1021
- steps: stepResults,
1022
- result: null,
1023
- error: null
1024
- }
1025
- },
1026
- eventTimestamp: Date.now()
1027
- });
1028
- await emitter.emit("watch-v2", {
1029
1311
  type: "workflow-step-result",
1030
1312
  payload: {
1031
1313
  id: step.id,
@@ -1033,7 +1315,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1033
1315
  output: result?.result
1034
1316
  }
1035
1317
  });
1036
- await emitter.emit("watch-v2", {
1318
+ await emitter.emit("watch", {
1037
1319
  type: "workflow-step-finish",
1038
1320
  payload: {
1039
1321
  id: step.id,
@@ -1044,153 +1326,204 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1044
1326
  }
1045
1327
  );
1046
1328
  Object.assign(executionContext, res.executionContext);
1047
- return res.result;
1329
+ return {
1330
+ ...res.result,
1331
+ startedAt,
1332
+ endedAt: Date.now(),
1333
+ payload: inputData,
1334
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1335
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1336
+ };
1048
1337
  }
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
- const result = await step.execute({
1055
- runId: executionContext.runId,
1056
- mastra: this.mastra,
1057
- runtimeContext,
1058
- writableStream,
1059
- inputData: prevOutput,
1060
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1061
- tracingContext: {
1062
- currentSpan: stepAISpan
1063
- },
1064
- getInitData: () => stepResults?.input,
1065
- getStepResult: (step2) => {
1066
- const result2 = stepResults[step2.id];
1067
- if (result2?.status === "success") {
1068
- return result2.output;
1069
- }
1070
- return null;
1071
- },
1072
- suspend: async (suspendPayload) => {
1073
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1074
- suspended = { payload: suspendPayload };
1075
- },
1076
- bail: (result2) => {
1077
- bailed = { payload: result2 };
1078
- },
1079
- resume: {
1080
- steps: resume?.steps?.slice(1) || [],
1081
- resumePayload: resume?.resumePayload,
1082
- // @ts-ignore
1083
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1084
- },
1085
- [_constants.EMITTER_SYMBOL]: emitter,
1086
- engine: {
1087
- step: this.inngestStep
1088
- },
1089
- abortSignal: abortController.signal
1338
+ const stepCallId = crypto.randomUUID();
1339
+ let stepRes;
1340
+ try {
1341
+ stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1342
+ let execResults;
1343
+ let suspended;
1344
+ let bailed;
1345
+ const { resumeData: timeTravelResumeData, validationError: timeTravelResumeValidationError } = await workflows.validateStepResumeData({
1346
+ resumeData: timeTravel?.stepResults[step.id]?.status === "suspended" ? timeTravel?.resumeData : void 0,
1347
+ step
1090
1348
  });
1091
- const endedAt = Date.now();
1092
- execResults = {
1093
- status: "success",
1094
- output: result,
1095
- startedAt,
1096
- endedAt,
1097
- payload: prevOutput,
1098
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1099
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1100
- };
1101
- } catch (e) {
1102
- execResults = {
1103
- status: "failed",
1104
- payload: prevOutput,
1105
- error: e instanceof Error ? e.message : String(e),
1106
- endedAt: Date.now(),
1107
- startedAt,
1108
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1109
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1110
- };
1111
- }
1112
- if (suspended) {
1113
- execResults = {
1114
- status: "suspended",
1115
- suspendedPayload: suspended.payload,
1116
- payload: prevOutput,
1117
- suspendedAt: Date.now(),
1118
- startedAt,
1119
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1120
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1121
- };
1122
- } else if (bailed) {
1123
- execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
1124
- }
1125
- if (execResults.status === "failed") {
1126
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1127
- const error = new Error(execResults.error);
1128
- stepAISpan?.error({ error });
1129
- throw error;
1349
+ let resumeDataToUse;
1350
+ if (timeTravelResumeData && !timeTravelResumeValidationError) {
1351
+ resumeDataToUse = timeTravelResumeData;
1352
+ } else if (timeTravelResumeData && timeTravelResumeValidationError) {
1353
+ this.logger.warn("Time travel resume data validation failed", {
1354
+ stepId: step.id,
1355
+ error: timeTravelResumeValidationError.message
1356
+ });
1357
+ } else if (resume?.steps[0] === step.id) {
1358
+ resumeDataToUse = resume?.resumePayload;
1130
1359
  }
1131
- }
1132
- await emitter.emit("watch", {
1133
- type: "watch",
1134
- payload: {
1135
- currentStep: {
1136
- id: step.id,
1137
- ...execResults
1138
- },
1139
- workflowState: {
1140
- status: "running",
1141
- steps: { ...stepResults, [step.id]: execResults },
1142
- result: null,
1143
- error: null
1360
+ try {
1361
+ if (validationError) {
1362
+ throw validationError;
1144
1363
  }
1145
- },
1146
- eventTimestamp: Date.now()
1364
+ const retryCount = this.getOrGenerateRetryCount(step.id);
1365
+ const result = await step.execute({
1366
+ runId: executionContext.runId,
1367
+ workflowId: executionContext.workflowId,
1368
+ mastra: this.mastra,
1369
+ requestContext,
1370
+ retryCount,
1371
+ writer: new tools.ToolStream(
1372
+ {
1373
+ prefix: "workflow-step",
1374
+ callId: stepCallId,
1375
+ name: step.id,
1376
+ runId: executionContext.runId
1377
+ },
1378
+ writableStream
1379
+ ),
1380
+ state: executionContext?.state ?? {},
1381
+ setState: (state) => {
1382
+ executionContext.state = state;
1383
+ },
1384
+ inputData,
1385
+ resumeData: resumeDataToUse,
1386
+ tracingContext: {
1387
+ currentSpan: stepSpan
1388
+ },
1389
+ getInitData: () => stepResults?.input,
1390
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
1391
+ suspend: async (suspendPayload, suspendOptions) => {
1392
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1393
+ if (suspendOptions?.resumeLabel) {
1394
+ const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1395
+ for (const label of resumeLabel) {
1396
+ executionContext.resumeLabels[label] = {
1397
+ stepId: step.id,
1398
+ foreachIndex: executionContext.foreachIndex
1399
+ };
1400
+ }
1401
+ }
1402
+ suspended = { payload: suspendPayload };
1403
+ },
1404
+ bail: (result2) => {
1405
+ bailed = { payload: result2 };
1406
+ },
1407
+ abort: () => {
1408
+ abortController?.abort();
1409
+ },
1410
+ [_constants.EMITTER_SYMBOL]: emitter,
1411
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1412
+ engine: {
1413
+ step: this.inngestStep
1414
+ },
1415
+ abortSignal: abortController.signal
1416
+ });
1417
+ const endedAt = Date.now();
1418
+ execResults = {
1419
+ status: "success",
1420
+ output: result,
1421
+ startedAt,
1422
+ endedAt,
1423
+ payload: inputData,
1424
+ resumedAt: resumeDataToUse ? startedAt : void 0,
1425
+ resumePayload: resumeDataToUse
1426
+ };
1427
+ } catch (e) {
1428
+ const stepFailure = {
1429
+ status: "failed",
1430
+ payload: inputData,
1431
+ error: e instanceof Error ? e.message : String(e),
1432
+ endedAt: Date.now(),
1433
+ startedAt,
1434
+ resumedAt: resumeDataToUse ? startedAt : void 0,
1435
+ resumePayload: resumeDataToUse
1436
+ };
1437
+ execResults = stepFailure;
1438
+ const fallbackErrorMessage = `Step ${step.id} failed`;
1439
+ stepSpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1440
+ throw new inngest.RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1441
+ cause: execResults
1442
+ });
1443
+ }
1444
+ if (suspended) {
1445
+ execResults = {
1446
+ status: "suspended",
1447
+ suspendPayload: suspended.payload,
1448
+ ...execResults.output ? { suspendOutput: execResults.output } : {},
1449
+ payload: inputData,
1450
+ suspendedAt: Date.now(),
1451
+ startedAt,
1452
+ resumedAt: resumeDataToUse ? startedAt : void 0,
1453
+ resumePayload: resumeDataToUse
1454
+ };
1455
+ } else if (bailed) {
1456
+ execResults = {
1457
+ status: "bailed",
1458
+ output: bailed.payload,
1459
+ payload: inputData,
1460
+ endedAt: Date.now(),
1461
+ startedAt
1462
+ };
1463
+ }
1464
+ if (execResults.status === "suspended") {
1465
+ await emitter.emit("watch", {
1466
+ type: "workflow-step-suspended",
1467
+ payload: {
1468
+ id: step.id,
1469
+ ...execResults
1470
+ }
1471
+ });
1472
+ } else {
1473
+ await emitter.emit("watch", {
1474
+ type: "workflow-step-result",
1475
+ payload: {
1476
+ id: step.id,
1477
+ ...execResults
1478
+ }
1479
+ });
1480
+ await emitter.emit("watch", {
1481
+ type: "workflow-step-finish",
1482
+ payload: {
1483
+ id: step.id,
1484
+ metadata: {}
1485
+ }
1486
+ });
1487
+ }
1488
+ stepSpan?.end({ output: execResults });
1489
+ return { result: execResults, executionContext, stepResults };
1147
1490
  });
1148
- if (execResults.status === "suspended") {
1149
- await emitter.emit("watch-v2", {
1150
- type: "workflow-step-suspended",
1151
- payload: {
1152
- id: step.id,
1153
- ...execResults
1154
- }
1155
- });
1156
- } else {
1157
- await emitter.emit("watch-v2", {
1158
- type: "workflow-step-result",
1159
- payload: {
1160
- id: step.id,
1161
- ...execResults
1162
- }
1163
- });
1164
- await emitter.emit("watch-v2", {
1165
- type: "workflow-step-finish",
1166
- payload: {
1167
- id: step.id,
1168
- metadata: {}
1169
- }
1170
- });
1171
- }
1172
- stepAISpan?.end({ output: execResults });
1173
- return { result: execResults, executionContext, stepResults };
1174
- });
1175
- if (disableScorers !== false) {
1491
+ } catch (e) {
1492
+ const stepFailure = e instanceof Error ? e?.cause : {
1493
+ status: "failed",
1494
+ error: e instanceof Error ? e.message : String(e),
1495
+ payload: inputData,
1496
+ startedAt,
1497
+ endedAt: Date.now()
1498
+ };
1499
+ stepRes = {
1500
+ result: stepFailure,
1501
+ executionContext,
1502
+ stepResults: {
1503
+ ...stepResults,
1504
+ [step.id]: stepFailure
1505
+ }
1506
+ };
1507
+ }
1508
+ if (disableScorers !== false && stepRes.result.status === "success") {
1176
1509
  await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1177
1510
  if (step.scorers) {
1178
1511
  await this.runScorers({
1179
1512
  scorers: step.scorers,
1180
1513
  runId: executionContext.runId,
1181
- input: prevOutput,
1514
+ input: inputData,
1182
1515
  output: stepRes.result,
1183
1516
  workflowId: executionContext.workflowId,
1184
1517
  stepId: step.id,
1185
- runtimeContext,
1518
+ requestContext,
1186
1519
  disableScorers,
1187
- tracingContext: { currentSpan: stepAISpan }
1520
+ tracingContext: { currentSpan: stepSpan }
1188
1521
  });
1189
1522
  }
1190
1523
  });
1191
1524
  }
1192
1525
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1193
- Object.assign(stepResults, stepRes.stepResults);
1526
+ executionContext.state = stepRes.executionContext.state;
1194
1527
  return stepRes.result;
1195
1528
  }
1196
1529
  async persistStepUpdate({
@@ -1207,22 +1540,27 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1207
1540
  await this.inngestStep.run(
1208
1541
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1209
1542
  async () => {
1543
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1544
+ if (!shouldPersistSnapshot) {
1545
+ return;
1546
+ }
1210
1547
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1211
1548
  workflowName: workflowId,
1212
1549
  runId,
1213
1550
  resourceId,
1214
1551
  snapshot: {
1215
1552
  runId,
1216
- value: {},
1553
+ status: workflowStatus,
1554
+ value: executionContext.state,
1217
1555
  context: stepResults,
1218
- activePaths: [],
1556
+ activePaths: executionContext.executionPath,
1557
+ activeStepsPath: executionContext.activeStepsPath,
1219
1558
  suspendedPaths: executionContext.suspendedPaths,
1559
+ resumeLabels: executionContext.resumeLabels,
1220
1560
  waitingPaths: {},
1221
1561
  serializedStepGraph,
1222
- status: workflowStatus,
1223
1562
  result,
1224
1563
  error,
1225
- // @ts-ignore
1226
1564
  timestamp: Date.now()
1227
1565
  }
1228
1566
  });
@@ -1234,20 +1572,19 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1234
1572
  runId,
1235
1573
  entry,
1236
1574
  prevOutput,
1237
- prevStep,
1238
1575
  stepResults,
1239
- serializedStepGraph,
1576
+ timeTravel,
1240
1577
  resume,
1241
1578
  executionContext,
1242
1579
  emitter,
1243
1580
  abortController,
1244
- runtimeContext,
1581
+ requestContext,
1245
1582
  writableStream,
1246
1583
  disableScorers,
1247
1584
  tracingContext
1248
1585
  }) {
1249
1586
  const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1250
- type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
1587
+ type: observability.SpanType.WORKFLOW_CONDITIONAL,
1251
1588
  name: `conditional: '${entry.conditions.length} conditions'`,
1252
1589
  input: prevOutput,
1253
1590
  attributes: {
@@ -1260,7 +1597,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1260
1597
  entry.conditions.map(
1261
1598
  (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1262
1599
  const evalSpan = conditionalSpan?.createChildSpan({
1263
- type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1600
+ type: observability.SpanType.WORKFLOW_CONDITIONAL_EVAL,
1264
1601
  name: `condition: '${index}'`,
1265
1602
  input: prevOutput,
1266
1603
  attributes: {
@@ -1269,52 +1606,55 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1269
1606
  tracingPolicy: this.options?.tracingPolicy
1270
1607
  });
1271
1608
  try {
1272
- const result = await cond({
1273
- runId,
1274
- workflowId,
1275
- mastra: this.mastra,
1276
- runtimeContext,
1277
- runCount: -1,
1278
- inputData: prevOutput,
1279
- tracingContext: {
1280
- currentSpan: evalSpan
1281
- },
1282
- getInitData: () => stepResults?.input,
1283
- getStepResult: (step) => {
1284
- if (!step?.id) {
1285
- return null;
1286
- }
1287
- const result2 = stepResults[step.id];
1288
- if (result2?.status === "success") {
1289
- return result2.output;
1290
- }
1291
- return null;
1292
- },
1293
- // TODO: this function shouldn't have suspend probably?
1294
- suspend: async (_suspendPayload) => {
1295
- },
1296
- bail: () => {
1297
- },
1298
- abort: () => {
1299
- abortController.abort();
1300
- },
1301
- [_constants.EMITTER_SYMBOL]: emitter,
1302
- [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1303
- // TODO: add streamVNext support
1304
- engine: {
1305
- step: this.inngestStep
1306
- },
1307
- abortSignal: abortController.signal,
1308
- writer: new tools.ToolStream(
1609
+ const result = await cond(
1610
+ workflows.createDeprecationProxy(
1309
1611
  {
1310
- prefix: "workflow-step",
1311
- callId: crypto.randomUUID(),
1312
- name: "conditional",
1313
- runId
1612
+ runId,
1613
+ workflowId,
1614
+ mastra: this.mastra,
1615
+ requestContext,
1616
+ retryCount: -1,
1617
+ inputData: prevOutput,
1618
+ state: executionContext.state,
1619
+ setState: (state) => {
1620
+ executionContext.state = state;
1621
+ },
1622
+ tracingContext: {
1623
+ currentSpan: evalSpan
1624
+ },
1625
+ getInitData: () => stepResults?.input,
1626
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
1627
+ // TODO: this function shouldn't have suspend probably?
1628
+ suspend: async (_suspendPayload) => {
1629
+ },
1630
+ bail: () => {
1631
+ },
1632
+ abort: () => {
1633
+ abortController.abort();
1634
+ },
1635
+ [_constants.EMITTER_SYMBOL]: emitter,
1636
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1637
+ engine: {
1638
+ step: this.inngestStep
1639
+ },
1640
+ abortSignal: abortController.signal,
1641
+ writer: new tools.ToolStream(
1642
+ {
1643
+ prefix: "workflow-step",
1644
+ callId: crypto.randomUUID(),
1645
+ name: "conditional",
1646
+ runId
1647
+ },
1648
+ writableStream
1649
+ )
1314
1650
  },
1315
- writableStream
1651
+ {
1652
+ paramName: "runCount",
1653
+ deprecationMessage: workflows.runCountDeprecationMessage,
1654
+ logger: this.logger
1655
+ }
1316
1656
  )
1317
- });
1657
+ );
1318
1658
  evalSpan?.end({
1319
1659
  output: result,
1320
1660
  attributes: {
@@ -1342,46 +1682,58 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1342
1682
  }
1343
1683
  });
1344
1684
  const results = await Promise.all(
1345
- stepsToRun.map(
1346
- (step, index) => this.executeEntry({
1347
- workflowId,
1348
- runId,
1349
- entry: step,
1350
- serializedStepGraph,
1351
- prevStep,
1685
+ stepsToRun.map(async (step, index) => {
1686
+ const currStepResult = stepResults[step.step.id];
1687
+ if (currStepResult && currStepResult.status === "success") {
1688
+ return currStepResult;
1689
+ }
1690
+ const result = await this.executeStep({
1691
+ step: step.step,
1692
+ prevOutput,
1352
1693
  stepResults,
1353
1694
  resume,
1695
+ timeTravel,
1354
1696
  executionContext: {
1355
1697
  workflowId,
1356
1698
  runId,
1357
1699
  executionPath: [...executionContext.executionPath, index],
1700
+ activeStepsPath: executionContext.activeStepsPath,
1358
1701
  suspendedPaths: executionContext.suspendedPaths,
1702
+ resumeLabels: executionContext.resumeLabels,
1359
1703
  retryConfig: executionContext.retryConfig,
1360
- executionSpan: executionContext.executionSpan
1704
+ state: executionContext.state
1361
1705
  },
1362
1706
  emitter,
1363
1707
  abortController,
1364
- runtimeContext,
1708
+ requestContext,
1365
1709
  writableStream,
1366
1710
  disableScorers,
1367
1711
  tracingContext: {
1368
1712
  currentSpan: conditionalSpan
1369
1713
  }
1370
- })
1371
- )
1714
+ });
1715
+ stepResults[step.step.id] = result;
1716
+ return result;
1717
+ })
1372
1718
  );
1373
- const hasFailed = results.find((result) => result.result.status === "failed");
1374
- const hasSuspended = results.find((result) => result.result.status === "suspended");
1719
+ const hasFailed = results.find((result) => result.status === "failed");
1720
+ const hasSuspended = results.find((result) => result.status === "suspended");
1375
1721
  if (hasFailed) {
1376
- execResults = { status: "failed", error: hasFailed.result.error };
1722
+ execResults = { status: "failed", error: hasFailed.error };
1377
1723
  } else if (hasSuspended) {
1378
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1724
+ execResults = {
1725
+ status: "suspended",
1726
+ suspendPayload: hasSuspended.suspendPayload,
1727
+ ...hasSuspended.suspendOutput ? { suspendOutput: hasSuspended.suspendOutput } : {}
1728
+ };
1379
1729
  } else {
1380
1730
  execResults = {
1381
1731
  status: "success",
1382
1732
  output: results.reduce((acc, result, index) => {
1383
- if (result.result.status === "success") {
1384
- acc[stepsToRun[index].step.id] = result.output;
1733
+ if (result.status === "success") {
1734
+ if ("step" in stepsToRun[index]) {
1735
+ acc[stepsToRun[index].step.id] = result.output;
1736
+ }
1385
1737
  }
1386
1738
  return acc;
1387
1739
  }, {})