@mastra/inngest 0.0.0-transpile-packages-20250731152758 → 0.0.0-unified-sidebar-20251010130811

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
@@ -2,17 +2,24 @@
2
2
 
3
3
  var crypto = require('crypto');
4
4
  var realtime = require('@inngest/realtime');
5
+ var aiTracing = require('@mastra/core/ai-tracing');
5
6
  var di = require('@mastra/core/di');
6
7
  var tools = require('@mastra/core/tools');
7
8
  var workflows = require('@mastra/core/workflows');
8
9
  var _constants = require('@mastra/core/workflows/_constants');
10
+ var inngest = require('inngest');
9
11
  var hono = require('inngest/hono');
10
12
  var zod = require('zod');
11
13
 
12
14
  // src/index.ts
13
- function serve({ mastra, inngest }) {
15
+ function serve({
16
+ mastra,
17
+ inngest,
18
+ functions: userFunctions = [],
19
+ registerOptions
20
+ }) {
14
21
  const wfs = mastra.getWorkflows();
15
- const functions = Array.from(
22
+ const workflowFunctions = Array.from(
16
23
  new Set(
17
24
  Object.values(wfs).flatMap((wf) => {
18
25
  if (wf instanceof InngestWorkflow) {
@@ -24,8 +31,9 @@ function serve({ mastra, inngest }) {
24
31
  )
25
32
  );
26
33
  return hono.serve({
34
+ ...registerOptions,
27
35
  client: inngest,
28
- functions
36
+ functions: [...workflowFunctions, ...userFunctions]
29
37
  });
30
38
  }
31
39
  var InngestRun = class extends workflows.Run {
@@ -53,9 +61,15 @@ var InngestRun = class extends workflows.Run {
53
61
  await new Promise((resolve) => setTimeout(resolve, 1e3));
54
62
  runs = await this.getRuns(eventId);
55
63
  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") {
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") {
59
73
  const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
60
74
  workflowName: this.workflowId,
61
75
  runId: this.runId
@@ -86,6 +100,7 @@ var InngestRun = class extends workflows.Run {
86
100
  await this.#mastra?.storage?.persistWorkflowSnapshot({
87
101
  workflowName: this.workflowId,
88
102
  runId: this.runId,
103
+ resourceId: this.resourceId,
89
104
  snapshot: {
90
105
  ...snapshot,
91
106
  status: "canceled"
@@ -94,11 +109,13 @@ var InngestRun = class extends workflows.Run {
94
109
  }
95
110
  }
96
111
  async start({
97
- inputData
112
+ inputData,
113
+ initialState
98
114
  }) {
99
115
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
100
116
  workflowName: this.workflowId,
101
117
  runId: this.runId,
118
+ resourceId: this.resourceId,
102
119
  snapshot: {
103
120
  runId: this.runId,
104
121
  serializedStepGraph: this.serializedStepGraph,
@@ -106,15 +123,20 @@ var InngestRun = class extends workflows.Run {
106
123
  context: {},
107
124
  activePaths: [],
108
125
  suspendedPaths: {},
126
+ waitingPaths: {},
109
127
  timestamp: Date.now(),
110
128
  status: "running"
111
129
  }
112
130
  });
131
+ const inputDataToUse = await this._validateInput(inputData);
132
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
113
133
  const eventOutput = await this.inngest.send({
114
134
  name: `workflow.${this.workflowId}`,
115
135
  data: {
116
- inputData,
117
- runId: this.runId
136
+ inputData: inputDataToUse,
137
+ initialState: initialStateToUse,
138
+ runId: this.runId,
139
+ resourceId: this.resourceId
118
140
  }
119
141
  });
120
142
  const eventId = eventOutput.ids[0];
@@ -150,17 +172,20 @@ var InngestRun = class extends workflows.Run {
150
172
  workflowName: this.workflowId,
151
173
  runId: this.runId
152
174
  });
175
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
176
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
153
177
  const eventOutput = await this.inngest.send({
154
178
  name: `workflow.${this.workflowId}`,
155
179
  data: {
156
- inputData: params.resumeData,
180
+ inputData: resumeDataToUse,
181
+ initialState: snapshot?.value ?? {},
157
182
  runId: this.runId,
158
183
  workflowId: this.workflowId,
159
184
  stepResults: snapshot?.context,
160
185
  resume: {
161
186
  steps,
162
187
  stepResults: snapshot?.context,
163
- resumePayload: params.resumeData,
188
+ resumePayload: resumeDataToUse,
164
189
  // @ts-ignore
165
190
  resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
166
191
  }
@@ -205,7 +230,11 @@ var InngestRun = class extends workflows.Run {
205
230
  const writer = writable.getWriter();
206
231
  const unwatch = this.watch(async (event) => {
207
232
  try {
208
- await writer.write(event);
233
+ const e = {
234
+ ...event,
235
+ type: event.type.replace("workflow-", "")
236
+ };
237
+ await writer.write(e);
209
238
  } catch {
210
239
  }
211
240
  }, "watch-v2");
@@ -236,8 +265,14 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
236
265
  #mastra;
237
266
  inngest;
238
267
  function;
268
+ flowControlConfig;
239
269
  constructor(params, inngest) {
240
- super(params);
270
+ const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
271
+ super(workflowParams);
272
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
273
+ ([_, value]) => value !== void 0
274
+ );
275
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
241
276
  this.#mastra = params.mastra;
242
277
  this.inngest = inngest;
243
278
  }
@@ -258,27 +293,6 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
258
293
  const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
259
294
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
260
295
  }
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
296
  __registerMastra(mastra) {
283
297
  this.#mastra = mastra;
284
298
  this.executionEngine.__registerMastra(mastra);
@@ -297,23 +311,14 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
297
311
  }
298
312
  }
299
313
  }
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
314
+ /**
315
+ * @deprecated Use createRunAsync() instead.
316
+ * @throws {Error} Always throws an error directing users to use createRunAsync()
317
+ */
318
+ createRun(_options) {
319
+ throw new Error(
320
+ "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
321
  );
315
- this.runs.set(runIdToUse, run);
316
- return run;
317
322
  }
318
323
  async createRunAsync(options) {
319
324
  const runIdToUse = options?.runId || crypto.randomUUID();
@@ -321,27 +326,35 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
321
326
  {
322
327
  workflowId: this.id,
323
328
  runId: runIdToUse,
329
+ resourceId: options?.resourceId,
324
330
  executionEngine: this.executionEngine,
325
331
  executionGraph: this.executionGraph,
326
332
  serializedStepGraph: this.serializedStepGraph,
327
333
  mastra: this.#mastra,
328
334
  retryConfig: this.retryConfig,
329
- cleanup: () => this.runs.delete(runIdToUse)
335
+ cleanup: () => this.runs.delete(runIdToUse),
336
+ workflowSteps: this.steps
330
337
  },
331
338
  this.inngest
332
339
  );
333
340
  this.runs.set(runIdToUse, run);
334
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
335
- if (!workflowSnapshotInStorage) {
341
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
342
+ workflowStatus: run.workflowRunStatus,
343
+ stepResults: {}
344
+ });
345
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
346
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
336
347
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
337
348
  workflowName: this.id,
338
349
  runId: runIdToUse,
350
+ resourceId: options?.resourceId,
339
351
  snapshot: {
340
352
  runId: runIdToUse,
341
353
  status: "pending",
342
354
  value: {},
343
355
  context: {},
344
356
  activePaths: [],
357
+ waitingPaths: {},
345
358
  serializedStepGraph: this.serializedStepGraph,
346
359
  suspendedPaths: {},
347
360
  result: void 0,
@@ -362,11 +375,13 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
362
375
  id: `workflow.${this.id}`,
363
376
  // @ts-ignore
364
377
  retries: this.retryConfig?.attempts ?? 0,
365
- cancelOn: [{ event: `cancel.workflow.${this.id}` }]
378
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
379
+ // Spread flow control configuration
380
+ ...this.flowControlConfig
366
381
  },
367
382
  { event: `workflow.${this.id}` },
368
383
  async ({ event, step, attempt, publish }) => {
369
- let { inputData, runId, resume } = event.data;
384
+ let { inputData, initialState, runId, resourceId, resume, outputOptions } = event.data;
370
385
  if (!runId) {
371
386
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
372
387
  return crypto.randomUUID();
@@ -394,19 +409,32 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
394
409
  once: (_event, _callback) => {
395
410
  }
396
411
  };
397
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
412
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
398
413
  const result = await engine.execute({
399
414
  workflowId: this.id,
400
415
  runId,
416
+ resourceId,
401
417
  graph: this.executionGraph,
402
418
  serializedStepGraph: this.serializedStepGraph,
403
419
  input: inputData,
420
+ initialState,
404
421
  emitter,
405
422
  retryConfig: this.retryConfig,
406
423
  runtimeContext: new di.RuntimeContext(),
407
424
  // TODO
408
425
  resume,
409
- abortController: new AbortController()
426
+ abortController: new AbortController(),
427
+ currentSpan: void 0,
428
+ // TODO: Pass actual parent AI span from workflow execution context
429
+ outputOptions
430
+ });
431
+ await step.run(`workflow.${this.id}.finalize`, async () => {
432
+ if (result.status === "failed") {
433
+ throw new inngest.NonRetriableError(`Workflow failed`, {
434
+ cause: result
435
+ });
436
+ }
437
+ return result;
410
438
  });
411
439
  return { result, runId };
412
440
  }
@@ -440,17 +468,16 @@ function createStep(params) {
440
468
  if (isAgent(params)) {
441
469
  return {
442
470
  id: params.name,
471
+ description: params.getDescription(),
443
472
  // @ts-ignore
444
473
  inputSchema: zod.z.object({
445
474
  prompt: zod.z.string()
446
- // resourceId: z.string().optional(),
447
- // threadId: z.string().optional(),
448
475
  }),
449
476
  // @ts-ignore
450
477
  outputSchema: zod.z.object({
451
478
  text: zod.z.string()
452
479
  }),
453
- execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
480
+ execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
454
481
  let streamPromise = {};
455
482
  streamPromise.promise = new Promise((resolve, reject) => {
456
483
  streamPromise.resolve = resolve;
@@ -460,50 +487,66 @@ function createStep(params) {
460
487
  name: params.name,
461
488
  args: inputData
462
489
  };
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":
490
+ if ((await params.getLLM()).getModel().specificationVersion === `v2`) {
491
+ const { fullStream } = await params.stream(inputData.prompt, {
492
+ runtimeContext,
493
+ tracingContext,
494
+ onFinish: (result) => {
495
+ streamPromise.resolve(result.text);
496
+ },
497
+ abortSignal
498
+ });
499
+ if (abortSignal.aborted) {
500
+ return abort();
501
+ }
502
+ await emitter.emit("watch-v2", {
503
+ type: "tool-call-streaming-start",
504
+ ...toolData ?? {}
505
+ });
506
+ for await (const chunk of fullStream) {
507
+ if (chunk.type === "text-delta") {
508
+ await emitter.emit("watch-v2", {
509
+ type: "tool-call-delta",
510
+ ...toolData ?? {},
511
+ argsTextDelta: chunk.payload.text
512
+ });
513
+ }
514
+ }
515
+ } else {
516
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
517
+ runtimeContext,
518
+ tracingContext,
519
+ onFinish: (result) => {
520
+ streamPromise.resolve(result.text);
521
+ },
522
+ abortSignal
523
+ });
524
+ if (abortSignal.aborted) {
525
+ return abort();
526
+ }
527
+ await emitter.emit("watch-v2", {
528
+ type: "tool-call-streaming-start",
529
+ ...toolData ?? {}
530
+ });
531
+ for await (const chunk of fullStream) {
532
+ if (chunk.type === "text-delta") {
482
533
  await emitter.emit("watch-v2", {
483
534
  type: "tool-call-delta",
484
- ...toolData,
535
+ ...toolData ?? {},
485
536
  argsTextDelta: chunk.textDelta
486
537
  });
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;
538
+ }
501
539
  }
502
540
  }
541
+ await emitter.emit("watch-v2", {
542
+ type: "tool-call-streaming-finish",
543
+ ...toolData ?? {}
544
+ });
503
545
  return {
504
546
  text: await streamPromise.promise
505
547
  };
506
- }
548
+ },
549
+ component: params.component
507
550
  };
508
551
  }
509
552
  if (isTool(params)) {
@@ -514,15 +557,20 @@ function createStep(params) {
514
557
  // TODO: tool probably should have strong id type
515
558
  // @ts-ignore
516
559
  id: params.id,
560
+ description: params.description,
517
561
  inputSchema: params.inputSchema,
518
562
  outputSchema: params.outputSchema,
519
- execute: async ({ inputData, mastra, runtimeContext }) => {
563
+ execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
520
564
  return params.execute({
521
565
  context: inputData,
522
- mastra,
523
- runtimeContext
566
+ mastra: aiTracing.wrapMastra(mastra, tracingContext),
567
+ runtimeContext,
568
+ tracingContext,
569
+ suspend,
570
+ resumeData
524
571
  });
525
- }
572
+ },
573
+ component: "TOOL"
526
574
  };
527
575
  }
528
576
  return {
@@ -538,7 +586,10 @@ function createStep(params) {
538
586
  function init(inngest) {
539
587
  return {
540
588
  createWorkflow(params) {
541
- return new InngestWorkflow(params, inngest);
589
+ return new InngestWorkflow(
590
+ params,
591
+ inngest
592
+ );
542
593
  },
543
594
  createStep,
544
595
  cloneStep(step, opts) {
@@ -547,7 +598,11 @@ function init(inngest) {
547
598
  description: step.description,
548
599
  inputSchema: step.inputSchema,
549
600
  outputSchema: step.outputSchema,
550
- execute: step.execute
601
+ resumeSchema: step.resumeSchema,
602
+ suspendSchema: step.suspendSchema,
603
+ stateSchema: step.stateSchema,
604
+ execute: step.execute,
605
+ component: step.component
551
606
  };
552
607
  },
553
608
  cloneWorkflow(workflow, opts) {
@@ -567,19 +622,19 @@ function init(inngest) {
567
622
  var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
568
623
  inngestStep;
569
624
  inngestAttempts;
570
- constructor(mastra, inngestStep, inngestAttempts = 0) {
571
- super({ mastra });
625
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
626
+ super({ mastra, options });
572
627
  this.inngestStep = inngestStep;
573
628
  this.inngestAttempts = inngestAttempts;
574
629
  }
575
630
  async execute(params) {
576
631
  await params.emitter.emit("watch-v2", {
577
- type: "start",
632
+ type: "workflow-start",
578
633
  payload: { runId: params.runId }
579
634
  });
580
635
  const result = await super.execute(params);
581
636
  await params.emitter.emit("watch-v2", {
582
- type: "finish",
637
+ type: "workflow-finish",
583
638
  payload: { runId: params.runId }
584
639
  });
585
640
  return result;
@@ -631,7 +686,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
631
686
  });
632
687
  const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
633
688
  if (stepResult?.status === "suspended") {
634
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
689
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
635
690
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
636
691
  }
637
692
  return [];
@@ -641,33 +696,6 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
641
696
  executionSpan?.end();
642
697
  return base;
643
698
  }
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
699
  // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
672
700
  // await this.inngestStep.sleep(id, duration);
673
701
  // }
@@ -680,9 +708,20 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
680
708
  emitter,
681
709
  abortController,
682
710
  runtimeContext,
683
- writableStream
711
+ executionContext,
712
+ writableStream,
713
+ tracingContext
684
714
  }) {
685
715
  let { duration, fn } = entry;
716
+ const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
717
+ type: aiTracing.AISpanType.WORKFLOW_SLEEP,
718
+ name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
719
+ attributes: {
720
+ durationMs: duration,
721
+ sleepType: fn ? "dynamic" : "fixed"
722
+ },
723
+ tracingPolicy: this.options?.tracingPolicy
724
+ });
686
725
  if (fn) {
687
726
  const stepCallId = crypto.randomUUID();
688
727
  duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
@@ -692,18 +731,16 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
692
731
  mastra: this.mastra,
693
732
  runtimeContext,
694
733
  inputData: prevOutput,
734
+ state: executionContext.state,
735
+ setState: (state) => {
736
+ executionContext.state = state;
737
+ },
695
738
  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;
739
+ tracingContext: {
740
+ currentSpan: sleepSpan
706
741
  },
742
+ getInitData: () => stepResults?.input,
743
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
707
744
  // TODO: this function shouldn't have suspend probably?
708
745
  suspend: async (_suspendPayload) => {
709
746
  },
@@ -713,11 +750,13 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
713
750
  abortController?.abort();
714
751
  },
715
752
  [_constants.EMITTER_SYMBOL]: emitter,
753
+ // TODO: add streamVNext support
754
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
716
755
  engine: { step: this.inngestStep },
717
756
  abortSignal: abortController?.signal,
718
757
  writer: new tools.ToolStream(
719
758
  {
720
- prefix: "step",
759
+ prefix: "workflow-step",
721
760
  callId: stepCallId,
722
761
  name: "sleep",
723
762
  runId
@@ -726,8 +765,19 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
726
765
  )
727
766
  });
728
767
  });
768
+ sleepSpan?.update({
769
+ attributes: {
770
+ durationMs: duration
771
+ }
772
+ });
773
+ }
774
+ try {
775
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
776
+ sleepSpan?.end();
777
+ } catch (e) {
778
+ sleepSpan?.error({ error: e });
779
+ throw e;
729
780
  }
730
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
731
781
  }
732
782
  async executeSleepUntil({
733
783
  workflowId,
@@ -738,9 +788,21 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
738
788
  emitter,
739
789
  abortController,
740
790
  runtimeContext,
741
- writableStream
791
+ executionContext,
792
+ writableStream,
793
+ tracingContext
742
794
  }) {
743
795
  let { date, fn } = entry;
796
+ const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
797
+ type: aiTracing.AISpanType.WORKFLOW_SLEEP,
798
+ name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
799
+ attributes: {
800
+ untilDate: date,
801
+ durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
802
+ sleepType: fn ? "dynamic" : "fixed"
803
+ },
804
+ tracingPolicy: this.options?.tracingPolicy
805
+ });
744
806
  if (fn) {
745
807
  date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
746
808
  const stepCallId = crypto.randomUUID();
@@ -750,18 +812,16 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
750
812
  mastra: this.mastra,
751
813
  runtimeContext,
752
814
  inputData: prevOutput,
815
+ state: executionContext.state,
816
+ setState: (state) => {
817
+ executionContext.state = state;
818
+ },
753
819
  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;
820
+ tracingContext: {
821
+ currentSpan: sleepUntilSpan
764
822
  },
823
+ getInitData: () => stepResults?.input,
824
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
765
825
  // TODO: this function shouldn't have suspend probably?
766
826
  suspend: async (_suspendPayload) => {
767
827
  },
@@ -771,11 +831,13 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
771
831
  abortController?.abort();
772
832
  },
