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