@mastra/inngest 0.0.0-generate-message-id-20250512171942 → 0.0.0-issue-7498-20250905233741

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,35 +2,45 @@
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
- var vNext = require('@mastra/core/workflows/vNext');
7
+ var tools = require('@mastra/core/tools');
8
+ var workflows = require('@mastra/core/workflows');
9
+ var _constants = require('@mastra/core/workflows/_constants');
7
10
  var hono = require('inngest/hono');
11
+ var zod = require('zod');
8
12
 
9
13
  // src/index.ts
10
14
  function serve({ mastra, inngest }) {
11
- const wfs = mastra.vnext_getWorkflows();
12
- const functions = Object.values(wfs).flatMap((wf) => {
13
- if (wf instanceof InngestWorkflow) {
14
- wf.__registerMastra(mastra);
15
- return wf.getFunctions();
16
- }
17
- return [];
18
- });
15
+ const wfs = mastra.getWorkflows();
16
+ const functions = Array.from(
17
+ new Set(
18
+ Object.values(wfs).flatMap((wf) => {
19
+ if (wf instanceof InngestWorkflow) {
20
+ wf.__registerMastra(mastra);
21
+ return wf.getFunctions();
22
+ }
23
+ return [];
24
+ })
25
+ )
26
+ );
19
27
  return hono.serve({
20
28
  client: inngest,
21
29
  functions
22
30
  });
23
31
  }
24
- var InngestRun = class extends vNext.Run {
32
+ var InngestRun = class extends workflows.Run {
25
33
  inngest;
34
+ serializedStepGraph;
26
35
  #mastra;
27
36
  constructor(params, inngest) {
28
37
  super(params);
29
38
  this.inngest = inngest;
39
+ this.serializedStepGraph = params.serializedStepGraph;
30
40
  this.#mastra = params.mastra;
31
41
  }
32
42
  async getRuns(eventId) {
33
- const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
43
+ const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
34
44
  headers: {
35
45
  Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
36
46
  }
@@ -40,15 +50,50 @@ var InngestRun = class extends vNext.Run {
40
50
  }
41
51
  async getRunOutput(eventId) {
42
52
  let runs = await this.getRuns(eventId);
43
- while (runs?.[0]?.status !== "Completed") {
53
+ while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
44
54
  await new Promise((resolve) => setTimeout(resolve, 1e3));
45
55
  runs = await this.getRuns(eventId);
46
- if (runs?.[0]?.status === "Failed" || runs?.[0]?.status === "Cancelled") {
56
+ if (runs?.[0]?.status === "Failed") {
57
+ console.log("run", runs?.[0]);
47
58
  throw new Error(`Function run ${runs?.[0]?.status}`);
59
+ } else if (runs?.[0]?.status === "Cancelled") {
60
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
61
+ workflowName: this.workflowId,
62
+ runId: this.runId
63
+ });
64
+ return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
48
65
  }
49
66
  }
50
67
  return runs?.[0];
51
68
  }
69
+ async sendEvent(event, data) {
70
+ await this.inngest.send({
71
+ name: `user-event-${event}`,
72
+ data
73
+ });
74
+ }
75
+ async cancel() {
76
+ await this.inngest.send({
77
+ name: `cancel.workflow.${this.workflowId}`,
78
+ data: {
79
+ runId: this.runId
80
+ }
81
+ });
82
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
83
+ workflowName: this.workflowId,
84
+ runId: this.runId
85
+ });
86
+ if (snapshot) {
87
+ await this.#mastra?.storage?.persistWorkflowSnapshot({
88
+ workflowName: this.workflowId,
89
+ runId: this.runId,
90
+ snapshot: {
91
+ ...snapshot,
92
+ status: "canceled"
93
+ }
94
+ });
95
+ }
96
+ }
52
97
  async start({
53
98
  inputData
54
99
  }) {
@@ -57,11 +102,14 @@ var InngestRun = class extends vNext.Run {
57
102
  runId: this.runId,
58
103
  snapshot: {
59
104
  runId: this.runId,
105
+ serializedStepGraph: this.serializedStepGraph,
60
106
  value: {},
61
107
  context: {},
62
108
  activePaths: [],
63
109
  suspendedPaths: {},
64
- timestamp: Date.now()
110
+ waitingPaths: {},
111
+ timestamp: Date.now(),
112
+ status: "running"
65
113
  }
66
114
  });
