@mastra/inngest 0.0.0-remove-unused-import-20250909212718 → 0.0.0-remove-ai-peer-dep-from-evals-20260105220639

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,34 +1,513 @@
1
1
  'use strict';
2
2
 
3
- var crypto = require('crypto');
4
- var realtime = require('@inngest/realtime');
5
- var aiTracing = require('@mastra/core/ai-tracing');
6
- var di = require('@mastra/core/di');
3
+ var agent = require('@mastra/core/agent');
7
4
  var tools = require('@mastra/core/tools');
8
5
  var workflows = require('@mastra/core/workflows');
9
6
  var _constants = require('@mastra/core/workflows/_constants');
10
- var hono = require('inngest/hono');
11
7
  var zod = require('zod');
8
+ var crypto$1 = require('crypto');
9
+ var di = require('@mastra/core/di');
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');
16
+ var hono = require('inngest/hono');
12
17
 
13
18
  // src/index.ts
14
- function serve({ mastra, inngest }) {
15
- const wfs = mastra.getWorkflows();
16
- const functions = Array.from(
17
- new Set(
18
- Object.values(wfs).flatMap((wf) => {
19
- if (wf instanceof InngestWorkflow) {
20
- wf.__registerMastra(mastra);
21
- 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
+ };
22
100
  }
23
- return [];
24
- })
25
- )
26
- );
27
- return hono.serve({
28
- client: inngest,
29
- functions
30
- });
31
- }
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
+ };
32
511
  var InngestRun = class extends workflows.Run {
33
512
  inngest;
34
513
  serializedStepGraph;
@@ -40,95 +519,219 @@ var InngestRun = class extends workflows.Run {
40
519
  this.#mastra = params.mastra;
41
520
  }
42
521
  async getRuns(eventId) {
43
- const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
44
- headers: {
45
- 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
+ }
46
554
  }
47
- });
48
- const json = await response.json();
49
- return json.data;
555
+ }
556
+ throw new inngest.NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
50
557
  }
51
- async getRunOutput(eventId) {
52
- let runs = await this.getRuns(eventId);
53
- while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
54
- await new Promise((resolve) => setTimeout(resolve, 1e3));
55
- runs = await this.getRuns(eventId);
558
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
559
+ const startTime = Date.now();
560
+ const storage = this.#mastra?.getStorage();
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
+ }
56
577
  if (runs?.[0]?.status === "Failed") {
57
- console.log("run", runs?.[0]);
58
- throw new Error(`Function run ${runs?.[0]?.status}`);
59
- } else if (runs?.[0]?.status === "Cancelled") {
60
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
578
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
579
+ workflowName: this.workflowId,
580
+ runId: this.runId
581
+ });
582
+ if (snapshot?.context) {
583
+ snapshot.context = workflows.hydrateSerializedStepErrors(snapshot.context);
584
+ }
585
+ return {
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
+ }
594
+ };
595
+ }
596
+ if (runs?.[0]?.status === "Cancelled") {
597
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
61
598
  workflowName: this.workflowId,
62
599
  runId: this.runId
63
600
  });
64
601
  return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
65
602
  }
603
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
66
604
  }
67
- return runs?.[0];
68
- }
69
- async sendEvent(event, data) {
70
- await this.inngest.send({
71
- name: `user-event-${event}`,
72
- data
73
- });
605
+ throw new inngest.NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
74
606
  }
75
607
  async cancel() {
608
+ const storage = this.#mastra?.getStorage();
76
609
  await this.inngest.send({
77
610
  name: `cancel.workflow.${this.workflowId}`,
78
611
  data: {
79
612
  runId: this.runId
80
613
  }
81
614
  });
82
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
615
+ const workflowsStore = await storage?.getStore("workflows");
616
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
83
617
  workflowName: this.workflowId,
84
618
  runId: this.runId
85
619
  });
86
620
  if (snapshot) {
87
- await this.#mastra?.storage?.persistWorkflowSnapshot({
621
+ await workflowsStore?.persistWorkflowSnapshot({
88
622
  workflowName: this.workflowId,
89
623
  runId: this.runId,
624
+ resourceId: this.resourceId,
90
625
  snapshot: {
91
626
  ...snapshot,
92
- status: "canceled"
627
+ status: "canceled",
628
+ value: snapshot.value
93
629
  }
94
630
  });
95
631
  }
96
632
  }
97
- async start({
98
- inputData
99
- }) {
100
- await this.#mastra.getStorage()?.persistWorkflowSnapshot({
633
+ async start(params) {
634
+ return this._start(params);
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({
101
645
  workflowName: this.workflowId,
102
646
  runId: this.runId,
647
+ resourceId: this.resourceId,
103
648
  snapshot: {
104
649
  runId: this.runId,
105
650
  serializedStepGraph: this.serializedStepGraph,
651
+ status: "running",
106
652
  value: {},
107
653
  context: {},
108
654
  activePaths: [],
109
655
  suspendedPaths: {},
656
+ activeStepsPath: {},
657
+ resumeLabels: {},
110
658
  waitingPaths: {},
111
- timestamp: Date.now(),
112
- status: "running"
659
+ timestamp: Date.now()
113
660
  }
114
661
  });
662
+ const inputDataToUse = await this._validateInput(params.inputData);
663
+ const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
115
664
  const eventOutput = await this.inngest.send({
116
665
  name: `workflow.${this.workflowId}`,
117
666
  data: {
118
- inputData,
119
- runId: this.runId
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
120
675
  }
121
676
  });
122
677
  const eventId = eventOutput.ids[0];
123
678
  if (!eventId) {
124
679
  throw new Error("Event ID is not set");
125
680
  }
126
- const runOutput = await this.getRunOutput(eventId);
127
- const result = runOutput?.output?.result;
128
- if (result.status === "failed") {
129
- result.error = new Error(result.error);
130
- }
131
- if (result.status !== "suspended") {
681
+ return { runId: this.runId };
682
+ }
683
+ async _start({
684
+ inputData,
685
+ initialState,
686
+ outputOptions,
687
+ tracingOptions,
688
+ format,
689
+ requestContext,
690
+ perStep
691
+ }) {
692
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
693
+ await workflowsStore?.persistWorkflowSnapshot({
694
+ workflowName: this.workflowId,
695
+ runId: this.runId,
696
+ resourceId: this.resourceId,
697
+ snapshot: {
698
+ runId: this.runId,
699
+ serializedStepGraph: this.serializedStepGraph,
700
+ status: "running",
701
+ value: {},
702
+ context: {},
703
+ activePaths: [],
704
+ suspendedPaths: {},
705
+ activeStepsPath: {},
706
+ resumeLabels: {},
707
+ waitingPaths: {},
708
+ timestamp: Date.now()
709
+ }
710
+ });
711
+ const inputDataToUse = await this._validateInput(inputData);
712
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
713
+ const eventOutput = await this.inngest.send({
714
+ name: `workflow.${this.workflowId}`,
715
+ data: {
716
+ inputData: inputDataToUse,
717
+ initialState: initialStateToUse,
718
+ runId: this.runId,
719
+ resourceId: this.resourceId,
720
+ outputOptions,
721
+ tracingOptions,
722
+ format,
723
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
724
+ perStep
725
+ }
726
+ });
727
+ const eventId = eventOutput.ids[0];
728
+ if (!eventId) {
729
+ throw new Error("Event ID is not set");
730
+ }
731
+ const runOutput = await this.getRunOutput(eventId);
732
+ const result = runOutput?.output?.result;
733
+ this.hydrateFailedResult(result);
734
+ if (result.status !== "suspended") {
132
735
  this.cleanup?.();
133
736
  }
134
737
  return result;
@@ -145,27 +748,41 @@ var InngestRun = class extends workflows.Run {
145
748
  return p;
146
749
  }
147
750
  async _resume(params) {
148
- const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
149
- (step) => typeof step === "string" ? step : step?.id
150
- );
151
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
751
+ const storage = this.#mastra?.getStorage();
752
+ let steps = [];
753
+ if (typeof params.step === "string") {
754
+ steps = params.step.split(".");
755
+ } else {
756
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
757
+ (step) => typeof step === "string" ? step : step?.id
758
+ );
759
+ }
760
+ const workflowsStore = await storage?.getStore("workflows");
761
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
152
762
  workflowName: this.workflowId,
153
763
  runId: this.runId
154
764
  });
