@mastra/inngest 0.0.0-tsconfig-compile-20250703214351 → 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 { 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,82 +634,209 @@ 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
790
+ }
791
+ });
792
+ const eventId = eventOutput.ids[0];
793
+ if (!eventId) {
794
+ throw new Error("Event ID is not set");
795
+ }
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
116
840
  }
117
841
  });
118
842
  const eventId = eventOutput.ids[0];
@@ -121,9 +845,7 @@ var InngestRun = class extends Run {
121
845
  }
122
846
  const runOutput = await this.getRunOutput(eventId);
123
847
  const result = runOutput?.output?.result;
124
- if (result.status === "failed") {
125
- result.error = new Error(result.error);
126
- }
848
+ this.hydrateFailedResult(result);
127
849
  if (result.status !== "suspended") {
128
850
  this.cleanup?.();
129
851
  }
@@ -141,26 +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,
891
+ workflowId: this.workflowId,
156
892
  stepResults: snapshot?.context,
157
893
  resume: {
158
894
  steps,
159
895
  stepResults: snapshot?.context,
160
- resumePayload: params.resumeData,
161
- // @ts-ignore
162
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
163
- }
896
+ resumePayload: resumeDataToUse,
897
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
898
+ },
899
+ requestContext: mergedRequestContext,
900
+ perStep: params.perStep
164
901
  }
165
902
  });
166
903
  const eventId = eventOutput.ids[0];
@@ -169,17 +906,108 @@ var InngestRun = class extends Run {
169
906
  }
170
907
  const runOutput = await this.getRunOutput(eventId);
171
908
  const result = runOutput?.output?.result;
172
- if (result.status === "failed") {
173
- 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");
174
999
  }
1000
+ const runOutput = await this.getRunOutput(eventId);
1001
+ const result = runOutput?.output?.result;
1002
+ this.hydrateFailedResult(result);
175
1003
  return result;
176
1004
  }
177
- watch(cb, type = "watch") {
1005
+ watch(cb) {
178
1006
  let active = true;
179
1007
  const streamPromise = subscribe(
180
1008
  {
181
1009
  channel: `workflow:${this.workflowId}:${this.runId}`,
182
- topics: [type],
1010
+ topics: ["watch"],
183
1011
  app: this.inngest
184
1012
  },
185
1013
  (message) => {
@@ -197,16 +1025,35 @@ var InngestRun = class extends Run {
197
1025
  });
198
1026
  };
199
1027
  }
200
- stream({ inputData, runtimeContext } = {}) {
1028
+ streamLegacy({ inputData, requestContext } = {}) {
201
1029
  const { readable, writable } = new TransformStream();
202
1030
  const writer = writable.getWriter();
1031
+ void writer.write({
1032
+ // @ts-ignore
1033
+ type: "start",
1034
+ // @ts-ignore
1035
+ payload: { runId: this.runId }
1036
+ });
203
1037
  const unwatch = this.watch(async (event) => {
204
1038
  try {
205
- 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);
206
1048
  } catch {
207
1049
  }
208
- }, "watch-v2");
1050
+ });
209
1051
  this.closeStreamAction = async () => {
1052
+ await writer.write({
1053
+ type: "finish",
1054
+ // @ts-ignore
1055
+ payload: { runId: this.runId }
1056
+ });
210
1057
  unwatch();
211
1058
  try {
212
1059
  await writer.close();
@@ -216,7 +1063,7 @@ var InngestRun = class extends Run {
216
1063
  writer.releaseLock();
217
1064
  }
218
1065
  };
219
- this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
1066
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
220
1067
  if (result.status !== "suspended") {
221
1068
  this.closeStreamAction?.().catch(() => {
222
1069
  });
@@ -228,55 +1075,203 @@ var InngestRun = class extends Run {
228
1075
  getWorkflowState: () => this.executionResults
229
1076
  };
230
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
+ }
231
1237
  };
