@mastra/inngest 0.0.0-netlify-no-bundle-20251127120354 → 0.0.0-new-button-export-20251219130424

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,41 +1,473 @@
1
+ import { Tool } from '@mastra/core/tools';
2
+ import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, hydrateSerializedStepErrors, Workflow } from '@mastra/core/workflows';
3
+ import { PUBSUB_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
4
+ import { z } from 'zod';
1
5
  import { randomUUID } from 'crypto';
2
- import { ReadableStream, WritableStream } from 'stream/web';
3
- import { subscribe } from '@inngest/realtime';
4
6
  import { RequestContext } from '@mastra/core/di';
5
- import { SpanType } from '@mastra/core/observability';
7
+ import { RetryAfterError, NonRetriableError } from 'inngest';
8
+ import { getErrorFromUnknown } from '@mastra/core/error';
9
+ import { subscribe } from '@inngest/realtime';
10
+ import { PubSub } from '@mastra/core/events';
11
+ import { ReadableStream } from 'stream/web';
6
12
  import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
7
- import { ToolStream, Tool } from '@mastra/core/tools';
8
- import { Run, createTimeTravelExecutionParams, Workflow, DefaultExecutionEngine, createDeprecationProxy, getStepResult, runCountDeprecationMessage, validateStepInput, validateStepResumeData, validateStepSuspendData } from '@mastra/core/workflows';
9
- import { EMITTER_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
10
- import { NonRetriableError, RetryAfterError } from 'inngest';
11
13
  import { serve as serve$1 } from 'inngest/hono';
12
- import { z } from 'zod';
13
14
 
14
15
  // src/index.ts
15
- function serve({
16
- mastra,
17
- inngest,
18
- functions: userFunctions = [],
19
- registerOptions
20
- }) {
21
- const wfs = mastra.listWorkflows();
22
- const workflowFunctions = Array.from(
23
- new Set(
24
- Object.values(wfs).flatMap((wf) => {
25
- if (wf instanceof InngestWorkflow) {
26
- wf.__registerMastra(mastra);
27
- return wf.getFunctions();
16
+ var InngestExecutionEngine = class extends DefaultExecutionEngine {
17
+ inngestStep;
18
+ inngestAttempts;
19
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
20
+ super({ mastra, options });
21
+ this.inngestStep = inngestStep;
22
+ this.inngestAttempts = inngestAttempts;
23
+ }
24
+ // =============================================================================
25
+ // Hook Overrides
26
+ // =============================================================================
27
+ /**
28
+ * Format errors while preserving Error instances and their custom properties.
29
+ * Uses getErrorFromUnknown to ensure all error properties are preserved.
30
+ */
31
+ formatResultError(error, lastOutput) {
32
+ const outputError = lastOutput?.error;
33
+ const errorSource = error || outputError;
34
+ const errorInstance = getErrorFromUnknown(errorSource, {
35
+ serializeStack: true,
36
+ // Include stack in JSON for better debugging in Inngest
37
+ fallbackMessage: "Unknown workflow error"
38
+ });
39
+ return errorInstance.toJSON();
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
+ if (cause.error && !(cause.error instanceof Error)) {
72
+ cause.error = getErrorFromUnknown(cause.error, { serializeStack: false });
28
73
  }
29
- return [];
30
- })
31
- )
32
- );
33
- return serve$1({
34
- ...registerOptions,
35
- client: inngest,
36
- functions: [...workflowFunctions, ...userFunctions]
37
- });
38
- }
74
+ return { ok: false, error: cause };
75
+ }
76
+ const errorInstance = getErrorFromUnknown(e, {
77
+ serializeStack: false,
78
+ fallbackMessage: "Unknown step execution error"
79
+ });
80
+ params.stepSpan?.error({
81
+ error: errorInstance,
82
+ attributes: { status: "failed" }
83
+ });
84
+ return {
85
+ ok: false,
86
+ error: {
87
+ status: "failed",
88
+ error: errorInstance,
89
+ endedAt: Date.now()
90
+ }
91
+ };
92
+ }
93
+ }
94
+ /**
95
+ * Use Inngest's sleep primitive for durability
96
+ */
97
+ async executeSleepDuration(duration, sleepId, workflowId) {
98
+ await this.inngestStep.sleep(`workflow.${workflowId}.sleep.${sleepId}`, duration < 0 ? 0 : duration);
99
+ }
100
+ /**
101
+ * Use Inngest's sleepUntil primitive for durability
102
+ */
103
+ async executeSleepUntilDate(date, sleepUntilId, workflowId) {
104
+ await this.inngestStep.sleepUntil(`workflow.${workflowId}.sleepUntil.${sleepUntilId}`, date);
105
+ }
106
+ /**
107
+ * Wrap durable operations in Inngest step.run() for durability.
108
+ * If retryConfig is provided, throws RetryAfterError INSIDE step.run() to trigger
109
+ * Inngest's step-level retry mechanism (not function-level retry).
110
+ */
111
+ async wrapDurableOperation(operationId, operationFn, retryConfig) {
112
+ return this.inngestStep.run(operationId, async () => {
113
+ try {
114
+ return await operationFn();
115
+ } catch (e) {
116
+ if (retryConfig) {
117
+ const errorInstance = getErrorFromUnknown(e, {
118
+ serializeStack: false,
119
+ fallbackMessage: "Unknown step execution error"
120
+ });
121
+ throw new RetryAfterError(errorInstance.message, retryConfig.delay, {
122
+ cause: {
123
+ status: "failed",
124
+ error: errorInstance,
125
+ endedAt: Date.now()
126
+ }
127
+ });
128
+ }
129
+ throw e;
130
+ }
131
+ });
132
+ }
133
+ /**
134
+ * Provide Inngest step primitive in engine context
135
+ */
136
+ getEngineContext() {
137
+ return { step: this.inngestStep };
138
+ }
139
+ /**
140
+ * For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
141
+ * (wrapped in step.run for durability), not in execute(). Override to skip.
142
+ */
143
+ async invokeLifecycleCallbacks(_result) {
144
+ }
145
+ /**
146
+ * Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
147
+ */
148
+ async invokeLifecycleCallbacksInternal(result) {
149
+ return super.invokeLifecycleCallbacks(result);
150
+ }
151
+ /**
152
+ * Execute nested InngestWorkflow using inngestStep.invoke() for durability.
153
+ * This MUST be called directly (not inside step.run()) due to Inngest constraints.
154
+ */
155
+ async executeWorkflowStep(params) {
156
+ if (!(params.step instanceof InngestWorkflow)) {
157
+ return null;
158
+ }
159
+ const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData, pubsub, startedAt } = params;
160
+ const isResume = !!resume?.steps?.length;
161
+ let result;
162
+ let runId;
163
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
164
+ try {
165
+ if (isResume) {
166
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
167
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
168
+ workflowName: step.id,
169
+ runId
170
+ });
171
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
172
+ function: step.getFunction(),
173
+ data: {
174
+ inputData,
175
+ initialState: executionContext.state ?? snapshot?.value ?? {},
176
+ runId,
177
+ resume: {
178
+ runId,
179
+ steps: resume.steps.slice(1),
180
+ stepResults: snapshot?.context,
181
+ resumePayload: resume.resumePayload,
182
+ resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
183
+ },
184
+ outputOptions: { includeState: true }
185
+ }
186
+ });
187
+ result = invokeResp.result;
188
+ runId = invokeResp.runId;
189
+ executionContext.state = invokeResp.result.state;
190
+ } else if (isTimeTravel) {
191
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
192
+ workflowName: step.id,
193
+ runId: executionContext.runId
194
+ }) ?? { context: {} };
195
+ const timeTravelParams = createTimeTravelExecutionParams({
196
+ steps: timeTravel.steps.slice(1),
197
+ inputData: timeTravel.inputData,
198
+ resumeData: timeTravel.resumeData,
199
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
200
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
201
+ snapshot,
202
+ graph: step.buildExecutionGraph()
203
+ });
204
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
205
+ function: step.getFunction(),
206
+ data: {
207
+ timeTravel: timeTravelParams,
208
+ initialState: executionContext.state ?? {},
209
+ runId: executionContext.runId,
210
+ outputOptions: { includeState: true }
211
+ }
212
+ });
213
+ result = invokeResp.result;
214
+ runId = invokeResp.runId;
215
+ executionContext.state = invokeResp.result.state;
216
+ } else {
217
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
218
+ function: step.getFunction(),
219
+ data: {
220
+ inputData,
221
+ initialState: executionContext.state ?? {},
222
+ outputOptions: { includeState: true }
223
+ }
224
+ });
225
+ result = invokeResp.result;
226
+ runId = invokeResp.runId;
227
+ executionContext.state = invokeResp.result.state;
228
+ }
229
+ } catch (e) {
230
+ const errorCause = e?.cause;
231
+ if (errorCause && typeof errorCause === "object") {
232
+ result = errorCause;
233
+ runId = errorCause.runId || randomUUID();
234
+ } else {
235
+ runId = randomUUID();
236
+ result = {
237
+ status: "failed",
238
+ error: e instanceof Error ? e : new Error(String(e)),
239
+ steps: {},
240
+ input: inputData
241
+ };
242
+ }
243
+ }
244
+ const res = await this.inngestStep.run(
245
+ `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
246
+ async () => {
247
+ if (result.status === "failed") {
248
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
249
+ type: "watch",
250
+ runId: executionContext.runId,
251
+ data: {
252
+ type: "workflow-step-result",
253
+ payload: {
254
+ id: step.id,
255
+ status: "failed",
256
+ error: result?.error,
257
+ payload: prevOutput
258
+ }
259
+ }
260
+ });
261
+ return { executionContext, result: { status: "failed", error: result?.error } };
262
+ } else if (result.status === "suspended") {
263
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
264
+ const stepRes = stepResult;
265
+ return stepRes?.status === "suspended";
266
+ });
267
+ for (const [stepName, stepResult] of suspendedSteps) {
268
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
269
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
270
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
271
+ type: "watch",
272
+ runId: executionContext.runId,
273
+ data: {
274
+ type: "workflow-step-suspended",
275
+ payload: {
276
+ id: step.id,
277
+ status: "suspended"
278
+ }
279
+ }
280
+ });
281
+ return {
282
+ executionContext,
283
+ result: {
284
+ status: "suspended",
285
+ payload: stepResult.payload,
286
+ suspendPayload: {
287
+ ...stepResult?.suspendPayload,
288
+ __workflow_meta: { runId, path: suspendPath }
289
+ }
290
+ }
291
+ };
292
+ }
293
+ return {
294
+ executionContext,
295
+ result: {
296
+ status: "suspended",
297
+ payload: {}
298
+ }
299
+ };
300
+ } else if (result.status === "tripwire") {
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: "tripwire",
309
+ error: result?.tripwire?.reason,
310
+ payload: prevOutput
311
+ }
312
+ }
313
+ });
314
+ return {
315
+ executionContext,
316
+ result: {
317
+ status: "tripwire",
318
+ tripwire: result?.tripwire
319
+ }
320
+ };
321
+ }
322
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
323
+ type: "watch",
324
+ runId: executionContext.runId,
325
+ data: {
326
+ type: "workflow-step-result",
327
+ payload: {
328
+ id: step.id,
329
+ status: "success",
330
+ output: result?.result
331
+ }
332
+ }
333
+ });
334
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
335
+ type: "watch",
336
+ runId: executionContext.runId,
337
+ data: {
338
+ type: "workflow-step-finish",
339
+ payload: {
340
+ id: step.id,
341
+ metadata: {}
342
+ }
343
+ }
344
+ });
345
+ return { executionContext, result: { status: "success", output: result?.result } };
346
+ }
347
+ );
348
+ Object.assign(executionContext, res.executionContext);
349
+ return {
350
+ ...res.result,
351
+ startedAt,
352
+ endedAt: Date.now(),
353
+ payload: inputData,
354
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
355
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
356
+ };
357
+ }
358
+ };
359
+ var InngestPubSub = class extends PubSub {
360
+ inngest;
361
+ workflowId;
362
+ publishFn;
363
+ subscriptions = /* @__PURE__ */ new Map();
364
+ constructor(inngest, workflowId, publishFn) {
365
+ super();
366
+ this.inngest = inngest;
367
+ this.workflowId = workflowId;
368
+ this.publishFn = publishFn;
369
+ }
370
+ /**
371
+ * Publish an event to Inngest's realtime system.
372
+ *
373
+ * Topic format: "workflow.events.v2.{runId}"
374
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
375
+ */
376
+ async publish(topic, event) {
377
+ if (!this.publishFn) {
378
+ return;
379
+ }
380
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
381
+ if (!match) {
382
+ return;
383
+ }
384
+ const runId = match[1];
385
+ try {
386
+ await this.publishFn({
387
+ channel: `workflow:${this.workflowId}:${runId}`,
388
+ topic: "watch",
389
+ data: event.data
390
+ });
391
+ } catch (err) {
392
+ console.error("InngestPubSub publish error:", err?.message ?? err);
393
+ }
394
+ }
395
+ /**
396
+ * Subscribe to events from Inngest's realtime system.
397
+ *
398
+ * Topic format: "workflow.events.v2.{runId}"
399
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
400
+ */
401
+ async subscribe(topic, cb) {
402
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
403
+ if (!match || !match[1]) {
404
+ return;
405
+ }
406
+ const runId = match[1];
407
+ if (this.subscriptions.has(topic)) {
408
+ this.subscriptions.get(topic).callbacks.add(cb);
409
+ return;
410
+ }
411
+ const callbacks = /* @__PURE__ */ new Set([cb]);
412
+ const channel = `workflow:${this.workflowId}:${runId}`;
413
+ const streamPromise = subscribe(
414
+ {
415
+ channel,
416
+ topics: ["watch"],
417
+ app: this.inngest
418
+ },
419
+ (message) => {
420
+ const event = {
421
+ id: crypto.randomUUID(),
422
+ type: "watch",
423
+ runId,
424
+ data: message.data,
425
+ createdAt: /* @__PURE__ */ new Date()
426
+ };
427
+ for (const callback of callbacks) {
428
+ callback(event);
429
+ }
430
+ }
431
+ );
432
+ this.subscriptions.set(topic, {
433
+ unsubscribe: () => {
434
+ streamPromise.then((stream) => stream.cancel()).catch((err) => {
435
+ console.error("InngestPubSub unsubscribe error:", err);
436
+ });
437
+ },
438
+ callbacks
439
+ });
440
+ }
441
+ /**
442
+ * Unsubscribe a callback from a topic.
443
+ * If no callbacks remain, the underlying Inngest subscription is cancelled.
444
+ */
445
+ async unsubscribe(topic, cb) {
446
+ const sub = this.subscriptions.get(topic);
447
+ if (!sub) {
448
+ return;
449
+ }
450
+ sub.callbacks.delete(cb);
451
+ if (sub.callbacks.size === 0) {
452
+ sub.unsubscribe();
453
+ this.subscriptions.delete(topic);
454
+ }
455
+ }
456
+ /**
457
+ * Flush any pending operations. No-op for Inngest.
458
+ */
459
+ async flush() {
460
+ }
461
+ /**
462
+ * Clean up all subscriptions during graceful shutdown.
463
+ */
464
+ async close() {
465
+ for (const [, sub] of this.subscriptions) {
466
+ sub.unsubscribe();
467
+ }
468
+ this.subscriptions.clear();
469
+ }
470
+ };
39
471
  var InngestRun = class extends Run {
40
472
  inngest;
41
473
  serializedStepGraph;
@@ -47,27 +479,77 @@ var InngestRun = class extends Run {
47
479
  this.#mastra = params.mastra;
48
480
  }
49
481
  async getRuns(eventId) {
50
- const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
51
- headers: {
52
- Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
482
+ const maxRetries = 3;
483
+ let lastError = null;
484
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
485
+ try {
486
+ const response = await fetch(
487
+ `${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
488
+ {
489
+ headers: {
490
+ Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
491
+ }
492
+ }
493
+ );
494
+ if (response.status === 429) {
495
+ const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
496
+ await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
497
+ continue;
498
+ }
499
+ if (!response.ok) {
500
+ throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
501
+ }
502
+ const text = await response.text();
503
+ if (!text) {
504
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
505
+ continue;
506
+ }
507
+ const json = JSON.parse(text);
508
+ return json.data;
509
+ } catch (error) {
510
+ lastError = error;
511
+ if (attempt < maxRetries - 1) {
512
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
513
+ }
53
514
  }
