@mastra/inngest 0.0.0-allow-to-pass-a-mastra-url-instance-20251105224938 → 0.0.0-auth-and-authz-20260130180831

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