@mastra/inngest 0.0.0-vnext-inngest-20250508131921 → 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,40 +224,175 @@ 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
  }
147
- async getWorkflowRuns(args) {
383
+ async listWorkflowRuns(args) {
148
384
  const storage = this.#mastra?.getStorage();
149
385
  if (!storage) {
150
386
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
151
387
  return { runs: [], total: 0 };
152
388
  }
153
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
389
+ return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
154
390
  }
155
391
  async getWorkflowRunById(runId) {
156
392
  const storage = this.#mastra?.getStorage();
157
393
  if (!storage) {
158
394
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
159
- return null;
395
+ return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
160
396
  }
161
397
  const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
162
398
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
@@ -179,21 +415,51 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
179
415
  }
180
416
  }
181
417
  }
182
- createRun(options) {
418
+ async createRun(options) {
183
419
  const runIdToUse = options?.runId || randomUUID();
184
420
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
185
421
  {
186
422
  workflowId: this.id,
187
423
  runId: runIdToUse,
424
+ resourceId: options?.resourceId,
188
425
  executionEngine: this.executionEngine,
189
426
  executionGraph: this.executionGraph,
427
+ serializedStepGraph: this.serializedStepGraph,
190
428
  mastra: this.#mastra,
191
429
  retryConfig: this.retryConfig,
192
- cleanup: () => this.runs.delete(runIdToUse)
430
+ cleanup: () => this.runs.delete(runIdToUse),
431
+ workflowSteps: this.steps
193
432
  },
194
433
  this.inngest
195
434
  );
196
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
+ }
197
463
  return run;
198
464
  }
199
465
  getFunction() {
@@ -201,11 +467,17 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
201
467
  return this.function;
202
468
  }
203
469
  this.function = this.inngest.createFunction(
204
- // @ts-ignore
205
- { 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
+ },
206
478
  { event: `workflow.${this.id}` },
207
479
  async ({ event, step, attempt, publish }) => {
208
- let { inputData, runId, resume } = event.data;
480
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format } = event.data;
209
481
  if (!runId) {
210
482
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
211
483
  return randomUUID();
@@ -219,25 +491,52 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
219
491
  try {
220
492
  await publish({
221
493
  channel: `workflow:${this.id}:${runId}`,
222
- topic: "watch",
494
+ topic: event2,
223
495
  data
224
496
  });
225
497
  } catch (err) {
226
498
  this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
227
499
  }
500
+ },
501
+ on: (_event, _callback) => {
502
+ },
503
+ off: (_event, _callback) => {
504
+ },
505
+ once: (_event, _callback) => {
228
506
  }
229
507
  };
230
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
508
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
231
509
  const result = await engine.execute({
232
510
  workflowId: this.id,
233
511
  runId,
512
+ resourceId,
234
513
  graph: this.executionGraph,
514
+ serializedStepGraph: this.serializedStepGraph,
235
515
  input: inputData,
516
+ initialState,
236
517
  emitter,
237
518
  retryConfig: this.retryConfig,
238
- runtimeContext: new RuntimeContext(),
519
+ requestContext: new RequestContext(),
239
520
  // TODO
240
- 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;
241
540
  });
242
541
  return { result, runId };
243
542
  }
@@ -261,118 +560,403 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
261
560
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
262
561
  }
263
562
  };
264
- function cloneWorkflow(workflow, opts) {
265
- const wf = new InngestWorkflow(
266
- {
267
- id: opts.id,
268
- inputSchema: workflow.inputSchema,
269
- outputSchema: workflow.outputSchema,
270
- steps: workflow.stepDefs,
271
- mastra: workflow.mastra
272
- },
273
- workflow.inngest
274
- );
275
- wf.setStepFlow(workflow.stepGraph);
276
- wf.commit();
277
- 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
+ };
278
697
  }
279
698
  function init(inngest) {
280
699
  return {
281
700
  createWorkflow(params) {
282
- return new InngestWorkflow(params, inngest);
701
+ return new InngestWorkflow(
702
+ params,
703
+ inngest
704
+ );
283
705
  },
284
706
  createStep,
285
- cloneStep,
286
- 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
+ }
287
732
  };
288
733
  }
