@mastra/inngest 0.0.0-bundle-recursion-20251030002519 → 0.0.0-bundle-studio-cloud-20251222034739

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 } from 'stream/web';
6
+ import { RequestContext } from '@mastra/core/di';
7
+ import { RetryAfterError, NonRetriableError } from 'inngest';
8
+ import { getErrorFromUnknown } from '@mastra/core/error';
3
9
  import { subscribe } from '@inngest/realtime';
4
- import { wrapMastra, AISpanType } from '@mastra/core/ai-tracing';
5
- import { RuntimeContext } from '@mastra/core/di';
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, Workflow, DefaultExecutionEngine, createDeprecationProxy, getStepResult, runCountDeprecationMessage, validateStepInput } 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.getWorkflows();
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,63 +479,111 @@ 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);
60
- while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
61
- await new Promise((resolve) => setTimeout(resolve, 1e3));
62
- runs = await this.getRuns(eventId);
518
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
519
+ const startTime = Date.now();
520
+ const storage = this.#mastra?.getStorage();
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
+ }
63
536
  if (runs?.[0]?.status === "Failed") {
64
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
537
+ const snapshot = await storage?.loadWorkflowSnapshot({
65
538
  workflowName: this.workflowId,
66
539
  runId: this.runId
67
540
  });
541
+ if (snapshot?.context) {
542
+ snapshot.context = hydrateSerializedStepErrors(snapshot.context);
543
+ }
68
544
  return {
69
- 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
+ }
70
553
  };
71
554
  }
72
555
  if (runs?.[0]?.status === "Cancelled") {
73
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
556
+ const snapshot = await storage?.loadWorkflowSnapshot({
74
557
  workflowName: this.workflowId,
75
558
  runId: this.runId
76
559
  });
77
560
  return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
78
561
  }
562
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
79
563
  }
80
- return runs?.[0];
81
- }
82
- async sendEvent(event, data) {
83
- await this.inngest.send({
84
- name: `user-event-${event}`,
85
- data
86
- });
564
+ throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
87
565
  }
88
566
  async cancel() {
567
+ const storage = this.#mastra?.getStorage();
89
568
  await this.inngest.send({
90
569
  name: `cancel.workflow.${this.workflowId}`,
91
570
  data: {
92
571
  runId: this.runId
93
572
  }
94
573
  });
95
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
574
+ const snapshot = await storage?.loadWorkflowSnapshot({
96
575
  workflowName: this.workflowId,
97
576
  runId: this.runId
98
577
  });
99
578
  if (snapshot) {
100
- await this.#mastra?.storage?.persistWorkflowSnapshot({
579
+ await storage?.persistWorkflowSnapshot({
101
580
  workflowName: this.workflowId,
102
581
  runId: this.runId,
103
582
  resourceId: this.resourceId,
104
583
  snapshot: {
105
584
  ...snapshot,
106
- status: "canceled"
585
+ status: "canceled",
586
+ value: snapshot.value
107
587
  }
108
588
  });
109
589
  }
@@ -111,13 +591,13 @@ var InngestRun = class extends Run {
111
591
  async start(params) {
112
592
  return this._start(params);
113
593
  }
114
- async _start({
115
- inputData,
116
- initialState,
117
- outputOptions,
118
- tracingOptions,
119
- format
120
- }) {
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) {
121
601
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
122
602
  workflowName: this.workflowId,
123
603
  runId: this.runId,
@@ -125,18 +605,65 @@ var InngestRun = class extends Run {
125
605
  snapshot: {
126
606
  runId: this.runId,
127
607
  serializedStepGraph: this.serializedStepGraph,
608
+ status: "running",
128
609
  value: {},
129
610
  context: {},
130
611
  activePaths: [],
131
612
  suspendedPaths: {},
613
+ activeStepsPath: {},
132
614
  resumeLabels: {},
133
615
  waitingPaths: {},
134
- timestamp: Date.now(),
135
- status: "running"
616
+ timestamp: Date.now()
136
617
  }
137
618
  });
138
- const inputDataToUse = await this._validateInput(inputData);
139
- const initialStateToUse = await this._validateInitialState(initialState ?? {});
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
+ }
639
+ async _start({
640
+ inputData,
641
+ initialState,
642
+ outputOptions,
643
+ tracingOptions,
644
+ format,
645
+ requestContext
646
+ }) {
647
+ await this.#mastra.getStorage()?.persistWorkflowSnapshot({
648
+ workflowName: this.workflowId,
649
+ runId: this.runId,
650
+ resourceId: this.resourceId,
651
+ snapshot: {
652
+ runId: this.runId,
653
+ serializedStepGraph: this.serializedStepGraph,
654
+ status: "running",
655
+ value: {},
656
+ context: {},
657
+ activePaths: [],
658
+ suspendedPaths: {},
659
+ activeStepsPath: {},
660
+ resumeLabels: {},
661
+ waitingPaths: {},
662
+ timestamp: Date.now()
663
+ }
664
+ });
665
+ const inputDataToUse = await this._validateInput(inputData);
666
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
140
667
  const eventOutput = await this.inngest.send({
141
668
  name: `workflow.${this.workflowId}`,
142
669
  data: {
@@ -146,7 +673,8 @@ var InngestRun = class extends Run {
146
673
  resourceId: this.resourceId,
147
674
  outputOptions,
148
675
  tracingOptions,
149
- format
676
+ format,
677
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {}
150
678
  }
151
679
  });
152
680
  const eventId = eventOutput.ids[0];
@@ -155,9 +683,7 @@ var InngestRun = class extends Run {
155
683
  }
156
684
  const runOutput = await this.getRunOutput(eventId);
157
685
  const result = runOutput?.output?.result;
158
- if (result.status === "failed") {
159
- result.error = new Error(result.error);
160
- }
686
+ this.hydrateFailedResult(result);
161
687
  if (result.status !== "suspended") {
162
688
  this.cleanup?.();
163
689
  }
@@ -175,15 +701,24 @@ var InngestRun = class extends Run {
175
701
  return p;
176
702
  }
177
703
  async _resume(params) {
178
- const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
179
- (step) => typeof step === "string" ? step : step?.id
180
- );
181
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
704
+ const storage = this.#mastra?.getStorage();
705
+ let steps = [];
706
+ if (typeof params.step === "string") {
707
+ steps = params.step.split(".");
708
+ } else {
709
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
710
+ (step) => typeof step === "string" ? step : step?.id
711
+ );
712
+ }
713
+ const snapshot = await storage?.loadWorkflowSnapshot({
182
714
  workflowName: this.workflowId,
183
715
  runId: this.runId
184
716
  });
185
717
  const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
186
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 };
187
722
  const eventOutput = await this.inngest.send({
188
723
  name: `workflow.${this.workflowId}`,
189
724
  data: {
@@ -196,9 +731,9 @@ var InngestRun = class extends Run {
196
731
  steps,
197
732
  stepResults: snapshot?.context,
198
733
  resumePayload: resumeDataToUse,
199
- // @ts-ignore
200
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
201
- }
734
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
735
+ },
736
+ requestContext: mergedRequestContext
202
737
  }
203
738
  });