54
- });
55
- const json = await response.json();
56
- return json.data;
515
+ }
516
+ throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
57
517
  }
58
- async getRunOutput(eventId) {
59
- let runs = await this.getRuns(eventId);
518
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
519
+ const startTime = Date.now();
60
520
  const storage = this.#mastra?.getStorage();
61
- while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
62
- await new Promise((resolve) => setTimeout(resolve, 1e3));
63
- runs = await this.getRuns(eventId);
521
+ while (Date.now() - startTime < maxWaitMs) {
522
+ let runs;
523
+ try {
524
+ runs = await this.getRuns(eventId);
525
+ } catch (error) {
526
+ if (error instanceof NonRetriableError) {
527
+ throw error;
528
+ }
529
+ throw new NonRetriableError(
530
+ `Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
531
+ );
532
+ }
533
+ if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
534
+ return runs[0];
535
+ }
64
536
  if (runs?.[0]?.status === "Failed") {
65
537
  const snapshot = await storage?.loadWorkflowSnapshot({
66
538
  workflowName: this.workflowId,
67
539
  runId: this.runId
68
540
  });
541
+ if (snapshot?.context) {
542
+ snapshot.context = hydrateSerializedStepErrors(snapshot.context);
543
+ }
69
544
  return {
70
- output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
545
+ output: {
546
+ result: {
547
+ steps: snapshot?.context,
548
+ status: "failed",
549
+ // Get the original error from NonRetriableError's cause (which contains the workflow result)
550
+ error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
551
+ }
552
+ }
71
553
  };
72
554
  }
73
555
  if (runs?.[0]?.status === "Cancelled") {
@@ -77,8 +559,9 @@ var InngestRun = class extends Run {
77
559
  });
78
560
  return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
79
561
  }
562
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
80
563
  }
81
- return runs?.[0];
564
+ throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
82
565
  }
83
566
  async cancel() {
84
567
  const storage = this.#mastra?.getStorage();
@@ -108,12 +591,58 @@ var InngestRun = class extends Run {
108
591
  async start(params) {
109
592
  return this._start(params);
110
593
  }
594
+ /**
595
+ * Starts the workflow execution without waiting for completion (fire-and-forget).
596
+ * Returns immediately with the runId after sending the event to Inngest.
597
+ * The workflow executes independently in Inngest.
598
+ * Use this when you don't need to wait for the result or want to avoid polling failures.
599
+ */
600
+ async startAsync(params) {
601
+ await this.#mastra.getStorage()?.persistWorkflowSnapshot({
602
+ workflowName: this.workflowId,
603
+ runId: this.runId,
604
+ resourceId: this.resourceId,
605
+ snapshot: {
606
+ runId: this.runId,
607
+ serializedStepGraph: this.serializedStepGraph,
608
+ status: "running",
609
+ value: {},
610
+ context: {},
611
+ activePaths: [],
612
+ suspendedPaths: {},
613
+ activeStepsPath: {},
614
+ resumeLabels: {},
615
+ waitingPaths: {},
616
+ timestamp: Date.now()
617
+ }
618
+ });
619
+ const inputDataToUse = await this._validateInput(params.inputData);
620
+ const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
621
+ const eventOutput = await this.inngest.send({
622
+ name: `workflow.${this.workflowId}`,
623
+ data: {
624
+ inputData: inputDataToUse,
625
+ initialState: initialStateToUse,
626
+ runId: this.runId,
627
+ resourceId: this.resourceId,
628
+ outputOptions: params.outputOptions,
629
+ tracingOptions: params.tracingOptions,
630
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
631
+ }
632
+ });
633
+ const eventId = eventOutput.ids[0];
634
+ if (!eventId) {
635
+ throw new Error("Event ID is not set");
636
+ }
637
+ return { runId: this.runId };
638
+ }
111
639
  async _start({
112
640
  inputData,
113
641
  initialState,
114
642
  outputOptions,
115
643
  tracingOptions,
116
- format
644
+ format,
645
+ requestContext
117
646
  }) {
118
647
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
119
648
  workflowName: this.workflowId,
@@ -144,7 +673,8 @@ var InngestRun = class extends Run {
144
673
  resourceId: this.resourceId,
145
674
  outputOptions,
146
675
  tracingOptions,
147
- format
676
+ format,
677
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {}
148
678
  }
149
679
  });
150
680
  const eventId = eventOutput.ids[0];
@@ -153,9 +683,7 @@ var InngestRun = class extends Run {
153
683
  }
154
684
  const runOutput = await this.getRunOutput(eventId);
155
685
  const result = runOutput?.output?.result;
156
- if (result.status === "failed") {
157
- result.error = new Error(result.error);
158
- }
686
+ this.hydrateFailedResult(result);
159
687
  if (result.status !== "suspended") {
160
688
  this.cleanup?.();
161
689
  }
@@ -188,6 +716,9 @@ var InngestRun = class extends Run {
188
716
  });
189
717
  const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
190
718
  const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
719
+ const persistedRequestContext = snapshot?.requestContext ?? {};
720
+ const newRequestContext = params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {};
721
+ const mergedRequestContext = { ...persistedRequestContext, ...newRequestContext };
191
722
  const eventOutput = await this.inngest.send({
192
723
  name: `workflow.${this.workflowId}`,
193
724
  data: {
@@ -201,7 +732,8 @@ var InngestRun = class extends Run {
201
732
  stepResults: snapshot?.context,
202
733
  resumePayload: resumeDataToUse,
203
734
  resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
204
- }
735
+ },
736
+ requestContext: mergedRequestContext
205
737
  }
206
738
  });
207
739
  const eventId = eventOutput.ids[0];
@@ -210,9 +742,7 @@ var InngestRun = class extends Run {
210
742
  }
211
743
  const runOutput = await this.getRunOutput(eventId);
212
744
  const result = runOutput?.output?.result;
213
- if (result.status === "failed") {
214
- result.error = new Error(result.error);
215
- }
745
+ this.hydrateFailedResult(result);
216
746
  return result;
217
747
  }
218
748
  async timeTravel(params) {
@@ -292,7 +822,8 @@ var InngestRun = class extends Run {
292
822
  stepResults: timeTravelData.stepResults,
293
823
  timeTravel: timeTravelData,
294
824
  tracingOptions: params.tracingOptions,
295
- outputOptions: params.outputOptions
825
+ outputOptions: params.outputOptions,
826
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
296
827
  }
297
828
  });
298
829
  const eventId = eventOutput.ids[0];
@@ -301,9 +832,7 @@ var InngestRun = class extends Run {
301
832
  }
302
833
  const runOutput = await this.getRunOutput(eventId);
303
834
  const result = runOutput?.output?.result;
304
- if (result.status === "failed") {
305
- result.error = new Error(result.error);
306
- }
835
+ this.hydrateFailedResult(result);
307
836
  return result;
308
837
  }
309
838
  watch(cb) {
@@ -485,7 +1014,7 @@ var InngestRun = class extends Run {
485
1014
  self.closeStreamAction = async () => {
486
1015
  unwatch();
487
1016
  try {
488
- await controller.close();
1017
+ controller.close();
489
1018
  } catch (err) {
490
1019
  console.error("Error closing stream:", err);
491
1020
  }
@@ -524,7 +1053,21 @@ var InngestRun = class extends Run {
524
1053
  });
525
1054
  return this.streamOutput;
526
1055
  }
1056
+ /**
1057
+ * Hydrates errors in a failed workflow result back to proper Error instances.
1058
+ * This ensures error.cause chains and custom properties are preserved.
1059
+ */
1060
+ hydrateFailedResult(result) {
1061
+ if (result.status === "failed") {
1062
+ result.error = getErrorFromUnknown(result.error, { serializeStack: false });
1063
+ if (result.steps) {
1064
+ hydrateSerializedStepErrors(result.steps);
1065
+ }
1066
+ }
1067
+ }
527
1068
  };
1069
+
1070
+ // src/workflow.ts
528
1071
  var InngestWorkflow = class _InngestWorkflow extends Workflow {
529
1072
  #mastra;
530
1073
  inngest;
@@ -559,6 +1102,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
559
1102
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
560
1103
  }
561
1104
  __registerMastra(mastra) {
1105
+ super.__registerMastra(mastra);
562
1106
  this.#mastra = mastra;
563
1107
  this.executionEngine.__registerMastra(mastra);
564
1108
  const updateNested = (step) => {
@@ -600,7 +1144,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
600
1144
  workflowStatus: run.workflowRunStatus,
601
1145
  stepResults: {}
602
1146
  });
603
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
1147
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
1148
+ withNestedWorkflows: false
1149
+ });
604
1150
  if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
605
1151
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
606
1152
  workflowName: this.id,
@@ -645,28 +1191,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
645
1191
  return randomUUID();
646
1192
  });
647
1193
  }
648
- const emitter = {
649
- emit: async (event2, data) => {
650
- if (!publish) {
651
- return;
652
- }
653
- try {
654
- await publish({
655
- channel: `workflow:${this.id}:${runId}`,
656
- topic: event2,
657
- data
658
- });
659
- } catch (err) {
660
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
661
- }
662
- },
663
- on: (_event, _callback) => {
664
- },
665
- off: (_event, _callback) => {
666
- },
667
- once: (_event, _callback) => {
668
- }
669
- };
1194
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
670
1195
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
671
1196
  const result = await engine.execute({
672
1197
  workflowId: this.id,
@@ -676,24 +1201,29 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
676
1201
  serializedStepGraph: this.serializedStepGraph,
677
1202
  input: inputData,
678
1203
  initialState,
679
- emitter,
1204
+ pubsub,
680
1205
  retryConfig: this.retryConfig,
681
- requestContext: new RequestContext(),
682
- // TODO
1206
+ requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
683
1207
  resume,
684
1208
  timeTravel,
685
1209
  format,
686
1210
  abortController: new AbortController(),
687
1211
  // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
688
1212
  outputOptions,
689
- writableStream: new WritableStream({
690
- write(chunk) {
691
- void emitter.emit("watch", chunk).catch(() => {
1213
+ outputWriter: async (chunk) => {
1214
+ try {
1215
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1216
+ type: "watch",
1217
+ runId,
1218
+ data: chunk
692
1219
  });
1220
+ } catch (err) {
1221
+ this.logger.debug?.("Failed to publish watch event:", err);
693
1222
  }
694
- })
1223
+ }
695
1224
  });
696
1225
  await step.run(`workflow.${this.id}.finalize`, async () => {
1226
+ await engine.invokeLifecycleCallbacksInternal(result);
697
1227
  if (result.status === "failed") {
698
1228
  throw new NonRetriableError(`Workflow failed`, {
699
1229
  cause: result
@@ -723,14 +1253,50 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
723
1253
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
724
1254
  }
725
1255
  };
1256
+ function serve({
1257
+ mastra,
1258
+ inngest,
1259
+ functions: userFunctions = [],
1260
+ registerOptions
1261
+ }) {
1262
+ const wfs = mastra.listWorkflows();
1263
+ const workflowFunctions = Array.from(
1264
+ new Set(
1265
+ Object.values(wfs).flatMap((wf) => {
1266
+ if (wf instanceof InngestWorkflow) {
1267
+ wf.__registerMastra(mastra);
1268
+ return wf.getFunctions();
1269
+ }
1270
+ return [];
1271
+ })
1272
+ )
1273
+ );
1274
+ return serve$1({
1275
+ ...registerOptions,
1276
+ client: inngest,
1277
+ functions: [...workflowFunctions, ...userFunctions]
1278
+ });
1279
+ }
1280
+
1281
+ // src/types.ts
1282
+ var _compatibilityCheck = true;
1283
+
1284
+ // src/index.ts
726
1285
  function isAgent(params) {
727
1286
  return params?.component === "AGENT";
728
1287
  }
729
1288
  function isTool(params) {
730
1289
  return params instanceof Tool;
731
1290
  }
1291
+ function isInngestWorkflow(params) {
1292
+ return params instanceof InngestWorkflow;
1293
+ }
732
1294
  function createStep(params, agentOptions) {
1295
+ if (isInngestWorkflow(params)) {
1296
+ return params;
1297
+ }
733
1298
  if (isAgent(params)) {
1299
+ const outputSchema = agentOptions?.structuredOutput?.schema ?? z.object({ text: z.string() });
734
1300
  return {
735
1301
  id: params.name,
736
1302
  description: params.getDescription(),
@@ -739,12 +1305,11 @@ function createStep(params, agentOptions) {
739
1305
  // resourceId: z.string().optional(),
740
1306
  // threadId: z.string().optional(),
741
1307
  }),
742
- outputSchema: z.object({
743
- text: z.string()
744
- }),
1308
+ outputSchema,
745
1309
  execute: async ({
746
1310
  inputData,
747
- [EMITTER_SYMBOL]: emitter,
1311
+ runId,
1312
+ [PUBSUB_SYMBOL]: pubsub,
748
1313
  [STREAM_FORMAT_SYMBOL]: streamFormat,
749
1314
  requestContext,
750
1315
  tracingContext,
@@ -757,6 +1322,7 @@ function createStep(params, agentOptions) {
757
1322
  streamPromise.resolve = resolve;
758
1323
  streamPromise.reject = reject;
759
1324
  });
1325
+ let structuredResult = null;
760
1326
  const toolData = {
761
1327
  name: params.name,
762
1328
  args: inputData
@@ -770,6 +1336,10 @@ function createStep(params, agentOptions) {
770
1336
  requestContext,
771
1337
  tracingContext,
772
1338
  onFinish: (result) => {
1339
+ const resultWithObject = result;
1340
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1341
+ structuredResult = resultWithObject.object;
1342
+ }
773
1343
  streamPromise.resolve(result.text);
774
1344
  void agentOptions?.onFinish?.(result);
775
1345
  },
@@ -782,6 +1352,10 @@ function createStep(params, agentOptions) {
782
1352
  requestContext,
783
1353
  tracingContext,
784
1354
  onFinish: (result) => {
1355
+ const resultWithObject = result;
1356
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1357
+ structuredResult = resultWithObject.object;
1358
+ }
785
1359
  streamPromise.resolve(result.text);
786
1360
  void agentOptions?.onFinish?.(result);
787
1361
  },
@@ -790,22 +1364,24 @@ function createStep(params, agentOptions) {
790
1364
  stream = modelOutput.fullStream;
791
1365
  }
792
1366
  if (streamFormat === "legacy") {
793
- await emitter.emit("watch", {
794
- type: "tool-call-streaming-start",
795
- ...toolData ?? {}
1367
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1368
+ type: "watch",
1369
+ runId,
1370
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
796
1371
  });
797
1372
  for await (const chunk of stream) {
798
1373
  if (chunk.type === "text-delta") {
799
- await emitter.emit("watch", {
800
- type: "tool-call-delta",
801
- ...toolData ?? {},
802
- argsTextDelta: chunk.textDelta
1374
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1375
+ type: "watch",
1376
+ runId,
1377
+ data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
803
1378
  });
804
1379
  }
805
1380
  }
806
- await emitter.emit("watch", {
807
- type: "tool-call-streaming-finish",
808
- ...toolData ?? {}
1381
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1382
+ type: "watch",
1383
+ runId,
1384
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
809
1385
  });
810
1386
  } else {
811
1387
  for await (const chunk of stream) {
@@ -815,6 +1391,9 @@ function createStep(params, agentOptions) {
815
1391
  if (abortSignal.aborted) {
816
1392
  return abort();
817
1393
  }
1394
+ if (structuredResult !== null) {
1395
+ return structuredResult;
1396
+ }
818
1397
  return {
819
1398
  text: await streamPromise.promise
820
1399
  };
@@ -913,867 +1492,7 @@ function init(inngest) {
913
1492
  }
914
1493
  };
915
1494
  }
916
- var InngestExecutionEngine = class extends DefaultExecutionEngine {
917
- inngestStep;
918
- inngestAttempts;
919
- constructor(mastra, inngestStep, inngestAttempts = 0, options) {
920
- super({ mastra, options });
921
- this.inngestStep = inngestStep;
922
- this.inngestAttempts = inngestAttempts;
923
- }
924
- async fmtReturnValue(emitter, stepResults, lastOutput, error) {
925
- const base = {
926
- status: lastOutput.status,
927
- steps: stepResults
928
- };
929
- if (lastOutput.status === "success") {
930
- base.result = lastOutput.output;
931
- } else if (lastOutput.status === "failed") {
932
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
933
- } else if (lastOutput.status === "suspended") {
934
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
935
- if (stepResult?.status === "suspended") {
936
- const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
937
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
938
- }
939
- return [];
940
- });
941
- base.suspended = suspendedStepIds;
942
- }
943
- return base;
944
- }
945
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
946
- // await this.inngestStep.sleep(id, duration);
947
- // }
948
- async executeSleep({
949
- workflowId,
950
- runId,
951
- entry,
952
- prevOutput,
953
- stepResults,
954
- emitter,
955
- abortController,
956
- requestContext,
957
- executionContext,
958
- writableStream,
959
- tracingContext
960
- }) {
961
- let { duration, fn } = entry;
962
- const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
963
- type: SpanType.WORKFLOW_SLEEP,
964
- name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
965
- attributes: {
966
- durationMs: duration,
967
- sleepType: fn ? "dynamic" : "fixed"
968
- },
969
- tracingPolicy: this.options?.tracingPolicy
970
- });
971
- if (fn) {
972
- const stepCallId = randomUUID();
973
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
974
- return await fn(
975
- createDeprecationProxy(
976
- {
977
- runId,
978
- workflowId,
979
- mastra: this.mastra,
980
- requestContext,
981
- inputData: prevOutput,
982
- state: executionContext.state,
983
- setState: (state) => {
984
- executionContext.state = state;
985
- },
986
- retryCount: -1,
987
- tracingContext: {
988
- currentSpan: sleepSpan
989
- },
990
- getInitData: () => stepResults?.input,
991
- getStepResult: getStepResult.bind(this, stepResults),
992
- // TODO: this function shouldn't have suspend probably?
993
- suspend: async (_suspendPayload) => {
994
- },
995
- bail: () => {
996
- },
997
- abort: () => {
998
- abortController?.abort();
999
- },
1000
- [EMITTER_SYMBOL]: emitter,
1001
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1002
- engine: { step: this.inngestStep },
1003
- abortSignal: abortController?.signal,
1004
- writer: new ToolStream(
1005
- {
1006
- prefix: "workflow-step",
1007
- callId: stepCallId,
1008
- name: "sleep",
1009
- runId
1010
- },
1011
- writableStream
1012
- )
1013
- },
1014
- {
1015
- paramName: "runCount",
1016
- deprecationMessage: runCountDeprecationMessage,
1017
- logger: this.logger
1018
- }
1019
- )
1020
- );
1021
- });
1022
- sleepSpan?.update({
1023
- attributes: {
1024
- durationMs: duration
1025
- }
1026
- });
1027
- }
1028
- try {
1029
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
1030
- sleepSpan?.end();
1031
- } catch (e) {
1032
- sleepSpan?.error({ error: e });
1033
- throw e;
1034
- }
1035
- }
1036
- async executeSleepUntil({
1037
- workflowId,
1038
- runId,
1039
- entry,
1040
- prevOutput,
1041
- stepResults,
1042
- emitter,
1043
- abortController,
1044
- requestContext,
1045
- executionContext,
1046
- writableStream,
1047
- tracingContext
1048
- }) {
1049
- let { date, fn } = entry;
1050
- const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
1051
- type: SpanType.WORKFLOW_SLEEP,
1052
- name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
1053
- attributes: {
1054
- untilDate: date,
1055
- durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
1056
- sleepType: fn ? "dynamic" : "fixed"
1057
- },
1058
- tracingPolicy: this.options?.tracingPolicy
1059
- });
1060
- if (fn) {
1061
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
1062
- const stepCallId = randomUUID();
1063
- return await fn(
1064
- createDeprecationProxy(
1065
- {
1066
- runId,
1067
- workflowId,
1068
- mastra: this.mastra,
1069
- requestContext,
1070
- inputData: prevOutput,
1071
- state: executionContext.state,
1072
- setState: (state) => {
1073
- executionContext.state = state;
1074
- },
1075
- retryCount: -1,
1076
- tracingContext: {
1077
- currentSpan: sleepUntilSpan
1078
- },
1079
- getInitData: () => stepResults?.input,
1080
- getStepResult: getStepResult.bind(this, stepResults),
1081
- // TODO: this function shouldn't have suspend probably?
1082
- suspend: async (_suspendPayload) => {
1083
- },
1084
- bail: () => {
1085
- },
1086
- abort: () => {
1087
- abortController?.abort();
1088
- },
1089
- [EMITTER_SYMBOL]: emitter,
1090
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1091
- engine: { step: this.inngestStep },
1092
- abortSignal: abortController?.signal,
1093
- writer: new ToolStream(
1094
- {
1095
- prefix: "workflow-step",
1096
- callId: stepCallId,
1097
- name: "sleep",
1098
- runId
1099
- },
1100
- writableStream
1101
- )
1102
- },
1103
- {
1104
- paramName: "runCount",
1105
- deprecationMessage: runCountDeprecationMessage,
1106
- logger: this.logger
1107
- }
1108
- )
1109
- );
1110
- });
1111
- if (date && !(date instanceof Date)) {
1112
- date = new Date(date);
1113
- }
1114
- const time = !date ? 0 : date.getTime() - Date.now();
1115
- sleepUntilSpan?.update({
1116
- attributes: {
1117
- durationMs: Math.max(0, time)
1118
- }
1119
- });
1120
- }
1121
- if (!(date instanceof Date)) {
1122
- sleepUntilSpan?.end();
1123
- return;
1124
- }
1125
- try {
1126
- await this.inngestStep.sleepUntil(entry.id, date);
1127
- sleepUntilSpan?.end();
1128
- } catch (e) {
1129
- sleepUntilSpan?.error({ error: e });
1130
- throw e;
1131
- }
1132
- }
1133
- async executeStep({
1134
- step,
1135
- stepResults,
1136
- executionContext,
1137
- resume,
1138
- timeTravel,
1139
- prevOutput,
1140
- emitter,
1141
- abortController,
1142
- requestContext,
1143
- tracingContext,
1144
- writableStream,
1145
- disableScorers
1146
- }) {
1147
- const stepSpan = tracingContext?.currentSpan?.createChildSpan({
1148
- name: `workflow step: '${step.id}'`,
1149
- type: SpanType.WORKFLOW_STEP,
1150
- input: prevOutput,
1151
- attributes: {
1152
- stepId: step.id
1153
- },
1154
- tracingPolicy: this.options?.tracingPolicy
1155
- });
1156
- const { inputData, validationError } = await validateStepInput({
1157
- prevOutput,
1158
- step,
1159
- validateInputs: this.options?.validateInputs ?? true
1160
- });
1161
- const startedAt = await this.inngestStep.run(
1162
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
1163
- async () => {
1164
- const startedAt2 = Date.now();
1165
- await emitter.emit("watch", {
1166
- type: "workflow-step-start",
1167
- payload: {
1168
- id: step.id,
1169
- status: "running",
1170
- payload: inputData,
1171
- startedAt: startedAt2
1172
- }
1173
- });
1174
- return startedAt2;
1175
- }
1176
- );
1177
- if (step instanceof InngestWorkflow) {
1178
- const isResume = !!resume?.steps?.length;
1179
- let result;
1180
- let runId;
1181
- const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
1182
- try {
1183
- if (isResume) {
1184
- runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
1185
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1186
- workflowName: step.id,
1187
- runId
1188
- });
1189
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1190
- function: step.getFunction(),
1191
- data: {
1192
- inputData,
1193
- initialState: executionContext.state ?? snapshot?.value ?? {},
1194
- runId,
1195
- resume: {
1196
- runId,
1197
- steps: resume.steps.slice(1),
1198
- stepResults: snapshot?.context,
1199
- resumePayload: resume.resumePayload,
1200
- resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
1201
- },
1202
- outputOptions: { includeState: true }
1203
- }
1204
- });
1205
- result = invokeResp.result;
1206
- runId = invokeResp.runId;
1207
- executionContext.state = invokeResp.result.state;
1208
- } else if (isTimeTravel) {
1209
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1210
- workflowName: step.id,
1211
- runId: executionContext.runId
1212
- }) ?? { context: {} };
1213
- const timeTravelParams = createTimeTravelExecutionParams({
1214
- steps: timeTravel.steps.slice(1),
1215
- inputData: timeTravel.inputData,
1216
- resumeData: timeTravel.resumeData,
1217
- context: timeTravel.nestedStepResults?.[step.id] ?? {},
1218
- nestedStepsContext: timeTravel.nestedStepResults ?? {},
1219
- snapshot,
1220
- graph: step.buildExecutionGraph()
1221
- });
1222
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1223
- function: step.getFunction(),
1224
- data: {
1225
- timeTravel: timeTravelParams,
1226
- initialState: executionContext.state ?? {},
1227
- runId: executionContext.runId,
1228
- outputOptions: { includeState: true }
1229
- }
1230
- });
1231
- result = invokeResp.result;
1232
- runId = invokeResp.runId;
1233
- executionContext.state = invokeResp.result.state;
1234
- } else {
1235
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1236
- function: step.getFunction(),
1237
- data: {
1238
- inputData,
1239
- initialState: executionContext.state ?? {},
1240
- outputOptions: { includeState: true }
1241
- }
1242
- });
1243
- result = invokeResp.result;
1244
- runId = invokeResp.runId;
1245
- executionContext.state = invokeResp.result.state;
1246
- }
1247
- } catch (e) {
1248
- const errorCause = e?.cause;
1249
- if (errorCause && typeof errorCause === "object") {
1250
- result = errorCause;
1251
- runId = errorCause.runId || randomUUID();
1252
- } else {
1253
- runId = randomUUID();
1254
- result = {
1255
- status: "failed",
1256
- error: e instanceof Error ? e : new Error(String(e)),
1257
- steps: {},
1258
- input: inputData
1259
- };
1260
- }
1261
- }
1262
- const res = await this.inngestStep.run(
1263
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
1264
- async () => {
1265
- if (result.status === "failed") {
1266
- await emitter.emit("watch", {
1267
- type: "workflow-step-result",
1268
- payload: {
1269
- id: step.id,
1270
- status: "failed",
1271
- error: result?.error,
1272
- payload: prevOutput
1273
- }
1274
- });
1275
- return { executionContext, result: { status: "failed", error: result?.error } };
1276
- } else if (result.status === "suspended") {
1277
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
1278
- const stepRes2 = stepResult;
1279
- return stepRes2?.status === "suspended";
1280
- });
1281
- for (const [stepName, stepResult] of suspendedSteps) {
1282
- const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
1283
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1284
- await emitter.emit("watch", {
1285
- type: "workflow-step-suspended",
1286
- payload: {
1287
- id: step.id,
1288
- status: "suspended"
1289
- }
1290
- });
1291
- return {
1292
- executionContext,
1293
- result: {
1294
- status: "suspended",
1295
- payload: stepResult.payload,
1296
- suspendPayload: {
1297
- ...stepResult?.suspendPayload,
1298
- __workflow_meta: { runId, path: suspendPath }
1299
- }
1300
- }
1301
- };
1302
- }
1303
- return {
1304
- executionContext,
1305
- result: {
1306
- status: "suspended",
1307
- payload: {}
1308
- }
1309
- };
1310
- }
1311
- await emitter.emit("watch", {
1312
- type: "workflow-step-result",
1313
- payload: {
1314
- id: step.id,
1315
- status: "success",
1316
- output: result?.result
1317
- }
1318
- });
1319
- await emitter.emit("watch", {
1320
- type: "workflow-step-finish",
1321
- payload: {
1322
- id: step.id,
1323
- metadata: {}
1324
- }
1325
- });
1326
- return { executionContext, result: { status: "success", output: result?.result } };
1327
- }
1328
- );
1329
- Object.assign(executionContext, res.executionContext);
1330
- return {
1331
- ...res.result,
1332
- startedAt,
1333
- endedAt: Date.now(),
1334
- payload: inputData,
1335
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1336
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1337
- };
1338
- }
1339
- const stepCallId = randomUUID();
1340
- let stepRes;
1341
- try {
1342
- stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1343
- let execResults;
1344
- let suspended;
1345
- let bailed;
1346
- const { resumeData: timeTravelResumeData, validationError: timeTravelResumeValidationError } = await validateStepResumeData({
1347
- resumeData: timeTravel?.stepResults[step.id]?.status === "suspended" ? timeTravel?.resumeData : void 0,
1348
- step
1349
- });
1350
- let resumeDataToUse;
1351
- if (timeTravelResumeData && !timeTravelResumeValidationError) {
1352
- resumeDataToUse = timeTravelResumeData;
1353
- } else if (timeTravelResumeData && timeTravelResumeValidationError) {
1354
- this.logger.warn("Time travel resume data validation failed", {
1355
- stepId: step.id,
1356
- error: timeTravelResumeValidationError.message
1357
- });
1358
- } else if (resume?.steps[0] === step.id) {
1359
- resumeDataToUse = resume?.resumePayload;
1360
- }
1361
- try {
1362
- if (validationError) {
1363
- throw validationError;
1364
- }
1365
- const retryCount = this.getOrGenerateRetryCount(step.id);
1366
- const result = await step.execute({
1367
- runId: executionContext.runId,
1368
- workflowId: executionContext.workflowId,
1369
- mastra: this.mastra,
1370
- requestContext,
1371
- retryCount,
1372
- writer: new ToolStream(
1373
- {
1374
- prefix: "workflow-step",
1375
- callId: stepCallId,
1376
- name: step.id,
1377
- runId: executionContext.runId
1378
- },
1379
- writableStream
1380
- ),
1381
- state: executionContext?.state ?? {},
1382
- setState: (state) => {
1383
- executionContext.state = state;
1384
- },
1385
- inputData,
1386
- resumeData: resumeDataToUse,
1387
- tracingContext: {
1388
- currentSpan: stepSpan
1389
- },
1390
- getInitData: () => stepResults?.input,
1391
- getStepResult: getStepResult.bind(this, stepResults),
1392
- suspend: async (suspendPayload, suspendOptions) => {
1393
- const { suspendData, validationError: validationError2 } = await validateStepSuspendData({
1394
- suspendData: suspendPayload,
1395
- step
1396
- });
1397
- if (validationError2) {
1398
- throw validationError2;
1399
- }
1400
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1401
- if (suspendOptions?.resumeLabel) {
1402
- const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1403
- for (const label of resumeLabel) {
1404
- executionContext.resumeLabels[label] = {
1405
- stepId: step.id,
1406
- foreachIndex: executionContext.foreachIndex
1407
- };
1408
- }
1409
- }
1410
- suspended = { payload: suspendData };
1411
- },
1412
- bail: (result2) => {
1413
- bailed = { payload: result2 };
1414
- },
1415
- abort: () => {
1416
- abortController?.abort();
1417
- },
1418
- [EMITTER_SYMBOL]: emitter,
1419
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1420
- engine: {
1421
- step: this.inngestStep
1422
- },
1423
- abortSignal: abortController.signal
1424
- });
1425
- const endedAt = Date.now();
1426
- execResults = {
1427
- status: "success",
1428
- output: result,
1429
- startedAt,
1430
- endedAt,
1431
- payload: inputData,
1432
- resumedAt: resumeDataToUse ? startedAt : void 0,
1433
- resumePayload: resumeDataToUse
1434
- };
1435
- } catch (e) {
1436
- const stepFailure = {
1437
- status: "failed",
1438
- payload: inputData,
1439
- error: e instanceof Error ? e.message : String(e),
1440
- endedAt: Date.now(),
1441
- startedAt,
1442
- resumedAt: resumeDataToUse ? startedAt : void 0,
1443
- resumePayload: resumeDataToUse
1444
- };
1445
- execResults = stepFailure;
1446
- const fallbackErrorMessage = `Step ${step.id} failed`;
1447
- stepSpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1448
- throw new RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1449
- cause: execResults
1450
- });
1451
- }
1452
- if (suspended) {
1453
- execResults = {
1454
- status: "suspended",
1455
- suspendPayload: suspended.payload,
1456
- ...execResults.output ? { suspendOutput: execResults.output } : {},
1457
- payload: inputData,
1458
- suspendedAt: Date.now(),
1459
- startedAt,
1460
- resumedAt: resumeDataToUse ? startedAt : void 0,
1461
- resumePayload: resumeDataToUse
1462
- };
1463
- } else if (bailed) {
1464
- execResults = {
1465
- status: "bailed",
1466
- output: bailed.payload,
1467
- payload: inputData,
1468
- endedAt: Date.now(),
1469
- startedAt
1470
- };
1471
- }
1472
- if (execResults.status === "suspended") {
1473
- await emitter.emit("watch", {
1474
- type: "workflow-step-suspended",
1475
- payload: {
1476
- id: step.id,
1477
- ...execResults
1478
- }
1479
- });
1480
- } else {
1481
- await emitter.emit("watch", {
1482
- type: "workflow-step-result",
1483
- payload: {
1484
- id: step.id,
1485
- ...execResults
1486
- }
1487
- });
1488
- await emitter.emit("watch", {
1489
- type: "workflow-step-finish",
1490
- payload: {
1491
- id: step.id,
1492
- metadata: {}
1493
- }
1494
- });
1495
- }
1496
- stepSpan?.end({ output: execResults });
1497
- return { result: execResults, executionContext, stepResults };
1498
- });
1499
- } catch (e) {
1500
- const stepFailure = e instanceof Error ? e?.cause : {
1501
- status: "failed",
1502
- error: e instanceof Error ? e.message : String(e),
1503
- payload: inputData,
1504
- startedAt,
1505
- endedAt: Date.now()
1506
- };
1507
- await emitter.emit("watch", {
1508
- type: "workflow-step-result",
1509
- payload: {
1510
- id: step.id,
1511
- ...stepFailure
1512
- }
1513
- });
1514
- await emitter.emit("watch", {
1515
- type: "workflow-step-finish",
1516
- payload: {
1517
- id: step.id,
1518
- metadata: {}
1519
- }
1520
- });
1521
- stepRes = {
1522
- result: stepFailure,
1523
- executionContext,
1524
- stepResults: {
1525
- ...stepResults,
1526
- [step.id]: stepFailure
1527
- }
1528
- };
1529
- }
1530
- if (disableScorers !== false && stepRes.result.status === "success") {
1531
- await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1532
- if (step.scorers) {
1533
- await this.runScorers({
1534
- scorers: step.scorers,
1535
- runId: executionContext.runId,
1536
- input: inputData,
1537
- output: stepRes.result,
1538
- workflowId: executionContext.workflowId,
1539
- stepId: step.id,
1540
- requestContext,
1541
- disableScorers,
1542
- tracingContext: { currentSpan: stepSpan }
1543
- });
1544
- }
1545
- });
1546
- }
1547
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1548
- executionContext.state = stepRes.executionContext.state;
1549
- return stepRes.result;
1550
- }
1551
- async persistStepUpdate({
1552
- workflowId,
1553
- runId,
1554
- stepResults,
1555
- resourceId,
1556
- executionContext,
1557
- serializedStepGraph,
1558
- workflowStatus,
1559
- result,
1560
- error
1561
- }) {
1562
- await this.inngestStep.run(
1563
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1564
- async () => {
1565
- const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1566
- if (!shouldPersistSnapshot) {
1567
- return;
1568
- }
1569
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1570
- workflowName: workflowId,
1571
- runId,
1572
- resourceId,
1573
- snapshot: {
1574
- runId,
1575
- status: workflowStatus,
1576
- value: executionContext.state,
1577
- context: stepResults,
1578
- activePaths: executionContext.executionPath,
1579
- activeStepsPath: executionContext.activeStepsPath,
1580
- suspendedPaths: executionContext.suspendedPaths,
1581
- resumeLabels: executionContext.resumeLabels,
1582
- waitingPaths: {},
1583
- serializedStepGraph,
1584
- result,
1585
- error,
1586
- timestamp: Date.now()
1587
- }
1588
- });
1589
- }
1590
- );
1591
- }
1592
- async executeConditional({
1593
- workflowId,
1594
- runId,
1595
- entry,
1596
- prevOutput,
1597
- stepResults,
1598
- timeTravel,
1599
- resume,
1600
- executionContext,
1601
- emitter,
1602
- abortController,
1603
- requestContext,
1604
- writableStream,
1605
- disableScorers,
1606
- tracingContext
1607
- }) {
1608
- const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1609
- type: SpanType.WORKFLOW_CONDITIONAL,
1610
- name: `conditional: '${entry.conditions.length} conditions'`,
1611
- input: prevOutput,
1612
- attributes: {
1613
- conditionCount: entry.conditions.length
1614
- },
1615
- tracingPolicy: this.options?.tracingPolicy
1616
- });
1617
- let execResults;
1618
- const truthyIndexes = (await Promise.all(
1619
- entry.conditions.map(
1620
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1621
- const evalSpan = conditionalSpan?.createChildSpan({
1622
- type: SpanType.WORKFLOW_CONDITIONAL_EVAL,
1623
- name: `condition: '${index}'`,
1624
- input: prevOutput,
1625
- attributes: {
1626
- conditionIndex: index
1627
- },
1628
- tracingPolicy: this.options?.tracingPolicy
1629
- });
1630
- try {
1631
- const result = await cond(
1632
- createDeprecationProxy(
1633
- {
1634
- runId,
1635
- workflowId,
1636
- mastra: this.mastra,
1637
- requestContext,
1638
- retryCount: -1,
1639
- inputData: prevOutput,
1640
- state: executionContext.state,
1641
- setState: (state) => {
1642
- executionContext.state = state;
1643
- },
1644
- tracingContext: {
1645
- currentSpan: evalSpan
1646
- },
1647
- getInitData: () => stepResults?.input,
1648
- getStepResult: getStepResult.bind(this, stepResults),
1649
- // TODO: this function shouldn't have suspend probably?
1650
- suspend: async (_suspendPayload) => {
1651
- },
1652
- bail: () => {
1653
- },
1654
- abort: () => {
1655
- abortController.abort();
1656
- },
1657
- [EMITTER_SYMBOL]: emitter,
1658
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1659
- engine: {
1660
- step: this.inngestStep
1661
- },
1662
- abortSignal: abortController.signal,
1663
- writer: new ToolStream(
1664
- {
1665
- prefix: "workflow-step",
1666
- callId: randomUUID(),
1667
- name: "conditional",
1668
- runId
1669
- },
1670
- writableStream
1671
- )
1672
- },
1673
- {
1674
- paramName: "runCount",
1675
- deprecationMessage: runCountDeprecationMessage,
1676
- logger: this.logger
1677
- }
1678
- )
1679
- );
1680
- evalSpan?.end({
1681
- output: result,
1682
- attributes: {
1683
- result: !!result
1684
- }
1685
- });
1686
- return result ? index : null;
1687
- } catch (e) {
1688
- evalSpan?.error({
1689
- error: e instanceof Error ? e : new Error(String(e)),
1690
- attributes: {
1691
- result: false
1692
- }
1693
- });
1694
- return null;
1695
- }
1696
- })
1697
- )
1698
- )).filter((index) => index !== null);
1699
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1700
- conditionalSpan?.update({
1701
- attributes: {
1702
- truthyIndexes,
1703
- selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1704
- }
1705
- });
1706
- const results = await Promise.all(
1707
- stepsToRun.map(async (step, index) => {
1708
- const currStepResult = stepResults[step.step.id];
1709
- if (currStepResult && currStepResult.status === "success") {
1710
- return currStepResult;
1711
- }
1712
- const result = await this.executeStep({
1713
- step: step.step,
1714
- prevOutput,
1715
- stepResults,
1716
- resume,
1717
- timeTravel,
1718
- executionContext: {
1719
- workflowId,
1720
- runId,
1721
- executionPath: [...executionContext.executionPath, index],
1722
- activeStepsPath: executionContext.activeStepsPath,
1723
- suspendedPaths: executionContext.suspendedPaths,
1724
- resumeLabels: executionContext.resumeLabels,
1725
- retryConfig: executionContext.retryConfig,
1726
- state: executionContext.state
1727
- },
1728
- emitter,
1729
- abortController,
1730
- requestContext,
1731
- writableStream,
1732
- disableScorers,
1733
- tracingContext: {
1734
- currentSpan: conditionalSpan
1735
- }
1736
- });
1737
- stepResults[step.step.id] = result;
1738
- return result;
1739
- })
1740
- );
1741
- const hasFailed = results.find((result) => result.status === "failed");
1742
- const hasSuspended = results.find((result) => result.status === "suspended");
1743
- if (hasFailed) {
1744
- execResults = { status: "failed", error: hasFailed.error };
1745
- } else if (hasSuspended) {
1746
- execResults = {
1747
- status: "suspended",
1748
- suspendPayload: hasSuspended.suspendPayload,
1749
- ...hasSuspended.suspendOutput ? { suspendOutput: hasSuspended.suspendOutput } : {}
1750
- };
1751
- } else {
1752
- execResults = {
1753
- status: "success",
1754
- output: results.reduce((acc, result, index) => {
1755
- if (result.status === "success") {
1756
- if ("step" in stepsToRun[index]) {
1757
- acc[stepsToRun[index].step.id] = result.output;
1758
- }
1759
- }
1760
- return acc;
1761
- }, {})
1762
- };
1763
- }
1764
- if (execResults.status === "failed") {
1765
- conditionalSpan?.error({
1766
- error: new Error(execResults.error)
1767
- });
1768
- } else {
1769
- conditionalSpan?.end({
1770
- output: execResults.output || execResults
1771
- });
1772
- }
1773
- return execResults;
1774
- }
1775
- };
1776
1495
 
1777
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1496
+ export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
1778
1497
  //# sourceMappingURL=index.js.map
1779
1498
  //# sourceMappingURL=index.js.map