@mastra/inngest 0.0.0-fix-prompt-enhance-route-20251210210827 → 0.0.0-fix-11329-windows-path-20251222155941

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
@@ -4,11 +4,13 @@ var tools = require('@mastra/core/tools');
4
4
  var workflows = require('@mastra/core/workflows');
5
5
  var _constants = require('@mastra/core/workflows/_constants');
6
6
  var zod = require('zod');
7
- var crypto = require('crypto');
7
+ var crypto$1 = require('crypto');
8
8
  var di = require('@mastra/core/di');
9
9
  var inngest = require('inngest');
10
- var web = require('stream/web');
10
+ var error = require('@mastra/core/error');
11
11
  var realtime = require('@inngest/realtime');
12
+ var events = require('@mastra/core/events');
13
+ var web = require('stream/web');
12
14
  var stream = require('@mastra/core/stream');
13
15
  var hono = require('inngest/hono');
14
16
 
@@ -25,17 +27,18 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
25
27
  // Hook Overrides
26
28
  // =============================================================================
27
29
  /**
28
- * Format errors with stack traces for better debugging in Inngest
30
+ * Format errors while preserving Error instances and their custom properties.
31
+ * Uses getErrorFromUnknown to ensure all error properties are preserved.
29
32
  */
30
- formatResultError(error, lastOutput) {
31
- if (error instanceof Error) {
32
- return error.stack ?? error.message;
33
- }
33
+ formatResultError(error$1, lastOutput) {
34
34
  const outputError = lastOutput?.error;
35
- if (outputError instanceof Error) {
36
- return outputError.message;
37
- }
38
- return outputError ?? error ?? "Unknown 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();
39
42
  }
40
43
  /**
41
44
  * Detect InngestWorkflow instances for special nested workflow handling
@@ -67,18 +70,24 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
67
70
  error: e,
68
71
  attributes: { status: "failed" }
69
72
  });
73
+ if (cause.error && !(cause.error instanceof Error)) {
74
+ cause.error = error.getErrorFromUnknown(cause.error, { serializeStack: false });
75
+ }
70
76
  return { ok: false, error: cause };
71
77
  }
72
- const errorMessage = e instanceof Error ? e.message : String(e);
78
+ const errorInstance = error.getErrorFromUnknown(e, {
79
+ serializeStack: false,
80
+ fallbackMessage: "Unknown step execution error"
81
+ });
73
82
  params.stepSpan?.error({
74
- error: e,
83
+ error: errorInstance,
75
84
  attributes: { status: "failed" }
76
85
  });
77
86
  return {
78
87
  ok: false,
79
88
  error: {
80
89
  status: "failed",
81
- error: `Error: ${errorMessage}`,
90
+ error: errorInstance,
82
91
  endedAt: Date.now()
83
92
  }
84
93
  };
@@ -107,11 +116,14 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
107
116
  return await operationFn();
108
117
  } catch (e) {
109
118
  if (retryConfig) {
110
- const errorMessage = e instanceof Error ? e.message : String(e);
111
- throw new inngest.RetryAfterError(errorMessage, retryConfig.delay, {
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, {
112
124
  cause: {
113
125
  status: "failed",
114
- error: `Error: ${errorMessage}`,
126
+ error: errorInstance,
115
127
  endedAt: Date.now()
116
128
  }
117
129
  });
@@ -126,6 +138,18 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
126
138
  getEngineContext() {
127
139
  return { step: this.inngestStep };
128
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
+ }
129
153
  /**
130
154
  * Execute nested InngestWorkflow using inngestStep.invoke() for durability.
131
155
  * This MUST be called directly (not inside step.run()) due to Inngest constraints.
@@ -134,14 +158,25 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
134
158
  if (!(params.step instanceof InngestWorkflow)) {
135
159
  return null;
136
160
  }
137
- const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData, emitter, startedAt } = params;
161
+ const {
162
+ step,
163
+ stepResults,
164
+ executionContext,
165
+ resume,
166
+ timeTravel,
167
+ prevOutput,
168
+ inputData,
169
+ pubsub,
170
+ startedAt,
171
+ perStep
172
+ } = params;
138
173
  const isResume = !!resume?.steps?.length;
139
174
  let result;
140
175
  let runId;
141
176
  const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
142
177
  try {
143
178
  if (isResume) {
144
- runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
179
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? crypto$1.randomUUID();
145
180
  const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
146
181
  workflowName: step.id,
147
182
  runId
@@ -159,7 +194,8 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
159
194
  resumePayload: resume.resumePayload,
160
195
  resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
161
196
  },
162
- outputOptions: { includeState: true }
197
+ outputOptions: { includeState: true },
198
+ perStep
163
199
  }
164
200
  });
165
201
  result = invokeResp.result;
@@ -185,7 +221,8 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
185
221
  timeTravel: timeTravelParams,
186
222
  initialState: executionContext.state ?? {},
187
223
  runId: executionContext.runId,
188
- outputOptions: { includeState: true }
224
+ outputOptions: { includeState: true },
225
+ perStep
189
226
  }
190
227
  });
191
228
  result = invokeResp.result;
@@ -197,7 +234,8 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
197
234
  data: {
198
235
  inputData,
199
236
  initialState: executionContext.state ?? {},
200
- outputOptions: { includeState: true }
237
+ outputOptions: { includeState: true },
238
+ perStep
201
239
  }
202
240
  });
203
241
  result = invokeResp.result;
@@ -208,9 +246,9 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
208
246
  const errorCause = e?.cause;
209
247
  if (errorCause && typeof errorCause === "object") {
210
248
  result = errorCause;
211
- runId = errorCause.runId || crypto.randomUUID();
249
+ runId = errorCause.runId || crypto$1.randomUUID();
212
250
  } else {
213
- runId = crypto.randomUUID();
251
+ runId = crypto$1.randomUUID();
214
252
  result = {
215
253
  status: "failed",
216
254
  error: e instanceof Error ? e : new Error(String(e)),
@@ -223,16 +261,20 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
223
261
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
224
262
  async () => {
225
263
  if (result.status === "failed") {
226
- await emitter.emit("watch", {
227
- type: "workflow-step-result",
228
- payload: {
229
- id: step.id,
230
- status: "failed",
231
- error: result?.error,
232
- payload: prevOutput
264
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
265
+ type: "watch",
266
+ runId: executionContext.runId,
267
+ data: {
268
+ type: "workflow-step-result",
269
+ payload: {
270
+ id: step.id,
271
+ status: "failed",
272
+ error: result?.error,
273
+ payload: prevOutput
274
+ }
233
275
  }
234
276
  });
235
- return { executionContext, result: { status: "failed", error: result?.error } };
277
+ return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
236
278
  } else if (result.status === "suspended") {
237
279
  const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
238
280
  const stepRes = stepResult;
@@ -241,17 +283,22 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
241
283
  for (const [stepName, stepResult] of suspendedSteps) {
242
284
  const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
243
285
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
244
- await emitter.emit("watch", {
245
- type: "workflow-step-suspended",
246
- payload: {
247
- id: step.id,
248
- status: "suspended"
286
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
287
+ type: "watch",
288
+ runId: executionContext.runId,
289
+ data: {
290
+ type: "workflow-step-suspended",
291
+ payload: {
292
+ id: step.id,
293
+ status: "suspended"
294
+ }
249
295
  }
250
296
  });
251
297
  return {
252
298
  executionContext,
253
299
  result: {
254
300
  status: "suspended",
301
+ suspendedAt: Date.now(),
255
302
  payload: stepResult.payload,
256
303
  suspendPayload: {
257
304
  ...stepResult?.suspendPayload,
@@ -264,39 +311,205 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
264
311
  executionContext,
265
312
  result: {
266
313
  status: "suspended",
314
+ suspendedAt: Date.now(),
267
315
  payload: {}
268
316
  }
269
317
  };
318
+ } else if (result.status === "tripwire") {
319
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
320
+ type: "watch",
321
+ runId: executionContext.runId,
322
+ data: {
323
+ type: "workflow-step-result",
324
+ payload: {
325
+ id: step.id,
326
+ status: "tripwire",
327
+ error: result?.tripwire?.reason,
328
+ payload: prevOutput
329
+ }
330
+ }
331
+ });
332
+ return {
333
+ executionContext,
334
+ result: {
335
+ status: "tripwire",
336
+ tripwire: result?.tripwire,
337
+ endedAt: Date.now()
338
+ }
339
+ };
340
+ } else if (perStep || result.status === "paused") {
341
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
342
+ type: "watch",
343
+ runId: executionContext.runId,
344
+ data: {
345
+ type: "workflow-step-result",
346
+ payload: {
347
+ id: step.id,
348
+ status: "paused"
349
+ }
350
+ }
351
+ });
352
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
353
+ type: "watch",
354
+ runId: executionContext.runId,
355
+ data: {
356
+ type: "workflow-step-finish",
357
+ payload: {
358
+ id: step.id,
359
+ metadata: {}
360
+ }
361
+ }
362
+ });
363
+ return { executionContext, result: { status: "paused" } };
270
364
  }
271
- await emitter.emit("watch", {
272
- type: "workflow-step-result",
273
- payload: {
274
- id: step.id,
275
- status: "success",
276
- output: result?.result
365
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
366
+ type: "watch",
367
+ runId: executionContext.runId,
368
+ data: {
369
+ type: "workflow-step-result",
370
+ payload: {
371
+ id: step.id,
372
+ status: "success",
373
+ output: result?.result
374
+ }
277
375
  }
278
376
  });
279
- await emitter.emit("watch", {
280
- type: "workflow-step-finish",
281
- payload: {
282
- id: step.id,
283
- metadata: {}
377
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
378
+ type: "watch",
379
+ runId: executionContext.runId,
380
+ data: {
381
+ type: "workflow-step-finish",
382
+ payload: {
383
+ id: step.id,
384
+ metadata: {}
385
+ }
284
386
  }
285
387
  });
286
- return { executionContext, result: { status: "success", output: result?.result } };
388
+ return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
287
389
  }
288
390
  );
289
391
  Object.assign(executionContext, res.executionContext);
290
392
  return {
291
393
  ...res.result,
292
394
  startedAt,
293
- endedAt: Date.now(),
294
395
  payload: inputData,
295
396
  resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
296
397
  resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
297
398
  };
298
399
  }
299
400
  };
401
+ var InngestPubSub = class extends events.PubSub {
402
+ inngest;
403
+ workflowId;
404
+ publishFn;
405
+ subscriptions = /* @__PURE__ */ new Map();
406
+ constructor(inngest, workflowId, publishFn) {
407
+ super();
408
+ this.inngest = inngest;
409
+ this.workflowId = workflowId;
410
+ this.publishFn = publishFn;
411
+ }
412
+ /**
413
+ * Publish an event to Inngest's realtime system.
414
+ *
415
+ * Topic format: "workflow.events.v2.{runId}"
416
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
417
+ */
418
+ async publish(topic, event) {
419
+ if (!this.publishFn) {
420
+ return;
421
+ }
422
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
423
+ if (!match) {
424
+ return;
425
+ }
426
+ const runId = match[1];
427
+ try {
428
+ await this.publishFn({
429
+ channel: `workflow:${this.workflowId}:${runId}`,
430
+ topic: "watch",
431
+ data: event.data
432
+ });
433
+ } catch (err) {
434
+ console.error("InngestPubSub publish error:", err?.message ?? err);
435
+ }
436
+ }
437
+ /**
438
+ * Subscribe to events from Inngest's realtime system.
439
+ *
440
+ * Topic format: "workflow.events.v2.{runId}"
441
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
442
+ */
443
+ async subscribe(topic, cb) {
444
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
445
+ if (!match || !match[1]) {
446
+ return;
447
+ }
448
+ const runId = match[1];
449
+ if (this.subscriptions.has(topic)) {
450
+ this.subscriptions.get(topic).callbacks.add(cb);
451
+ return;
452
+ }
453
+ const callbacks = /* @__PURE__ */ new Set([cb]);
454
+ const channel = `workflow:${this.workflowId}:${runId}`;
455
+ const streamPromise = realtime.subscribe(
456
+ {
457
+ channel,
458
+ topics: ["watch"],
459
+ app: this.inngest
460
+ },
461
+ (message) => {
462
+ const event = {
463
+ id: crypto.randomUUID(),
464
+ type: "watch",
465
+ runId,
466
+ data: message.data,
467
+ createdAt: /* @__PURE__ */ new Date()
468
+ };
469
+ for (const callback of callbacks) {
470
+ callback(event);
471
+ }
472
+ }
473
+ );
474
+ this.subscriptions.set(topic, {
475
+ unsubscribe: () => {
476
+ streamPromise.then((stream) => stream.cancel()).catch((err) => {
477
+ console.error("InngestPubSub unsubscribe error:", err);
478
+ });
479
+ },
480
+ callbacks
481
+ });
482
+ }
483
+ /**
484
+ * Unsubscribe a callback from a topic.
485
+ * If no callbacks remain, the underlying Inngest subscription is cancelled.
486
+ */
487
+ async unsubscribe(topic, cb) {
488
+ const sub = this.subscriptions.get(topic);
489
+ if (!sub) {
490
+ return;
491
+ }
492
+ sub.callbacks.delete(cb);
493
+ if (sub.callbacks.size === 0) {
494
+ sub.unsubscribe();
495
+ this.subscriptions.delete(topic);
496
+ }
497
+ }
498
+ /**
499
+ * Flush any pending operations. No-op for Inngest.
500
+ */
501
+ async flush() {
502
+ }
503
+ /**
504
+ * Clean up all subscriptions during graceful shutdown.
505
+ */
506
+ async close() {
507
+ for (const [, sub] of this.subscriptions) {
508
+ sub.unsubscribe();
509
+ }
510
+ this.subscriptions.clear();
511
+ }
512
+ };
300
513
  var InngestRun = class extends workflows.Run {
301
514
  inngest;
302
515
  serializedStepGraph;
@@ -308,27 +521,77 @@ var InngestRun = class extends workflows.Run {
308
521
  this.#mastra = params.mastra;
309
522
  }
310
523
  async getRuns(eventId) {
311
- const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
312
- headers: {
313
- Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
524
+ const maxRetries = 3;
525
+ let lastError = null;
526
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
527
+ try {
528
+ const response = await fetch(
529
+ `${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
530
+ {
531
+ headers: {
532
+ Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
533
+ }
534
+ }
535
+ );
536
+ if (response.status === 429) {
537
+ const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
538
+ await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
539
+ continue;
540
+ }
541
+ if (!response.ok) {
542
+ throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
543
+ }
544
+ const text = await response.text();
545
+ if (!text) {
546
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
547
+ continue;
548
+ }
549
+ const json = JSON.parse(text);
550
+ return json.data;
551
+ } catch (error) {
552
+ lastError = error;
553
+ if (attempt < maxRetries - 1) {
554
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
555
+ }
314
556
  }
315
- });
316
- const json = await response.json();
317
- return json.data;
557
+ }
558
+ throw new inngest.NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
318
559
  }
319
- async getRunOutput(eventId) {
320
- let runs = await this.getRuns(eventId);
560
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
561
+ const startTime = Date.now();
321
562
  const storage = this.#mastra?.getStorage();
322
- while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
323
- await new Promise((resolve) => setTimeout(resolve, 1e3));
324
- runs = await this.getRuns(eventId);
563
+ while (Date.now() - startTime < maxWaitMs) {
564
+ let runs;
565
+ try {
566
+ runs = await this.getRuns(eventId);
567
+ } catch (error) {
568
+ if (error instanceof inngest.NonRetriableError) {
569
+ throw error;
570
+ }
571
+ throw new inngest.NonRetriableError(
572
+ `Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
573
+ );
574
+ }
575
+ if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
576
+ return runs[0];
577
+ }
325
578
  if (runs?.[0]?.status === "Failed") {
326
579
  const snapshot = await storage?.loadWorkflowSnapshot({
327
580
  workflowName: this.workflowId,
328
581
  runId: this.runId
329
582
  });
583
+ if (snapshot?.context) {
584
+ snapshot.context = workflows.hydrateSerializedStepErrors(snapshot.context);
585
+ }
330
586
  return {
331
- output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
587
+ output: {
588
+ result: {
589
+ steps: snapshot?.context,
590
+ status: "failed",
591
+ // Get the original error from NonRetriableError's cause (which contains the workflow result)
592
+ error: error.getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
593
+ }
594
+ }
332
595
  };
333
596
  }
334
597
  if (runs?.[0]?.status === "Cancelled") {
@@ -338,8 +601,9 @@ var InngestRun = class extends workflows.Run {
338
601
  });
339
602
  return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
340
603
  }
604
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
341
605
  }