1238
+
1239
+ // src/workflow.ts
232
1240
  var InngestWorkflow = class _InngestWorkflow extends Workflow {
233
1241
  #mastra;
234
1242
  inngest;
235
1243
  function;
1244
+ cronFunction;
1245
+ flowControlConfig;
1246
+ cronConfig;
236
1247
  constructor(params, inngest) {
237
- 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;
238
1255
  this.#mastra = params.mastra;
239
1256
  this.inngest = inngest;
240
- }
241
- async getWorkflowRuns(args) {
242
- const storage = this.#mastra?.getStorage();
243
- if (!storage) {
244
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
245
- return { runs: [], total: 0 };
1257
+ if (cron) {
1258
+ this.cronConfig = { cron, inputData, initialState };
246
1259
  }
247
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
248
1260
  }
249
- async getWorkflowRunById(runId) {
1261
+ async listWorkflowRuns(args) {
250
1262
  const storage = this.#mastra?.getStorage();
251
1263
  if (!storage) {
252
1264
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
253
- return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
254
- }
255
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
256
- return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
257
- }
258
- async getWorkflowRunExecutionResult(runId) {
259
- const storage = this.#mastra?.getStorage();
260
- if (!storage) {
261
- this.logger.debug("Cannot get workflow run execution result. Mastra storage is not initialized");
262
- return null;
263
- }
264
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
265
- if (!run?.snapshot) {
266
- return null;
1265
+ return { runs: [], total: 0 };
267
1266
  }
268
- if (typeof run.snapshot === "string") {
269
- return null;
1267
+ const workflowsStore = await storage.getStore("workflows");
1268
+ if (!workflowsStore) {
1269
+ return { runs: [], total: 0 };
270
1270
  }
271
- return {
272
- status: run.snapshot.status,
273
- result: run.snapshot.result,
274
- error: run.snapshot.error,
275
- payload: run.snapshot.context?.input,
276
- steps: run.snapshot.context
277
- };
1271
+ return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
278
1272
  }
279
1273
  __registerMastra(mastra) {
1274
+ super.__registerMastra(mastra);
280
1275
  this.#mastra = mastra;
281
1276
  this.executionEngine.__registerMastra(mastra);
282
1277
  const updateNested = (step) => {
@@ -294,62 +1289,85 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
294
1289
  }
295
1290
  }
296
1291
  }
297
- createRun(options) {
298
- const runIdToUse = options?.runId || randomUUID();
299
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
300
- {
301
- workflowId: this.id,
302
- runId: runIdToUse,
303
- executionEngine: this.executionEngine,
304
- executionGraph: this.executionGraph,
305
- serializedStepGraph: this.serializedStepGraph,
306
- mastra: this.#mastra,
307
- retryConfig: this.retryConfig,
308
- cleanup: () => this.runs.delete(runIdToUse)
309
- },
310
- this.inngest
311
- );
312
- this.runs.set(runIdToUse, run);
313
- return run;
314
- }
315
- async createRunAsync(options) {
1292
+ async createRun(options) {
316
1293
  const runIdToUse = options?.runId || randomUUID();
317
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
1294
+ const existingInMemoryRun = this.runs.get(runIdToUse);
1295
+ const newRun = new InngestRun(
318
1296
  {
319
1297
  workflowId: this.id,
320
1298
  runId: runIdToUse,
1299
+ resourceId: options?.resourceId,
321
1300
  executionEngine: this.executionEngine,
322
1301
  executionGraph: this.executionGraph,
323
1302
  serializedStepGraph: this.serializedStepGraph,
324
1303
  mastra: this.#mastra,
325
1304
  retryConfig: this.retryConfig,
326
- cleanup: () => this.runs.delete(runIdToUse)
1305
+ cleanup: () => this.runs.delete(runIdToUse),
1306
+ workflowSteps: this.steps,
1307
+ workflowEngineType: this.engineType,
1308
+ validateInputs: this.options.validateInputs
327
1309
  },
328
1310
  this.inngest
329
1311
  );
1312
+ const run = existingInMemoryRun ?? newRun;
330
1313
  this.runs.set(runIdToUse, run);
331
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
332
- if (!workflowSnapshotInStorage) {
333
- 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({
334
1325
  workflowName: this.id,
335
1326
  runId: runIdToUse,
1327
+ resourceId: options?.resourceId,
336
1328
  snapshot: {
337
1329
  runId: runIdToUse,
338
1330
  status: "pending",
339
1331
  value: {},
340
1332
  context: {},
341
1333
  activePaths: [],
1334
+ activeStepsPath: {},
1335
+ waitingPaths: {},
342
1336
  serializedStepGraph: this.serializedStepGraph,
343
1337
  suspendedPaths: {},
1338
+ resumeLabels: {},
344
1339
  result: void 0,
345
1340
  error: void 0,
346
- // @ts-ignore
347
1341
  timestamp: Date.now()
348
1342
  }
349
1343
  });
350
1344
  }
351
1345
  return run;
352
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
+ }
353
1371
  getFunction() {
354
1372
  if (this.function) {
355
1373
  return this.function;
@@ -357,53 +1375,134 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
357
1375
  this.function = this.inngest.createFunction(
358
1376
  {
359
1377
  id: `workflow.${this.id}`,
360
- // @ts-ignore
361
- retries: this.retryConfig?.attempts ?? 0,
362
- cancelOn: [{ event: `cancel.workflow.${this.id}` }]
1378
+ retries: 0,
1379
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1380
+ // Spread flow control configuration
1381
+ ...this.flowControlConfig
363
1382
  },
364
1383
  { event: `workflow.${this.id}` },
365
1384
  async ({ event, step, attempt, publish }) => {
366
- 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;
367
1397
  if (!runId) {
368
1398
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
369
1399
  return randomUUID();
370
1400
  });
371
1401
  }
372
- const emitter = {
373
- emit: async (event2, data) => {
374
- if (!publish) {
375
- 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
+ }
376
1462
  }
377
- try {
378
- await publish({
379
- channel: `workflow:${this.id}:${runId}`,
380
- topic: event2,
381
- data
382
- });
383
- } catch (err) {
384
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
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
+ }
385
1498
  }
386
- },
387
- on: (_event, _callback) => {
388
- },
389
- off: (_event, _callback) => {
390
- },
391
- once: (_event, _callback) => {
392
1499
  }
393
- };
394
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
395
- const result = await engine.execute({
396
- workflowId: this.id,
397
- runId,
398
- graph: this.executionGraph,
399
- serializedStepGraph: this.serializedStepGraph,
400
- input: inputData,
401
- emitter,
402
- retryConfig: this.retryConfig,
403
- runtimeContext: new RuntimeContext(),
404
- // TODO
405
- resume,
406
- abortController: new AbortController()
1500
+ if (result.status === "failed") {
1501
+ throw new NonRetriableError(`Workflow failed`, {
1502
+ cause: result
1503
+ });
1504
+ }
1505
+ return result;
407
1506
  });
408
1507
  return { result, runId };
409
1508
  }
@@ -424,103 +1523,201 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
424
1523
  });
