@mastra/inngest 0.0.0-feat-tool-input-validation-20250731232758 → 0.0.0-feat-improve-processors-20251205191721

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,31 +1,320 @@
1
+ import { Tool } from '@mastra/core/tools';
2
+ import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, Workflow } from '@mastra/core/workflows';
3
+ import { EMITTER_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
4
+ import { z } from 'zod';
1
5
  import { randomUUID } from 'crypto';
6
+ import { ReadableStream, WritableStream } from 'stream/web';
7
+ import { RequestContext } from '@mastra/core/di';
8
+ import { RetryAfterError, NonRetriableError } from 'inngest';
2
9
  import { subscribe } from '@inngest/realtime';
3
- import { RuntimeContext } from '@mastra/core/di';
4
- import { ToolStream, Tool } from '@mastra/core/tools';
5
- import { Run, Workflow, DefaultExecutionEngine } from '@mastra/core/workflows';
6
- import { EMITTER_SYMBOL } from '@mastra/core/workflows/_constants';
10
+ import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
7
11
  import { serve as serve$1 } from 'inngest/hono';
8
- import { z } from 'zod';
9
12
 
10
13
  // src/index.ts
11
- function serve({ mastra, inngest }) {
12
- const wfs = mastra.getWorkflows();
13
- const functions = Array.from(
14
- new Set(
15
- Object.values(wfs).flatMap((wf) => {
16
- if (wf instanceof InngestWorkflow) {
17
- wf.__registerMastra(mastra);
18
- return wf.getFunctions();
14
+ var InngestExecutionEngine = class extends DefaultExecutionEngine {
15
+ inngestStep;
16
+ inngestAttempts;
17
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
18
+ super({ mastra, options });
19
+ this.inngestStep = inngestStep;
20
+ this.inngestAttempts = inngestAttempts;
21
+ }
22
+ // =============================================================================
23
+ // Hook Overrides
24
+ // =============================================================================
25
+ /**
26
+ * Format errors with stack traces for better debugging in Inngest
27
+ */
28
+ formatResultError(error, lastOutput) {
29
+ if (error instanceof Error) {
30
+ return error.stack ?? error.message;
31
+ }
32
+ const outputError = lastOutput?.error;
33
+ if (outputError instanceof Error) {
34
+ return outputError.message;
35
+ }
36
+ return outputError ?? error ?? "Unknown error";
37
+ }
38
+ /**
39
+ * Detect InngestWorkflow instances for special nested workflow handling
40
+ */
41
+ isNestedWorkflowStep(step) {
42
+ return step instanceof InngestWorkflow;
43
+ }
44
+ /**
45
+ * Inngest requires requestContext serialization for memoization.
46
+ * When steps are replayed, the original function doesn't re-execute,
47
+ * so requestContext modifications must be captured and restored.
48
+ */
49
+ requiresDurableContextSerialization() {
50
+ return true;
51
+ }
52
+ /**
53
+ * Execute a step with retry logic for Inngest.
54
+ * Retries are handled via step-level retry (RetryAfterError thrown INSIDE step.run()).
55
+ * After retries exhausted, error propagates here and we return a failed result.
56
+ */
57
+ async executeStepWithRetry(stepId, runStep, params) {
58
+ try {
59
+ const result = await this.wrapDurableOperation(stepId, runStep, { delay: params.delay });
60
+ return { ok: true, result };
61
+ } catch (e) {
62
+ const cause = e?.cause;
63
+ if (cause?.status === "failed") {
64
+ params.stepSpan?.error({
65
+ error: e,
66
+ attributes: { status: "failed" }
67
+ });
68
+ return { ok: false, error: cause };
69
+ }
70
+ const errorMessage = e instanceof Error ? e.message : String(e);
71
+ params.stepSpan?.error({
72
+ error: e,
73
+ attributes: { status: "failed" }
74
+ });
75
+ return {
76
+ ok: false,
77
+ error: {
78
+ status: "failed",
79
+ error: `Error: ${errorMessage}`,
80
+ endedAt: Date.now()
19
81
  }
20
- return [];
21
- })
22
- )
23
- );
24
- return serve$1({
25
- client: inngest,
26
- functions
27
- });
28
- }
82
+ };
83
+ }
84
+ }
85
+ /**
86
+ * Use Inngest's sleep primitive for durability
87
+ */
88
+ async executeSleepDuration(duration, sleepId, workflowId) {
89
+ await this.inngestStep.sleep(`workflow.${workflowId}.sleep.${sleepId}`, duration < 0 ? 0 : duration);
90
+ }
91
+ /**
92
+ * Use Inngest's sleepUntil primitive for durability
93
+ */
94
+ async executeSleepUntilDate(date, sleepUntilId, workflowId) {
95
+ await this.inngestStep.sleepUntil(`workflow.${workflowId}.sleepUntil.${sleepUntilId}`, date);
96
+ }
97
+ /**
98
+ * Wrap durable operations in Inngest step.run() for durability.
99
+ * If retryConfig is provided, throws RetryAfterError INSIDE step.run() to trigger
100
+ * Inngest's step-level retry mechanism (not function-level retry).
101
+ */
102
+ async wrapDurableOperation(operationId, operationFn, retryConfig) {
103
+ return this.inngestStep.run(operationId, async () => {
104
+ try {
105
+ return await operationFn();
106
+ } catch (e) {
107
+ if (retryConfig) {
108
+ const errorMessage = e instanceof Error ? e.message : String(e);
109
+ throw new RetryAfterError(errorMessage, retryConfig.delay, {
110
+ cause: {
111
+ status: "failed",
112
+ error: `Error: ${errorMessage}`,
113
+ endedAt: Date.now()
114
+ }
115
+ });
116
+ }
117
+ throw e;
118
+ }
119
+ });
120
+ }
121
+ /**
122
+ * Provide Inngest step primitive in engine context
123
+ */
124
+ getEngineContext() {
125
+ return { step: this.inngestStep };
126
+ }
127
+ /**
128
+ * Execute nested InngestWorkflow using inngestStep.invoke() for durability.
129
+ * This MUST be called directly (not inside step.run()) due to Inngest constraints.
130
+ */
131
+ async executeWorkflowStep(params) {
132
+ if (!(params.step instanceof InngestWorkflow)) {
133
+ return null;
134
+ }
135
+ const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData, emitter, startedAt } = params;
136
+ const isResume = !!resume?.steps?.length;
137
+ let result;
138
+ let runId;
139
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
140
+ try {
141
+ if (isResume) {
142
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
143
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
144
+ workflowName: step.id,
145
+ runId
146
+ });
147
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
148
+ function: step.getFunction(),
149
+ data: {
150
+ inputData,
151
+ initialState: executionContext.state ?? snapshot?.value ?? {},
152
+ runId,
153
+ resume: {
154
+ runId,
155
+ steps: resume.steps.slice(1),
156
+ stepResults: snapshot?.context,
157
+ resumePayload: resume.resumePayload,
158
+ resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
159
+ },
160
+ outputOptions: { includeState: true }
161
+ }
162
+ });
163
+ result = invokeResp.result;
164
+ runId = invokeResp.runId;
165
+ executionContext.state = invokeResp.result.state;
166
+ } else if (isTimeTravel) {
167
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
168
+ workflowName: step.id,
169
+ runId: executionContext.runId
170
+ }) ?? { context: {} };
171
+ const timeTravelParams = createTimeTravelExecutionParams({
172
+ steps: timeTravel.steps.slice(1),
173
+ inputData: timeTravel.inputData,
174
+ resumeData: timeTravel.resumeData,
175
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
176
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
177
+ snapshot,
178
+ graph: step.buildExecutionGraph()
179
+ });
180
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
181
+ function: step.getFunction(),
182
+ data: {
183
+ timeTravel: timeTravelParams,
184
+ initialState: executionContext.state ?? {},
185
+ runId: executionContext.runId,
186
+ outputOptions: { includeState: true }
187
+ }
188
+ });
189
+ result = invokeResp.result;
190
+ runId = invokeResp.runId;
191
+ executionContext.state = invokeResp.result.state;
192
+ } else {
193
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
194
+ function: step.getFunction(),
195
+ data: {
196
+ inputData,
197
+ initialState: executionContext.state ?? {},
198
+ outputOptions: { includeState: true }
199
+ }
200
+ });
201
+ result = invokeResp.result;
202
+ runId = invokeResp.runId;
203
+ executionContext.state = invokeResp.result.state;
204
+ }
205
+ } catch (e) {
206
+ const errorCause = e?.cause;
207
+ if (errorCause && typeof errorCause === "object") {
208
+ result = errorCause;
209
+ runId = errorCause.runId || randomUUID();
210
+ } else {
211
+ runId = randomUUID();
212
+ result = {
213
+ status: "failed",
214
+ error: e instanceof Error ? e : new Error(String(e)),
215
+ steps: {},
216
+ input: inputData
217
+ };
218
+ }
219
+ }
220
+ const res = await this.inngestStep.run(
221
+ `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
222
+ async () => {
223
+ if (result.status === "failed") {
224
+ await emitter.emit("watch", {
225
+ type: "workflow-step-result",
226
+ payload: {
227
+ id: step.id,
228
+ status: "failed",
229
+ error: result?.error,
230
+ payload: prevOutput
231
+ }
232
+ });
233
+ return { executionContext, result: { status: "failed", error: result?.error } };
234
+ } else if (result.status === "suspended") {
235
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
236
+ const stepRes = stepResult;
237
+ return stepRes?.status === "suspended";
238
+ });
239
+ for (const [stepName, stepResult] of suspendedSteps) {
240
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
241
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
242
+ await emitter.emit("watch", {
243
+ type: "workflow-step-suspended",
244
+ payload: {
245
+ id: step.id,
246
+ status: "suspended"
247
+ }
248
+ });
249
+ return {
250
+ executionContext,
251
+ result: {
252
+ status: "suspended",
253
+ payload: stepResult.payload,
254
+ suspendPayload: {
255
+ ...stepResult?.suspendPayload,
256
+ __workflow_meta: { runId, path: suspendPath }
257
+ }
258
+ }
259
+ };
260
+ }
261
+ return {
262
+ executionContext,
263
+ result: {
264
+ status: "suspended",
265
+ payload: {}
266
+ }
267
+ };
268
+ } else if (result.status === "tripwire") {
269
+ await emitter.emit("watch", {
270
+ type: "workflow-step-result",
271
+ payload: {
272
+ id: step.id,
273
+ status: "tripwire",
274
+ error: result?.reason,
275
+ payload: prevOutput
276
+ }
277
+ });
278
+ return {
279
+ executionContext,
280
+ result: {
281
+ status: "tripwire",
282
+ reason: result?.reason,
283
+ retry: result?.retry,
284
+ metadata: result?.metadata,
285
+ processorId: result?.processorId
286
+ }
287
+ };
288
+ }
289
+ await emitter.emit("watch", {
290
+ type: "workflow-step-result",
291
+ payload: {
292
+ id: step.id,
293
+ status: "success",
294
+ output: result?.result
295
+ }
296
+ });
297
+ await emitter.emit("watch", {
298
+ type: "workflow-step-finish",
299
+ payload: {
300
+ id: step.id,
301
+ metadata: {}
302
+ }
303
+ });
304
+ return { executionContext, result: { status: "success", output: result?.result } };
305
+ }
306
+ );
307
+ Object.assign(executionContext, res.executionContext);
308
+ return {
309
+ ...res.result,
310
+ startedAt,
311
+ endedAt: Date.now(),
312
+ payload: inputData,
313
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
314
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
315
+ };
316
+ }
317
+ };
29
318
  var InngestRun = class extends Run {
30
319
  inngest;
31
320
  serializedStepGraph;
@@ -47,14 +336,21 @@ var InngestRun = class extends Run {
47
336
  }
48
337
  async getRunOutput(eventId) {
49
338
  let runs = await this.getRuns(eventId);
339
+ const storage = this.#mastra?.getStorage();
50
340
  while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
51
341
  await new Promise((resolve) => setTimeout(resolve, 1e3));
52
342
  runs = await this.getRuns(eventId);
53
343
  if (runs?.[0]?.status === "Failed") {
54
- console.log("run", runs?.[0]);
55
- throw new Error(`Function run ${runs?.[0]?.status}`);
56
- } else if (runs?.[0]?.status === "Cancelled") {
57
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
344
+ const snapshot = await storage?.loadWorkflowSnapshot({
345
+ workflowName: this.workflowId,
346
+ runId: this.runId
347
+ });
348
+ return {
349
+ output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
350
+ };
351
+ }
352
+ if (runs?.[0]?.status === "Cancelled") {
353
+ const snapshot = await storage?.loadWorkflowSnapshot({
58
354
  workflowName: this.workflowId,
59
355
  runId: this.runId
60
356
  });
@@ -63,56 +359,73 @@ var InngestRun = class extends Run {
63
359
  }
64
360
  return runs?.[0];
65
361
  }
66
- async sendEvent(event, data) {
67
- await this.inngest.send({
68
- name: `user-event-${event}`,
69
- data
70
- });
71
- }
72
362
  async cancel() {
363
+ const storage = this.#mastra?.getStorage();
73
364
  await this.inngest.send({
74
365
  name: `cancel.workflow.${this.workflowId}`,
75
366
  data: {
76
367
  runId: this.runId
77
368
  }
78
369
  });
79
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
370
+ const snapshot = await storage?.loadWorkflowSnapshot({
80
371
  workflowName: this.workflowId,
81
372
  runId: this.runId
82
373
  });
83
374
  if (snapshot) {
84
- await this.#mastra?.storage?.persistWorkflowSnapshot({
375
+ await storage?.persistWorkflowSnapshot({
85
376
  workflowName: this.workflowId,
86
377
  runId: this.runId,
378
+ resourceId: this.resourceId,
87
379
  snapshot: {
88
380
  ...snapshot,
89
- status: "canceled"
381
+ status: "canceled",
382
+ value: snapshot.value
90
383
  }
91
384
  });
92
385
  }
93
386
  }
94
- async start({
95
- inputData
387
+ async start(params) {
388
+ return this._start(params);
389
+ }
390
+ async _start({
391
+ inputData,
392
+ initialState,
393
+ outputOptions,
394
+ tracingOptions,
395
+ format,
396
+ requestContext
96
397
  }) {
97
398
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
98
399
  workflowName: this.workflowId,
99
400
  runId: this.runId,
401
+ resourceId: this.resourceId,
100
402
  snapshot: {
101
403
  runId: this.runId,
102
404
  serializedStepGraph: this.serializedStepGraph,
405
+ status: "running",
103
406
  value: {},
104
407
  context: {},
105
408
  activePaths: [],
106
409
  suspendedPaths: {},
107
- timestamp: Date.now(),
108
- status: "running"
410
+ activeStepsPath: {},
411
+ resumeLabels: {},
412
+ waitingPaths: {},
413
+ timestamp: Date.now()
109
414
  }
110
415
  });
416
+ const inputDataToUse = await this._validateInput(inputData);
417
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
111
418
  const eventOutput = await this.inngest.send({
112
419
  name: `workflow.${this.workflowId}`,
113
420
  data: {
114
- inputData,
115
- runId: this.runId
421
+ inputData: inputDataToUse,
422
+ initialState: initialStateToUse,
423
+ runId: this.runId,
424
+ resourceId: this.resourceId,
425
+ outputOptions,
426
+ tracingOptions,
427
+ format,
428
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {}
116
429
  }
117
430
  });
118
431
  const eventId = eventOutput.ids[0];
@@ -141,27 +454,131 @@ var InngestRun = class extends Run {
141
454
  return p;
142
455
  }
143
456
  async _resume(params) {
144
- const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
145
- (step) => typeof step === "string" ? step : step?.id
146
- );
147
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
457
+ const storage = this.#mastra?.getStorage();
458
+ let steps = [];
459
+ if (typeof params.step === "string") {
460
+ steps = params.step.split(".");
461
+ } else {
462
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
463
+ (step) => typeof step === "string" ? step : step?.id
464
+ );
465
+ }
466
+ const snapshot = await storage?.loadWorkflowSnapshot({
148
467
  workflowName: this.workflowId,
149
468
  runId: this.runId
150
469
  });
470
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
471
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
472
+ const persistedRequestContext = snapshot?.requestContext ?? {};
473
+ const newRequestContext = params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {};
474
+ const mergedRequestContext = { ...persistedRequestContext, ...newRequestContext };
151
475
  const eventOutput = await this.inngest.send({
152
476
  name: `workflow.${this.workflowId}`,
153
477
  data: {
154
- inputData: params.resumeData,
478
+ inputData: resumeDataToUse,
479
+ initialState: snapshot?.value ?? {},
155
480
  runId: this.runId,
156
481
  workflowId: this.workflowId,
157
482
  stepResults: snapshot?.context,
158
483
  resume: {
159
484
  steps,
160
485
  stepResults: snapshot?.context,
161
- resumePayload: params.resumeData,
162
- // @ts-ignore
163
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
486
+ resumePayload: resumeDataToUse,
487
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
488
+ },
489
+ requestContext: mergedRequestContext
490
+ }
491
+ });
492
+ const eventId = eventOutput.ids[0];
493
+ if (!eventId) {
494
+ throw new Error("Event ID is not set");
495
+ }
496
+ const runOutput = await this.getRunOutput(eventId);
497
+ const result = runOutput?.output?.result;
498
+ if (result.status === "failed") {
499
+ result.error = new Error(result.error);
500
+ }
501
+ return result;
502
+ }
503
+ async timeTravel(params) {
504
+ const p = this._timeTravel(params).then((result) => {
505
+ if (result.status !== "suspended") {
506
+ this.closeStreamAction?.().catch(() => {
507
+ });
508
+ }
509
+ return result;
510
+ });
511
+ this.executionResults = p;
512
+ return p;
513
+ }
514
+ async _timeTravel(params) {
515
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
516
+ throw new Error("Step is required and must be a valid step or array of steps");
517
+ }
518
+ let steps = [];
519
+ if (typeof params.step === "string") {
520
+ steps = params.step.split(".");
521
+ } else {
522
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
523
+ (step) => typeof step === "string" ? step : step?.id
524
+ );
525
+ }
526
+ if (steps.length === 0) {
527
+ throw new Error("No steps provided to timeTravel");
528
+ }
529
+ const storage = this.#mastra?.getStorage();
530
+ const snapshot = await storage?.loadWorkflowSnapshot({
531
+ workflowName: this.workflowId,
532
+ runId: this.runId
533
+ });
534
+ if (!snapshot) {
535
+ await storage?.persistWorkflowSnapshot({
536
+ workflowName: this.workflowId,
537
+ runId: this.runId,
538
+ resourceId: this.resourceId,
539
+ snapshot: {
540
+ runId: this.runId,
541
+ serializedStepGraph: this.serializedStepGraph,
542
+ status: "pending",
543
+ value: {},
544
+ context: {},
545
+ activePaths: [],
546
+ suspendedPaths: {},
547
+ activeStepsPath: {},
548
+ resumeLabels: {},
549
+ waitingPaths: {},
550
+ timestamp: Date.now()
164
551
  }
552
+ });
553
+ }
554
+ if (snapshot?.status === "running") {
555
+ throw new Error("This workflow run is still running, cannot time travel");
556
+ }
557
+ let inputDataToUse = params.inputData;
558
+ if (inputDataToUse && steps.length === 1) {
559
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
560
+ }
561
+ const timeTravelData = createTimeTravelExecutionParams({
562
+ steps,
563
+ inputData: inputDataToUse,
564
+ resumeData: params.resumeData,
565
+ context: params.context,
566
+ nestedStepsContext: params.nestedStepsContext,
567
+ snapshot: snapshot ?? { context: {} },
568
+ graph: this.executionGraph,
569
+ initialState: params.initialState
570
+ });
571
+ const eventOutput = await this.inngest.send({
572
+ name: `workflow.${this.workflowId}`,
573
+ data: {
574
+ initialState: timeTravelData.state,
575
+ runId: this.runId,
576
+ workflowId: this.workflowId,
577
+ stepResults: timeTravelData.stepResults,
578
+ timeTravel: timeTravelData,
579
+ tracingOptions: params.tracingOptions,
580
+ outputOptions: params.outputOptions,
581
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
165
582
  }
166
583
  });
167
584
  const eventId = eventOutput.ids[0];
@@ -175,12 +592,12 @@ var InngestRun = class extends Run {
175
592
  }
176
593
  return result;
177
594
  }
178
- watch(cb, type = "watch") {
595
+ watch(cb) {
179
596
  let active = true;
180
597
  const streamPromise = subscribe(
181
598
  {
182
599
  channel: `workflow:${this.workflowId}:${this.runId}`,
183
- topics: [type],
600
+ topics: ["watch"],
184
601
  app: this.inngest
185
602
  },
186
603
  (message) => {
@@ -198,16 +615,35 @@ var InngestRun = class extends Run {
198
615
  });
199
616
  };
200
617
  }
201
- stream({ inputData, runtimeContext } = {}) {
618
+ streamLegacy({ inputData, requestContext } = {}) {
202
619
  const { readable, writable } = new TransformStream();
203
620
  const writer = writable.getWriter();
621
+ void writer.write({
622
+ // @ts-ignore
623
+ type: "start",
624
+ // @ts-ignore
625
+ payload: { runId: this.runId }
626
+ });
204
627
  const unwatch = this.watch(async (event) => {
205
628
  try {
206
- await writer.write(event);
629
+ const e = {
630
+ ...event,
631
+ type: event.type.replace("workflow-", "")
632
+ };
633
+ if (e.type === "step-output") {
634
+ e.type = e.payload.output.type;
635
+ e.payload = e.payload.output.payload;
636
+ }
637
+ await writer.write(e);
207
638
  } catch {
208
639
  }
209
- }, "watch-v2");
640
+ });
210
641
  this.closeStreamAction = async () => {
642
+ await writer.write({
643
+ type: "finish",
644
+ // @ts-ignore
645
+ payload: { runId: this.runId }
646
+ });
211
647
  unwatch();
212
648
  try {
213
649
  await writer.close();
@@ -217,7 +653,7 @@ var InngestRun = class extends Run {
217
653
  writer.releaseLock();
218
654
  }
219
655
  };
220
- this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
656
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
221
657
  if (result.status !== "suspended") {
222
658
  this.closeStreamAction?.().catch(() => {
223
659
  });
@@ -229,57 +665,190 @@ var InngestRun = class extends Run {
229
665
  getWorkflowState: () => this.executionResults
230
666
  };
231
667
  }
232
- };
233
- var InngestWorkflow = class _InngestWorkflow extends Workflow {
234
- #mastra;
235
- inngest;
236
- function;
237
- constructor(params, inngest) {
238
- super(params);
239
- this.#mastra = params.mastra;
240
- this.inngest = inngest;
241
- }
242
- async getWorkflowRuns(args) {
243
- const storage = this.#mastra?.getStorage();
244
- if (!storage) {
245
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
246
- return { runs: [], total: 0 };
247
- }
248
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
249
- }
250
- async getWorkflowRunById(runId) {
251
- const storage = this.#mastra?.getStorage();
252
- if (!storage) {
253
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
254
- return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
255
- }
256
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
257
- return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
258
- }
259
- async getWorkflowRunExecutionResult(runId) {
260
- const storage = this.#mastra?.getStorage();
261
- if (!storage) {
262
- this.logger.debug("Cannot get workflow run execution result. Mastra storage is not initialized");
263
- return null;
264
- }
265
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
266
- if (!run?.snapshot) {
267
- return null;
668
+ stream({
669
+ inputData,
670
+ requestContext,
671
+ tracingOptions,
672
+ closeOnSuspend = true,
673
+ initialState,
674
+ outputOptions
675
+ } = {}) {
676
+ if (this.closeStreamAction && this.streamOutput) {
677
+ return this.streamOutput;
268
678
  }
269
- if (typeof run.snapshot === "string") {
270
- return null;
271
- }
272
- return {
273
- status: run.snapshot.status,
274
- result: run.snapshot.result,
275
- error: run.snapshot.error,
276
- payload: run.snapshot.context?.input,
277
- steps: run.snapshot.context
679
+ this.closeStreamAction = async () => {
278
680
  };
279
- }
280
- __registerMastra(mastra) {
281
- this.#mastra = mastra;
282
- this.executionEngine.__registerMastra(mastra);
681
+ const self = this;
682
+ const stream = new ReadableStream({
683
+ async start(controller) {
684
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
685
+ controller.enqueue({
686
+ type,
687
+ runId: self.runId,
688
+ from,
689
+ payload: {
690
+ stepName: payload?.id,
691
+ ...payload
692
+ }
693
+ });
694
+ });
695
+ self.closeStreamAction = async () => {
696
+ unwatch();
697
+ try {
698
+ await controller.close();
699
+ } catch (err) {
700
+ console.error("Error closing stream:", err);
701
+ }
702
+ };
703
+ const executionResultsPromise = self._start({
704
+ inputData,
705
+ requestContext,
706
+ // tracingContext, // We are not able to pass a reference to a span here, what to do?
707
+ initialState,
708
+ tracingOptions,
709
+ outputOptions,
710
+ format: "vnext"
711
+ });
712
+ let executionResults;
713
+ try {
714
+ executionResults = await executionResultsPromise;
715
+ if (closeOnSuspend) {
716
+ self.closeStreamAction?.().catch(() => {
717
+ });
718
+ } else if (executionResults.status !== "suspended") {
719
+ self.closeStreamAction?.().catch(() => {
720
+ });
721
+ }
722
+ if (self.streamOutput) {
723
+ self.streamOutput.updateResults(
724
+ executionResults
725
+ );
726
+ }
727
+ } catch (err) {
728
+ self.streamOutput?.rejectResults(err);
729
+ self.closeStreamAction?.().catch(() => {
730
+ });
731
+ }
732
+ }
733
+ });
734
+ this.streamOutput = new WorkflowRunOutput({
735
+ runId: this.runId,
736
+ workflowId: this.workflowId,
737
+ stream
738
+ });
739
+ return this.streamOutput;
740
+ }
741
+ streamVNext(args = {}) {
742
+ return this.stream(args);
743
+ }
744
+ timeTravelStream({
745
+ inputData,
746
+ resumeData,
747
+ initialState,
748
+ step,
749
+ context,
750
+ nestedStepsContext,
751
+ requestContext,
752
+ tracingOptions,
753
+ outputOptions
754
+ }) {
755
+ this.closeStreamAction = async () => {
756
+ };
757
+ const self = this;
758
+ const stream = new ReadableStream({
759
+ async start(controller) {
760
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
761
+ controller.enqueue({
762
+ type,
763
+ runId: self.runId,
764
+ from,
765
+ payload: {
766
+ stepName: payload?.id,
767
+ ...payload
768
+ }
769
+ });
770
+ });
771
+ self.closeStreamAction = async () => {
772
+ unwatch();
773
+ try {
774
+ controller.close();
775
+ } catch (err) {
776
+ console.error("Error closing stream:", err);
777
+ }
778
+ };
779
+ const executionResultsPromise = self._timeTravel({
780
+ inputData,
781
+ step,
782
+ context,
783
+ nestedStepsContext,
784
+ resumeData,
785
+ initialState,
786
+ requestContext,
787
+ tracingOptions,
788
+ outputOptions
789
+ });
790
+ self.executionResults = executionResultsPromise;
791
+ let executionResults;
792
+ try {
793
+ executionResults = await executionResultsPromise;
794
+ self.closeStreamAction?.().catch(() => {
795
+ });
796
+ if (self.streamOutput) {
797
+ self.streamOutput.updateResults(executionResults);
798
+ }
799
+ } catch (err) {
800
+ self.streamOutput?.rejectResults(err);
801
+ self.closeStreamAction?.().catch(() => {
802
+ });
803
+ }
804
+ }
805
+ });
806
+ this.streamOutput = new WorkflowRunOutput({
807
+ runId: this.runId,
808
+ workflowId: this.workflowId,
809
+ stream
810
+ });
811
+ return this.streamOutput;
812
+ }
813
+ };
814
+
815
+ // src/workflow.ts
816
+ var InngestWorkflow = class _InngestWorkflow extends Workflow {
817
+ #mastra;
818
+ inngest;
819
+ function;
820
+ flowControlConfig;
821
+ constructor(params, inngest) {
822
+ const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
823
+ super(workflowParams);
824
+ this.engineType = "inngest";
825
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
826
+ ([_, value]) => value !== void 0
827
+ );
828
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
829
+ this.#mastra = params.mastra;
830
+ this.inngest = inngest;
831
+ }
832
+ async listWorkflowRuns(args) {
833
+ const storage = this.#mastra?.getStorage();
834
+ if (!storage) {
835
+ this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
836
+ return { runs: [], total: 0 };
837
+ }
838
+ return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
839
+ }
840
+ async getWorkflowRunById(runId) {
841
+ const storage = this.#mastra?.getStorage();
842
+ if (!storage) {
843
+ this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
844
+ return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
845
+ }
846
+ const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
847
+ return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
848
+ }
849
+ __registerMastra(mastra) {
850
+ this.#mastra = mastra;
851
+ this.executionEngine.__registerMastra(mastra);
283
852
  const updateNested = (step) => {
284
853
  if ((step.type === "step" || step.type === "loop" || step.type === "foreach") && step.step instanceof _InngestWorkflow) {
285
854
  step.step.__registerMastra(mastra);
@@ -295,56 +864,49 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
295
864
  }
296
865
  }
297
866
  }
298
- createRun(options) {
867
+ async createRun(options) {
299
868
  const runIdToUse = options?.runId || randomUUID();
300
869
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
301
870
  {
302
871
  workflowId: this.id,
303
872
  runId: runIdToUse,
873
+ resourceId: options?.resourceId,
304
874
  executionEngine: this.executionEngine,
305
875
  executionGraph: this.executionGraph,
306
876
  serializedStepGraph: this.serializedStepGraph,
307
877
  mastra: this.#mastra,
308
878
  retryConfig: this.retryConfig,
309
- cleanup: () => this.runs.delete(runIdToUse)
879
+ cleanup: () => this.runs.delete(runIdToUse),
880
+ workflowSteps: this.steps,
881
+ workflowEngineType: this.engineType,
882
+ validateInputs: this.options.validateInputs
310
883
  },
311
884
  this.inngest
312
885
  );
313
886
  this.runs.set(runIdToUse, run);
314
- return run;
315
- }
316
- async createRunAsync(options) {
317
- const runIdToUse = options?.runId || randomUUID();
318
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
319
- {
320
- workflowId: this.id,
321
- runId: runIdToUse,
322
- executionEngine: this.executionEngine,
323
- executionGraph: this.executionGraph,
324
- serializedStepGraph: this.serializedStepGraph,
325
- mastra: this.#mastra,
326
- retryConfig: this.retryConfig,
327
- cleanup: () => this.runs.delete(runIdToUse)
328
- },
329
- this.inngest
330
- );
331
- this.runs.set(runIdToUse, run);
332
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
333
- if (!workflowSnapshotInStorage) {
887
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
888
+ workflowStatus: run.workflowRunStatus,
889
+ stepResults: {}
890
+ });
891
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
892
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
334
893
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
335
894
  workflowName: this.id,
336
895
  runId: runIdToUse,
896
+ resourceId: options?.resourceId,
337
897
  snapshot: {
338
898
  runId: runIdToUse,
339
899
  status: "pending",
340
900
  value: {},
341
901
  context: {},
342
902
  activePaths: [],
903
+ activeStepsPath: {},
904
+ waitingPaths: {},
343
905
  serializedStepGraph: this.serializedStepGraph,
344
906
  suspendedPaths: {},
907
+ resumeLabels: {},
345
908
  result: void 0,
346
909
  error: void 0,
347
- // @ts-ignore
348
910
  timestamp: Date.now()
349
911
  }
350
912
  });
@@ -358,13 +920,14 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
358
920
  this.function = this.inngest.createFunction(
359
921
  {
360
922
  id: `workflow.${this.id}`,
361
- // @ts-ignore
362
- retries: this.retryConfig?.attempts ?? 0,
363
- cancelOn: [{ event: `cancel.workflow.${this.id}` }]
923
+ retries: Math.min(this.retryConfig?.attempts ?? 0, 20),
924
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
925
+ // Spread flow control configuration
926
+ ...this.flowControlConfig
364
927
  },
365
928
  { event: `workflow.${this.id}` },
366
929
  async ({ event, step, attempt, publish }) => {
367
- let { inputData, runId, resume } = event.data;
930
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
368
931
  if (!runId) {
369
932
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
370
933
  return randomUUID();
@@ -392,19 +955,38 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
392
955
  once: (_event, _callback) => {
393
956
  }
394
957
  };
395
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
958
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
396
959
  const result = await engine.execute({
397
960
  workflowId: this.id,
398
961
  runId,
962
+ resourceId,
399
963
  graph: this.executionGraph,
400
964
  serializedStepGraph: this.serializedStepGraph,
401
965
  input: inputData,
966
+ initialState,
402
967
  emitter,
403
968
  retryConfig: this.retryConfig,
404
- runtimeContext: new RuntimeContext(),
405
- // TODO
969
+ requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
406
970
  resume,
407
- abortController: new AbortController()
971
+ timeTravel,
972
+ format,
973
+ abortController: new AbortController(),
974
+ // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
975
+ outputOptions,
976
+ writableStream: new WritableStream({
977
+ write(chunk) {
978
+ void emitter.emit("watch", chunk).catch(() => {
979
+ });
980
+ }
981
+ })
982
+ });
983
+ await step.run(`workflow.${this.id}.finalize`, async () => {
984
+ if (result.status === "failed") {
985
+ throw new NonRetriableError(`Workflow failed`, {
986
+ cause: result
987
+ });
988
+ }
989
+ return result;
408
990
  });
409
991
  return { result, runId };
410
992
  }
@@ -428,27 +1010,70 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
428
1010
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
429
1011
  }
430
1012
  };
1013
+ function serve({
1014
+ mastra,
1015
+ inngest,
1016
+ functions: userFunctions = [],
1017
+ registerOptions
1018
+ }) {
1019
+ const wfs = mastra.listWorkflows();
1020
+ const workflowFunctions = Array.from(
1021
+ new Set(
1022
+ Object.values(wfs).flatMap((wf) => {
1023
+ if (wf instanceof InngestWorkflow) {
1024
+ wf.__registerMastra(mastra);
1025
+ return wf.getFunctions();
1026
+ }
1027
+ return [];
1028
+ })
1029
+ )
1030
+ );
1031
+ return serve$1({
1032
+ ...registerOptions,
1033
+ client: inngest,
1034
+ functions: [...workflowFunctions, ...userFunctions]
1035
+ });
1036
+ }
1037
+
1038
+ // src/types.ts
1039
+ var _compatibilityCheck = true;
1040
+
1041
+ // src/index.ts
431
1042
  function isAgent(params) {
432
1043
  return params?.component === "AGENT";
433
1044
  }
434
1045
  function isTool(params) {
435
1046
  return params instanceof Tool;
436
1047
  }
437
- function createStep(params) {
1048
+ function isInngestWorkflow(params) {
1049
+ return params instanceof InngestWorkflow;
1050
+ }
1051
+ function createStep(params, agentOptions) {
1052
+ if (isInngestWorkflow(params)) {
1053
+ return params;
1054
+ }
438
1055
  if (isAgent(params)) {
439
1056
  return {
440
1057
  id: params.name,
441
- // @ts-ignore
1058
+ description: params.getDescription(),
442
1059
  inputSchema: z.object({
443
1060
  prompt: z.string()
444
1061
  // resourceId: z.string().optional(),
445
1062
  // threadId: z.string().optional(),
446
1063
  }),
447
- // @ts-ignore
448
1064
  outputSchema: z.object({
449
1065
  text: z.string()
450
1066
  }),
451
- execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
1067
+ execute: async ({
1068
+ inputData,
1069
+ [EMITTER_SYMBOL]: emitter,
1070
+ [STREAM_FORMAT_SYMBOL]: streamFormat,
1071
+ requestContext,
1072
+ tracingContext,
1073
+ abortSignal,
1074
+ abort,
1075
+ writer
1076
+ }) => {
452
1077
  let streamPromise = {};
453
1078
  streamPromise.promise = new Promise((resolve, reject) => {
454
1079
  streamPromise.resolve = resolve;
@@ -458,50 +1083,65 @@ function createStep(params) {
458
1083
  name: params.name,
459
1084
  args: inputData
460
1085
  };
461
- await emitter.emit("watch-v2", {
462
- type: "tool-call-streaming-start",
463
- ...toolData
464
- });
465
- const { fullStream } = await params.stream(inputData.prompt, {
466
- // resourceId: inputData.resourceId,
467
- // threadId: inputData.threadId,
468
- runtimeContext,
469
- onFinish: (result) => {
470
- streamPromise.resolve(result.text);
471
- },
472
- abortSignal
473
- });
474
- if (abortSignal.aborted) {
475
- return abort();
1086
+ let stream;
1087
+ if ((await params.getModel()).specificationVersion === "v1") {
1088
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
1089
+ ...agentOptions ?? {},
1090
+ // resourceId: inputData.resourceId,
1091
+ // threadId: inputData.threadId,
1092
+ requestContext,
1093
+ tracingContext,
1094
+ onFinish: (result) => {
1095
+ streamPromise.resolve(result.text);
1096
+ void agentOptions?.onFinish?.(result);
1097
+ },
1098
+ abortSignal
1099
+ });
1100
+ stream = fullStream;
1101
+ } else {
1102
+ const modelOutput = await params.stream(inputData.prompt, {
1103
+ ...agentOptions ?? {},
1104
+ requestContext,
1105
+ tracingContext,
1106
+ onFinish: (result) => {
1107
+ streamPromise.resolve(result.text);
1108
+ void agentOptions?.onFinish?.(result);
1109
+ },
1110
+ abortSignal
1111
+ });
1112
+ stream = modelOutput.fullStream;
476
1113
  }
477
- for await (const chunk of fullStream) {
478
- switch (chunk.type) {
479
- case "text-delta":
480
- await emitter.emit("watch-v2", {
1114
+ if (streamFormat === "legacy") {
1115
+ await emitter.emit("watch", {
1116
+ type: "tool-call-streaming-start",
1117
+ ...toolData ?? {}
1118
+ });
1119
+ for await (const chunk of stream) {
1120
+ if (chunk.type === "text-delta") {
1121
+ await emitter.emit("watch", {
481
1122
  type: "tool-call-delta",
482
- ...toolData,
1123
+ ...toolData ?? {},
483
1124
  argsTextDelta: chunk.textDelta
484
1125
  });
485
- break;
486
- case "step-start":
487
- case "step-finish":
488
- case "finish":
489
- break;
490
- case "tool-call":
491
- case "tool-result":
492
- case "tool-call-streaming-start":
493
- case "tool-call-delta":
494
- case "source":
495
- case "file":
496
- default:
497
- await emitter.emit("watch-v2", chunk);
498
- break;
1126
+ }
1127
+ }
1128
+ await emitter.emit("watch", {
1129
+ type: "tool-call-streaming-finish",
1130
+ ...toolData ?? {}
1131
+ });
1132
+ } else {
1133
+ for await (const chunk of stream) {
1134
+ await writer.write(chunk);
499
1135
  }
500
1136
  }
1137
+ if (abortSignal.aborted) {
1138
+ return abort();
1139
+ }
501
1140
  return {
502
1141
  text: await streamPromise.promise
503
1142
  };
504
- }
1143
+ },
1144
+ component: params.component
505
1145
  };
506
1146
  }
507
1147
  if (isTool(params)) {
@@ -510,17 +1150,40 @@ function createStep(params) {
510
1150
  }
511
1151
  return {
512
1152
  // TODO: tool probably should have strong id type
513
- // @ts-ignore
514
1153
  id: params.id,
1154
+ description: params.description,
515
1155
  inputSchema: params.inputSchema,
516
1156
  outputSchema: params.outputSchema,
517
- execute: async ({ inputData, mastra, runtimeContext }) => {
518
- return params.execute({
519
- context: inputData,
1157
+ suspendSchema: params.suspendSchema,
1158
+ resumeSchema: params.resumeSchema,
1159
+ execute: async ({
1160
+ inputData,
1161
+ mastra,
1162
+ requestContext,
1163
+ tracingContext,
1164
+ suspend,
1165
+ resumeData,
1166
+ runId,
1167
+ workflowId,
1168
+ state,
1169
+ setState
1170
+ }) => {
1171
+ const toolContext = {
520
1172
  mastra,
521
- runtimeContext
522
- });
523
- }
1173
+ requestContext,
1174
+ tracingContext,
1175
+ workflow: {
1176
+ runId,
1177
+ resumeData,
1178
+ suspend,
1179
+ workflowId,
1180
+ state,
1181
+ setState
1182
+ }
1183
+ };
1184
+ return params.execute(inputData, toolContext);
1185
+ },
1186
+ component: "TOOL"
524
1187
  };
525
1188
  }
526
1189
  return {
@@ -536,7 +1199,10 @@ function createStep(params) {
536
1199
  function init(inngest) {
537
1200
  return {
538
1201
  createWorkflow(params) {
539
- return new InngestWorkflow(params, inngest);
1202
+ return new InngestWorkflow(
1203
+ params,
1204
+ inngest
1205
+ );
540
1206
  },
541
1207
  createStep,
542
1208
  cloneStep(step, opts) {
@@ -545,7 +1211,13 @@ function init(inngest) {
545
1211
  description: step.description,
546
1212
  inputSchema: step.inputSchema,
547
1213
  outputSchema: step.outputSchema,
548
- execute: step.execute
1214
+ resumeSchema: step.resumeSchema,
1215
+ suspendSchema: step.suspendSchema,
1216
+ stateSchema: step.stateSchema,
1217
+ execute: step.execute,
1218
+ retries: step.retries,
1219
+ scorers: step.scorers,
1220
+ component: step.component
549
1221
  };
550
1222
  },
551
1223
  cloneWorkflow(workflow, opts) {
@@ -554,7 +1226,8 @@ function init(inngest) {
554
1226
  inputSchema: workflow.inputSchema,
555
1227
  outputSchema: workflow.outputSchema,
556
1228
  steps: workflow.stepDefs,
557
- mastra: workflow.mastra
1229
+ mastra: workflow.mastra,
1230
+ options: workflow.options
558
1231
  });
559
1232
  wf.setStepFlow(workflow.stepGraph);
560
1233
  wf.commit();
@@ -562,731 +1235,7 @@ function init(inngest) {
562
1235
  }
563
1236
  };
564
1237
  }
565
- var InngestExecutionEngine = class extends DefaultExecutionEngine {
566
- inngestStep;
567
- inngestAttempts;
568
- constructor(mastra, inngestStep, inngestAttempts = 0) {
569
- super({ mastra });
570
- this.inngestStep = inngestStep;
571
- this.inngestAttempts = inngestAttempts;
572
- }
573
- async execute(params) {
574
- await params.emitter.emit("watch-v2", {
575
- type: "start",
576
- payload: { runId: params.runId }
577
- });
578
- const result = await super.execute(params);
579
- await params.emitter.emit("watch-v2", {
580
- type: "finish",
581
- payload: { runId: params.runId }
582
- });
583
- return result;
584
- }
585
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
586
- const base = {
587
- status: lastOutput.status,
588
- steps: stepResults
589
- };
590
- if (lastOutput.status === "success") {
591
- await emitter.emit("watch", {
592
- type: "watch",
593
- payload: {
594
- workflowState: {
595
- status: lastOutput.status,
596
- steps: stepResults,
597
- result: lastOutput.output
598
- }
599
- },
600
- eventTimestamp: Date.now()
601
- });
602
- base.result = lastOutput.output;
603
- } else if (lastOutput.status === "failed") {
604
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
605
- await emitter.emit("watch", {
606
- type: "watch",
607
- payload: {
608
- workflowState: {
609
- status: lastOutput.status,
610
- steps: stepResults,
611
- result: null,
612
- error: base.error
613
- }
614
- },
615
- eventTimestamp: Date.now()
616
- });
617
- } else if (lastOutput.status === "suspended") {
618
- await emitter.emit("watch", {
619
- type: "watch",
620
- payload: {
621
- workflowState: {
622
- status: lastOutput.status,
623
- steps: stepResults,
624
- result: null,
625
- error: null
626
- }
627
- },
628
- eventTimestamp: Date.now()
629
- });
630
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
631
- if (stepResult?.status === "suspended") {
632
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
633
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
634
- }
635
- return [];
636
- });
637
- base.suspended = suspendedStepIds;
638
- }
639
- executionSpan?.end();
640
- return base;
641
- }
642
- async superExecuteStep({
643
- workflowId,
644
- runId,
645
- step,
646
- stepResults,
647
- executionContext,
648
- resume,
649
- prevOutput,
650
- emitter,
651
- abortController,
652
- runtimeContext,
653
- writableStream
654
- }) {
655
- return super.executeStep({
656
- workflowId,
657
- runId,
658
- step,
659
- stepResults,
660
- executionContext,
661
- resume,
662
- prevOutput,
663
- emitter,
664
- abortController,
665
- runtimeContext,
666
- writableStream
667
- });
668
- }
669
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
670
- // await this.inngestStep.sleep(id, duration);
671
- // }
672
- async executeSleep({
673
- workflowId,
674
- runId,
675
- entry,
676
- prevOutput,
677
- stepResults,
678
- emitter,
679
- abortController,
680
- runtimeContext,
681
- writableStream
682
- }) {
683
- let { duration, fn } = entry;
684
- if (fn) {
685
- const stepCallId = randomUUID();
686
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
687
- return await fn({
688
- runId,
689
- workflowId,
690
- mastra: this.mastra,
691
- runtimeContext,
692
- inputData: prevOutput,
693
- runCount: -1,
694
- getInitData: () => stepResults?.input,
695
- getStepResult: (step) => {
696
- if (!step?.id) {
697
- return null;
698
- }
699
- const result = stepResults[step.id];
700
- if (result?.status === "success") {
701
- return result.output;
702
- }
703
- return null;
704
- },
705
- // TODO: this function shouldn't have suspend probably?
706
- suspend: async (_suspendPayload) => {
707
- },
708
- bail: () => {
709
- },
710
- abort: () => {
711
- abortController?.abort();
712
- },
713
- [EMITTER_SYMBOL]: emitter,
714
- engine: { step: this.inngestStep },
715
- abortSignal: abortController?.signal,
716
- writer: new ToolStream(
717
- {
718
- prefix: "step",
719
- callId: stepCallId,
720
- name: "sleep",
721
- runId
722
- },
723
- writableStream
724
- )
725
- });
726
- });
727
- }
728
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
729
- }
730
- async executeSleepUntil({
731
- workflowId,
732
- runId,
733
- entry,
734
- prevOutput,
735
- stepResults,
736
- emitter,
737
- abortController,
738
- runtimeContext,
739
- writableStream
740
- }) {
741
- let { date, fn } = entry;
742
- if (fn) {
743
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
744
- const stepCallId = randomUUID();
745
- return await fn({
746
- runId,
747
- workflowId,
748
- mastra: this.mastra,
749
- runtimeContext,
750
- inputData: prevOutput,
751
- runCount: -1,
752
- getInitData: () => stepResults?.input,
753
- getStepResult: (step) => {
754
- if (!step?.id) {
755
- return null;
756
- }
757
- const result = stepResults[step.id];
758
- if (result?.status === "success") {
759
- return result.output;
760
- }
761
- return null;
762
- },
763
- // TODO: this function shouldn't have suspend probably?
764
- suspend: async (_suspendPayload) => {
765
- },
766
- bail: () => {
767
- },
768
- abort: () => {
769
- abortController?.abort();
770
- },
771
- [EMITTER_SYMBOL]: emitter,
772
- engine: { step: this.inngestStep },
773
- abortSignal: abortController?.signal,
774
- writer: new ToolStream(
775
- {
776
- prefix: "step",
777
- callId: stepCallId,
778
- name: "sleep",
779
- runId
780
- },
781
- writableStream
782
- )
783
- });
784
- });
785
- }
786
- if (!(date instanceof Date)) {
787
- return;
788
- }
789
- await this.inngestStep.sleepUntil(entry.id, date);
790
- }
791
- async executeWaitForEvent({ event, timeout }) {
792
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
793
- event: `user-event-${event}`,
794
- timeout: timeout ?? 5e3
795
- });
796
- if (eventData === null) {
797
- throw "Timeout waiting for event";
798
- }
799
- return eventData?.data;
800
- }
801
- async executeStep({
802
- step,
803
- stepResults,
804
- executionContext,
805
- resume,
806
- prevOutput,
807
- emitter,
808
- abortController,
809
- runtimeContext,
810
- writableStream
811
- }) {
812
- const startedAt = await this.inngestStep.run(
813
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
814
- async () => {
815
- const startedAt2 = Date.now();
816
- await emitter.emit("watch", {
817
- type: "watch",
818
- payload: {
819
- currentStep: {
820
- id: step.id,
821
- status: "running"
822
- },
823
- workflowState: {
824
- status: "running",
825
- steps: {
826
- ...stepResults,
827
- [step.id]: {
828
- status: "running"
829
- }
830
- },
831
- result: null,
832
- error: null
833
- }
834
- },
835
- eventTimestamp: Date.now()
836
- });
837
- await emitter.emit("watch-v2", {
838
- type: "step-start",
839
- payload: {
840
- id: step.id,
841
- status: "running",
842
- payload: prevOutput,
843
- startedAt: startedAt2
844
- }
845
- });
846
- return startedAt2;
847
- }
848
- );
849
- if (step instanceof InngestWorkflow) {
850
- const isResume = !!resume?.steps?.length;
851
- let result;
852
- let runId;
853
- if (isResume) {
854
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
855
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
856
- workflowName: step.id,
857
- runId
858
- });
859
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
860
- function: step.getFunction(),
861
- data: {
862
- inputData: prevOutput,
863
- runId,
864
- resume: {
865
- runId,
866
- steps: resume.steps.slice(1),
867
- stepResults: snapshot?.context,
868
- resumePayload: resume.resumePayload,
869
- // @ts-ignore
870
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
871
- }
872
- }
873
- });
874
- result = invokeResp.result;
875
- runId = invokeResp.runId;
876
- } else {
877
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
878
- function: step.getFunction(),
879
- data: {
880
- inputData: prevOutput
881
- }
882
- });
883
- result = invokeResp.result;
884
- runId = invokeResp.runId;
885
- }
886
- const res = await this.inngestStep.run(
887
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
888
- async () => {
889
- if (result.status === "failed") {
890
- await emitter.emit("watch", {
891
- type: "watch",
892
- payload: {
893
- currentStep: {
894
- id: step.id,
895
- status: "failed",
896
- error: result?.error
897
- },
898
- workflowState: {
899
- status: "running",
900
- steps: stepResults,
901
- result: null,
902
- error: null
903
- }
904
- },
905
- eventTimestamp: Date.now()
906
- });
907
- await emitter.emit("watch-v2", {
908
- type: "step-result",
909
- payload: {
910
- id: step.id,
911
- status: "failed",
912
- error: result?.error,
913
- payload: prevOutput
914
- }
915
- });
916
- return { executionContext, result: { status: "failed", error: result?.error } };
917
- } else if (result.status === "suspended") {
918
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
919
- const stepRes2 = stepResult;
920
- return stepRes2?.status === "suspended";
921
- });
922
- for (const [stepName, stepResult] of suspendedSteps) {
923
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
924
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
925
- await emitter.emit("watch", {
926
- type: "watch",
927
- payload: {
928
- currentStep: {
929
- id: step.id,
930
- status: "suspended",
931
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
932
- },
933
- workflowState: {
934
- status: "running",
935
- steps: stepResults,
936
- result: null,
937
- error: null
938
- }
939
- },
940
- eventTimestamp: Date.now()
941
- });
942
- await emitter.emit("watch-v2", {
943
- type: "step-suspended",
944
- payload: {
945
- id: step.id,
946
- status: "suspended"
947
- }
948
- });
949
- return {
950
- executionContext,
951
- result: {
952
- status: "suspended",
953
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
954
- }
955
- };
956
- }
957
- await emitter.emit("watch", {
958
- type: "watch",
959
- payload: {
960
- currentStep: {
961
- id: step.id,
962
- status: "suspended",
963
- payload: {}
964
- },
965
- workflowState: {
966
- status: "running",
967
- steps: stepResults,
968
- result: null,
969
- error: null
970
- }
971
- },
972
- eventTimestamp: Date.now()
973
- });
974
- return {
975
- executionContext,
976
- result: {
977
- status: "suspended",
978
- payload: {}
979
- }
980
- };
981
- }
982
- await emitter.emit("watch", {
983
- type: "watch",
984
- payload: {
985
- currentStep: {
986
- id: step.id,
987
- status: "success",
988
- output: result?.result
989
- },
990
- workflowState: {
991
- status: "running",
992
- steps: stepResults,
993
- result: null,
994
- error: null
995
- }
996
- },
997
- eventTimestamp: Date.now()
998
- });
999
- await emitter.emit("watch-v2", {
1000
- type: "step-result",
1001
- payload: {
1002
- id: step.id,
1003
- status: "success",
1004
- output: result?.result
1005
- }
1006
- });
1007
- await emitter.emit("watch-v2", {
1008
- type: "step-finish",
1009
- payload: {
1010
- id: step.id,
1011
- metadata: {}
1012
- }
1013
- });
1014
- return { executionContext, result: { status: "success", output: result?.result } };
1015
- }
1016
- );
1017
- Object.assign(executionContext, res.executionContext);
1018
- return res.result;
1019
- }
1020
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1021
- let execResults;
1022
- let suspended;
1023
- let bailed;
1024
- try {
1025
- const result = await step.execute({
1026
- runId: executionContext.runId,
1027
- mastra: this.mastra,
1028
- runtimeContext,
1029
- writableStream,
1030
- inputData: prevOutput,
1031
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1032
- getInitData: () => stepResults?.input,
1033
- getStepResult: (step2) => {
1034
- const result2 = stepResults[step2.id];
1035
- if (result2?.status === "success") {
1036
- return result2.output;
1037
- }
1038
- return null;
1039
- },
1040
- suspend: async (suspendPayload) => {
1041
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1042
- suspended = { payload: suspendPayload };
1043
- },
1044
- bail: (result2) => {
1045
- bailed = { payload: result2 };
1046
- },
1047
- resume: {
1048
- steps: resume?.steps?.slice(1) || [],
1049
- resumePayload: resume?.resumePayload,
1050
- // @ts-ignore
1051
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1052
- },
1053
- [EMITTER_SYMBOL]: emitter,
1054
- engine: {
1055
- step: this.inngestStep
1056
- },
1057
- abortSignal: abortController.signal
1058
- });
1059
- const endedAt = Date.now();
1060
- execResults = {
1061
- status: "success",
1062
- output: result,
1063
- startedAt,
1064
- endedAt,
1065
- payload: prevOutput,
1066
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1067
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1068
- };
1069
- } catch (e) {
1070
- execResults = {
1071
- status: "failed",
1072
- payload: prevOutput,
1073
- error: e instanceof Error ? e.message : String(e),
1074
- endedAt: Date.now(),
1075
- startedAt,
1076
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1077
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1078
- };
1079
- }
1080
- if (suspended) {
1081
- execResults = {
1082
- status: "suspended",
1083
- suspendedPayload: suspended.payload,
1084
- payload: prevOutput,
1085
- suspendedAt: Date.now(),
1086
- startedAt,
1087
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1088
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1089
- };
1090
- } else if (bailed) {
1091
- execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
1092
- }
1093
- if (execResults.status === "failed") {
1094
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1095
- throw execResults.error;
1096
- }
1097
- }
1098
- await emitter.emit("watch", {
1099
- type: "watch",
1100
- payload: {
1101
- currentStep: {
1102
- id: step.id,
1103
- ...execResults
1104
- },
1105
- workflowState: {
1106
- status: "running",
1107
- steps: { ...stepResults, [step.id]: execResults },
1108
- result: null,
1109
- error: null
1110
- }
1111
- },
1112
- eventTimestamp: Date.now()
1113
- });
1114
- if (execResults.status === "suspended") {
1115
- await emitter.emit("watch-v2", {
1116
- type: "step-suspended",
1117
- payload: {
1118
- id: step.id,
1119
- ...execResults
1120
- }
1121
- });
1122
- } else {
1123
- await emitter.emit("watch-v2", {
1124
- type: "step-result",
1125
- payload: {
1126
- id: step.id,
1127
- ...execResults
1128
- }
1129
- });
1130
- await emitter.emit("watch-v2", {
1131
- type: "step-finish",
1132
- payload: {
1133
- id: step.id,
1134
- metadata: {}
1135
- }
1136
- });
1137
- }
1138
- return { result: execResults, executionContext, stepResults };
1139
- });
1140
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1141
- Object.assign(stepResults, stepRes.stepResults);
1142
- return stepRes.result;
1143
- }
1144
- async persistStepUpdate({
1145
- workflowId,
1146
- runId,
1147
- stepResults,
1148
- executionContext,
1149
- serializedStepGraph,
1150
- workflowStatus,
1151
- result,
1152
- error
1153
- }) {
1154
- await this.inngestStep.run(
1155
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1156
- async () => {
1157
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1158
- workflowName: workflowId,
1159
- runId,
1160
- snapshot: {
1161
- runId,
1162
- value: {},
1163
- context: stepResults,
1164
- activePaths: [],
1165
- suspendedPaths: executionContext.suspendedPaths,
1166
- serializedStepGraph,
1167
- status: workflowStatus,
1168
- result,
1169
- error,
1170
- // @ts-ignore
1171
- timestamp: Date.now()
1172
- }
1173
- });
1174
- }
1175
- );
1176
- }
1177
- async executeConditional({
1178
- workflowId,
1179
- runId,
1180
- entry,
1181
- prevOutput,
1182
- prevStep,
1183
- stepResults,
1184
- serializedStepGraph,
1185
- resume,
1186
- executionContext,
1187
- emitter,
1188
- abortController,
1189
- runtimeContext,
1190
- writableStream
1191
- }) {
1192
- let execResults;
1193
- const truthyIndexes = (await Promise.all(
1194
- entry.conditions.map(
1195
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1196
- try {
1197
- const result = await cond({
1198
- runId,
1199
- workflowId,
1200
- mastra: this.mastra,
1201
- runtimeContext,
1202
- runCount: -1,
1203
- inputData: prevOutput,
1204
- getInitData: () => stepResults?.input,
1205
- getStepResult: (step) => {
1206
- if (!step?.id) {
1207
- return null;
1208
- }
1209
- const result2 = stepResults[step.id];
1210
- if (result2?.status === "success") {
1211
- return result2.output;
1212
- }
1213
- return null;
1214
- },
1215
- // TODO: this function shouldn't have suspend probably?
1216
- suspend: async (_suspendPayload) => {
1217
- },
1218
- bail: () => {
1219
- },
1220
- abort: () => {
1221
- abortController.abort();
1222
- },
1223
- [EMITTER_SYMBOL]: emitter,
1224
- engine: {
1225
- step: this.inngestStep
1226
- },
1227
- abortSignal: abortController.signal,
1228
- writer: new ToolStream(
1229
- {
1230
- prefix: "step",
1231
- callId: randomUUID(),
1232
- name: "conditional",
1233
- runId
1234
- },
1235
- writableStream
1236
- )
1237
- });
1238
- return result ? index : null;
1239
- } catch (e) {
1240
- return null;
1241
- }
1242
- })
1243
- )
1244
- )).filter((index) => index !== null);
1245
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1246
- const results = await Promise.all(
1247
- stepsToRun.map(
1248
- (step, index) => this.executeEntry({
1249
- workflowId,
1250
- runId,
1251
- entry: step,
1252
- prevStep,
1253
- stepResults,
1254
- resume,
1255
- serializedStepGraph,
1256
- executionContext: {
1257
- workflowId,
1258
- runId,
1259
- executionPath: [...executionContext.executionPath, index],
1260
- suspendedPaths: executionContext.suspendedPaths,
1261
- retryConfig: executionContext.retryConfig,
1262
- executionSpan: executionContext.executionSpan
1263
- },
1264
- emitter,
1265
- abortController,
1266
- runtimeContext,
1267
- writableStream
1268
- })
1269
- )
1270
- );
1271
- const hasFailed = results.find((result) => result.result.status === "failed");
1272
- const hasSuspended = results.find((result) => result.result.status === "suspended");
1273
- if (hasFailed) {
1274
- execResults = { status: "failed", error: hasFailed.result.error };
1275
- } else if (hasSuspended) {
1276
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1277
- } else {
1278
- execResults = {
1279
- status: "success",
1280
- output: results.reduce((acc, result, index) => {
1281
- if (result.result.status === "success") {
1282
- acc[stepsToRun[index].step.id] = result.output;
1283
- }
1284
- return acc;
1285
- }, {})
1286
- };
1287
- }
1288
- return execResults;
1289
- }
1290
- };
1291
1238
 
1292
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1239
+ export { InngestExecutionEngine, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
1240
+ //# sourceMappingURL=index.js.map
1241
+ //# sourceMappingURL=index.js.map