@mastra/inngest 0.0.0-transpile-packages-20250731152758 → 0.0.0-type-testing-20260120105120

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,628 @@
1
+ import { MessageList, Agent, TripWire } from '@mastra/core/agent';
2
+ import { getErrorFromUnknown, MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
3
+ import { EntityType, SpanType } from '@mastra/core/observability';
4
+ import { ProcessorStepOutputSchema, ProcessorStepSchema, ProcessorRunner } from '@mastra/core/processors';
5
+ import { Tool } from '@mastra/core/tools';
6
+ import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, hydrateSerializedStepErrors, Workflow } from '@mastra/core/workflows';
7
+ import { PUBSUB_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
8
+ import { z } from 'zod';
1
9
  import { randomUUID } from 'crypto';
10
+ import { RequestContext } from '@mastra/core/di';
11
+ import { NonRetriableError } from 'inngest';
2
12
  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';
13
+ import { PubSub } from '@mastra/core/events';
14
+ import { ReadableStream } from 'stream/web';
15
+ import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
7
16
  import { serve as serve$1 } from 'inngest/hono';
8
- import { z } from 'zod';
9
17
 
10
18
  // 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();
19
+ var InngestExecutionEngine = class extends DefaultExecutionEngine {
20
+ inngestStep;
21
+ inngestAttempts;
22
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
23
+ super({ mastra, options });
24
+ this.inngestStep = inngestStep;
25
+ this.inngestAttempts = inngestAttempts;
26
+ }
27
+ // =============================================================================
28
+ // Hook Overrides
29
+ // =============================================================================
30
+ /**
31
+ * Format errors while preserving Error instances and their custom properties.
32
+ * Uses getErrorFromUnknown to ensure all error properties are preserved.
33
+ */
34
+ formatResultError(error, lastOutput) {
35
+ const outputError = lastOutput?.error;
36
+ const errorSource = error || outputError;
37
+ const errorInstance = getErrorFromUnknown(errorSource, {
38
+ serializeStack: true,
39
+ // Include stack in JSON for better debugging in Inngest
40
+ fallbackMessage: "Unknown workflow error"
41
+ });
42
+ return errorInstance.toJSON();
43
+ }
44
+ /**
45
+ * Detect InngestWorkflow instances for special nested workflow handling
46
+ */
47
+ isNestedWorkflowStep(step) {
48
+ return step instanceof InngestWorkflow;
49
+ }
50
+ /**
51
+ * Inngest requires requestContext serialization for memoization.
52
+ * When steps are replayed, the original function doesn't re-execute,
53
+ * so requestContext modifications must be captured and restored.
54
+ */
55
+ requiresDurableContextSerialization() {
56
+ return true;
57
+ }
58
+ /**
59
+ * Execute a step with retry logic for Inngest.
60
+ * Retries are handled via step-level retry (RetryAfterError thrown INSIDE step.run()).
61
+ * After retries exhausted, error propagates here and we return a failed result.
62
+ */
63
+ async executeStepWithRetry(stepId, runStep, params) {
64
+ for (let i = 0; i < params.retries + 1; i++) {
65
+ if (i > 0 && params.delay) {
66
+ await new Promise((resolve) => setTimeout(resolve, params.delay));
67
+ }
68
+ try {
69
+ const result = await this.wrapDurableOperation(stepId, runStep);
70
+ return { ok: true, result };
71
+ } catch (e) {
72
+ if (i === params.retries) {
73
+ const cause = e?.cause;
74
+ if (cause?.status === "failed") {
75
+ params.stepSpan?.error({
76
+ error: e,
77
+ attributes: { status: "failed" }
78
+ });
79
+ if (cause.error && !(cause.error instanceof Error)) {
80
+ cause.error = getErrorFromUnknown(cause.error, { serializeStack: false });
81
+ }
82
+ return { ok: false, error: cause };
83
+ }
84
+ const errorInstance = getErrorFromUnknown(e, {
85
+ serializeStack: false,
86
+ fallbackMessage: "Unknown step execution error"
87
+ });
88
+ params.stepSpan?.error({
89
+ error: errorInstance,
90
+ attributes: { status: "failed" }
91
+ });
92
+ return {
93
+ ok: false,
94
+ error: {
95
+ status: "failed",
96
+ error: errorInstance,
97
+ endedAt: Date.now()
98
+ }
99
+ };
19
100
  }
20
- return [];
21
- })
22
- )
23
- );
24
- return serve$1({
25
- client: inngest,
26
- functions
27
- });
28
- }
101
+ }
102
+ }
103
+ return { ok: false, error: { status: "failed", error: new Error("Unknown error"), endedAt: Date.now() } };
104
+ }
105
+ /**
106
+ * Use Inngest's sleep primitive for durability
107
+ */
108
+ async executeSleepDuration(duration, sleepId, workflowId) {
109
+ await this.inngestStep.sleep(`workflow.${workflowId}.sleep.${sleepId}`, duration < 0 ? 0 : duration);
110
+ }
111
+ /**
112
+ * Use Inngest's sleepUntil primitive for durability
113
+ */
114
+ async executeSleepUntilDate(date, sleepUntilId, workflowId) {
115
+ await this.inngestStep.sleepUntil(`workflow.${workflowId}.sleepUntil.${sleepUntilId}`, date);
116
+ }
117
+ /**
118
+ * Wrap durable operations in Inngest step.run() for durability.
119
+ *
120
+ * IMPORTANT: Errors are wrapped with a cause structure before throwing.
121
+ * This is necessary because Inngest's error serialization (serialize-error-cjs)
122
+ * only captures standard Error properties (message, name, stack, code, cause).
123
+ * Custom properties like statusCode, responseHeaders from AI SDK errors would
124
+ * be lost. By putting our serialized error (via getErrorFromUnknown with toJSON())
125
+ * in the cause property, we ensure custom properties survive serialization.
126
+ * The cause property is in serialize-error-cjs's allowlist, and when the cause
127
+ * object is finally JSON.stringify'd, our error's toJSON() is called.
128
+ */
129
+ async wrapDurableOperation(operationId, operationFn) {
130
+ return this.inngestStep.run(operationId, async () => {
131
+ try {
132
+ return await operationFn();
133
+ } catch (e) {
134
+ const errorInstance = getErrorFromUnknown(e, {
135
+ serializeStack: false,
136
+ fallbackMessage: "Unknown step execution error"
137
+ });
138
+ throw new Error(errorInstance.message, {
139
+ cause: {
140
+ status: "failed",
141
+ error: errorInstance,
142
+ endedAt: Date.now()
143
+ }
144
+ });
145
+ }
146
+ });
147
+ }
148
+ /**
149
+ * Provide Inngest step primitive in engine context
150
+ */
151
+ getEngineContext() {
152
+ return { step: this.inngestStep };
153
+ }
154
+ /**
155
+ * For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
156
+ * (wrapped in step.run for durability), not in execute(). Override to skip.
157
+ */
158
+ async invokeLifecycleCallbacks(_result) {
159
+ }
160
+ /**
161
+ * Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
162
+ */
163
+ async invokeLifecycleCallbacksInternal(result) {
164
+ return super.invokeLifecycleCallbacks(result);
165
+ }
166
+ // =============================================================================
167
+ // Durable Span Lifecycle Hooks
168
+ // =============================================================================
169
+ /**
170
+ * Create a step span durably - on first execution, creates and exports span.
171
+ * On replay, returns cached span data without re-creating.
172
+ */
173
+ async createStepSpan(params) {
174
+ const { executionContext, operationId, options, parentSpan } = params;
175
+ const parentSpanId = parentSpan?.id ?? executionContext.tracingIds?.workflowSpanId;
176
+ const exportedSpan = await this.wrapDurableOperation(operationId, async () => {
177
+ const observability = this.mastra?.observability?.getSelectedInstance({});
178
+ if (!observability) return void 0;
179
+ const span = observability.startSpan({
180
+ ...options,
181
+ entityType: options.entityType,
182
+ traceId: executionContext.tracingIds?.traceId,
183
+ parentSpanId
184
+ });
185
+ return span?.exportSpan();
186
+ });
187
+ if (exportedSpan) {
188
+ const observability = this.mastra?.observability?.getSelectedInstance({});
189
+ return observability?.rebuildSpan(exportedSpan);
190
+ }
191
+ return void 0;
192
+ }
193
+ /**
194
+ * End a step span durably.
195
+ */
196
+ async endStepSpan(params) {
197
+ const { span, operationId, endOptions } = params;
198
+ if (!span) return;
199
+ await this.wrapDurableOperation(operationId, async () => {
200
+ span.end(endOptions);
201
+ });
202
+ }
203
+ /**
204
+ * Record error on step span durably.
205
+ */
206
+ async errorStepSpan(params) {
207
+ const { span, operationId, errorOptions } = params;
208
+ if (!span) return;
209
+ await this.wrapDurableOperation(operationId, async () => {
210
+ span.error(errorOptions);
211
+ });
212
+ }
213
+ /**
214
+ * Create a generic child span durably (for control-flow operations).
215
+ * On first execution, creates and exports span. On replay, returns cached span data.
216
+ */
217
+ async createChildSpan(params) {
218
+ const { executionContext, operationId, options, parentSpan } = params;
219
+ const parentSpanId = parentSpan?.id ?? executionContext.tracingIds?.workflowSpanId;
220
+ const exportedSpan = await this.wrapDurableOperation(operationId, async () => {
221
+ const observability = this.mastra?.observability?.getSelectedInstance({});
222
+ if (!observability) return void 0;
223
+ const span = observability.startSpan({
224
+ ...options,
225
+ traceId: executionContext.tracingIds?.traceId,
226
+ parentSpanId
227
+ });
228
+ return span?.exportSpan();
229
+ });
230
+ if (exportedSpan) {
231
+ const observability = this.mastra?.observability?.getSelectedInstance({});
232
+ return observability?.rebuildSpan(exportedSpan);
233
+ }
234
+ return void 0;
235
+ }
236
+ /**
237
+ * End a generic child span durably (for control-flow operations).
238
+ */
239
+ async endChildSpan(params) {
240
+ const { span, operationId, endOptions } = params;
241
+ if (!span) return;
242
+ await this.wrapDurableOperation(operationId, async () => {
243
+ span.end(endOptions);
244
+ });
245
+ }
246
+ /**
247
+ * Record error on a generic child span durably (for control-flow operations).
248
+ */
249
+ async errorChildSpan(params) {
250
+ const { span, operationId, errorOptions } = params;
251
+ if (!span) return;
252
+ await this.wrapDurableOperation(operationId, async () => {
253
+ span.error(errorOptions);
254
+ });
255
+ }
256
+ /**
257
+ * Execute nested InngestWorkflow using inngestStep.invoke() for durability.
258
+ * This MUST be called directly (not inside step.run()) due to Inngest constraints.
259
+ */
260
+ async executeWorkflowStep(params) {
261
+ if (!(params.step instanceof InngestWorkflow)) {
262
+ return null;
263
+ }
264
+ const {
265
+ step,
266
+ stepResults,
267
+ executionContext,
268
+ resume,
269
+ timeTravel,
270
+ prevOutput,
271
+ inputData,
272
+ pubsub,
273
+ startedAt,
274
+ perStep,
275
+ stepSpan
276
+ } = params;
277
+ const nestedTracingContext = executionContext.tracingIds?.traceId ? {
278
+ traceId: executionContext.tracingIds.traceId,
279
+ parentSpanId: stepSpan?.id
280
+ } : void 0;
281
+ const isResume = !!resume?.steps?.length;
282
+ let result;
283
+ let runId;
284
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
285
+ try {
286
+ if (isResume) {
287
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
288
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
289
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
290
+ workflowName: step.id,
291
+ runId
292
+ });
293
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
294
+ function: step.getFunction(),
295
+ data: {
296
+ inputData,
297
+ initialState: executionContext.state ?? snapshot?.value ?? {},
298
+ runId,
299
+ resume: {
300
+ runId,
301
+ steps: resume.steps.slice(1),
302
+ stepResults: snapshot?.context,
303
+ resumePayload: resume.resumePayload,
304
+ resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
305
+ },
306
+ outputOptions: { includeState: true },
307
+ perStep,
308
+ tracingOptions: nestedTracingContext
309
+ }
310
+ });
311
+ result = invokeResp.result;
312
+ runId = invokeResp.runId;
313
+ executionContext.state = invokeResp.result.state;
314
+ } else if (isTimeTravel) {
315
+ const workflowsStoreForTimeTravel = await this.mastra?.getStorage()?.getStore("workflows");
316
+ const snapshot = await workflowsStoreForTimeTravel?.loadWorkflowSnapshot({
317
+ workflowName: step.id,
318
+ runId: executionContext.runId
319
+ }) ?? { context: {} };
320
+ const timeTravelParams = createTimeTravelExecutionParams({
321
+ steps: timeTravel.steps.slice(1),
322
+ inputData: timeTravel.inputData,
323
+ resumeData: timeTravel.resumeData,
324
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
325
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
326
+ snapshot,
327
+ graph: step.buildExecutionGraph()
328
+ });
329
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
330
+ function: step.getFunction(),
331
+ data: {
332
+ timeTravel: timeTravelParams,
333
+ initialState: executionContext.state ?? {},
334
+ runId: executionContext.runId,
335
+ outputOptions: { includeState: true },
336
+ perStep,
337
+ tracingOptions: nestedTracingContext
338
+ }
339
+ });
340
+ result = invokeResp.result;
341
+ runId = invokeResp.runId;
342
+ executionContext.state = invokeResp.result.state;
343
+ } else {
344
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
345
+ function: step.getFunction(),
346
+ data: {
347
+ inputData,
348
+ initialState: executionContext.state ?? {},
349
+ outputOptions: { includeState: true },
350
+ perStep,
351
+ tracingOptions: nestedTracingContext
352
+ }
353
+ });
354
+ result = invokeResp.result;
355
+ runId = invokeResp.runId;
356
+ executionContext.state = invokeResp.result.state;
357
+ }
358
+ } catch (e) {
359
+ const errorCause = e?.cause;
360
+ if (errorCause && typeof errorCause === "object") {
361
+ result = errorCause;
362
+ runId = errorCause.runId || randomUUID();
363
+ } else {
364
+ runId = randomUUID();
365
+ result = {
366
+ status: "failed",
367
+ error: e instanceof Error ? e : new Error(String(e)),
368
+ steps: {},
369
+ input: inputData
370
+ };
371
+ }
372
+ }
373
+ const res = await this.inngestStep.run(
374
+ `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
375
+ async () => {
376
+ if (result.status === "failed") {
377
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
378
+ type: "watch",
379
+ runId: executionContext.runId,
380
+ data: {
381
+ type: "workflow-step-result",
382
+ payload: {
383
+ id: step.id,
384
+ status: "failed",
385
+ error: result?.error,
386
+ payload: prevOutput
387
+ }
388
+ }
389
+ });
390
+ return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
391
+ } else if (result.status === "suspended") {
392
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
393
+ const stepRes = stepResult;
394
+ return stepRes?.status === "suspended";
395
+ });
396
+ for (const [stepName, stepResult] of suspendedSteps) {
397
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
398
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
399
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
400
+ type: "watch",
401
+ runId: executionContext.runId,
402
+ data: {
403
+ type: "workflow-step-suspended",
404
+ payload: {
405
+ id: step.id,
406
+ status: "suspended"
407
+ }
408
+ }
409
+ });
410
+ return {
411
+ executionContext,
412
+ result: {
413
+ status: "suspended",
414
+ suspendedAt: Date.now(),
415
+ payload: stepResult.payload,
416
+ suspendPayload: {
417
+ ...stepResult?.suspendPayload,
418
+ __workflow_meta: { runId, path: suspendPath }
419
+ }
420
+ }
421
+ };
422
+ }
423
+ return {
424
+ executionContext,
425
+ result: {
426
+ status: "suspended",
427
+ suspendedAt: Date.now(),
428
+ payload: {}
429
+ }
430
+ };
431
+ } else if (result.status === "tripwire") {
432
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
433
+ type: "watch",
434
+ runId: executionContext.runId,
435
+ data: {
436
+ type: "workflow-step-result",
437
+ payload: {
438
+ id: step.id,
439
+ status: "tripwire",
440
+ error: result?.tripwire?.reason,
441
+ payload: prevOutput
442
+ }
443
+ }
444
+ });
445
+ return {
446
+ executionContext,
447
+ result: {
448
+ status: "tripwire",
449
+ tripwire: result?.tripwire,
450
+ endedAt: Date.now()
451
+ }
452
+ };
453
+ } else if (perStep || result.status === "paused") {
454
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
455
+ type: "watch",
456
+ runId: executionContext.runId,
457
+ data: {
458
+ type: "workflow-step-result",
459
+ payload: {
460
+ id: step.id,
461
+ status: "paused"
462
+ }
463
+ }
464
+ });
465
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
466
+ type: "watch",
467
+ runId: executionContext.runId,
468
+ data: {
469
+ type: "workflow-step-finish",
470
+ payload: {
471
+ id: step.id,
472
+ metadata: {}
473
+ }
474
+ }
475
+ });
476
+ return { executionContext, result: { status: "paused" } };
477
+ }
478
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
479
+ type: "watch",
480
+ runId: executionContext.runId,
481
+ data: {
482
+ type: "workflow-step-result",
483
+ payload: {
484
+ id: step.id,
485
+ status: "success",
486
+ output: result?.result
487
+ }
488
+ }
489
+ });
490
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
491
+ type: "watch",
492
+ runId: executionContext.runId,
493
+ data: {
494
+ type: "workflow-step-finish",
495
+ payload: {
496
+ id: step.id,
497
+ metadata: {}
498
+ }
499
+ }
500
+ });
501
+ return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
502
+ }
503
+ );
504
+ Object.assign(executionContext, res.executionContext);
505
+ return {
506
+ ...res.result,
507
+ startedAt,
508
+ payload: inputData,
509
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
510
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
511
+ };
512
+ }
513
+ };
514
+ var InngestPubSub = class extends PubSub {
515
+ inngest;
516
+ workflowId;
517
+ publishFn;
518
+ subscriptions = /* @__PURE__ */ new Map();
519
+ constructor(inngest, workflowId, publishFn) {
520
+ super();
521
+ this.inngest = inngest;
522
+ this.workflowId = workflowId;
523
+ this.publishFn = publishFn;
524
+ }
525
+ /**
526
+ * Publish an event to Inngest's realtime system.
527
+ *
528
+ * Topic format: "workflow.events.v2.{runId}"
529
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
530
+ */
531
+ async publish(topic, event) {
532
+ if (!this.publishFn) {
533
+ return;
534
+ }
535
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
536
+ if (!match) {
537
+ return;
538
+ }
539
+ const runId = match[1];
540
+ try {
541
+ await this.publishFn({
542
+ channel: `workflow:${this.workflowId}:${runId}`,
543
+ topic: "watch",
544
+ data: event.data
545
+ });
546
+ } catch (err) {
547
+ console.error("InngestPubSub publish error:", err?.message ?? err);
548
+ }
549
+ }
550
+ /**
551
+ * Subscribe to events from Inngest's realtime system.
552
+ *
553
+ * Topic format: "workflow.events.v2.{runId}"
554
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
555
+ */
556
+ async subscribe(topic, cb) {
557
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
558
+ if (!match || !match[1]) {
559
+ return;
560
+ }
561
+ const runId = match[1];
562
+ if (this.subscriptions.has(topic)) {
563
+ this.subscriptions.get(topic).callbacks.add(cb);
564
+ return;
565
+ }
566
+ const callbacks = /* @__PURE__ */ new Set([cb]);
567
+ const channel = `workflow:${this.workflowId}:${runId}`;
568
+ const streamPromise = subscribe(
569
+ {
570
+ channel,
571
+ topics: ["watch"],
572
+ app: this.inngest
573
+ },
574
+ (message) => {
575
+ const event = {
576
+ id: crypto.randomUUID(),
577
+ type: "watch",
578
+ runId,
579
+ data: message.data,
580
+ createdAt: /* @__PURE__ */ new Date()
581
+ };
582
+ for (const callback of callbacks) {
583
+ callback(event);
584
+ }
585
+ }
586
+ );
587
+ this.subscriptions.set(topic, {
588
+ unsubscribe: () => {
589
+ streamPromise.then((stream) => stream.cancel()).catch((err) => {
590
+ console.error("InngestPubSub unsubscribe error:", err);
591
+ });
592
+ },
593
+ callbacks
594
+ });
595
+ }
596
+ /**
597
+ * Unsubscribe a callback from a topic.
598
+ * If no callbacks remain, the underlying Inngest subscription is cancelled.
599
+ */
600
+ async unsubscribe(topic, cb) {
601
+ const sub = this.subscriptions.get(topic);
602
+ if (!sub) {
603
+ return;
604
+ }
605
+ sub.callbacks.delete(cb);
606
+ if (sub.callbacks.size === 0) {
607
+ sub.unsubscribe();
608
+ this.subscriptions.delete(topic);
609
+ }
610
+ }
611
+ /**
612
+ * Flush any pending operations. No-op for Inngest.
613
+ */
614
+ async flush() {
615
+ }
616
+ /**
617
+ * Clean up all subscriptions during graceful shutdown.
618
+ */
619
+ async close() {
620
+ for (const [, sub] of this.subscriptions) {
621
+ sub.unsubscribe();
622
+ }
623
+ this.subscriptions.clear();
624
+ }
625
+ };
29
626
  var InngestRun = class extends Run {
30
627
  inngest;
31
628
  serializedStepGraph;
@@ -37,93 +634,218 @@ var InngestRun = class extends Run {
37
634
  this.#mastra = params.mastra;
38
635
  }
39
636
  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}`
637
+ const maxRetries = 3;
638
+ let lastError = null;
639
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
640
+ try {
641
+ const response = await fetch(
642
+ `${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
643
+ {
644
+ headers: {
645
+ Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
646
+ }
647
+ }
648
+ );
649
+ if (response.status === 429) {
650
+ const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
651
+ await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
652
+ continue;
653
+ }
654
+ if (!response.ok) {
655
+ throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
656
+ }
657
+ const text = await response.text();
658
+ if (!text) {
659
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
660
+ continue;
661
+ }
662
+ const json = JSON.parse(text);
663
+ return json.data;
664
+ } catch (error) {
665
+ lastError = error;
666
+ if (attempt < maxRetries - 1) {
667
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
668
+ }
43
669
  }
44
- });
45
- const json = await response.json();
46
- return json.data;
670
+ }
671
+ throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
47
672
  }
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);
673
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
674
+ const startTime = Date.now();
675
+ const storage = this.#mastra?.getStorage();
676
+ const workflowsStore = await storage?.getStore("workflows");
677
+ while (Date.now() - startTime < maxWaitMs) {
678
+ let runs;
679
+ try {
680
+ runs = await this.getRuns(eventId);
681
+ } catch (error) {
682
+ if (error instanceof NonRetriableError) {
683
+ throw error;
684
+ }
685
+ throw new NonRetriableError(
686
+ `Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
687
+ );
688
+ }
689
+ if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
690
+ return runs[0];
691
+ }
53
692
  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({
693
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
694
+ workflowName: this.workflowId,
695
+ runId: this.runId
696
+ });
697
+ if (snapshot?.context) {
698
+ snapshot.context = hydrateSerializedStepErrors(snapshot.context);
699
+ }
700
+ return {
701
+ output: {
702
+ result: {
703
+ steps: snapshot?.context,
704
+ status: "failed",
705
+ // Get the original error from NonRetriableError's cause (which contains the workflow result)
706
+ error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
707
+ }
708
+ }
709
+ };
710
+ }
711
+ if (runs?.[0]?.status === "Cancelled") {
712
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
58
713
  workflowName: this.workflowId,
59
714
  runId: this.runId
60
715
  });