342
- return runs?.[0];
606
+ throw new inngest.NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
343
607
  }
344
608
  async cancel() {
345
609
  const storage = this.#mastra?.getStorage();
@@ -369,13 +633,60 @@ var InngestRun = class extends workflows.Run {
369
633
  async start(params) {
370
634
  return this._start(params);
371
635
  }
636
+ /**
637
+ * Starts the workflow execution without waiting for completion (fire-and-forget).
638
+ * Returns immediately with the runId after sending the event to Inngest.
639
+ * The workflow executes independently in Inngest.
640
+ * Use this when you don't need to wait for the result or want to avoid polling failures.
641
+ */
642
+ async startAsync(params) {
643
+ await this.#mastra.getStorage()?.persistWorkflowSnapshot({
644
+ workflowName: this.workflowId,
645
+ runId: this.runId,
646
+ resourceId: this.resourceId,
647
+ snapshot: {
648
+ runId: this.runId,
649
+ serializedStepGraph: this.serializedStepGraph,
650
+ status: "running",
651
+ value: {},
652
+ context: {},
653
+ activePaths: [],
654
+ suspendedPaths: {},
655
+ activeStepsPath: {},
656
+ resumeLabels: {},
657
+ waitingPaths: {},
658
+ timestamp: Date.now()
659
+ }
660
+ });
661
+ const inputDataToUse = await this._validateInput(params.inputData);
662
+ const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
663
+ const eventOutput = await this.inngest.send({
664
+ name: `workflow.${this.workflowId}`,
665
+ data: {
666
+ inputData: inputDataToUse,
667
+ initialState: initialStateToUse,
668
+ runId: this.runId,
669
+ resourceId: this.resourceId,
670
+ outputOptions: params.outputOptions,
671
+ tracingOptions: params.tracingOptions,
672
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
673
+ perStep: params.perStep
674
+ }
675
+ });
676
+ const eventId = eventOutput.ids[0];
677
+ if (!eventId) {
678
+ throw new Error("Event ID is not set");
679
+ }
680
+ return { runId: this.runId };
681
+ }
372
682
  async _start({
373
683
  inputData,
374
684
  initialState,
375
685
  outputOptions,
376
686
  tracingOptions,
377
687
  format,
378
- requestContext
688
+ requestContext,
689
+ perStep
379
690
  }) {
380
691
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
381
692
  workflowName: this.workflowId,
@@ -407,7 +718,8 @@ var InngestRun = class extends workflows.Run {
407
718
  outputOptions,
408
719
  tracingOptions,
409
720
  format,
410
- requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {}
721
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
722
+ perStep
411
723
  }
412
724
  });