289
734
  var InngestExecutionEngine = class extends DefaultExecutionEngine {
290
735
  inngestStep;
291
736
  inngestAttempts;
292
- constructor(mastra, inngestStep, inngestAttempts = 0) {
293
- super({ mastra });
737
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
738
+ super({ mastra, options });
294
739
  this.inngestStep = inngestStep;
295
740
  this.inngestAttempts = inngestAttempts;
296
741
  }
297
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
742
+ async fmtReturnValue(emitter, stepResults, lastOutput, error) {
298
743
  const base = {
299
744
  status: lastOutput.status,
300
745
  steps: stepResults
301
746
  };
302
747
  if (lastOutput.status === "success") {
303
- await emitter.emit("watch", {
304
- type: "watch",
305
- payload: {
306
- workflowState: {
307
- status: lastOutput.status,
308
- steps: stepResults,
309
- result: lastOutput.output
310
- }
311
- },
312
- eventTimestamp: Date.now()
313
- });
314
748
  base.result = lastOutput.output;
315
749
  } else if (lastOutput.status === "failed") {
316
750
  base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
317
- await emitter.emit("watch", {
318
- type: "watch",
319
- payload: {
320
- workflowState: {
321
- status: lastOutput.status,
322
- steps: stepResults,
323
- result: null,
324
- error: base.error
325
- }
326
- },
327
- eventTimestamp: Date.now()
328
- });
329
751
  } else if (lastOutput.status === "suspended") {
330
- await emitter.emit("watch", {
331
- type: "watch",
332
- payload: {
333
- workflowState: {
334
- status: lastOutput.status,
335
- steps: stepResults,
336
- result: null,
337
- error: null
338
- }
339
- },
340
- eventTimestamp: Date.now()
341
- });
342
752
  const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
343
753
  if (stepResult?.status === "suspended") {
344
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
754
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
345
755
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
346
756
  }
347
757
  return [];
348
758
  });
349
759
  base.suspended = suspendedStepIds;
350
760
  }
351
- executionSpan?.end();
352
761
  return base;
353
762
  }
354
- async superExecuteStep({
763
+ // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
764
+ // await this.inngestStep.sleep(id, duration);
765
+ // }
766
+ async executeSleep({
355
767
  workflowId,
356
768
  runId,
357
- step,
769
+ entry,
770
+ prevOutput,
358
771
  stepResults,
772
+ emitter,
773
+ abortController,
774
+ requestContext,
359
775
  executionContext,
360
- 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,
361
858
  prevOutput,
859
+ stepResults,
362
860
  emitter,
363
- runtimeContext
861
+ abortController,
862
+ requestContext,
863
+ executionContext,
864
+ writableStream,
865
+ tracingContext
364
866
  }) {
365
- return super.executeStep({
366
- workflowId,
367
- runId,
368
- step,
369
- stepResults,
370
- executionContext,
371
- resume,
372
- prevOutput,
373
- emitter,
374
- 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
375
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;
376
960
  }
377
961
  async executeStep({
378
962
  step,
@@ -381,91 +965,113 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
381
965
  resume,
382
966
  prevOutput,
383
967
  emitter,
384
- runtimeContext
968
+ abortController,
969
+ requestContext,
970
+ tracingContext,
971
+ writableStream,
972
+ disableScorers
385
973
  }) {
386
- 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(
387
989
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
388
990
  async () => {
991
+ const startedAt2 = Date.now();
389
992
  await emitter.emit("watch", {
390
- type: "watch",
993
+ type: "workflow-step-start",
391
994
  payload: {
392
- currentStep: {
393
- id: step.id,
394
- status: "running"
395
- },
396
- workflowState: {
397
- status: "running",
398
- steps: {
399
- ...stepResults,
400
- [step.id]: {
401
- status: "running"
402
- }
403
- },
404
- result: null,
405
- error: null
406
- }
407
- },
408
- eventTimestamp: Date.now()
995
+ id: step.id,
996
+ status: "running",
997
+ payload: inputData,
998
+ startedAt: startedAt2
999
+ }
409
1000
  });
1001
+ return startedAt2;
410
1002
  }
411
1003
  );
412
1004
  if (step instanceof InngestWorkflow) {
413
1005
  const isResume = !!resume?.steps?.length;
414
1006
  let result;
415
1007
  let runId;
416
- if (isResume) {
417
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
418
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
419
- workflowName: step.id,
420
- runId
421
- });
422
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
423
- function: step.getFunction(),
424
- data: {
425
- inputData: prevOutput,
426
- runId,
427
- 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 ?? {},
428
1020
  runId,
429
- steps: resume.steps.slice(1),
430
- stepResults: snapshot?.context,
431
- resumePayload: resume.resumePayload,
432
- // @ts-ignore
433
- 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 }
434
1030
  }
435
- }
436
- });
437
- result = invokeResp.result;
438
- runId = invokeResp.runId;
439
- } else {
440
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
441
- function: step.getFunction(),
442
- data: {
443
- inputData: prevOutput
444
- }
445
- });
446
- result = invokeResp.result;
447
- 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
+ }
448
1062
  }
