@mastra/inngest 0.0.0-redis-cloud-transporter-20250508194049 → 0.0.0-share-agent-metadata-with-cloud-20250718110128

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/src/index.ts CHANGED
@@ -1,34 +1,49 @@
1
1
  import { randomUUID } from 'crypto';
2
+ import type { ReadableStream } from 'node:stream/web';
2
3
  import { subscribe } from '@inngest/realtime';
3
- import type { Mastra, WorkflowRun } from '@mastra/core';
4
+ import type { Agent, Mastra, ToolExecutionContext, WorkflowRun, WorkflowRuns } from '@mastra/core';
4
5
  import { RuntimeContext } from '@mastra/core/di';
5
- import { NewWorkflow, createStep, Run, DefaultExecutionEngine, cloneStep } from '@mastra/core/workflows/vNext';
6
+ import { Tool } from '@mastra/core/tools';
7
+ import { Workflow, Run, DefaultExecutionEngine } from '@mastra/core/workflows';
6
8
  import type {
7
9
  ExecuteFunction,
8
10
  ExecutionContext,
9
11
  ExecutionEngine,
10
12
  ExecutionGraph,
11
- NewStep,
12
- NewStep as Step,
13
- NewWorkflowConfig,
13
+ Step,
14
+ WorkflowConfig,
14
15
  StepFlowEntry,
15
16
  StepResult,
16
17
  WorkflowResult,
17
- } from '@mastra/core/workflows/vNext';
18
+ SerializedStepFlowEntry,
19
+ StepFailure,
20
+ Emitter,
21
+ WatchEvent,
22
+ StreamEvent,
23
+ } from '@mastra/core/workflows';
24
+ import { EMITTER_SYMBOL } from '@mastra/core/workflows/_constants';
18
25
  import type { Span } from '@opentelemetry/api';
19
26
  import type { Inngest, BaseContext } from 'inngest';
20
27
  import { serve as inngestServe } from 'inngest/hono';
21
- import type { z } from 'zod';
28
+ import { z } from 'zod';
29
+
30
+ export type InngestEngineType = {
31
+ step: any;
32
+ };
22
33
 
23
34
  export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest }): ReturnType<typeof inngestServe> {
24
- const wfs = mastra.vnext_getWorkflows();
25
- const functions = Object.values(wfs).flatMap(wf => {
26
- if (wf instanceof InngestWorkflow) {
27
- wf.__registerMastra(mastra);
28
- return wf.getFunctions();
29
- }
30
- return [];
31
- });
35
+ const wfs = mastra.getWorkflows();
36
+ const functions = Array.from(
37
+ new Set(
38
+ Object.values(wfs).flatMap(wf => {
39
+ if (wf instanceof InngestWorkflow) {
40
+ wf.__registerMastra(mastra);
41
+ return wf.getFunctions();
42
+ }
43
+ return [];
44
+ }),
45
+ ),
46
+ );
32
47
  return inngestServe({
33
48
  client: inngest,
34
49
  functions,
@@ -36,11 +51,13 @@ export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest })
36
51
  }
37
52
 
38
53
  export class InngestRun<
39
- TSteps extends NewStep<string, any, any>[] = NewStep<string, any, any>[],
54
+ TEngineType = InngestEngineType,
55
+ TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
40
56
  TInput extends z.ZodType<any> = z.ZodType<any>,
41
57
  TOutput extends z.ZodType<any> = z.ZodType<any>,