765
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
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 };
155
770
  const eventOutput = await this.inngest.send({
156
771
  name: `workflow.${this.workflowId}`,
157
772
  data: {
158
- inputData: params.resumeData,
773
+ inputData: resumeDataToUse,
774
+ initialState: snapshot?.value ?? {},
159
775
  runId: this.runId,
160
776
  workflowId: this.workflowId,
161
777
  stepResults: snapshot?.context,
162
778
  resume: {
163
779
  steps,
164
780
  stepResults: snapshot?.context,
165
- resumePayload: params.resumeData,
166
- // @ts-ignore
167
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
168
- }
781
+ resumePayload: resumeDataToUse,
782
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
783
+ },
784
+ requestContext: mergedRequestContext,
785
+ perStep: params.perStep
169
786
  }
170
787
  });
171
788
  const eventId = eventOutput.ids[0];
@@ -174,17 +791,108 @@ var InngestRun = class extends workflows.Run {
174
791
  }
175
792
  const runOutput = await this.getRunOutput(eventId);
176
793
  const result = runOutput?.output?.result;
177
- if (result.status === "failed") {
178
- result.error = new Error(result.error);
794
+ this.hydrateFailedResult(result);
795
+ return result;
796
+ }
797
+ async timeTravel(params) {
798
+ const p = this._timeTravel(params).then((result) => {
799
+ if (result.status !== "suspended") {
800
+ this.closeStreamAction?.().catch(() => {
801
+ });
802
+ }
803
+ return result;
804
+ });
805
+ this.executionResults = p;
806
+ return p;
807
+ }
808
+ async _timeTravel(params) {
809
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
810
+ throw new Error("Step is required and must be a valid step or array of steps");
811
+ }
812
+ let steps = [];
813
+ if (typeof params.step === "string") {
814
+ steps = params.step.split(".");
815
+ } else {
816
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
817
+ (step) => typeof step === "string" ? step : step?.id
818
+ );
819
+ }
820
+ if (steps.length === 0) {
821
+ throw new Error("No steps provided to timeTravel");
822
+ }
823
+ const storage = this.#mastra?.getStorage();
824
+ const workflowsStore = await storage?.getStore("workflows");
825
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
826
+ workflowName: this.workflowId,
827
+ runId: this.runId
828
+ });
829
+ if (!snapshot) {
830
+ await workflowsStore?.persistWorkflowSnapshot({
831
+ workflowName: this.workflowId,
832
+ runId: this.runId,
833
+ resourceId: this.resourceId,
834
+ snapshot: {
835
+ runId: this.runId,
836
+ serializedStepGraph: this.serializedStepGraph,
837
+ status: "pending",
838
+ value: {},
839
+ context: {},
840
+ activePaths: [],
841
+ suspendedPaths: {},
842
+ activeStepsPath: {},
843
+ resumeLabels: {},
844
+ waitingPaths: {},
845
+ timestamp: Date.now()
846
+ }
847
+ });
848
+ }
849
+ if (snapshot?.status === "running") {
850
+ throw new Error("This workflow run is still running, cannot time travel");
851
+ }
852
+ let inputDataToUse = params.inputData;
853
+ if (inputDataToUse && steps.length === 1) {
854
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
179
855
  }
856
+ const timeTravelData = workflows.createTimeTravelExecutionParams({
857
+ steps,
858
+ inputData: inputDataToUse,
859
+ resumeData: params.resumeData,
860
+ context: params.context,
861
+ nestedStepsContext: params.nestedStepsContext,
862
+ snapshot: snapshot ?? { context: {} },
863
+ graph: this.executionGraph,
864
+ initialState: params.initialState,
865
+ perStep: params.perStep
866
+ });
867
+ const eventOutput = await this.inngest.send({
868
+ name: `workflow.${this.workflowId}`,
869
+ data: {
870
+ initialState: timeTravelData.state,
871
+ runId: this.runId,
872
+ workflowId: this.workflowId,
873
+ stepResults: timeTravelData.stepResults,
874
+ timeTravel: timeTravelData,
875
+ tracingOptions: params.tracingOptions,
876
+ outputOptions: params.outputOptions,
877
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
878
+ perStep: params.perStep
879
+ }
880
+ });
881
+ const eventId = eventOutput.ids[0];
882
+ if (!eventId) {
883
+ throw new Error("Event ID is not set");
884
+ }
885
+ const runOutput = await this.getRunOutput(eventId);
886
+ const result = runOutput?.output?.result;
887
+ this.hydrateFailedResult(result);
180
888
  return result;
181
889
  }
