@mastra/inngest 0.10.5 → 0.10.6-alpha.0

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.test.ts CHANGED
@@ -5,10 +5,12 @@ import { openai } from '@ai-sdk/openai';
5
5
  import { serve } from '@hono/node-server';
6
6
  import { realtimeMiddleware } from '@inngest/realtime';
7
7
  import { createTool, Mastra, Telemetry } from '@mastra/core';
8
+ import type { StreamEvent } from '@mastra/core';
8
9
  import { Agent } from '@mastra/core/agent';
9
10
  import { RuntimeContext } from '@mastra/core/runtime-context';
10
11
  import { createHonoServer } from '@mastra/deployer/server';
11
12
  import { DefaultStorage } from '@mastra/libsql';
13
+ import { MockLanguageModelV1, simulateReadableStream } from 'ai/test';
12
14
  import { $ } from 'execa';
13
15
  import getPort from 'get-port';
14
16
  import { Inngest } from 'inngest';
@@ -33,13 +35,15 @@ describe('MastraInngestWorkflow', () => {
33
35
  ctx.inngestPort = inngestPort;
34
36
  ctx.handlerPort = handlerPort;
35
37
  ctx.containerName = containerName;
38
+
39
+ vi.restoreAllMocks();
36
40
  });
37
41
 
38
42
  afterEach<LocalTestContext>(async ctx => {
39
43
  await $`docker stop ${ctx.containerName}`;
40
44
  });
41
45
 
