@mastra/inngest 0.0.0-new-scorer-api-20250801075530 → 0.0.0-new-button-export-20251219130424

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