61
716
  return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
62
717
  }
718
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
63
719
  }
64
- return runs?.[0];
65
- }
66
- async sendEvent(event, data) {
67
- await this.inngest.send({
68
- name: `user-event-${event}`,
69
- data
70
- });
720
+ throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
71
721
  }
72
722
  async cancel() {
723
+ const storage = this.#mastra?.getStorage();
73
724
  await this.inngest.send({
74
725
  name: `cancel.workflow.${this.workflowId}`,
75
726
  data: {
76
727
  runId: this.runId
77
728
  }
78
729
  });
79
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
730
+ const workflowsStore = await storage?.getStore("workflows");
731
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
80
732
  workflowName: this.workflowId,
81
733
  runId: this.runId
82
734
  });
83
735
  if (snapshot) {
84
- await this.#mastra?.storage?.persistWorkflowSnapshot({
736
+ await workflowsStore?.persistWorkflowSnapshot({
85
737
  workflowName: this.workflowId,
86
738
  runId: this.runId,
739
+ resourceId: this.resourceId,
87
740
  snapshot: {
88
741
  ...snapshot,
89
- status: "canceled"
742
+ status: "canceled",
743
+ value: snapshot.value
90
744
  }
91
745
  });
92
746
  }
93
747
  }
94
- async start({
95
- inputData
96
- }) {
97
- await this.#mastra.getStorage()?.persistWorkflowSnapshot({
748
+ async start(args) {
749
+ return this._start(args);
750
+ }
751
+ /**
752
+ * Starts the workflow execution without waiting for completion (fire-and-forget).
753
+ * Returns immediately with the runId after sending the event to Inngest.
754
+ * The workflow executes independently in Inngest.
755
+ * Use this when you don't need to wait for the result or want to avoid polling failures.
756
+ */
757
+ async startAsync(args) {
758
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
759
+ await workflowsStore?.persistWorkflowSnapshot({
98
760
  workflowName: this.workflowId,
99
761
  runId: this.runId,
762
+ resourceId: this.resourceId,
100
763
  snapshot: {
101
764
  runId: this.runId,
102
765
  serializedStepGraph: this.serializedStepGraph,
766
+ status: "running",
103
767
  value: {},
104
768
  context: {},
105
769
  activePaths: [],
106
770
  suspendedPaths: {},
107
- timestamp: Date.now(),
108
- status: "running"
771
+ activeStepsPath: {},
772
+ resumeLabels: {},
773
+ waitingPaths: {},
774
+ timestamp: Date.now()
109
775
  }
110
776
  });
777
+ const inputDataToUse = await this._validateInput(args.inputData);
778
+ const initialStateToUse = await this._validateInitialState(args.initialState ?? {});
111
779
  const eventOutput = await this.inngest.send({
112
780
  name: `workflow.${this.workflowId}`,
113
781
  data: {
114
- inputData,
115
- runId: this.runId
782
+ inputData: inputDataToUse,
783
+ initialState: initialStateToUse,
784
+ runId: this.runId,
785
+ resourceId: this.resourceId,
786
+ outputOptions: args.outputOptions,
787
+ tracingOptions: args.tracingOptions,
788
+ requestContext: args.requestContext ? Object.fromEntries(args.requestContext.entries()) : {},
789
+ perStep: args.perStep
116
790
  }
117
791
  });
118
792
  const eventId = eventOutput.ids[0];
119
793
  if (!eventId) {
120
794
  throw new Error("Event ID is not set");
121
795
  }
122
- const runOutput = await this.getRunOutput(eventId);
123
- const result = runOutput?.output?.result;
124
- if (result.status === "failed") {
125
- result.error = new Error(result.error);
796
+ return { runId: this.runId };
797
+ }
798
+ async _start({
799
+ inputData,
800
+ initialState,
801
+ outputOptions,
802
+ tracingOptions,
803
+ format,
804
+ requestContext,
805
+ perStep
806
+ }) {
807
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
808
+ await workflowsStore?.persistWorkflowSnapshot({
809
+ workflowName: this.workflowId,
810
+ runId: this.runId,
811
+ resourceId: this.resourceId,
812
+ snapshot: {
813
+ runId: this.runId,
814
+ serializedStepGraph: this.serializedStepGraph,
815
+ status: "running",
816
+ value: {},
817
+ context: {},
818
+ activePaths: [],
819
+ suspendedPaths: {},
820
+ activeStepsPath: {},
821
+ resumeLabels: {},
822
+ waitingPaths: {},
823
+ timestamp: Date.now()
824
+ }
825
+ });
826
+ const inputDataToUse = await this._validateInput(inputData);
827
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
828
+ const eventOutput = await this.inngest.send({
829
+ name: `workflow.${this.workflowId}`,
830
+ data: {
831
+ inputData: inputDataToUse,
832
+ initialState: initialStateToUse,
833
+ runId: this.runId,
834
+ resourceId: this.resourceId,
835
+ outputOptions,
836
+ tracingOptions,
837
+ format,
838
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
839
+ perStep
840
+ }
841
+ });
842
+ const eventId = eventOutput.ids[0];
843
+ if (!eventId) {
844
+ throw new Error("Event ID is not set");
126
845
  }
846
+ const runOutput = await this.getRunOutput(eventId);
847
+ const result = runOutput?.output?.result;
848
+ this.hydrateFailedResult(result);
127
849
  if (result.status !== "suspended") {
128
850
  this.cleanup?.();
129
851
  }
@@ -141,27 +863,41 @@ var InngestRun = class extends Run {
141
863
  return p;
142
864
  }
143
865
  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({
866
+ const storage = this.#mastra?.getStorage();
867
+ let steps = [];
868
+ if (typeof params.step === "string") {
869
+ steps = params.step.split(".");
870
+ } else {
871
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
872
+ (step) => typeof step === "string" ? step : step?.id
873
+ );
874
+ }
875
+ const workflowsStore = await storage?.getStore("workflows");
876
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
148
877
  workflowName: this.workflowId,
149
878
  runId: this.runId
150
879
  });
880
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
881
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
882
+ const persistedRequestContext = snapshot?.requestContext ?? {};
883
+ const newRequestContext = params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {};
884
+ const mergedRequestContext = { ...persistedRequestContext, ...newRequestContext };
151
885
  const eventOutput = await this.inngest.send({
152
886
  name: `workflow.${this.workflowId}`,
153
887
  data: {
154
- inputData: params.resumeData,
888
+ inputData: resumeDataToUse,
889
+ initialState: snapshot?.value ?? {},
155
890
  runId: this.runId,
156
891
  workflowId: this.workflowId,
157
892
  stepResults: snapshot?.context,
158
893
  resume: {
159
894
  steps,
160
895
  stepResults: snapshot?.context,
161
- resumePayload: params.resumeData,
162
- // @ts-ignore
163
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
164
- }
896
+ resumePayload: resumeDataToUse,
897
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
898
+ },
899
+ requestContext: mergedRequestContext,
900
+ perStep: params.perStep
165
901
  }
166
902
  });
167
903
  const eventId = eventOutput.ids[0];
@@ -170,17 +906,108 @@ var InngestRun = class extends Run {
170
906
  }
171
907
  const runOutput = await this.getRunOutput(eventId);
172
908
  const result = runOutput?.output?.result;
173
- if (result.status === "failed") {
174
- result.error = new Error(result.error);
909
+ this.hydrateFailedResult(result);
910
+ return result;
911
+ }
912
+ async timeTravel(params) {
913
+ const p = this._timeTravel(params).then((result) => {
914
+ if (result.status !== "suspended") {
915
+ this.closeStreamAction?.().catch(() => {
916
+ });
917
+ }
918
+ return result;
919
+ });
920
+ this.executionResults = p;
921
+ return p;
922
+ }
923
+ async _timeTravel(params) {
924
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
925
+ throw new Error("Step is required and must be a valid step or array of steps");
926
+ }
927
+ let steps = [];
928
+ if (typeof params.step === "string") {
929
+ steps = params.step.split(".");
930
+ } else {
931
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
932
+ (step) => typeof step === "string" ? step : step?.id
933
+ );
934
+ }
935
+ if (steps.length === 0) {
936
+ throw new Error("No steps provided to timeTravel");
937
+ }
938
+ const storage = this.#mastra?.getStorage();
939
+ const workflowsStore = await storage?.getStore("workflows");
940
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
941
+ workflowName: this.workflowId,
942
+ runId: this.runId
943
+ });
944
+ if (!snapshot) {
945
+ await workflowsStore?.persistWorkflowSnapshot({
946
+ workflowName: this.workflowId,
947
+ runId: this.runId,
948
+ resourceId: this.resourceId,
949
+ snapshot: {
950
+ runId: this.runId,
951
+ serializedStepGraph: this.serializedStepGraph,
952
+ status: "pending",
953
+ value: {},
954
+ context: {},
955
+ activePaths: [],
956
+ suspendedPaths: {},
957
+ activeStepsPath: {},
958
+ resumeLabels: {},
959
+ waitingPaths: {},
960
+ timestamp: Date.now()
961
+ }
962
+ });
963
+ }
964
+ if (snapshot?.status === "running") {
965
+ throw new Error("This workflow run is still running, cannot time travel");
966
+ }
967
+ let inputDataToUse = params.inputData;
968
+ if (inputDataToUse && steps.length === 1) {
969
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
970
+ }
971
+ const timeTravelData = createTimeTravelExecutionParams({
972
+ steps,
973
+ inputData: inputDataToUse,
974
+ resumeData: params.resumeData,
975
+ context: params.context,
976
+ nestedStepsContext: params.nestedStepsContext,
977
+ snapshot: snapshot ?? { context: {} },
978
+ graph: this.executionGraph,
979
+ initialState: params.initialState,
980
+ perStep: params.perStep
981
+ });
982
+ const eventOutput = await this.inngest.send({
983
+ name: `workflow.${this.workflowId}`,
984
+ data: {
985
+ initialState: timeTravelData.state,
986
+ runId: this.runId,
987
+ workflowId: this.workflowId,
988
+ stepResults: timeTravelData.stepResults,
989
+ timeTravel: timeTravelData,
990
+ tracingOptions: params.tracingOptions,
991
+ outputOptions: params.outputOptions,
992
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
993
+ perStep: params.perStep
994
+ }
995
+ });
996
+ const eventId = eventOutput.ids[0];
997
+ if (!eventId) {
998
+ throw new Error("Event ID is not set");
175
999
  }
