@mastra/inngest 0.0.0-main-test-2-20251127211532 → 0.0.0-mastra-auto-detect-server-20260108233416

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,41 +1,511 @@
1
+ import { Agent } from '@mastra/core/agent';
2
+ import { Tool } from '@mastra/core/tools';
3
+ import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, hydrateSerializedStepErrors, Workflow } from '@mastra/core/workflows';
4
+ import { PUBSUB_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
5
+ import { z } from 'zod';
1
6
  import { randomUUID } from 'crypto';
2
- import { ReadableStream, WritableStream } from 'stream/web';
3
- import { subscribe } from '@inngest/realtime';
4
7
  import { RequestContext } from '@mastra/core/di';
5
- import { SpanType } from '@mastra/core/observability';
8
+ import { NonRetriableError } from 'inngest';
9
+ import { getErrorFromUnknown } from '@mastra/core/error';
10
+ import { subscribe } from '@inngest/realtime';
11
+ import { PubSub } from '@mastra/core/events';
12
+ import { ReadableStream } from 'stream/web';
6
13
  import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
7
- import { ToolStream, Tool } from '@mastra/core/tools';
8
- import { Run, createTimeTravelExecutionParams, Workflow, DefaultExecutionEngine, createDeprecationProxy, getStepResult, runCountDeprecationMessage, validateStepInput, validateStepResumeData, validateStepSuspendData } from '@mastra/core/workflows';
9
- import { EMITTER_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
10
- import { NonRetriableError, RetryAfterError } from 'inngest';
11
14
  import { serve as serve$1 } from 'inngest/hono';
12
- import { z } from 'zod';
13
15
 
14
16
  // src/index.ts
15
- function serve({
16
- mastra,
17
- inngest,
18
- functions: userFunctions = [],
19
- registerOptions
20
- }) {
21
- const wfs = mastra.listWorkflows();
22
- const workflowFunctions = Array.from(
23
- new Set(
24
- Object.values(wfs).flatMap((wf) => {
25
- if (wf instanceof InngestWorkflow) {
26
- wf.__registerMastra(mastra);
27
- return wf.getFunctions();
17
+ var InngestExecutionEngine = class extends DefaultExecutionEngine {
18
+ inngestStep;
19
+ inngestAttempts;
20
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
21
+ super({ mastra, options });
22
+ this.inngestStep = inngestStep;
23
+ this.inngestAttempts = inngestAttempts;
24
+ }
25
+ // =============================================================================
26
+ // Hook Overrides
27
+ // =============================================================================
28
+ /**
29
+ * Format errors while preserving Error instances and their custom properties.
30
+ * Uses getErrorFromUnknown to ensure all error properties are preserved.
31
+ */
32
+ formatResultError(error, lastOutput) {
33
+ const outputError = lastOutput?.error;
34
+ const errorSource = error || outputError;
35
+ const errorInstance = getErrorFromUnknown(errorSource, {
36
+ serializeStack: true,
37
+ // Include stack in JSON for better debugging in Inngest
38
+ fallbackMessage: "Unknown workflow error"
39
+ });
40
+ return errorInstance.toJSON();
41
+ }
42
+ /**
43
+ * Detect InngestWorkflow instances for special nested workflow handling
44
+ */
45
+ isNestedWorkflowStep(step) {
46
+ return step instanceof InngestWorkflow;
47
+ }
48
+ /**
49
+ * Inngest requires requestContext serialization for memoization.
50
+ * When steps are replayed, the original function doesn't re-execute,
51
+ * so requestContext modifications must be captured and restored.
52
+ */
53
+ requiresDurableContextSerialization() {
54
+ return true;
55
+ }
56
+ /**
57
+ * Execute a step with retry logic for Inngest.
58
+ * Retries are handled via step-level retry (RetryAfterError thrown INSIDE step.run()).
59
+ * After retries exhausted, error propagates here and we return a failed result.
60
+ */
61
+ async executeStepWithRetry(stepId, runStep, params) {
62
+ for (let i = 0; i < params.retries + 1; i++) {
63
+ if (i > 0 && params.delay) {
64
+ await new Promise((resolve) => setTimeout(resolve, params.delay));
65
+ }
66
+ try {
67
+ const result = await this.wrapDurableOperation(stepId, runStep);
68
+ return { ok: true, result };
69
+ } catch (e) {
70
+ if (i === params.retries) {
71
+ const cause = e?.cause;
72
+ if (cause?.status === "failed") {
73
+ params.stepSpan?.error({
74
+ error: e,
75
+ attributes: { status: "failed" }
76
+ });
77
+ if (cause.error && !(cause.error instanceof Error)) {
78
+ cause.error = getErrorFromUnknown(cause.error, { serializeStack: false });
79
+ }
80
+ return { ok: false, error: cause };
81
+ }
82
+ const errorInstance = getErrorFromUnknown(e, {
83
+ serializeStack: false,
84
+ fallbackMessage: "Unknown step execution error"
85
+ });
86
+ params.stepSpan?.error({
87
+ error: errorInstance,
88
+ attributes: { status: "failed" }
89
+ });
90
+ return {
91
+ ok: false,
92
+ error: {
93
+ status: "failed",
94
+ error: errorInstance,
95
+ endedAt: Date.now()
96
+ }
97
+ };
28
98
  }
29
- return [];
30
- })
31
- )
32
- );
33
- return serve$1({
34
- ...registerOptions,
35
- client: inngest,
36
- functions: [...workflowFunctions, ...userFunctions]
37
- });
38
- }
99
+ }
100
+ }
101
+ return { ok: false, error: { status: "failed", error: new Error("Unknown error"), endedAt: Date.now() } };
102
+ }
103
+ /**
104
+ * Use Inngest's sleep primitive for durability
105
+ */
106
+ async executeSleepDuration(duration, sleepId, workflowId) {
107
+ await this.inngestStep.sleep(`workflow.${workflowId}.sleep.${sleepId}`, duration < 0 ? 0 : duration);
108
+ }
109
+ /**
110
+ * Use Inngest's sleepUntil primitive for durability
111
+ */
112
+ async executeSleepUntilDate(date, sleepUntilId, workflowId) {
113
+ await this.inngestStep.sleepUntil(`workflow.${workflowId}.sleepUntil.${sleepUntilId}`, date);
114
+ }
115
+ /**
116
+ * Wrap durable operations in Inngest step.run() for durability.
117
+ * If retryConfig is provided, throws RetryAfterError INSIDE step.run() to trigger
118
+ * Inngest's step-level retry mechanism (not function-level retry).
119
+ */
120
+ async wrapDurableOperation(operationId, operationFn) {
121
+ return this.inngestStep.run(operationId, async () => {
122
+ try {
123
+ return await operationFn();
124
+ } catch (e) {
125
+ throw e;
126
+ }
127
+ });
128
+ }
129
+ /**
130
+ * Provide Inngest step primitive in engine context
131
+ */
132
+ getEngineContext() {
133
+ return { step: this.inngestStep };
134
+ }
135
+ /**
136
+ * For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
137
+ * (wrapped in step.run for durability), not in execute(). Override to skip.
138
+ */
139
+ async invokeLifecycleCallbacks(_result) {
140
+ }
141
+ /**
142
+ * Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
143
+ */
144
+ async invokeLifecycleCallbacksInternal(result) {
145
+ return super.invokeLifecycleCallbacks(result);
146
+ }
147
+ /**
148
+ * Execute nested InngestWorkflow using inngestStep.invoke() for durability.
149
+ * This MUST be called directly (not inside step.run()) due to Inngest constraints.
150
+ */
151
+ async executeWorkflowStep(params) {
152
+ if (!(params.step instanceof InngestWorkflow)) {
153
+ return null;
154
+ }
155
+ const {
156
+ step,
157
+ stepResults,
158
+ executionContext,
159
+ resume,
160
+ timeTravel,
161
+ prevOutput,
162
+ inputData,
163
+ pubsub,
164
+ startedAt,
165
+ perStep
166
+ } = params;
167
+ const isResume = !!resume?.steps?.length;
168
+ let result;
169
+ let runId;
170
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
171
+ try {
172
+ if (isResume) {
173
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
174
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
175
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
176
+ workflowName: step.id,
177
+ runId
178
+ });
179
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
180
+ function: step.getFunction(),
181
+ data: {
182
+ inputData,
183
+ initialState: executionContext.state ?? snapshot?.value ?? {},
184
+ runId,
185
+ resume: {
186
+ runId,
187
+ steps: resume.steps.slice(1),
188
+ stepResults: snapshot?.context,
189
+ resumePayload: resume.resumePayload,
190
+ resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
191
+ },
192
+ outputOptions: { includeState: true },
193
+ perStep
194
+ }
195
+ });
196
+ result = invokeResp.result;
197
+ runId = invokeResp.runId;
198
+ executionContext.state = invokeResp.result.state;
199
+ } else if (isTimeTravel) {
200
+ const workflowsStoreForTimeTravel = await this.mastra?.getStorage()?.getStore("workflows");
201
+ const snapshot = await workflowsStoreForTimeTravel?.loadWorkflowSnapshot({
202
+ workflowName: step.id,
203
+ runId: executionContext.runId
204
+ }) ?? { context: {} };
205
+ const timeTravelParams = createTimeTravelExecutionParams({
206
+ steps: timeTravel.steps.slice(1),
207
+ inputData: timeTravel.inputData,
208
+ resumeData: timeTravel.resumeData,
209
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
210
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
211
+ snapshot,
212
+ graph: step.buildExecutionGraph()
213
+ });
214
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
215
+ function: step.getFunction(),
216
+ data: {
217
+ timeTravel: timeTravelParams,
218
+ initialState: executionContext.state ?? {},
219
+ runId: executionContext.runId,
220
+ outputOptions: { includeState: true },
221
+ perStep
222
+ }
223
+ });
224
+ result = invokeResp.result;
225
+ runId = invokeResp.runId;
226
+ executionContext.state = invokeResp.result.state;
227
+ } else {
228
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
229
+ function: step.getFunction(),
230
+ data: {
231
+ inputData,
232
+ initialState: executionContext.state ?? {},
233
+ outputOptions: { includeState: true },
234
+ perStep
235
+ }
236
+ });
237
+ result = invokeResp.result;
238
+ runId = invokeResp.runId;
239
+ executionContext.state = invokeResp.result.state;
240
+ }
241
+ } catch (e) {
242
+ const errorCause = e?.cause;
243
+ if (errorCause && typeof errorCause === "object") {
244
+ result = errorCause;
245
+ runId = errorCause.runId || randomUUID();
246
+ } else {
247
+ runId = randomUUID();
248
+ result = {
249
+ status: "failed",
250
+ error: e instanceof Error ? e : new Error(String(e)),
251
+ steps: {},
252
+ input: inputData
253
+ };
254
+ }
255
+ }
256
+ const res = await this.inngestStep.run(
257
+ `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
258
+ async () => {
259
+ if (result.status === "failed") {
260
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
261
+ type: "watch",
262
+ runId: executionContext.runId,
263
+ data: {
264
+ type: "workflow-step-result",
265
+ payload: {
266
+ id: step.id,
267
+ status: "failed",
268
+ error: result?.error,
269
+ payload: prevOutput
270
+ }
271
+ }
272
+ });
273
+ return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
274
+ } else if (result.status === "suspended") {
275
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
276
+ const stepRes = stepResult;
277
+ return stepRes?.status === "suspended";
278
+ });
279
+ for (const [stepName, stepResult] of suspendedSteps) {
280
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
281
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
282
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
283
+ type: "watch",
284
+ runId: executionContext.runId,
285
+ data: {
286
+ type: "workflow-step-suspended",
287
+ payload: {
288
+ id: step.id,
289
+ status: "suspended"
290
+ }
291
+ }
292
+ });
293
+ return {
294
+ executionContext,
295
+ result: {
296
+ status: "suspended",
297
+ suspendedAt: Date.now(),
298
+ payload: stepResult.payload,
299
+ suspendPayload: {
300
+ ...stepResult?.suspendPayload,
301
+ __workflow_meta: { runId, path: suspendPath }
302
+ }
303
+ }
304
+ };
305
+ }
306
+ return {
307
+ executionContext,
308
+ result: {
309
+ status: "suspended",
310
+ suspendedAt: Date.now(),
311
+ payload: {}
312
+ }
313
+ };
314
+ } else if (result.status === "tripwire") {
315
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
316
+ type: "watch",
317
+ runId: executionContext.runId,
318
+ data: {
319
+ type: "workflow-step-result",
320
+ payload: {
321
+ id: step.id,
322
+ status: "tripwire",
323
+ error: result?.tripwire?.reason,
324
+ payload: prevOutput
325
+ }
326
+ }
327
+ });
328
+ return {
329
+ executionContext,
330
+ result: {
331
+ status: "tripwire",
332
+ tripwire: result?.tripwire,
333
+ endedAt: Date.now()
334
+ }
335
+ };
336
+ } else if (perStep || result.status === "paused") {
337
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
338
+ type: "watch",
339
+ runId: executionContext.runId,
340
+ data: {
341
+ type: "workflow-step-result",
342
+ payload: {
343
+ id: step.id,
344
+ status: "paused"
345
+ }
346
+ }
347
+ });
348
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
349
+ type: "watch",
350
+ runId: executionContext.runId,
351
+ data: {
352
+ type: "workflow-step-finish",
353
+ payload: {
354
+ id: step.id,
355
+ metadata: {}
356
+ }
357
+ }
358
+ });
359
+ return { executionContext, result: { status: "paused" } };
360
+ }
361
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
362
+ type: "watch",
363
+ runId: executionContext.runId,
364
+ data: {
365
+ type: "workflow-step-result",
366
+ payload: {
367
+ id: step.id,
368
+ status: "success",
369
+ output: result?.result
370
+ }
371
+ }
372
+ });
373
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
374
+ type: "watch",
375
+ runId: executionContext.runId,
376
+ data: {
377
+ type: "workflow-step-finish",
378
+ payload: {
379
+ id: step.id,
380
+ metadata: {}
381
+ }
382
+ }
383
+ });
384
+ return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
385
+ }
386
+ );
387
+ Object.assign(executionContext, res.executionContext);
388
+ return {
389
+ ...res.result,
390
+ startedAt,
391
+ payload: inputData,
392
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
393
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
394
+ };
395
+ }
396
+ };
397
+ var InngestPubSub = class extends PubSub {
398
+ inngest;
399
+ workflowId;
400
+ publishFn;
401
+ subscriptions = /* @__PURE__ */ new Map();
402
+ constructor(inngest, workflowId, publishFn) {
403
+ super();
404
+ this.inngest = inngest;
405
+ this.workflowId = workflowId;
406
+ this.publishFn = publishFn;
407
+ }
408
+ /**
409
+ * Publish an event to Inngest's realtime system.
410
+ *
411
+ * Topic format: "workflow.events.v2.{runId}"
412
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
413
+ */
414
+ async publish(topic, event) {
415
+ if (!this.publishFn) {
416
+ return;
417
+ }
418
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
419
+ if (!match) {
420
+ return;
421
+ }
422
+ const runId = match[1];
423
+ try {
424
+ await this.publishFn({
425
+ channel: `workflow:${this.workflowId}:${runId}`,
426
+ topic: "watch",
427
+ data: event.data
428
+ });
429
+ } catch (err) {
430
+ console.error("InngestPubSub publish error:", err?.message ?? err);
431
+ }
432
+ }
433
+ /**
434
+ * Subscribe to events from Inngest's realtime system.
435
+ *
436
+ * Topic format: "workflow.events.v2.{runId}"
437
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
438
+ */
439
+ async subscribe(topic, cb) {
440
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
441
+ if (!match || !match[1]) {
442
+ return;
443
+ }
444
+ const runId = match[1];
445
+ if (this.subscriptions.has(topic)) {
446
+ this.subscriptions.get(topic).callbacks.add(cb);
447
+ return;
448
+ }
449
+ const callbacks = /* @__PURE__ */ new Set([cb]);
450
+ const channel = `workflow:${this.workflowId}:${runId}`;
451
+ const streamPromise = subscribe(
452
+ {
453
+ channel,
454
+ topics: ["watch"],
455
+ app: this.inngest
456
+ },
457
+ (message) => {
458
+ const event = {
459
+ id: crypto.randomUUID(),
460
+ type: "watch",
461
+ runId,
462
+ data: message.data,
463
+ createdAt: /* @__PURE__ */ new Date()
464
+ };
465
+ for (const callback of callbacks) {
466
+ callback(event);
467
+ }
468
+ }
469
+ );
470
+ this.subscriptions.set(topic, {
471
+ unsubscribe: () => {
472
+ streamPromise.then((stream) => stream.cancel()).catch((err) => {
473
+ console.error("InngestPubSub unsubscribe error:", err);
474
+ });
475
+ },
476
+ callbacks
477
+ });
478
+ }
479
+ /**
480
+ * Unsubscribe a callback from a topic.
481
+ * If no callbacks remain, the underlying Inngest subscription is cancelled.
482
+ */
483
+ async unsubscribe(topic, cb) {
484
+ const sub = this.subscriptions.get(topic);
485
+ if (!sub) {
486
+ return;
487
+ }
488
+ sub.callbacks.delete(cb);
489
+ if (sub.callbacks.size === 0) {
490
+ sub.unsubscribe();
491
+ this.subscriptions.delete(topic);
492
+ }
493
+ }
494
+ /**
495
+ * Flush any pending operations. No-op for Inngest.
496
+ */
497
+ async flush() {
498
+ }
499
+ /**
500
+ * Clean up all subscriptions during graceful shutdown.
501
+ */
502
+ async close() {
503
+ for (const [, sub] of this.subscriptions) {
504
+ sub.unsubscribe();
505
+ }
506
+ this.subscriptions.clear();
507
+ }
508
+ };
39
509
  var InngestRun = class extends Run {
40
510
  inngest;
41
511
  serializedStepGraph;
@@ -47,38 +517,90 @@ var InngestRun = class extends Run {
47
517
  this.#mastra = params.mastra;
48
518
  }
49
519
  async getRuns(eventId) {
50
- const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
51
- headers: {
52
- Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
520
+ const maxRetries = 3;
521
+ let lastError = null;
522
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
523
+ try {
524
+ const response = await fetch(
525
+ `${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
526
+ {
527
+ headers: {
528
+ Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
529
+ }
530
+ }
531
+ );
532
+ if (response.status === 429) {
533
+ const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
534
+ await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
535
+ continue;
536
+ }
537
+ if (!response.ok) {
538
+ throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
539
+ }
540
+ const text = await response.text();
541
+ if (!text) {
542
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
543
+ continue;
544
+ }
545
+ const json = JSON.parse(text);
546
+ return json.data;
547
+ } catch (error) {
548
+ lastError = error;
549
+ if (attempt < maxRetries - 1) {
550
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
551
+ }
53
552
  }