773
833
  [_constants.EMITTER_SYMBOL]: emitter,
834
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
835
+ // TODO: add streamVNext support
774
836
  engine: { step: this.inngestStep },
775
837
  abortSignal: abortController?.signal,
776
838
  writer: new tools.ToolStream(
777
839
  {
778
- prefix: "step",
840
+ prefix: "workflow-step",
779
841
  callId: stepCallId,
780
842
  name: "sleep",
781
843
  runId
@@ -784,11 +846,27 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
784
846
  )
785
847
  });
786
848
  });
849
+ if (date && !(date instanceof Date)) {
850
+ date = new Date(date);
851
+ }
852
+ const time = !date ? 0 : date.getTime() - Date.now();
853
+ sleepUntilSpan?.update({
854
+ attributes: {
855
+ durationMs: Math.max(0, time)
856
+ }
857
+ });
787
858
  }
788
859
  if (!(date instanceof Date)) {
860
+ sleepUntilSpan?.end();
789
861
  return;
790
862
  }
791
- await this.inngestStep.sleepUntil(entry.id, date);
863
+ try {
864
+ await this.inngestStep.sleepUntil(entry.id, date);
865
+ sleepUntilSpan?.end();
866
+ } catch (e) {
867
+ sleepUntilSpan?.error({ error: e });
868
+ throw e;
869
+ }
792
870
  }