42
- > extends Run<TSteps, TInput, TOutput> {
58
+ > extends Run<TEngineType, TSteps, TInput, TOutput> {
43
59
  private inngest: Inngest;
60
+ serializedStepGraph: SerializedStepFlowEntry[];
44
61
  #mastra: Mastra;
45
62
 
46
63
  constructor(
@@ -49,6 +66,7 @@ export class InngestRun<
49
66
  runId: string;
50
67
  executionEngine: ExecutionEngine;
51
68
  executionGraph: ExecutionGraph;
69
+ serializedStepGraph: SerializedStepFlowEntry[];
52
70
  mastra?: Mastra;
53
71
  retryConfig?: {
54
72
  attempts?: number;
@@ -60,11 +78,12 @@ export class InngestRun<
60
78
  ) {
61
79
  super(params);
62
80
  this.inngest = inngest;
81
+ this.serializedStepGraph = params.serializedStepGraph;
63
82
  this.#mastra = params.mastra!;
64
83
  }
65
84
 
66
85
  async getRuns(eventId: string) {
67
- const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
86
+ const response = await fetch(`${this.inngest.apiBaseUrl ?? 'https://api.inngest.com'}/v1/events/${eventId}/runs`, {
68
87
  headers: {
69
88
  Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`,
70
89
  },
@@ -75,16 +94,55 @@ export class InngestRun<
75
94
 
76
95
  async getRunOutput(eventId: string) {
77
96
  let runs = await this.getRuns(eventId);
78
- while (runs?.[0]?.status !== 'Completed') {
97
+
98
+ while (runs?.[0]?.status !== 'Completed' || runs?.[0]?.event_id !== eventId) {
79
99
  await new Promise(resolve => setTimeout(resolve, 1000));
80
100
  runs = await this.getRuns(eventId);
81
- if (runs?.[0]?.status === 'Failed' || runs?.[0]?.status === 'Cancelled') {
101
+ if (runs?.[0]?.status === 'Failed') {
102
+ console.log('run', runs?.[0]);
82
103
  throw new Error(`Function run ${runs?.[0]?.status}`);
104
+ } else if (runs?.[0]?.status === 'Cancelled') {
105
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
106
+ workflowName: this.workflowId,
107
+ runId: this.runId,
108
+ });
109
+ return { output: { result: { steps: snapshot?.context, status: 'canceled' } } };
83
110
  }
84
111
  }
85
112
  return runs?.[0];
86
113
  }
87
114
 
115
+ async sendEvent(event: string, data: any) {
116
+ await this.inngest.send({
117
+ name: `user-event-${event}`,
118
+ data,
119
+ });
120
+ }
121
+
122
+ async cancel() {
123
+ await this.inngest.send({
124
+ name: `cancel.workflow.${this.workflowId}`,
125
+ data: {
126
+ runId: this.runId,
127
+ },
128
+ });
129
+
130
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
131
+ workflowName: this.workflowId,
132
+ runId: this.runId,
133
+ });
134
+ if (snapshot) {
135
+ await this.#mastra?.storage?.persistWorkflowSnapshot({
136
+ workflowName: this.workflowId,
137
+ runId: this.runId,
138
+ snapshot: {
139
+ ...snapshot,
140
+ status: 'canceled' as any,
141
+ },
142
+ });
143
+ }
144
+ }
145
+
88
146
  async start({
89
147
  inputData,
90
148
  }: {
@@ -96,11 +154,13 @@ export class InngestRun<
96
154
  runId: this.runId,
97
155
  snapshot: {
98
156
  runId: this.runId,
157
+ serializedStepGraph: this.serializedStepGraph,
99
158
  value: {},
100
159
  context: {} as any,
101
160
  activePaths: [],
102
161
  suspendedPaths: {},
103
162
  timestamp: Date.now(),
163
+ status: 'running',
104
164
  },
105
165
  });
106
166
 
@@ -122,7 +182,9 @@ export class InngestRun<
122
182
  result.error = new Error(result.error);
123
183
  }
124
184
 
125
- this.cleanup?.();
185
+ if (result.status !== 'suspended') {
186
+ this.cleanup?.();
187
+ }
126
188
  return result;
127
189
  }
128
190
 
@@ -134,6 +196,27 @@ export class InngestRun<
134
196
  | string
135
197
  | string[];
136
198
  runtimeContext?: RuntimeContext;
199
+ }): Promise<WorkflowResult<TOutput, TSteps>> {
200
+ const p = this._resume(params).then(result => {
201
+ if (result.status !== 'suspended') {
202
+ this.closeStreamAction?.().catch(() => {});
203
+ }
204
+
205
+ return result;
206
+ });
207
+
208
+ this.executionResults = p;
209
+ return p;
210
+ }
211
+
212
+ async _resume<TResumeSchema extends z.ZodType<any>>(params: {
213
+ resumeData?: z.infer<TResumeSchema>;
214
+ step:
215
+ | Step<string, any, any, TResumeSchema, any>
216
+ | [...Step<string, any, any, any, any>[], Step<string, any, any, TResumeSchema, any>]
217
+ | string
218
+ | string[];
219
+ runtimeContext?: RuntimeContext;
137
220
  }): Promise<WorkflowResult<TOutput, TSteps>> {
138
221
  const steps: string[] = (Array.isArray(params.step) ? params.step : [params.step]).map(step =>
139
222
  typeof step === 'string' ? step : step?.id,
@@ -171,43 +254,88 @@ export class InngestRun<
171
254
  return result;
172
255
  }
173
256
 
174
- watch(cb: (event: any) => void): () => void {
257
+ watch(cb: (event: WatchEvent) => void, type: 'watch' | 'watch-v2' = 'watch'): () => void {
258
+ let active = true;
175
259
  const streamPromise = subscribe(
176
260
  {
177
261
  channel: `workflow:${this.workflowId}:${this.runId}`,
178
- topics: ['watch'],
262
+ topics: [type],
179
263
  app: this.inngest,
180
264
  },
181
265
  (message: any) => {
182
- cb(message.data);
266
+ if (active) {
267
+ cb(message.data);
268
+ }
183
269
  },
184
270
  );
185
271
 
186
272
  return () => {
273
+ active = false;
187
274
  streamPromise
188
- .then((stream: any) => {
189
- stream.cancel();
275
+ .then(async (stream: Awaited<typeof streamPromise>) => {
276
+ return stream.cancel();
190
277
  })
191
278
  .catch(err => {
192
279
  console.error(err);
193
280
  });
194
281
  };
195
282
  }
283
+
284
+ stream({ inputData, runtimeContext }: { inputData?: z.infer<TInput>; runtimeContext?: RuntimeContext } = {}): {
285
+ stream: ReadableStream<StreamEvent>;
286
+ getWorkflowState: () => Promise<WorkflowResult<TOutput, TSteps>>;
287
+ } {
288
+ const { readable, writable } = new TransformStream<StreamEvent, StreamEvent>();
289
+
290
+ const writer = writable.getWriter();
291
+ const unwatch = this.watch(async event => {
292
+ try {
293
+ // watch-v2 events are data stream events, so we need to cast them to the correct type
294
+ await writer.write(event as any);
295
+ } catch {}
296
+ }, 'watch-v2');
297
+
298
+ this.closeStreamAction = async () => {
299
+ unwatch();
300
+
301
+ try {
302
+ await writer.close();
303
+ } catch (err) {
304
+ console.error('Error closing stream:', err);
305
+ } finally {
306
+ writer.releaseLock();
307
+ }
308
+ };
309
+
310
+ this.executionResults = this.start({ inputData, runtimeContext }).then(result => {
311
+ if (result.status !== 'suspended') {
312
+ this.closeStreamAction?.().catch(() => {});
313
+ }
314
+
315
+ return result;
316
+ });
317
+
318
+ return {
319
+ stream: readable as ReadableStream<StreamEvent>,
320
+ getWorkflowState: () => this.executionResults!,
321
+ };
322
+ }
196
323
  }
197
324
 
198
325
  export class InngestWorkflow<
199
- TSteps extends NewStep<string, any, any>[] = NewStep<string, any, any>[],
326
+ TEngineType = InngestEngineType,
327
+ TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
200
328
  TWorkflowId extends string = string,
201
329
  TInput extends z.ZodType<any> = z.ZodType<any>,
202
330
  TOutput extends z.ZodType<any> = z.ZodType<any>,
203
331
  TPrevSchema extends z.ZodType<any> = TInput,
204
- > extends NewWorkflow<TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
332
+ > extends Workflow<TEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
205
333
  #mastra: Mastra;
206
334
  public inngest: Inngest;
207
335
 
208
336
  private function: ReturnType<Inngest['createFunction']> | undefined;
209
337
 
210
- constructor(params: NewWorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>, inngest: Inngest) {
338
+ constructor(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>, inngest: Inngest) {
211
339
  super(params);
212
340
  this.#mastra = params.mastra!;
213
341
  this.inngest = inngest;
@@ -226,16 +354,19 @@ export class InngestWorkflow<
226
354
  return { runs: [], total: 0 };
227
355
  }
228
356
 
229
- return storage.getWorkflowRuns({ workflowName: this.id, ...(args ?? {}) });
357
+ return storage.getWorkflowRuns({ workflowName: this.id, ...(args ?? {}) }) as unknown as WorkflowRuns;
230
358
  }
231
359
 
232
360
  async getWorkflowRunById(runId: string): Promise<WorkflowRun | null> {
233
361
  const storage = this.#mastra?.getStorage();
234
362
  if (!storage) {
235
363
  this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
236
- return null;
364
+ //returning in memory run if no storage is initialized
365
+ return this.runs.get(runId)
366
+ ? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as WorkflowRun)
367
+ : null;
237
368
  }
238
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
369
+ const run = (await storage.getWorkflowRunById({ runId, workflowName: this.id })) as unknown as WorkflowRun;
239
370
 
240
371
  return (
241
372
  run ??
@@ -243,6 +374,32 @@ export class InngestWorkflow<
243
374
  );
244
375
  }
245
376
 
377
+ async getWorkflowRunExecutionResult(runId: string): Promise<WatchEvent['payload']['workflowState'] | null> {
378
+ const storage = this.#mastra?.getStorage();
379
+ if (!storage) {
380
+ this.logger.debug('Cannot get workflow run execution result. Mastra storage is not initialized');
381
+ return null;
382
+ }
383
+
384
+ const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
385
+
386
+ if (!run?.snapshot) {
387
+ return null;
388
+ }
389
+
390
+ if (typeof run.snapshot === 'string') {
391
+ return null;
392
+ }
393
+
394
+ return {
395
+ status: run.snapshot.status,
396
+ result: run.snapshot.result,
397
+ error: run.snapshot.error,
398
+ payload: run.snapshot.context?.input,
399
+ steps: run.snapshot.context as any,
400
+ };
401
+ }
402
+
246
403
  __registerMastra(mastra: Mastra) {
247
404
  this.#mastra = mastra;
248
405
  this.executionEngine.__registerMastra(mastra);
@@ -266,11 +423,35 @@ export class InngestWorkflow<
266
423
  }
267
424
  }
268
425
 
269
- createRun(options?: { runId?: string }): Run<TSteps, TInput, TOutput> {
426
+ createRun(options?: { runId?: string }): Run<TEngineType, TSteps, TInput, TOutput> {
427
+ const runIdToUse = options?.runId || randomUUID();
428
+
429
+ // Return a new Run instance with object parameters
430
+ const run: Run<TEngineType, TSteps, TInput, TOutput> =
431
+ this.runs.get(runIdToUse) ??
432
+ new InngestRun(
433
+ {
434
+ workflowId: this.id,
435
+ runId: runIdToUse,
436
+ executionEngine: this.executionEngine,
437
+ executionGraph: this.executionGraph,
438
+ serializedStepGraph: this.serializedStepGraph,
439
+ mastra: this.#mastra,
440
+ retryConfig: this.retryConfig,
441
+ cleanup: () => this.runs.delete(runIdToUse),
442
+ },
443
+ this.inngest,
444
+ );
445
+
446
+ this.runs.set(runIdToUse, run);
447
+ return run;
448
+ }
449
+
450
+ async createRunAsync(options?: { runId?: string }): Promise<Run<TEngineType, TSteps, TInput, TOutput>> {
270
451
  const runIdToUse = options?.runId || randomUUID();
271
452
 
272
453
  // Return a new Run instance with object parameters
273
- const run: Run<TSteps, TInput, TOutput> =
454
+ const run: Run<TEngineType, TSteps, TInput, TOutput> =
274
455
  this.runs.get(runIdToUse) ??
275
456
  new InngestRun(
276
457
  {
@@ -278,6 +459,7 @@ export class InngestWorkflow<
278
459
  runId: runIdToUse,
279
460
  executionEngine: this.executionEngine,
280
461
  executionGraph: this.executionGraph,
462
+ serializedStepGraph: this.serializedStepGraph,
281
463
  mastra: this.#mastra,
282
464
  retryConfig: this.retryConfig,
283
465
  cleanup: () => this.runs.delete(runIdToUse),
@@ -286,6 +468,29 @@ export class InngestWorkflow<
286
468
  );
287
469
 
288
470
  this.runs.set(runIdToUse, run);
471
+
472
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
473
+
474
+ if (!workflowSnapshotInStorage) {
475
+ await this.mastra?.getStorage()?.persistWorkflowSnapshot({
476
+ workflowName: this.id,
477
+ runId: runIdToUse,
478
+ snapshot: {
479
+ runId: runIdToUse,
480
+ status: 'pending',
481
+ value: {},
482
+ context: {},
483
+ activePaths: [],
484
+ serializedStepGraph: this.serializedStepGraph,
485
+ suspendedPaths: {},
486
+ result: undefined,
487
+ error: undefined,
488
+ // @ts-ignore
489
+ timestamp: Date.now(),
490
+ },
491
+ });
492
+ }
493
+
289
494
  return run;
290
495
  }
291
496
 
@@ -294,8 +499,12 @@ export class InngestWorkflow<
294
499
  return this.function;
295
500
  }
296
501
  this.function = this.inngest.createFunction(
297
- // @ts-ignore
298
- { id: `workflow.${this.id}`, retries: this.retryConfig?.attempts ?? 0 },
502
+ {
503
+ id: `workflow.${this.id}`,
504
+ // @ts-ignore
505
+ retries: this.retryConfig?.attempts ?? 0,
506
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
507
+ },
299
508
  { event: `workflow.${this.id}` },
300
509
  async ({ event, step, attempt, publish }) => {
301
510
  let { inputData, runId, resume } = event.data;
@@ -315,13 +524,22 @@ export class InngestWorkflow<
315
524
  try {
316
525
  await publish({
317
526
  channel: `workflow:${this.id}:${runId}`,
318
- topic: 'watch',
527
+ topic: event,
319
528
  data,
320
529
  });
321
530
  } catch (err: any) {
322
531
  this.logger.error('Error emitting event: ' + (err?.stack ?? err?.message ?? err));
323
532
  }
324
533
  },
534
+ on: (_event: string, _callback: (data: any) => void) => {
535
+ // no-op
536
+ },
537
+ off: (_event: string, _callback: (data: any) => void) => {
538
+ // no-op
539
+ },
540
+ once: (_event: string, _callback: (data: any) => void) => {
541
+ // no-op
542
+ },
325
543
  };
326
544
 
327
545
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
@@ -329,11 +547,13 @@ export class InngestWorkflow<
329
547
  workflowId: this.id,
330
548
  runId,
331
549
  graph: this.executionGraph,
550
+ serializedStepGraph: this.serializedStepGraph,
332
551
  input: inputData,
333
552
  emitter,
334
553
  retryConfig: this.retryConfig,
335
554
  runtimeContext: new RuntimeContext(), // TODO
336
555
  resume,
556
+ abortController: new AbortController(),
337
557
  });
338
558
 
339
559
  return { result, runId };
@@ -362,29 +582,197 @@ export class InngestWorkflow<
362
582
  }
363
583
  }
364
584
 
365
- function cloneWorkflow<
366
- TWorkflowId extends string = string,
367
- TInput extends z.ZodType<any> = z.ZodType<any>,
368
- TOutput extends z.ZodType<any> = z.ZodType<any>,
369
- TSteps extends Step<string, any, any, any, any>[] = Step<string, any, any, any, any>[],
585
+ function isAgent(params: any): params is Agent<any, any, any> {
586
+ return params?.component === 'AGENT';
587
+ }
588
+
589
+ function isTool(params: any): params is Tool<any, any, any> {
590
+ return params instanceof Tool;
591
+ }
592
+
593
+ export function createStep<
594
+ TStepId extends string,
595
+ TStepInput extends z.ZodType<any>,
596
+ TStepOutput extends z.ZodType<any>,
597
+ TResumeSchema extends z.ZodType<any>,
598
+ TSuspendSchema extends z.ZodType<any>,
599
+ >(params: {
600
+ id: TStepId;
601
+ description?: string;
602
+ inputSchema: TStepInput;
603
+ outputSchema: TStepOutput;
604
+ resumeSchema?: TResumeSchema;
605
+ suspendSchema?: TSuspendSchema;
606
+ execute: ExecuteFunction<
607
+ z.infer<TStepInput>,
608
+ z.infer<TStepOutput>,
609
+ z.infer<TResumeSchema>,
610
+ z.infer<TSuspendSchema>,
611
+ InngestEngineType
612
+ >;
613
+ }): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType>;
614
+
615
+ export function createStep<
616
+ TStepId extends string,
617
+ TStepInput extends z.ZodObject<{ prompt: z.ZodString }>,
618
+ TStepOutput extends z.ZodObject<{ text: z.ZodString }>,
619
+ TResumeSchema extends z.ZodType<any>,
620
+ TSuspendSchema extends z.ZodType<any>,
370
621
  >(
371
- workflow: InngestWorkflow<TSteps, string, TInput, TOutput>,
372
- opts: { id: TWorkflowId },
373
- ): InngestWorkflow<TSteps, TWorkflowId, TInput, TOutput> {
374
- const wf = new InngestWorkflow(
375
- {
376
- id: opts.id,
377
- inputSchema: workflow.inputSchema,
378
- outputSchema: workflow.outputSchema,
379
- steps: workflow.stepDefs,
380
- mastra: workflow.mastra,
381
- },
382
- workflow.inngest,
383
- );
622
+ agent: Agent<TStepId, any, any>,
623
+ ): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType>;
624
+
625
+ export function createStep<
626
+ TSchemaIn extends z.ZodType<any>,
627
+ TSchemaOut extends z.ZodType<any>,
628
+ TContext extends ToolExecutionContext<TSchemaIn>,
629
+ >(
630
+ tool: Tool<TSchemaIn, TSchemaOut, TContext> & {
631
+ inputSchema: TSchemaIn;
632
+ outputSchema: TSchemaOut;
633
+ execute: (context: TContext) => Promise<any>;
634
+ },
635
+ ): Step<string, TSchemaIn, TSchemaOut, z.ZodType<any>, z.ZodType<any>, InngestEngineType>;
636
+ export function createStep<
637
+ TStepId extends string,
638
+ TStepInput extends z.ZodType<any>,
639
+ TStepOutput extends z.ZodType<any>,
640
+ TResumeSchema extends z.ZodType<any>,
641
+ TSuspendSchema extends z.ZodType<any>,
642
+ >(
643
+ params:
644
+ | {
645
+ id: TStepId;
646
+ description?: string;
647
+ inputSchema: TStepInput;
648
+ outputSchema: TStepOutput;
649
+ resumeSchema?: TResumeSchema;
650
+ suspendSchema?: TSuspendSchema;
651
+ execute: ExecuteFunction<
652
+ z.infer<TStepInput>,
653
+ z.infer<TStepOutput>,
654
+ z.infer<TResumeSchema>,
655
+ z.infer<TSuspendSchema>,
656
+ InngestEngineType
657
+ >;
658
+ }
659
+ | Agent<any, any, any>
660
+ | (Tool<TStepInput, TStepOutput, any> & {
661
+ inputSchema: TStepInput;
662
+ outputSchema: TStepOutput;
663
+ execute: (context: ToolExecutionContext<TStepInput>) => Promise<any>;
664
+ }),
665
+ ): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType> {
666
+ if (isAgent(params)) {
667
+ return {
668
+ id: params.name,
669
+ // @ts-ignore
670
+ inputSchema: z.object({
671
+ prompt: z.string(),
672
+ // resourceId: z.string().optional(),
673
+ // threadId: z.string().optional(),
674
+ }),
675
+ // @ts-ignore
676
+ outputSchema: z.object({
677
+ text: z.string(),
678
+ }),
679
+ execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
680
+ let streamPromise = {} as {
681
+ promise: Promise<string>;
682
+ resolve: (value: string) => void;
683
+ reject: (reason?: any) => void;
684
+ };
685
+
686
+ streamPromise.promise = new Promise((resolve, reject) => {
687
+ streamPromise.resolve = resolve;
688
+ streamPromise.reject = reject;
689
+ });
690
+ const toolData = {
691
+ name: params.name,
692
+ args: inputData,
693
+ };
694
+ await emitter.emit('watch-v2', {
695
+ type: 'tool-call-streaming-start',
696
+ ...toolData,
697
+ });
698
+ const { fullStream } = await params.stream(inputData.prompt, {
699
+ // resourceId: inputData.resourceId,
700
+ // threadId: inputData.threadId,
701
+ runtimeContext,
702
+ onFinish: result => {
703
+ streamPromise.resolve(result.text);
704
+ },
705
+ abortSignal,
706
+ });
707
+
708
+ if (abortSignal.aborted) {
709
+ return abort();
710
+ }
711
+
712
+ for await (const chunk of fullStream) {
713
+ switch (chunk.type) {
714
+ case 'text-delta':
715
+ await emitter.emit('watch-v2', {
716
+ type: 'tool-call-delta',
717
+ ...toolData,
718
+ argsTextDelta: chunk.textDelta,
719
+ });
720
+ break;
721
+
722
+ case 'step-start':
723
+ case 'step-finish':
724
+ case 'finish':
725
+ break;
726
+
727
+ case 'tool-call':
728
+ case 'tool-result':
729
+ case 'tool-call-streaming-start':
730
+ case 'tool-call-delta':
731
+ case 'source':
732
+ case 'file':
733
+ default:
734
+ await emitter.emit('watch-v2', chunk);
735
+ break;
736
+ }
737
+ }
384
738
 
385
- wf.setStepFlow(workflow.stepGraph);
386
- wf.commit();
387
- return wf;
739
+ return {
740
+ text: await streamPromise.promise,
741
+ };
742
+ },
743
+ };
744
+ }
745
+
746
+ if (isTool(params)) {
747
+ if (!params.inputSchema || !params.outputSchema) {
748
+ throw new Error('Tool must have input and output schemas defined');
749
+ }
750
+
751
+ return {
752
+ // TODO: tool probably should have strong id type
753
+ // @ts-ignore
754
+ id: params.id,
755
+ inputSchema: params.inputSchema,
756
+ outputSchema: params.outputSchema,
757
+ execute: async ({ inputData, mastra, runtimeContext }) => {
758
+ return params.execute({
759
+ context: inputData,
760
+ mastra,
761
+ runtimeContext,
762
+ });
763
+ },
764
+ };
765
+ }
766
+
767
+ return {
768
+ id: params.id,
769
+ description: params.description,
770
+ inputSchema: params.inputSchema,
771
+ outputSchema: params.outputSchema,
772
+ resumeSchema: params.resumeSchema,
773
+ suspendSchema: params.suspendSchema,
774
+ execute: params.execute,
775
+ };
388
776
  }
389
777
 
390
778
  export function init(inngest: Inngest) {
@@ -393,13 +781,59 @@ export function init(inngest: Inngest) {
393
781
  TWorkflowId extends string = string,
394
782
  TInput extends z.ZodType<any> = z.ZodType<any>,
395
783
  TOutput extends z.ZodType<any> = z.ZodType<any>,
396
- TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
397
- >(params: NewWorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>) {
398
- return new InngestWorkflow(params, inngest);
784
+ TSteps extends Step<string, any, any, any, any, InngestEngineType>[] = Step<
785
+ string,
786
+ any,
787
+ any,
788
+ any,
789
+ any,
790
+ InngestEngineType
791
+ >[],
792
+ >(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>) {
793
+ return new InngestWorkflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TInput>(params, inngest);
399
794
  },
400
795
  createStep,
401
- cloneStep,
402
- cloneWorkflow,
796
+ cloneStep<TStepId extends string>(
797
+ step: Step<string, any, any, any, any, InngestEngineType>,
798
+ opts: { id: TStepId },
799
+ ): Step<TStepId, any, any, any, any, InngestEngineType> {
800
+ return {
801
+ id: opts.id,
802
+ description: step.description,
803
+ inputSchema: step.inputSchema,
804
+ outputSchema: step.outputSchema,
805
+ execute: step.execute,
806
+ };
807
+ },
808
+ cloneWorkflow<
809
+ TWorkflowId extends string = string,
810
+ TInput extends z.ZodType<any> = z.ZodType<any>,
811
+ TOutput extends z.ZodType<any> = z.ZodType<any>,
812
+ TSteps extends Step<string, any, any, any, any, InngestEngineType>[] = Step<
813
+ string,
814
+ any,
815
+ any,
816
+ any,
817
+ any,
818
+ InngestEngineType
819
+ >[],
820
+ TPrevSchema extends z.ZodType<any> = TInput,
821
+ >(
822
+ workflow: Workflow<InngestEngineType, TSteps, string, TInput, TOutput, TPrevSchema>,
823
+ opts: { id: TWorkflowId },
824
+ ): Workflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
825
+ const wf: Workflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> = new Workflow({
826
+ id: opts.id,
827
+ inputSchema: workflow.inputSchema,
828
+ outputSchema: workflow.outputSchema,
829
+ steps: workflow.stepDefs,
830
+ mastra: workflow.mastra,
831
+ });
832
+
833
+ wf.setStepFlow(workflow.stepGraph);
834
+ wf.commit();
835
+ return wf;
836
+ },
403
837
  };
404
838
  }
405
839
 
@@ -413,11 +847,47 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
413
847
  this.inngestAttempts = inngestAttempts;
414
848
  }
415
849
 
850
+ async execute<TInput, TOutput>(params: {
851
+ workflowId: string;
852
+ runId: string;
853
+ graph: ExecutionGraph;
854
+ serializedStepGraph: SerializedStepFlowEntry[];
855
+ input?: TInput;
856
+ resume?: {
857
+ // TODO: add execute path
858
+ steps: string[];
859
+ stepResults: Record<string, StepResult<any, any, any, any>>;
860
+ resumePayload: any;
861
+ resumePath: number[];
862
+ };
863
+ emitter: Emitter;
864
+ retryConfig?: {
865
+ attempts?: number;
866
+ delay?: number;
867
+ };
868
+ runtimeContext: RuntimeContext;
869
+ abortController: AbortController;
870
+ }): Promise<TOutput> {
871
+ await params.emitter.emit('watch-v2', {
872
+ type: 'start',
873
+ payload: { runId: params.runId },
874
+ });
875
+
876
+ const result = await super.execute<TInput, TOutput>(params);
877
+
878
+ await params.emitter.emit('watch-v2', {
879
+ type: 'finish',
880
+ payload: { runId: params.runId },
881
+ });
882
+
883
+ return result;
884
+ }
885
+
416
886
  protected async fmtReturnValue<TOutput>(
417
887
  executionSpan: Span | undefined,
418
- emitter: { emit: (event: string, data: any) => Promise<void> },
419
- stepResults: Record<string, StepResult<any>>,
420
- lastOutput: StepResult<any>,
888
+ emitter: Emitter,
889
+ stepResults: Record<string, StepResult<any, any, any, any>>,
890
+ lastOutput: StepResult<any, any, any, any>,
421
891
  error?: Error | string,
422
892
  ): Promise<TOutput> {
423
893
  const base: any = {
@@ -496,21 +966,23 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
496
966
  resume,
497
967
  prevOutput,
498
968
  emitter,
969
+ abortController,
499
970
  runtimeContext,
500
971
  }: {
501
972
  workflowId: string;
502
973
  runId: string;
503
974
  step: Step<string, any, any>;
504
- stepResults: Record<string, StepResult<any>>;
975
+ stepResults: Record<string, StepResult<any, any, any, any>>;
505
976
  executionContext: ExecutionContext;
506
977
  resume?: {
507
978
  steps: string[];
508
979
  resumePayload: any;
509
980
  };
510
981
  prevOutput: any;
511
- emitter: { emit: (event: string, data: any) => Promise<void> };
982
+ emitter: Emitter;
983
+ abortController: AbortController;
512
984
  runtimeContext: RuntimeContext;
513
- }): Promise<StepResult<any>> {
985
+ }): Promise<StepResult<any, any, any, any>> {
514
986
  return super.executeStep({
515
987
  workflowId,
516
988
  runId,
@@ -520,10 +992,178 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
520
992
  resume,
521
993
  prevOutput,
522
994
  emitter,
995
+ abortController,
523
996
  runtimeContext,
524
997
  });
525
998
  }
526
999
 
1000
+ // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
1001
+ // await this.inngestStep.sleep(id, duration);
1002
+ // }
1003
+
1004
+ async executeSleep({
1005
+ workflowId,
1006
+ runId,
1007
+ entry,
1008
+ prevOutput,
1009
+ stepResults,
1010
+ emitter,
1011
+ abortController,
1012
+ runtimeContext,
1013
+ }: {
1014
+ workflowId: string;
1015
+ runId: string;
1016
+ serializedStepGraph: SerializedStepFlowEntry[];
1017
+ entry: {
1018
+ type: 'sleep';
1019
+ id: string;
1020
+ duration?: number;
1021
+ fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
1022
+ };
1023
+ prevStep: StepFlowEntry;
1024
+ prevOutput: any;
1025
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1026
+ resume?: {
1027
+ steps: string[];
1028
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1029
+ resumePayload: any;
1030
+ resumePath: number[];
1031
+ };
1032
+ executionContext: ExecutionContext;
1033
+ emitter: Emitter;
1034
+ abortController: AbortController;
1035
+ runtimeContext: RuntimeContext;
1036
+ }): Promise<void> {
1037
+ let { duration, fn } = entry;
1038
+
1039
+ if (fn) {
1040
+ duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
1041
+ return await fn({
1042
+ runId,
1043
+ mastra: this.mastra!,
1044
+ runtimeContext,
1045
+ inputData: prevOutput,
1046
+ runCount: -1,
1047
+ getInitData: () => stepResults?.input as any,
1048
+ getStepResult: (step: any) => {
1049
+ if (!step?.id) {
1050
+ return null;
1051
+ }
1052
+
1053
+ const result = stepResults[step.id];
1054
+ if (result?.status === 'success') {
1055
+ return result.output;
1056
+ }
1057
+
1058
+ return null;
1059
+ },
1060
+
1061
+ // TODO: this function shouldn't have suspend probably?
1062
+ suspend: async (_suspendPayload: any): Promise<any> => {},
1063
+ bail: () => {},
1064
+ abort: () => {
1065
+ abortController?.abort();
1066
+ },
1067
+ [EMITTER_SYMBOL]: emitter,
1068
+ engine: { step: this.inngestStep },
1069
+ abortSignal: abortController?.signal,
1070
+ });
1071
+ });
1072
+ }
1073
+
1074
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
1075
+ }
1076
+
1077
+ async executeSleepUntil({
1078
+ workflowId,
1079
+ runId,
1080
+ entry,
1081
+ prevOutput,
1082
+ stepResults,
1083
+ emitter,
1084
+ abortController,
1085
+ runtimeContext,
1086
+ }: {
1087
+ workflowId: string;
1088
+ runId: string;
1089
+ serializedStepGraph: SerializedStepFlowEntry[];
1090
+ entry: {
1091
+ type: 'sleepUntil';
1092
+ id: string;
1093
+ date?: Date;
1094
+ fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
1095
+ };
1096
+ prevStep: StepFlowEntry;
1097
+ prevOutput: any;
1098
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1099
+ resume?: {
1100
+ steps: string[];
1101
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1102
+ resumePayload: any;
1103
+ resumePath: number[];
1104
+ };
1105
+ executionContext: ExecutionContext;
1106
+ emitter: Emitter;
1107
+ abortController: AbortController;
1108
+ runtimeContext: RuntimeContext;
1109
+ }): Promise<void> {
1110
+ let { date, fn } = entry;
1111
+
1112
+ if (fn) {
1113
+ date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
1114
+ return await fn({
1115
+ runId,
1116
+ mastra: this.mastra!,
1117
+ runtimeContext,
1118
+ inputData: prevOutput,
1119
+ runCount: -1,
1120
+ getInitData: () => stepResults?.input as any,
1121
+ getStepResult: (step: any) => {
1122
+ if (!step?.id) {
1123
+ return null;
1124
+ }
1125
+
1126
+ const result = stepResults[step.id];
1127
+ if (result?.status === 'success') {
1128
+ return result.output;
1129
+ }
1130
+
1131
+ return null;
1132
+ },
1133
+
1134
+ // TODO: this function shouldn't have suspend probably?
1135
+ suspend: async (_suspendPayload: any): Promise<any> => {},
1136
+ bail: () => {},
1137
+ abort: () => {
1138
+ abortController?.abort();
1139
+ },
1140
+ [EMITTER_SYMBOL]: emitter,
1141
+ engine: { step: this.inngestStep },
1142
+ abortSignal: abortController?.signal,
1143
+ });
1144
+ });
1145
+ }
1146
+
1147
+ if (!(date instanceof Date)) {
1148
+ return;
1149
+ }
1150
+
1151
+ await this.inngestStep.sleepUntil(entry.id, date);
1152
+ }
1153
+
1154
+ async executeWaitForEvent({ event, timeout }: { event: string; timeout?: number }): Promise<any> {
1155
+ const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
1156
+ event: `user-event-${event}`,
1157
+ timeout: timeout ?? 5e3,
1158
+ });
1159
+
1160
+ if (eventData === null) {
1161
+ throw 'Timeout waiting for event';
1162
+ }
1163
+
1164
+ return eventData?.data;
1165
+ }
1166
+
527
1167
  async executeStep({
528
1168
  step,
529
1169
  stepResults,
@@ -531,29 +1171,26 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
531
1171
  resume,
532
1172
  prevOutput,
533
1173
  emitter,
1174
+ abortController,
534
1175
  runtimeContext,
535
1176
  }: {
536
1177
  step: Step<string, any, any>;
537
- stepResults: Record<string, StepResult<any>>;
538
- executionContext: {
539
- workflowId: string;
540
- runId: string;
541
- executionPath: number[];
542
- suspendedPaths: Record<string, number[]>;
543
- retryConfig: { attempts: number; delay: number };
544
- };
1178
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1179
+ executionContext: ExecutionContext;
545
1180
  resume?: {
546
1181
  steps: string[];
547
1182
  resumePayload: any;
548
1183
  runId?: string;
549
1184
  };
550
1185
  prevOutput: any;
551
- emitter: { emit: (event: string, data: any) => Promise<void> };
1186
+ emitter: Emitter;
1187
+ abortController: AbortController;
552
1188
  runtimeContext: RuntimeContext;
553
- }): Promise<StepResult<any>> {
554
- await this.inngestStep.run(
1189
+ }): Promise<StepResult<any, any, any, any>> {
1190
+ const startedAt = await this.inngestStep.run(
555
1191
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
556
1192
  async () => {
1193
+ const startedAt = Date.now();
557
1194
  await emitter.emit('watch', {
558
1195
  type: 'watch',
559
1196
  payload: {
@@ -575,6 +1212,18 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
575
1212
  },
576
1213
  eventTimestamp: Date.now(),
577
1214
  });
1215
+
1216
+ await emitter.emit('watch-v2', {
1217
+ type: 'step-start',
1218
+ payload: {
1219
+ id: step.id,
1220
+ status: 'running',
1221
+ payload: prevOutput,
1222
+ startedAt,
1223
+ },
1224
+ });
1225
+
1226
+ return startedAt;
578
1227
  },
579
1228
  );
580
1229
 
@@ -641,10 +1290,20 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
641
1290
  eventTimestamp: Date.now(),
642
1291
  });
643
1292
 
1293
+ await emitter.emit('watch-v2', {
1294
+ type: 'step-result',
1295
+ payload: {
1296
+ id: step.id,
1297
+ status: 'failed',
1298
+ error: result?.error,
1299
+ payload: prevOutput,
1300
+ },
1301
+ });
1302
+
644
1303
  return { executionContext, result: { status: 'failed', error: result?.error } };
645
1304
  } else if (result.status === 'suspended') {
646
1305
  const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
647
- const stepRes: StepResult<any> = stepResult as StepResult<any>;
1306
+ const stepRes: StepResult<any, any, any, any> = stepResult as StepResult<any, any, any, any>;
648
1307
  return stepRes?.status === 'suspended';
649
1308
  });
650
1309
 
@@ -671,6 +1330,14 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
671
1330
  eventTimestamp: Date.now(),
672
1331
  });
673
1332
 
1333
+ await emitter.emit('watch-v2', {
1334
+ type: 'step-suspended',
1335
+ payload: {
1336
+ id: step.id,
1337
+ status: 'suspended',
1338
+ },
1339
+ });
1340
+
674
1341
  return {
675
1342
  executionContext,
676
1343
  result: {
@@ -727,19 +1394,39 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
727
1394
  eventTimestamp: Date.now(),
728
1395
  });
729
1396
 
1397
+ await emitter.emit('watch-v2', {
1398
+ type: 'step-result',
1399
+ payload: {
1400
+ id: step.id,
1401
+ status: 'success',
1402
+ output: result?.result,
1403
+ },
1404
+ });
1405
+
1406
+ await emitter.emit('watch-v2', {
1407
+ type: 'step-finish',
1408
+ payload: {
1409
+ id: step.id,
1410
+ metadata: {},
1411
+ },
1412
+ });
1413
+
730
1414
  return { executionContext, result: { status: 'success', output: result?.result } };
731
1415
  },
732
1416
  );
733
1417
 
734
1418
  Object.assign(executionContext, res.executionContext);
735
- return res.result as StepResult<any>;
1419
+ return res.result as StepResult<any, any, any, any>;
736
1420
  }
737
1421
 
738
1422
  const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
739
1423
  let execResults: any;
740
1424
  let suspended: { payload: any } | undefined;
1425
+ let bailed: { payload: any } | undefined;
1426
+
741
1427
  try {
742
1428
  const result = await step.execute({
1429
+ runId: executionContext.runId,
743
1430
  mastra: this.mastra!,
744
1431
  runtimeContext,
745
1432
  inputData: prevOutput,
@@ -757,22 +1444,56 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
757
1444
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
758
1445
  suspended = { payload: suspendPayload };
759
1446
  },
1447
+ bail: (result: any) => {
1448
+ bailed = { payload: result };
1449
+ },
760
1450
  resume: {
761
1451
  steps: resume?.steps?.slice(1) || [],
762
1452
  resumePayload: resume?.resumePayload,
763
1453
  // @ts-ignore
764
1454
  runId: stepResults[step.id]?.payload?.__workflow_meta?.runId,
765
1455
  },
766
- emitter,
1456
+ [EMITTER_SYMBOL]: emitter,
1457
+ engine: {
1458
+ step: this.inngestStep,
1459
+ },
1460
+ abortSignal: abortController.signal,
767
1461
  });
768
-
769
- execResults = { status: 'success', output: result };
1462
+ const endedAt = Date.now();
1463
+
1464
+ execResults = {
1465
+ status: 'success',
1466
+ output: result,
1467
+ startedAt,
1468
+ endedAt,
1469
+ payload: prevOutput,
1470
+ resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
1471
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1472
+ };
770
1473
  } catch (e) {
771
- execResults = { status: 'failed', error: e instanceof Error ? e.message : String(e) };
1474
+ execResults = {
1475
+ status: 'failed',
1476
+ payload: prevOutput,
1477
+ error: e instanceof Error ? e.message : String(e),
1478
+ endedAt: Date.now(),
1479
+ startedAt,
1480
+ resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
1481
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1482
+ };
772
1483
  }
773
1484
 
774
1485
  if (suspended) {
775
- execResults = { status: 'suspended', payload: suspended.payload };
1486
+ execResults = {
1487
+ status: 'suspended',
1488
+ suspendedPayload: suspended.payload,
1489
+ payload: prevOutput,
1490
+ suspendedAt: Date.now(),
1491
+ startedAt,
1492
+ resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
1493
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1494
+ };
1495
+ } else if (bailed) {
1496
+ execResults = { status: 'bailed', output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
776
1497
  }
777
1498
 
778
1499
  if (execResults.status === 'failed') {
@@ -786,12 +1507,11 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
786
1507
  payload: {
787
1508
  currentStep: {
788
1509
  id: step.id,
789
- status: execResults.status,
790
- output: execResults.output,
1510
+ ...execResults,
791
1511
  },
792
1512
  workflowState: {
793
1513
  status: 'running',
794
- steps: stepResults,
1514
+ steps: { ...stepResults, [step.id]: execResults },
795
1515
  result: null,
796
1516
  error: null,
797
1517
  },
@@ -799,6 +1519,32 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
799
1519
  eventTimestamp: Date.now(),
800
1520
  });
801
1521
 
1522
+ if (execResults.status === 'suspended') {
1523
+ await emitter.emit('watch-v2', {
1524
+ type: 'step-suspended',
1525
+ payload: {
1526
+ id: step.id,
1527
+ ...execResults,
1528
+ },
1529
+ });
1530
+ } else {
1531
+ await emitter.emit('watch-v2', {
1532
+ type: 'step-result',
1533
+ payload: {
1534
+ id: step.id,
1535
+ ...execResults,
1536
+ },
1537
+ });
1538
+
1539
+ await emitter.emit('watch-v2', {
1540
+ type: 'step-finish',
1541
+ payload: {
1542
+ id: step.id,
1543
+ metadata: {},
1544
+ },
1545
+ });
1546
+ }
1547
+
802
1548
  return { result: execResults, executionContext, stepResults };
803
1549
  });
804
1550
 
@@ -816,11 +1562,20 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
816
1562
  runId,
817
1563
  stepResults,
818
1564
  executionContext,
1565
+ serializedStepGraph,
1566
+ workflowStatus,
1567
+ result,
1568
+ error,
819
1569
  }: {
820
1570
  workflowId: string;
821
1571
  runId: string;
822
- stepResults: Record<string, StepResult<any>>;
1572
+ stepResults: Record<string, StepResult<any, any, any, any>>;
1573
+ serializedStepGraph: SerializedStepFlowEntry[];
823
1574
  executionContext: ExecutionContext;
1575
+ workflowStatus: 'success' | 'failed' | 'suspended' | 'running';
1576
+ result?: Record<string, any>;
1577
+ error?: string | Error;
1578
+ runtimeContext: RuntimeContext;
824
1579
  }) {
825
1580
  await this.inngestStep.run(
826
1581
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
@@ -834,6 +1589,10 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
834
1589
  context: stepResults as any,
835
1590
  activePaths: [],
836
1591
  suspendedPaths: executionContext.suspendedPaths,
1592
+ serializedStepGraph,
1593
+ status: workflowStatus,
1594
+ result,
1595
+ error,
837
1596
  // @ts-ignore
838
1597
  timestamp: Date.now(),
839
1598
  },
@@ -849,27 +1608,35 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
849
1608
  prevOutput,
850
1609
  prevStep,
851
1610
  stepResults,
1611
+ serializedStepGraph,
852
1612
  resume,
853
1613
  executionContext,
854
1614
  emitter,
1615
+ abortController,
855
1616
  runtimeContext,
856
1617
  }: {
857
1618
  workflowId: string;
858
1619
  runId: string;
859
- entry: { type: 'conditional'; steps: StepFlowEntry[]; conditions: ExecuteFunction<any, any, any, any>[] };
1620
+ entry: {
1621
+ type: 'conditional';
1622
+ steps: StepFlowEntry[];
1623
+ conditions: ExecuteFunction<any, any, any, any, InngestEngineType>[];
1624
+ };
860
1625
  prevStep: StepFlowEntry;
1626
+ serializedStepGraph: SerializedStepFlowEntry[];
861
1627
  prevOutput: any;
862
- stepResults: Record<string, StepResult<any>>;
1628
+ stepResults: Record<string, StepResult<any, any, any, any>>;
863
1629
  resume?: {
864
1630
  steps: string[];
865
- stepResults: Record<string, StepResult<any>>;
1631
+ stepResults: Record<string, StepResult<any, any, any, any>>;
866
1632
  resumePayload: any;
867
1633
  resumePath: number[];
868
1634
  };
869
1635
  executionContext: ExecutionContext;
870
- emitter: { emit: (event: string, data: any) => Promise<void> };
1636
+ emitter: Emitter;
1637
+ abortController: AbortController;
871
1638
  runtimeContext: RuntimeContext;
872
- }): Promise<StepResult<any>> {
1639
+ }): Promise<StepResult<any, any, any, any>> {
873
1640
  let execResults: any;
874
1641
  const truthyIndexes = (
875
1642
  await Promise.all(
@@ -877,8 +1644,10 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
877
1644
  this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
878
1645
  try {
879
1646
  const result = await cond({
1647
+ runId,
880
1648
  mastra: this.mastra!,
881
1649
  runtimeContext,
1650
+ runCount: -1,
882
1651
  inputData: prevOutput,
883
1652
  getInitData: () => stepResults?.input as any,
884
1653
  getStepResult: (step: any) => {
@@ -896,7 +1665,15 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
896
1665
 
897
1666
  // TODO: this function shouldn't have suspend probably?
898
1667
  suspend: async (_suspendPayload: any) => {},
899
- emitter,
1668
+ bail: () => {},
1669
+ abort: () => {
1670
+ abortController.abort();
1671
+ },
1672
+ [EMITTER_SYMBOL]: emitter,
1673
+ engine: {
1674
+ step: this.inngestStep,
1675
+ },
1676
+ abortSignal: abortController.signal,
900
1677
  });
901
1678
  return result ? index : null;
902
1679
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -909,7 +1686,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
909
1686
  ).filter((index: any): index is number => index !== null);
910
1687
 
911
1688
  const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
912
- const results: StepResult<any>[] = await Promise.all(
1689
+ const results: { result: StepResult<any, any, any, any> }[] = await Promise.all(
913
1690
  stepsToRun.map((step, index) =>
914
1691
  this.executeEntry({
915
1692
  workflowId,
@@ -918,6 +1695,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
918
1695
  prevStep,
919
1696
  stepResults,
920
1697
  resume,
1698
+ serializedStepGraph,
921
1699
  executionContext: {
922
1700
  workflowId,
923
1701
  runId,
@@ -927,21 +1705,24 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
927
1705
  executionSpan: executionContext.executionSpan,
928
1706
  },
929
1707
  emitter,
1708
+ abortController,
930
1709
  runtimeContext,
931
1710
  }),
932
1711
  ),
933
1712
  );
934
- const hasFailed = results.find(result => result.status === 'failed');
935
- const hasSuspended = results.find(result => result.status === 'suspended');
1713
+ const hasFailed = results.find(result => result.result.status === 'failed') as {
1714
+ result: StepFailure<any, any, any>;
1715
+ };
1716
+ const hasSuspended = results.find(result => result.result.status === 'suspended');
936
1717
  if (hasFailed) {
937
- execResults = { status: 'failed', error: hasFailed.error };
1718
+ execResults = { status: 'failed', error: hasFailed.result.error };
938
1719
  } else if (hasSuspended) {
939
- execResults = { status: 'suspended', payload: hasSuspended.payload };
1720
+ execResults = { status: 'suspended', payload: hasSuspended.result.suspendPayload };
940
1721
  } else {
941
1722
  execResults = {
942
1723
  status: 'success',
943
1724
  output: results.reduce((acc: Record<string, any>, result, index) => {
944
- if (result.status === 'success') {
1725
+ if (result.result.status === 'success') {
945
1726
  // @ts-ignore
946
1727
  acc[stepsToRun[index]!.step.id] = result.output;
947
1728
  }