@mastra/inngest 0.0.0-redis-cloud-transporter-20250508194049 → 0.0.0-refactor-agent-information-for-recomposable-ui-20251112151814

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