@mastra/inngest 0.0.0-cloudflare-deployer-dont-install-deps-20250714111754 → 0.0.0-cloudflare-file-url-lazy-20260130204903

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