1000
+ const runOutput = await this.getRunOutput(eventId);
1001
+ const result = runOutput?.output?.result;
1002
+ this.hydrateFailedResult(result);
176
1003
  return result;
177
1004
  }
178
- watch(cb, type = "watch") {
1005
+ watch(cb) {
179
1006
  let active = true;
180
1007
  const streamPromise = subscribe(
181
1008
  {
182
1009
  channel: `workflow:${this.workflowId}:${this.runId}`,
183
- topics: [type],
1010
+ topics: ["watch"],
184
1011
  app: this.inngest
185
1012
  },
186
1013
  (message) => {
@@ -198,16 +1025,35 @@ var InngestRun = class extends Run {
198
1025
  });
199
1026
  };
200
1027
  }
201
- stream({ inputData, runtimeContext } = {}) {
1028
+ streamLegacy({ inputData, requestContext } = {}) {
202
1029
  const { readable, writable } = new TransformStream();
203
1030
  const writer = writable.getWriter();
1031
+ void writer.write({
1032
+ // @ts-ignore
1033
+ type: "start",
1034
+ // @ts-ignore
1035
+ payload: { runId: this.runId }
1036
+ });
204
1037
  const unwatch = this.watch(async (event) => {
205
1038
  try {
206
- await writer.write(event);
1039
+ const e = {
1040
+ ...event,
1041
+ type: event.type.replace("workflow-", "")
1042
+ };
1043
+ if (e.type === "step-output") {
1044
+ e.type = e.payload.output.type;
1045
+ e.payload = e.payload.output.payload;
1046
+ }
1047
+ await writer.write(e);
207
1048
  } catch {
208
1049
  }
209
- }, "watch-v2");
1050
+ });
210
1051
  this.closeStreamAction = async () => {
1052
+ await writer.write({
1053
+ type: "finish",
1054
+ // @ts-ignore
1055
+ payload: { runId: this.runId }
1056
+ });
211
1057
  unwatch();
212
1058
  try {
213
1059
  await writer.close();
@@ -217,7 +1063,7 @@ var InngestRun = class extends Run {
217
1063
  writer.releaseLock();
218
1064
  }
219
1065
  };
220
- this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
1066
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
221
1067
  if (result.status !== "suspended") {
222
1068
  this.closeStreamAction?.().catch(() => {
223
1069
  });
@@ -229,55 +1075,203 @@ var InngestRun = class extends Run {
229
1075
  getWorkflowState: () => this.executionResults
230
1076
  };
231
1077
  }
1078
+ stream({
1079
+ inputData,
1080
+ requestContext,
1081
+ tracingOptions,
1082
+ closeOnSuspend = true,
1083
+ initialState,
1084
+ outputOptions,
1085
+ perStep
1086
+ } = {}) {
1087
+ if (this.closeStreamAction && this.streamOutput) {
1088
+ return this.streamOutput;
1089
+ }
1090
+ this.closeStreamAction = async () => {
1091
+ };
1092
+ const self = this;
1093
+ const stream = new ReadableStream({
1094
+ async start(controller) {
1095
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
1096
+ controller.enqueue({
1097
+ type,
1098
+ runId: self.runId,
1099
+ from,
1100
+ payload: {
1101
+ stepName: payload?.id,
1102
+ ...payload
1103
+ }
1104
+ });
1105
+ });
1106
+ self.closeStreamAction = async () => {
1107
+ unwatch();
1108
+ try {
1109
+ await controller.close();
1110
+ } catch (err) {
1111
+ console.error("Error closing stream:", err);
1112
+ }
1113
+ };
1114
+ const executionResultsPromise = self._start({
1115
+ inputData,
1116
+ requestContext,
1117
+ // tracingContext, // We are not able to pass a reference to a span here, what to do?
1118
+ initialState,
1119
+ tracingOptions,
1120
+ outputOptions,
1121
+ format: "vnext",
1122
+ perStep
1123
+ });
1124
+ let executionResults;
1125
+ try {
1126
+ executionResults = await executionResultsPromise;
1127
+ if (closeOnSuspend) {
1128
+ self.closeStreamAction?.().catch(() => {
1129
+ });
1130
+ } else if (executionResults.status !== "suspended") {
1131
+ self.closeStreamAction?.().catch(() => {
1132
+ });
1133
+ }
1134
+ if (self.streamOutput) {
1135
+ self.streamOutput.updateResults(
1136
+ executionResults
1137
+ );
1138
+ }
1139
+ } catch (err) {
1140
+ self.streamOutput?.rejectResults(err);
1141
+ self.closeStreamAction?.().catch(() => {
1142
+ });
1143
+ }
1144
+ }
1145
+ });
1146
+ this.streamOutput = new WorkflowRunOutput({
1147
+ runId: this.runId,
1148
+ workflowId: this.workflowId,
1149
+ stream
1150
+ });
1151
+ return this.streamOutput;
1152
+ }
1153
+ timeTravelStream({
1154
+ inputData,
1155
+ resumeData,
1156
+ initialState,
1157
+ step,
1158
+ context,
1159
+ nestedStepsContext,
1160
+ requestContext,
1161
+ // tracingContext,
1162
+ tracingOptions,
1163
+ outputOptions,
1164
+ perStep
1165
+ }) {
1166
+ this.closeStreamAction = async () => {
1167
+ };
1168
+ const self = this;
1169
+ const stream = new ReadableStream({
1170
+ async start(controller) {
1171
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
1172
+ controller.enqueue({
1173
+ type,
1174
+ runId: self.runId,
1175
+ from,
1176
+ payload: {
1177
+ stepName: payload?.id,
1178
+ ...payload
1179
+ }
1180
+ });
1181
+ });
1182
+ self.closeStreamAction = async () => {
1183
+ unwatch();
1184
+ try {
1185
+ controller.close();
1186
+ } catch (err) {
1187
+ console.error("Error closing stream:", err);
1188
+ }
1189
+ };
1190
+ const executionResultsPromise = self._timeTravel({
1191
+ inputData,
1192
+ step,
1193
+ context,
1194
+ nestedStepsContext,
1195
+ resumeData,
1196
+ initialState,
1197
+ requestContext,
1198
+ tracingOptions,
1199
+ outputOptions,
1200
+ perStep
1201
+ });
1202
+ self.executionResults = executionResultsPromise;
1203
+ let executionResults;
1204
+ try {
1205
+ executionResults = await executionResultsPromise;
1206
+ self.closeStreamAction?.().catch(() => {
1207
+ });
1208
+ if (self.streamOutput) {
1209
+ self.streamOutput.updateResults(executionResults);
1210
+ }
1211
+ } catch (err) {
1212
+ self.streamOutput?.rejectResults(err);
1213
+ self.closeStreamAction?.().catch(() => {
1214
+ });
1215
+ }
1216
+ }
1217
+ });
1218
+ this.streamOutput = new WorkflowRunOutput({
1219
+ runId: this.runId,
1220
+ workflowId: this.workflowId,
1221
+ stream
1222
+ });
1223
+ return this.streamOutput;
1224
+ }
1225
+ /**
1226
+ * Hydrates errors in a failed workflow result back to proper Error instances.
1227
+ * This ensures error.cause chains and custom properties are preserved.
1228
+ */
1229
+ hydrateFailedResult(result) {
1230
+ if (result.status === "failed") {
1231
+ result.error = getErrorFromUnknown(result.error, { serializeStack: false });
1232
+ if (result.steps) {
1233
+ hydrateSerializedStepErrors(result.steps);
1234
+ }
1235
+ }
1236
+ }
232
1237
  };
