@mastra/inngest 0.0.0-new-scorer-api-20250801075530 → 0.0.0-new-button-export-20251219133013

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