67
115
  const eventOutput = await this.inngest.send({
@@ -80,10 +128,23 @@ var InngestRun = class extends vNext.Run {
80
128
  if (result.status === "failed") {
81
129
  result.error = new Error(result.error);
82
130
  }
83
- this.cleanup?.();
131
+ if (result.status !== "suspended") {
132
+ this.cleanup?.();
133
+ }
84
134
  return result;
85
135
  }
86
136
  async resume(params) {
137
+ const p = this._resume(params).then((result) => {
138
+ if (result.status !== "suspended") {
139
+ this.closeStreamAction?.().catch(() => {
140
+ });
141
+ }
142
+ return result;
143
+ });
144
+ this.executionResults = p;
145
+ return p;
146
+ }
147
+ async _resume(params) {
87
148
  const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
88
149
  (step) => typeof step === "string" ? step : step?.id
89
150
  );
@@ -96,6 +157,7 @@ var InngestRun = class extends vNext.Run {
96
157
  data: {
97
158
  inputData: params.resumeData,
98
159
  runId: this.runId,
160
+ workflowId: this.workflowId,
99
161
  stepResults: snapshot?.context,
100
162
  resume: {
101
163
  steps,
@@ -117,32 +179,101 @@ var InngestRun = class extends vNext.Run {
117
179
  }
118
180
  return result;
119
181
  }
120
- watch(cb) {
182
+ watch(cb, type = "watch") {
183
+ let active = true;
121
184
  const streamPromise = realtime.subscribe(
122
185
  {
123
186
  channel: `workflow:${this.workflowId}:${this.runId}`,
124
- topics: ["watch"],
187
+ topics: [type],
125
188
  app: this.inngest
126
189
  },
127
190
  (message) => {
128
- cb(message.data);
191
+ if (active) {
192
+ cb(message.data);
193
+ }
129
194
  }
130
195
  );
131
196
  return () => {
132
- streamPromise.then((stream) => {
133
- stream.cancel();
197
+ active = false;
198
+ streamPromise.then(async (stream) => {
199
+ return stream.cancel();
134
200
  }).catch((err) => {
135
201
  console.error(err);
136
202
  });
137
203
  };
138
204
  }
205
+ stream({ inputData, runtimeContext } = {}) {
206
+ const { readable, writable } = new TransformStream();
207
+ let currentToolData = void 0;
208
+ const writer = writable.getWriter();
209
+ const unwatch = this.watch(async (event) => {
210
+ if (event.type === "workflow-agent-call-start") {
211
+ currentToolData = {
212
+ name: event.payload.name,
213
+ args: event.payload.args
214
+ };
215
+ await writer.write({
216
+ ...event.payload,
217
+ type: "tool-call-streaming-start"
218
+ });
219
+ return;
220
+ }
221
+ try {
222
+ if (event.type === "workflow-agent-call-finish") {
223
+ return;
224
+ } else if (!event.type.startsWith("workflow-")) {
225
+ if (event.type === "text-delta") {
226
+ await writer.write({
227
+ type: "tool-call-delta",
228
+ ...currentToolData ?? {},
229
+ argsTextDelta: event.textDelta
230
+ });
231
+ }
232
+ return;
233
+ }
234
+ const e = {
235
+ ...event,
236
+ type: event.type.replace("workflow-", "")
237
+ };
238
+ await writer.write(e);
239
+ } catch {
240
+ }
241
+ }, "watch-v2");
242
+ this.closeStreamAction = async () => {
243
+ unwatch();
244
+ try {
245
+ await writer.close();
246
+ } catch (err) {
247
+ console.error("Error closing stream:", err);
248
+ } finally {
249
+ writer.releaseLock();
250
+ }
251
+ };
252
+ this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
253
+ if (result.status !== "suspended") {
254
+ this.closeStreamAction?.().catch(() => {
255
+ });
256
+ }
257
+ return result;
258
+ });
259
+ return {
260
+ stream: readable,
261
+ getWorkflowState: () => this.executionResults
262
+ };
263
+ }
139
264
  };
140
- var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
265
+ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
141
266
  #mastra;
142
267
  inngest;
143
268
  function;
269
+ flowControlConfig;
144
270
  constructor(params, inngest) {
145
- super(params);
271
+ const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
272
+ super(workflowParams);
273
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
274
+ ([_, value]) => value !== void 0
275
+ );
276
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
146
277
  this.#mastra = params.mastra;
147
278
  this.inngest = inngest;
148
279
  }
@@ -158,7 +289,7 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
158
289
  const storage = this.#mastra?.getStorage();
159
290
  if (!storage) {
160
291
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
161
- return null;
292
+ return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
162
293
  }
163
294
  const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
164
295
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
@@ -189,6 +320,25 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
189
320
  runId: runIdToUse,
190
321
  executionEngine: this.executionEngine,
191
322
  executionGraph: this.executionGraph,
323
+ serializedStepGraph: this.serializedStepGraph,
324
+ mastra: this.#mastra,
325
+ retryConfig: this.retryConfig,
326
+ cleanup: () => this.runs.delete(runIdToUse)
327
+ },
328
+ this.inngest
329
+ );
330
+ this.runs.set(runIdToUse, run);
331
+ return run;
332
+ }
333
+ async createRunAsync(options) {
334
+ const runIdToUse = options?.runId || crypto.randomUUID();
335
+ const run = this.runs.get(runIdToUse) ?? new InngestRun(
336
+ {
337
+ workflowId: this.id,
338
+ runId: runIdToUse,
339
+ executionEngine: this.executionEngine,
340
+ executionGraph: this.executionGraph,
341
+ serializedStepGraph: this.serializedStepGraph,
192
342
  mastra: this.#mastra,
193
343
  retryConfig: this.retryConfig,
194
344
  cleanup: () => this.runs.delete(runIdToUse)
@@ -196,6 +346,27 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
196
346
  this.inngest
197
347
  );
198
348
  this.runs.set(runIdToUse, run);
349
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
350
+ if (!workflowSnapshotInStorage) {
351
+ await this.mastra?.getStorage()?.persistWorkflowSnapshot({
352
+ workflowName: this.id,
353
+ runId: runIdToUse,
354
+ snapshot: {
355
+ runId: runIdToUse,
356
+ status: "pending",
357
+ value: {},
358
+ context: {},
359
+ activePaths: [],
360
+ waitingPaths: {},
361
+ serializedStepGraph: this.serializedStepGraph,
362
+ suspendedPaths: {},
363
+ result: void 0,
364
+ error: void 0,
365
+ // @ts-ignore
366
+ timestamp: Date.now()
367
+ }
368
+ });
369
+ }
199
370
  return run;
200
371
  }
201
372
  getFunction() {
@@ -203,8 +374,14 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
203
374
  return this.function;
204
375
  }
205
376
  this.function = this.inngest.createFunction(
206
- // @ts-ignore
207
- { id: `workflow.${this.id}`, retries: this.retryConfig?.attempts ?? 0 },
377
+ {
378
+ id: `workflow.${this.id}`,
379
+ // @ts-ignore
380
+ retries: this.retryConfig?.attempts ?? 0,
381
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
382
+ // Spread flow control configuration
383
+ ...this.flowControlConfig
384
+ },
208
385
  { event: `workflow.${this.id}` },
209
386
  async ({ event, step, attempt, publish }) => {
210
387
  let { inputData, runId, resume } = event.data;
@@ -221,12 +398,18 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
221
398
  try {
222
399
  await publish({
223
400
  channel: `workflow:${this.id}:${runId}`,
224
- topic: "watch",
401
+ topic: event2,
225
402
  data
226
403
  });
227
404
  } catch (err) {
228
405
  this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
229
406
  }
407
+ },
408
+ on: (_event, _callback) => {
409
+ },
410
+ off: (_event, _callback) => {
411
+ },
412
+ once: (_event, _callback) => {
230
413
  }
231
414
  };
232
415
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
@@ -234,12 +417,16 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
234
417
  workflowId: this.id,
235
418
  runId,
236
419
  graph: this.executionGraph,
420
+ serializedStepGraph: this.serializedStepGraph,
237
421
  input: inputData,
238
422
  emitter,
239
423
  retryConfig: this.retryConfig,
240
424
  runtimeContext: new di.RuntimeContext(),
241
425
  // TODO
242
- resume
426
+ resume,
427
+ abortController: new AbortController(),
428
+ currentSpan: void 0
429
+ // TODO: Pass actual parent AI span from workflow execution context
243
430
  });