204
739
  const eventId = eventOutput.ids[0];
@@ -207,17 +742,105 @@ var InngestRun = class extends Run {
207
742
  }
208
743
  const runOutput = await this.getRunOutput(eventId);
209
744
  const result = runOutput?.output?.result;
210
- if (result.status === "failed") {
211
- result.error = new Error(result.error);
745
+ this.hydrateFailedResult(result);
746
+ return result;
747
+ }
748
+ async timeTravel(params) {
749
+ const p = this._timeTravel(params).then((result) => {
750
+ if (result.status !== "suspended") {
751
+ this.closeStreamAction?.().catch(() => {
752
+ });
753
+ }
754
+ return result;
755
+ });
756
+ this.executionResults = p;
757
+ return p;
758
+ }
759
+ async _timeTravel(params) {
760
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
761
+ throw new Error("Step is required and must be a valid step or array of steps");
762
+ }
763
+ let steps = [];
764
+ if (typeof params.step === "string") {
765
+ steps = params.step.split(".");
766
+ } else {
767
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
768
+ (step) => typeof step === "string" ? step : step?.id
769
+ );
212
770
  }
771
+ if (steps.length === 0) {
772
+ throw new Error("No steps provided to timeTravel");
773
+ }
774
+ const storage = this.#mastra?.getStorage();
775
+ const snapshot = await storage?.loadWorkflowSnapshot({
776
+ workflowName: this.workflowId,
777
+ runId: this.runId
778
+ });
779
+ if (!snapshot) {
780
+ await storage?.persistWorkflowSnapshot({
781
+ workflowName: this.workflowId,
782
+ runId: this.runId,
783
+ resourceId: this.resourceId,
784
+ snapshot: {
785
+ runId: this.runId,
786
+ serializedStepGraph: this.serializedStepGraph,
787
+ status: "pending",
788
+ value: {},
789
+ context: {},
790
+ activePaths: [],
791
+ suspendedPaths: {},
792
+ activeStepsPath: {},
793
+ resumeLabels: {},
794
+ waitingPaths: {},
795
+ timestamp: Date.now()
796
+ }
797
+ });
798
+ }
799
+ if (snapshot?.status === "running") {
800
+ throw new Error("This workflow run is still running, cannot time travel");
801
+ }
802
+ let inputDataToUse = params.inputData;
803
+ if (inputDataToUse && steps.length === 1) {
804
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
805
+ }
806
+ const timeTravelData = createTimeTravelExecutionParams({
807
+ steps,
808
+ inputData: inputDataToUse,
809
+ resumeData: params.resumeData,
810
+ context: params.context,
811
+ nestedStepsContext: params.nestedStepsContext,
812
+ snapshot: snapshot ?? { context: {} },
813
+ graph: this.executionGraph,
814
+ initialState: params.initialState
815
+ });
816
+ const eventOutput = await this.inngest.send({
817
+ name: `workflow.${this.workflowId}`,
818
+ data: {
819
+ initialState: timeTravelData.state,
820
+ runId: this.runId,
821
+ workflowId: this.workflowId,
822
+ stepResults: timeTravelData.stepResults,
823
+ timeTravel: timeTravelData,
824
+ tracingOptions: params.tracingOptions,
825
+ outputOptions: params.outputOptions,
826
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
827
+ }
828
+ });
829
+ const eventId = eventOutput.ids[0];
830
+ if (!eventId) {
831
+ throw new Error("Event ID is not set");
832
+ }
833
+ const runOutput = await this.getRunOutput(eventId);
834
+ const result = runOutput?.output?.result;
835
+ this.hydrateFailedResult(result);
213
836
  return result;
214
837
  }