1238
+
1239
+ // src/workflow.ts
233
1240
  var InngestWorkflow = class _InngestWorkflow extends Workflow {
234
1241
  #mastra;
235
1242
  inngest;
236
1243
  function;
1244
+ cronFunction;
1245
+ flowControlConfig;
1246
+ cronConfig;
237
1247
  constructor(params, inngest) {
238
- super(params);
1248
+ const { concurrency, rateLimit, throttle, debounce, priority, cron, inputData, initialState, ...workflowParams } = params;
1249
+ super(workflowParams);
1250
+ this.engineType = "inngest";
1251
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
1252
+ ([_, value]) => value !== void 0
1253
+ );
1254
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
239
1255
  this.#mastra = params.mastra;
240
1256
  this.inngest = inngest;
241
- }
242
- async getWorkflowRuns(args) {
243
- const storage = this.#mastra?.getStorage();
244
- if (!storage) {
245
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
246
- return { runs: [], total: 0 };
1257
+ if (cron) {
1258
+ this.cronConfig = { cron, inputData, initialState };
247
1259
  }
248
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
249
1260
  }
250
- async getWorkflowRunById(runId) {
1261
+ async listWorkflowRuns(args) {
251
1262
  const storage = this.#mastra?.getStorage();
252
1263
  if (!storage) {
253
1264
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
254
- return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
255
- }
256
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
257
- return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
258
- }
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;
1265
+ return { runs: [], total: 0 };
268
1266
  }
269
- if (typeof run.snapshot === "string") {
270
- return null;
1267
+ const workflowsStore = await storage.getStore("workflows");
1268
+ if (!workflowsStore) {
1269
+ return { runs: [], total: 0 };
271
1270
  }
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
- };
1271
+ return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
279
1272
  }
