@mastra/inngest 0.0.0-vnext-inngest-20250508122351 → 0.0.0-vnext-20251104230439

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