@mastra/inngest 0.0.0-cloud-deployer-for-core-0.19.1-20251001164939 → 0.0.0-cloud-604-map-nested-flow-details-to-side-panel-20251212192149

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