182
- watch(cb, type = "watch") {
890
+ watch(cb) {
183
891
  let active = true;
184
892
  const streamPromise = realtime.subscribe(
185
893
  {
186
894
  channel: `workflow:${this.workflowId}:${this.runId}`,
187
- topics: [type],
895
+ topics: ["watch"],
188
896
  app: this.inngest
189
897
  },
190
898
  (message) => {
@@ -202,44 +910,35 @@ var InngestRun = class extends workflows.Run {
202
910
  });
203
911
  };
204
912
  }
205
- stream({ inputData, runtimeContext } = {}) {
913
+ streamLegacy({ inputData, requestContext } = {}) {
206
914
  const { readable, writable } = new TransformStream();
207
- let currentToolData = void 0;
208
915
  const writer = writable.getWriter();
916
+ void writer.write({
917
+ // @ts-ignore
918
+ type: "start",
919
+ // @ts-ignore
920
+ payload: { runId: this.runId }
921
+ });
209
922
  const unwatch = this.watch(async (event) => {
210
- if (event.type === "workflow-agent-call-start") {
211
- currentToolData = {
212
- name: event.payload.name,
213
- args: event.payload.args
214
- };
215
- await writer.write({
216
- ...event.payload,
217
- type: "tool-call-streaming-start"
218
- });
219
- return;
220
- }
221
923
  try {
222
- if (event.type === "workflow-agent-call-finish") {
223
- return;
224
- } else if (!event.type.startsWith("workflow-")) {
225
- if (event.type === "text-delta") {
226
- await writer.write({
227
- type: "tool-call-delta",
228
- ...currentToolData ?? {},
229
- argsTextDelta: event.textDelta
230
- });
231
- }
232
- return;
233
- }
234
924
  const e = {
235
925
  ...event,
236
926
  type: event.type.replace("workflow-", "")
237
927
  };
928
+ if (e.type === "step-output") {
929
+ e.type = e.payload.output.type;
930
+ e.payload = e.payload.output.payload;
931
+ }
238
932
  await writer.write(e);
239
933
  } catch {
240
934
  }
241
- }, "watch-v2");
935
+ });
242
936
  this.closeStreamAction = async () => {
937
+ await writer.write({
938
+ type: "finish",
939
+ // @ts-ignore
940
+ payload: { runId: this.runId }
941
+ });
243
942
  unwatch();
244
943
  try {
245
944
  await writer.close();
@@ -249,7 +948,7 @@ var InngestRun = class extends workflows.Run {
249
948
  writer.releaseLock();
250
949
  }
251
950
  };
252
- this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
951
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
253
952
  if (result.status !== "suspended") {
254
953
  this.closeStreamAction?.().catch(() => {
255
954
  });
@@ -261,29 +960,199 @@ var InngestRun = class extends workflows.Run {
261
960
  getWorkflowState: () => this.executionResults
262
961
  };
263
962
  }
963
+ stream({
964
+ inputData,
965
+ requestContext,
966
+ tracingOptions,
967
+ closeOnSuspend = true,
968
+ initialState,
969
+ outputOptions,
970
+ perStep
971
+ } = {}) {
972
+ if (this.closeStreamAction && this.streamOutput) {
973
+ return this.streamOutput;
974
+ }
975
+ this.closeStreamAction = async () => {
976
+ };
977
+ const self = this;
978
+ const stream$1 = new web.ReadableStream({
979
+ async start(controller) {
980
+ const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
981
+ controller.enqueue({
982
+ type,
983
+ runId: self.runId,
984
+ from,
985
+ payload: {
986
+ stepName: payload?.id,
987
+ ...payload
988
+ }
989
+ });
990
+ });
991
+ self.closeStreamAction = async () => {
992
+ unwatch();
993
+ try {
994
+ await controller.close();
995
+ } catch (err) {
996
+ console.error("Error closing stream:", err);
997
+ }
998
+ };
999
+ const executionResultsPromise = self._start({
1000
+ inputData,
1001
+ requestContext,
1002
+ // tracingContext, // We are not able to pass a reference to a span here, what to do?
1003
+ initialState,
1004
+ tracingOptions,
1005
+ outputOptions,
1006
+ format: "vnext",
1007
+ perStep
1008
+ });
1009
+ let executionResults;
1010
+ try {
1011
+ executionResults = await executionResultsPromise;
1012
+ if (closeOnSuspend) {
1013
+ self.closeStreamAction?.().catch(() => {
1014
+ });
1015
+ } else if (executionResults.status !== "suspended") {
1016
+ self.closeStreamAction?.().catch(() => {
1017
+ });
1018
+ }
1019
+ if (self.streamOutput) {
1020
+ self.streamOutput.updateResults(
1021
+ executionResults
1022
+ );
1023
+ }
1024
+ } catch (err) {
1025
+ self.streamOutput?.rejectResults(err);
1026
+ self.closeStreamAction?.().catch(() => {
1027
+ });
1028
+ }
1029
+ }
1030
+ });
1031
+ this.streamOutput = new stream.WorkflowRunOutput({
1032
+ runId: this.runId,
1033
+ workflowId: this.workflowId,
1034
+ stream: stream$1
1035
+ });
1036
+ return this.streamOutput;
1037
+ }
1038
+ timeTravelStream({
1039
+ inputData,
1040
+ resumeData,
1041
+ initialState,
1042
+ step,
1043
+ context,
1044
+ nestedStepsContext,
1045
+ requestContext,
1046
+ tracingOptions,
1047
+ outputOptions,
1048
+ perStep
1049
+ }) {
1050
+ this.closeStreamAction = async () => {
1051
+ };
1052
+ const self = this;
1053
+ const stream$1 = new web.ReadableStream({
1054
+ async start(controller) {
1055
+ const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
1056
+ controller.enqueue({
1057
+ type,
1058
+ runId: self.runId,
1059
+ from,
1060
+ payload: {
1061
+ stepName: payload?.id,
1062
+ ...payload
1063
+ }
1064
+ });
1065
+ });
1066
+ self.closeStreamAction = async () => {
1067
+ unwatch();
1068
+ try {
1069
+ controller.close();
1070
+ } catch (err) {
1071
+ console.error("Error closing stream:", err);
1072
+ }
1073
+ };
1074
+ const executionResultsPromise = self._timeTravel({
1075
+ inputData,
1076
+ step,
1077
+ context,
1078
+ nestedStepsContext,
1079
+ resumeData,
1080
+ initialState,
1081
+ requestContext,
1082
+ tracingOptions,
1083
+ outputOptions,
1084
+ perStep
1085
+ });
1086
+ self.executionResults = executionResultsPromise;
1087
+ let executionResults;
1088
+ try {
1089
+ executionResults = await executionResultsPromise;
1090
+ self.closeStreamAction?.().catch(() => {
1091
+ });
1092
+ if (self.streamOutput) {
1093
+ self.streamOutput.updateResults(executionResults);
1094
+ }
1095
+ } catch (err) {
1096
+ self.streamOutput?.rejectResults(err);
1097
+ self.closeStreamAction?.().catch(() => {
1098
+ });
1099
+ }
1100
+ }
1101
+ });
1102
+ this.streamOutput = new stream.WorkflowRunOutput({
1103
+ runId: this.runId,
1104
+ workflowId: this.workflowId,
1105
+ stream: stream$1
1106
+ });
1107
+ return this.streamOutput;
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
+ }
264
1121
  };
1122
+
1123
+ // src/workflow.ts
265
1124
  var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
266
1125
  #mastra;
267
1126
  inngest;
268
1127
  function;
1128
+ cronFunction;
269
1129
  flowControlConfig;
1130
+ cronConfig;
270
1131
  constructor(params, inngest) {
271
- const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
1132
+ const { concurrency, rateLimit, throttle, debounce, priority, cron, inputData, initialState, ...workflowParams } = params;
272
1133
  super(workflowParams);
1134
+ this.engineType = "inngest";
273
1135
  const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
274
1136
  ([_, value]) => value !== void 0
275
1137
  );
276
1138
  this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
277
1139
  this.#mastra = params.mastra;
278
1140
  this.inngest = inngest;
1141
+ if (cron) {
1142
+ this.cronConfig = { cron, inputData, initialState };
1143
+ }
279
1144
  }
280
- async getWorkflowRuns(args) {
1145
+ async listWorkflowRuns(args) {
281
1146
  const storage = this.#mastra?.getStorage();
282
1147
  if (!storage) {
283
1148
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
284
1149
  return { runs: [], total: 0 };
285
1150
  }
286
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
1151
+ const workflowsStore = await storage.getStore("workflows");
1152
+ if (!workflowsStore) {
1153
+ return { runs: [], total: 0 };
1154
+ }
1155
+ return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
287
1156
  }
288
1157
  async getWorkflowRunById(runId) {
289
1158
  const storage = this.#mastra?.getStorage();
@@ -291,10 +1160,15 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
291
1160
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
292
1161
  return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
293
1162
  }
294
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
1163
+ const workflowsStore = await storage.getStore("workflows");
1164
+ if (!workflowsStore) {
1165
+ return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
1166
+ }
1167
+ const run = await workflowsStore.getWorkflowRunById({ runId, workflowName: this.id });
295
1168
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
296
1169
  }
297
1170
  __registerMastra(mastra) {
1171
+ super.__registerMastra(mastra);
298
1172
  this.#mastra = mastra;
299
1173
  this.executionEngine.__registerMastra(mastra);
300
1174
  const updateNested = (step) => {
@@ -312,63 +1186,82 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
312
1186
  }
313
1187
  }
314
1188
  }