54
- });
55
- const json = await response.json();
56
- return json.data;
553
+ }
554
+ throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
57
555
  }
58
- async getRunOutput(eventId) {
59
- let runs = await this.getRuns(eventId);
556
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
557
+ const startTime = Date.now();
60
558
  const storage = this.#mastra?.getStorage();
61
- while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
62
- await new Promise((resolve) => setTimeout(resolve, 1e3));
63
- runs = await this.getRuns(eventId);
559
+ const workflowsStore = await storage?.getStore("workflows");
560
+ while (Date.now() - startTime < maxWaitMs) {
561
+ let runs;
562
+ try {
563
+ runs = await this.getRuns(eventId);
564
+ } catch (error) {
565
+ if (error instanceof NonRetriableError) {
566
+ throw error;
567
+ }
568
+ throw new NonRetriableError(
569
+ `Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
570
+ );
571
+ }
572
+ if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
573
+ return runs[0];
574
+ }
64
575
  if (runs?.[0]?.status === "Failed") {
65
- const snapshot = await storage?.loadWorkflowSnapshot({
576
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
66
577
  workflowName: this.workflowId,
67
578
  runId: this.runId
68
579
  });
580
+ if (snapshot?.context) {
581
+ snapshot.context = hydrateSerializedStepErrors(snapshot.context);
582
+ }
69
583
  return {
70
- output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
584
+ output: {
585
+ result: {
586
+ steps: snapshot?.context,
587
+ status: "failed",
588
+ // Get the original error from NonRetriableError's cause (which contains the workflow result)
589
+ error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
590
+ }
591
+ }
71
592
  };
72
593
  }
73
594
  if (runs?.[0]?.status === "Cancelled") {
74
- const snapshot = await storage?.loadWorkflowSnapshot({
595
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
75
596
  workflowName: this.workflowId,
76
597
  runId: this.runId
77
598
  });
78
599
  return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
79
600
  }
601
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
80
602
  }
81
- return runs?.[0];
603
+ throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
82
604
  }
83
605
  async cancel() {
84
606
  const storage = this.#mastra?.getStorage();
@@ -88,12 +610,13 @@ var InngestRun = class extends Run {
88
610
  runId: this.runId
89
611
  }
90
612
  });
91
- const snapshot = await storage?.loadWorkflowSnapshot({
613
+ const workflowsStore = await storage?.getStore("workflows");
614
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
92
615
  workflowName: this.workflowId,
93
616
  runId: this.runId
94
617
  });
95
618
  if (snapshot) {
96
- await storage?.persistWorkflowSnapshot({
619
+ await workflowsStore?.persistWorkflowSnapshot({
97
620
  workflowName: this.workflowId,
98
621
  runId: this.runId,
99
622
  resourceId: this.resourceId,
@@ -108,14 +631,64 @@ var InngestRun = class extends Run {
108
631
  async start(params) {
109
632
  return this._start(params);
110
633
  }
634
+ /**
635
+ * Starts the workflow execution without waiting for completion (fire-and-forget).
636
+ * Returns immediately with the runId after sending the event to Inngest.
637
+ * The workflow executes independently in Inngest.
638
+ * Use this when you don't need to wait for the result or want to avoid polling failures.
639
+ */
640
+ async startAsync(params) {
641
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
642
+ await workflowsStore?.persistWorkflowSnapshot({
643
+ workflowName: this.workflowId,
644
+ runId: this.runId,
645
+ resourceId: this.resourceId,
646
+ snapshot: {
647
+ runId: this.runId,
648
+ serializedStepGraph: this.serializedStepGraph,
649
+ status: "running",
650
+ value: {},
651
+ context: {},
652
+ activePaths: [],
653
+ suspendedPaths: {},
654
+ activeStepsPath: {},
655
+ resumeLabels: {},
656
+ waitingPaths: {},
657
+ timestamp: Date.now()
658
+ }
659
+ });
660
+ const inputDataToUse = await this._validateInput(params.inputData);
661
+ const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
662
+ const eventOutput = await this.inngest.send({
663
+ name: `workflow.${this.workflowId}`,
664
+ data: {
665
+ inputData: inputDataToUse,
666
+ initialState: initialStateToUse,
667
+ runId: this.runId,
668
+ resourceId: this.resourceId,
669
+ outputOptions: params.outputOptions,
670
+ tracingOptions: params.tracingOptions,
671
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
672
+ perStep: params.perStep
673
+ }
674
+ });
675
+ const eventId = eventOutput.ids[0];
676
+ if (!eventId) {
677
+ throw new Error("Event ID is not set");
678
+ }
679
+ return { runId: this.runId };
680
+ }
111
681
  async _start({
112
682
  inputData,
113
683
  initialState,
114
684
  outputOptions,
115
685
  tracingOptions,
116
- format
686
+ format,
687
+ requestContext,
688
+ perStep
117
689
  }) {
118
- await this.#mastra.getStorage()?.persistWorkflowSnapshot({
690
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
691
+ await workflowsStore?.persistWorkflowSnapshot({
119
692
  workflowName: this.workflowId,
120
693
  runId: this.runId,
121
694
  resourceId: this.resourceId,
@@ -144,7 +717,9 @@ var InngestRun = class extends Run {
144
717
  resourceId: this.resourceId,
145
718
  outputOptions,
146
719
  tracingOptions,
147
- format
720
+ format,
721
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
722
+ perStep
148
723
  }
149
724
  });
150
725
  const eventId = eventOutput.ids[0];
@@ -153,9 +728,7 @@ var InngestRun = class extends Run {
153
728
  }
154
729
  const runOutput = await this.getRunOutput(eventId);
155
730
  const result = runOutput?.output?.result;
156
- if (result.status === "failed") {
157
- result.error = new Error(result.error);
158
- }
731
+ this.hydrateFailedResult(result);
159
732
  if (result.status !== "suspended") {
160
733
  this.cleanup?.();
161
734
  }
@@ -182,12 +755,16 @@ var InngestRun = class extends Run {
182
755
  (step) => typeof step === "string" ? step : step?.id
183
756
  );
184
757
  }
185
- const snapshot = await storage?.loadWorkflowSnapshot({
758
+ const workflowsStore = await storage?.getStore("workflows");
759
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
186
760
  workflowName: this.workflowId,
187
761
  runId: this.runId
188
762
  });
189
763
  const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
190
764
  const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
765
+ const persistedRequestContext = snapshot?.requestContext ?? {};
766
+ const newRequestContext = params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {};
767
+ const mergedRequestContext = { ...persistedRequestContext, ...newRequestContext };
191
768
  const eventOutput = await this.inngest.send({
192
769
  name: `workflow.${this.workflowId}`,
193
770
  data: {
@@ -201,7 +778,9 @@ var InngestRun = class extends Run {
201
778
  stepResults: snapshot?.context,
202
779
  resumePayload: resumeDataToUse,
203
780
  resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
204
- }
781
+ },
782
+ requestContext: mergedRequestContext,
783
+ perStep: params.perStep
205
784
  }
206
785
  });
207
786
  const eventId = eventOutput.ids[0];
@@ -210,9 +789,7 @@ var InngestRun = class extends Run {
210
789
  }
211
790
  const runOutput = await this.getRunOutput(eventId);
212
791
  const result = runOutput?.output?.result;
213
- if (result.status === "failed") {
214
- result.error = new Error(result.error);
215
- }
792
+ this.hydrateFailedResult(result);
216
793
  return result;
217
794
  }
218
795
  async timeTravel(params) {
@@ -242,12 +819,13 @@ var InngestRun = class extends Run {
242
819
  throw new Error("No steps provided to timeTravel");
243
820
  }
244
821
  const storage = this.#mastra?.getStorage();
245
- const snapshot = await storage?.loadWorkflowSnapshot({
822
+ const workflowsStore = await storage?.getStore("workflows");
823
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
246
824
  workflowName: this.workflowId,
247
825
  runId: this.runId
248
826
  });
249
827
  if (!snapshot) {
250
- await storage?.persistWorkflowSnapshot({
828
+ await workflowsStore?.persistWorkflowSnapshot({
251
829
  workflowName: this.workflowId,
252
830
  runId: this.runId,
253
831
  resourceId: this.resourceId,
@@ -281,7 +859,8 @@ var InngestRun = class extends Run {
281
859
  nestedStepsContext: params.nestedStepsContext,
282
860
  snapshot: snapshot ?? { context: {} },
283
861
  graph: this.executionGraph,
284
- initialState: params.initialState
862
+ initialState: params.initialState,
863
+ perStep: params.perStep
285
864
  });
286
865
  const eventOutput = await this.inngest.send({
287
866
  name: `workflow.${this.workflowId}`,
@@ -292,7 +871,9 @@ var InngestRun = class extends Run {
292
871
  stepResults: timeTravelData.stepResults,
293
872
  timeTravel: timeTravelData,
294
873
  tracingOptions: params.tracingOptions,
295
- outputOptions: params.outputOptions
874
+ outputOptions: params.outputOptions,
875
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
876
+ perStep: params.perStep
296
877
  }
297
878
  });
298
879
  const eventId = eventOutput.ids[0];
@@ -301,9 +882,7 @@ var InngestRun = class extends Run {
301
882
  }
302
883
  const runOutput = await this.getRunOutput(eventId);
303
884
  const result = runOutput?.output?.result;
304
- if (result.status === "failed") {
305
- result.error = new Error(result.error);
306
- }
885
+ this.hydrateFailedResult(result);
307
886
  return result;
308
887
  }
309
888
  watch(cb) {
@@ -385,7 +964,8 @@ var InngestRun = class extends Run {
385
964
  tracingOptions,
386
965
  closeOnSuspend = true,
387
966
  initialState,
388
- outputOptions
967
+ outputOptions,
968
+ perStep
389
969
  } = {}) {
390
970
  if (this.closeStreamAction && this.streamOutput) {
391
971
  return this.streamOutput;
@@ -421,7 +1001,8 @@ var InngestRun = class extends Run {
421
1001
  initialState,
422
1002
  tracingOptions,
423
1003
  outputOptions,
424
- format: "vnext"
1004
+ format: "vnext",
1005
+ perStep
425
1006
  });
426
1007
  let executionResults;
427
1008
  try {
@@ -452,9 +1033,6 @@ var InngestRun = class extends Run {
452
1033
  });
453
1034
  return this.streamOutput;
454
1035
  }
455
- streamVNext(args = {}) {
456
- return this.stream(args);
457
- }
458
1036
  timeTravelStream({
459
1037
  inputData,
460
1038
  resumeData,
@@ -464,7 +1042,8 @@ var InngestRun = class extends Run {
464
1042
  nestedStepsContext,
465
1043
  requestContext,
466
1044
  tracingOptions,
467
- outputOptions
1045
+ outputOptions,
1046
+ perStep
468
1047
  }) {
469
1048
  this.closeStreamAction = async () => {
470
1049
  };
@@ -485,7 +1064,7 @@ var InngestRun = class extends Run {
485
1064
  self.closeStreamAction = async () => {
486
1065
  unwatch();
487
1066
  try {
488
- await controller.close();
1067
+ controller.close();
489
1068
  } catch (err) {
490
1069
  console.error("Error closing stream:", err);
491
1070
  }
@@ -499,7 +1078,8 @@ var InngestRun = class extends Run {
499
1078
  initialState,
500
1079
  requestContext,
501
1080
  tracingOptions,
502
- outputOptions
1081
+ outputOptions,
1082
+ perStep
503
1083
  });
504
1084
  self.executionResults = executionResultsPromise;
505
1085
  let executionResults;
@@ -524,14 +1104,30 @@ var InngestRun = class extends Run {
524
1104
  });
525
1105
  return this.streamOutput;
526
1106
  }
1107
+ /**
1108
+ * Hydrates errors in a failed workflow result back to proper Error instances.
1109
+ * This ensures error.cause chains and custom properties are preserved.
1110
+ */
1111
+ hydrateFailedResult(result) {
1112
+ if (result.status === "failed") {
1113
+ result.error = getErrorFromUnknown(result.error, { serializeStack: false });
1114
+ if (result.steps) {
1115
+ hydrateSerializedStepErrors(result.steps);
1116
+ }
1117
+ }
1118
+ }
527
1119
  };
1120
+
1121
+ // src/workflow.ts
528
1122
  var InngestWorkflow = class _InngestWorkflow extends Workflow {
529
1123
  #mastra;
530
1124
  inngest;
531
1125
  function;
1126
+ cronFunction;
532
1127
  flowControlConfig;
1128
+ cronConfig;
533
1129
  constructor(params, inngest) {
534
- const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
1130
+ const { concurrency, rateLimit, throttle, debounce, priority, cron, inputData, initialState, ...workflowParams } = params;
535
1131
  super(workflowParams);
536
1132
  this.engineType = "inngest";
537
1133
  const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
@@ -540,6 +1136,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
540
1136
  this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
541
1137
  this.#mastra = params.mastra;
542
1138
  this.inngest = inngest;
1139
+ if (cron) {
1140
+ this.cronConfig = { cron, inputData, initialState };
1141
+ }
543
1142
  }
544
1143
  async listWorkflowRuns(args) {
545
1144
  const storage = this.#mastra?.getStorage();
@@ -547,18 +1146,14 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
547
1146
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
548
1147
  return { runs: [], total: 0 };
549
1148
  }
550
- return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
551
- }
552
- async getWorkflowRunById(runId) {
553
- const storage = this.#mastra?.getStorage();
554
- if (!storage) {
555
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
556
- return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
1149
+ const workflowsStore = await storage.getStore("workflows");
1150
+ if (!workflowsStore) {
1151
+ return { runs: [], total: 0 };
557
1152
  }
558
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
559
- return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
1153
+ return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
560
1154
  }
561
1155
  __registerMastra(mastra) {
1156
+ super.__registerMastra(mastra);
562
1157
  this.#mastra = mastra;
563
1158
  this.executionEngine.__registerMastra(mastra);
564
1159
  const updateNested = (step) => {
@@ -600,9 +1195,13 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
600
1195
  workflowStatus: run.workflowRunStatus,
601
1196
  stepResults: {}
602
1197
  });
603
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
604
- if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
605
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1198
+ const existingRun = await this.getWorkflowRunById(runIdToUse, {
1199
+ withNestedWorkflows: false
1200
+ });
1201
+ const existsInStorage = existingRun && !existingRun.isFromInMemory;
1202
+ if (!existsInStorage && shouldPersistSnapshot) {
1203
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
1204
+ await workflowsStore?.persistWorkflowSnapshot({
606
1205
  workflowName: this.id,
607
1206
  runId: runIdToUse,
608
1207
  resourceId: options?.resourceId,
@@ -625,6 +1224,30 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
625
1224
  }
626
1225
  return run;
627
1226
  }
1227
+ //createCronFunction is only called if cronConfig.cron is defined.
1228
+ createCronFunction() {
1229
+ if (this.cronFunction) {
1230
+ return this.cronFunction;
1231
+ }
1232
+ this.cronFunction = this.inngest.createFunction(
1233
+ {
1234
+ id: `workflow.${this.id}.cron`,
1235
+ retries: 0,
1236
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1237
+ ...this.flowControlConfig
1238
+ },
1239
+ { cron: this.cronConfig?.cron ?? "" },
1240
+ async () => {
1241
+ const run = await this.createRun();
1242
+ const result = await run.start({
1243
+ inputData: this.cronConfig?.inputData,
1244
+ initialState: this.cronConfig?.initialState
1245
+ });
1246
+ return { result, runId: run.runId };
1247
+ }
1248
+ );
1249
+ return this.cronFunction;
1250
+ }
628
1251
  getFunction() {
629
1252
  if (this.function) {
630
1253
  return this.function;
@@ -632,41 +1255,21 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
632
1255
  this.function = this.inngest.createFunction(
633
1256
  {
634
1257
  id: `workflow.${this.id}`,
635
- retries: Math.min(this.retryConfig?.attempts ?? 0, 20),
1258
+ retries: 0,
636
1259
  cancelOn: [{ event: `cancel.workflow.${this.id}` }],
637
1260
  // Spread flow control configuration
638
1261
  ...this.flowControlConfig
639
1262
  },
640
1263
  { event: `workflow.${this.id}` },
641
1264
  async ({ event, step, attempt, publish }) => {
642
- let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
1265
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel, perStep } = event.data;
643
1266
  if (!runId) {
644
1267
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
645
1268
  return randomUUID();
646
1269
  });
647
1270
  }
648
- const emitter = {
649
- emit: async (event2, data) => {
650
- if (!publish) {
651
- return;
652
- }
653
- try {
654
- await publish({
655
- channel: `workflow:${this.id}:${runId}`,
656
- topic: event2,
657
- data
658
- });
659
- } catch (err) {
660
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
661
- }
662
- },
663
- on: (_event, _callback) => {
664
- },
665
- off: (_event, _callback) => {
666
- },
667
- once: (_event, _callback) => {
668
- }
669
- };
1271
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
1272
+ const requestContext = new RequestContext(Object.entries(event.data.requestContext ?? {}));
670
1273
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
671
1274
  const result = await engine.execute({
672
1275
  workflowId: this.id,
@@ -676,24 +1279,44 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
676
1279
  serializedStepGraph: this.serializedStepGraph,
677
1280
  input: inputData,
678
1281
  initialState,
679
- emitter,
1282
+ pubsub,
680
1283
  retryConfig: this.retryConfig,
681
- requestContext: new RequestContext(),
682
- // TODO
1284
+ requestContext,
683
1285
  resume,
684
1286
  timeTravel,
1287
+ perStep,
685
1288
  format,
686
1289
  abortController: new AbortController(),
687
1290
  // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
688
1291
  outputOptions,
689
- writableStream: new WritableStream({
690
- write(chunk) {
691
- void emitter.emit("watch", chunk).catch(() => {
1292
+ outputWriter: async (chunk) => {
1293
+ try {
1294
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1295
+ type: "watch",
1296
+ runId,
1297
+ data: chunk
692
1298
  });
1299
+ } catch (err) {
1300
+ this.logger.debug?.("Failed to publish watch event:", err);
693
1301
  }
694
- })
1302
+ }
695
1303
  });
696
1304
  await step.run(`workflow.${this.id}.finalize`, async () => {
1305
+ if (result.status !== "paused") {
1306
+ await engine.invokeLifecycleCallbacksInternal({
1307
+ status: result.status,
1308
+ result: "result" in result ? result.result : void 0,
1309
+ error: "error" in result ? result.error : void 0,
1310
+ steps: result.steps,
1311
+ tripwire: "tripwire" in result ? result.tripwire : void 0,
1312
+ runId,
1313
+ workflowId: this.id,
1314
+ resourceId,
1315
+ input: inputData,
1316
+ requestContext,
1317
+ state: result.state ?? initialState ?? {}
1318
+ });
1319
+ }
697
1320
  if (result.status === "failed") {
698
1321
  throw new NonRetriableError(`Workflow failed`, {
699
1322
  cause: result
@@ -720,17 +1343,50 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
720
1343
  });
721
1344
  }
722
1345
  getFunctions() {
723
- return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
1346
+ return [
1347
+ this.getFunction(),
1348
+ ...this.cronConfig?.cron ? [this.createCronFunction()] : [],
1349
+ ...this.getNestedFunctions(this.executionGraph.steps)
1350
+ ];
724
1351
  }
725
1352
  };
726
- function isAgent(params) {
727
- return params?.component === "AGENT";
728
- }
729
- function isTool(params) {
730
- return params instanceof Tool;
1353
+ function serve({
1354
+ mastra,
1355
+ inngest,
1356
+ functions: userFunctions = [],
1357
+ registerOptions
1358
+ }) {
1359
+ const wfs = mastra.listWorkflows();
1360
+ const workflowFunctions = Array.from(
1361
+ new Set(
1362
+ Object.values(wfs).flatMap((wf) => {
1363
+ if (wf instanceof InngestWorkflow) {
1364
+ wf.__registerMastra(mastra);
1365
+ return wf.getFunctions();
1366
+ }
1367
+ return [];
1368
+ })
1369
+ )
1370
+ );
1371
+ return serve$1({
1372
+ ...registerOptions,
1373
+ client: inngest,
1374
+ functions: [...workflowFunctions, ...userFunctions]
1375
+ });
731
1376
  }
732
- function createStep(params, agentOptions) {
733
- if (isAgent(params)) {
1377
+
1378
+ // src/types.ts
1379
+ var _compatibilityCheck = true;
1380
+
1381
+ // src/index.ts
1382
+ function createStep(params, agentOrToolOptions) {
1383
+ if (params instanceof InngestWorkflow) {
1384
+ return params;
1385
+ }
1386
+ if (params instanceof Agent) {
1387
+ const options = agentOrToolOptions;
1388
+ const outputSchema = options?.structuredOutput?.schema ?? z.object({ text: z.string() });
1389
+ const { retries, scorers, ...agentOptions } = options ?? {};
734
1390
  return {
735
1391
  id: params.name,
736
1392
  description: params.getDescription(),
@@ -739,12 +1395,13 @@ function createStep(params, agentOptions) {
739
1395
  // resourceId: z.string().optional(),
740
1396
  // threadId: z.string().optional(),
741
1397
  }),
742
- outputSchema: z.object({
743
- text: z.string()
744
- }),
1398
+ outputSchema,
1399
+ retries,
1400
+ scorers,
745
1401
  execute: async ({
746
1402
  inputData,
747
- [EMITTER_SYMBOL]: emitter,
1403
+ runId,
1404
+ [PUBSUB_SYMBOL]: pubsub,
748
1405
  [STREAM_FORMAT_SYMBOL]: streamFormat,
749
1406
  requestContext,
750
1407
  tracingContext,
@@ -757,6 +1414,7 @@ function createStep(params, agentOptions) {
757
1414
  streamPromise.resolve = resolve;
758
1415
  streamPromise.reject = reject;
759
1416
  });
1417
+ let structuredResult = null;
760
1418
  const toolData = {
761
1419
  name: params.name,
762
1420
  args: inputData
@@ -770,6 +1428,10 @@ function createStep(params, agentOptions) {
770
1428
  requestContext,
771
1429
  tracingContext,
772
1430
  onFinish: (result) => {
1431
+ const resultWithObject = result;
1432
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1433
+ structuredResult = resultWithObject.object;
1434
+ }
773
1435
  streamPromise.resolve(result.text);
774
1436
  void agentOptions?.onFinish?.(result);
775
1437
  },
@@ -782,6 +1444,10 @@ function createStep(params, agentOptions) {
782
1444
  requestContext,
783
1445
  tracingContext,
784
1446
  onFinish: (result) => {
1447
+ const resultWithObject = result;
1448
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1449
+ structuredResult = resultWithObject.object;
1450
+ }
785
1451
  streamPromise.resolve(result.text);
786
1452
  void agentOptions?.onFinish?.(result);
787
1453
  },
@@ -790,22 +1456,24 @@ function createStep(params, agentOptions) {
790
1456
  stream = modelOutput.fullStream;
791
1457
  }
792
1458
  if (streamFormat === "legacy") {
793
- await emitter.emit("watch", {
794
- type: "tool-call-streaming-start",
795
- ...toolData ?? {}
1459
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1460
+ type: "watch",
1461
+ runId,
1462
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
796
1463
  });
797
1464
  for await (const chunk of stream) {
798
1465
  if (chunk.type === "text-delta") {
799
- await emitter.emit("watch", {
800
- type: "tool-call-delta",
801
- ...toolData ?? {},
802
- argsTextDelta: chunk.textDelta
1466
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1467
+ type: "watch",
1468
+ runId,
1469
+ data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
803
1470
  });
804
1471
  }
805
1472
  }
806
- await emitter.emit("watch", {
807
- type: "tool-call-streaming-finish",
808
- ...toolData ?? {}
1473
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1474
+ type: "watch",
1475
+ runId,
1476
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
809
1477
  });
810
1478
  } else {
811
1479
  for await (const chunk of stream) {
@@ -815,6 +1483,9 @@ function createStep(params, agentOptions) {
815
1483
  if (abortSignal.aborted) {
816
1484
  return abort();
817
1485
  }
1486
+ if (structuredResult !== null) {
1487
+ return structuredResult;
1488
+ }
818
1489
  return {
819
1490
  text: await streamPromise.promise
820
1491
  };
@@ -822,7 +1493,8 @@ function createStep(params, agentOptions) {
822
1493
  component: params.component
823
1494
  };
824
1495
  }
825
- if (isTool(params)) {
1496
+ if (params instanceof Tool) {
1497
+ const toolOpts = agentOrToolOptions;
826
1498
  if (!params.inputSchema || !params.outputSchema) {
827
1499
  throw new Error("Tool must have input and output schemas defined");
828
1500
  }
@@ -834,6 +1506,8 @@ function createStep(params, agentOptions) {
834
1506
  outputSchema: params.outputSchema,
835
1507
  suspendSchema: params.suspendSchema,
836
1508
  resumeSchema: params.resumeSchema,
1509
+ retries: toolOpts?.retries,
1510
+ scorers: toolOpts?.scorers,
837
1511
  execute: async ({
838
1512
  inputData,
839
1513
  mastra,
@@ -871,6 +1545,8 @@ function createStep(params, agentOptions) {
871
1545
  outputSchema: params.outputSchema,
872
1546
  resumeSchema: params.resumeSchema,
873
1547
  suspendSchema: params.suspendSchema,
1548
+ retries: params.retries,
1549
+ scorers: params.scorers,
874
1550
  execute: params.execute
875
1551
  };
876
1552
  }
@@ -913,867 +1589,7 @@ function init(inngest) {
913
1589
  }
914
1590
  };
915
1591
  }
916
- var InngestExecutionEngine = class extends DefaultExecutionEngine {
917
- inngestStep;
918
- inngestAttempts;
919
- constructor(mastra, inngestStep, inngestAttempts = 0, options) {
920
- super({ mastra, options });
921
- this.inngestStep = inngestStep;
922
- this.inngestAttempts = inngestAttempts;
923
- }
924
- async fmtReturnValue(emitter, stepResults, lastOutput, error) {
925
- const base = {
926
- status: lastOutput.status,
927
- steps: stepResults
928
- };
929
- if (lastOutput.status === "success") {
930
- base.result = lastOutput.output;
931
- } else if (lastOutput.status === "failed") {
932
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
933
- } else if (lastOutput.status === "suspended") {
934
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
935
- if (stepResult?.status === "suspended") {
936
- const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
937
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
938
- }
939
- return [];
940
- });
941
- base.suspended = suspendedStepIds;
942
- }
943
- return base;
944
- }
945
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
946
- // await this.inngestStep.sleep(id, duration);
947
- // }
948
- async executeSleep({
949
- workflowId,
950
- runId,
951
- entry,
952
- prevOutput,
953
- stepResults,
954
- emitter,
955
- abortController,
956
- requestContext,
957
- executionContext,
958
- writableStream,
959
- tracingContext
960
- }) {
961
- let { duration, fn } = entry;
962
- const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
963
- type: SpanType.WORKFLOW_SLEEP,
964
- name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
965
- attributes: {
966
- durationMs: duration,
967
- sleepType: fn ? "dynamic" : "fixed"
968
- },
969
- tracingPolicy: this.options?.tracingPolicy
970
- });
971
- if (fn) {
972
- const stepCallId = randomUUID();
973
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
974
- return await fn(
975
- createDeprecationProxy(
976
- {
977
- runId,
978
- workflowId,
979
- mastra: this.mastra,
980
- requestContext,
981
- inputData: prevOutput,
982
- state: executionContext.state,
983
- setState: (state) => {
984
- executionContext.state = state;
985
- },
986
- retryCount: -1,
987
- tracingContext: {
988
- currentSpan: sleepSpan
989
- },
990
- getInitData: () => stepResults?.input,
991
- getStepResult: getStepResult.bind(this, stepResults),
992
- // TODO: this function shouldn't have suspend probably?
993
- suspend: async (_suspendPayload) => {
994
- },
995
- bail: () => {
996
- },
997
- abort: () => {
998
- abortController?.abort();
999
- },
1000
- [EMITTER_SYMBOL]: emitter,
1001
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1002
- engine: { step: this.inngestStep },
1003
- abortSignal: abortController?.signal,
1004
- writer: new ToolStream(
1005
- {
1006
- prefix: "workflow-step",
1007
- callId: stepCallId,
1008
- name: "sleep",
1009
- runId
1010
- },
1011
- writableStream
1012
- )
1013
- },
1014
- {
1015
- paramName: "runCount",
1016
- deprecationMessage: runCountDeprecationMessage,
1017
- logger: this.logger
1018
- }
1019
- )
1020
- );
1021
- });
1022
- sleepSpan?.update({
1023
- attributes: {
1024
- durationMs: duration
1025
- }
1026
- });
1027
- }
1028
- try {
1029
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
1030
- sleepSpan?.end();
1031
- } catch (e) {
1032
- sleepSpan?.error({ error: e });
1033
- throw e;
1034
- }
1035
- }
1036
- async executeSleepUntil({
1037
- workflowId,
1038
- runId,
1039
- entry,
1040
- prevOutput,
1041
- stepResults,
1042
- emitter,
1043
- abortController,
1044
- requestContext,
1045
- executionContext,
1046
- writableStream,
1047
- tracingContext
1048
- }) {
1049
- let { date, fn } = entry;
1050
- const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
1051
- type: SpanType.WORKFLOW_SLEEP,
1052
- name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
1053
- attributes: {
1054
- untilDate: date,
1055
- durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
1056
- sleepType: fn ? "dynamic" : "fixed"
1057
- },
1058
- tracingPolicy: this.options?.tracingPolicy
1059
- });
1060
- if (fn) {
1061
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
1062
- const stepCallId = randomUUID();
1063
- return await fn(
1064
- createDeprecationProxy(
1065
- {
1066
- runId,
1067
- workflowId,
1068
- mastra: this.mastra,
1069
- requestContext,
1070
- inputData: prevOutput,
1071
- state: executionContext.state,
1072
- setState: (state) => {
1073
- executionContext.state = state;
1074
- },
1075
- retryCount: -1,
1076
- tracingContext: {
1077
- currentSpan: sleepUntilSpan
1078
- },
1079
- getInitData: () => stepResults?.input,
1080
- getStepResult: getStepResult.bind(this, stepResults),
1081
- // TODO: this function shouldn't have suspend probably?
1082
- suspend: async (_suspendPayload) => {
1083
- },
1084
- bail: () => {
1085
- },
1086
- abort: () => {
1087
- abortController?.abort();
1088
- },
1089
- [EMITTER_SYMBOL]: emitter,
1090
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1091
- engine: { step: this.inngestStep },
1092
- abortSignal: abortController?.signal,
1093
- writer: new ToolStream(
1094
- {
1095
- prefix: "workflow-step",
1096
- callId: stepCallId,
1097
- name: "sleep",
1098
- runId
1099
- },
1100
- writableStream
1101
- )
1102
- },
1103
- {
1104
- paramName: "runCount",
1105
- deprecationMessage: runCountDeprecationMessage,
1106
- logger: this.logger
1107
- }
1108
- )
1109
- );
1110
- });
1111
- if (date && !(date instanceof Date)) {
1112
- date = new Date(date);
1113
- }
1114
- const time = !date ? 0 : date.getTime() - Date.now();
1115
- sleepUntilSpan?.update({
1116
- attributes: {
1117
- durationMs: Math.max(0, time)
1118
- }
1119
- });
1120
- }
1121
- if (!(date instanceof Date)) {
1122
- sleepUntilSpan?.end();
1123
- return;
1124
- }
1125
- try {
1126
- await this.inngestStep.sleepUntil(entry.id, date);
1127
- sleepUntilSpan?.end();
1128
- } catch (e) {
1129
- sleepUntilSpan?.error({ error: e });
1130
- throw e;
1131
- }
1132
- }
1133
- async executeStep({
1134
- step,
1135
- stepResults,
1136
- executionContext,
1137
- resume,
1138
- timeTravel,
1139
- prevOutput,
1140
- emitter,
1141
- abortController,
1142
- requestContext,
1143
- tracingContext,
1144
- writableStream,
1145
- disableScorers
1146
- }) {
1147
- const stepSpan = tracingContext?.currentSpan?.createChildSpan({
1148
- name: `workflow step: '${step.id}'`,
1149
- type: SpanType.WORKFLOW_STEP,
1150
- input: prevOutput,
1151
- attributes: {
1152
- stepId: step.id
1153
- },
1154
- tracingPolicy: this.options?.tracingPolicy
1155
- });
1156
- const { inputData, validationError } = await validateStepInput({
1157
- prevOutput,
1158
- step,
1159
- validateInputs: this.options?.validateInputs ?? true
1160
- });
1161
- const startedAt = await this.inngestStep.run(
1162
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
1163
- async () => {
1164
- const startedAt2 = Date.now();
1165
- await emitter.emit("watch", {
1166
- type: "workflow-step-start",
1167
- payload: {
1168
- id: step.id,
1169
- status: "running",
1170
- payload: inputData,
1171
- startedAt: startedAt2
1172
- }
1173
- });
1174
- return startedAt2;
1175
- }
1176
- );
1177
- if (step instanceof InngestWorkflow) {
1178
- const isResume = !!resume?.steps?.length;
1179
- let result;
1180
- let runId;
1181
- const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
1182
- try {
1183
- if (isResume) {
1184
- runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
1185
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1186
- workflowName: step.id,
1187
- runId
1188
- });
1189
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1190
- function: step.getFunction(),
1191
- data: {
1192
- inputData,
1193
- initialState: executionContext.state ?? snapshot?.value ?? {},
1194
- runId,
1195
- resume: {
1196
- runId,
1197
- steps: resume.steps.slice(1),
1198
- stepResults: snapshot?.context,
1199
- resumePayload: resume.resumePayload,
1200
- resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
1201
- },
1202
- outputOptions: { includeState: true }
1203
- }
1204
- });
1205
- result = invokeResp.result;
1206
- runId = invokeResp.runId;
1207
- executionContext.state = invokeResp.result.state;
1208
- } else if (isTimeTravel) {
1209
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1210
- workflowName: step.id,
1211
- runId: executionContext.runId
1212
- }) ?? { context: {} };
1213
- const timeTravelParams = createTimeTravelExecutionParams({
1214
- steps: timeTravel.steps.slice(1),
1215
- inputData: timeTravel.inputData,
1216
- resumeData: timeTravel.resumeData,
1217
- context: timeTravel.nestedStepResults?.[step.id] ?? {},
1218
- nestedStepsContext: timeTravel.nestedStepResults ?? {},
1219
- snapshot,
1220
- graph: step.buildExecutionGraph()
1221
- });
1222
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1223
- function: step.getFunction(),
1224
- data: {
1225
- timeTravel: timeTravelParams,
1226
- initialState: executionContext.state ?? {},
1227
- runId: executionContext.runId,
1228
- outputOptions: { includeState: true }
1229
- }
1230
- });
1231
- result = invokeResp.result;
1232
- runId = invokeResp.runId;
1233
- executionContext.state = invokeResp.result.state;
1234
- } else {
1235
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1236
- function: step.getFunction(),
1237
- data: {
1238
- inputData,
1239
- initialState: executionContext.state ?? {},
1240
- outputOptions: { includeState: true }
1241
- }
1242
- });
1243
- result = invokeResp.result;
1244
- runId = invokeResp.runId;
1245
- executionContext.state = invokeResp.result.state;
1246
- }
1247
- } catch (e) {
1248
- const errorCause = e?.cause;
1249
- if (errorCause && typeof errorCause === "object") {
1250
- result = errorCause;
1251
- runId = errorCause.runId || randomUUID();
1252
- } else {
1253
- runId = randomUUID();
1254
- result = {
1255
- status: "failed",
1256
- error: e instanceof Error ? e : new Error(String(e)),
1257
- steps: {},
1258
- input: inputData
1259
- };
1260
- }
1261
- }
1262
- const res = await this.inngestStep.run(
1263
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
1264
- async () => {
1265
- if (result.status === "failed") {
1266
- await emitter.emit("watch", {
1267
- type: "workflow-step-result",
1268
- payload: {
1269
- id: step.id,
1270
- status: "failed",
1271
- error: result?.error,
1272
- payload: prevOutput
1273
- }
1274
- });
1275
- return { executionContext, result: { status: "failed", error: result?.error } };
1276
- } else if (result.status === "suspended") {
1277
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
1278
- const stepRes2 = stepResult;
1279
- return stepRes2?.status === "suspended";
1280
- });
1281
- for (const [stepName, stepResult] of suspendedSteps) {
1282
- const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
1283
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1284
- await emitter.emit("watch", {
1285
- type: "workflow-step-suspended",
1286
- payload: {
1287
- id: step.id,
1288
- status: "suspended"
1289
- }
1290
- });
1291
- return {
1292
- executionContext,
1293
- result: {
1294
- status: "suspended",
1295
- payload: stepResult.payload,
1296
- suspendPayload: {
1297
- ...stepResult?.suspendPayload,
1298
- __workflow_meta: { runId, path: suspendPath }
1299
- }
1300
- }
1301
- };
1302
- }
1303
- return {
1304
- executionContext,
1305
- result: {
1306
- status: "suspended",
1307
- payload: {}
1308
- }
1309
- };
1310
- }
1311
- await emitter.emit("watch", {
1312
- type: "workflow-step-result",
1313
- payload: {
1314
- id: step.id,
1315
- status: "success",
1316
- output: result?.result
1317
- }
1318
- });
1319
- await emitter.emit("watch", {
1320
- type: "workflow-step-finish",
1321
- payload: {
1322
- id: step.id,
1323
- metadata: {}
1324
- }
1325
- });
1326
- return { executionContext, result: { status: "success", output: result?.result } };
1327
- }
1328
- );
1329
- Object.assign(executionContext, res.executionContext);
1330
- return {
1331
- ...res.result,
1332
- startedAt,
1333
- endedAt: Date.now(),
1334
- payload: inputData,
1335
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1336
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1337
- };
1338
- }
1339
- const stepCallId = randomUUID();
1340
- let stepRes;
1341
- try {
1342
- stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1343
- let execResults;
1344
- let suspended;
1345
- let bailed;
1346
- const { resumeData: timeTravelResumeData, validationError: timeTravelResumeValidationError } = await validateStepResumeData({
1347
- resumeData: timeTravel?.stepResults[step.id]?.status === "suspended" ? timeTravel?.resumeData : void 0,
1348
- step
1349
- });
1350
- let resumeDataToUse;
1351
- if (timeTravelResumeData && !timeTravelResumeValidationError) {
1352
- resumeDataToUse = timeTravelResumeData;
1353
- } else if (timeTravelResumeData && timeTravelResumeValidationError) {
1354
- this.logger.warn("Time travel resume data validation failed", {
1355
- stepId: step.id,
1356
- error: timeTravelResumeValidationError.message
1357
- });
1358
- } else if (resume?.steps[0] === step.id) {
1359
- resumeDataToUse = resume?.resumePayload;
1360
- }
1361
- try {
1362
- if (validationError) {
1363
- throw validationError;
1364
- }
1365
- const retryCount = this.getOrGenerateRetryCount(step.id);
1366
- const result = await step.execute({
1367
- runId: executionContext.runId,
1368
- workflowId: executionContext.workflowId,
1369
- mastra: this.mastra,
1370
- requestContext,
1371
- retryCount,
1372
- writer: new ToolStream(
1373
- {
1374
- prefix: "workflow-step",
1375
- callId: stepCallId,
1376
- name: step.id,
1377
- runId: executionContext.runId
1378
- },
1379
- writableStream
1380
- ),
1381
- state: executionContext?.state ?? {},
1382
- setState: (state) => {
1383
- executionContext.state = state;
1384
- },
1385
- inputData,
1386
- resumeData: resumeDataToUse,
1387
- tracingContext: {
1388
- currentSpan: stepSpan
1389
- },
1390
- getInitData: () => stepResults?.input,
1391
- getStepResult: getStepResult.bind(this, stepResults),
1392
- suspend: async (suspendPayload, suspendOptions) => {
1393
- const { suspendData, validationError: validationError2 } = await validateStepSuspendData({
1394
- suspendData: suspendPayload,
1395
- step
1396
- });
1397
- if (validationError2) {
1398
- throw validationError2;
1399
- }
1400
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1401
- if (suspendOptions?.resumeLabel) {
1402
- const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1403
- for (const label of resumeLabel) {
1404
- executionContext.resumeLabels[label] = {
1405
- stepId: step.id,
1406
- foreachIndex: executionContext.foreachIndex
1407
- };
1408
- }
1409
- }
1410
- suspended = { payload: suspendData };
1411
- },
1412
- bail: (result2) => {
1413
- bailed = { payload: result2 };
1414
- },
1415
- abort: () => {
1416
- abortController?.abort();
1417
- },
1418
- [EMITTER_SYMBOL]: emitter,
1419
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1420
- engine: {
1421
- step: this.inngestStep
1422
- },
1423
- abortSignal: abortController.signal
1424
- });
1425
- const endedAt = Date.now();
1426
- execResults = {
1427
- status: "success",
1428
- output: result,
1429
- startedAt,
1430
- endedAt,
1431
- payload: inputData,
1432
- resumedAt: resumeDataToUse ? startedAt : void 0,
1433
- resumePayload: resumeDataToUse
1434
- };
1435
- } catch (e) {
1436
- const stepFailure = {
1437
- status: "failed",
1438
- payload: inputData,
1439
- error: e instanceof Error ? e.message : String(e),
1440
- endedAt: Date.now(),
1441
- startedAt,
1442
- resumedAt: resumeDataToUse ? startedAt : void 0,
1443
- resumePayload: resumeDataToUse
1444
- };
1445
- execResults = stepFailure;
1446
- const fallbackErrorMessage = `Step ${step.id} failed`;
1447
- stepSpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1448
- throw new RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1449
- cause: execResults
1450
- });
1451
- }
1452
- if (suspended) {
1453
- execResults = {
1454
- status: "suspended",
1455
- suspendPayload: suspended.payload,
1456
- ...execResults.output ? { suspendOutput: execResults.output } : {},
1457
- payload: inputData,
1458
- suspendedAt: Date.now(),
1459
- startedAt,
1460
- resumedAt: resumeDataToUse ? startedAt : void 0,
1461
- resumePayload: resumeDataToUse
1462
- };
1463
- } else if (bailed) {
1464
- execResults = {
1465
- status: "bailed",
1466
- output: bailed.payload,
1467
- payload: inputData,
1468
- endedAt: Date.now(),
1469
- startedAt
1470
- };
1471
- }
1472
- if (execResults.status === "suspended") {
1473
- await emitter.emit("watch", {
1474
- type: "workflow-step-suspended",
1475
- payload: {
1476
- id: step.id,
1477
- ...execResults
1478
- }
1479
- });
1480
- } else {
1481
- await emitter.emit("watch", {
1482
- type: "workflow-step-result",
1483
- payload: {
1484
- id: step.id,
1485
- ...execResults
1486
- }
1487
- });
1488
- await emitter.emit("watch", {
1489
- type: "workflow-step-finish",
1490
- payload: {
1491
- id: step.id,
1492
- metadata: {}
1493
- }
1494
- });
1495
- }
1496
- stepSpan?.end({ output: execResults });
1497
- return { result: execResults, executionContext, stepResults };
1498
- });
1499
- } catch (e) {
1500
- const stepFailure = e instanceof Error ? e?.cause : {
1501
- status: "failed",
1502
- error: e instanceof Error ? e.message : String(e),
1503
- payload: inputData,
1504
- startedAt,
1505
- endedAt: Date.now()
1506
- };
1507
- await emitter.emit("watch", {
1508
- type: "workflow-step-result",
1509
- payload: {
1510
- id: step.id,
1511
- ...stepFailure
1512
- }
1513
- });
1514
- await emitter.emit("watch", {
1515
- type: "workflow-step-finish",
1516
- payload: {
1517
- id: step.id,
1518
- metadata: {}
1519
- }
1520
- });
1521
- stepRes = {
1522
- result: stepFailure,
1523
- executionContext,
1524
- stepResults: {
1525
- ...stepResults,
1526
- [step.id]: stepFailure
1527
- }
1528
- };
1529
- }
1530
- if (disableScorers !== false && stepRes.result.status === "success") {
1531
- await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1532
- if (step.scorers) {
1533
- await this.runScorers({
1534
- scorers: step.scorers,
1535
- runId: executionContext.runId,
1536
- input: inputData,
1537
- output: stepRes.result,
1538
- workflowId: executionContext.workflowId,
1539
- stepId: step.id,
1540
- requestContext,
1541
- disableScorers,
1542
- tracingContext: { currentSpan: stepSpan }
1543
- });
1544
- }
1545
- });
1546
- }
1547
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1548
- executionContext.state = stepRes.executionContext.state;
1549
- return stepRes.result;
1550
- }
1551
- async persistStepUpdate({
1552
- workflowId,
1553
- runId,
1554
- stepResults,
1555
- resourceId,
1556
- executionContext,
1557
- serializedStepGraph,
1558
- workflowStatus,
1559
- result,
1560
- error
1561
- }) {
1562
- await this.inngestStep.run(
1563
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1564
- async () => {
1565
- const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1566
- if (!shouldPersistSnapshot) {
1567
- return;
1568
- }
1569
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1570
- workflowName: workflowId,
1571
- runId,
1572
- resourceId,
1573
- snapshot: {
1574
- runId,
1575
- status: workflowStatus,
1576
- value: executionContext.state,
1577
- context: stepResults,
1578
- activePaths: executionContext.executionPath,
1579
- activeStepsPath: executionContext.activeStepsPath,
1580
- suspendedPaths: executionContext.suspendedPaths,
1581
- resumeLabels: executionContext.resumeLabels,
1582
- waitingPaths: {},
1583
- serializedStepGraph,
1584
- result,
1585
- error,
1586
- timestamp: Date.now()
1587
- }
1588
- });
1589
- }
1590
- );
1591
- }
1592
- async executeConditional({
1593
- workflowId,
1594
- runId,
1595
- entry,
1596
- prevOutput,
1597
- stepResults,
1598
- timeTravel,
1599
- resume,
1600
- executionContext,
1601
- emitter,
1602
- abortController,
1603
- requestContext,
1604
- writableStream,
1605
- disableScorers,
1606
- tracingContext
1607
- }) {
1608
- const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1609
- type: SpanType.WORKFLOW_CONDITIONAL,
1610
- name: `conditional: '${entry.conditions.length} conditions'`,
1611
- input: prevOutput,
1612
- attributes: {
1613
- conditionCount: entry.conditions.length
1614
- },
1615
- tracingPolicy: this.options?.tracingPolicy
1616
- });
1617
- let execResults;
1618
- const truthyIndexes = (await Promise.all(
1619
- entry.conditions.map(
1620
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1621
- const evalSpan = conditionalSpan?.createChildSpan({
1622
- type: SpanType.WORKFLOW_CONDITIONAL_EVAL,
1623
- name: `condition: '${index}'`,
1624
- input: prevOutput,
1625
- attributes: {
1626
- conditionIndex: index
1627
- },
1628
- tracingPolicy: this.options?.tracingPolicy
1629
- });
1630
- try {
1631
- const result = await cond(
1632
- createDeprecationProxy(
1633
- {
1634
- runId,
1635
- workflowId,
1636
- mastra: this.mastra,
1637
- requestContext,
1638
- retryCount: -1,
1639
- inputData: prevOutput,
1640
- state: executionContext.state,
1641
- setState: (state) => {
1642
- executionContext.state = state;
1643
- },
1644
- tracingContext: {
1645
- currentSpan: evalSpan
1646
- },
1647
- getInitData: () => stepResults?.input,
1648
- getStepResult: getStepResult.bind(this, stepResults),
1649
- // TODO: this function shouldn't have suspend probably?
1650
- suspend: async (_suspendPayload) => {
1651
- },
1652
- bail: () => {
1653
- },
1654
- abort: () => {
1655
- abortController.abort();
1656
- },
1657
- [EMITTER_SYMBOL]: emitter,
1658
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1659
- engine: {
1660
- step: this.inngestStep
1661
- },
1662
- abortSignal: abortController.signal,
1663
- writer: new ToolStream(
1664
- {
1665
- prefix: "workflow-step",
1666
- callId: randomUUID(),
1667
- name: "conditional",
1668
- runId
1669
- },
1670
- writableStream
1671
- )
1672
- },
1673
- {
1674
- paramName: "runCount",
1675
- deprecationMessage: runCountDeprecationMessage,
1676
- logger: this.logger
1677
- }
1678
- )
1679
- );
1680
- evalSpan?.end({
1681
- output: result,
1682
- attributes: {
1683
- result: !!result
1684
- }
1685
- });
1686
- return result ? index : null;
1687
- } catch (e) {
1688
- evalSpan?.error({
1689
- error: e instanceof Error ? e : new Error(String(e)),
1690
- attributes: {
1691
- result: false
1692
- }
1693
- });
1694
- return null;
1695
- }
1696
- })
1697
- )
1698
- )).filter((index) => index !== null);
1699
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1700
- conditionalSpan?.update({
1701
- attributes: {
1702
- truthyIndexes,
1703
- selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1704
- }
1705
- });
1706
- const results = await Promise.all(
1707
- stepsToRun.map(async (step, index) => {
1708
- const currStepResult = stepResults[step.step.id];
1709
- if (currStepResult && currStepResult.status === "success") {
1710
- return currStepResult;
1711
- }
1712
- const result = await this.executeStep({
1713
- step: step.step,
1714
- prevOutput,
1715
- stepResults,
1716
- resume,
1717
- timeTravel,
1718
- executionContext: {
1719
- workflowId,
1720
- runId,
1721
- executionPath: [...executionContext.executionPath, index],
1722
- activeStepsPath: executionContext.activeStepsPath,
1723
- suspendedPaths: executionContext.suspendedPaths,
1724
- resumeLabels: executionContext.resumeLabels,
1725
- retryConfig: executionContext.retryConfig,
1726
- state: executionContext.state
1727
- },
1728
- emitter,
1729
- abortController,
1730
- requestContext,
1731
- writableStream,
1732
- disableScorers,
1733
- tracingContext: {
1734
- currentSpan: conditionalSpan
1735
- }
1736
- });
1737
- stepResults[step.step.id] = result;
1738
- return result;
1739
- })
1740
- );
1741
- const hasFailed = results.find((result) => result.status === "failed");
1742
- const hasSuspended = results.find((result) => result.status === "suspended");
1743
- if (hasFailed) {
1744
- execResults = { status: "failed", error: hasFailed.error };
1745
- } else if (hasSuspended) {
1746
- execResults = {
1747
- status: "suspended",
1748
- suspendPayload: hasSuspended.suspendPayload,
1749
- ...hasSuspended.suspendOutput ? { suspendOutput: hasSuspended.suspendOutput } : {}
1750
- };
1751
- } else {
1752
- execResults = {
1753
- status: "success",
1754
- output: results.reduce((acc, result, index) => {
1755
- if (result.status === "success") {
1756
- if ("step" in stepsToRun[index]) {
1757
- acc[stepsToRun[index].step.id] = result.output;
1758
- }
1759
- }
1760
- return acc;
1761
- }, {})
1762
- };
1763
- }
1764
- if (execResults.status === "failed") {
1765
- conditionalSpan?.error({
1766
- error: new Error(execResults.error)
1767
- });
1768
- } else {
1769
- conditionalSpan?.end({
1770
- output: execResults.output || execResults
1771
- });
1772
- }
1773
- return execResults;
1774
- }
1775
- };
1776
1592
 
1777
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1593
+ export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
1778
1594
  //# sourceMappingURL=index.js.map
1779
1595
  //# sourceMappingURL=index.js.map