425
1524
  }
426
1525
  getFunctions() {
427
- 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
+ ];
428
1531
  }
429
1532
  };
430
- function isAgent(params) {
431
- 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;
1569
+ }
1570
+ function isToolStep(input) {
1571
+ return input instanceof Tool;
1572
+ }
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);
432
1575
  }
433
- function isTool(params) {
434
- return params instanceof Tool;
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");
435
1578
  }
436
- function createStep(params) {
1579
+ function createStep(params, agentOrToolOptions) {
1580
+ if (isInngestWorkflow(params)) {
1581
+ return params;
1582
+ }
437
1583
  if (isAgent(params)) {
438
- return {
439
- id: params.name,
440
- // @ts-ignore
441
- inputSchema: z.object({
442
- prompt: z.string()
443
- // resourceId: z.string().optional(),
444
- // threadId: z.string().optional(),
445
- }),
446
- // @ts-ignore
447
- outputSchema: z.object({
448
- text: z.string()
449
- }),
450
- execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
451
- let streamPromise = {};
452
- streamPromise.promise = new Promise((resolve, reject) => {
453
- streamPromise.resolve = resolve;
454
- streamPromise.reject = reject;
455
- });
456
- const toolData = {
457
- name: params.name,
458
- args: inputData
459
- };
460
- await emitter.emit("watch-v2", {
461
- type: "tool-call-streaming-start",
462
- ...toolData
463
- });
464
- const { fullStream } = await params.stream(inputData.prompt, {
465
- // resourceId: inputData.resourceId,
466
- // threadId: inputData.threadId,
467
- 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,
468
1651
  onFinish: (result) => {
1652
+ const resultWithObject = result;
1653
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1654
+ structuredResult = resultWithObject.object;
1655
+ }
469
1656
  streamPromise.resolve(result.text);
1657
+ void agentOptions?.onFinish?.(result);
470
1658
  },
471
1659
  abortSignal
472
1660
  });
473
- if (abortSignal.aborted) {
474
- return abort();
475
- }
476
- for await (const chunk of fullStream) {
477
- switch (chunk.type) {
478
- case "text-delta":
479
- await emitter.emit("watch-v2", {
480
- type: "tool-call-delta",
481
- ...toolData,
482
- argsTextDelta: chunk.textDelta
483
- });
484
- break;
485
- case "step-start":
486
- case "step-finish":
487
- case "finish":
488
- break;
489
- case "tool-call":
490
- case "tool-result":
491
- case "tool-call-streaming-start":
492
- case "tool-call-delta":
493
- case "source":
494
- case "file":
495
- default:
496
- await emitter.emit("watch-v2", chunk);
497
- 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
+ });
498
1692
  }
499
1693
  }
500
- return {
501
- text: await streamPromise.promise
502
- };
503
- }
504
- };
505
- }
506
- if (isTool(params)) {
507
- if (!params.inputSchema || !params.outputSchema) {
508
- throw new Error("Tool must have input and output schemas defined");
509
- }
510
- return {
511
- // TODO: tool probably should have strong id type
512
- // @ts-ignore
513
- id: params.id,
514
- inputSchema: params.inputSchema,
515
- outputSchema: params.outputSchema,
516
- execute: async ({ inputData, mastra, runtimeContext }) => {
517
- return params.execute({
518
- context: inputData,
519
- mastra,
520
- runtimeContext
1694
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1695
+ type: "watch",
1696
+ runId,
1697
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
521
1698
  });
1699
+ } else {
1700
+ for await (const chunk of stream) {
1701
+ await writer.write(chunk);
1702
+ }
522
1703
  }
523
- };
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");
524
1721
  }