315
- createRun(options) {
316
- const runIdToUse = options?.runId || crypto.randomUUID();
317
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
318
- {
319
- workflowId: this.id,
320
- runId: runIdToUse,
321
- executionEngine: this.executionEngine,
322
- executionGraph: this.executionGraph,
323
- serializedStepGraph: this.serializedStepGraph,
324
- mastra: this.#mastra,
325
- retryConfig: this.retryConfig,
326
- cleanup: () => this.runs.delete(runIdToUse)
327
- },
328
- this.inngest
329
- );
330
- this.runs.set(runIdToUse, run);
331
- return run;
332
- }
333
- async createRunAsync(options) {
334
- const runIdToUse = options?.runId || crypto.randomUUID();
1189
+ async createRun(options) {
1190
+ const runIdToUse = options?.runId || crypto$1.randomUUID();
335
1191
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
336
1192
  {
337
1193
  workflowId: this.id,
338
1194
  runId: runIdToUse,
1195
+ resourceId: options?.resourceId,
339
1196
  executionEngine: this.executionEngine,
340
1197
  executionGraph: this.executionGraph,
341
1198
  serializedStepGraph: this.serializedStepGraph,
342
1199
  mastra: this.#mastra,
343
1200
  retryConfig: this.retryConfig,
344
- cleanup: () => this.runs.delete(runIdToUse)
1201
+ cleanup: () => this.runs.delete(runIdToUse),
1202
+ workflowSteps: this.steps,
1203
+ workflowEngineType: this.engineType,
1204
+ validateInputs: this.options.validateInputs
345
1205
  },
346
1206
  this.inngest
347
1207
  );
348
1208
  this.runs.set(runIdToUse, run);
349
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
350
- if (!workflowSnapshotInStorage) {
351
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1209
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
1210
+ workflowStatus: run.workflowRunStatus,
1211
+ stepResults: {}
1212
+ });
1213
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
1214
+ withNestedWorkflows: false
1215
+ });
1216
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
1217
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
1218
+ await workflowsStore?.persistWorkflowSnapshot({
352
1219
  workflowName: this.id,
353
1220
  runId: runIdToUse,
1221
+ resourceId: options?.resourceId,
354
1222
  snapshot: {
355
1223
  runId: runIdToUse,
356
1224
  status: "pending",
357
1225
  value: {},
358
1226
  context: {},
359
1227
  activePaths: [],
1228
+ activeStepsPath: {},
360
1229
  waitingPaths: {},
361
1230
  serializedStepGraph: this.serializedStepGraph,
362
1231
  suspendedPaths: {},
1232
+ resumeLabels: {},
363
1233
  result: void 0,
364
1234
  error: void 0,
365
- // @ts-ignore
366
1235
  timestamp: Date.now()
367
1236
  }
368
1237
  });
369
1238
  }
370
1239
  return run;
371
1240
  }
1241
+ //createCronFunction is only called if cronConfig.cron is defined.
1242
+ createCronFunction() {
1243
+ if (this.cronFunction) {
1244
+ return this.cronFunction;
1245
+ }
1246
+ this.cronFunction = this.inngest.createFunction(
1247
+ {
1248
+ id: `workflow.${this.id}.cron`,
1249
+ retries: 0,
1250
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1251
+ ...this.flowControlConfig
1252
+ },
1253
+ { cron: this.cronConfig?.cron ?? "" },
1254
+ async () => {
1255
+ const run = await this.createRun();
1256
+ const result = await run.start({
1257
+ inputData: this.cronConfig?.inputData,
1258
+ initialState: this.cronConfig?.initialState
1259
+ });
1260
+ return { result, runId: run.runId };
1261
+ }
1262
+ );
1263
+ return this.cronFunction;
1264
+ }
372
1265
  getFunction() {
373
1266
  if (this.function) {
374
1267
  return this.function;
@@ -376,57 +1269,61 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
376
1269
  this.function = this.inngest.createFunction(
377
1270
  {
378
1271
  id: `workflow.${this.id}`,
379
- // @ts-ignore
380
- retries: this.retryConfig?.attempts ?? 0,
1272
+ retries: 0,
381
1273
  cancelOn: [{ event: `cancel.workflow.${this.id}` }],
382
1274
  // Spread flow control configuration
383
1275
  ...this.flowControlConfig
384
1276
  },
385
1277
  { event: `workflow.${this.id}` },
386
1278
  async ({ event, step, attempt, publish }) => {
387
- let { inputData, runId, resume } = event.data;
1279
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel, perStep } = event.data;
388
1280
  if (!runId) {
389
1281
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
390
- return crypto.randomUUID();
1282
+ return crypto$1.randomUUID();
391
1283
  });
392
1284
  }
393
- const emitter = {
394
- emit: async (event2, data) => {
395
- if (!publish) {
396
- return;
397
- }
398
- try {
399
- await publish({
400
- channel: `workflow:${this.id}:${runId}`,
401
- topic: event2,
402
- data
403
- });
404
- } catch (err) {
405
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
406
- }
407
- },
408
- on: (_event, _callback) => {
409
- },
410
- off: (_event, _callback) => {
411
- },
412
- once: (_event, _callback) => {
413
- }
414
- };
415
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
1285
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
1286
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
416
1287
  const result = await engine.execute({
417
1288
  workflowId: this.id,
418
1289
  runId,
1290
+ resourceId,
419
1291
  graph: this.executionGraph,
420
1292
  serializedStepGraph: this.serializedStepGraph,
421
1293
  input: inputData,
422
- emitter,
1294
+ initialState,
1295
+ pubsub,
423
1296
  retryConfig: this.retryConfig,
424
- runtimeContext: new di.RuntimeContext(),
425
- // TODO
1297
+ requestContext: new di.RequestContext(Object.entries(event.data.requestContext ?? {})),
426
1298
  resume,
1299
+ timeTravel,
1300
+ perStep,
1301
+ format,
427
1302
  abortController: new AbortController(),
428
- currentSpan: void 0
429
- // TODO: Pass actual parent AI span from workflow execution context
1303
+ // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
1304
+ outputOptions,
1305
+ outputWriter: async (chunk) => {
1306
+ try {
1307
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1308
+ type: "watch",
1309
+ runId,
1310
+ data: chunk
1311
+ });
1312
+ } catch (err) {
1313
+ this.logger.debug?.("Failed to publish watch event:", err);
1314
+ }
1315
+ }
1316
+ });
1317
+ await step.run(`workflow.${this.id}.finalize`, async () => {
1318
+ if (result.status !== "paused") {
1319
+ await engine.invokeLifecycleCallbacksInternal(result);
1320
+ }
1321
+ if (result.status === "failed") {
1322
+ throw new inngest.NonRetriableError(`Workflow failed`, {
1323
+ cause: result
1324
+ });
1325
+ }
1326
+ return result;
430
1327
  });
431
1328
  return { result, runId };
432
1329
  }
@@ -447,87 +1344,199 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
447
1344
  });
448
1345
  }
449
1346
  getFunctions() {
450
- return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
1347
+ return [
1348
+ this.getFunction(),
1349
+ ...this.cronConfig?.cron ? [this.createCronFunction()] : [],
1350
+ ...this.getNestedFunctions(this.executionGraph.steps)
1351
+ ];
451
1352
  }
452
1353
  };