244
431
  return { result, runId };
245
432
  }
@@ -263,32 +450,126 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
263
450
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
264
451
  }
265
452
  };
266
- function cloneWorkflow(workflow, opts) {
267
- const wf = new InngestWorkflow(
268
- {
269
- id: opts.id,
270
- inputSchema: workflow.inputSchema,
271
- outputSchema: workflow.outputSchema,
272
- steps: workflow.stepDefs,
273
- mastra: workflow.mastra
274
- },
275
- workflow.inngest
276
- );
277
- wf.setStepFlow(workflow.stepGraph);
278
- wf.commit();
279
- return wf;
453
+ function isAgent(params) {
454
+ return params?.component === "AGENT";
455
+ }
456
+ function isTool(params) {
457
+ return params instanceof tools.Tool;
458
+ }
459
+ function createStep(params) {
460
+ if (isAgent(params)) {
461
+ return {
462
+ id: params.name,
463
+ // @ts-ignore
464
+ inputSchema: zod.z.object({
465
+ prompt: zod.z.string()
466
+ // resourceId: z.string().optional(),
467
+ // threadId: z.string().optional(),
468
+ }),
469
+ // @ts-ignore
470
+ outputSchema: zod.z.object({
471
+ text: zod.z.string()
472
+ }),
473
+ execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
474
+ let streamPromise = {};
475
+ streamPromise.promise = new Promise((resolve, reject) => {
476
+ streamPromise.resolve = resolve;
477
+ streamPromise.reject = reject;
478
+ });
479
+ const toolData = {
480
+ name: params.name,
481
+ args: inputData
482
+ };
483
+ await emitter.emit("watch-v2", {
484
+ type: "workflow-agent-call-start",
485
+ payload: toolData
486
+ });
487
+ const { fullStream } = await params.stream(inputData.prompt, {
488
+ // resourceId: inputData.resourceId,
489
+ // threadId: inputData.threadId,
490
+ runtimeContext,
491
+ tracingContext,
492
+ onFinish: (result) => {
493
+ streamPromise.resolve(result.text);
494
+ },
495
+ abortSignal
496
+ });
497
+ if (abortSignal.aborted) {
498
+ return abort();
499
+ }
500
+ for await (const chunk of fullStream) {
501
+ await emitter.emit("watch-v2", chunk);
502
+ }
503
+ await emitter.emit("watch-v2", {
504
+ type: "workflow-agent-call-finish",
505
+ payload: toolData
506
+ });
507
+ return {
508
+ text: await streamPromise.promise
509
+ };
510
+ }
511
+ };
512
+ }
513
+ if (isTool(params)) {
514
+ if (!params.inputSchema || !params.outputSchema) {
515
+ throw new Error("Tool must have input and output schemas defined");
516
+ }
517
+ return {
518
+ // TODO: tool probably should have strong id type
519
+ // @ts-ignore
520
+ id: params.id,
521
+ inputSchema: params.inputSchema,
522
+ outputSchema: params.outputSchema,
523
+ execute: async ({ inputData, mastra, runtimeContext, tracingContext }) => {
524
+ return params.execute({
525
+ context: inputData,
526
+ mastra: aiTracing.wrapMastra(mastra, tracingContext),
527
+ runtimeContext,
528
+ tracingContext
529
+ });
530
+ }
531
+ };
532
+ }
533
+ return {
534
+ id: params.id,
535
+ description: params.description,
536
+ inputSchema: params.inputSchema,
537
+ outputSchema: params.outputSchema,
538
+ resumeSchema: params.resumeSchema,
539
+ suspendSchema: params.suspendSchema,
540
+ execute: params.execute
541
+ };
280
542
  }