413
725
  const eventId = eventOutput.ids[0];
@@ -416,9 +728,7 @@ var InngestRun = class extends workflows.Run {
416
728
  }
417
729
  const runOutput = await this.getRunOutput(eventId);
418
730
  const result = runOutput?.output?.result;
419
- if (result.status === "failed") {
420
- result.error = new Error(result.error);
421
- }
731
+ this.hydrateFailedResult(result);
422
732
  if (result.status !== "suspended") {
423
733
  this.cleanup?.();
424
734
  }
@@ -468,7 +778,8 @@ var InngestRun = class extends workflows.Run {
468
778
  resumePayload: resumeDataToUse,
469
779
  resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
470
780
  },
471
- requestContext: mergedRequestContext
781
+ requestContext: mergedRequestContext,
782
+ perStep: params.perStep
472
783
  }
473
784
  });
474
785
  const eventId = eventOutput.ids[0];
@@ -477,9 +788,7 @@ var InngestRun = class extends workflows.Run {
477
788
  }
478
789
  const runOutput = await this.getRunOutput(eventId);
479
790
  const result = runOutput?.output?.result;
480
- if (result.status === "failed") {
481
- result.error = new Error(result.error);
482
- }
791
+ this.hydrateFailedResult(result);
483
792
  return result;
