@mastra/inngest 0.0.0-generate-message-id-20250512171942 → 0.0.0-gl-test-20250917080133

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