@mastra/inngest 0.0.0-taofeeq-fix-tool-call-showing-after-message-20250806184630 → 0.0.0-testing-cloud-studios-20260114234039

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