280
1273
  __registerMastra(mastra) {
1274
+ super.__registerMastra(mastra);
281
1275
  this.#mastra = mastra;
282
1276
  this.executionEngine.__registerMastra(mastra);
283
1277
  const updateNested = (step) => {
@@ -295,62 +1289,85 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
295
1289
  }
296
1290
  }
297
1291
  }
298
- createRun(options) {
299
- const runIdToUse = options?.runId || randomUUID();
300
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
301
- {
302
- workflowId: this.id,
303
- runId: runIdToUse,
304
- executionEngine: this.executionEngine,
305
- executionGraph: this.executionGraph,
306
- serializedStepGraph: this.serializedStepGraph,
307
- mastra: this.#mastra,
308
- retryConfig: this.retryConfig,
309
- cleanup: () => this.runs.delete(runIdToUse)
310
- },
311
- this.inngest
312
- );
313
- this.runs.set(runIdToUse, run);
314
- return run;
315
- }
316
- async createRunAsync(options) {
1292
+ async createRun(options) {
317
1293
  const runIdToUse = options?.runId || randomUUID();
318
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
1294
+ const existingInMemoryRun = this.runs.get(runIdToUse);
1295
+ const newRun = new InngestRun(
319
1296
  {
320
1297
  workflowId: this.id,
321
1298
  runId: runIdToUse,
1299
+ resourceId: options?.resourceId,
322
1300
  executionEngine: this.executionEngine,
323
1301
  executionGraph: this.executionGraph,
324
1302
  serializedStepGraph: this.serializedStepGraph,
325
1303
  mastra: this.#mastra,
326
1304
  retryConfig: this.retryConfig,
327
- cleanup: () => this.runs.delete(runIdToUse)
1305
+ cleanup: () => this.runs.delete(runIdToUse),
1306
+ workflowSteps: this.steps,
1307
+ workflowEngineType: this.engineType,
1308
+ validateInputs: this.options.validateInputs
328
1309
  },
329
1310
  this.inngest
330
1311
  );
1312
+ const run = existingInMemoryRun ?? newRun;
331
1313
  this.runs.set(runIdToUse, run);
332
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
333
- if (!workflowSnapshotInStorage) {
334
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1314
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
1315
+ workflowStatus: run.workflowRunStatus,
1316
+ stepResults: {}
1317
+ });
1318
+ const existingStoredRun = await this.getWorkflowRunById(runIdToUse, {
1319
+ withNestedWorkflows: false
1320
+ });
1321
+ const existsInStorage = existingStoredRun && !existingStoredRun.isFromInMemory;
1322
+ if (!existsInStorage && shouldPersistSnapshot) {
1323
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
1324
+ await workflowsStore?.persistWorkflowSnapshot({
335
1325
  workflowName: this.id,
336
1326
  runId: runIdToUse,
1327
+ resourceId: options?.resourceId,
337
1328
  snapshot: {
338
1329
  runId: runIdToUse,
339
1330
  status: "pending",
340
1331
  value: {},
341
1332
  context: {},
342
1333
  activePaths: [],
1334
+ activeStepsPath: {},
1335
+ waitingPaths: {},
343
1336
  serializedStepGraph: this.serializedStepGraph,
344
1337
  suspendedPaths: {},
1338
+ resumeLabels: {},
345
1339
  result: void 0,
346
1340
  error: void 0,
347
- // @ts-ignore
348
1341
  timestamp: Date.now()
349
1342
  }
350
1343
  });
351
1344
  }
352
1345
  return run;
353
1346
  }
1347
+ //createCronFunction is only called if cronConfig.cron is defined.
1348
+ createCronFunction() {
1349
+ if (this.cronFunction) {
1350
+ return this.cronFunction;
1351
+ }
1352
+ this.cronFunction = this.inngest.createFunction(
1353
+ {
1354
+ id: `workflow.${this.id}.cron`,
1355
+ retries: 0,
1356
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1357
+ ...this.flowControlConfig
1358
+ },
1359
+ { cron: this.cronConfig?.cron ?? "" },
1360
+ async () => {
1361
+ const run = await this.createRun();
1362
+ const result = await run.start({
1363
+ inputData: this.cronConfig?.inputData,
1364
+ initialState: this.cronConfig?.initialState
1365
+ });
1366
+ return { result, runId: run.runId };
1367
+ }
1368
+ );
1369
+ return this.cronFunction;
1370
+ }
354
1371
  getFunction() {
355
1372
  if (this.function) {
356
1373
  return this.function;
@@ -358,53 +1375,134 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
358
1375
  this.function = this.inngest.createFunction(
359
1376
  {
360
1377
  id: `workflow.${this.id}`,
361
- // @ts-ignore
362
- retries: this.retryConfig?.attempts ?? 0,
363
- cancelOn: [{ event: `cancel.workflow.${this.id}` }]
1378
+ retries: 0,
1379
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1380
+ // Spread flow control configuration
1381
+ ...this.flowControlConfig
364
1382
  },
365
1383
  { event: `workflow.${this.id}` },
366
1384
  async ({ event, step, attempt, publish }) => {
367
- let { inputData, runId, resume } = event.data;
1385
+ let {
1386
+ inputData,
1387
+ initialState,
1388
+ runId,
1389
+ resourceId,
1390
+ resume,
1391
+ outputOptions,
1392
+ format,
1393
+ timeTravel,
1394
+ perStep,
1395
+ tracingOptions
1396
+ } = event.data;
368
1397
  if (!runId) {
369
1398
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
370
1399
  return randomUUID();
371
1400
  });
372
1401
  }
373
- const emitter = {
374
- emit: async (event2, data) => {
375
- if (!publish) {
376
- return;
1402
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
1403
+ const requestContext = new RequestContext(Object.entries(event.data.requestContext ?? {}));
1404
+ const mastra = this.#mastra;
1405
+ const tracingPolicy = this.options.tracingPolicy;
1406
+ const workflowSpanData = await step.run(`workflow.${this.id}.span.start`, async () => {
1407
+ const observability = mastra?.observability?.getSelectedInstance({ requestContext });
1408
+ if (!observability) return void 0;
1409
+ const span = observability.startSpan({
1410
+ type: SpanType.WORKFLOW_RUN,
1411
+ name: `workflow run: '${this.id}'`,
1412
+ entityType: EntityType.WORKFLOW_RUN,
1413
+ entityId: this.id,
1414
+ input: inputData,
1415
+ metadata: {
1416
+ resourceId,
1417
+ runId
1418
+ },
1419
+ tracingPolicy,
1420
+ tracingOptions,
1421
+ requestContext
1422
+ });
1423
+ return span?.exportSpan();
1424
+ });
1425
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
1426
+ let result;
1427
+ try {
1428
+ result = await engine.execute({
1429
+ workflowId: this.id,
1430
+ runId,
1431
+ resourceId,
1432
+ graph: this.executionGraph,
1433
+ serializedStepGraph: this.serializedStepGraph,
1434
+ input: inputData,
1435
+ initialState,
1436
+ pubsub,
1437
+ retryConfig: this.retryConfig,
1438
+ requestContext,
1439
+ resume,
1440
+ timeTravel,
1441
+ perStep,
1442
+ format,
1443
+ abortController: new AbortController(),
1444
+ // For Inngest, we don't pass workflowSpan - step spans use tracingIds instead
1445
+ workflowSpan: void 0,
1446
+ // Pass tracing IDs for durable span operations
1447
+ tracingIds: workflowSpanData ? {
1448
+ traceId: workflowSpanData.traceId,
1449
+ workflowSpanId: workflowSpanData.id
1450
+ } : void 0,
1451
+ outputOptions,
1452
+ outputWriter: async (chunk) => {
1453
+ try {
1454
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1455
+ type: "watch",
1456
+ runId,
1457
+ data: chunk
1458
+ });
1459
+ } catch (err) {
1460
+ this.logger.debug?.("Failed to publish watch event:", err);
1461
+ }
377
1462
  }
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));
1463
+ });
1464
+ } catch (error) {
1465
+ throw error;
1466
+ }
1467
+ await step.run(`workflow.${this.id}.finalize`, async () => {
1468
+ if (result.status !== "paused") {
1469
+ await engine.invokeLifecycleCallbacksInternal({
1470
+ status: result.status,
1471
+ result: "result" in result ? result.result : void 0,
1472
+ error: "error" in result ? result.error : void 0,
1473
+ steps: result.steps,
1474
+ tripwire: "tripwire" in result ? result.tripwire : void 0,
1475
+ runId,
1476
+ workflowId: this.id,
1477
+ resourceId,
1478
+ input: inputData,
1479
+ requestContext,
1480
+ state: result.state ?? initialState ?? {}
1481
+ });
1482
+ }
1483
+ if (workflowSpanData) {
1484
+ const observability = mastra?.observability?.getSelectedInstance({ requestContext });
1485
+ if (observability) {
1486
+ const workflowSpan = observability.rebuildSpan(workflowSpanData);
1487
+ if (result.status === "failed") {
1488
+ workflowSpan.error({
1489
+ error: result.error instanceof Error ? result.error : new Error(String(result.error)),
1490
+ attributes: { status: "failed" }
1491
+ });
1492
+ } else {
1493
+ workflowSpan.end({
1494
+ output: result.status === "success" ? result.result : void 0,
1495
+ attributes: { status: result.status }
1496
+ });
1497
+ }
386
1498
  }
387
- },
388
- on: (_event, _callback) => {
389
- },
390
- off: (_event, _callback) => {
391
- },
392
- once: (_event, _callback) => {
393
1499
  }
394
- };
395
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
396
- const result = await engine.execute({
397
- workflowId: this.id,
398
- runId,
399
- graph: this.executionGraph,
400
- serializedStepGraph: this.serializedStepGraph,
401
- input: inputData,
402
- emitter,
403
- retryConfig: this.retryConfig,
404
- runtimeContext: new RuntimeContext(),
405
- // TODO
406
- resume,
407
- abortController: new AbortController()
1500
+ if (result.status === "failed") {
1501
+ throw new NonRetriableError(`Workflow failed`, {
1502
+ cause: result
1503
+ });
1504
+ }
1505
+ return result;
408
1506
  });
409
1507
  return { result, runId };
410
1508
  }
@@ -425,103 +1523,201 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
425
1523
  });