793
871
  async executeWaitForEvent({ event, timeout }) {
794
872
  const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
@@ -809,8 +887,24 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
809
887
  emitter,
810
888
  abortController,
811
889
  runtimeContext,
812
- writableStream
890
+ tracingContext,
891
+ writableStream,
892
+ disableScorers
813
893
  }) {
894
+ const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
895
+ name: `workflow step: '${step.id}'`,
896
+ type: aiTracing.AISpanType.WORKFLOW_STEP,
897
+ input: prevOutput,
898
+ attributes: {
899
+ stepId: step.id
900
+ },
901
+ tracingPolicy: this.options?.tracingPolicy
902
+ });
903
+ const { inputData, validationError } = await workflows.validateStepInput({
904
+ prevOutput,
905
+ step,
906
+ validateInputs: this.options?.validateInputs ?? false
907
+ });
814
908
  const startedAt = await this.inngestStep.run(
815
909
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
816
910
  async () => {
@@ -837,11 +931,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
837
931
  eventTimestamp: Date.now()
838
932
  });
839
933
  await emitter.emit("watch-v2", {
840
- type: "step-start",
934
+ type: "workflow-step-start",
841
935
  payload: {
842
936
  id: step.id,
843
937
  status: "running",
844
- payload: prevOutput,
938
+ payload: inputData,
845
939
  startedAt: startedAt2
846
940
  }
847
941
  });