484
793
  }
485
794
  async timeTravel(params) {
@@ -560,7 +869,8 @@ var InngestRun = class extends workflows.Run {
560
869
  timeTravel: timeTravelData,
561
870
  tracingOptions: params.tracingOptions,
562
871
  outputOptions: params.outputOptions,
563
- requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
872
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
873
+ perStep: params.perStep
564
874
  }
565
875
  });
566
876
  const eventId = eventOutput.ids[0];
@@ -569,9 +879,7 @@ var InngestRun = class extends workflows.Run {
569
879
  }
570
880
  const runOutput = await this.getRunOutput(eventId);
571
881
  const result = runOutput?.output?.result;
572
- if (result.status === "failed") {
573
- result.error = new Error(result.error);
574
- }
882
+ this.hydrateFailedResult(result);
575
883
  return result;
576
884
  }
577
885
  watch(cb) {
@@ -653,7 +961,8 @@ var InngestRun = class extends workflows.Run {
653
961
  tracingOptions,
654
962
  closeOnSuspend = true,
655
963
  initialState,
656
- outputOptions
964
+ outputOptions,
965
+ perStep
657
966
  } = {}) {
658
967
  if (this.closeStreamAction && this.streamOutput) {
659
968
  return this.streamOutput;
@@ -689,7 +998,8 @@ var InngestRun = class extends workflows.Run {
689
998
  initialState,
690
999
  tracingOptions,
691
1000
  outputOptions,
692
- format: "vnext"
1001
+ format: "vnext",
1002
+ perStep
693
1003
  });
694
1004
  let executionResults;
695
1005
  try {
@@ -732,7 +1042,8 @@ var InngestRun = class extends workflows.Run {
732
1042
  nestedStepsContext,
733
1043
  requestContext,
734
1044
  tracingOptions,
735
- outputOptions
1045
+ outputOptions,
1046
+ perStep
736
1047
  }) {
737
1048
  this.closeStreamAction = async () => {
738
1049
  };
@@ -767,7 +1078,8 @@ var InngestRun = class extends workflows.Run {
767
1078
  initialState,
768
1079
  requestContext,
769
1080
  tracingOptions,
770
- outputOptions
1081
+ outputOptions,
1082
+ perStep
771
1083
  });
772
1084
  self.executionResults = executionResultsPromise;
773
1085
  let executionResults;
@@ -792,6 +1104,18 @@ var InngestRun = class extends workflows.Run {
792
1104
  });
