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