@@ -852,38 +946,60 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
852
946
  const isResume = !!resume?.steps?.length;
853
947
  let result;
854
948
  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: {
949
+ try {
950
+ if (isResume) {
951
+ runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
952
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
953
+ workflowName: step.id,
954
+ runId
955
+ });
956
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
957
+ function: step.getFunction(),
958
+ data: {
959
+ inputData,
960
+ initialState: executionContext.state ?? snapshot?.value ?? {},
867
961
  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]]
962
+ resume: {
963
+ runId,
964
+ steps: resume.steps.slice(1),
965
+ stepResults: snapshot?.context,
966
+ resumePayload: resume.resumePayload,
967
+ // @ts-ignore
968
+ resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
969
+ },
970
+ outputOptions: { includeState: true }
873
971
  }
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;
972
+ });
973
+ result = invokeResp.result;
974
+ runId = invokeResp.runId;
975
+ executionContext.state = invokeResp.result.state;
976
+ } else {
977
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
978
+ function: step.getFunction(),
979
+ data: {
980
+ inputData,
981
+ initialState: executionContext.state ?? {},
982
+ outputOptions: { includeState: true }
983
+ }
984
+ });
985
+ result = invokeResp.result;
986
+ runId = invokeResp.runId;
987
+ executionContext.state = invokeResp.result.state;
988
+ }
989
+ } catch (e) {
990
+ const errorCause = e?.cause;
991
+ if (errorCause && typeof errorCause === "object") {
992
+ result = errorCause;
993
+ runId = errorCause.runId || crypto.randomUUID();
994
+ } else {
995
+ runId = crypto.randomUUID();
996
+ result = {
997
+ status: "failed",
998
+ error: e instanceof Error ? e : new Error(String(e)),
999
+ steps: {},
1000
+ input: inputData
1001
+ };
1002
+ }
887
1003
  }
