@mastra/inngest 0.0.0-toolOptionTypes-20250917085558 → 0.0.0-top-level-fix-20251211111608

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