449
1063
  const res = await this.inngestStep.run(
450
1064
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
451
1065
  async () => {
452
1066
  if (result.status === "failed") {
453
1067
  await emitter.emit("watch", {
454
- type: "watch",
1068
+ type: "workflow-step-result",
455
1069
  payload: {
456
- currentStep: {
457
- id: step.id,
458
- status: "failed",
459
- error: result?.error
460
- },
461
- workflowState: {
462
- status: "running",
463
- steps: stepResults,
464
- result: null,
465
- error: null
466
- }
467
- },
468
- eventTimestamp: Date.now()
1070
+ id: step.id,
1071
+ status: "failed",
1072
+ error: result?.error,
1073
+ payload: prevOutput
1074
+ }
469
1075
  });
470
1076
  return { executionContext, result: { status: "failed", error: result?.error } };
471
1077
  } else if (result.status === "suspended") {
@@ -474,50 +1080,27 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
474
1080
  return stepRes2?.status === "suspended";
475
1081
  });
476
1082
  for (const [stepName, stepResult] of suspendedSteps) {
477
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
1083
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
478
1084
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
479
1085
  await emitter.emit("watch", {
480
- type: "watch",
1086
+ type: "workflow-step-suspended",
481
1087
  payload: {
482
- currentStep: {
483
- id: step.id,
484
- status: "suspended",
485
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
486
- },
487
- workflowState: {
488
- status: "running",
489
- steps: stepResults,
490
- result: null,
491
- error: null
492
- }
493
- },
494
- eventTimestamp: Date.now()
1088
+ id: step.id,
1089
+ status: "suspended"
1090
+ }
495
1091
  });
496
1092
  return {
497
1093
  executionContext,
498
1094
  result: {
499
1095
  status: "suspended",
500
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1096
+ payload: stepResult.payload,
1097
+ suspendPayload: {
1098
+ ...stepResult?.suspendPayload,
1099
+ __workflow_meta: { runId, path: suspendPath }
1100
+ }
501
1101
  }
502
1102
  };
503
1103
  }
504
- await emitter.emit("watch", {
505
- type: "watch",
506
- payload: {
507
- currentStep: {
508
- id: step.id,
509
- status: "suspended",
510
- payload: {}
511
- },
512
- workflowState: {
513
- status: "running",
514
- steps: stepResults,
515
- result: null,
516
- error: null
517
- }
518
- },
519
- eventTimestamp: Date.now()
520
- });
521
1104
  return {
522
1105
  executionContext,
523
1106
  result: {
@@ -527,110 +1110,243 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
527
1110
  };
528
1111
  }
529
1112
  await emitter.emit("watch", {
530
- type: "watch",
1113
+ type: "workflow-step-result",
531
1114
  payload: {
532
- currentStep: {
533
- id: step.id,
534
- status: "success",
535
- output: result?.result
536
- },
537
- workflowState: {
538
- status: "running",
539
- steps: stepResults,
540
- result: null,
541
- error: null
542
- }
543
- },
544
- 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
+ }
545
1126
  });
546
1127
  return { executionContext, result: { status: "success", output: result?.result } };
547
1128
  }
548
1129
  );
549
1130
  Object.assign(executionContext, res.executionContext);
550
- 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
+ };
551
1139
  }
552
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
553
- let execResults;
554
- let suspended;
555
- try {
556
- const result = await step.execute({
557
- mastra: this.mastra,
558
- runtimeContext,
559
- inputData: prevOutput,
560
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
561
- getInitData: () => stepResults?.input,
562
- getStepResult: (step2) => {
563
- const result2 = stepResults[step2.id];
564
- if (result2?.status === "success") {
565
- 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
566
1256
  }
567
- return null;
568
- },
569
- suspend: async (suspendPayload) => {
570
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
571
- suspended = { payload: suspendPayload };
572
- },
573
- resume: {
574
- steps: resume?.steps?.slice(1) || [],
575
- resumePayload: resume?.resumePayload,
576
- // @ts-ignore
577
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
578
- },
579
- emitter
580
- });
581
- execResults = { status: "success", output: result };
582
- } catch (e) {
583
- execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
584
- }
585
- if (suspended) {
586
- execResults = { status: "suspended", payload: suspended.payload };
587
- }
588
- if (execResults.status === "failed") {
589
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
590
- 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
+ });
591
1273
  }
592
- }
593
- await emitter.emit("watch", {
594
- type: "watch",
595
- payload: {
596
- currentStep: {
597
- id: step.id,
598
- status: execResults.status,
599
- output: execResults.output
600
- },
601
- workflowState: {
602
- status: "running",
603
- steps: stepResults,
604
- result: null,
605
- error: null
606
- }
607
- },
608
- eventTimestamp: Date.now()
1274
+ stepAISpan?.end({ output: execResults });
1275
+ return { result: execResults, executionContext, stepResults };
609
1276
  });
610
- return { result: execResults, executionContext, stepResults };
611
- });
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
+ }
612
1311
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
613
1312
  Object.assign(stepResults, stepRes.stepResults);
1313
+ executionContext.state = stepRes.executionContext.state;
614
1314
  return stepRes.result;