888
1004
  const res = await this.inngestStep.run(
889
1005
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
@@ -907,7 +1023,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
907
1023
  eventTimestamp: Date.now()
908
1024
  });
909
1025
  await emitter.emit("watch-v2", {
910
- type: "step-result",
1026
+ type: "workflow-step-result",
911
1027
  payload: {
912
1028
  id: step.id,
913
1029
  status: "failed",
@@ -922,7 +1038,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
922
1038
  return stepRes2?.status === "suspended";
923
1039
  });
924
1040
  for (const [stepName, stepResult] of suspendedSteps) {
925
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
1041
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
926
1042
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
927
1043
  await emitter.emit("watch", {
928
1044
  type: "watch",
@@ -930,7 +1046,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
930
1046
  currentStep: {
931
1047
  id: step.id,
932
1048
  status: "suspended",
933
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1049
+ payload: stepResult.payload,
1050
+ suspendPayload: {
1051
+ ...stepResult?.suspendPayload,
1052
+ __workflow_meta: { runId, path: suspendPath }
1053
+ }
934
1054
  },
935
1055
  workflowState: {
936
1056
  status: "running",
@@ -942,7 +1062,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
942
1062
  eventTimestamp: Date.now()
943
1063
  });
944
1064
  await emitter.emit("watch-v2", {
945
- type: "step-suspended",
1065
+ type: "workflow-step-suspended",
946
1066
  payload: {
947
1067
  id: step.id,
948
1068
  status: "suspended"
@@ -952,7 +1072,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
952
1072
  executionContext,
953
1073
  result: {
954
1074
  status: "suspended",
955
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1075
+ payload: stepResult.payload,
1076
+ suspendPayload: {
1077
+ ...stepResult?.suspendPayload,
1078
+ __workflow_meta: { runId, path: suspendPath }
1079
+ }
956
1080
  }
957
1081
  };
958
1082
  }
@@ -999,7 +1123,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
999
1123
  eventTimestamp: Date.now()
1000
1124
  });
1001
1125
  await emitter.emit("watch-v2", {
1002
- type: "step-result",
1126
+ type: "workflow-step-result",
1003
1127
  payload: {
1004
1128
  id: step.id,
1005
1129
  status: "success",
@@ -1007,7 +1131,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1007
1131
  }
1008
1132
  });
1009
1133
  await emitter.emit("watch-v2", {
1010
- type: "step-finish",
1134
+ type: "workflow-step-finish",
1011
1135
  payload: {
1012
1136
  id: step.id,
1013
1137
  metadata: {}
@@ -1017,136 +1141,193 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1017
1141
  }
1018
1142
  );
1019
1143
  Object.assign(executionContext, res.executionContext);
1020
- return res.result;
1144
+ return {
1145
+ ...res.result,
1146
+ startedAt,
1147
+ endedAt: Date.now(),
1148
+ payload: inputData,
1149
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1150
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1151
+ };
1021
1152
  }
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;
1153
+ let stepRes;
1154
+ try {
1155
+ stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1156
+ let execResults;
1157
+ let suspended;
1158
+ let bailed;
1159
+ try {
1160
+ if (validationError) {
1161
+ throw validationError;
1162
+ }
1163
+ const result = await step.execute({
1164
+ runId: executionContext.runId,
1165
+ mastra: this.mastra,
1166
+ runtimeContext,
1167
+ writableStream,
1168
+ state: executionContext?.state ?? {},
1169
+ setState: (state) => {
1170
+ executionContext.state = state;
1171
+ },
1172
+ inputData,
1173
+ resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1174
+ tracingContext: {
1175
+ currentSpan: stepAISpan
1176
+ },
1177
+ getInitData: () => stepResults?.input,
1178
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
1179
+ suspend: async (suspendPayload) => {
1180
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1181
+ suspended = { payload: suspendPayload };
1182
+ },
1183
+ bail: (result2) => {
1184
+ bailed = { payload: result2 };
1185
+ },
1186
+ resume: {
1187
+ steps: resume?.steps?.slice(1) || [],
1188
+ resumePayload: resume?.resumePayload,
1189
+ // @ts-ignore
1190
+ runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
1191
+ },
1192
+ [_constants.EMITTER_SYMBOL]: emitter,
1193
+ engine: {
1194
+ step: this.inngestStep
1195
+ },
1196
+ abortSignal: abortController.signal
1197
+ });
1198
+ const endedAt = Date.now();
1199
+ execResults = {
1200
+ status: "success",
1201
+ output: result,
1202
+ startedAt,
1203
+ endedAt,
1204
+ payload: inputData,
1205
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1206
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1207
+ };
1208
+ } catch (e) {
1209
+ const stepFailure = {
1210
+ status: "failed",
1211
+ payload: inputData,
1212
+ error: e instanceof Error ? e.message : String(e),
1213
+ endedAt: Date.now(),
1214
+ startedAt,
1215
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1216
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1217
+ };
1218
+ execResults = stepFailure;
1219
+ const fallbackErrorMessage = `Step ${step.id} failed`;
1220
+ stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1221
+ throw new inngest.RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1222
+ cause: execResults
1223
+ });
1224
+ }
1225
+ if (suspended) {
1226
+ execResults = {
1227
+ status: "suspended",
1228
+ suspendPayload: suspended.payload,
1229
+ payload: inputData,
1230
+ suspendedAt: Date.now(),
1231
+ startedAt,
1232
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1233
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1234
+ };
1235
+ } else if (bailed) {
1236
+ execResults = {
1237
+ status: "bailed",
1238
+ output: bailed.payload,
1239
+ payload: inputData,
1240
+ endedAt: Date.now(),
1241
+ startedAt
1242
+ };
1243
+ }
1244
+ await emitter.emit("watch", {
1245
+ type: "watch",
1246
+ payload: {
1247
+ currentStep: {
1248
+ id: step.id,
1249
+ ...execResults
1250
+ },
1251
+ workflowState: {
1252
+ status: "running",
1253
+ steps: { ...stepResults, [step.id]: execResults },
1254
+ result: null,
1255
+ error: null
1039
1256
  }
1040
- return null;
1041
- },
1042
- suspend: async (suspendPayload) => {
1043
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1044
- suspended = { payload: suspendPayload };
1045
- },
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
1257
  },
1059
- abortSignal: abortController.signal
1258
+ eventTimestamp: Date.now()
1060
1259
  });
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;
1260
+ if (execResults.status === "suspended") {
1261
+ await emitter.emit("watch-v2", {
1262
+ type: "workflow-step-suspended",
1263
+ payload: {
1264
+ id: step.id,
1265
+ ...execResults
1266
+ }
1267
+ });
1268
+ } else {
1269
+ await emitter.emit("watch-v2", {
1270
+ type: "workflow-step-result",
1271
+ payload: {
1272
+ id: step.id,
1273
+ ...execResults
1274
+ }
1275
+ });
1276
+ await emitter.emit("watch-v2", {
1277
+ type: "workflow-step-finish",
1278
+ payload: {
1279
+ id: step.id,
1280
+ metadata: {}
1281
+ }
1282
+ });
1098
1283
  }
