@mastra/inngest 0.0.0-new-scorer-api-20250801075530 → 0.0.0-partial-response-backport-20251204204441

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