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