793
1105
  return this.streamOutput;
794
1106
  }
1107
+ /**
1108
+ * Hydrates errors in a failed workflow result back to proper Error instances.
1109
+ * This ensures error.cause chains and custom properties are preserved.
1110
+ */
1111
+ hydrateFailedResult(result) {
1112
+ if (result.status === "failed") {
1113
+ result.error = error.getErrorFromUnknown(result.error, { serializeStack: false });
1114
+ if (result.steps) {
1115
+ workflows.hydrateSerializedStepErrors(result.steps);
1116
+ }
1117
+ }
1118
+ }
795
1119
  };
796
1120
 
797
1121
  // src/workflow.ts
@@ -829,6 +1153,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
829
1153
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
830
1154
  }
831
1155
  __registerMastra(mastra) {
1156
+ super.__registerMastra(mastra);
832
1157
  this.#mastra = mastra;
833
1158
  this.executionEngine.__registerMastra(mastra);
834
1159
  const updateNested = (step) => {
@@ -847,7 +1172,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
847
1172
  }
848
1173
  }
849
1174
  async createRun(options) {
850
- const runIdToUse = options?.runId || crypto.randomUUID();
1175
+ const runIdToUse = options?.runId || crypto$1.randomUUID();
851
1176
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
852
1177
  {
853
1178
  workflowId: this.id,
@@ -870,7 +1195,9 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
870
1195
  workflowStatus: run.workflowRunStatus,
871
1196
  stepResults: {}
872
1197
  });
