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