42
- describe('Basic Workflow Execution', () => {
46
+ describe.sequential('Basic Workflow Execution', () => {
43
47
  it('should execute a single step workflow successfully', async ctx => {
44
48
  const inngest = new Inngest({
45
49
  id: 'mastra',
@@ -95,7 +99,7 @@ describe('MastraInngestWorkflow', () => {
95
99
  const result = await run.start({ inputData: {} });
96
100
 
97
101
  expect(execute).toHaveBeenCalled();
98
- expect(result.steps['step1']).toEqual({
102
+ expect(result.steps['step1']).toMatchObject({
99
103
  status: 'success',
100
104
  output: { result: 'success' },
101
105
  });
@@ -171,7 +175,7 @@ describe('MastraInngestWorkflow', () => {
171
175
 
172
176
  expect(step1Action).toHaveBeenCalled();
173
177
  expect(step2Action).toHaveBeenCalled();
174
- expect(result.steps).toEqual({
178
+ expect(result.steps).toMatchObject({
175
179
  input: {},
176
180
  step1: { status: 'success', output: { value: 'step1' } },
177
181
  step2: { status: 'success', output: { value: 'step2' } },
@@ -250,8 +254,8 @@ describe('MastraInngestWorkflow', () => {
250
254
  const run = workflow.createRun();
251
255
  const result = await run.start({ inputData: {} });
252
256
 
253
- expect(executionOrder).toEqual(['step1', 'step2']);
254
- expect(result.steps).toEqual({
257
+ expect(executionOrder).toMatchObject(['step1', 'step2']);
258
+ expect(result.steps).toMatchObject({
255
259
  input: {},
256
260
  step1: { status: 'success', output: { value: 'step1' } },
257
261
  step2: { status: 'success', output: { value: 'step2' } },
@@ -327,7 +331,7 @@ describe('MastraInngestWorkflow', () => {
327
331
  const endTime = Date.now();
328
332
 
329
333
  expect(execute).toHaveBeenCalled();
330
- expect(result.steps['step1']).toEqual({
334
+ expect(result.steps['step1']).toMatchObject({
331
335
  status: 'success',
332
336
  output: { result: 'success' },
333
337
  // payload: {},
@@ -335,7 +339,7 @@ describe('MastraInngestWorkflow', () => {
335
339
  // endedAt: expect.any(Number),
336
340
  });
337
341
 
338
- expect(result.steps['step2']).toEqual({
342
+ expect(result.steps['step2']).toMatchObject({
339
343
  status: 'success',
340
344
  output: { result: 'slept successfully: success' },
341
345
  // payload: { result: 'success' },
@@ -419,7 +423,7 @@ describe('MastraInngestWorkflow', () => {
419
423
  const endTime = Date.now();
420
424
 
421
425
  expect(execute).toHaveBeenCalled();
422
- expect(result.steps['step1']).toEqual({
426
+ expect(result.steps['step1']).toMatchObject({
423
427
  status: 'success',
424
428
  output: { result: 'success' },
425
429
  // payload: {},
@@ -427,7 +431,7 @@ describe('MastraInngestWorkflow', () => {
427
431
  // endedAt: expect.any(Number),
428
432
  });
429
433
 
430
- expect(result.steps['step2']).toEqual({
434
+ expect(result.steps['step2']).toMatchObject({
431
435
  status: 'success',
432
436
  output: { result: 'slept successfully: success' },
433
437
  // payload: { result: 'success' },
@@ -439,6 +443,190 @@ describe('MastraInngestWorkflow', () => {
439
443
 
440
444
  srv.close();
441
445
  });
446
+
447
+ it('should execute a a waitForEvent step', async ctx => {
448
+ const inngest = new Inngest({
449
+ id: 'mastra',
450
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
451
+ });
452
+
453
+ const { createWorkflow, createStep } = init(inngest);
454
+
455
+ const execute = vi.fn<any>().mockResolvedValue({ result: 'success' });
456
+ const step1 = createStep({
457
+ id: 'step1',
458
+ execute,
459
+ inputSchema: z.object({}),
460
+ outputSchema: z.object({ result: z.string() }),
461
+ });
462
+ const step2 = createStep({
463
+ id: 'step2',
464
+ execute: async ({ inputData, resumeData }) => {
465
+ return { result: inputData.result, resumed: resumeData };
466
+ },
467
+ inputSchema: z.object({ result: z.string() }),
468
+ outputSchema: z.object({ result: z.string(), resumed: z.any() }),
469
+ resumeSchema: z.any(),
470
+ });
471
+
472
+ const workflow = createWorkflow({
473
+ id: 'test-workflow',
474
+ inputSchema: z.object({}),
475
+ outputSchema: z.object({
476
+ result: z.string(),
477
+ resumed: z.any(),
478
+ }),
479
+ steps: [step1],
480
+ });
481
+
482
+ workflow.then(step1).waitForEvent('hello-event', step2).commit();
483
+
484
+ const mastra = new Mastra({
485
+ storage: new DefaultStorage({
486
+ url: ':memory:',
487
+ }),
488
+ workflows: {
489
+ 'test-workflow': workflow,
490
+ },
491
+ server: {
492
+ apiRoutes: [
493
+ {
494
+ path: '/inngest/api',
495
+ method: 'ALL',
496
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
497
+ },
498
+ ],
499
+ },
500
+ });
501
+
502
+ const app = await createHonoServer(mastra);
503
+
504
+ const srv = serve({
505
+ fetch: app.fetch,
506
+ port: (ctx as any).handlerPort,
507
+ });
508
+ await new Promise(resolve => setTimeout(resolve, 2000));
509
+
510
+ const run = workflow.createRun();
511
+ const startTime = Date.now();
512
+ setTimeout(() => {
513
+ run.sendEvent('hello-event', { data: 'hello' });
514
+ }, 1000);
515
+ const result = await run.start({ inputData: {} });
516
+ const endTime = Date.now();
517
+
518
+ expect(execute).toHaveBeenCalled();
519
+ expect(result.steps['step1']).toMatchObject({
520
+ status: 'success',
521
+ output: { result: 'success' },
522
+ // payload: {},
523
+ // startedAt: expect.any(Number),
524
+ // endedAt: expect.any(Number),
525
+ });
526
+
527
+ expect(result.steps['step2']).toMatchObject({
528
+ status: 'success',
529
+ output: { result: 'success', resumed: { data: 'hello' } },
530
+ payload: { result: 'success' },
531
+ // resumePayload: { data: 'hello' },
532
+ startedAt: expect.any(Number),
533
+ endedAt: expect.any(Number),
534
+ });
535
+
536
+ expect(endTime - startTime).toBeGreaterThan(1000);
537
+
538
+ srv.close();
539
+ });
540
+
541
+ it('should execute a a waitForEvent step after timeout', async ctx => {
542
+ const inngest = new Inngest({
543
+ id: 'mastra',
544
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
545
+ });
546
+
547
+ const { createWorkflow, createStep } = init(inngest);
548
+
549
+ const execute = vi.fn<any>().mockResolvedValue({ result: 'success' });
550
+ const step1 = createStep({
551
+ id: 'step1',
552
+ execute,
553
+ inputSchema: z.object({}),
554
+ outputSchema: z.object({ result: z.string() }),
555
+ });
556
+ const step2 = createStep({
557
+ id: 'step2',
558
+ execute: async ({ inputData, resumeData }) => {
559
+ return { result: inputData.result, resumed: resumeData };
560
+ },
561
+ inputSchema: z.object({ result: z.string() }),
562
+ outputSchema: z.object({ result: z.string(), resumed: z.any() }),
563
+ resumeSchema: z.any(),
564
+ });
565
+
566
+ const workflow = createWorkflow({
567
+ id: 'test-workflow',
568
+ inputSchema: z.object({}),
569
+ outputSchema: z.object({
570
+ result: z.string(),
571
+ resumed: z.any(),
572
+ }),
573
+ steps: [step1],
574
+ });
575
+
576
+ workflow.then(step1).waitForEvent('hello-event', step2, { timeout: 1000 }).commit();
577
+
578
+ const mastra = new Mastra({
579
+ storage: new DefaultStorage({
580
+ url: ':memory:',
581
+ }),
582
+ workflows: {
583
+ 'test-workflow': workflow,
584
+ },
585
+ server: {
586
+ apiRoutes: [
587
+ {
588
+ path: '/inngest/api',
589
+ method: 'ALL',
590
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
591
+ },
592
+ ],
593
+ },
594
+ });
595
+
596
+ const app = await createHonoServer(mastra);
597
+
598
+ const srv = serve({
599
+ fetch: app.fetch,
600
+ port: (ctx as any).handlerPort,
601
+ });
602
+ await new Promise(resolve => setTimeout(resolve, 2000));
603
+
604
+ const run = workflow.createRun();
605
+ const startTime = Date.now();
606
+ const result = await run.start({ inputData: {} });
607
+ const endTime = Date.now();
608
+
609
+ expect(execute).toHaveBeenCalled();
610
+ expect(result.steps['step1']).toMatchObject({
611
+ status: 'success',
612
+ output: { result: 'success' },
613
+ // payload: {},
614
+ // startedAt: expect.any(Number),
615
+ // endedAt: expect.any(Number),
616
+ });
617
+
618
+ expect(result.steps['step2']).toMatchObject({
619
+ status: 'failed',
620
+ error: expect.any(String),
621
+ payload: { result: 'success' },
622
+ startedAt: expect.any(Number),
623
+ endedAt: expect.any(Number),
624
+ });
625
+
626
+ expect(endTime - startTime).toBeGreaterThan(1000);
627
+
628
+ srv.close();
629
+ });
442
630
  });
443
631
 
444
632
  describe('Variable Resolution', () => {
@@ -501,8 +689,8 @@ describe('MastraInngestWorkflow', () => {
501
689
  const run = workflow.createRun();
502
690
  const result = await run.start({ inputData: { inputData: 'test-input' } });
503
691
 
504
- expect(result.steps.step1).toEqual({ status: 'success', output: { result: 'success' } });
505
- expect(result.steps.step2).toEqual({ status: 'success', output: { result: 'success' } });
692
+ expect(result.steps.step1).toMatchObject({ status: 'success', output: { result: 'success' } });
693
+ expect(result.steps.step2).toMatchObject({ status: 'success', output: { result: 'success' } });
506
694
 
507
695
  srv.close();
508
696
  });
@@ -517,14 +705,14 @@ describe('MastraInngestWorkflow', () => {
517
705
 
518
706
  const step1Action = vi.fn().mockImplementation(async ({ inputData }) => {
519
707
  // Test accessing trigger data with correct type
520
- expect(inputData).toEqual({ inputValue: 'test-input' });
708
+ expect(inputData).toMatchObject({ inputValue: 'test-input' });
521
709
  return { value: 'step1-result' };
522
710
  });
523
711
 
524
712
  const step2Action = vi.fn().mockImplementation(async ({ getStepResult }) => {
525
713
  // Test accessing previous step result with type
526
714
  const step1Result = getStepResult(step1);
527
- expect(step1Result).toEqual({ value: 'step1-result' });
715
+ expect(step1Result).toMatchObject({ value: 'step1-result' });
528
716
 
529
717
  const failedStep = getStepResult(nonExecutedStep);
530
718
  expect(failedStep).toBe(null);
@@ -590,7 +778,7 @@ describe('MastraInngestWorkflow', () => {
590
778
 
591
779
  expect(step1Action).toHaveBeenCalled();
592
780
  expect(step2Action).toHaveBeenCalled();
593
- expect(result.steps).toEqual({
781
+ expect(result.steps).toMatchObject({
594
782
  input: { inputValue: 'test-input' },
595
783
  step1: { status: 'success', output: { value: 'step1-result' } },
596
784
  step2: { status: 'success', output: { value: 'step2-result' } },
@@ -736,7 +924,7 @@ describe('MastraInngestWorkflow', () => {
736
924
  }),
737
925
  );
738
926
 
739
- expect(result.steps.step2).toEqual({ status: 'success', output: { result: { cool: 'test-input' } } });
927
+ expect(result.steps.step2).toMatchObject({ status: 'success', output: { result: { cool: 'test-input' } } });
740
928
 
741
929
  srv.close();
742
930
  });
@@ -919,7 +1107,7 @@ describe('MastraInngestWorkflow', () => {
919
1107
  expect(step1Action).toHaveBeenCalled();
920
1108
  expect(step2Action).toHaveBeenCalled();
921
1109
  expect(step3Action).not.toHaveBeenCalled();
922
- expect(result.steps).toEqual({
1110
+ expect(result.steps).toMatchObject({
923
1111
  input: { status: 'success' },
924
1112
  step1: { status: 'success', output: { status: 'success' } },
925
1113
  step2: { status: 'success', output: { result: 'step2' } },
@@ -1000,7 +1188,7 @@ describe('MastraInngestWorkflow', () => {
1000
1188
 
1001
1189
  expect(step1Action).toHaveBeenCalled();
1002
1190
  expect(step2Action).not.toHaveBeenCalled();
1003
- expect(result?.steps).toEqual({
1191
+ expect(result?.steps).toMatchObject({
1004
1192
  input: {},
1005
1193
  step1: { status: 'failed', error: 'Failed' },
1006
1194
  });
@@ -1181,13 +1369,12 @@ describe('MastraInngestWorkflow', () => {
1181
1369
  srv.close();
1182
1370
 
1183
1371
  expect(step2Action).toHaveBeenCalled();
1184
- expect(result.steps.step1).toEqual({
1372
+ expect(result.steps.step1).toMatchObject({
1185
1373
  status: 'success',
1186
1374
  output: { count: 5 },
1187
1375
  });
1188
- expect(result.steps.step2).toEqual({
1376
+ expect(result.steps.step2).toMatchObject({
1189
1377
  status: 'success',
1190
- output: undefined,
1191
1378
  });
1192
1379
  });
1193
1380
  });
@@ -1556,7 +1743,7 @@ describe('MastraInngestWorkflow', () => {
1556
1743
 
1557
1744
  expect(step2Action).toHaveBeenCalled();
1558
1745
  expect(step3Action).not.toHaveBeenCalled();
1559
- expect(result.steps.step2).toEqual({ status: 'success', output: { result: 'step2' } });
1746
+ expect(result.steps.step2).toMatchObject({ status: 'success', output: { result: 'step2' } });
1560
1747
 
1561
1748
  srv.close();
1562
1749
  });
@@ -1659,9 +1846,9 @@ describe('MastraInngestWorkflow', () => {
1659
1846
  expect(increment).toHaveBeenCalledTimes(12);
1660
1847
  expect(final).toHaveBeenCalledTimes(1);
1661
1848
  // @ts-ignore
1662
- expect(result.result).toEqual({ finalValue: 12 });
1849
+ expect(result.result).toMatchObject({ finalValue: 12 });
1663
1850
  // @ts-ignore
1664
- expect(result.steps.increment.output).toEqual({ value: 12 });
1851
+ expect(result.steps.increment.output).toMatchObject({ value: 12 });
1665
1852
 
1666
1853
  srv.close();
1667
1854
  });
@@ -1762,9 +1949,9 @@ describe('MastraInngestWorkflow', () => {
1762
1949
  expect(increment).toHaveBeenCalledTimes(12);
1763
1950
  expect(final).toHaveBeenCalledTimes(1);
1764
1951
  // @ts-ignore
1765
- expect(result.result).toEqual({ finalValue: 12 });
1952
+ expect(result.result).toMatchObject({ finalValue: 12 });
1766
1953
  // @ts-ignore
1767
- expect(result.steps.increment.output).toEqual({ value: 12 });
1954
+ expect(result.steps.increment.output).toMatchObject({ value: 12 });
1768
1955
 
1769
1956
  srv.close();
1770
1957
  });
@@ -1853,7 +2040,7 @@ describe('MastraInngestWorkflow', () => {
1853
2040
  expect(duration).toBeGreaterThan(1e3 * 3);
1854
2041
 
1855
2042
  expect(map).toHaveBeenCalledTimes(3);
1856
- expect(result.steps).toEqual({
2043
+ expect(result.steps).toMatchObject({
1857
2044
  input: [{ value: 1 }, { value: 22 }, { value: 333 }],
1858
2045
  map: { status: 'success', output: [{ value: 12 }, { value: 33 }, { value: 344 }] },
1859
2046
  final: { status: 'success', output: { finalValue: 1 + 11 + (22 + 11) + (333 + 11) } },
@@ -2005,9 +2192,9 @@ describe('MastraInngestWorkflow', () => {
2005
2192
  expect(other).toHaveBeenCalledTimes(0);
2006
2193
  expect(final).toHaveBeenCalledTimes(1);
2007
2194
  // @ts-ignore
2008
- expect(result.steps.finalIf.output).toEqual({ finalValue: 2 });
2195
+ expect(result.steps.finalIf.output).toMatchObject({ finalValue: 2 });
2009
2196
  // @ts-ignore
2010
- expect(result.steps.start.output).toEqual({ newValue: 2 });
2197
+ expect(result.steps.start.output).toMatchObject({ newValue: 2 });
2011
2198
 
2012
2199
  srv.close();
2013
2200
  });
@@ -2154,9 +2341,9 @@ describe('MastraInngestWorkflow', () => {
2154
2341
  expect(other).toHaveBeenCalledTimes(1);
2155
2342
  expect(final).toHaveBeenCalledTimes(1);
2156
2343
  // @ts-ignore
2157
- expect(result.steps['else-branch'].output).toEqual({ finalValue: 26 + 6 + 1 });
2344
+ expect(result.steps['else-branch'].output).toMatchObject({ finalValue: 26 + 6 + 1 });
2158
2345
  // @ts-ignore
2159
- expect(result.steps.start.output).toEqual({ newValue: 7 });
2346
+ expect(result.steps.start.output).toMatchObject({ newValue: 7 });
2160
2347
 
2161
2348
  srv.close();
2162
2349
  });
@@ -2322,8 +2509,8 @@ describe('MastraInngestWorkflow', () => {
2322
2509
  const run = workflow.createRun();
2323
2510
  const result = await run.start({ inputData: {} });
2324
2511
 
2325
- expect(result.steps['nested-a']).toEqual({ status: 'success', output: { result: 'success3' } });
2326
- expect(result.steps['nested-b']).toEqual({ status: 'success', output: { result: 'success5' } });
2512
+ expect(result.steps['nested-a']).toMatchObject({ status: 'success', output: { result: 'success3' } });
2513
+ expect(result.steps['nested-b']).toMatchObject({ status: 'success', output: { result: 'success5' } });
2327
2514
 
2328
2515
  srv.close();
2329
2516
  });
@@ -2388,8 +2575,8 @@ describe('MastraInngestWorkflow', () => {
2388
2575
  const run = workflow.createRun();
2389
2576
  const result = await run.start({ inputData: {} });
2390
2577
 
2391
- expect(result.steps.step1).toEqual({ status: 'success', output: { result: 'success' } });
2392
- expect(result.steps.step2).toEqual({ status: 'failed', error: 'Step failed' });
2578
+ expect(result.steps.step1).toMatchObject({ status: 'success', output: { result: 'success' } });
2579
+ expect(result.steps.step2).toMatchObject({ status: 'failed', error: 'Step failed' });
2393
2580
  expect(step1.execute).toHaveBeenCalledTimes(1);
2394
2581
  expect(step2.execute).toHaveBeenCalledTimes(1); // 0 retries + 1 initial call
2395
2582
 
@@ -2445,8 +2632,8 @@ describe('MastraInngestWorkflow', () => {
2445
2632
  const run = workflow.createRun();
2446
2633
  const result = await run.start({ inputData: {} });
2447
2634
 
2448
- expect(result.steps.step1).toEqual({ status: 'success', output: { result: 'success' } });
2449
- expect(result.steps.step2).toEqual({ status: 'failed', error: 'Step failed' });
2635
+ expect(result.steps.step1).toMatchObject({ status: 'success', output: { result: 'success' } });
2636
+ expect(result.steps.step2).toMatchObject({ status: 'failed', error: 'Step failed' });
2450
2637
  expect(step1.execute).toHaveBeenCalledTimes(1);
2451
2638
  expect(step2.execute).toHaveBeenCalledTimes(6); // 5 retries + 1 initial call
2452
2639
  });
@@ -2523,8 +2710,8 @@ describe('MastraInngestWorkflow', () => {
2523
2710
 
2524
2711
  expect(step1Action).toHaveBeenCalled();
2525
2712
  expect(toolAction).toHaveBeenCalled();
2526
- expect(result.steps.step1).toEqual({ status: 'success', output: { name: 'step1' } });
2527
- expect(result.steps['random-tool']).toEqual({ status: 'success', output: { name: 'step1' } });
2713
+ expect(result.steps.step1).toMatchObject({ status: 'success', output: { name: 'step1' } });
2714
+ expect(result.steps['random-tool']).toMatchObject({ status: 'success', output: { name: 'step1' } });
2528
2715
  }, 10000);
2529
2716
  });
2530
2717
 
@@ -2703,11 +2890,11 @@ describe('MastraInngestWorkflow', () => {
2703
2890
  });
2704
2891
 
2705
2892
  // Verify execution completed successfully
2706
- expect(executionResult.steps.step1).toEqual({
2893
+ expect(executionResult.steps.step1).toMatchObject({
2707
2894
  status: 'success',
2708
2895
  output: { result: 'success1' },
2709
2896
  });
2710
- expect(executionResult.steps.step2).toEqual({
2897
+ expect(executionResult.steps.step2).toMatchObject({
2711
2898
  status: 'success',
2712
2899
  output: { result: 'success2' },
2713
2900
  });
@@ -2976,7 +3163,7 @@ describe('MastraInngestWorkflow', () => {
2976
3163
  throw new Error('Resume failed to return a result');
2977
3164
  }
2978
3165
 
2979
- expect(resumeResult.steps).toEqual({
3166
+ expect(resumeResult.steps).toMatchObject({
2980
3167
  input: { input: 'test' },
2981
3168
  getUserInput: { status: 'success', output: { userInput: 'test input' } },
2982
3169
  promptAgent: { status: 'success', output: { modelOutput: 'test output' } },
@@ -3139,7 +3326,7 @@ describe('MastraInngestWorkflow', () => {
3139
3326
  throw new Error('Resume failed to return a result');
3140
3327
  }
3141
3328
 
3142
- expect(result.steps).toEqual({
3329
+ expect(result.steps).toMatchObject({
3143
3330
  input: { input: 'test' },
3144
3331
  getUserInput: { status: 'success', output: { userInput: 'test input' } },
3145
3332
  promptAgent: { status: 'success', output: { modelOutput: 'test output' } },
@@ -3499,8 +3686,8 @@ describe('MastraInngestWorkflow', () => {
3499
3686
  expect(promptAgentAction).toHaveBeenCalledTimes(1);
3500
3687
  // expect(initialResult.activePaths.size).toBe(1);
3501
3688
  // expect(initialResult.activePaths.get('promptAgent')?.status).toBe('suspended');
3502
- // expect(initialResult.activePaths.get('promptAgent')?.suspendPayload).toEqual({ testPayload: 'hello' });
3503
- expect(initialResult.steps).toEqual({
3689
+ // expect(initialResult.activePaths.get('promptAgent')?.suspendPayload).toMatchObject({ testPayload: 'hello' });
3690
+ expect(initialResult.steps).toMatchObject({
3504
3691
  input: { input: 'test' },
3505
3692
  getUserInput: { status: 'success', output: { userInput: 'test input' } },
3506
3693
  promptAgent: { status: 'suspended', payload: { testPayload: 'hello' } },
@@ -3520,7 +3707,7 @@ describe('MastraInngestWorkflow', () => {
3520
3707
 
3521
3708
  // expect(firstResumeResult.activePaths.size).toBe(1);
3522
3709
  // expect(firstResumeResult.activePaths.get('improveResponse')?.status).toBe('suspended');
3523
- expect(firstResumeResult.steps).toEqual({
3710
+ expect(firstResumeResult.steps).toMatchObject({
3524
3711
  input: { input: 'test' },
3525
3712
  getUserInput: { status: 'success', output: { userInput: 'test input' } },
3526
3713
  promptAgent: { status: 'success', output: { modelOutput: 'test output' } },
@@ -3547,7 +3734,7 @@ describe('MastraInngestWorkflow', () => {
3547
3734
 
3548
3735
  expect(promptAgentAction).toHaveBeenCalledTimes(2);
3549
3736
 
3550
- expect(secondResumeResult.steps).toEqual({
3737
+ expect(secondResumeResult.steps).toMatchObject({
3551
3738
  input: { input: 'test' },
3552
3739
  getUserInput: { status: 'success', output: { userInput: 'test input' } },
3553
3740
  promptAgent: { status: 'success', output: { modelOutput: 'test output' } },
@@ -3632,6 +3819,7 @@ describe('MastraInngestWorkflow', () => {
3632
3819
  const inngest = new Inngest({
3633
3820
  id: 'mastra',
3634
3821
  baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
3822
+ middleware: [realtimeMiddleware()],
3635
3823
  });
3636
3824
 
3637
3825
  const { createWorkflow, createStep } = init(inngest);
@@ -3724,23 +3912,24 @@ describe('MastraInngestWorkflow', () => {
3724
3912
  inputData: { prompt1: 'Capital of France, just the name', prompt2: 'Capital of UK, just the name' },
3725
3913
  });
3726
3914
 
3727
- expect(result.steps['test-agent-1']).toEqual({
3915
+ srv.close();
3916
+
3917
+ expect(result.steps['test-agent-1']).toMatchObject({
3728
3918
  status: 'success',
3729
3919
  output: { text: 'Paris' },
3730
3920
  });
3731
3921
 
3732
- expect(result.steps['test-agent-2']).toEqual({
3922
+ expect(result.steps['test-agent-2']).toMatchObject({
3733
3923
  status: 'success',
3734
3924
  output: { text: 'London' },
3735
3925
  });
3736
-
3737
- srv.close();
3738
3926
  });
3739
3927
 
3740
3928
  it('should be able to use an agent in parallel', async ctx => {
3741
3929
  const inngest = new Inngest({
3742
3930
  id: 'mastra',
3743
3931
  baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
3932
+ middleware: [realtimeMiddleware()],
3744
3933
  });
3745
3934
 
3746
3935
  const { createWorkflow, createStep } = init(inngest);
@@ -3861,17 +4050,17 @@ describe('MastraInngestWorkflow', () => {
3861
4050
  });
3862
4051
 
3863
4052
  expect(execute).toHaveBeenCalledTimes(1);
3864
- expect(result.steps['finalStep']).toEqual({
4053
+ expect(result.steps['finalStep']).toMatchObject({
3865
4054
  status: 'success',
3866
4055
  output: { result: 'success' },
3867
4056
  });
3868
4057
 
3869
- expect(result.steps['nested-workflow']).toEqual({
4058
+ expect(result.steps['nested-workflow']).toMatchObject({
3870
4059
  status: 'success',
3871
4060
  output: { text: 'Paris' },
3872
4061
  });
3873
4062
 
3874
- expect(result.steps['nested-workflow-2']).toEqual({
4063
+ expect(result.steps['nested-workflow-2']).toMatchObject({
3875
4064
  status: 'success',
3876
4065
  output: { text: 'London' },
3877
4066
  });
@@ -4008,16 +4197,16 @@ describe('MastraInngestWorkflow', () => {
4008
4197
  expect(final).toHaveBeenCalledTimes(2);
4009
4198
  expect(last).toHaveBeenCalledTimes(1);
4010
4199
  // @ts-ignore
4011
- expect(result.steps['nested-workflow-a'].output).toEqual({
4200
+ expect(result.steps['nested-workflow-a'].output).toMatchObject({
4012
4201
  finalValue: 26 + 1,
4013
4202
  });
4014
4203
 
4015
4204
  // @ts-ignore
4016
- expect(result.steps['nested-workflow-b'].output).toEqual({
4205
+ expect(result.steps['nested-workflow-b'].output).toMatchObject({
4017
4206
  finalValue: 1,
4018
4207
  });
4019
4208
 
4020
- expect(result.steps['last-step']).toEqual({
4209
+ expect(result.steps['last-step']).toMatchObject({
4021
4210
  output: { success: true },
4022
4211
  status: 'success',
4023
4212
  });
@@ -4160,16 +4349,16 @@ describe('MastraInngestWorkflow', () => {
4160
4349
  expect(final).toHaveBeenCalledTimes(2);
4161
4350
  expect(last).toHaveBeenCalledTimes(1);
4162
4351
  // @ts-ignore
4163
- expect(result.steps['nested-workflow-a'].output).toEqual({
4352
+ expect(result.steps['nested-workflow-a'].output).toMatchObject({
4164
4353
  finalValue: 26 + 1,
4165
4354
  });
4166
4355
 
4167
4356
  // @ts-ignore
4168
- expect(result.steps['nested-workflow-b'].output).toEqual({
4357
+ expect(result.steps['nested-workflow-b'].output).toMatchObject({
4169
4358
  finalValue: 1,
4170
4359
  });
4171
4360
 
4172
- expect(result.steps['last-step']).toEqual({
4361
+ expect(result.steps['last-step']).toMatchObject({
4173
4362
  output: { success: true },
4174
4363
  status: 'success',
4175
4364
  });
@@ -4320,16 +4509,16 @@ describe('MastraInngestWorkflow', () => {
4320
4509
  expect(first).toHaveBeenCalledTimes(1);
4321
4510
  expect(last).toHaveBeenCalledTimes(1);
4322
4511
  // @ts-ignore
4323
- expect(result.steps['nested-workflow-a'].output).toEqual({
4512
+ expect(result.steps['nested-workflow-a'].output).toMatchObject({
4324
4513
  finalValue: 26 + 1,
4325
4514
  });
4326
4515
 
4327
- expect(result.steps['first-step']).toEqual({
4516
+ expect(result.steps['first-step']).toMatchObject({
4328
4517
  output: { success: true },
4329
4518
  status: 'success',
4330
4519
  });
4331
4520
 
4332
- expect(result.steps['last-step']).toEqual({
4521
+ expect(result.steps['last-step']).toMatchObject({
4333
4522
  output: { success: true },
4334
4523
  status: 'success',
4335
4524
  });
@@ -4480,16 +4669,16 @@ describe('MastraInngestWorkflow', () => {
4480
4669
  expect(last).toHaveBeenCalledTimes(1);
4481
4670
 
4482
4671
  // @ts-ignore
4483
- expect(result.steps['nested-workflow-b'].output).toEqual({
4672
+ expect(result.steps['nested-workflow-b'].output).toMatchObject({
4484
4673
  finalValue: 1,
4485
4674
  });
4486
4675
 
4487
- expect(result.steps['first-step']).toEqual({
4676
+ expect(result.steps['first-step']).toMatchObject({
4488
4677
  output: { success: true },
4489
4678
  status: 'success',
4490
4679
  });
4491
4680
 
4492
- expect(result.steps['last-step']).toEqual({
4681
+ expect(result.steps['last-step']).toMatchObject({
4493
4682
  output: { success: true },
4494
4683
  status: 'success',
4495
4684
  });
@@ -4677,16 +4866,16 @@ describe('MastraInngestWorkflow', () => {
4677
4866
  // expect(last).toHaveBeenCalledTimes(1);
4678
4867
 
4679
4868
  // @ts-ignore
4680
- expect(result.steps['nested-workflow-b'].output).toEqual({
4869
+ expect(result.steps['nested-workflow-b'].output).toMatchObject({
4681
4870
  finalValue: 1,
4682
4871
  });
4683
4872
 
4684
- expect(result.steps['first-step']).toEqual({
4873
+ expect(result.steps['first-step']).toMatchObject({
4685
4874
  output: { success: true },
4686
4875
  status: 'success',
4687
4876
  });
4688
4877
 
4689
- expect(result.steps['last-step']).toEqual({
4878
+ expect(result.steps['last-step']).toMatchObject({
4690
4879
  output: { success: true },
4691
4880
  status: 'success',
4692
4881
  });
@@ -4832,12 +5021,12 @@ describe('MastraInngestWorkflow', () => {
4832
5021
  });
4833
5022
 
4834
5023
  // @ts-ignore
4835
- expect(result.steps['last-step']).toEqual(undefined);
5024
+ expect(result.steps['last-step']).toMatchObject(undefined);
4836
5025
 
4837
5026
  const resumedResults = await run.resume({ step: [wfA, otherStep], resumeData: { newValue: 0 } });
4838
5027
 
4839
5028
  // @ts-ignore
4840
- expect(resumedResults.steps['nested-workflow-a'].output).toEqual({
5029
+ expect(resumedResults.steps['nested-workflow-a'].output).toMatchObject({
4841
5030
  finalValue: 26 + 1,
4842
5031
  });
4843
5032
 
@@ -4988,7 +5177,7 @@ describe('MastraInngestWorkflow', () => {
4988
5177
  },
4989
5178
  });
4990
5179
 
4991
- expect(result.steps['last-step']).toEqual({
5180
+ expect(result.steps['last-step']).toMatchObject({
4992
5181
  status: 'success',
4993
5182
  output: { success: true },
4994
5183
  });
@@ -5165,18 +5354,23 @@ describe('MastraInngestWorkflow', () => {
5165
5354
  });
5166
5355
 
5167
5356
  // @ts-ignore
5168
- expect(result.steps['last-step']).toEqual(undefined);
5357
+ expect(result.steps['last-step']).toMatchObject(undefined);
5169
5358
 
5170
5359
  if (result.status !== 'suspended') {
5171
5360
  expect.fail('Workflow should be suspended');
5172
5361
  }
5173
- expect(result.suspended[0]).toEqual(['nested-workflow-c', 'nested-workflow-b', 'nested-workflow-a', 'other']);
5362
+ expect(result.suspended[0]).toMatchObject([
5363
+ 'nested-workflow-c',
5364
+ 'nested-workflow-b',
5365
+ 'nested-workflow-a',
5366
+ 'other',
5367
+ ]);
5174
5368
  const resumedResults = await run.resume({ step: result.suspended[0], resumeData: { newValue: 0 } });
5175
5369
 
5176
5370
  srv.close();
5177
5371
 
5178
5372
  // @ts-ignore
5179
- expect(resumedResults.steps['nested-workflow-c'].output).toEqual({
5373
+ expect(resumedResults.steps['nested-workflow-c'].output).toMatchObject({
5180
5374
  finalValue: 26 + 1,
5181
5375
  });
5182
5376
 
@@ -5318,16 +5512,16 @@ describe('MastraInngestWorkflow', () => {
5318
5512
  expect(final).toHaveBeenCalledTimes(2);
5319
5513
  expect(last).toHaveBeenCalledTimes(1);
5320
5514
  // @ts-ignore
5321
- expect(result.steps['nested-workflow-a-clone'].output).toEqual({
5515
+ expect(result.steps['nested-workflow-a-clone'].output).toMatchObject({
5322
5516
  finalValue: 26 + 1,
5323
5517
  });
5324
5518
 
5325
5519
  // @ts-ignore
5326
- expect(result.steps['nested-workflow-b'].output).toEqual({
5520
+ expect(result.steps['nested-workflow-b'].output).toMatchObject({
5327
5521
  finalValue: 1,
5328
5522
  });
5329
5523
 
5330
- expect(result.steps['last-step']).toEqual({
5524
+ expect(result.steps['last-step']).toMatchObject({
5331
5525
  output: { success: true },
5332
5526
  status: 'success',
5333
5527
  });
@@ -5517,5 +5711,1049 @@ describe('MastraInngestWorkflow', () => {
5517
5711
  });
5518
5712
  });
5519
5713
 
5520
- describe('Access to inngest step primitives', () => {});
5714
+ describe('Access to inngest step primitives', () => {
5715
+ it('should inject inngest step primitives into steps during run', async ctx => {
5716
+ const inngest = new Inngest({
5717
+ id: 'mastra',
5718
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
5719
+ });
5720
+
5721
+ const { createWorkflow, createStep } = init(inngest);
5722
+
5723
+ const step = createStep({
5724
+ id: 'step1',
5725
+ execute: async ({ engine }) => {
5726
+ return {
5727
+ hasEngine: !!engine.step,
5728
+ };
5729
+ },
5730
+ inputSchema: z.object({}),
5731
+ outputSchema: z.object({}),
5732
+ });
5733
+ const workflow = createWorkflow({
5734
+ id: 'test-workflow',
5735
+ inputSchema: z.object({}),
5736
+ outputSchema: z.object({
5737
+ hasEngine: z.boolean(),
5738
+ }),
5739
+ });
5740
+ workflow.then(step).commit();
5741
+
5742
+ const mastra = new Mastra({
5743
+ storage: new DefaultStorage({
5744
+ url: ':memory:',
5745
+ }),
5746
+ workflows: {
5747
+ 'test-workflow': workflow,
5748
+ },
5749
+ server: {
5750
+ apiRoutes: [
5751
+ {
5752
+ path: '/inngest/api',
5753
+ method: 'ALL',
5754
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
5755
+ },
5756
+ ],
5757
+ },
5758
+ });
5759
+
5760
+ const app = await createHonoServer(mastra);
5761
+
5762
+ const srv = serve({
5763
+ fetch: app.fetch,
5764
+ port: (ctx as any).handlerPort,
5765
+ });
5766
+
5767
+ const run = workflow.createRun();
5768
+ const result = await run.start({});
5769
+
5770
+ srv.close();
5771
+
5772
+ // @ts-ignore
5773
+ expect(result?.steps.step1.output.hasEngine).toBe(true);
5774
+ });
5775
+ });
5776
+
5777
+ describe('Streaming', () => {
5778
+ it('should generate a stream', async ctx => {
5779
+ const inngest = new Inngest({
5780
+ id: 'mastra',
5781
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
5782
+ middleware: [realtimeMiddleware()],
5783
+ });
5784
+
5785
+ const { createWorkflow, createStep } = init(inngest);
5786
+
5787
+ const step1Action = vi.fn<any>().mockResolvedValue({ result: 'success1' });
5788
+ const step2Action = vi.fn<any>().mockResolvedValue({ result: 'success2' });
5789
+
5790
+ const step1 = createStep({
5791
+ id: 'step1',
5792
+ execute: step1Action,
5793
+ inputSchema: z.object({}),
5794
+ outputSchema: z.object({ value: z.string() }),
5795
+ });
5796
+ const step2 = createStep({
5797
+ id: 'step2',
5798
+ execute: step2Action,
5799
+ inputSchema: z.object({ value: z.string() }),
5800
+ outputSchema: z.object({}),
5801
+ });
5802
+
5803
+ const workflow = createWorkflow({
5804
+ id: 'test-workflow',
5805
+ inputSchema: z.object({}),
5806
+ outputSchema: z.object({}),
5807
+ steps: [step1, step2],
5808
+ });
5809
+ workflow.then(step1).then(step2).commit();
5810
+
5811
+ const mastra = new Mastra({
5812
+ storage: new DefaultStorage({
5813
+ url: ':memory:',
5814
+ }),
5815
+ workflows: {
5816
+ 'test-workflow': workflow,
5817
+ },
5818
+ server: {
5819
+ apiRoutes: [
5820
+ {
5821
+ path: '/inngest/api',
5822
+ method: 'ALL',
5823
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
5824
+ },
5825
+ ],
5826
+ },
5827
+ });
5828
+
5829
+ const app = await createHonoServer(mastra);
5830
+
5831
+ const srv = serve({
5832
+ fetch: app.fetch,
5833
+ port: (ctx as any).handlerPort,
5834
+ });
5835
+
5836
+ const runId = 'test-run-id';
5837
+ let watchData: StreamEvent[] = [];
5838
+ const run = workflow.createRun({
5839
+ runId,
5840
+ });
5841
+
5842
+ await new Promise(resolve => setTimeout(resolve, 1000));
5843
+
5844
+ const { stream, getWorkflowState } = run.stream({ inputData: {} });
5845
+
5846
+ // Start watching the workflow
5847
+ const collectedStreamData: StreamEvent[] = [];
5848
+ for await (const data of stream) {
5849
+ collectedStreamData.push(JSON.parse(JSON.stringify(data)));
5850
+ }
5851
+ watchData = collectedStreamData;
5852
+
5853
+ const executionResult = await getWorkflowState();
5854
+
5855
+ await new Promise(resolve => setTimeout(resolve, 1000));
5856
+
5857
+ srv.close();
5858
+
5859
+ expect(watchData.length).toBe(8);
5860
+ expect(watchData).toMatchInlineSnapshot(`
5861
+ [
5862
+ {
5863
+ "payload": {
5864
+ "runId": "test-run-id",
5865
+ },
5866
+ "type": "start",
5867
+ },
5868
+ {
5869
+ "payload": {
5870
+ "id": "step1",
5871
+ },
5872
+ "type": "step-start",
5873
+ },
5874
+ {
5875
+ "payload": {
5876
+ "id": "step1",
5877
+ "output": {
5878
+ "result": "success1",
5879
+ },
5880
+ "status": "success",
5881
+ },
5882
+ "type": "step-result",
5883
+ },
5884
+ {
5885
+ "payload": {
5886
+ "id": "step1",
5887
+ "metadata": {},
5888
+ },
5889
+ "type": "step-finish",
5890
+ },
5891
+ {
5892
+ "payload": {
5893
+ "id": "step2",
5894
+ },
5895
+ "type": "step-start",
5896
+ },
5897
+ {
5898
+ "payload": {
5899
+ "id": "step2",
5900
+ "output": {
5901
+ "result": "success2",
5902
+ },
5903
+ "status": "success",
5904
+ },
5905
+ "type": "step-result",
5906
+ },
5907
+ {
5908
+ "payload": {
5909
+ "id": "step2",
5910
+ "metadata": {},
5911
+ },
5912
+ "type": "step-finish",
5913
+ },
5914
+ {
5915
+ "payload": {
5916
+ "runId": "test-run-id",
5917
+ },
5918
+ "type": "finish",
5919
+ },
5920
+ ]
5921
+ `);
5922
+ // Verify execution completed successfully
5923
+ expect(executionResult.steps.step1).toMatchObject({
5924
+ status: 'success',
5925
+ output: { result: 'success1' },
5926
+ payload: {},
5927
+ startedAt: expect.any(Number),
5928
+ endedAt: expect.any(Number),
5929
+ });
5930
+ expect(executionResult.steps.step2).toMatchObject({
5931
+ status: 'success',
5932
+ output: { result: 'success2' },
5933
+ payload: {
5934
+ result: 'success1',
5935
+ },
5936
+ startedAt: expect.any(Number),
5937
+ endedAt: expect.any(Number),
5938
+ });
5939
+ });
5940
+
5941
+ it('should handle basic sleep waiting flow', async ctx => {
5942
+ const inngest = new Inngest({
5943
+ id: 'mastra',
5944
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
5945
+ middleware: [realtimeMiddleware()],
5946
+ });
5947
+
5948
+ const { createWorkflow, createStep } = init(inngest);
5949
+
5950
+ const step1Action = vi.fn<any>().mockResolvedValue({ result: 'success1' });
5951
+ const step2Action = vi.fn<any>().mockResolvedValue({ result: 'success2' });
5952
+
5953
+ const step1 = createStep({
5954
+ id: 'step1',
5955
+ execute: step1Action,
5956
+ inputSchema: z.object({}),
5957
+ outputSchema: z.object({ value: z.string() }),
5958
+ });
5959
+ const step2 = createStep({
5960
+ id: 'step2',
5961
+ execute: step2Action,
5962
+ inputSchema: z.object({ value: z.string() }),
5963
+ outputSchema: z.object({}),
5964
+ });
5965
+
5966
+ const workflow = createWorkflow({
5967
+ id: 'test-workflow',
5968
+ inputSchema: z.object({}),
5969
+ outputSchema: z.object({}),
5970
+ steps: [step1, step2],
5971
+ });
5972
+ workflow.then(step1).sleep(1000).then(step2).commit();
5973
+
5974
+ const mastra = new Mastra({
5975
+ storage: new DefaultStorage({
5976
+ url: ':memory:',
5977
+ }),
5978
+ workflows: {
5979
+ 'test-workflow': workflow,
5980
+ },
5981
+ server: {
5982
+ apiRoutes: [
5983
+ {
5984
+ path: '/inngest/api',
5985
+ method: 'ALL',
5986
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
5987
+ },
5988
+ ],
5989
+ },
5990
+ });
5991
+
5992
+ const app = await createHonoServer(mastra);
5993
+
5994
+ const srv = serve({
5995
+ fetch: app.fetch,
5996
+ port: (ctx as any).handlerPort,
5997
+ });
5998
+
5999
+ const runId = 'test-run-id';
6000
+ let watchData: StreamEvent[] = [];
6001
+ const run = workflow.createRun({
6002
+ runId,
6003
+ });
6004
+
6005
+ await new Promise(resolve => setTimeout(resolve, 1000));
6006
+
6007
+ const { stream, getWorkflowState } = run.stream({ inputData: {} });
6008
+
6009
+ // Start watching the workflow
6010
+ const collectedStreamData: StreamEvent[] = [];
6011
+ for await (const data of stream) {
6012
+ collectedStreamData.push(JSON.parse(JSON.stringify(data)));
6013
+ }
6014
+ watchData = collectedStreamData;
6015
+
6016
+ const executionResult = await getWorkflowState();
6017
+
6018
+ await new Promise(resolve => setTimeout(resolve, 1000));
6019
+
6020
+ srv.close();
6021
+
6022
+ expect(watchData.length).toBe(9);
6023
+ expect(watchData).toMatchObject([
6024
+ {
6025
+ payload: {
6026
+ runId: 'test-run-id',
6027
+ },
6028
+ type: 'start',
6029
+ },
6030
+ {
6031
+ payload: {
6032
+ id: 'step1',
6033
+ },
6034
+ type: 'step-start',
6035
+ },
6036
+ {
6037
+ payload: {
6038
+ id: 'step1',
6039
+ output: {
6040
+ result: 'success1',
6041
+ },
6042
+ status: 'success',
6043
+ },
6044
+ type: 'step-result',
6045
+ },
6046
+ {
6047
+ payload: {
6048
+ id: 'step1',
6049
+ metadata: {},
6050
+ },
6051
+ type: 'step-finish',
6052
+ },
6053
+ {
6054
+ payload: {},
6055
+ type: 'step-waiting',
6056
+ },
6057
+ {
6058
+ payload: {
6059
+ id: 'step2',
6060
+ },
6061
+ type: 'step-start',
6062
+ },
6063
+ {
6064
+ payload: {
6065
+ id: 'step2',
6066
+ output: {
6067
+ result: 'success2',
6068
+ },
6069
+ status: 'success',
6070
+ },
6071
+ type: 'step-result',
6072
+ },
6073
+ {
6074
+ payload: {
6075
+ id: 'step2',
6076
+ metadata: {},
6077
+ },
6078
+ type: 'step-finish',
6079
+ },
6080
+ {
6081
+ payload: {
6082
+ runId: 'test-run-id',
6083
+ },
6084
+ type: 'finish',
6085
+ },
6086
+ ]);
6087
+ // Verify execution completed successfully
6088
+ expect(executionResult.steps.step1).toMatchObject({
6089
+ status: 'success',
6090
+ output: { result: 'success1' },
6091
+ payload: {},
6092
+ startedAt: expect.any(Number),
6093
+ endedAt: expect.any(Number),
6094
+ });
6095
+ expect(executionResult.steps.step2).toMatchObject({
6096
+ status: 'success',
6097
+ output: { result: 'success2' },
6098
+ payload: {
6099
+ result: 'success1',
6100
+ },
6101
+ startedAt: expect.any(Number),
6102
+ endedAt: expect.any(Number),
6103
+ });
6104
+ });
6105
+
6106
+ it('should handle waitForEvent waiting flow', async ctx => {
6107
+ const inngest = new Inngest({
6108
+ id: 'mastra',
6109
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
6110
+ middleware: [realtimeMiddleware()],
6111
+ });
6112
+
6113
+ const { createWorkflow, createStep } = init(inngest);
6114
+
6115
+ const step1Action = vi.fn<any>().mockResolvedValue({ result: 'success1' });
6116
+ const step2Action = vi.fn<any>().mockResolvedValue({ result: 'success2' });
6117
+
6118
+ const step1 = createStep({
6119
+ id: 'step1',
6120
+ execute: step1Action,
6121
+ inputSchema: z.object({}),
6122
+ outputSchema: z.object({ value: z.string() }),
6123
+ });
6124
+ const step2 = createStep({
6125
+ id: 'step2',
6126
+ execute: step2Action,
6127
+ inputSchema: z.object({ value: z.string() }),
6128
+ outputSchema: z.object({}),
6129
+ });
6130
+
6131
+ const workflow = createWorkflow({
6132
+ id: 'test-workflow',
6133
+ inputSchema: z.object({}),
6134
+ outputSchema: z.object({}),
6135
+ steps: [step1, step2],
6136
+ });
6137
+ workflow.then(step1).waitForEvent('user-event-test', step2).commit();
6138
+
6139
+ const mastra = new Mastra({
6140
+ storage: new DefaultStorage({
6141
+ url: ':memory:',
6142
+ }),
6143
+ workflows: {
6144
+ 'test-workflow': workflow,
6145
+ },
6146
+ server: {
6147
+ apiRoutes: [
6148
+ {
6149
+ path: '/inngest/api',
6150
+ method: 'ALL',
6151
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
6152
+ },
6153
+ ],
6154
+ },
6155
+ });
6156
+
6157
+ const app = await createHonoServer(mastra);
6158
+
6159
+ const srv = serve({
6160
+ fetch: app.fetch,
6161
+ port: (ctx as any).handlerPort,
6162
+ });
6163
+
6164
+ const runId = 'test-run-id';
6165
+ let watchData: StreamEvent[] = [];
6166
+ const run = workflow.createRun({
6167
+ runId,
6168
+ });
6169
+
6170
+ await new Promise(resolve => setTimeout(resolve, 1000));
6171
+
6172
+ const { stream, getWorkflowState } = run.stream({ inputData: {} });
6173
+
6174
+ setTimeout(() => {
6175
+ run.sendEvent('user-event-test', {
6176
+ value: 'eventdata',
6177
+ });
6178
+ }, 3000);
6179
+
6180
+ // Start watching the workflow
6181
+ const collectedStreamData: StreamEvent[] = [];
6182
+ for await (const data of stream) {
6183
+ collectedStreamData.push(JSON.parse(JSON.stringify(data)));
6184
+ }
6185
+ watchData = collectedStreamData;
6186
+ console.dir({ watchData }, { depth: null });
6187
+
6188
+ const executionResult = await getWorkflowState();
6189
+
6190
+ await new Promise(resolve => setTimeout(resolve, 1000));
6191
+
6192
+ srv.close();
6193
+
6194
+ expect(watchData.length).toBe(9);
6195
+ expect(watchData).toMatchObject([
6196
+ {
6197
+ payload: {
6198
+ runId: 'test-run-id',
6199
+ },
6200
+ type: 'start',
6201
+ },
6202
+ {
6203
+ payload: {
6204
+ id: 'step1',
6205
+ },
6206
+ type: 'step-start',
6207
+ },
6208
+ {
6209
+ payload: {
6210
+ id: 'step1',
6211
+ output: {
6212
+ result: 'success1',
6213
+ },
6214
+ status: 'success',
6215
+ },
6216
+ type: 'step-result',
6217
+ },
6218
+ {
6219
+ payload: {
6220
+ id: 'step1',
6221
+ metadata: {},
6222
+ },
6223
+ type: 'step-finish',
6224
+ },
6225
+ {
6226
+ payload: {
6227
+ id: 'step2',
6228
+ },
6229
+ type: 'step-waiting',
6230
+ },
6231
+ {
6232
+ payload: {
6233
+ id: 'step2',
6234
+ },
6235
+ type: 'step-start',
6236
+ },
6237
+ {
6238
+ payload: {
6239
+ id: 'step2',
6240
+ output: {
6241
+ result: 'success2',
6242
+ },
6243
+ status: 'success',
6244
+ },
6245
+ type: 'step-result',
6246
+ },
6247
+ {
6248
+ payload: {
6249
+ id: 'step2',
6250
+ metadata: {},
6251
+ },
6252
+ type: 'step-finish',
6253
+ },
6254
+ {
6255
+ payload: {
6256
+ runId: 'test-run-id',
6257
+ },
6258
+ type: 'finish',
6259
+ },
6260
+ ]);
6261
+ // Verify execution completed successfully
6262
+ expect(executionResult.steps.step1).toMatchObject({
6263
+ status: 'success',
6264
+ output: { result: 'success1' },
6265
+ payload: {},
6266
+ startedAt: expect.any(Number),
6267
+ endedAt: expect.any(Number),
6268
+ });
6269
+ expect(executionResult.steps.step2).toMatchObject({
6270
+ status: 'success',
6271
+ output: { result: 'success2' },
6272
+ payload: {
6273
+ result: 'success1',
6274
+ },
6275
+ resumePayload: {
6276
+ value: 'eventdata',
6277
+ },
6278
+ startedAt: expect.any(Number),
6279
+ resumedAt: expect.any(Number),
6280
+ endedAt: expect.any(Number),
6281
+ });
6282
+ });
6283
+
6284
+ it('should handle basic suspend and resume flow', async ctx => {
6285
+ const inngest = new Inngest({
6286
+ id: 'mastra',
6287
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
6288
+ middleware: [realtimeMiddleware()],
6289
+ });
6290
+
6291
+ const { createWorkflow, createStep } = init(inngest);
6292
+
6293
+ const getUserInputAction = vi.fn().mockResolvedValue({ userInput: 'test input' });
6294
+ const promptAgentAction = vi
6295
+ .fn()
6296
+ .mockImplementationOnce(async ({ suspend }) => {
6297
+ console.log('suspend');
6298
+ await suspend();
6299
+ return undefined;
6300
+ })
6301
+ .mockImplementationOnce(() => ({ modelOutput: 'test output' }));
6302
+ const evaluateToneAction = vi.fn().mockResolvedValue({
6303
+ toneScore: { score: 0.8 },
6304
+ completenessScore: { score: 0.7 },
6305
+ });
6306
+ const improveResponseAction = vi.fn().mockResolvedValue({ improvedOutput: 'improved output' });
6307
+ const evaluateImprovedAction = vi.fn().mockResolvedValue({
6308
+ toneScore: { score: 0.9 },
6309
+ completenessScore: { score: 0.8 },
6310
+ });
6311
+
6312
+ const getUserInput = createStep({
6313
+ id: 'getUserInput',
6314
+ execute: getUserInputAction,
6315
+ inputSchema: z.object({ input: z.string() }),
6316
+ outputSchema: z.object({ userInput: z.string() }),
6317
+ });
6318
+ const promptAgent = createStep({
6319
+ id: 'promptAgent',
6320
+ execute: promptAgentAction,
6321
+ inputSchema: z.object({ userInput: z.string() }),
6322
+ outputSchema: z.object({ modelOutput: z.string() }),
6323
+ });
6324
+ const evaluateTone = createStep({
6325
+ id: 'evaluateToneConsistency',
6326
+ execute: evaluateToneAction,
6327
+ inputSchema: z.object({ modelOutput: z.string() }),
6328
+ outputSchema: z.object({
6329
+ toneScore: z.any(),
6330
+ completenessScore: z.any(),
6331
+ }),
6332
+ });
6333
+ const improveResponse = createStep({
6334
+ id: 'improveResponse',
6335
+ execute: improveResponseAction,
6336
+ inputSchema: z.object({ toneScore: z.any(), completenessScore: z.any() }),
6337
+ outputSchema: z.object({ improvedOutput: z.string() }),
6338
+ });
6339
+ const evaluateImproved = createStep({
6340
+ id: 'evaluateImprovedResponse',
6341
+ execute: evaluateImprovedAction,
6342
+ inputSchema: z.object({ improvedOutput: z.string() }),
6343
+ outputSchema: z.object({
6344
+ toneScore: z.any(),
6345
+ completenessScore: z.any(),
6346
+ }),
6347
+ });
6348
+
6349
+ const promptEvalWorkflow = createWorkflow({
6350
+ id: 'test-workflow',
6351
+ inputSchema: z.object({ input: z.string() }),
6352
+ outputSchema: z.object({}),
6353
+ steps: [getUserInput, promptAgent, evaluateTone, improveResponse, evaluateImproved],
6354
+ });
6355
+
6356
+ promptEvalWorkflow
6357
+ .then(getUserInput)
6358
+ .then(promptAgent)
6359
+ .then(evaluateTone)
6360
+ .then(improveResponse)
6361
+ .then(evaluateImproved)
6362
+ .commit();
6363
+
6364
+ const mastra = new Mastra({
6365
+ storage: new DefaultStorage({
6366
+ url: ':memory:',
6367
+ }),
6368
+ workflows: {
6369
+ 'test-workflow': promptEvalWorkflow,
6370
+ },
6371
+ server: {
6372
+ apiRoutes: [
6373
+ {
6374
+ path: '/inngest/api',
6375
+ method: 'ALL',
6376
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
6377
+ },
6378
+ ],
6379
+ },
6380
+ });
6381
+
6382
+ const app = await createHonoServer(mastra);
6383
+
6384
+ const srv = serve({
6385
+ fetch: app.fetch,
6386
+ port: (ctx as any).handlerPort,
6387
+ });
6388
+
6389
+ await new Promise(resolve => setTimeout(resolve, 1000));
6390
+
6391
+ const run = promptEvalWorkflow.createRun();
6392
+
6393
+ const { stream, getWorkflowState } = run.stream({ inputData: { input: 'test' } });
6394
+
6395
+ for await (const data of stream) {
6396
+ if (data.type === 'step-suspended') {
6397
+ expect(promptAgentAction).toHaveBeenCalledTimes(1);
6398
+
6399
+ // make it async to show that execution is not blocked
6400
+ setImmediate(() => {
6401
+ const resumeData = { stepId: 'promptAgent', context: { userInput: 'test input for resumption' } };
6402
+ run.resume({ resumeData: resumeData as any, step: promptAgent });
6403
+ });
6404
+ expect(evaluateToneAction).not.toHaveBeenCalledTimes(1);
6405
+ }
6406
+ }
6407
+
6408
+ await new Promise(resolve => setTimeout(resolve, 5000));
6409
+ const resumeResult = await getWorkflowState();
6410
+
6411
+ srv.close();
6412
+
6413
+ expect(evaluateToneAction).toHaveBeenCalledTimes(1);
6414
+ expect(resumeResult.steps).toMatchObject({
6415
+ input: { input: 'test' },
6416
+ getUserInput: {
6417
+ status: 'success',
6418
+ output: { userInput: 'test input' },
6419
+ payload: { input: 'test' },
6420
+ startedAt: expect.any(Number),
6421
+ endedAt: expect.any(Number),
6422
+ },
6423
+ promptAgent: {
6424
+ status: 'success',
6425
+ output: { modelOutput: 'test output' },
6426
+ payload: { userInput: 'test input' },
6427
+ startedAt: expect.any(Number),
6428
+ endedAt: expect.any(Number),
6429
+ resumePayload: { stepId: 'promptAgent', context: { userInput: 'test input for resumption' } },
6430
+ resumedAt: expect.any(Number),
6431
+ // suspendedAt: expect.any(Number),
6432
+ },
6433
+ evaluateToneConsistency: {
6434
+ status: 'success',
6435
+ output: { toneScore: { score: 0.8 }, completenessScore: { score: 0.7 } },
6436
+ payload: { modelOutput: 'test output' },
6437
+ startedAt: expect.any(Number),
6438
+ endedAt: expect.any(Number),
6439
+ },
6440
+ improveResponse: {
6441
+ status: 'success',
6442
+ output: { improvedOutput: 'improved output' },
6443
+ payload: { toneScore: { score: 0.8 }, completenessScore: { score: 0.7 } },
6444
+ startedAt: expect.any(Number),
6445
+ endedAt: expect.any(Number),
6446
+ },
6447
+ evaluateImprovedResponse: {
6448
+ status: 'success',
6449
+ output: { toneScore: { score: 0.9 }, completenessScore: { score: 0.8 } },
6450
+ payload: { improvedOutput: 'improved output' },
6451
+ startedAt: expect.any(Number),
6452
+ endedAt: expect.any(Number),
6453
+ },
6454
+ });
6455
+ });
6456
+
6457
+ it('should be able to use an agent as a step', async ctx => {
6458
+ const inngest = new Inngest({
6459
+ id: 'mastra',
6460
+ baseUrl: `http://localhost:${(ctx as any).inngestPort}`,
6461
+ middleware: [realtimeMiddleware()],
6462
+ });
6463
+
6464
+ const { createWorkflow, createStep } = init(inngest);
6465
+
6466
+ const workflow = createWorkflow({
6467
+ id: 'test-workflow',
6468
+ inputSchema: z.object({
6469
+ prompt1: z.string(),
6470
+ prompt2: z.string(),
6471
+ }),
6472
+ outputSchema: z.object({}),
6473
+ });
6474
+
6475
+ const agent = new Agent({
6476
+ name: 'test-agent-1',
6477
+ instructions: 'test agent instructions"',
6478
+ model: new MockLanguageModelV1({
6479
+ doStream: async () => ({
6480
+ stream: simulateReadableStream({
6481
+ chunks: [
6482
+ { type: 'text-delta', textDelta: 'Paris' },
6483
+ {
6484
+ type: 'finish',
6485
+ finishReason: 'stop',
6486
+ logprobs: undefined,
6487
+ usage: { completionTokens: 10, promptTokens: 3 },
6488
+ },
6489
+ ],
6490
+ }),
6491
+ rawCall: { rawPrompt: null, rawSettings: {} },
6492
+ }),
6493
+ }),
6494
+ });
6495
+
6496
+ const agent2 = new Agent({
6497
+ name: 'test-agent-2',
6498
+ instructions: 'test agent instructions',
6499
+ model: new MockLanguageModelV1({
6500
+ doStream: async () => ({
6501
+ stream: simulateReadableStream({
6502
+ chunks: [
6503
+ { type: 'text-delta', textDelta: 'London' },
6504
+ {
6505
+ type: 'finish',
6506
+ finishReason: 'stop',
6507
+ logprobs: undefined,
6508
+ usage: { completionTokens: 10, promptTokens: 3 },
6509
+ },
6510
+ ],
6511
+ }),
6512
+ rawCall: { rawPrompt: null, rawSettings: {} },
6513
+ }),
6514
+ }),
6515
+ });
6516
+
6517
+ const startStep = createStep({
6518
+ id: 'start',
6519
+ inputSchema: z.object({
6520
+ prompt1: z.string(),
6521
+ prompt2: z.string(),
6522
+ }),
6523
+ outputSchema: z.object({ prompt1: z.string(), prompt2: z.string() }),
6524
+ execute: async ({ inputData }) => {
6525
+ return {
6526
+ prompt1: inputData.prompt1,
6527
+ prompt2: inputData.prompt2,
6528
+ };
6529
+ },
6530
+ });
6531
+
6532
+ const agentStep1 = createStep(agent);
6533
+ const agentStep2 = createStep(agent2);
6534
+
6535
+ workflow
6536
+ .then(startStep)
6537
+ .map({
6538
+ prompt: {
6539
+ step: startStep,
6540
+ path: 'prompt1',
6541
+ },
6542
+ })
6543
+ .then(agentStep1)
6544
+ .map({
6545
+ prompt: {
6546
+ step: startStep,
6547
+ path: 'prompt2',
6548
+ },
6549
+ })
6550
+ .then(agentStep2)
6551
+ .commit();
6552
+
6553
+ const mastra = new Mastra({
6554
+ storage: new DefaultStorage({
6555
+ url: ':memory:',
6556
+ }),
6557
+ workflows: {
6558
+ 'test-workflow': workflow,
6559
+ },
6560
+ server: {
6561
+ apiRoutes: [
6562
+ {
6563
+ path: '/inngest/api',
6564
+ method: 'ALL',
6565
+ createHandler: async ({ mastra }) => inngestServe({ mastra, inngest }),
6566
+ },
6567
+ ],
6568
+ },
6569
+ });
6570
+
6571
+ const app = await createHonoServer(mastra);
6572
+
6573
+ const srv = serve({
6574
+ fetch: app.fetch,
6575
+ port: (ctx as any).handlerPort,
6576
+ });
6577
+
6578
+ await new Promise(resolve => setTimeout(resolve, 1000));
6579
+
6580
+ const run = workflow.createRun({
6581
+ runId: 'test-run-id',
6582
+ });
6583
+ const { stream } = await run.stream({
6584
+ inputData: {
6585
+ prompt1: 'Capital of France, just the name',
6586
+ prompt2: 'Capital of UK, just the name',
6587
+ },
6588
+ });
6589
+
6590
+ const values: StreamEvent[] = [];
6591
+ for await (const value of stream.values()) {
6592
+ values.push(value);
6593
+ }
6594
+
6595
+ srv.close();
6596
+
6597
+ expect(values).toMatchObject([
6598
+ {
6599
+ payload: {
6600
+ runId: 'test-run-id',
6601
+ },
6602
+ type: 'start',
6603
+ },
6604
+ {
6605
+ payload: {
6606
+ id: 'start',
6607
+ },
6608
+ type: 'step-start',
6609
+ },
6610
+ {
6611
+ payload: {
6612
+ id: 'start',
6613
+ output: {
6614
+ prompt1: 'Capital of France, just the name',
6615
+ prompt2: 'Capital of UK, just the name',
6616
+ },
6617
+ status: 'success',
6618
+ },
6619
+ type: 'step-result',
6620
+ },
6621
+ {
6622
+ payload: {
6623
+ id: 'start',
6624
+ metadata: {},
6625
+ },
6626
+ type: 'step-finish',
6627
+ },
6628
+ {
6629
+ payload: {
6630
+ id: expect.any(String),
6631
+ },
6632
+ type: 'step-start',
6633
+ },
6634
+ {
6635
+ payload: {
6636
+ id: expect.any(String),
6637
+ output: {
6638
+ prompt: 'Capital of France, just the name',
6639
+ },
6640
+ status: 'success',
6641
+ },
6642
+ type: 'step-result',
6643
+ },
6644
+ {
6645
+ payload: {
6646
+ id: expect.any(String),
6647
+ metadata: {},
6648
+ },
6649
+ type: 'step-finish',
6650
+ },
6651
+ {
6652
+ payload: {
6653
+ id: 'test-agent-1',
6654
+ },
6655
+ type: 'step-start',
6656
+ },
6657
+ {
6658
+ args: {
6659
+ prompt: 'Capital of France, just the name',
6660
+ },
6661
+ name: 'test-agent-1',
6662
+ type: 'tool-call-streaming-start',
6663
+ },
6664
+ {
6665
+ args: {
6666
+ prompt: 'Capital of France, just the name',
6667
+ },
6668
+ argsTextDelta: 'Paris',
6669
+ name: 'test-agent-1',
6670
+ type: 'tool-call-delta',
6671
+ },
6672
+ {
6673
+ payload: {
6674
+ id: 'test-agent-1',
6675
+ output: {
6676
+ text: 'Paris',
6677
+ },
6678
+ status: 'success',
6679
+ },
6680
+ type: 'step-result',
6681
+ },
6682
+ {
6683
+ payload: {
6684
+ id: expect.any(String),
6685
+ metadata: {},
6686
+ },
6687
+ type: 'step-finish',
6688
+ },
6689
+ {
6690
+ payload: {
6691
+ id: expect.any(String),
6692
+ },
6693
+ type: 'step-start',
6694
+ },
6695
+ {
6696
+ payload: {
6697
+ id: expect.any(String),
6698
+ output: {
6699
+ prompt: 'Capital of UK, just the name',
6700
+ },
6701
+ status: 'success',
6702
+ },
6703
+ type: 'step-result',
6704
+ },
6705
+ {
6706
+ payload: {
6707
+ id: expect.any(String),
6708
+ metadata: {},
6709
+ },
6710
+ type: 'step-finish',
6711
+ },
6712
+ {
6713
+ payload: {
6714
+ id: expect.any(String),
6715
+ },
6716
+ type: 'step-start',
6717
+ },
6718
+ {
6719
+ args: {
6720
+ prompt: 'Capital of UK, just the name',
6721
+ },
6722
+ name: 'test-agent-2',
6723
+ type: 'tool-call-streaming-start',
6724
+ },
6725
+ {
6726
+ args: {
6727
+ prompt: 'Capital of UK, just the name',
6728
+ },
6729
+ argsTextDelta: 'London',
6730
+ name: 'test-agent-2',
6731
+ type: 'tool-call-delta',
6732
+ },
6733
+ {
6734
+ payload: {
6735
+ id: expect.any(String),
6736
+ output: {
6737
+ text: 'London',
6738
+ },
6739
+ status: 'success',
6740
+ },
6741
+ type: 'step-result',
6742
+ },
6743
+ {
6744
+ payload: {
6745
+ id: expect.any(String),
6746
+ metadata: {},
6747
+ },
6748
+ type: 'step-finish',
6749
+ },
6750
+ {
6751
+ payload: {
6752
+ runId: 'test-run-id',
6753
+ },
6754
+ type: 'finish',
6755
+ },
6756
+ ]);
6757
+ });
6758
+ });
5521
6759
  }, 40e3);