453
- function isAgent(params) {
454
- return params?.component === "AGENT";
455
- }
456
- function isTool(params) {
457
- return params instanceof tools.Tool;
1354
+ function serve({
1355
+ mastra,
1356
+ inngest,
1357
+ functions: userFunctions = [],
1358
+ registerOptions
1359
+ }) {
1360
+ const wfs = mastra.listWorkflows();
1361
+ const workflowFunctions = Array.from(
1362
+ new Set(
1363
+ Object.values(wfs).flatMap((wf) => {
1364
+ if (wf instanceof InngestWorkflow) {
1365
+ wf.__registerMastra(mastra);
1366
+ return wf.getFunctions();
1367
+ }
1368
+ return [];
1369
+ })
1370
+ )
1371
+ );
1372
+ return hono.serve({
1373
+ ...registerOptions,
1374
+ client: inngest,
1375
+ functions: [...workflowFunctions, ...userFunctions]
1376
+ });
458
1377
  }
459
- function createStep(params) {
460
- if (isAgent(params)) {
1378
+
1379
+ // src/types.ts
1380
+ var _compatibilityCheck = true;
1381
+
1382
+ // src/index.ts
1383
+ function createStep(params, agentOrToolOptions) {
1384
+ if (params instanceof InngestWorkflow) {
1385
+ return params;
1386
+ }
1387
+ if (params instanceof agent.Agent) {
1388
+ const options = agentOrToolOptions;
1389
+ const outputSchema = options?.structuredOutput?.schema ?? zod.z.object({ text: zod.z.string() });
1390
+ const { retries, scorers, ...agentOptions } = options ?? {};
461
1391
  return {
462
1392
  id: params.name,
463
- // @ts-ignore
1393
+ description: params.getDescription(),
464
1394
  inputSchema: zod.z.object({
465
1395
  prompt: zod.z.string()
466
1396
  // resourceId: z.string().optional(),
467
1397
  // threadId: z.string().optional(),
468
1398
  }),
469
- // @ts-ignore
470
- outputSchema: zod.z.object({
471
- text: zod.z.string()
472
- }),
473
- execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
1399
+ outputSchema,
1400
+ retries,
1401
+ scorers,
1402
+ execute: async ({
1403
+ inputData,
1404
+ runId,
1405
+ [_constants.PUBSUB_SYMBOL]: pubsub,
1406
+ [_constants.STREAM_FORMAT_SYMBOL]: streamFormat,
1407
+ requestContext,
1408
+ tracingContext,
1409
+ abortSignal,
1410
+ abort,
1411
+ writer
1412
+ }) => {
474
1413
  let streamPromise = {};
475
1414
  streamPromise.promise = new Promise((resolve, reject) => {
476
1415
  streamPromise.resolve = resolve;
477
1416
  streamPromise.reject = reject;
478
1417
  });
1418
+ let structuredResult = null;
479
1419
  const toolData = {
480
1420
  name: params.name,
481
1421
  args: inputData
482
1422
  };
483
- await emitter.emit("watch-v2", {
484
- type: "workflow-agent-call-start",
485
- payload: toolData
486
- });
487
- const { fullStream } = await params.stream(inputData.prompt, {
488
- // resourceId: inputData.resourceId,
489
- // threadId: inputData.threadId,
490
- runtimeContext,
491
- tracingContext,
492
- onFinish: (result) => {
493
- streamPromise.resolve(result.text);
494
- },
495
- abortSignal
496
- });
1423
+ let stream;
1424
+ if ((await params.getModel()).specificationVersion === "v1") {
1425
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
1426
+ ...agentOptions ?? {},
1427
+ // resourceId: inputData.resourceId,
1428
+ // threadId: inputData.threadId,
1429
+ requestContext,
1430
+ tracingContext,
1431
+ onFinish: (result) => {
1432
+ const resultWithObject = result;
1433
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1434
+ structuredResult = resultWithObject.object;
1435
+ }
1436
+ streamPromise.resolve(result.text);
1437
+ void agentOptions?.onFinish?.(result);
1438
+ },
1439
+ abortSignal
1440
+ });
1441
+ stream = fullStream;
1442
+ } else {
1443
+ const modelOutput = await params.stream(inputData.prompt, {
1444
+ ...agentOptions ?? {},
1445
+ requestContext,
1446
+ tracingContext,
1447
+ onFinish: (result) => {
1448
+ const resultWithObject = result;
1449
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1450
+ structuredResult = resultWithObject.object;
1451
+ }
1452
+ streamPromise.resolve(result.text);
1453
+ void agentOptions?.onFinish?.(result);
1454
+ },
1455
+ abortSignal
1456
+ });
1457
+ stream = modelOutput.fullStream;
1458
+ }
1459
+ if (streamFormat === "legacy") {
1460
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1461
+ type: "watch",
1462
+ runId,
1463
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
1464
+ });
1465
+ for await (const chunk of stream) {
1466
+ if (chunk.type === "text-delta") {
1467
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1468
+ type: "watch",
1469
+ runId,
1470
+ data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
1471
+ });
1472
+ }
1473
+ }
1474
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1475
+ type: "watch",
1476
+ runId,
1477
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
1478
+ });
1479
+ } else {
1480
+ for await (const chunk of stream) {
1481
+ await writer.write(chunk);
1482
+ }
1483
+ }
497
1484
  if (abortSignal.aborted) {
498
1485
  return abort();
499
1486
  }
500
- for await (const chunk of fullStream) {
501
- await emitter.emit("watch-v2", chunk);
1487
+ if (structuredResult !== null) {
1488
+ return structuredResult;
502
1489
  }
503
- await emitter.emit("watch-v2", {
504
- type: "workflow-agent-call-finish",
505
- payload: toolData
506
- });
507
1490
  return {
508
1491
  text: await streamPromise.promise
509
1492
  };
510
- }
1493
+ },
1494
+ component: params.component
511
1495
  };
512
1496
  }