615
1315
  }
616
1316
  async persistStepUpdate({
617
1317
  workflowId,
618
1318
  runId,
619
1319
  stepResults,
620
- executionContext
1320
+ resourceId,
1321
+ executionContext,
1322
+ serializedStepGraph,
1323
+ workflowStatus,
1324
+ result,
1325
+ error
621
1326
  }) {
622
1327
  await this.inngestStep.run(
623
1328
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
624
1329
  async () => {
1330
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1331
+ if (!shouldPersistSnapshot) {
1332
+ return;
1333
+ }
625
1334
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
626
1335
  workflowName: workflowId,
627
1336
  runId,
1337
+ resourceId,
628
1338
  snapshot: {
629
1339
  runId,
630
- value: {},
1340
+ value: executionContext.state,
631
1341
  context: stepResults,
632
1342
  activePaths: [],
633
1343
  suspendedPaths: executionContext.suspendedPaths,
1344
+ resumeLabels: executionContext.resumeLabels,
1345
+ waitingPaths: {},
1346
+ serializedStepGraph,
1347
+ status: workflowStatus,
1348
+ result,
1349
+ error,
634
1350
  // @ts-ignore
635
1351
  timestamp: Date.now()
636
1352
  }
@@ -643,53 +1359,123 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
643
1359
  runId,
644
1360
  entry,
645
1361
  prevOutput,
646
- prevStep,
647
1362
  stepResults,
648
1363
  resume,
649
1364
  executionContext,
650
1365
  emitter,
651
- runtimeContext
1366
+ abortController,
1367
+ requestContext,
1368
+ writableStream,
1369
+ disableScorers,
1370
+ tracingContext
652
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
+ });
653
1381
  let execResults;
654
1382
  const truthyIndexes = (await Promise.all(
655
1383
  entry.conditions.map(
656
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
+ });
657
1394
  try {
658
- const result = await cond({
659
- mastra: this.mastra,
660
- runtimeContext,
661
- inputData: prevOutput,
662
- getInitData: () => stepResults?.input,
663
- getStepResult: (step) => {
664
- if (!step?.id) {
665
- return null;
666
- }
667
- const result2 = stepResults[step.id];
668
- if (result2?.status === "success") {
669
- 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
670
1441
  }
671
- return null;
672
- },
673
- // TODO: this function shouldn't have suspend probably?
674
- suspend: async (_suspendPayload) => {
675
- },
676
- emitter
1442
+ )
1443
+ );
1444
+ evalSpan?.end({
1445
+ output: result,
1446
+ attributes: {
1447
+ result: !!result
1448
+ }
677
1449
  });
678
1450
  return result ? index : null;
679
1451
  } catch (e) {
1452
+ evalSpan?.error({
1453
+ error: e instanceof Error ? e : new Error(String(e)),
1454
+ attributes: {
1455
+ result: false
1456
+ }
1457
+ });
680
1458
  return null;
681
1459
  }
682
1460
  })
683
1461
  )
684
1462
  )).filter((index) => index !== null);
685
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
+ });
686
1470
  const results = await Promise.all(
687
- stepsToRun.map(
688
- (step, index) => this.executeEntry({
689
- workflowId,
690
- runId,
691
- entry: step,
692
- 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,
693
1479
  stepResults,
694
1480
  resume,
695
1481
  executionContext: {
@@ -697,20 +1483,29 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
697
1483
  runId,
698
1484
  executionPath: [...executionContext.executionPath, index],
699
1485
  suspendedPaths: executionContext.suspendedPaths,
1486
+ resumeLabels: executionContext.resumeLabels,
700
1487
  retryConfig: executionContext.retryConfig,
701
- executionSpan: executionContext.executionSpan
1488
+ state: executionContext.state
702
1489
  },
703
1490
  emitter,
704
- runtimeContext
705
- })
706
- )
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
+ })
707
1502
  );
708
1503
  const hasFailed = results.find((result) => result.status === "failed");
709
1504
  const hasSuspended = results.find((result) => result.status === "suspended");
710
1505
  if (hasFailed) {
711
1506
  execResults = { status: "failed", error: hasFailed.error };
712
1507
  } else if (hasSuspended) {
713
- execResults = { status: "suspended", payload: hasSuspended.payload };
1508
+ execResults = { status: "suspended", suspendPayload: hasSuspended.suspendPayload };
714
1509
  } else {
715
1510
  execResults = {
716
1511
  status: "success",
@@ -722,8 +1517,19 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
722
1517
  }, {})
723
1518
  };
724
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
+ }
725
1529
  return execResults;
726
1530
  }
727
1531
  };
728
1532
 
729
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, init, serve };
1533
+ export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1534
+ //# sourceMappingURL=index.js.map
1535
+ //# sourceMappingURL=index.js.map