525
1722
  return {
526
1723
  id: params.id,
@@ -529,13 +1726,489 @@ function createStep(params) {
529
1726
  outputSchema: params.outputSchema,
530
1727
  resumeSchema: params.resumeSchema,
531
1728
  suspendSchema: params.suspendSchema,
532
- 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"
533
2203
  };
534
2204
  }
535
2205
  function init(inngest) {
536
2206
  return {
537
2207
  createWorkflow(params) {
538
- return new InngestWorkflow(params, inngest);
2208
+ return new InngestWorkflow(
2209
+ params,
2210
+ inngest
2211
+ );
539
2212
  },
540
2213
  createStep,
541
2214
  cloneStep(step, opts) {
@@ -544,7 +2217,13 @@ function init(inngest) {
544
2217
  description: step.description,
545
2218
  inputSchema: step.inputSchema,
546
2219
  outputSchema: step.outputSchema,
547
- 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
548
2227
  };
549
2228
  },
550
2229
  cloneWorkflow(workflow, opts) {
@@ -553,7 +2232,8 @@ function init(inngest) {
553
2232
  inputSchema: workflow.inputSchema,
554
2233
  outputSchema: workflow.outputSchema,
555
2234
  steps: workflow.stepDefs,
556
- mastra: workflow.mastra
2235
+ mastra: workflow.mastra,
2236
+ options: workflow.options
557
2237
  });
558
2238
  wf.setStepFlow(workflow.stepGraph);
559
2239
  wf.commit();
@@ -561,691 +2241,7 @@ function init(inngest) {
561
2241
  }
562
2242
  };
563
2243
  }