513
- if (isTool(params)) {
1497
+ if (params instanceof tools.Tool) {
1498
+ const toolOpts = agentOrToolOptions;
514
1499
  if (!params.inputSchema || !params.outputSchema) {
515
1500
  throw new Error("Tool must have input and output schemas defined");
516
1501
  }
517
1502
  return {
518
1503
  // TODO: tool probably should have strong id type
519
- // @ts-ignore
520
1504
  id: params.id,
1505
+ description: params.description,
521
1506
  inputSchema: params.inputSchema,
522
1507
  outputSchema: params.outputSchema,
523
- execute: async ({ inputData, mastra, runtimeContext, tracingContext }) => {
524
- return params.execute({
525
- context: inputData,
526
- mastra: aiTracing.wrapMastra(mastra, tracingContext),
527
- runtimeContext,
528
- tracingContext
529
- });
530
- }
1508
+ suspendSchema: params.suspendSchema,
1509
+ resumeSchema: params.resumeSchema,
1510
+ retries: toolOpts?.retries,
1511
+ scorers: toolOpts?.scorers,
1512
+ execute: async ({
1513
+ inputData,
1514
+ mastra,
1515
+ requestContext,
1516
+ tracingContext,
1517
+ suspend,
1518
+ resumeData,
1519
+ runId,
1520
+ workflowId,
1521
+ state,
1522
+ setState
1523
+ }) => {
1524
+ const toolContext = {
1525
+ mastra,
1526
+ requestContext,
1527
+ tracingContext,
1528
+ workflow: {
1529
+ runId,
1530
+ resumeData,
1531
+ suspend,
1532
+ workflowId,
1533
+ state,
1534
+ setState
1535
+ }
1536
+ };
1537
+ return params.execute(inputData, toolContext);
1538
+ },
1539
+ component: "TOOL"
531
1540
  };
532
1541
  }
533
1542
  return {
@@ -537,13 +1546,18 @@ function createStep(params) {
537
1546
  outputSchema: params.outputSchema,
538
1547
  resumeSchema: params.resumeSchema,
539
1548
  suspendSchema: params.suspendSchema,
1549
+ retries: params.retries,
1550
+ scorers: params.scorers,
540
1551
  execute: params.execute
541
1552
  };
542
1553
  }
543
1554
  function init(inngest) {
544
1555
  return {
545
1556
  createWorkflow(params) {
546
- return new InngestWorkflow(params, inngest);
1557
+ return new InngestWorkflow(
1558
+ params,
1559
+ inngest
1560
+ );
547
1561
  },
548
1562
  createStep,
549
1563
  cloneStep(step, opts) {
@@ -552,7 +1566,13 @@ function init(inngest) {
552
1566
  description: step.description,
553
1567
  inputSchema: step.inputSchema,
554
1568
  outputSchema: step.outputSchema,
555
- execute: step.execute
1569
+ resumeSchema: step.resumeSchema,
1570
+ suspendSchema: step.suspendSchema,
1571
+ stateSchema: step.stateSchema,
1572
+ execute: step.execute,
1573
+ retries: step.retries,
1574
+ scorers: step.scorers,
1575
+ component: step.component
556
1576
  };
557
1577
  },
558
1578
  cloneWorkflow(workflow, opts) {
@@ -561,7 +1581,8 @@ function init(inngest) {
561
1581
  inputSchema: workflow.inputSchema,
562
1582
  outputSchema: workflow.outputSchema,
563
1583
  steps: workflow.stepDefs,
564
- mastra: workflow.mastra
1584
+ mastra: workflow.mastra,
1585
+ options: workflow.options
565
1586
  });
566
1587
  wf.setStepFlow(workflow.stepGraph);
567
1588
  wf.commit();
@@ -569,852 +1590,12 @@ function init(inngest) {
569
1590
  }
570
1591
  };
571
1592
  }
572
- var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
573
- inngestStep;
574
- inngestAttempts;
575
- constructor(mastra, inngestStep, inngestAttempts = 0) {
576
- super({ mastra });
577
- this.inngestStep = inngestStep;
578
- this.inngestAttempts = inngestAttempts;
579
- }
580
- async execute(params) {
581
- await params.emitter.emit("watch-v2", {
582
- type: "workflow-start",
583
- payload: { runId: params.runId }
584
- });
585
- const result = await super.execute(params);
586
- await params.emitter.emit("watch-v2", {
587
- type: "workflow-finish",
588
- payload: { runId: params.runId }
589
- });
590
- return result;
591
- }
592
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
593
- const base = {
594
- status: lastOutput.status,
595
- steps: stepResults
596
- };
597
- if (lastOutput.status === "success") {
598
- await emitter.emit("watch", {
599
- type: "watch",
600
- payload: {
601
- workflowState: {
602
- status: lastOutput.status,
603
- steps: stepResults,
604
- result: lastOutput.output
605
- }
606
- },
607
- eventTimestamp: Date.now()
608
- });
609
- base.result = lastOutput.output;
610
- } else if (lastOutput.status === "failed") {
611
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
612
- await emitter.emit("watch", {
613
- type: "watch",
614
- payload: {
615
- workflowState: {
616
- status: lastOutput.status,
617
- steps: stepResults,
618
- result: null,
619
- error: base.error
620
- }
621
- },
622
- eventTimestamp: Date.now()
623
- });
624
- } else if (lastOutput.status === "suspended") {
625
- await emitter.emit("watch", {
626
- type: "watch",
627
- payload: {
628
- workflowState: {
629
- status: lastOutput.status,
630
- steps: stepResults,
631
- result: null,
632
- error: null
633
- }
634
- },
635
- eventTimestamp: Date.now()
636
- });
637
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
638
- if (stepResult?.status === "suspended") {
639
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
640
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
641
- }
642
- return [];
643
- });
644
- base.suspended = suspendedStepIds;
645
- }
646
- executionSpan?.end();
647
- return base;
648
- }
649
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
650
- // await this.inngestStep.sleep(id, duration);
651
- // }
652
- async executeSleep({
653
- workflowId,
654
- runId,
655
- entry,
656
- prevOutput,
657
- stepResults,
658
- emitter,
659
- abortController,
660
- runtimeContext,
661
- executionContext,
662
- writableStream,
663
- tracingContext
664
- }) {
665
- let { duration, fn } = entry;
666
- const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
667
- type: aiTracing.AISpanType.WORKFLOW_SLEEP,
668
- name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
669
- attributes: {
670
- durationMs: duration,
671
- sleepType: fn ? "dynamic" : "fixed"
672
- }
673
- });
674
- if (fn) {
675
- const stepCallId = crypto.randomUUID();
676
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
677
- return await fn({
678
- runId,
679
- workflowId,
680
- mastra: this.mastra,
681
- runtimeContext,
682
- inputData: prevOutput,
683
- runCount: -1,
684
- tracingContext: {
685
- currentSpan: sleepSpan
686
- },
687
- getInitData: () => stepResults?.input,
688
- getStepResult: (step) => {
689
- if (!step?.id) {
690
- return null;
691
- }
692
- const result = stepResults[step.id];
693
- if (result?.status === "success") {
694
- return result.output;
695
- }
696
- return null;
697
- },
698
- // TODO: this function shouldn't have suspend probably?
699
- suspend: async (_suspendPayload) => {
700
- },
701
- bail: () => {
702
- },
703
- abort: () => {
704
- abortController?.abort();
705
- },
706
- [_constants.EMITTER_SYMBOL]: emitter,
707
- // TODO: add streamVNext support
708
- [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
709
- engine: { step: this.inngestStep },
710
- abortSignal: abortController?.signal,
711
- writer: new tools.ToolStream(
712
- {
713
- prefix: "workflow-step",
714
- callId: stepCallId,
715
- name: "sleep",
716
- runId
717
- },
718
- writableStream
719
- )
720
- });
721
- });
722
- sleepSpan?.update({
723
- attributes: {
724
- durationMs: duration
725
- }
726
- });
727
- }
728
- try {
729
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
730
- sleepSpan?.end();
731
- } catch (e) {
732
- sleepSpan?.error({ error: e });
733
- throw e;
734
- }
735
- }
736
- async executeSleepUntil({
737
- workflowId,
738
- runId,
739
- entry,
740
- prevOutput,
741
- stepResults,
742
- emitter,
743
- abortController,
744
- runtimeContext,
745
- executionContext,
746
- writableStream,
747
- tracingContext
748
- }) {
749
- let { date, fn } = entry;
750
- const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
751
- type: aiTracing.AISpanType.WORKFLOW_SLEEP,
752
- name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
753
- attributes: {
754
- untilDate: date,
755
- durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
756
- sleepType: fn ? "dynamic" : "fixed"
757
- }
758
- });
759
- if (fn) {
760
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
761
- const stepCallId = crypto.randomUUID();
762
- return await fn({
763
- runId,
764
- workflowId,
765
- mastra: this.mastra,
766
- runtimeContext,
767
- inputData: prevOutput,
768
- runCount: -1,
769
- tracingContext: {
770
- currentSpan: sleepUntilSpan
771
- },
772
- getInitData: () => stepResults?.input,
773
- getStepResult: (step) => {
774
- if (!step?.id) {
775
- return null;
776
- }
777
- const result = stepResults[step.id];
778
- if (result?.status === "success") {
779
- return result.output;
780
- }
781
- return null;
782
- },
783
- // TODO: this function shouldn't have suspend probably?
784
- suspend: async (_suspendPayload) => {
785
- },
786
- bail: () => {
787
- },
788
- abort: () => {
789
- abortController?.abort();
790
- },
791
- [_constants.EMITTER_SYMBOL]: emitter,
792
- [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
793
- // TODO: add streamVNext support
794
- engine: { step: this.inngestStep },
795
- abortSignal: abortController?.signal,
796
- writer: new tools.ToolStream(
797
- {
798
- prefix: "workflow-step",
799
- callId: stepCallId,
800
- name: "sleep",
801
- runId
802
- },
803
- writableStream
804
- )
805
- });
806
- });
807
- const time = !date ? 0 : date.getTime() - Date.now();
808
- sleepUntilSpan?.update({
809
- attributes: {
810
- durationMs: Math.max(0, time)
811
- }
812
- });
813
- }
814
- if (!(date instanceof Date)) {
815
- sleepUntilSpan?.end();
816
- return;
817
- }
818
- try {
819
- await this.inngestStep.sleepUntil(entry.id, date);
820
- sleepUntilSpan?.end();
821
- } catch (e) {
822
- sleepUntilSpan?.error({ error: e });
823
- throw e;
824
- }
825
- }
826
- async executeWaitForEvent({ event, timeout }) {
827
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
828
- event: `user-event-${event}`,
829
- timeout: timeout ?? 5e3
830
- });
831
- if (eventData === null) {
832
- throw "Timeout waiting for event";
833
- }
834
- return eventData?.data;
835
- }
836
- async executeStep({
837
- step,
838
- stepResults,
839
- executionContext,
840
- resume,
841
- prevOutput,
842
- emitter,
843
- abortController,
844
- runtimeContext,
845
- tracingContext,
846
- writableStream,
847
- disableScorers
848
- }) {
849
- const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
850
- name: `workflow step: '${step.id}'`,
851
- type: aiTracing.AISpanType.WORKFLOW_STEP,
852
- input: prevOutput,
853
- attributes: {
854
- stepId: step.id
855
- }
856
- });
857
- const startedAt = await this.inngestStep.run(
858
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
859
- async () => {
860
- const startedAt2 = Date.now();
861
- await emitter.emit("watch", {
862
- type: "watch",
863
- payload: {
864
- currentStep: {
865
- id: step.id,
866
- status: "running"
867
- },
868
- workflowState: {
869
- status: "running",
870
- steps: {
871
- ...stepResults,
872
- [step.id]: {
873
- status: "running"
874
- }
875
- },
876
- result: null,
877
- error: null
878
- }
879
- },
880
- eventTimestamp: Date.now()
881
- });
882
- await emitter.emit("watch-v2", {
883
- type: "workflow-step-start",
884
- payload: {
885
- id: step.id,
886
- status: "running",
887
- payload: prevOutput,
888
- startedAt: startedAt2
889
- }
890
- });
891
- return startedAt2;
892
- }
893
- );
894
- if (step instanceof InngestWorkflow) {
895
- const isResume = !!resume?.steps?.length;
896
- let result;
897
- let runId;
898
- if (isResume) {
899
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? crypto.randomUUID();
900
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
901
- workflowName: step.id,
902
- runId
903
- });
904
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
905
- function: step.getFunction(),
906
- data: {
907
- inputData: prevOutput,
908
- runId,
909
- resume: {
910
- runId,
911
- steps: resume.steps.slice(1),
912
- stepResults: snapshot?.context,
913
- resumePayload: resume.resumePayload,
914
- // @ts-ignore
915
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
916
- }
917
- }
918
- });
919
- result = invokeResp.result;
920
- runId = invokeResp.runId;
921
- } else {
922
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
923
- function: step.getFunction(),
924
- data: {
925
- inputData: prevOutput
926
- }
927
- });
928
- result = invokeResp.result;
929
- runId = invokeResp.runId;
930
- }
931
- const res = await this.inngestStep.run(
932
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
933
- async () => {
934
- if (result.status === "failed") {
935
- await emitter.emit("watch", {
936
- type: "watch",
937
- payload: {
938
- currentStep: {
939
- id: step.id,
940
- status: "failed",
941
- error: result?.error
942
- },
943
- workflowState: {
944
- status: "running",
945
- steps: stepResults,
946
- result: null,
947
- error: null
948
- }
949
- },
950
- eventTimestamp: Date.now()
951
- });
952
- await emitter.emit("watch-v2", {
953
- type: "workflow-step-result",
954
- payload: {
955
- id: step.id,
956
- status: "failed",
957
- error: result?.error,
958
- payload: prevOutput
959
- }
960
- });
961
- return { executionContext, result: { status: "failed", error: result?.error } };
962
- } else if (result.status === "suspended") {
963
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
964
- const stepRes2 = stepResult;
965
- return stepRes2?.status === "suspended";
966
- });
967
- for (const [stepName, stepResult] of suspendedSteps) {
968
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
969
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
970
- await emitter.emit("watch", {
971
- type: "watch",
972
- payload: {
973
- currentStep: {
974
- id: step.id,
975
- status: "suspended",
976
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
977
- },
978
- workflowState: {
979
- status: "running",
980
- steps: stepResults,
981
- result: null,
982
- error: null
983
- }
984
- },
985
- eventTimestamp: Date.now()
986
- });
987
- await emitter.emit("watch-v2", {
988
- type: "workflow-step-suspended",
989
- payload: {
990
- id: step.id,
991
- status: "suspended"
992
- }
993
- });
994
- return {
995
- executionContext,
996
- result: {
997
- status: "suspended",
998
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
999
- }
1000
- };
1001
- }
1002
- await emitter.emit("watch", {
1003
- type: "watch",
1004
- payload: {
1005
- currentStep: {
1006
- id: step.id,
1007
- status: "suspended",
1008
- payload: {}
1009
- },
1010
- workflowState: {
1011
- status: "running",
1012
- steps: stepResults,
1013
- result: null,
1014
- error: null
1015
- }
1016
- },
1017
- eventTimestamp: Date.now()
1018
- });
1019
- return {
1020
- executionContext,
1021
- result: {
1022
- status: "suspended",
1023
- payload: {}
1024
- }
1025
- };
1026
- }
1027
- await emitter.emit("watch", {
1028
- type: "watch",
1029
- payload: {
1030
- currentStep: {
1031
- id: step.id,
1032
- status: "success",
1033
- output: result?.result
1034
- },
1035
- workflowState: {
1036
- status: "running",
1037
- steps: stepResults,
1038
- result: null,
1039
- error: null
1040
- }
1041
- },
1042
- eventTimestamp: Date.now()
1043
- });
1044
- await emitter.emit("watch-v2", {
1045
- type: "workflow-step-result",
1046
- payload: {
1047
- id: step.id,
1048
- status: "success",
1049
- output: result?.result
1050
- }
1051
- });
1052
- await emitter.emit("watch-v2", {
1053
- type: "workflow-step-finish",
1054
- payload: {
1055
- id: step.id,
1056
- metadata: {}
1057
- }
1058
- });
1059
- return { executionContext, result: { status: "success", output: result?.result } };
1060
- }
1061
- );
1062
- Object.assign(executionContext, res.executionContext);
1063
- return res.result;
1064
- }
1065
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1066
- let execResults;
1067
- let suspended;
1068
- let bailed;
1069
- try {
1070
- const result = await step.execute({
1071
- runId: executionContext.runId,
1072
- mastra: this.mastra,
1073
- runtimeContext,
1074
- writableStream,
1075
- inputData: prevOutput,
1076
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1077
- tracingContext: {
1078
- currentSpan: stepAISpan
1079
- },
1080
- getInitData: () => stepResults?.input,
1081
- getStepResult: (step2) => {
1082
- const result2 = stepResults[step2.id];
1083
- if (result2?.status === "success") {
1084
- return result2.output;
1085
- }
1086
- return null;
1087
- },
1088
- suspend: async (suspendPayload) => {
1089
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1090
- suspended = { payload: suspendPayload };
1091
- },
1092
- bail: (result2) => {
1093
- bailed = { payload: result2 };
1094
- },
1095
- resume: {
1096
- steps: resume?.steps?.slice(1) || [],
1097
- resumePayload: resume?.resumePayload,
1098
- // @ts-ignore
1099
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1100
- },
1101
- [_constants.EMITTER_SYMBOL]: emitter,
1102
- engine: {
1103
- step: this.inngestStep
1104
- },
1105
- abortSignal: abortController.signal
1106
- });
1107
- const endedAt = Date.now();
1108
- execResults = {
1109
- status: "success",
1110
- output: result,
1111
- startedAt,
1112
- endedAt,
1113
- payload: prevOutput,
1114
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1115
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1116
- };
1117
- } catch (e) {
1118
- execResults = {
1119
- status: "failed",
1120
- payload: prevOutput,
1121
- error: e instanceof Error ? e.message : String(e),
1122
- endedAt: Date.now(),
1123
- startedAt,
1124
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1125
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1126
- };
1127
- }
1128
- if (suspended) {
1129
- execResults = {
1130
- status: "suspended",
1131
- suspendedPayload: suspended.payload,
1132
- payload: prevOutput,
1133
- suspendedAt: Date.now(),
1134
- startedAt,
1135
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1136
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1137
- };
1138
- } else if (bailed) {
1139
- execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
1140
- }
1141
- if (execResults.status === "failed") {
1142
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1143
- const error = new Error(execResults.error);
1144
- stepAISpan?.error({ error });
1145
- throw error;
1146
- }
1147
- }
1148
- await emitter.emit("watch", {
1149
- type: "watch",
1150
- payload: {
1151
- currentStep: {
1152
- id: step.id,
1153
- ...execResults
1154
- },
1155
- workflowState: {
1156
- status: "running",
1157
- steps: { ...stepResults, [step.id]: execResults },
1158
- result: null,
1159
- error: null
1160
- }
1161
- },
1162
- eventTimestamp: Date.now()
1163
- });
1164
- if (execResults.status === "suspended") {
1165
- await emitter.emit("watch-v2", {
1166
- type: "workflow-step-suspended",
1167
- payload: {
1168
- id: step.id,
1169
- ...execResults
1170
- }
1171
- });
1172
- } else {
1173
- await emitter.emit("watch-v2", {
1174
- type: "workflow-step-result",
1175
- payload: {
1176
- id: step.id,
1177
- ...execResults
1178
- }
1179
- });
1180
- await emitter.emit("watch-v2", {
1181
- type: "workflow-step-finish",
1182
- payload: {
1183
- id: step.id,
1184
- metadata: {}
1185
- }
1186
- });
1187
- }
1188
- stepAISpan?.end({ output: execResults });
1189
- return { result: execResults, executionContext, stepResults };
1190
- });
1191
- if (disableScorers !== false) {
1192
- await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1193
- if (step.scorers) {
1194
- await this.runScorers({
1195
- scorers: step.scorers,
1196
- runId: executionContext.runId,
1197
- input: prevOutput,
1198
- output: stepRes.result,
1199
- workflowId: executionContext.workflowId,
1200
- stepId: step.id,
1201
- runtimeContext,
1202
- disableScorers,
1203
- tracingContext: { currentSpan: stepAISpan }
1204
- });
1205
- }
1206
- });
1207
- }
1208
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1209
- Object.assign(stepResults, stepRes.stepResults);
1210
- return stepRes.result;
1211
- }
1212
- async persistStepUpdate({
1213
- workflowId,
1214
- runId,
1215
- stepResults,
1216
- executionContext,
1217
- serializedStepGraph,
1218
- workflowStatus,
1219
- result,
1220
- error
1221
- }) {
1222
- await this.inngestStep.run(
1223
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1224
- async () => {
1225
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1226
- workflowName: workflowId,
1227
- runId,
1228
- snapshot: {
1229
- runId,
1230
- value: {},
1231
- context: stepResults,
1232
- activePaths: [],
1233
- suspendedPaths: executionContext.suspendedPaths,
1234
- waitingPaths: {},
1235
- serializedStepGraph,
1236
- status: workflowStatus,
1237
- result,
1238
- error,
1239
- // @ts-ignore
1240
- timestamp: Date.now()
1241
- }
1242
- });
1243
- }
1244
- );
1245
- }
1246
- async executeConditional({
1247
- workflowId,
1248
- runId,
1249
- entry,
1250
- prevOutput,
1251
- prevStep,
1252
- stepResults,
1253
- serializedStepGraph,
1254
- resume,
1255
- executionContext,
1256
- emitter,
1257
- abortController,
1258
- runtimeContext,
1259
- writableStream,
1260
- disableScorers,
1261
- tracingContext
1262
- }) {
1263
- const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1264
- type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
1265
- name: `conditional: ${entry.conditions.length} conditions`,
1266
- input: prevOutput,
1267
- attributes: {
1268
- conditionCount: entry.conditions.length
1269
- }
1270
- });
1271
- let execResults;
1272
- const truthyIndexes = (await Promise.all(
1273
- entry.conditions.map(
1274
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1275
- const evalSpan = conditionalSpan?.createChildSpan({
1276
- type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1277
- name: `condition ${index}`,
1278
- input: prevOutput,
1279
- attributes: {
1280
- conditionIndex: index
1281
- }
1282
- });
1283
- try {
1284
- const result = await cond({
1285
- runId,
1286
- workflowId,
1287
- mastra: this.mastra,
1288
- runtimeContext,
1289
- runCount: -1,
1290
- inputData: prevOutput,
1291
- tracingContext: {
1292
- currentSpan: evalSpan
1293
- },
1294
- getInitData: () => stepResults?.input,
1295
- getStepResult: (step) => {
1296
- if (!step?.id) {
1297
- return null;
1298
- }
1299
- const result2 = stepResults[step.id];
1300
- if (result2?.status === "success") {
1301
- return result2.output;
1302
- }
1303
- return null;
1304
- },
1305
- // TODO: this function shouldn't have suspend probably?
1306
- suspend: async (_suspendPayload) => {
1307
- },
1308
- bail: () => {
1309
- },
1310
- abort: () => {
1311
- abortController.abort();
1312
- },
1313
- [_constants.EMITTER_SYMBOL]: emitter,
1314
- [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1315
- // TODO: add streamVNext support
1316
- engine: {
1317
- step: this.inngestStep
1318
- },
1319
- abortSignal: abortController.signal,
1320
- writer: new tools.ToolStream(
1321
- {
1322
- prefix: "workflow-step",
1323
- callId: crypto.randomUUID(),
1324
- name: "conditional",
1325
- runId
1326
- },
1327
- writableStream
1328
- )
1329
- });
1330
- evalSpan?.end({
1331
- output: result,
1332
- attributes: {
1333
- result: !!result
1334
- }
1335
- });
1336
- return result ? index : null;
1337
- } catch (e) {
1338
- evalSpan?.error({
1339
- error: e instanceof Error ? e : new Error(String(e)),
1340
- attributes: {
1341
- result: false
1342
- }
1343
- });
1344
- return null;
1345
- }
1346
- })
1347
- )
1348
- )).filter((index) => index !== null);
1349
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1350
- conditionalSpan?.update({
1351
- attributes: {
1352
- truthyIndexes,
1353
- selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1354
- }
1355
- });
1356
- const results = await Promise.all(
1357
- stepsToRun.map(
1358
- (step, index) => this.executeEntry({
1359
- workflowId,
1360
- runId,
1361
- entry: step,
1362
- serializedStepGraph,
1363
- prevStep,
1364
- stepResults,
1365
- resume,
1366
- executionContext: {
1367
- workflowId,
1368
- runId,
1369
- executionPath: [...executionContext.executionPath, index],
1370
- suspendedPaths: executionContext.suspendedPaths,
1371
- retryConfig: executionContext.retryConfig,
1372
- executionSpan: executionContext.executionSpan
1373
- },
1374
- emitter,
1375
- abortController,
1376
- runtimeContext,
1377
- writableStream,
1378
- disableScorers,
1379
- tracingContext: {
1380
- currentSpan: conditionalSpan
1381
- }
1382
- })
1383
- )
1384
- );
1385
- const hasFailed = results.find((result) => result.result.status === "failed");
1386
- const hasSuspended = results.find((result) => result.result.status === "suspended");
1387
- if (hasFailed) {
1388
- execResults = { status: "failed", error: hasFailed.result.error };
1389
- } else if (hasSuspended) {
1390
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1391
- } else {
1392
- execResults = {
1393
- status: "success",
1394
- output: results.reduce((acc, result, index) => {
1395
- if (result.result.status === "success") {
1396
- acc[stepsToRun[index].step.id] = result.output;
1397
- }
1398
- return acc;
1399
- }, {})
1400
- };
1401
- }
1402
- if (execResults.status === "failed") {
1403
- conditionalSpan?.error({
1404
- error: new Error(execResults.error)
1405
- });
1406
- } else {
1407
- conditionalSpan?.end({
1408
- output: execResults.output || execResults
1409
- });
1410
- }
1411
- return execResults;
1412
- }
1413
- };
1414
1593
 
1415
1594
  exports.InngestExecutionEngine = InngestExecutionEngine;
1595
+ exports.InngestPubSub = InngestPubSub;
1416
1596
  exports.InngestRun = InngestRun;
1417
1597
  exports.InngestWorkflow = InngestWorkflow;
1598
+ exports._compatibilityCheck = _compatibilityCheck;
1418
1599
  exports.createStep = createStep;
1419
1600
  exports.init = init;
1420
1601
  exports.serve = serve;