281
543
  function init(inngest) {
282
544
  return {
283
545
  createWorkflow(params) {
284
546
  return new InngestWorkflow(params, inngest);
285
547
  },
286
- createStep: vNext.createStep,
287
- cloneStep: vNext.cloneStep,
288
- cloneWorkflow
548
+ createStep,
549
+ cloneStep(step, opts) {
550
+ return {
551
+ id: opts.id,
552
+ description: step.description,
553
+ inputSchema: step.inputSchema,
554
+ outputSchema: step.outputSchema,
555
+ execute: step.execute
556
+ };
557
+ },
558
+ cloneWorkflow(workflow, opts) {
559
+ const wf = new workflows.Workflow({
560
+ id: opts.id,
561
+ inputSchema: workflow.inputSchema,
562
+ outputSchema: workflow.outputSchema,
563
+ steps: workflow.stepDefs,
564
+ mastra: workflow.mastra
565
+ });
566
+ wf.setStepFlow(workflow.stepGraph);
567
+ wf.commit();
568
+ return wf;
569
+ }
289
570
  };
290
571
  }
291
- var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
572
+ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
292
573
  inngestStep;
293
574
  inngestAttempts;
294
575
  constructor(mastra, inngestStep, inngestAttempts = 0) {
@@ -296,6 +577,18 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
296
577
  this.inngestStep = inngestStep;
297
578
  this.inngestAttempts = inngestAttempts;
298
579
  }
580
+ async execute(params) {
581
+ await params.emitter.emit("watch-v2", {
582
+ type: "workflow-start",
583
+ payload: { runId: params.runId }
584
+ });
585
+ const result = await super.execute(params);
586
+ await params.emitter.emit("watch-v2", {
587
+ type: "workflow-finish",
588
+ payload: { runId: params.runId }
589
+ });
590
+ return result;
591
+ }
299
592
  async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
300
593
  const base = {
301
594
  status: lastOutput.status,
@@ -353,28 +646,192 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
353
646
  executionSpan?.end();
354
647
  return base;
355
648
  }