873
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
1198
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
1199
+ withNestedWorkflows: false
1200
+ });
874
1201
  if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
875
1202
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
876
1203
  workflowName: this.id,
@@ -909,34 +1236,13 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
909
1236
  },
910
1237
  { event: `workflow.${this.id}` },
911
1238
  async ({ event, step, attempt, publish }) => {
912
- let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
1239
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel, perStep } = event.data;
913
1240
  if (!runId) {
914
1241
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
915
- return crypto.randomUUID();
1242
+ return crypto$1.randomUUID();
916
1243
  });
917
1244
  }
918
- const emitter = {
919
- emit: async (event2, data) => {
920
- if (!publish) {
921
- return;
922
- }
923
- try {
924
- await publish({
925
- channel: `workflow:${this.id}:${runId}`,
926
- topic: event2,
927
- data
928
- });
929
- } catch (err) {
930
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
931
- }
932
- },
933
- on: (_event, _callback) => {
934
- },
935
- off: (_event, _callback) => {
936
- },
937
- once: (_event, _callback) => {
938
- }
939
- };
1245
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
940
1246
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
941
1247
  const result = await engine.execute({
942
1248
  workflowId: this.id,
@@ -946,21 +1252,32 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
946
1252
  serializedStepGraph: this.serializedStepGraph,
947
1253
  input: inputData,
948
1254
  initialState,
949
- emitter,
1255
+ pubsub,
950
1256
  retryConfig: this.retryConfig,
951
1257
  requestContext: new di.RequestContext(Object.entries(event.data.requestContext ?? {})),
952
1258
  resume,
953
1259
  timeTravel,
1260
+ perStep,
954
1261
  format,
955
1262
  abortController: new AbortController(),
956
1263
  // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
957
1264
  outputOptions,
958
1265
  outputWriter: async (chunk) => {
959
- void emitter.emit("watch", chunk).catch(() => {
960
- });
1266
+ try {
1267
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1268
+ type: "watch",
1269
+ runId,
1270
+ data: chunk
1271
+ });
1272
+ } catch (err) {
1273
+ this.logger.debug?.("Failed to publish watch event:", err);
1274
+ }
961
1275
  }