215
- watch(cb, type = "watch") {
838
+ watch(cb) {
216
839
  let active = true;
217
840
  const streamPromise = subscribe(
218
841
  {
219
842
  channel: `workflow:${this.workflowId}:${this.runId}`,
220
- topics: [type],
843
+ topics: ["watch"],
221
844
  app: this.inngest
222
845
  },
223
846
  (message) => {
@@ -235,17 +858,17 @@ var InngestRun = class extends Run {
235
858
  });
236
859
  };
237
860
  }
238
- streamLegacy({ inputData, runtimeContext } = {}) {
861
+ streamLegacy({ inputData, requestContext } = {}) {
239
862
  const { readable, writable } = new TransformStream();
240
863
  const writer = writable.getWriter();
864
+ void writer.write({
865
+ // @ts-ignore
866
+ type: "start",
867
+ // @ts-ignore
868
+ payload: { runId: this.runId }
869
+ });
241
870
  const unwatch = this.watch(async (event) => {
242
871
  try {
243
- await writer.write({
244
- // @ts-ignore
245
- type: "start",
246
- // @ts-ignore
247
- payload: { runId: this.runId }
248
- });
249
872
  const e = {
250
873
  ...event,
251
874
  type: event.type.replace("workflow-", "")
@@ -257,7 +880,7 @@ var InngestRun = class extends Run {
257
880
  await writer.write(e);
258
881
  } catch {
259
882
  }
260
- }, "watch-v2");
883
+ });
261
884
  this.closeStreamAction = async () => {
262
885
  await writer.write({
263
886
  type: "finish",
@@ -273,7 +896,7 @@ var InngestRun = class extends Run {
273
896
  writer.releaseLock();
274
897
  }
275
898
  };
276
- this.executionResults = this._start({ inputData, runtimeContext, format: "legacy" }).then((result) => {
899
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
277
900
  if (result.status !== "suspended") {
278
901
  this.closeStreamAction?.().catch(() => {
279
902
  });
@@ -287,7 +910,7 @@ var InngestRun = class extends Run {
287
910
  }
288
911
  stream({
289
912
  inputData,
290
- runtimeContext,
913
+ requestContext,
291
914
  tracingOptions,
292
915
  closeOnSuspend = true,
293
916
  initialState,
@@ -311,7 +934,7 @@ var InngestRun = class extends Run {
311
934
  ...payload
312
935
  }
313
936
  });
314
- }, "watch-v2");
937
+ });
315
938
  self.closeStreamAction = async () => {
316
939
  unwatch();
317
940
  try {
@@ -322,7 +945,7 @@ var InngestRun = class extends Run {
322
945
  };
323
946
  const executionResultsPromise = self._start({
324
947
  inputData,
325
- runtimeContext,
948
+ requestContext,
326
949
  // tracingContext, // We are not able to pass a reference to a span here, what to do?
327
950
  initialState,
328
951
  tracingOptions,
@@ -361,7 +984,90 @@ var InngestRun = class extends Run {
361
984
  streamVNext(args = {}) {
362
985
  return this.stream(args);
363
986
  }
987
+ timeTravelStream({
988
+ inputData,
989
+ resumeData,
990
+ initialState,
991
+ step,
992
+ context,
993
+ nestedStepsContext,
994
+ requestContext,
995
+ tracingOptions,
996
+ outputOptions
997
+ }) {
998
+ this.closeStreamAction = async () => {
999
+ };
1000
+ const self = this;
1001
+ const stream = new ReadableStream({
1002
+ async start(controller) {
1003
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
1004
+ controller.enqueue({
1005
+ type,
1006
+ runId: self.runId,
1007
+ from,
1008
+ payload: {
1009
+ stepName: payload?.id,
1010
+ ...payload
1011
+ }
1012
+ });
1013
+ });
1014
+ self.closeStreamAction = async () => {
1015
+ unwatch();
1016
+ try {
1017
+ controller.close();
1018
+ } catch (err) {
1019
+ console.error("Error closing stream:", err);
1020
+ }
1021
+ };
1022
+ const executionResultsPromise = self._timeTravel({
1023
+ inputData,
1024
+ step,
1025
+ context,
1026
+ nestedStepsContext,
1027
+ resumeData,
1028
+ initialState,
1029
+ requestContext,
1030
+ tracingOptions,
1031
+ outputOptions
1032
+ });
1033
+ self.executionResults = executionResultsPromise;
1034
+ let executionResults;
1035
+ try {
1036
+ executionResults = await executionResultsPromise;
1037
+ self.closeStreamAction?.().catch(() => {
1038
+ });
1039
+ if (self.streamOutput) {
1040
+ self.streamOutput.updateResults(executionResults);
1041
+ }
1042
+ } catch (err) {
1043
+ self.streamOutput?.rejectResults(err);
1044
+ self.closeStreamAction?.().catch(() => {
1045
+ });
1046
+ }
1047
+ }
1048
+ });
1049
+ this.streamOutput = new WorkflowRunOutput({
1050
+ runId: this.runId,
1051
+ workflowId: this.workflowId,
1052
+ stream
1053
+ });
1054
+ return this.streamOutput;
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
+ }
364
1068
  };
1069
+
1070
+ // src/workflow.ts
365
1071
  var InngestWorkflow = class _InngestWorkflow extends Workflow {
366
1072
  #mastra;
367
1073
  inngest;
@@ -370,6 +1076,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
370
1076
  constructor(params, inngest) {
371
1077
  const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
372
1078
  super(workflowParams);
1079
+ this.engineType = "inngest";
373
1080
  const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
374
1081
  ([_, value]) => value !== void 0
375
1082
  );
@@ -377,13 +1084,13 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
377
1084
  this.#mastra = params.mastra;
378
1085
  this.inngest = inngest;
379
1086
  }
380
- async getWorkflowRuns(args) {
1087
+ async listWorkflowRuns(args) {
381
1088
  const storage = this.#mastra?.getStorage();
382
1089
  if (!storage) {
383
1090
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
384
1091
  return { runs: [], total: 0 };
385
1092
  }
386
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
1093
+ return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
387
1094
  }
388
1095
  async getWorkflowRunById(runId) {
389
1096
  const storage = this.#mastra?.getStorage();
@@ -395,6 +1102,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
395
1102
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
396
1103
  }
397
1104
  __registerMastra(mastra) {
1105
+ super.__registerMastra(mastra);
398
1106
  this.#mastra = mastra;
399
1107
  this.executionEngine.__registerMastra(mastra);
400
1108
  const updateNested = (step) => {
@@ -412,16 +1120,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
412
1120
  }
413
1121
  }
414
1122
  }
415
- /**
416
- * @deprecated Use createRunAsync() instead.
417
- * @throws {Error} Always throws an error directing users to use createRunAsync()
418
- */
419
- createRun(_options) {
420
- throw new Error(
421
- "createRun() has been deprecated. Please use createRunAsync() instead.\n\nMigration guide:\n Before: const run = workflow.createRun();\n After: const run = await workflow.createRunAsync();\n\nNote: createRunAsync() is an async method, so make sure your calling function is async."
422
- );
423
- }
424
- async createRunAsync(options) {
1123
+ async createRun(options) {
425
1124
  const runIdToUse = options?.runId || randomUUID();
426
1125
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
427
1126
  {
@@ -434,7 +1133,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
434
1133
  mastra: this.#mastra,
435
1134
  retryConfig: this.retryConfig,
436
1135
  cleanup: () => this.runs.delete(runIdToUse),
437
- workflowSteps: this.steps
1136
+ workflowSteps: this.steps,
1137
+ workflowEngineType: this.engineType,
1138
+ validateInputs: this.options.validateInputs
438
1139
  },
439
1140
  this.inngest
440
1141
  );
@@ -443,7 +1144,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
443
1144
  workflowStatus: run.workflowRunStatus,
444
1145
  stepResults: {}
445
1146
  });
446
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
1147
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
1148
+ withNestedWorkflows: false
1149
+ });
447
1150
  if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
448
1151
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
449
1152
  workflowName: this.id,
@@ -455,13 +1158,13 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
455
1158
  value: {},
456
1159
  context: {},
457
1160
  activePaths: [],
1161
+ activeStepsPath: {},
458
1162
  waitingPaths: {},
459
1163
  serializedStepGraph: this.serializedStepGraph,
460
1164
  suspendedPaths: {},
461
1165
  resumeLabels: {},
462
1166
  result: void 0,
463
1167
  error: void 0,
464
- // @ts-ignore
465
1168
  timestamp: Date.now()
466
1169
  }
467
1170
  });
@@ -475,42 +1178,20 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
475
1178
  this.function = this.inngest.createFunction(
476
1179
  {
477
1180
  id: `workflow.${this.id}`,
478
- // @ts-ignore
479
- retries: this.retryConfig?.attempts ?? 0,
1181
+ retries: Math.min(this.retryConfig?.attempts ?? 0, 20),
480
1182
  cancelOn: [{ event: `cancel.workflow.${this.id}` }],
481
1183
  // Spread flow control configuration
482
1184
  ...this.flowControlConfig
483
1185
  },
484
1186
  { event: `workflow.${this.id}` },
485
1187
  async ({ event, step, attempt, publish }) => {
486
- let { inputData, initialState, runId, resourceId, resume, outputOptions, format } = event.data;
1188
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
487
1189
  if (!runId) {
488
1190
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
489
1191
  return randomUUID();
490
1192
  });
491
1193
  }