426
1524
  }
427
1525
  getFunctions() {
428
- return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
1526
+ return [
1527
+ this.getFunction(),
1528
+ ...this.cronConfig?.cron ? [this.createCronFunction()] : [],
1529
+ ...this.getNestedFunctions(this.executionGraph.steps)
1530
+ ];
429
1531
  }
430
1532
  };
431
- function isAgent(params) {
432
- return params?.component === "AGENT";
1533
+ function prepareServeOptions({ mastra, inngest, functions: userFunctions = [], registerOptions }) {
1534
+ const wfs = mastra.listWorkflows();
1535
+ const workflowFunctions = Array.from(
1536
+ new Set(
1537
+ Object.values(wfs).flatMap((wf) => {
1538
+ if (wf instanceof InngestWorkflow) {
1539
+ wf.__registerMastra(mastra);
1540
+ return wf.getFunctions();
1541
+ }
1542
+ return [];
1543
+ })
1544
+ )
1545
+ );
1546
+ return {
1547
+ ...registerOptions,
1548
+ client: inngest,
1549
+ functions: [...workflowFunctions, ...userFunctions]
1550
+ };
1551
+ }
1552
+ function createServe(adapter) {
1553
+ return (options) => {
1554
+ const serveOptions = prepareServeOptions(options);
1555
+ return adapter(serveOptions);
1556
+ };
1557
+ }
1558
+ var serve = createServe(serve$1);
1559
+
1560
+ // src/types.ts
1561
+ var _compatibilityCheck = true;
1562
+
1563
+ // src/index.ts
1564
+ function isInngestWorkflow(input) {
1565
+ return input instanceof InngestWorkflow;
1566
+ }
1567
+ function isAgent(input) {
1568
+ return input instanceof Agent;
433
1569
  }
434
- function isTool(params) {
435
- return params instanceof Tool;
1570
+ function isToolStep(input) {
1571
+ return input instanceof Tool;
436
1572
  }
437
- function createStep(params) {
1573
+ function isStepParams(input) {
1574
+ return input !== null && typeof input === "object" && "id" in input && "execute" in input && !(input instanceof Agent) && !(input instanceof Tool) && !(input instanceof InngestWorkflow);
1575
+ }
1576
+ function isProcessor(obj) {
1577
+ return obj !== null && typeof obj === "object" && "id" in obj && typeof obj.id === "string" && !(obj instanceof Agent) && !(obj instanceof Tool) && !(obj instanceof InngestWorkflow) && (typeof obj.processInput === "function" || typeof obj.processInputStep === "function" || typeof obj.processOutputStream === "function" || typeof obj.processOutputResult === "function" || typeof obj.processOutputStep === "function");
1578
+ }
1579
+ function createStep(params, agentOrToolOptions) {
1580
+ if (isInngestWorkflow(params)) {
1581
+ return params;
1582
+ }
438
1583
  if (isAgent(params)) {
439
- return {
440
- id: params.name,
441
- // @ts-ignore
442
- inputSchema: z.object({
443
- prompt: z.string()
444
- // resourceId: z.string().optional(),
445
- // threadId: z.string().optional(),
446
- }),
447
- // @ts-ignore
448
- outputSchema: z.object({
449
- text: z.string()
450
- }),
451
- execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
452
- let streamPromise = {};
453
- streamPromise.promise = new Promise((resolve, reject) => {
454
- streamPromise.resolve = resolve;
455
- streamPromise.reject = reject;
456
- });
457
- const toolData = {
458
- name: params.name,
459
- args: inputData
460
- };
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,
1584
+ return createStepFromAgent(params, agentOrToolOptions);
1585
+ }
1586
+ if (isToolStep(params)) {
1587
+ return createStepFromTool(params, agentOrToolOptions);
1588
+ }
1589
+ if (isStepParams(params)) {
1590
+ return createStepFromParams(params);
1591
+ }
1592
+ if (isProcessor(params)) {
1593
+ return createStepFromProcessor(params);
1594
+ }
1595
+ throw new Error("Invalid input: expected StepParams, Agent, ToolStep, Processor, or InngestWorkflow");
1596
+ }
1597
+ function createStepFromParams(params) {
1598
+ return {
1599
+ id: params.id,
1600
+ description: params.description,
1601
+ inputSchema: params.inputSchema,
1602
+ stateSchema: params.stateSchema,
1603
+ outputSchema: params.outputSchema,
1604
+ resumeSchema: params.resumeSchema,
1605
+ suspendSchema: params.suspendSchema,
1606
+ scorers: params.scorers,
1607
+ retries: params.retries,
1608
+ execute: params.execute.bind(params)
1609
+ };
1610
+ }
1611
+ function createStepFromAgent(params, agentOrToolOptions) {
1612
+ const options = agentOrToolOptions ?? {};
1613
+ const outputSchema = options?.structuredOutput?.schema ?? z.object({ text: z.string() });
1614
+ const { retries, scorers, ...agentOptions } = options ?? {};
1615
+ return {
1616
+ id: params.name,
1617
+ description: params.getDescription(),
1618
+ inputSchema: z.object({
1619
+ prompt: z.string()
1620
+ }),
1621
+ outputSchema,
1622
+ retries,
1623
+ scorers,
1624
+ execute: async ({
1625
+ inputData,
1626
+ runId,
1627
+ [PUBSUB_SYMBOL]: pubsub,
1628
+ [STREAM_FORMAT_SYMBOL]: streamFormat,
1629
+ requestContext,
1630
+ tracingContext,
1631
+ abortSignal,
1632
+ abort,
1633
+ writer
1634
+ }) => {
1635
+ let streamPromise = {};
1636
+ streamPromise.promise = new Promise((resolve, reject) => {
1637
+ streamPromise.resolve = resolve;
1638
+ streamPromise.reject = reject;
1639
+ });
1640
+ let structuredResult = null;
1641
+ const toolData = {
1642
+ name: params.name,
1643
+ args: inputData
1644
+ };
1645
+ let stream;
1646
+ if ((await params.getModel()).specificationVersion === "v1") {
1647
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
1648
+ ...agentOptions ?? {},
1649
+ requestContext,
1650
+ tracingContext,
469
1651
  onFinish: (result) => {
1652
+ const resultWithObject = result;
1653
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1654
+ structuredResult = resultWithObject.object;
1655
+ }
470
1656
  streamPromise.resolve(result.text);
1657
+ void agentOptions?.onFinish?.(result);
471
1658
  },
472
1659
  abortSignal
473
1660
  });
474
- if (abortSignal.aborted) {
475
- return abort();
476
- }
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
484
- });
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;
1661
+ stream = fullStream;
1662
+ } else {
1663
+ const modelOutput = await params.stream(inputData.prompt, {
1664
+ ...agentOptions ?? {},
1665
+ requestContext,
1666
+ tracingContext,
1667
+ onFinish: (result) => {
1668
+ const resultWithObject = result;
1669
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1670
+ structuredResult = resultWithObject.object;
1671
+ }
1672
+ streamPromise.resolve(result.text);
1673
+ void agentOptions?.onFinish?.(result);
1674
+ },
1675
+ abortSignal
1676
+ });
1677
+ stream = modelOutput.fullStream;
1678
+ }
1679
+ if (streamFormat === "legacy") {
1680
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1681
+ type: "watch",
1682
+ runId,
1683
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
1684
+ });
1685
+ for await (const chunk of stream) {
1686
+ if (chunk.type === "text-delta") {
1687
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1688
+ type: "watch",
1689
+ runId,
1690
+ data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
1691
+ });
499
1692
  }
500
1693
  }
