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