356
- async superExecuteStep({
649
+ // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
650
+ // await this.inngestStep.sleep(id, duration);
651
+ // }
652
+ async executeSleep({
357
653
  workflowId,
358
654
  runId,
359
- step,
655
+ entry,
656
+ prevOutput,
360
657
  stepResults,
658
+ emitter,
659
+ abortController,
660
+ runtimeContext,
361
661
  executionContext,
362
- resume,
662
+ writableStream,
663
+ tracingContext
664
+ }) {
665
+ let { duration, fn } = entry;
666
+ const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
667
+ type: aiTracing.AISpanType.WORKFLOW_SLEEP,
668
+ name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
669
+ attributes: {
670
+ durationMs: duration,
671
+ sleepType: fn ? "dynamic" : "fixed"
672
+ }
673
+ });
674
+ if (fn) {
675
+ const stepCallId = crypto.randomUUID();
676
+ duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
677
+ return await fn({
678
+ runId,
679
+ workflowId,
680
+ mastra: this.mastra,
681
+ runtimeContext,
682
+ inputData: prevOutput,
683
+ runCount: -1,
684
+ tracingContext: {
685
+ currentSpan: sleepSpan
686
+ },
687
+ getInitData: () => stepResults?.input,
688
+ getStepResult: (step) => {
689
+ if (!step?.id) {
690
+ return null;
691
+ }
692
+ const result = stepResults[step.id];
693
+ if (result?.status === "success") {
694
+ return result.output;
695
+ }
696
+ return null;
697
+ },
698
+ // TODO: this function shouldn't have suspend probably?
699
+ suspend: async (_suspendPayload) => {
700
+ },
701
+ bail: () => {
702
+ },
703
+ abort: () => {
704
+ abortController?.abort();
705
+ },
706
+ [_constants.EMITTER_SYMBOL]: emitter,
707
+ // TODO: add streamVNext support
708
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
709
+ engine: { step: this.inngestStep },
710
+ abortSignal: abortController?.signal,
711
+ writer: new tools.ToolStream(
712
+ {
713
+ prefix: "workflow-step",
714
+ callId: stepCallId,
715
+ name: "sleep",
716
+ runId
717
+ },
718
+ writableStream
719
+ )
720
+ });
721
+ });
722
+ sleepSpan?.update({
723
+ attributes: {
724
+ durationMs: duration
725
+ }
726
+ });
727
+ }
728
+ try {
729
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
730
+ sleepSpan?.end();
731
+ } catch (e) {
732
+ sleepSpan?.error({ error: e });
733
+ throw e;
734
+ }
735
+ }
736
+ async executeSleepUntil({
737
+ workflowId,
738
+ runId,
739
+ entry,
363
740
  prevOutput,
741
+ stepResults,
364
742
  emitter,
365
- runtimeContext
743
+ abortController,
744
+ runtimeContext,
745
+ executionContext,
746
+ writableStream,
747
+ tracingContext
366
748
  }) {
367
- return super.executeStep({
368
- workflowId,
369
- runId,
370
- step,
371
- stepResults,
372
- executionContext,
373
- resume,
374
- prevOutput,
375
- emitter,
376
- runtimeContext
749
+ let { date, fn } = entry;
750
+ const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
751
+ type: aiTracing.AISpanType.WORKFLOW_SLEEP,
752
+ name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
753
+ attributes: {
754
+ untilDate: date,
755
+ durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
756
+ sleepType: fn ? "dynamic" : "fixed"
757
+ }
377
758
  });
759
+ if (fn) {
760
+ date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
761
+ const stepCallId = crypto.randomUUID();
762
+ return await fn({
763
+ runId,
764
+ workflowId,
765
+ mastra: this.mastra,
766
+ runtimeContext,
767
+ inputData: prevOutput,
768
+ runCount: -1,
769
+ tracingContext: {
770
+ currentSpan: sleepUntilSpan
771
+ },
772
+ getInitData: () => stepResults?.input,
773
+ getStepResult: (step) => {
774
+ if (!step?.id) {
775
+ return null;
776
+ }
777
+ const result = stepResults[step.id];
778
+ if (result?.status === "success") {
779
+ return result.output;
780
+ }
781
+ return null;
782
+ },
783
+ // TODO: this function shouldn't have suspend probably?
784
+ suspend: async (_suspendPayload) => {
785
+ },
786
+ bail: () => {
787
+ },
788
+ abort: () => {
789
+ abortController?.abort();
790
+ },
791
+ [_constants.EMITTER_SYMBOL]: emitter,
792
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
793
+ // TODO: add streamVNext support
794
+ engine: { step: this.inngestStep },
795
+ abortSignal: abortController?.signal,
796
+ writer: new tools.ToolStream(
797
+ {
798
+ prefix: "workflow-step",
799
+ callId: stepCallId,
800
+ name: "sleep",
801
+ runId
802
+ },
803
+ writableStream
804
+ )
805
+ });
806
+ });
807
+ const time = !date ? 0 : date.getTime() - Date.now();
808
+ sleepUntilSpan?.update({
809
+ attributes: {
810
+ durationMs: Math.max(0, time)
811
+ }
812
+ });
813
+ }
814
+ if (!(date instanceof Date)) {
815
+ sleepUntilSpan?.end();
816
+ return;
817
+ }
818
+ try {
819
+ await this.inngestStep.sleepUntil(entry.id, date);
820
+ sleepUntilSpan?.end();
821
+ } catch (e) {
822
+ sleepUntilSpan?.error({ error: e });
823
+ throw e;
824
+ }
825
+ }
826
+ async executeWaitForEvent({ event, timeout }) {
827
+ const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
828
+ event: `user-event-${event}`,
829
+ timeout: timeout ?? 5e3
830
+ });
831
+ if (eventData === null) {
832
+ throw "Timeout waiting for event";
833
+ }
834
+ return eventData?.data;
378
835
  }
