@mastra/inngest 0.0.0-cloud-transporter-20250513033346 → 0.0.0-cloud-storage-adapter-20251106204059

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