962
1276
  });
963
1277
  await step.run(`workflow.${this.id}.finalize`, async () => {
1278
+ if (result.status !== "paused") {
1279
+ await engine.invokeLifecycleCallbacksInternal(result);
1280
+ }
964
1281
  if (result.status === "failed") {
965
1282
  throw new inngest.NonRetriableError(`Workflow failed`, {
966
1283
  cause: result
@@ -1045,7 +1362,8 @@ function createStep(params, agentOptions) {
1045
1362
  outputSchema,
1046
1363
  execute: async ({
1047
1364
  inputData,
1048
- [_constants.EMITTER_SYMBOL]: emitter,
1365
+ runId,
1366
+ [_constants.PUBSUB_SYMBOL]: pubsub,
1049
1367
  [_constants.STREAM_FORMAT_SYMBOL]: streamFormat,
1050
1368
  requestContext,
1051
1369
  tracingContext,
@@ -1100,22 +1418,24 @@ function createStep(params, agentOptions) {
1100
1418
  stream = modelOutput.fullStream;
1101
1419
  }
1102
1420
  if (streamFormat === "legacy") {
1103
- await emitter.emit("watch", {
1104
- type: "tool-call-streaming-start",
1105
- ...toolData ?? {}
1421
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1422
+ type: "watch",
1423
+ runId,
1424
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
1106
1425
  });
1107
1426
  for await (const chunk of stream) {
1108
1427
  if (chunk.type === "text-delta") {
1109
- await emitter.emit("watch", {
1110
- type: "tool-call-delta",
1111
- ...toolData ?? {},
1112
- argsTextDelta: chunk.textDelta
1428
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1429
+ type: "watch",
1430
+ runId,
1431
+ data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
1113
1432
  });
1114
1433
  }
1115
1434
  }
1116
- await emitter.emit("watch", {
1117
- type: "tool-call-streaming-finish",
1118
- ...toolData ?? {}
1435
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1436
+ type: "watch",
1437
+ runId,
1438
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
1119
1439
  });
1120
1440
  } else {
1121
1441
  for await (const chunk of stream) {
@@ -1228,6 +1548,7 @@ function init(inngest) {
1228
1548
  }
1229
1549
 
1230
1550
  exports.InngestExecutionEngine = InngestExecutionEngine;
1551
+ exports.InngestPubSub = InngestPubSub;
1231
1552
  exports.InngestRun = InngestRun;
1232
1553
  exports.InngestWorkflow = InngestWorkflow;
1233
1554
  exports._compatibilityCheck = _compatibilityCheck;