379
836
  async executeStep({
380
837
  step,
@@ -383,11 +840,24 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
383
840
  resume,
384
841
  prevOutput,
385
842
  emitter,
386
- runtimeContext
843
+ abortController,
844
+ runtimeContext,
845
+ tracingContext,
846
+ writableStream,
847
+ disableScorers
387
848
  }) {
388
- await this.inngestStep.run(
849
+ const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
850
+ name: `workflow step: '${step.id}'`,
851
+ type: aiTracing.AISpanType.WORKFLOW_STEP,
852
+ input: prevOutput,
853
+ attributes: {
854
+ stepId: step.id
855
+ }
856
+ });
857
+ const startedAt = await this.inngestStep.run(
389
858
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
390
859
  async () => {
860
+ const startedAt2 = Date.now();
391
861
  await emitter.emit("watch", {
392
862
  type: "watch",
393
863
  payload: {
@@ -409,6 +879,16 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
409
879
  },
410
880
  eventTimestamp: Date.now()
411
881
  });
882
+ await emitter.emit("watch-v2", {
883
+ type: "workflow-step-start",
884
+ payload: {
885
+ id: step.id,
886
+ status: "running",
887
+ payload: prevOutput,
888
+ startedAt: startedAt2
889
+ }
890
+ });
891
+ return startedAt2;
412
892
  }
413
893
  );
414
894
  if (step instanceof InngestWorkflow) {
@@ -469,6 +949,15 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
469
949
  },
470
950
  eventTimestamp: Date.now()
471
951
  });
952
+ await emitter.emit("watch-v2", {
953
+ type: "workflow-step-result",
954
+ payload: {
955
+ id: step.id,
956
+ status: "failed",
957
+ error: result?.error,
958
+ payload: prevOutput
959
+ }
960
+ });
472
961
  return { executionContext, result: { status: "failed", error: result?.error } };
473
962
  } else if (result.status === "suspended") {
474
963
  const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
@@ -495,6 +984,13 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
495
984
  },
496
985
  eventTimestamp: Date.now()
497
986
  });
987
+ await emitter.emit("watch-v2", {
988
+ type: "workflow-step-suspended",
989
+ payload: {
990
+ id: step.id,
991
+ status: "suspended"
992
+ }
993
+ });
498
994
  return {
499
995
  executionContext,
500
996
  result: {
@@ -545,6 +1041,21 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
545
1041
  },
546
1042
  eventTimestamp: Date.now()
547
1043
  });
1044
+ await emitter.emit("watch-v2", {
1045
+ type: "workflow-step-result",
1046
+ payload: {
1047
+ id: step.id,
1048
+ status: "success",
1049
+ output: result?.result
1050
+ }
1051
+ });
1052
+ await emitter.emit("watch-v2", {
1053
+ type: "workflow-step-finish",
1054
+ payload: {
1055
+ id: step.id,
1056
+ metadata: {}
1057
+ }
1058
+ });
548
1059
  return { executionContext, result: { status: "success", output: result?.result } };
549
1060
  }
550
1061
  );
@@ -554,12 +1065,18 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
554
1065
  const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
555
1066
  let execResults;
556
1067
  let suspended;
1068
+ let bailed;
557
1069
  try {
558
1070
  const result = await step.execute({
1071
+ runId: executionContext.runId,
559
1072
  mastra: this.mastra,
560
1073
  runtimeContext,
1074
+ writableStream,
561
1075
  inputData: prevOutput,
562
1076
  resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1077
+ tracingContext: {
1078
+ currentSpan: stepAISpan
1079
+ },
563
1080
  getInitData: () => stepResults?.input,
564
1081
  getStepResult: (step2) => {
565
1082
  const result2 = stepResults[step2.id];
@@ -572,24 +1089,60 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
572
1089
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
573
1090
  suspended = { payload: suspendPayload };
574
1091
  },
1092
+ bail: (result2) => {
1093
+ bailed = { payload: result2 };
1094
+ },
575
1095
  resume: {
576
1096
  steps: resume?.steps?.slice(1) || [],
577
1097
  resumePayload: resume?.resumePayload,
578
1098
  // @ts-ignore
579
1099
  runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
580
1100
  },
581
- emitter
1101
+ [_constants.EMITTER_SYMBOL]: emitter,
1102
+ engine: {
1103
+ step: this.inngestStep
1104
+ },
1105
+ abortSignal: abortController.signal
582
1106
  });
583
- execResults = { status: "success", output: result };
1107
+ const endedAt = Date.now();
1108
+ execResults = {
1109
+ status: "success",
1110
+ output: result,
1111
+ startedAt,
1112
+ endedAt,
1113
+ payload: prevOutput,
1114
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1115
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1116
+ };
584
1117
  } catch (e) {
585
- execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
1118
+ execResults = {
1119
+ status: "failed",
1120
+ payload: prevOutput,
1121
+ error: e instanceof Error ? e.message : String(e),
1122
+ endedAt: Date.now(),
1123
+ startedAt,
1124
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1125
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1126
+ };
586
1127
  }