492
- const emitter = {
493
- emit: async (event2, data) => {
494
- if (!publish) {
495
- return;
496
- }
497
- try {
498
- await publish({
499
- channel: `workflow:${this.id}:${runId}`,
500
- topic: event2,
501
- data
502
- });
503
- } catch (err) {
504
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
505
- }
506
- },
507
- on: (_event, _callback) => {
508
- },
509
- off: (_event, _callback) => {
510
- },
511
- once: (_event, _callback) => {
512
- }
513
- };
1194
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
514
1195
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
515
1196
  const result = await engine.execute({
516
1197
  workflowId: this.id,
@@ -520,23 +1201,29 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
520
1201
  serializedStepGraph: this.serializedStepGraph,
521
1202
  input: inputData,
522
1203
  initialState,
523
- emitter,
1204
+ pubsub,
524
1205
  retryConfig: this.retryConfig,
525
- runtimeContext: new RuntimeContext(),
526
- // TODO
1206
+ requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
527
1207
  resume,
1208
+ timeTravel,
528
1209
  format,
529
1210
  abortController: new AbortController(),
530
- // currentSpan: undefined, // TODO: Pass actual parent AI span from workflow execution context
1211
+ // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
531
1212
  outputOptions,
532
- writableStream: new WritableStream({
533
- write(chunk) {
534
- void emitter.emit("watch-v2", chunk).catch(() => {
1213
+ outputWriter: async (chunk) => {
1214
+ try {
1215
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1216
+ type: "watch",
1217
+ runId,
1218
+ data: chunk
535
1219
  });
1220
+ } catch (err) {
1221
+ this.logger.debug?.("Failed to publish watch event:", err);
536
1222
  }
537
- })
1223
+ }
538
1224
  });
539
1225
  await step.run(`workflow.${this.id}.finalize`, async () => {
1226
+ await engine.invokeLifecycleCallbacksInternal(result);
540
1227
  if (result.status === "failed") {
541
1228
  throw new NonRetriableError(`Workflow failed`, {
542
1229
  cause: result
@@ -566,32 +1253,65 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
566
1253
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
567
1254
  }
568
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
569
1285
  function isAgent(params) {
570
1286
  return params?.component === "AGENT";
571
1287
  }
572
1288
  function isTool(params) {
573
1289
  return params instanceof Tool;
574
1290
  }
1291
+ function isInngestWorkflow(params) {
1292
+ return params instanceof InngestWorkflow;
1293
+ }
575
1294
  function createStep(params, agentOptions) {
1295
+ if (isInngestWorkflow(params)) {
1296
+ return params;
1297
+ }
576
1298
  if (isAgent(params)) {
1299
+ const outputSchema = agentOptions?.structuredOutput?.schema ?? z.object({ text: z.string() });
577
1300
  return {
578
1301
  id: params.name,
579
1302
  description: params.getDescription(),
580
- // @ts-ignore
581
1303
  inputSchema: z.object({
582
1304
  prompt: z.string()
583
1305
  // resourceId: z.string().optional(),
584
1306
  // threadId: z.string().optional(),
585
1307
  }),
586
- // @ts-ignore
587
- outputSchema: z.object({
588
- text: z.string()
589
- }),
1308
+ outputSchema,
590
1309
  execute: async ({
591
1310
  inputData,
592
- [EMITTER_SYMBOL]: emitter,
1311
+ runId,
1312
+ [PUBSUB_SYMBOL]: pubsub,
593
1313
  [STREAM_FORMAT_SYMBOL]: streamFormat,
594
- runtimeContext,
1314
+ requestContext,
595
1315
  tracingContext,
596
1316
  abortSignal,
597
1317
  abort,
@@ -602,6 +1322,7 @@ function createStep(params, agentOptions) {
602
1322
  streamPromise.resolve = resolve;
603
1323
  streamPromise.reject = reject;
604
1324
  });
1325
+ let structuredResult = null;
605
1326
  const toolData = {
606
1327
  name: params.name,
607
1328
  args: inputData
@@ -612,9 +1333,13 @@ function createStep(params, agentOptions) {
612
1333
  ...agentOptions ?? {},
613
1334
  // resourceId: inputData.resourceId,
614
1335
  // threadId: inputData.threadId,
615
- runtimeContext,
1336
+ requestContext,
616
1337
  tracingContext,
617
1338
  onFinish: (result) => {
1339
+ const resultWithObject = result;
1340
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1341
+ structuredResult = resultWithObject.object;
1342
+ }
618
1343
  streamPromise.resolve(result.text);
619
1344
  void agentOptions?.onFinish?.(result);
620
1345
  },
@@ -624,9 +1349,13 @@ function createStep(params, agentOptions) {
624
1349
  } else {
625
1350
  const modelOutput = await params.stream(inputData.prompt, {
626
1351
  ...agentOptions ?? {},
627
- runtimeContext,
1352
+ requestContext,
628
1353
  tracingContext,
629
1354
  onFinish: (result) => {
1355
+ const resultWithObject = result;
1356
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1357
+ structuredResult = resultWithObject.object;
1358
+ }
630
1359
  streamPromise.resolve(result.text);
631
1360
  void agentOptions?.onFinish?.(result);
632
1361
  },
@@ -635,22 +1364,24 @@ function createStep(params, agentOptions) {
635
1364
  stream = modelOutput.fullStream;
636
1365
  }
637
1366
  if (streamFormat === "legacy") {
638
- await emitter.emit("watch-v2", {
639
- type: "tool-call-streaming-start",
640
- ...toolData ?? {}
1367
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1368
+ type: "watch",
1369
+ runId,
1370
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
641
1371
  });
642
1372
  for await (const chunk of stream) {
643
1373
  if (chunk.type === "text-delta") {
644
- await emitter.emit("watch-v2", {
645
- type: "tool-call-delta",
646
- ...toolData ?? {},
647
- 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 }
648
1378
  });
649
1379
  }
650
1380
  }
651
- await emitter.emit("watch-v2", {
652
- type: "tool-call-streaming-finish",
653
- ...toolData ?? {}
1381
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1382
+ type: "watch",
1383
+ runId,
1384
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
654
1385
  });
655
1386
  } else {
656
1387
  for await (const chunk of stream) {
@@ -660,6 +1391,9 @@ function createStep(params, agentOptions) {
660
1391
  if (abortSignal.aborted) {
661
1392
  return abort();
662
1393
  }
1394
+ if (structuredResult !== null) {
1395
+ return structuredResult;
1396
+ }
663
1397
  return {
664
1398
  text: await streamPromise.promise
665
1399
  };
@@ -673,20 +1407,38 @@ function createStep(params, agentOptions) {
673
1407
  }
674
1408
  return {
675
1409
  // TODO: tool probably should have strong id type
676
- // @ts-ignore
677
1410
  id: params.id,
678
1411
  description: params.description,
679
1412
  inputSchema: params.inputSchema,
680
1413
  outputSchema: params.outputSchema,
681
- execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
682
- return params.execute({
683
- context: inputData,
684
- mastra: wrapMastra(mastra, tracingContext),
685
- runtimeContext,
1414
+ suspendSchema: params.suspendSchema,
1415
+ resumeSchema: params.resumeSchema,
1416
+ execute: async ({
1417
+ inputData,
1418
+ mastra,
1419
+ requestContext,
1420
+ tracingContext,
1421
+ suspend,
1422
+ resumeData,
1423
+ runId,
1424
+ workflowId,
1425
+ state,
1426
+ setState
1427
+ }) => {
1428
+ const toolContext = {
1429
+ mastra,
1430
+ requestContext,
686
1431
  tracingContext,
687
- suspend,
688
- resumeData
689
- });
1432
+ workflow: {
1433
+ runId,
1434
+ resumeData,
1435
+ suspend,
1436
+ workflowId,
1437
+ state,
1438
+ setState
1439
+ }
1440
+ };
1441
+ return params.execute(inputData, toolContext);
690
1442
  },
691
1443
  component: "TOOL"
692
1444
  };
@@ -720,6 +1472,8 @@ function init(inngest) {
720
1472
  suspendSchema: step.suspendSchema,
721
1473
  stateSchema: step.stateSchema,
722
1474
  execute: step.execute,
1475
+ retries: step.retries,
1476
+ scorers: step.scorers,
723
1477
  component: step.component
724
1478
  };
725
1479
  },
@@ -729,7 +1483,8 @@ function init(inngest) {
729
1483
  inputSchema: workflow.inputSchema,
730
1484
  outputSchema: workflow.outputSchema,
731
1485
  steps: workflow.stepDefs,
732
- mastra: workflow.mastra
1486
+ mastra: workflow.mastra,
1487
+ options: workflow.options
733
1488
  });
734
1489
  wf.setStepFlow(workflow.stepGraph);
735
1490
  wf.commit();
@@ -737,952 +1492,7 @@ function init(inngest) {
737
1492
  }
738
1493
  };
739
1494
  }
740
- var InngestExecutionEngine = class extends DefaultExecutionEngine {
741
- inngestStep;
742
- inngestAttempts;
743
- constructor(mastra, inngestStep, inngestAttempts = 0, options) {
744
- super({ mastra, options });
745
- this.inngestStep = inngestStep;
746
- this.inngestAttempts = inngestAttempts;
747
- }
748
- async fmtReturnValue(emitter, stepResults, lastOutput, error) {
749
- const base = {
750
- status: lastOutput.status,
751
- steps: stepResults
752
- };
753
- if (lastOutput.status === "success") {
754
- await emitter.emit("watch", {
755
- type: "watch",
756
- payload: {
757
- workflowState: {
758
- status: lastOutput.status,
759
- steps: stepResults,
760
- result: lastOutput.output
761
- }
762
- },
763
- eventTimestamp: Date.now()
764
- });
765
- base.result = lastOutput.output;
766
- } else if (lastOutput.status === "failed") {
767
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
768
- await emitter.emit("watch", {
769
- type: "watch",
770
- payload: {
771
- workflowState: {
772
- status: lastOutput.status,
773
- steps: stepResults,
774
- result: null,
775
- error: base.error
776
- }
777
- },
778
- eventTimestamp: Date.now()
779
- });
780
- } else if (lastOutput.status === "suspended") {
781
- await emitter.emit("watch", {
782
- type: "watch",
783
- payload: {
784
- workflowState: {
785
- status: lastOutput.status,
786
- steps: stepResults,
787
- result: null,
788
- error: null
789
- }
790
- },
791
- eventTimestamp: Date.now()
792
- });
793
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
794
- if (stepResult?.status === "suspended") {
795
- const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
796
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
797
- }
798
- return [];
799
- });
800
- base.suspended = suspendedStepIds;
801
- }
802
- return base;
803
- }
804
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
805
- // await this.inngestStep.sleep(id, duration);
806
- // }
807
- async executeSleep({
808
- workflowId,
809
- runId,
810
- entry,
811
- prevOutput,
812
- stepResults,
813
- emitter,
814
- abortController,
815
- runtimeContext,
816
- executionContext,
817
- writableStream,
818
- tracingContext
819
- }) {
820
- let { duration, fn } = entry;
821
- const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
822
- type: AISpanType.WORKFLOW_SLEEP,
823
- name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
824
- attributes: {
825
- durationMs: duration,
826
- sleepType: fn ? "dynamic" : "fixed"
827
- },
828
- tracingPolicy: this.options?.tracingPolicy
829
- });
830
- if (fn) {
831
- const stepCallId = randomUUID();
832
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
833
- return await fn(
834
- createDeprecationProxy(
835
- {
836
- runId,
837
- workflowId,
838
- mastra: this.mastra,
839
- runtimeContext,
840
- inputData: prevOutput,
841
- state: executionContext.state,
842
- setState: (state) => {
843
- executionContext.state = state;
844
- },
845
- runCount: -1,
846
- retryCount: -1,
847
- tracingContext: {
848
- currentSpan: sleepSpan
849
- },
850
- getInitData: () => stepResults?.input,
851
- getStepResult: getStepResult.bind(this, stepResults),
852
- // TODO: this function shouldn't have suspend probably?
853
- suspend: async (_suspendPayload) => {
854
- },
855
- bail: () => {
856
- },
857
- abort: () => {
858
- abortController?.abort();
859
- },
860
- [EMITTER_SYMBOL]: emitter,
861
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
862
- engine: { step: this.inngestStep },
863
- abortSignal: abortController?.signal,
864
- writer: new ToolStream(
865
- {
866
- prefix: "workflow-step",
867
- callId: stepCallId,
868
- name: "sleep",
869
- runId
870
- },
871
- writableStream
872
- )
873
- },
874
- {
875
- paramName: "runCount",
876
- deprecationMessage: runCountDeprecationMessage,
877
- logger: this.logger
878
- }
879
- )
880
- );
881
- });
882
- sleepSpan?.update({
883
- attributes: {
884
- durationMs: duration
885
- }
886
- });
887
- }
888
- try {
889
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
890
- sleepSpan?.end();
891
- } catch (e) {
892
- sleepSpan?.error({ error: e });
893
- throw e;
894
- }
895
- }
896
- async executeSleepUntil({
897
- workflowId,
898
- runId,
899
- entry,
900
- prevOutput,
901
- stepResults,
902
- emitter,
903
- abortController,
904
- runtimeContext,
905
- executionContext,
906
- writableStream,
907
- tracingContext
908
- }) {
909
- let { date, fn } = entry;
910
- const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
911
- type: AISpanType.WORKFLOW_SLEEP,
912
- name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
913
- attributes: {
914
- untilDate: date,
915
- durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
916
- sleepType: fn ? "dynamic" : "fixed"
917
- },
918
- tracingPolicy: this.options?.tracingPolicy
919
- });
920
- if (fn) {
921
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
922
- const stepCallId = randomUUID();
923
- return await fn(
924
- createDeprecationProxy(
925
- {
926
- runId,
927
- workflowId,
928
- mastra: this.mastra,
929
- runtimeContext,
930
- inputData: prevOutput,
931
- state: executionContext.state,
932
- setState: (state) => {
933
- executionContext.state = state;
934
- },
935
- runCount: -1,
936
- retryCount: -1,
937
- tracingContext: {
938
- currentSpan: sleepUntilSpan
939
- },
940
- getInitData: () => stepResults?.input,
941
- getStepResult: getStepResult.bind(this, stepResults),
942
- // TODO: this function shouldn't have suspend probably?
943
- suspend: async (_suspendPayload) => {
944
- },
945
- bail: () => {
946
- },
947
- abort: () => {
948
- abortController?.abort();
949
- },
950
- [EMITTER_SYMBOL]: emitter,
951
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
952
- engine: { step: this.inngestStep },
953
- abortSignal: abortController?.signal,
954
- writer: new ToolStream(
955
- {
956
- prefix: "workflow-step",
957
- callId: stepCallId,
958
- name: "sleep",
959
- runId
960
- },
961
- writableStream
962
- )
963
- },
964
- {
965
- paramName: "runCount",
966
- deprecationMessage: runCountDeprecationMessage,
967
- logger: this.logger
968
- }
969
- )
970
- );
971
- });
972
- if (date && !(date instanceof Date)) {
973
- date = new Date(date);
974
- }
975
- const time = !date ? 0 : date.getTime() - Date.now();
976
- sleepUntilSpan?.update({
977
- attributes: {
978
- durationMs: Math.max(0, time)
979
- }
980
- });
981
- }
982
- if (!(date instanceof Date)) {
983
- sleepUntilSpan?.end();
984
- return;
985
- }
986
- try {
987
- await this.inngestStep.sleepUntil(entry.id, date);
988
- sleepUntilSpan?.end();
989
- } catch (e) {
990
- sleepUntilSpan?.error({ error: e });
991
- throw e;
992
- }
993
- }
994
- async executeWaitForEvent({ event, timeout }) {
995
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
996
- event: `user-event-${event}`,
997
- timeout: timeout ?? 5e3
998
- });
999
- if (eventData === null) {
1000
- throw "Timeout waiting for event";
1001
- }
1002
- return eventData?.data;
1003
- }
1004
- async executeStep({
1005
- step,
1006
- stepResults,
1007
- executionContext,
1008
- resume,
1009
- prevOutput,
1010
- emitter,
1011
- abortController,
1012
- runtimeContext,
1013
- tracingContext,
1014
- writableStream,
1015
- disableScorers
1016
- }) {
1017
- const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
1018
- name: `workflow step: '${step.id}'`,
1019
- type: AISpanType.WORKFLOW_STEP,
1020
- input: prevOutput,
1021
- attributes: {
1022
- stepId: step.id
1023
- },
1024
- tracingPolicy: this.options?.tracingPolicy
1025
- });
1026
- const { inputData, validationError } = await validateStepInput({
1027
- prevOutput,
1028
- step,
1029
- validateInputs: this.options?.validateInputs ?? false
1030
- });
1031
- const startedAt = await this.inngestStep.run(
1032
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
1033
- async () => {
1034
- const startedAt2 = Date.now();
1035
- await emitter.emit("watch", {
1036
- type: "watch",
1037
- payload: {
1038
- currentStep: {
1039
- id: step.id,
1040
- status: "running"
1041
- },
1042
- workflowState: {
1043
- status: "running",
1044
- steps: {
1045
- ...stepResults,
1046
- [step.id]: {
1047
- status: "running"
1048
- }
1049
- },
1050
- result: null,
1051
- error: null
1052
- }
1053
- },
1054
- eventTimestamp: Date.now()
1055
- });
1056
- await emitter.emit("watch-v2", {
1057
- type: "workflow-step-start",
1058
- payload: {
1059
- id: step.id,
1060
- status: "running",
1061
- payload: inputData,
1062
- startedAt: startedAt2
1063
- }
1064
- });
1065
- return startedAt2;
1066
- }
1067
- );
1068
- if (step instanceof InngestWorkflow) {
1069
- const isResume = !!resume?.steps?.length;
1070
- let result;
1071
- let runId;
1072
- try {
1073
- if (isResume) {
1074
- runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
1075
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1076
- workflowName: step.id,
1077
- runId
1078
- });
1079
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1080
- function: step.getFunction(),
1081
- data: {
1082
- inputData,
1083
- initialState: executionContext.state ?? snapshot?.value ?? {},
1084
- runId,
1085
- resume: {
1086
- runId,
1087
- steps: resume.steps.slice(1),
1088
- stepResults: snapshot?.context,
1089
- resumePayload: resume.resumePayload,
1090
- // @ts-ignore
1091
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1092
- },
1093
- outputOptions: { includeState: true }
1094
- }
1095
- });
1096
- result = invokeResp.result;
1097
- runId = invokeResp.runId;
1098
- executionContext.state = invokeResp.result.state;
1099
- } else {
1100
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1101
- function: step.getFunction(),
1102
- data: {
1103
- inputData,
1104
- initialState: executionContext.state ?? {},
1105
- outputOptions: { includeState: true }
1106
- }
1107
- });
1108
- result = invokeResp.result;
1109
- runId = invokeResp.runId;
1110
- executionContext.state = invokeResp.result.state;
1111
- }
1112
- } catch (e) {
1113
- const errorCause = e?.cause;
1114
- if (errorCause && typeof errorCause === "object") {
1115
- result = errorCause;
1116
- runId = errorCause.runId || randomUUID();
1117
- } else {
1118
- runId = randomUUID();
1119
- result = {
1120
- status: "failed",
1121
- error: e instanceof Error ? e : new Error(String(e)),
1122
- steps: {},
1123
- input: inputData
1124
- };
1125
- }
1126
- }
1127
- const res = await this.inngestStep.run(
1128
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
1129
- async () => {
1130
- if (result.status === "failed") {
1131
- await emitter.emit("watch", {
1132
- type: "watch",
1133
- payload: {
1134
- currentStep: {
1135
- id: step.id,
1136
- status: "failed",
1137
- error: result?.error
1138
- },
1139
- workflowState: {
1140
- status: "running",
1141
- steps: stepResults,
1142
- result: null,
1143
- error: null
1144
- }
1145
- },
1146
- eventTimestamp: Date.now()
1147
- });
1148
- await emitter.emit("watch-v2", {
1149
- type: "workflow-step-result",
1150
- payload: {
1151
- id: step.id,
1152
- status: "failed",
1153
- error: result?.error,
1154
- payload: prevOutput
1155
- }
1156
- });
1157
- return { executionContext, result: { status: "failed", error: result?.error } };
1158
- } else if (result.status === "suspended") {
1159
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
1160
- const stepRes2 = stepResult;
1161
- return stepRes2?.status === "suspended";
1162
- });
1163
- for (const [stepName, stepResult] of suspendedSteps) {
1164
- const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
1165
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1166
- await emitter.emit("watch", {
1167
- type: "watch",
1168
- payload: {
1169
- currentStep: {
1170
- id: step.id,
1171
- status: "suspended",
1172
- payload: stepResult.payload,
1173
- suspendPayload: {
1174
- ...stepResult?.suspendPayload,
1175
- __workflow_meta: { runId, path: suspendPath }
1176
- }
1177
- },
1178
- workflowState: {
1179
- status: "running",
1180
- steps: stepResults,
1181
- result: null,
1182
- error: null
1183
- }
1184
- },
1185
- eventTimestamp: Date.now()
1186
- });
1187
- await emitter.emit("watch-v2", {
1188
- type: "workflow-step-suspended",
1189
- payload: {
1190
- id: step.id,
1191
- status: "suspended"
1192
- }
1193
- });
1194
- return {
1195
- executionContext,
1196
- result: {
1197
- status: "suspended",
1198
- payload: stepResult.payload,
1199
- suspendPayload: {
1200
- ...stepResult?.suspendPayload,
1201
- __workflow_meta: { runId, path: suspendPath }
1202
- }
1203
- }
1204
- };
1205
- }
1206
- await emitter.emit("watch", {
1207
- type: "watch",
1208
- payload: {
1209
- currentStep: {
1210
- id: step.id,
1211
- status: "suspended",
1212
- payload: {}
1213
- },
1214
- workflowState: {
1215
- status: "running",
1216
- steps: stepResults,
1217
- result: null,
1218
- error: null
1219
- }
1220
- },
1221
- eventTimestamp: Date.now()
1222
- });
1223
- return {
1224
- executionContext,
1225
- result: {
1226
- status: "suspended",
1227
- payload: {}
1228
- }
1229
- };
1230
- }
1231
- await emitter.emit("watch", {
1232
- type: "watch",
1233
- payload: {
1234
- currentStep: {
1235
- id: step.id,
1236
- status: "success",
1237
- output: result?.result
1238
- },
1239
- workflowState: {
1240
- status: "running",
1241
- steps: stepResults,
1242
- result: null,
1243
- error: null
1244
- }
1245
- },
1246
- eventTimestamp: Date.now()
1247
- });
1248
- await emitter.emit("watch-v2", {
1249
- type: "workflow-step-result",
1250
- payload: {
1251
- id: step.id,
1252
- status: "success",
1253
- output: result?.result
1254
- }
1255
- });
1256
- await emitter.emit("watch-v2", {
1257
- type: "workflow-step-finish",
1258
- payload: {
1259
- id: step.id,
1260
- metadata: {}
1261
- }
1262
- });
1263
- return { executionContext, result: { status: "success", output: result?.result } };
1264
- }
1265
- );
1266
- Object.assign(executionContext, res.executionContext);
1267
- return {
1268
- ...res.result,
1269
- startedAt,
1270
- endedAt: Date.now(),
1271
- payload: inputData,
1272
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1273
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1274
- };
1275
- }
1276
- const stepCallId = randomUUID();
1277
- let stepRes;
1278
- try {
1279
- stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1280
- let execResults;
1281
- let suspended;
1282
- let bailed;
1283
- try {
1284
- if (validationError) {
1285
- throw validationError;
1286
- }
1287
- const result = await step.execute({
1288
- runId: executionContext.runId,
1289
- mastra: this.mastra,
1290
- runtimeContext,
1291
- writer: new ToolStream(
1292
- {
1293
- prefix: "workflow-step",
1294
- callId: stepCallId,
1295
- name: step.id,
1296
- runId: executionContext.runId
1297
- },
1298
- writableStream
1299
- ),
1300
- state: executionContext?.state ?? {},
1301
- setState: (state) => {
1302
- executionContext.state = state;
1303
- },
1304
- inputData,
1305
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1306
- tracingContext: {
1307
- currentSpan: stepAISpan
1308
- },
1309
- getInitData: () => stepResults?.input,
1310
- getStepResult: getStepResult.bind(this, stepResults),
1311
- suspend: async (suspendPayload, suspendOptions) => {
1312
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1313
- if (suspendOptions?.resumeLabel) {
1314
- const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1315
- for (const label of resumeLabel) {
1316
- executionContext.resumeLabels[label] = {
1317
- stepId: step.id,
1318
- foreachIndex: executionContext.foreachIndex
1319
- };
1320
- }
1321
- }
1322
- suspended = { payload: suspendPayload };
1323
- },
1324
- bail: (result2) => {
1325
- bailed = { payload: result2 };
1326
- },
1327
- resume: {
1328
- steps: resume?.steps?.slice(1) || [],
1329
- resumePayload: resume?.resumePayload,
1330
- // @ts-ignore
1331
- runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
1332
- },
1333
- [EMITTER_SYMBOL]: emitter,
1334
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1335
- engine: {
1336
- step: this.inngestStep
1337
- },
1338
- abortSignal: abortController.signal
1339
- });
1340
- const endedAt = Date.now();
1341
- execResults = {
1342
- status: "success",
1343
- output: result,
1344
- startedAt,
1345
- endedAt,
1346
- payload: inputData,
1347
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1348
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1349
- };
1350
- } catch (e) {
1351
- const stepFailure = {
1352
- status: "failed",
1353
- payload: inputData,
1354
- error: e instanceof Error ? e.message : String(e),
1355
- endedAt: Date.now(),
1356
- startedAt,
1357
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1358
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1359
- };
1360
- execResults = stepFailure;
1361
- const fallbackErrorMessage = `Step ${step.id} failed`;
1362
- stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1363
- throw new RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1364
- cause: execResults
1365
- });
1366
- }
1367
- if (suspended) {
1368
- execResults = {
1369
- status: "suspended",
1370
- suspendPayload: suspended.payload,
1371
- payload: inputData,
1372
- suspendedAt: Date.now(),
1373
- startedAt,
1374
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1375
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1376
- };
1377
- } else if (bailed) {
1378
- execResults = {
1379
- status: "bailed",
1380
- output: bailed.payload,
1381
- payload: inputData,
1382
- endedAt: Date.now(),
1383
- startedAt
1384
- };
1385
- }
1386
- await emitter.emit("watch", {
1387
- type: "watch",
1388
- payload: {
1389
- currentStep: {
1390
- id: step.id,
1391
- ...execResults
1392
- },
1393
- workflowState: {
1394
- status: "running",
1395
- steps: { ...stepResults, [step.id]: execResults },
1396
- result: null,
1397
- error: null
1398
- }
1399
- },
1400
- eventTimestamp: Date.now()
1401
- });
1402
- if (execResults.status === "suspended") {
1403
- await emitter.emit("watch-v2", {
1404
- type: "workflow-step-suspended",
1405
- payload: {
1406
- id: step.id,
1407
- ...execResults
1408
- }
1409
- });
1410
- } else {
1411
- await emitter.emit("watch-v2", {
1412
- type: "workflow-step-result",
1413
- payload: {
1414
- id: step.id,
1415
- ...execResults
1416
- }
1417
- });
1418
- await emitter.emit("watch-v2", {
1419
- type: "workflow-step-finish",
1420
- payload: {
1421
- id: step.id,
1422
- metadata: {}
1423
- }
1424
- });
1425
- }
1426
- stepAISpan?.end({ output: execResults });
1427
- return { result: execResults, executionContext, stepResults };
1428
- });
1429
- } catch (e) {
1430
- const stepFailure = e instanceof Error ? e?.cause : {
1431
- status: "failed",
1432
- error: e instanceof Error ? e.message : String(e),
1433
- payload: inputData,
1434
- startedAt,
1435
- endedAt: Date.now()
1436
- };
1437
- stepRes = {
1438
- result: stepFailure,
1439
- executionContext,
1440
- stepResults: {
1441
- ...stepResults,
1442
- [step.id]: stepFailure
1443
- }
1444
- };
1445
- }
1446
- if (disableScorers !== false && stepRes.result.status === "success") {
1447
- await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1448
- if (step.scorers) {
1449
- await this.runScorers({
1450
- scorers: step.scorers,
1451
- runId: executionContext.runId,
1452
- input: inputData,
1453
- output: stepRes.result,
1454
- workflowId: executionContext.workflowId,
1455
- stepId: step.id,
1456
- runtimeContext,
1457
- disableScorers,
1458
- tracingContext: { currentSpan: stepAISpan }
1459
- });
1460
- }
1461
- });
1462
- }
1463
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1464
- Object.assign(stepResults, stepRes.stepResults);
1465
- executionContext.state = stepRes.executionContext.state;
1466
- return stepRes.result;
1467
- }
1468
- async persistStepUpdate({
1469
- workflowId,
1470
- runId,
1471
- stepResults,
1472
- resourceId,
1473
- executionContext,
1474
- serializedStepGraph,
1475
- workflowStatus,
1476
- result,
1477
- error
1478
- }) {
1479
- await this.inngestStep.run(
1480
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1481
- async () => {
1482
- const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1483
- if (!shouldPersistSnapshot) {
1484
- return;
1485
- }
1486
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1487
- workflowName: workflowId,
1488
- runId,
1489
- resourceId,
1490
- snapshot: {
1491
- runId,
1492
- value: executionContext.state,
1493
- context: stepResults,
1494
- activePaths: [],
1495
- suspendedPaths: executionContext.suspendedPaths,
1496
- resumeLabels: executionContext.resumeLabels,
1497
- waitingPaths: {},
1498
- serializedStepGraph,
1499
- status: workflowStatus,
1500
- result,
1501
- error,
1502
- // @ts-ignore
1503
- timestamp: Date.now()
1504
- }
1505
- });
1506
- }
1507
- );
1508
- }
1509
- async executeConditional({
1510
- workflowId,
1511
- runId,
1512
- entry,
1513
- prevOutput,
1514
- stepResults,
1515
- resume,
1516
- executionContext,
1517
- emitter,
1518
- abortController,
1519
- runtimeContext,
1520
- writableStream,
1521
- disableScorers,
1522
- tracingContext
1523
- }) {
1524
- const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1525
- type: AISpanType.WORKFLOW_CONDITIONAL,
1526
- name: `conditional: '${entry.conditions.length} conditions'`,
1527
- input: prevOutput,
1528
- attributes: {
1529
- conditionCount: entry.conditions.length
1530
- },
1531
- tracingPolicy: this.options?.tracingPolicy
1532
- });
1533
- let execResults;
1534
- const truthyIndexes = (await Promise.all(
1535
- entry.conditions.map(
1536
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1537
- const evalSpan = conditionalSpan?.createChildSpan({
1538
- type: AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1539
- name: `condition: '${index}'`,
1540
- input: prevOutput,
1541
- attributes: {
1542
- conditionIndex: index
1543
- },
1544
- tracingPolicy: this.options?.tracingPolicy
1545
- });
1546
- try {
1547
- const result = await cond(
1548
- createDeprecationProxy(
1549
- {
1550
- runId,
1551
- workflowId,
1552
- mastra: this.mastra,
1553
- runtimeContext,
1554
- runCount: -1,
1555
- retryCount: -1,
1556
- inputData: prevOutput,
1557
- state: executionContext.state,
1558
- setState: (state) => {
1559
- executionContext.state = state;
1560
- },
1561
- tracingContext: {
1562
- currentSpan: evalSpan
1563
- },
1564
- getInitData: () => stepResults?.input,
1565
- getStepResult: getStepResult.bind(this, stepResults),
1566
- // TODO: this function shouldn't have suspend probably?
1567
- suspend: async (_suspendPayload) => {
1568
- },
1569
- bail: () => {
1570
- },
1571
- abort: () => {
1572
- abortController.abort();
1573
- },
1574
- [EMITTER_SYMBOL]: emitter,
1575
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1576
- engine: {
1577
- step: this.inngestStep
1578
- },
1579
- abortSignal: abortController.signal,
1580
- writer: new ToolStream(
1581
- {
1582
- prefix: "workflow-step",
1583
- callId: randomUUID(),
1584
- name: "conditional",
1585
- runId
1586
- },
1587
- writableStream
1588
- )
1589
- },
1590
- {
1591
- paramName: "runCount",
1592
- deprecationMessage: runCountDeprecationMessage,
1593
- logger: this.logger
1594
- }
1595
- )
1596
- );
1597
- evalSpan?.end({
1598
- output: result,
1599
- attributes: {
1600
- result: !!result
1601
- }
1602
- });
1603
- return result ? index : null;
1604
- } catch (e) {
1605
- evalSpan?.error({
1606
- error: e instanceof Error ? e : new Error(String(e)),
1607
- attributes: {
1608
- result: false
1609
- }
1610
- });
1611
- return null;
1612
- }
1613
- })
1614
- )
1615
- )).filter((index) => index !== null);
1616
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1617
- conditionalSpan?.update({
1618
- attributes: {
1619
- truthyIndexes,
1620
- selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1621
- }
1622
- });
1623
- const results = await Promise.all(
1624
- stepsToRun.map(async (step, index) => {
1625
- const currStepResult = stepResults[step.step.id];
1626
- if (currStepResult && currStepResult.status === "success") {
1627
- return currStepResult;
1628
- }
1629
- const result = await this.executeStep({
1630
- step: step.step,
1631
- prevOutput,
1632
- stepResults,
1633
- resume,
1634
- executionContext: {
1635
- workflowId,
1636
- runId,
1637
- executionPath: [...executionContext.executionPath, index],
1638
- suspendedPaths: executionContext.suspendedPaths,
1639
- resumeLabels: executionContext.resumeLabels,
1640
- retryConfig: executionContext.retryConfig,
1641
- state: executionContext.state
1642
- },
1643
- emitter,
1644
- abortController,
1645
- runtimeContext,
1646
- writableStream,
1647
- disableScorers,
1648
- tracingContext: {
1649
- currentSpan: conditionalSpan
1650
- }
1651
- });
1652
- stepResults[step.step.id] = result;
1653
- return result;
1654
- })
1655
- );
1656
- const hasFailed = results.find((result) => result.status === "failed");
1657
- const hasSuspended = results.find((result) => result.status === "suspended");
1658
- if (hasFailed) {
1659
- execResults = { status: "failed", error: hasFailed.error };
1660
- } else if (hasSuspended) {
1661
- execResults = { status: "suspended", suspendPayload: hasSuspended.suspendPayload };
1662
- } else {
1663
- execResults = {
1664
- status: "success",
1665
- output: results.reduce((acc, result, index) => {
1666
- if (result.status === "success") {
1667
- acc[stepsToRun[index].step.id] = result.output;
1668
- }
1669
- return acc;
1670
- }, {})
1671
- };
1672
- }
1673
- if (execResults.status === "failed") {
1674
- conditionalSpan?.error({
1675
- error: new Error(execResults.error)
1676
- });
1677
- } else {
1678
- conditionalSpan?.end({
1679
- output: execResults.output || execResults
1680
- });
1681
- }
1682
- return execResults;
1683
- }
1684
- };
1685
1495
 
1686
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1496
+ export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
1687
1497
  //# sourceMappingURL=index.js.map
1688
1498
  //# sourceMappingURL=index.js.map