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