587
1128
  if (suspended) {
588
- execResults = { status: "suspended", payload: suspended.payload };
1129
+ execResults = {
1130
+ status: "suspended",
1131
+ suspendedPayload: suspended.payload,
1132
+ payload: prevOutput,
1133
+ suspendedAt: Date.now(),
1134
+ startedAt,
1135
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1136
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1137
+ };
1138
+ } else if (bailed) {
1139
+ execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
589
1140
  }
590
1141
  if (execResults.status === "failed") {
591
1142
  if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
592
- throw execResults.error;
1143
+ const error = new Error(execResults.error);
1144
+ stepAISpan?.error({ error });
1145
+ throw error;
593
1146
  }
594
1147
  }
595
1148
  await emitter.emit("watch", {
@@ -597,20 +1150,61 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
597
1150
  payload: {
598
1151
  currentStep: {
599
1152
  id: step.id,
600
- status: execResults.status,
601
- output: execResults.output
1153
+ ...execResults
602
1154
  },
603
1155
  workflowState: {
604
1156
  status: "running",
605
- steps: stepResults,
1157
+ steps: { ...stepResults, [step.id]: execResults },
606
1158
  result: null,
607
1159
  error: null
608
1160
  }
609
1161
  },
610
1162
  eventTimestamp: Date.now()
611
1163
  });
1164
+ if (execResults.status === "suspended") {
1165
+ await emitter.emit("watch-v2", {
1166
+ type: "workflow-step-suspended",
1167
+ payload: {
1168
+ id: step.id,
1169
+ ...execResults
1170
+ }
1171
+ });
1172
+ } else {
1173
+ await emitter.emit("watch-v2", {
1174
+ type: "workflow-step-result",
1175
+ payload: {
1176
+ id: step.id,
1177
+ ...execResults
1178
+ }
1179
+ });
1180
+ await emitter.emit("watch-v2", {
1181
+ type: "workflow-step-finish",
1182
+ payload: {
1183
+ id: step.id,
1184
+ metadata: {}
1185
+ }
1186
+ });
1187
+ }
1188
+ stepAISpan?.end({ output: execResults });
612
1189
  return { result: execResults, executionContext, stepResults };
613
1190
  });
1191
+ if (disableScorers !== false) {
1192
+ await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1193
+ if (step.scorers) {
1194
+ await this.runScorers({
1195
+ scorers: step.scorers,
1196
+ runId: executionContext.runId,
1197
+ input: prevOutput,
1198
+ output: stepRes.result,
1199
+ workflowId: executionContext.workflowId,
1200
+ stepId: step.id,
1201
+ runtimeContext,
1202
+ disableScorers,
1203
+ tracingContext: { currentSpan: stepAISpan }
1204
+ });
1205
+ }
1206
+ });
1207
+ }
614
1208
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
615
1209
  Object.assign(stepResults, stepRes.stepResults);
616
1210
  return stepRes.result;
@@ -619,7 +1213,11 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
619
1213
  workflowId,
620
1214
  runId,
621
1215
  stepResults,
622
- executionContext
1216
+ executionContext,
1217
+ serializedStepGraph,
1218
+ workflowStatus,
1219
+ result,
1220
+ error
623
1221
  }) {
624
1222
  await this.inngestStep.run(
625
1223
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
@@ -633,6 +1231,11 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
633
1231
  context: stepResults,
634
1232
  activePaths: [],
635
1233
  suspendedPaths: executionContext.suspendedPaths,
1234
+ waitingPaths: {},
1235
+ serializedStepGraph,
1236
+ status: workflowStatus,
1237
+ result,
1238
+ error,
636
1239
  // @ts-ignore
637
1240
  timestamp: Date.now()
638
1241
  }
@@ -647,20 +1250,47 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
647
1250
  prevOutput,
648
1251
  prevStep,
649
1252
  stepResults,
1253
+ serializedStepGraph,
650
1254
  resume,
651
1255
  executionContext,
652
1256
  emitter,
653
- runtimeContext
1257
+ abortController,
1258
+ runtimeContext,
1259
+ writableStream,
1260
+ disableScorers,
1261
+ tracingContext
654
1262
  }) {
1263
+ const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1264
+ type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
1265
+ name: `conditional: ${entry.conditions.length} conditions`,
1266
+ input: prevOutput,
1267
+ attributes: {
1268
+ conditionCount: entry.conditions.length
1269
+ }
1270
+ });
655
1271
  let execResults;