564
- var InngestExecutionEngine = class extends DefaultExecutionEngine {
565
- inngestStep;
566
- inngestAttempts;
567
- constructor(mastra, inngestStep, inngestAttempts = 0) {
568
- super({ mastra });
569
- this.inngestStep = inngestStep;
570
- this.inngestAttempts = inngestAttempts;
571
- }
572
- async execute(params) {
573
- await params.emitter.emit("watch-v2", {
574
- type: "start",
575
- payload: { runId: params.runId }
576
- });
577
- const result = await super.execute(params);
578
- await params.emitter.emit("watch-v2", {
579
- type: "finish",
580
- payload: { runId: params.runId }
581
- });
582
- return result;
583
- }
584
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
585
- const base = {
586
- status: lastOutput.status,
587
- steps: stepResults
588
- };
589
- if (lastOutput.status === "success") {
590
- await emitter.emit("watch", {
591
- type: "watch",
592
- payload: {
593
- workflowState: {
594
- status: lastOutput.status,
595
- steps: stepResults,
596
- result: lastOutput.output
597
- }
598
- },
599
- eventTimestamp: Date.now()
600
- });
601
- base.result = lastOutput.output;
602
- } else if (lastOutput.status === "failed") {
603
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
604
- await emitter.emit("watch", {
605
- type: "watch",
606
- payload: {
607
- workflowState: {
608
- status: lastOutput.status,
609
- steps: stepResults,
610
- result: null,
611
- error: base.error
612
- }
613
- },
614
- eventTimestamp: Date.now()
615
- });
616
- } else if (lastOutput.status === "suspended") {
617
- await emitter.emit("watch", {
618
- type: "watch",
619
- payload: {
620
- workflowState: {
621
- status: lastOutput.status,
622
- steps: stepResults,
623
- result: null,
624
- error: null
625
- }
626
- },
627
- eventTimestamp: Date.now()
628
- });
629
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
630
- if (stepResult?.status === "suspended") {
631
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
632
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
633
- }
634
- return [];
635
- });
636
- base.suspended = suspendedStepIds;
637
- }
638
- executionSpan?.end();
639
- return base;
640
- }
641
- async superExecuteStep({
642
- workflowId,
643
- runId,
644
- step,
645
- stepResults,
646
- executionContext,
647
- resume,
648
- prevOutput,
649
- emitter,
650
- abortController,
651
- runtimeContext
652
- }) {
653
- return super.executeStep({
654
- workflowId,
655
- runId,
656
- step,
657
- stepResults,
658
- executionContext,
659
- resume,
660
- prevOutput,
661
- emitter,
662
- abortController,
663
- runtimeContext
664
- });
665
- }
666
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
667
- // await this.inngestStep.sleep(id, duration);
668
- // }
669
- async executeSleep({
670
- workflowId,
671
- runId,
672
- entry,
673
- prevOutput,
674
- stepResults,
675
- emitter,
676
- abortController,
677
- runtimeContext
678
- }) {
679
- let { duration, fn } = entry;
680
- if (fn) {
681
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
682
- return await fn({
683
- runId,
684
- mastra: this.mastra,
685
- runtimeContext,
686
- inputData: prevOutput,
687
- runCount: -1,
688
- getInitData: () => stepResults?.input,
689
- getStepResult: (step) => {
690
- if (!step?.id) {
691
- return null;
692
- }
693
- const result = stepResults[step.id];
694
- if (result?.status === "success") {
695
- return result.output;
696
- }
697
- return null;
698
- },
699
- // TODO: this function shouldn't have suspend probably?
700
- suspend: async (_suspendPayload) => {
701
- },
702
- bail: () => {
703
- },
704
- abort: () => {
705
- abortController?.abort();
706
- },
707
- [EMITTER_SYMBOL]: emitter,
708
- engine: { step: this.inngestStep },
709
- abortSignal: abortController?.signal
710
- });
711
- });
712
- }
713
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
714
- }
715
- async executeSleepUntil({
716
- workflowId,
717
- runId,
718
- entry,
719
- prevOutput,
720
- stepResults,
721
- emitter,
722
- abortController,
723
- runtimeContext
724
- }) {
725
- let { date, fn } = entry;
726
- if (fn) {
727
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
728
- return await fn({
729
- runId,
730
- mastra: this.mastra,
731
- runtimeContext,
732
- inputData: prevOutput,
733
- runCount: -1,
734
- getInitData: () => stepResults?.input,
735
- getStepResult: (step) => {
736
- if (!step?.id) {
737
- return null;
738
- }
739
- const result = stepResults[step.id];
740
- if (result?.status === "success") {
741
- return result.output;
742
- }
743
- return null;
744
- },
745
- // TODO: this function shouldn't have suspend probably?
746
- suspend: async (_suspendPayload) => {
747
- },
748
- bail: () => {
749
- },
750
- abort: () => {
751
- abortController?.abort();
752
- },
753
- [EMITTER_SYMBOL]: emitter,
754
- engine: { step: this.inngestStep },
755
- abortSignal: abortController?.signal
756
- });
757
- });
758
- }
759
- if (!(date instanceof Date)) {
760
- return;
761
- }
762
- await this.inngestStep.sleepUntil(entry.id, date);
763
- }
764
- async executeWaitForEvent({ event, timeout }) {
765
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
766
- event: `user-event-${event}`,
767
- timeout: timeout ?? 5e3
768
- });
769
- if (eventData === null) {
770
- throw "Timeout waiting for event";
771
- }
772
- return eventData?.data;
773
- }
774
- async executeStep({
775
- step,
776
- stepResults,
777
- executionContext,
778
- resume,
779
- prevOutput,
780
- emitter,
781
- abortController,
782
- runtimeContext
783
- }) {
784
- const startedAt = await this.inngestStep.run(
785
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
786
- async () => {
787
- const startedAt2 = Date.now();
788
- await emitter.emit("watch", {
789
- type: "watch",
790
- payload: {
791
- currentStep: {
792
- id: step.id,
793
- status: "running"
794
- },
795
- workflowState: {
796
- status: "running",
797
- steps: {
798
- ...stepResults,
799
- [step.id]: {
800
- status: "running"
801
- }
802
- },
803
- result: null,
804
- error: null
805
- }
806
- },
807
- eventTimestamp: Date.now()
808
- });
809
- await emitter.emit("watch-v2", {
810
- type: "step-start",
811
- payload: {
812
- id: step.id,
813
- status: "running",
814
- payload: prevOutput,
815
- startedAt: startedAt2
816
- }
817
- });
818
- return startedAt2;
819
- }
820
- );
821
- if (step instanceof InngestWorkflow) {
822
- const isResume = !!resume?.steps?.length;
823
- let result;
824
- let runId;
825
- if (isResume) {
826
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
827
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
828
- workflowName: step.id,
829
- runId
830
- });
831
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
832
- function: step.getFunction(),
833
- data: {
834
- inputData: prevOutput,
835
- runId,
836
- resume: {
837
- runId,
838
- steps: resume.steps.slice(1),
839
- stepResults: snapshot?.context,
840
- resumePayload: resume.resumePayload,
841
- // @ts-ignore
842
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
843
- }
844
- }
845
- });
846
- result = invokeResp.result;
847
- runId = invokeResp.runId;
848
- } else {
849
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
850
- function: step.getFunction(),
851
- data: {
852
- inputData: prevOutput
853
- }
854
- });
855
- result = invokeResp.result;
856
- runId = invokeResp.runId;
857
- }
858
- const res = await this.inngestStep.run(
859
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
860
- async () => {
861
- if (result.status === "failed") {
862
- await emitter.emit("watch", {
863
- type: "watch",
864
- payload: {
865
- currentStep: {
866
- id: step.id,
867
- status: "failed",
868
- error: result?.error
869
- },
870
- workflowState: {
871
- status: "running",
872
- steps: stepResults,
873
- result: null,
874
- error: null
875
- }
876
- },
877
- eventTimestamp: Date.now()
878
- });
879
- await emitter.emit("watch-v2", {
880
- type: "step-result",
881
- payload: {
882
- id: step.id,
883
- status: "failed",
884
- error: result?.error,
885
- payload: prevOutput
886
- }
887
- });
888
- return { executionContext, result: { status: "failed", error: result?.error } };
889
- } else if (result.status === "suspended") {
890
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
891
- const stepRes2 = stepResult;
892
- return stepRes2?.status === "suspended";
893
- });
894
- for (const [stepName, stepResult] of suspendedSteps) {
895
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
896
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
897
- await emitter.emit("watch", {
898
- type: "watch",
899
- payload: {
900
- currentStep: {
901
- id: step.id,
902
- status: "suspended",
903
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
904
- },
905
- workflowState: {
906
- status: "running",
907
- steps: stepResults,
908
- result: null,
909
- error: null
910
- }
911
- },
912
- eventTimestamp: Date.now()
913
- });
914
- await emitter.emit("watch-v2", {
915
- type: "step-suspended",
916
- payload: {
917
- id: step.id,
918
- status: "suspended"
919
- }
920
- });
921
- return {
922
- executionContext,
923
- result: {
924
- status: "suspended",
925
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
926
- }
927
- };
928
- }
929
- await emitter.emit("watch", {
930
- type: "watch",
931
- payload: {
932
- currentStep: {
933
- id: step.id,
934
- status: "suspended",
935
- payload: {}
936
- },
937
- workflowState: {
938
- status: "running",
939
- steps: stepResults,
940
- result: null,
941
- error: null
942
- }
943
- },
944
- eventTimestamp: Date.now()
945
- });
946
- return {
947
- executionContext,
948
- result: {
949
- status: "suspended",
950
- payload: {}
951
- }
952
- };
953
- }
954
- await emitter.emit("watch", {
955
- type: "watch",
956
- payload: {
957
- currentStep: {
958
- id: step.id,
959
- status: "success",
960
- output: result?.result
961
- },
962
- workflowState: {
963
- status: "running",
964
- steps: stepResults,
965
- result: null,
966
- error: null
967
- }
968
- },
969
- eventTimestamp: Date.now()
970
- });
971
- await emitter.emit("watch-v2", {
972
- type: "step-result",
973
- payload: {
974
- id: step.id,
975
- status: "success",
976
- output: result?.result
977
- }
978
- });
979
- await emitter.emit("watch-v2", {
980
- type: "step-finish",
981
- payload: {
982
- id: step.id,
983
- metadata: {}
984
- }
985
- });
986
- return { executionContext, result: { status: "success", output: result?.result } };
987
- }
988
- );
989
- Object.assign(executionContext, res.executionContext);
990
- return res.result;
991
- }
992
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
993
- let execResults;
994
- let suspended;
995
- let bailed;
996
- try {
997
- const result = await step.execute({
998
- runId: executionContext.runId,
999
- mastra: this.mastra,
1000
- runtimeContext,
1001
- inputData: prevOutput,
1002
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1003
- getInitData: () => stepResults?.input,
1004
- getStepResult: (step2) => {
1005
- const result2 = stepResults[step2.id];
1006
- if (result2?.status === "success") {
1007
- return result2.output;
1008
- }
1009
- return null;
1010
- },
1011
- suspend: async (suspendPayload) => {
1012
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1013
- suspended = { payload: suspendPayload };
1014
- },
1015
- bail: (result2) => {
1016
- bailed = { payload: result2 };
1017
- },
1018
- resume: {
1019
- steps: resume?.steps?.slice(1) || [],
1020
- resumePayload: resume?.resumePayload,
1021
- // @ts-ignore
1022
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1023
- },
1024
- [EMITTER_SYMBOL]: emitter,
1025
- engine: {
1026
- step: this.inngestStep
1027
- },
1028
- abortSignal: abortController.signal
1029
- });
1030
- const endedAt = Date.now();
1031
- execResults = {
1032
- status: "success",
1033
- output: result,
1034
- startedAt,
1035
- endedAt,
1036
- payload: prevOutput,
1037
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1038
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1039
- };
1040
- } catch (e) {
1041
- execResults = {
1042
- status: "failed",
1043
- payload: prevOutput,
1044
- error: e instanceof Error ? e.message : String(e),
1045
- endedAt: Date.now(),
1046
- startedAt,
1047
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1048
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1049
- };
1050
- }
1051
- if (suspended) {
1052
- execResults = {
1053
- status: "suspended",
1054
- suspendedPayload: suspended.payload,
1055
- payload: prevOutput,
1056
- suspendedAt: Date.now(),
1057
- startedAt,
1058
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1059
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1060
- };
1061
- } else if (bailed) {
1062
- execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
1063
- }
1064
- if (execResults.status === "failed") {
1065
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1066
- throw execResults.error;
1067
- }
1068
- }
1069
- await emitter.emit("watch", {
1070
- type: "watch",
1071
- payload: {
1072
- currentStep: {
1073
- id: step.id,
1074
- ...execResults
1075
- },
1076
- workflowState: {
1077
- status: "running",
1078
- steps: { ...stepResults, [step.id]: execResults },
1079
- result: null,
1080
- error: null
1081
- }
1082
- },
1083
- eventTimestamp: Date.now()
1084
- });
1085
- if (execResults.status === "suspended") {
1086
- await emitter.emit("watch-v2", {
1087
- type: "step-suspended",
1088
- payload: {
1089
- id: step.id,
1090
- ...execResults
1091
- }
1092
- });
1093
- } else {
1094
- await emitter.emit("watch-v2", {
1095
- type: "step-result",
1096
- payload: {
1097
- id: step.id,
1098
- ...execResults
1099
- }
1100
- });
1101
- await emitter.emit("watch-v2", {
1102
- type: "step-finish",
1103
- payload: {
1104
- id: step.id,
1105
- metadata: {}
1106
- }
1107
- });
1108
- }
1109
- return { result: execResults, executionContext, stepResults };
1110
- });
1111
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1112
- Object.assign(stepResults, stepRes.stepResults);
1113
- return stepRes.result;
1114
- }
1115
- async persistStepUpdate({
1116
- workflowId,
1117
- runId,
1118
- stepResults,
1119
- executionContext,
1120
- serializedStepGraph,
1121
- workflowStatus,
1122
- result,
1123
- error
1124
- }) {
1125
- await this.inngestStep.run(
1126
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1127
- async () => {
1128
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1129
- workflowName: workflowId,
1130
- runId,
1131
- snapshot: {
1132
- runId,
1133
- value: {},
1134
- context: stepResults,
1135
- activePaths: [],
1136
- suspendedPaths: executionContext.suspendedPaths,
1137
- serializedStepGraph,
1138
- status: workflowStatus,
1139
- result,
1140
- error,
1141
- // @ts-ignore
1142
- timestamp: Date.now()
1143
- }
1144
- });
1145
- }
1146
- );
1147
- }
1148
- async executeConditional({
1149
- workflowId,
1150
- runId,
1151
- entry,
1152
- prevOutput,
1153
- prevStep,
1154
- stepResults,
1155
- serializedStepGraph,
1156
- resume,
1157
- executionContext,
1158
- emitter,
1159
- abortController,
1160
- runtimeContext
1161
- }) {
1162
- let execResults;
1163
- const truthyIndexes = (await Promise.all(
1164
- entry.conditions.map(
1165
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1166
- try {
1167
- const result = await cond({
1168
- runId,
1169
- mastra: this.mastra,
1170
- runtimeContext,
1171
- runCount: -1,
1172
- inputData: prevOutput,
1173
- getInitData: () => stepResults?.input,
1174
- getStepResult: (step) => {
1175
- if (!step?.id) {
1176
- return null;
1177
- }
1178
- const result2 = stepResults[step.id];
1179
- if (result2?.status === "success") {
1180
- return result2.output;
1181
- }
1182
- return null;
1183
- },
1184
- // TODO: this function shouldn't have suspend probably?
1185
- suspend: async (_suspendPayload) => {
1186
- },
1187
- bail: () => {
1188
- },
1189
- abort: () => {
1190
- abortController.abort();
1191
- },
1192
- [EMITTER_SYMBOL]: emitter,
1193
- engine: {
1194
- step: this.inngestStep
1195
- },
1196
- abortSignal: abortController.signal
1197
- });
1198
- return result ? index : null;
1199
- } catch (e) {
1200
- return null;
1201
- }
1202
- })
1203
- )
1204
- )).filter((index) => index !== null);
1205
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1206
- const results = await Promise.all(
1207
- stepsToRun.map(
1208
- (step, index) => this.executeEntry({
1209
- workflowId,
1210
- runId,
1211
- entry: step,
1212
- prevStep,
1213
- stepResults,
1214
- resume,
1215
- serializedStepGraph,
1216
- executionContext: {
1217
- workflowId,
1218
- runId,
1219
- executionPath: [...executionContext.executionPath, index],
1220
- suspendedPaths: executionContext.suspendedPaths,
1221
- retryConfig: executionContext.retryConfig,
1222
- executionSpan: executionContext.executionSpan
1223
- },
1224
- emitter,
1225
- abortController,
1226
- runtimeContext
1227
- })
1228
- )
1229
- );
1230
- const hasFailed = results.find((result) => result.result.status === "failed");
1231
- const hasSuspended = results.find((result) => result.result.status === "suspended");
1232
- if (hasFailed) {
1233
- execResults = { status: "failed", error: hasFailed.result.error };
1234
- } else if (hasSuspended) {
1235
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1236
- } else {
1237
- execResults = {
1238
- status: "success",
1239
- output: results.reduce((acc, result, index) => {
1240
- if (result.result.status === "success") {
1241
- acc[stepsToRun[index].step.id] = result.output;
1242
- }
1243
- return acc;
1244
- }, {})
1245
- };
1246
- }
1247
- return execResults;
1248
- }
1249
- };
1250
2244
 
1251
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
2245
+ export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createServe, createStep, init, serve };
2246
+ //# sourceMappingURL=index.js.map
2247
+ //# sourceMappingURL=index.js.map