1099
- }
1100
- await emitter.emit("watch", {
1101
- type: "watch",
1102
- 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()
1284
+ stepAISpan?.end({ output: execResults });
1285
+ return { result: execResults, executionContext, stepResults };
1115
1286
  });
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
- });
1287
+ } catch (e) {
1288
+ const stepFailure = e instanceof Error ? e?.cause : {
1289
+ status: "failed",
1290
+ error: e instanceof Error ? e.message : String(e),
1291
+ payload: inputData,
1292
+ startedAt,
1293
+ endedAt: Date.now()
1294
+ };
1295
+ stepRes = {
1296
+ result: stepFailure,
1297
+ executionContext,
1298
+ stepResults: {
1299
+ ...stepResults,
1300
+ [step.id]: stepFailure
1301
+ }
1302
+ };
1303
+ }
1304
+ if (disableScorers !== false && stepRes.result.status === "success") {
1305
+ await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1306
+ if (step.scorers) {
1307
+ await this.runScorers({
1308
+ scorers: step.scorers,
1309
+ runId: executionContext.runId,
1310
+ input: inputData,
1311
+ output: stepRes.result,
1312
+ workflowId: executionContext.workflowId,
1313
+ stepId: step.id,
1314
+ runtimeContext,
1315
+ disableScorers,
1316
+ tracingContext: { currentSpan: stepAISpan }
1317
+ });
1318
+ }
1319
+ });
1320
+ }
1142
1321
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1143
1322
  Object.assign(stepResults, stepRes.stepResults);