501
- return {
502
- text: await streamPromise.promise
503
- };
504
- }
505
- };
506
- }
507
- if (isTool(params)) {
508
- if (!params.inputSchema || !params.outputSchema) {
509
- throw new Error("Tool must have input and output schemas defined");
510
- }
511
- return {
512
- // TODO: tool probably should have strong id type
513
- // @ts-ignore
514
- id: params.id,
515
- inputSchema: params.inputSchema,
516
- outputSchema: params.outputSchema,
517
- execute: async ({ inputData, mastra, runtimeContext }) => {
518
- return params.execute({
519
- context: inputData,
520
- mastra,
521
- runtimeContext
1694
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1695
+ type: "watch",
1696
+ runId,
1697
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
522
1698
  });
1699
+ } else {
1700
+ for await (const chunk of stream) {
1701
+ await writer.write(chunk);
1702
+ }
523
1703
  }
524
- };
1704
+ if (abortSignal.aborted) {
1705
+ return abort();
1706
+ }
1707
+ if (structuredResult !== null) {
1708
+ return structuredResult;
1709
+ }
1710
+ return {
1711
+ text: await streamPromise.promise
1712
+ };
1713
+ },
1714
+ component: params.component
1715
+ };
1716
+ }
1717
+ function createStepFromTool(params, agentOrToolOptions) {
1718
+ const toolOpts = agentOrToolOptions;
1719
+ if (!params.inputSchema || !params.outputSchema) {
1720
+ throw new Error("Tool must have input and output schemas defined");
525
1721
  }
526
1722
  return {
527
1723
  id: params.id,
@@ -530,13 +1726,489 @@ function createStep(params) {
530
1726
  outputSchema: params.outputSchema,
531
1727
  resumeSchema: params.resumeSchema,
532
1728
  suspendSchema: params.suspendSchema,
533
- execute: params.execute
1729
+ retries: toolOpts?.retries,
1730
+ scorers: toolOpts?.scorers,
1731
+ execute: async ({
1732
+ inputData,
1733
+ mastra,
1734
+ requestContext,
1735
+ tracingContext,
1736
+ suspend,
1737
+ resumeData,
1738
+ runId,
1739
+ workflowId,
1740
+ state,
1741
+ setState
1742
+ }) => {
1743
+ const toolContext = {
1744
+ mastra,
1745
+ requestContext,
1746
+ tracingContext,
1747
+ workflow: {
1748
+ runId,
1749
+ resumeData,
1750
+ suspend,
1751
+ workflowId,
1752
+ state,
1753
+ setState
1754
+ }
1755
+ };
1756
+ return params.execute(inputData, toolContext);
1757
+ },
1758
+ component: "TOOL"
1759
+ };
1760
+ }
1761
+ function createStepFromProcessor(processor) {
1762
+ const getProcessorEntityType = (phase) => {
1763
+ switch (phase) {
1764
+ case "input":
1765
+ return EntityType.INPUT_PROCESSOR;
1766
+ case "inputStep":
1767
+ return EntityType.INPUT_STEP_PROCESSOR;
1768
+ case "outputStream":
1769
+ case "outputResult":
1770
+ return EntityType.OUTPUT_PROCESSOR;
1771
+ case "outputStep":
1772
+ return EntityType.OUTPUT_STEP_PROCESSOR;
1773
+ default:
1774
+ return EntityType.OUTPUT_PROCESSOR;
1775
+ }
1776
+ };
1777
+ const getSpanNamePrefix = (phase) => {
1778
+ switch (phase) {
1779
+ case "input":
1780
+ return "input processor";
1781
+ case "inputStep":
1782
+ return "input step processor";
1783
+ case "outputStream":
1784
+ return "output stream processor";
1785
+ case "outputResult":
1786
+ return "output processor";
1787
+ case "outputStep":
1788
+ return "output step processor";
1789
+ default:
1790
+ return "processor";
1791
+ }
1792
+ };
1793
+ const hasPhaseMethod = (phase) => {
1794
+ switch (phase) {
1795
+ case "input":
1796
+ return !!processor.processInput;
1797
+ case "inputStep":
1798
+ return !!processor.processInputStep;
1799
+ case "outputStream":
1800
+ return !!processor.processOutputStream;
1801
+ case "outputResult":
1802
+ return !!processor.processOutputResult;
1803
+ case "outputStep":
1804
+ return !!processor.processOutputStep;
1805
+ default:
1806
+ return false;
1807
+ }
1808
+ };
1809
+ return {
1810
+ id: `processor:${processor.id}`,
1811
+ description: processor.name ?? `Processor ${processor.id}`,
1812
+ inputSchema: ProcessorStepSchema,
1813
+ outputSchema: ProcessorStepOutputSchema,
1814
+ execute: async ({ inputData, requestContext, tracingContext }) => {
1815
+ const input = inputData;
1816
+ const {
1817
+ phase,
1818
+ messages,
1819
+ messageList,
1820
+ stepNumber,
1821
+ systemMessages,
1822
+ part,
1823
+ streamParts,
1824
+ state,
1825
+ finishReason,
1826
+ toolCalls,
1827
+ text,
1828
+ retryCount,
1829
+ // inputStep phase fields for model/tools configuration
1830
+ model,
1831
+ tools,
1832
+ toolChoice,
1833
+ activeTools,
1834
+ providerOptions,
1835
+ modelSettings,
1836
+ structuredOutput,
1837
+ steps
1838
+ } = input;
1839
+ const abort = (reason, options) => {
1840
+ throw new TripWire(reason || `Tripwire triggered by ${processor.id}`, options, processor.id);
1841
+ };
1842
+ if (!hasPhaseMethod(phase)) {
1843
+ return input;
1844
+ }
1845
+ const currentSpan = tracingContext?.currentSpan;
1846
+ const parentSpan = phase === "inputStep" || phase === "outputStep" ? currentSpan?.findParent(SpanType.MODEL_STEP) || currentSpan : currentSpan?.findParent(SpanType.AGENT_RUN) || currentSpan;
1847
+ const processorSpan = phase !== "outputStream" ? parentSpan?.createChildSpan({
1848
+ type: SpanType.PROCESSOR_RUN,
1849
+ name: `${getSpanNamePrefix(phase)}: ${processor.id}`,
1850
+ entityType: getProcessorEntityType(phase),
1851
+ entityId: processor.id,
1852
+ entityName: processor.name ?? processor.id,
1853
+ input: { phase, messageCount: messages?.length },
1854
+ attributes: {
1855
+ processorExecutor: "workflow",
1856
+ // Read processorIndex from processor (set in combineProcessorsIntoWorkflow)
1857
+ processorIndex: processor.processorIndex
1858
+ }
1859
+ }) : void 0;
1860
+ const processorTracingContext = processorSpan ? { currentSpan: processorSpan } : tracingContext;
1861
+ const baseContext = {
1862
+ abort,
1863
+ retryCount: retryCount ?? 0,
1864
+ requestContext,
1865
+ tracingContext: processorTracingContext
1866
+ };
1867
+ const passThrough = {
1868
+ phase,
1869
+ // Auto-create MessageList from messages if not provided
1870
+ // This enables running processor workflows from the UI where messageList can't be serialized
1871
+ messageList: messageList ?? (Array.isArray(messages) ? new MessageList().add(messages, "input").addSystem(systemMessages ?? []) : void 0),
1872
+ stepNumber,
1873
+ systemMessages,
1874
+ streamParts,
1875
+ state,
1876
+ finishReason,
1877
+ toolCalls,
1878
+ text,
1879
+ retryCount,
1880
+ // inputStep phase fields for model/tools configuration
1881
+ model,
1882
+ tools,
1883
+ toolChoice,
1884
+ activeTools,
1885
+ providerOptions,
1886
+ modelSettings,
1887
+ structuredOutput,
1888
+ steps
1889
+ };
1890
+ const executePhaseWithSpan = async (fn) => {
1891
+ try {
1892
+ const result = await fn();
1893
+ processorSpan?.end({ output: result });
1894
+ return result;
1895
+ } catch (error) {
1896
+ if (error instanceof TripWire) {
1897
+ processorSpan?.end({ output: { tripwire: error.message } });
1898
+ } else {
1899
+ processorSpan?.error({ error, endSpan: true });
1900
+ }
1901
+ throw error;
1902
+ }
1903
+ };
1904
+ return executePhaseWithSpan(async () => {
1905
+ switch (phase) {
1906
+ case "input": {
1907
+ if (processor.processInput) {
1908
+ if (!passThrough.messageList) {
1909
+ throw new MastraError({
1910
+ category: ErrorCategory.USER,
1911
+ domain: ErrorDomain.MASTRA_WORKFLOW,
1912
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
1913
+ text: `Processor ${processor.id} requires messageList or messages for processInput phase`
1914
+ });
1915
+ }
1916
+ const idsBeforeProcessing = messages.map((m) => m.id);
1917
+ const check = passThrough.messageList.makeMessageSourceChecker();
1918
+ const result = await processor.processInput({
1919
+ ...baseContext,
1920
+ messages,
1921
+ messageList: passThrough.messageList,
1922
+ systemMessages: systemMessages ?? []
1923
+ });
1924
+ if (result instanceof MessageList) {
1925
+ if (result !== passThrough.messageList) {
1926
+ throw new MastraError({
1927
+ category: ErrorCategory.USER,
1928
+ domain: ErrorDomain.MASTRA_WORKFLOW,
1929
+ id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
1930
+ text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
1931
+ });
1932
+ }
1933
+ return {
1934
+ ...passThrough,
1935
+ messages: result.get.all.db(),
1936
+ systemMessages: result.getAllSystemMessages()
1937
+ };
1938
+ } else if (Array.isArray(result)) {
1939
+ ProcessorRunner.applyMessagesToMessageList(
1940
+ result,
1941
+ passThrough.messageList,
1942
+ idsBeforeProcessing,
1943
+ check,
1944
+ "input"
1945
+ );
1946
+ return { ...passThrough, messages: result };
1947
+ } else if (result && "messages" in result && "systemMessages" in result) {
1948
+ const typedResult = result;
1949
+ ProcessorRunner.applyMessagesToMessageList(
1950
+ typedResult.messages,
1951
+ passThrough.messageList,
1952
+ idsBeforeProcessing,
1953
+ check,
1954
+ "input"
1955
+ );
1956
+ passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
1957
+ return {
1958
+ ...passThrough,
1959
+ messages: typedResult.messages,
1960
+ systemMessages: typedResult.systemMessages
1961
+ };
1962
+ }
1963
+ return { ...passThrough, messages };
1964
+ }
1965
+ return { ...passThrough, messages };
1966
+ }
1967
+ case "inputStep": {
1968
+ if (processor.processInputStep) {
1969
+ if (!passThrough.messageList) {
1970
+ throw new MastraError({
1971
+ category: ErrorCategory.USER,
1972
+ domain: ErrorDomain.MASTRA_WORKFLOW,
1973
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
1974
+ text: `Processor ${processor.id} requires messageList or messages for processInputStep phase`
1975
+ });
1976
+ }
1977
+ const idsBeforeProcessing = messages.map((m) => m.id);
1978
+ const check = passThrough.messageList.makeMessageSourceChecker();
1979
+ const result = await processor.processInputStep({
1980
+ ...baseContext,
1981
+ messages,
1982
+ messageList: passThrough.messageList,
1983
+ stepNumber: stepNumber ?? 0,
1984
+ systemMessages: systemMessages ?? [],
1985
+ // Pass model/tools configuration fields - types match ProcessInputStepArgs
1986
+ model,
1987
+ tools,
1988
+ toolChoice,
1989
+ activeTools,
1990
+ providerOptions,
1991
+ modelSettings,
1992
+ structuredOutput,
1993
+ steps: steps ?? []
1994
+ });
1995
+ const validatedResult = await ProcessorRunner.validateAndFormatProcessInputStepResult(result, {
1996
+ messageList: passThrough.messageList,
1997
+ processor,
1998
+ stepNumber: stepNumber ?? 0
1999
+ });
2000
+ if (validatedResult.messages) {
2001
+ ProcessorRunner.applyMessagesToMessageList(
2002
+ validatedResult.messages,
2003
+ passThrough.messageList,
2004
+ idsBeforeProcessing,
2005
+ check
2006
+ );
2007
+ }
2008
+ if (validatedResult.systemMessages) {
2009
+ passThrough.messageList.replaceAllSystemMessages(validatedResult.systemMessages);
2010
+ }
2011
+ return { ...passThrough, messages, ...validatedResult };
2012
+ }
2013
+ return { ...passThrough, messages };
2014
+ }
2015
+ case "outputStream": {
2016
+ if (processor.processOutputStream) {
2017
+ const spanKey = `__outputStreamSpan_${processor.id}`;
2018
+ const mutableState = state ?? {};
2019
+ let processorSpan2 = mutableState[spanKey];
2020
+ if (!processorSpan2 && parentSpan) {
2021
+ processorSpan2 = parentSpan.createChildSpan({
2022
+ type: SpanType.PROCESSOR_RUN,
2023
+ name: `output stream processor: ${processor.id}`,
2024
+ entityType: EntityType.OUTPUT_PROCESSOR,
2025
+ entityId: processor.id,
2026
+ entityName: processor.name ?? processor.id,
2027
+ input: { phase, streamParts: [] },
2028
+ attributes: {
2029
+ processorExecutor: "workflow",
2030
+ processorIndex: processor.processorIndex
2031
+ }
2032
+ });
2033
+ mutableState[spanKey] = processorSpan2;
2034
+ }
2035
+ if (processorSpan2) {
2036
+ processorSpan2.input = {
2037
+ phase,
2038
+ streamParts: streamParts ?? [],
2039
+ totalChunks: (streamParts ?? []).length
2040
+ };
2041
+ }
2042
+ const processorTracingContext2 = processorSpan2 ? { currentSpan: processorSpan2 } : baseContext.tracingContext;
2043
+ let result;
2044
+ try {
2045
+ result = await processor.processOutputStream({
2046
+ ...baseContext,
2047
+ tracingContext: processorTracingContext2,
2048
+ part,
2049
+ streamParts: streamParts ?? [],
2050
+ state: mutableState,
2051
+ messageList: passThrough.messageList
2052
+ // Optional for stream processing
2053
+ });
2054
+ if (part && part.type === "finish") {
2055
+ processorSpan2?.end({ output: result });
2056
+ delete mutableState[spanKey];
2057
+ }
2058
+ } catch (error) {
2059
+ if (error instanceof TripWire) {
2060
+ processorSpan2?.end({ output: { tripwire: error.message } });
2061
+ } else {
2062
+ processorSpan2?.error({ error, endSpan: true });
2063
+ }
2064
+ delete mutableState[spanKey];
2065
+ throw error;
2066
+ }
2067
+ return { ...passThrough, state: mutableState, part: result };
2068
+ }
2069
+ return { ...passThrough, part };
2070
+ }
2071
+ case "outputResult": {
2072
+ if (processor.processOutputResult) {
2073
+ if (!passThrough.messageList) {
2074
+ throw new MastraError({
2075
+ category: ErrorCategory.USER,
2076
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2077
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
2078
+ text: `Processor ${processor.id} requires messageList or messages for processOutputResult phase`
2079
+ });
2080
+ }
2081
+ const idsBeforeProcessing = messages.map((m) => m.id);
2082
+ const check = passThrough.messageList.makeMessageSourceChecker();
2083
+ const result = await processor.processOutputResult({
2084
+ ...baseContext,
2085
+ messages,
2086
+ messageList: passThrough.messageList
2087
+ });
2088
+ if (result instanceof MessageList) {
2089
+ if (result !== passThrough.messageList) {
2090
+ throw new MastraError({
2091
+ category: ErrorCategory.USER,
2092
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2093
+ id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
2094
+ text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
2095
+ });
2096
+ }
2097
+ return {
2098
+ ...passThrough,
2099
+ messages: result.get.all.db(),
2100
+ systemMessages: result.getAllSystemMessages()
2101
+ };
2102
+ } else if (Array.isArray(result)) {
2103
+ ProcessorRunner.applyMessagesToMessageList(
2104
+ result,
2105
+ passThrough.messageList,
2106
+ idsBeforeProcessing,
2107
+ check,
2108
+ "response"
2109
+ );
2110
+ return { ...passThrough, messages: result };
2111
+ } else if (result && "messages" in result && "systemMessages" in result) {
2112
+ const typedResult = result;
2113
+ ProcessorRunner.applyMessagesToMessageList(
2114
+ typedResult.messages,
2115
+ passThrough.messageList,
2116
+ idsBeforeProcessing,
2117
+ check,
2118
+ "response"
2119
+ );
2120
+ passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
2121
+ return {
2122
+ ...passThrough,
2123
+ messages: typedResult.messages,
2124
+ systemMessages: typedResult.systemMessages
2125
+ };
2126
+ }
2127
+ return { ...passThrough, messages };
2128
+ }
2129
+ return { ...passThrough, messages };
2130
+ }
2131
+ case "outputStep": {
2132
+ if (processor.processOutputStep) {
2133
+ if (!passThrough.messageList) {
2134
+ throw new MastraError({
2135
+ category: ErrorCategory.USER,
2136
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2137
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
2138
+ text: `Processor ${processor.id} requires messageList or messages for processOutputStep phase`
2139
+ });
2140
+ }
2141
+ const idsBeforeProcessing = messages.map((m) => m.id);
2142
+ const check = passThrough.messageList.makeMessageSourceChecker();
2143
+ const result = await processor.processOutputStep({
2144
+ ...baseContext,
2145
+ messages,
2146
+ messageList: passThrough.messageList,
2147
+ stepNumber: stepNumber ?? 0,
2148
+ finishReason,
2149
+ toolCalls,
2150
+ text,
2151
+ systemMessages: systemMessages ?? [],
2152
+ steps: steps ?? []
2153
+ });
2154
+ if (result instanceof MessageList) {
2155
+ if (result !== passThrough.messageList) {
2156
+ throw new MastraError({
2157
+ category: ErrorCategory.USER,
2158
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2159
+ id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
2160
+ text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
2161
+ });
2162
+ }
2163
+ return {
2164
+ ...passThrough,
2165
+ messages: result.get.all.db(),
2166
+ systemMessages: result.getAllSystemMessages()
2167
+ };
2168
+ } else if (Array.isArray(result)) {
2169
+ ProcessorRunner.applyMessagesToMessageList(
2170
+ result,
2171
+ passThrough.messageList,
2172
+ idsBeforeProcessing,
2173
+ check,
2174
+ "response"
2175
+ );
2176
+ return { ...passThrough, messages: result };
2177
+ } else if (result && "messages" in result && "systemMessages" in result) {
2178
+ const typedResult = result;
2179
+ ProcessorRunner.applyMessagesToMessageList(
2180
+ typedResult.messages,
2181
+ passThrough.messageList,
2182
+ idsBeforeProcessing,
2183
+ check,
2184
+ "response"
2185
+ );
2186
+ passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
2187
+ return {
2188
+ ...passThrough,
2189
+ messages: typedResult.messages,
2190
+ systemMessages: typedResult.systemMessages
2191
+ };
2192
+ }
2193
+ return { ...passThrough, messages };
2194
+ }
2195
+ return { ...passThrough, messages };
2196
+ }
2197
+ default:
2198
+ return { ...passThrough, messages };
2199
+ }
2200
+ });
2201
+ },
2202
+ component: "PROCESSOR"
534
2203
  };
535
2204
  }
536
2205
  function init(inngest) {
537
2206
  return {
538
2207
  createWorkflow(params) {
539
- return new InngestWorkflow(params, inngest);
2208
+ return new InngestWorkflow(
2209
+ params,
2210
+ inngest
2211
+ );
540
2212
  },
541
2213
  createStep,
542
2214
  cloneStep(step, opts) {
@@ -545,7 +2217,13 @@ function init(inngest) {
545
2217
  description: step.description,
546
2218
  inputSchema: step.inputSchema,
547
2219
  outputSchema: step.outputSchema,
548
- execute: step.execute
2220
+ resumeSchema: step.resumeSchema,
2221
+ suspendSchema: step.suspendSchema,
2222
+ stateSchema: step.stateSchema,
2223
+ execute: step.execute,
2224
+ retries: step.retries,
2225
+ scorers: step.scorers,
2226
+ component: step.component
549
2227
  };
550
2228
  },
551
2229
  cloneWorkflow(workflow, opts) {
@@ -554,7 +2232,8 @@ function init(inngest) {
554
2232
  inputSchema: workflow.inputSchema,
555
2233
  outputSchema: workflow.outputSchema,
556
2234
  steps: workflow.stepDefs,
557
- mastra: workflow.mastra
2235
+ mastra: workflow.mastra,
2236
+ options: workflow.options
558
2237
  });
559
2238
  wf.setStepFlow(workflow.stepGraph);
560
2239
  wf.commit();
@@ -562,733 +2241,7 @@ function init(inngest) {
562
2241
  }
563
2242
  };
564
2243
  }
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
2244
 
1292
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
2245
+ export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createServe, createStep, init, serve };
1293
2246
  //# sourceMappingURL=index.js.map
1294
2247
  //# sourceMappingURL=index.js.map