656
1272
  const truthyIndexes = (await Promise.all(
657
1273
  entry.conditions.map(
658
1274
  (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1275
+ const evalSpan = conditionalSpan?.createChildSpan({
1276
+ type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1277
+ name: `condition ${index}`,
1278
+ input: prevOutput,
1279
+ attributes: {
1280
+ conditionIndex: index
1281
+ }
1282
+ });
659
1283
  try {
660
1284
  const result = await cond({
1285
+ runId,
1286
+ workflowId,
661
1287
  mastra: this.mastra,
662
1288
  runtimeContext,
1289
+ runCount: -1,
663
1290
  inputData: prevOutput,
1291
+ tracingContext: {
1292
+ currentSpan: evalSpan
1293
+ },
664
1294
  getInitData: () => stepResults?.input,
665
1295
  getStepResult: (step) => {
666
1296
  if (!step?.id) {
@@ -675,22 +1305,61 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
675
1305
  // TODO: this function shouldn't have suspend probably?
676
1306
  suspend: async (_suspendPayload) => {
677
1307
  },
678
- emitter
1308
+ bail: () => {
1309
+ },
1310
+ abort: () => {
1311
+ abortController.abort();
1312
+ },
1313
+ [_constants.EMITTER_SYMBOL]: emitter,
1314
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1315
+ // TODO: add streamVNext support
1316
+ engine: {
1317
+ step: this.inngestStep
1318
+ },
1319
+ abortSignal: abortController.signal,
1320
+ writer: new tools.ToolStream(
1321
+ {
1322
+ prefix: "workflow-step",
1323
+ callId: crypto.randomUUID(),
1324
+ name: "conditional",
1325
+ runId
1326
+ },
1327
+ writableStream
1328
+ )
1329
+ });
1330
+ evalSpan?.end({
1331
+ output: result,
1332
+ attributes: {
1333
+ result: !!result
1334
+ }
679
1335
  });
680
1336
  return result ? index : null;
681
1337
  } catch (e) {
1338
+ evalSpan?.error({
1339
+ error: e instanceof Error ? e : new Error(String(e)),
1340
+ attributes: {
1341
+ result: false
1342
+ }
1343
+ });
682
1344
  return null;
683
1345
  }
684
1346
  })
685
1347
  )
686
1348
  )).filter((index) => index !== null);
687
1349
  const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1350
+ conditionalSpan?.update({
1351
+ attributes: {
1352
+ truthyIndexes,
1353
+ selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1354
+ }
1355
+ });
688
1356
  const results = await Promise.all(
689
1357
  stepsToRun.map(
690
1358
  (step, index) => this.executeEntry({
691
1359
  workflowId,
692
1360
  runId,
693
1361
  entry: step,
1362
+ serializedStepGraph,
694
1363
  prevStep,
695
1364
  stepResults,
696
1365
  resume,
@@ -703,27 +1372,42 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
703
1372
  executionSpan: executionContext.executionSpan
704
1373
  },
705
1374
  emitter,
706
- runtimeContext
1375
+ abortController,
1376
+ runtimeContext,
1377
+ writableStream,
1378
+ disableScorers,
1379
+ tracingContext: {
1380
+ currentSpan: conditionalSpan
1381
+ }
707
1382
  })
708
1383
  )
709
1384
  );
710
- const hasFailed = results.find((result) => result.status === "failed");
711
- const hasSuspended = results.find((result) => result.status === "suspended");
1385
+ const hasFailed = results.find((result) => result.result.status === "failed");
1386
+ const hasSuspended = results.find((result) => result.result.status === "suspended");
712
1387
  if (hasFailed) {
713
- execResults = { status: "failed", error: hasFailed.error };
1388
+ execResults = { status: "failed", error: hasFailed.result.error };
714
1389
  } else if (hasSuspended) {
715
- execResults = { status: "suspended", payload: hasSuspended.payload };
1390
+ execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
716
1391
  } else {
717
1392
  execResults = {
718
1393
  status: "success",
719
1394
  output: results.reduce((acc, result, index) => {
720
- if (result.status === "success") {
1395
+ if (result.result.status === "success") {
721
1396
  acc[stepsToRun[index].step.id] = result.output;
722
1397
  }
723
1398
  return acc;
724
1399
  }, {})
725
1400
  };
726
1401
  }
1402
+ if (execResults.status === "failed") {
1403
+ conditionalSpan?.error({
1404
+ error: new Error(execResults.error)
1405
+ });
1406
+ } else {
1407
+ conditionalSpan?.end({
1408
+ output: execResults.output || execResults
1409
+ });
1410
+ }
727
1411
  return execResults;
728
1412
  }
729
1413
  };
@@ -731,5 +1415,8 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
731
1415
  exports.InngestExecutionEngine = InngestExecutionEngine;
732
1416
  exports.InngestRun = InngestRun;
733
1417
  exports.InngestWorkflow = InngestWorkflow;
1418
+ exports.createStep = createStep;
734
1419
  exports.init = init;
735
1420
  exports.serve = serve;
1421
+ //# sourceMappingURL=index.cjs.map
1422
+ //# sourceMappingURL=index.cjs.map