1323
+ executionContext.state = stepRes.executionContext.state;
1144
1324
  return stepRes.result;
1145
1325
  }
1146
1326
  async persistStepUpdate({
1147
1327
  workflowId,
1148
1328
  runId,
1149
1329
  stepResults,
1330
+ resourceId,
1150
1331
  executionContext,
1151
1332
  serializedStepGraph,
1152
1333
  workflowStatus,
@@ -1156,15 +1337,21 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1156
1337
  await this.inngestStep.run(
1157
1338
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1158
1339
  async () => {
1340
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1341
+ if (!shouldPersistSnapshot) {
1342
+ return;
1343
+ }
1159
1344
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1160
1345
  workflowName: workflowId,
1161
1346
  runId,
1347
+ resourceId,
1162
1348
  snapshot: {
1163
1349
  runId,
1164
- value: {},
1350
+ value: executionContext.state,
1165
1351
  context: stepResults,
1166
1352
  activePaths: [],
1167
1353
  suspendedPaths: executionContext.suspendedPaths,
1354
+ waitingPaths: {},
1168
1355
  serializedStepGraph,
1169
1356
  status: workflowStatus,
1170
1357
  result,
@@ -1189,12 +1376,32 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1189
1376
  emitter,
1190
1377
  abortController,
1191
1378
  runtimeContext,
1192
- writableStream
1379
+ writableStream,
1380
+ disableScorers,
1381
+ tracingContext
1193
1382
  }) {
1383
+ const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1384
+ type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
1385
+ name: `conditional: '${entry.conditions.length} conditions'`,
1386
+ input: prevOutput,
1387
+ attributes: {
1388
+ conditionCount: entry.conditions.length
1389
+ },
1390
+ tracingPolicy: this.options?.tracingPolicy
1391
+ });
1194
1392
  let execResults;
1195
1393
  const truthyIndexes = (await Promise.all(
1196
1394
  entry.conditions.map(
1197
1395
  (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1396
+ const evalSpan = conditionalSpan?.createChildSpan({
1397
+ type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1398
+ name: `condition: '${index}'`,
1399
+ input: prevOutput,
1400
+ attributes: {
1401
+ conditionIndex: index
1402
+ },
1403
+ tracingPolicy: this.options?.tracingPolicy
1404
+ });
1198
1405
  try {
1199
1406
  const result = await cond({
1200
1407
  runId,
@@ -1203,17 +1410,15 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1203
1410
  runtimeContext,
1204
1411
  runCount: -1,
1205
1412
  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;
1413
+ state: executionContext.state,
1414
+ setState: (state) => {
1415
+ executionContext.state = state;
1216
1416
  },
1417
+ tracingContext: {
1418
+ currentSpan: evalSpan
1419
+ },
1420
+ getInitData: () => stepResults?.input,
1421
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
1217
1422
  // TODO: this function shouldn't have suspend probably?
1218
1423
  suspend: async (_suspendPayload) => {
1219
1424
  },
@@ -1223,13 +1428,15 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1223
1428
  abortController.abort();
1224
1429
  },
1225
1430
  [_constants.EMITTER_SYMBOL]: emitter,
1431
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1432
+ // TODO: add streamVNext support
1226
1433
  engine: {
1227
1434
  step: this.inngestStep
1228
1435
  },
1229
1436
  abortSignal: abortController.signal,
1230
1437
  writer: new tools.ToolStream(
1231
1438
  {
1232
- prefix: "step",
1439
+ prefix: "workflow-step",
1233
1440
  callId: crypto.randomUUID(),
1234
1441
  name: "conditional",
1235
1442
  runId
@@ -1237,36 +1444,59 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1237
1444
  writableStream
1238
1445
  )
1239
1446
  });
1447
+ evalSpan?.end({
1448
+ output: result,
1449
+ attributes: {
1450
+ result: !!result
1451
+ }
1452
+ });
1240
1453
  return result ? index : null;
1241
1454
  } catch (e) {
1455
+ evalSpan?.error({
1456
+ error: e instanceof Error ? e : new Error(String(e)),
1457
+ attributes: {
1458
+ result: false
1459
+ }
1460
+ });
1242
1461
  return null;
1243
1462
  }
1244
1463
  })
1245
1464
  )
1246
1465
  )).filter((index) => index !== null);
1247
1466
  const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1467
+ conditionalSpan?.update({
1468
+ attributes: {
1469
+ truthyIndexes,
1470
+ selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1471
+ }
1472
+ });
1248
1473
  const results = await Promise.all(
1249
1474
  stepsToRun.map(
1250
1475
  (step, index) => this.executeEntry({
1251
1476
  workflowId,
1252
1477
  runId,
1253
1478
  entry: step,
1479
+ serializedStepGraph,
1254
1480
  prevStep,
1255
1481
  stepResults,
1256
1482
  resume,
1257
- serializedStepGraph,
1258
1483
  executionContext: {
1259
1484
  workflowId,
1260
1485
  runId,
1261
1486
  executionPath: [...executionContext.executionPath, index],
1262
1487
  suspendedPaths: executionContext.suspendedPaths,
1263
1488
  retryConfig: executionContext.retryConfig,
1264
- executionSpan: executionContext.executionSpan
1489
+ executionSpan: executionContext.executionSpan,
1490
+ state: executionContext.state
1265
1491
  },
1266
1492
  emitter,
1267
1493
  abortController,
1268
1494
  runtimeContext,
1269
- writableStream
1495
+ writableStream,
1496
+ disableScorers,
1497
+ tracingContext: {
1498
+ currentSpan: conditionalSpan
1499
+ }
1270
1500
  })
1271
1501
  )
1272
1502
  );
@@ -1275,7 +1505,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1275
1505
  if (hasFailed) {
1276
1506
  execResults = { status: "failed", error: hasFailed.result.error };
1277
1507
  } else if (hasSuspended) {
1278
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1508
+ execResults = { status: "suspended", suspendPayload: hasSuspended.result.suspendPayload };
1279
1509
  } else {
1280
1510
  execResults = {
1281
1511
  status: "success",
@@ -1287,6 +1517,15 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
1287
1517
  }, {})
1288
1518
  };
1289
1519
  }
1520
+ if (execResults.status === "failed") {
1521
+ conditionalSpan?.error({
1522
+ error: new Error(execResults.error)
1523
+ });
1524
+ } else {
1525
+ conditionalSpan?.end({
1526
+ output: execResults.output || execResults
1527
+ });
1528
+ }
1290
1529
  return execResults;
1291
1530
  }
1292
1531
  };