@mastra/inngest 0.0.0-redis-cloud-transporter-20250508203756 → 0.0.0-roamin-openaivoice-speak-options-passing-20250926163614

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
@@ -2,35 +2,51 @@
2
2
 
3
3
  var crypto = require('crypto');
4
4
  var realtime = require('@inngest/realtime');
5
+ var aiTracing = require('@mastra/core/ai-tracing');
5
6
  var di = require('@mastra/core/di');
6
- var vNext = require('@mastra/core/workflows/vNext');
7
+ var tools = require('@mastra/core/tools');
8
+ var workflows = require('@mastra/core/workflows');
9
+ var _constants = require('@mastra/core/workflows/_constants');
7
10
  var hono = require('inngest/hono');
11
+ var zod = require('zod');
8
12
 
9
13
  // 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
- });
14
+ function serve({
15
+ mastra,
16
+ inngest,
17
+ functions: userFunctions = [],
18
+ registerOptions
19
+ }) {
20
+ const wfs = mastra.getWorkflows();
21
+ const workflowFunctions = Array.from(
22
+ new Set(
23
+ Object.values(wfs).flatMap((wf) => {
24
+ if (wf instanceof InngestWorkflow) {
25
+ wf.__registerMastra(mastra);
26
+ return wf.getFunctions();
27
+ }
28
+ return [];
29
+ })
30
+ )
31
+ );
19
32
  return hono.serve({
33
+ ...registerOptions,
20
34
  client: inngest,
21
- functions
35
+ functions: [...workflowFunctions, ...userFunctions]
22
36
  });
23
37
  }
24
- var InngestRun = class extends vNext.Run {
38
+ var InngestRun = class extends workflows.Run {
25
39
  inngest;
40
+ serializedStepGraph;
26
41
  #mastra;
27
42
  constructor(params, inngest) {
28
43
  super(params);
29
44
  this.inngest = inngest;
45
+ this.serializedStepGraph = params.serializedStepGraph;
30
46
  this.#mastra = params.mastra;
31
47
  }
32
48
  async getRuns(eventId) {
33
- const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
49
+ const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
34
50
  headers: {
35
51
  Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
36
52
  }
@@ -40,35 +56,76 @@ var InngestRun = class extends vNext.Run {
40
56
  }
41
57
  async getRunOutput(eventId) {
42
58
  let runs = await this.getRuns(eventId);
43
- while (runs?.[0]?.status !== "Completed") {
59
+ while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
44
60
  await new Promise((resolve) => setTimeout(resolve, 1e3));
45
61
  runs = await this.getRuns(eventId);
46
- if (runs?.[0]?.status === "Failed" || runs?.[0]?.status === "Cancelled") {
62
+ if (runs?.[0]?.status === "Failed") {
47
63
  throw new Error(`Function run ${runs?.[0]?.status}`);
64
+ } else if (runs?.[0]?.status === "Cancelled") {
65
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
66
+ workflowName: this.workflowId,
67
+ runId: this.runId
68
+ });
69
+ return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
48
70
  }
49
71
  }
50
72
  return runs?.[0];
51
73
  }
74
+ async sendEvent(event, data) {
75
+ await this.inngest.send({
76
+ name: `user-event-${event}`,
77
+ data
78
+ });
79
+ }
80
+ async cancel() {
81
+ await this.inngest.send({
82
+ name: `cancel.workflow.${this.workflowId}`,
83
+ data: {
84
+ runId: this.runId
85
+ }
86
+ });
87
+ const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
88
+ workflowName: this.workflowId,
89
+ runId: this.runId
90
+ });
91
+ if (snapshot) {
92
+ await this.#mastra?.storage?.persistWorkflowSnapshot({
93
+ workflowName: this.workflowId,
94
+ runId: this.runId,
95
+ resourceId: this.resourceId,
96
+ snapshot: {
97
+ ...snapshot,
98
+ status: "canceled"
99
+ }
100
+ });
101
+ }
102
+ }
52
103
  async start({
53
104
  inputData
54
105
  }) {
55
106
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
56
107
  workflowName: this.workflowId,
57
108
  runId: this.runId,
109
+ resourceId: this.resourceId,
58
110
  snapshot: {
59
111
  runId: this.runId,
112
+ serializedStepGraph: this.serializedStepGraph,
60
113
  value: {},
61
114
  context: {},
62
115
  activePaths: [],
63
116
  suspendedPaths: {},
64
- timestamp: Date.now()
117
+ waitingPaths: {},
118
+ timestamp: Date.now(),
119
+ status: "running"
65
120
  }
66
121
  });
122
+ const inputDataToUse = await this._validateInput(inputData);
67
123
  const eventOutput = await this.inngest.send({
68
124
  name: `workflow.${this.workflowId}`,
69
125
  data: {
70
- inputData,
71
- runId: this.runId
126
+ inputData: inputDataToUse,
127
+ runId: this.runId,
128
+ resourceId: this.resourceId
72
129
  }
73
130
  });
74
131
  const eventId = eventOutput.ids[0];
@@ -80,10 +137,23 @@ var InngestRun = class extends vNext.Run {
80
137
  if (result.status === "failed") {
81
138
  result.error = new Error(result.error);
82
139
  }
83
- this.cleanup?.();
140
+ if (result.status !== "suspended") {
141
+ this.cleanup?.();
142
+ }
84
143
  return result;
85
144
  }
86
145
  async resume(params) {
146
+ const p = this._resume(params).then((result) => {
147
+ if (result.status !== "suspended") {
148
+ this.closeStreamAction?.().catch(() => {
149
+ });
150
+ }
151
+ return result;
152
+ });
153
+ this.executionResults = p;
154
+ return p;
155
+ }
156
+ async _resume(params) {
87
157
  const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
88
158
  (step) => typeof step === "string" ? step : step?.id
89
159
  );
@@ -91,16 +161,19 @@ var InngestRun = class extends vNext.Run {
91
161
  workflowName: this.workflowId,
92
162
  runId: this.runId
93
163
  });
164
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
165
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
94
166
  const eventOutput = await this.inngest.send({
95
167
  name: `workflow.${this.workflowId}`,
96
168
  data: {
97
- inputData: params.resumeData,
169
+ inputData: resumeDataToUse,
98
170
  runId: this.runId,
171
+ workflowId: this.workflowId,
99
172
  stepResults: snapshot?.context,
100
173
  resume: {
101
174
  steps,
102
175
  stepResults: snapshot?.context,
103
- resumePayload: params.resumeData,
176
+ resumePayload: resumeDataToUse,
104
177
  // @ts-ignore
105
178
  resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
106
179
  }
@@ -117,32 +190,77 @@ var InngestRun = class extends vNext.Run {
117
190
  }
118
191
  return result;
119
192
  }
120
- watch(cb) {
193
+ watch(cb, type = "watch") {
194
+ let active = true;
121
195
  const streamPromise = realtime.subscribe(
122
196
  {
123
197
  channel: `workflow:${this.workflowId}:${this.runId}`,
124
- topics: ["watch"],
198
+ topics: [type],
125
199
  app: this.inngest
126
200
  },
127
201
  (message) => {
128
- cb(message.data);
202
+ if (active) {
203
+ cb(message.data);
204
+ }
129
205
  }
130
206
  );
131
207
  return () => {
132
- streamPromise.then((stream) => {
133
- stream.cancel();
208
+ active = false;
209
+ streamPromise.then(async (stream) => {
210
+ return stream.cancel();
134
211
  }).catch((err) => {
135
212
  console.error(err);
136
213
  });
137
214
  };
138
215
  }
216
+ stream({ inputData, runtimeContext } = {}) {
217
+ const { readable, writable } = new TransformStream();
218
+ const writer = writable.getWriter();
219
+ const unwatch = this.watch(async (event) => {
220
+ try {
221
+ const e = {
222
+ ...event,
223
+ type: event.type.replace("workflow-", "")
224
+ };
225
+ await writer.write(e);
226
+ } catch {
227
+ }
228
+ }, "watch-v2");
229
+ this.closeStreamAction = async () => {
230
+ unwatch();
231
+ try {
232
+ await writer.close();
233
+ } catch (err) {
234
+ console.error("Error closing stream:", err);
235
+ } finally {
236
+ writer.releaseLock();
237
+ }
238
+ };
239
+ this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
240
+ if (result.status !== "suspended") {
241
+ this.closeStreamAction?.().catch(() => {
242
+ });
243
+ }
244
+ return result;
245
+ });
246
+ return {
247
+ stream: readable,
248
+ getWorkflowState: () => this.executionResults
249
+ };
250
+ }
139
251
  };
140
- var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
252
+ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
141
253
  #mastra;
142
254
  inngest;
143
255
  function;
256
+ flowControlConfig;
144
257
  constructor(params, inngest) {
145
- super(params);
258
+ const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
259
+ super(workflowParams);
260
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
261
+ ([_, value]) => value !== void 0
262
+ );
263
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
146
264
  this.#mastra = params.mastra;
147
265
  this.inngest = inngest;
148
266
  }
@@ -158,7 +276,7 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
158
276
  const storage = this.#mastra?.getStorage();
159
277
  if (!storage) {
160
278
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
161
- return null;
279
+ return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
162
280
  }
163
281
  const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
164
282
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
@@ -181,21 +299,55 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
181
299
  }
182
300
  }
183
301
  }
184
- createRun(options) {
302
+ /**
303
+ * @deprecated Use createRunAsync() instead.
304
+ * @throws {Error} Always throws an error directing users to use createRunAsync()
305
+ */
306
+ createRun(_options) {
307
+ throw new Error(
308
+ "createRun() has been deprecated. Please use createRunAsync() instead.\n\nMigration guide:\n Before: const run = workflow.createRun();\n After: const run = await workflow.createRunAsync();\n\nNote: createRunAsync() is an async method, so make sure your calling function is async."
309
+ );
310
+ }
311
+ async createRunAsync(options) {
185
312
  const runIdToUse = options?.runId || crypto.randomUUID();
186
313
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
187
314
  {
188
315
  workflowId: this.id,
189
316
  runId: runIdToUse,
317
+ resourceId: options?.resourceId,
190
318
  executionEngine: this.executionEngine,
191
319
  executionGraph: this.executionGraph,
320
+ serializedStepGraph: this.serializedStepGraph,
192
321
  mastra: this.#mastra,
193
322
  retryConfig: this.retryConfig,
194
- cleanup: () => this.runs.delete(runIdToUse)
323
+ cleanup: () => this.runs.delete(runIdToUse),
324
+ workflowSteps: this.steps
195
325
  },
196
326
  this.inngest
197
327
  );
198
328
  this.runs.set(runIdToUse, run);
329
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
330
+ if (!workflowSnapshotInStorage) {
331
+ await this.mastra?.getStorage()?.persistWorkflowSnapshot({
332
+ workflowName: this.id,
333
+ runId: runIdToUse,
334
+ resourceId: options?.resourceId,
335
+ snapshot: {
336
+ runId: runIdToUse,
337
+ status: "pending",
338
+ value: {},
339
+ context: {},
340
+ activePaths: [],
341
+ waitingPaths: {},
342
+ serializedStepGraph: this.serializedStepGraph,
343
+ suspendedPaths: {},
344
+ result: void 0,
345
+ error: void 0,
346
+ // @ts-ignore
347
+ timestamp: Date.now()
348
+ }
349
+ });
350
+ }
199
351
  return run;
200
352
  }
201
353
  getFunction() {
@@ -203,11 +355,17 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
203
355
  return this.function;
204
356
  }
205
357
  this.function = this.inngest.createFunction(
206
- // @ts-ignore
207
- { id: `workflow.${this.id}`, retries: this.retryConfig?.attempts ?? 0 },
358
+ {
359
+ id: `workflow.${this.id}`,
360
+ // @ts-ignore
361
+ retries: this.retryConfig?.attempts ?? 0,
362
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
363
+ // Spread flow control configuration
364
+ ...this.flowControlConfig
365
+ },
208
366
  { event: `workflow.${this.id}` },
209
367
  async ({ event, step, attempt, publish }) => {
210
- let { inputData, runId, resume } = event.data;
368
+ let { inputData, runId, resourceId, resume } = event.data;
211
369
  if (!runId) {
212
370
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
213
371
  return crypto.randomUUID();
@@ -221,25 +379,36 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
221
379
  try {
222
380
  await publish({
223
381
  channel: `workflow:${this.id}:${runId}`,
224
- topic: "watch",
382
+ topic: event2,
225
383
  data
226
384
  });
227
385
  } catch (err) {
228
386
  this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
229
387
  }
388
+ },
389
+ on: (_event, _callback) => {
390
+ },
391
+ off: (_event, _callback) => {
392
+ },
393
+ once: (_event, _callback) => {
230
394
  }
231
395
  };
232
396
  const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
233
397
  const result = await engine.execute({
234
398
  workflowId: this.id,
235
399
  runId,
400
+ resourceId,
236
401
  graph: this.executionGraph,
402
+ serializedStepGraph: this.serializedStepGraph,
237
403
  input: inputData,
238
404
  emitter,
239
405
  retryConfig: this.retryConfig,
240
406
  runtimeContext: new di.RuntimeContext(),
241
407
  // TODO
242
- resume
408
+ resume,
409
+ abortController: new AbortController(),
410
+ currentSpan: void 0
411
+ // TODO: Pass actual parent AI span from workflow execution context
243
412
  });
244
413
  return { result, runId };
245
414
  }
@@ -263,39 +432,154 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
263
432
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
264
433
  }
265
434
  };
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;
435
+ function isAgent(params) {
436
+ return params?.component === "AGENT";
437
+ }
438
+ function isTool(params) {
439
+ return params instanceof tools.Tool;
440
+ }
441
+ function createStep(params) {
442
+ if (isAgent(params)) {
443
+ return {
444
+ id: params.name,
445
+ description: params.getDescription(),
446
+ // @ts-ignore
447
+ inputSchema: zod.z.object({
448
+ prompt: zod.z.string()
449
+ }),
450
+ // @ts-ignore
451
+ outputSchema: zod.z.object({
452
+ text: zod.z.string()
453
+ }),
454
+ execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
455
+ let streamPromise = {};
456
+ streamPromise.promise = new Promise((resolve, reject) => {
457
+ streamPromise.resolve = resolve;
458
+ streamPromise.reject = reject;
459
+ });
460
+ const toolData = {
461
+ name: params.name,
462
+ args: inputData
463
+ };
464
+ const { fullStream } = await params.stream(inputData.prompt, {
465
+ runtimeContext,
466
+ tracingContext,
467
+ onFinish: (result) => {
468
+ streamPromise.resolve(result.text);
469
+ },
470
+ abortSignal
471
+ });
472
+ if (abortSignal.aborted) {
473
+ return abort();
474
+ }
475
+ await emitter.emit("watch-v2", {
476
+ type: "tool-call-streaming-start",
477
+ ...toolData ?? {}
478
+ });
479
+ for await (const chunk of fullStream) {
480
+ if (chunk.type === "text-delta") {
481
+ await emitter.emit("watch-v2", {
482
+ type: "tool-call-delta",
483
+ ...toolData ?? {},
484
+ argsTextDelta: chunk.textDelta
485
+ });
486
+ }
487
+ }
488
+ await emitter.emit("watch-v2", {
489
+ type: "tool-call-streaming-finish",
490
+ ...toolData ?? {}
491
+ });
492
+ return {
493
+ text: await streamPromise.promise
494
+ };
495
+ },
496
+ component: params.component
497
+ };
498
+ }
499
+ if (isTool(params)) {
500
+ if (!params.inputSchema || !params.outputSchema) {
501
+ throw new Error("Tool must have input and output schemas defined");
502
+ }
503
+ return {
504
+ // TODO: tool probably should have strong id type
505
+ // @ts-ignore
506
+ id: params.id,
507
+ description: params.description,
508
+ inputSchema: params.inputSchema,
509
+ outputSchema: params.outputSchema,
510
+ execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
511
+ return params.execute({
512
+ context: inputData,
513
+ mastra: aiTracing.wrapMastra(mastra, tracingContext),
514
+ runtimeContext,
515
+ tracingContext,
516
+ suspend,
517
+ resumeData
518
+ });
519
+ },
520
+ component: "TOOL"
521
+ };
522
+ }
523
+ return {
524
+ id: params.id,
525
+ description: params.description,
526
+ inputSchema: params.inputSchema,
527
+ outputSchema: params.outputSchema,
528
+ resumeSchema: params.resumeSchema,
529
+ suspendSchema: params.suspendSchema,
530
+ execute: params.execute
531
+ };
280
532
  }
281
533
  function init(inngest) {
282
534
  return {
283
535
  createWorkflow(params) {
284
536
  return new InngestWorkflow(params, inngest);
285
537
  },
286
- createStep: vNext.createStep,
287
- cloneStep: vNext.cloneStep,
288
- cloneWorkflow
538
+ createStep,
539
+ cloneStep(step, opts) {
540
+ return {
541
+ id: opts.id,
542
+ description: step.description,
543
+ inputSchema: step.inputSchema,
544
+ outputSchema: step.outputSchema,
545
+ execute: step.execute,
546
+ component: step.component
547
+ };
548
+ },
549
+ cloneWorkflow(workflow, opts) {
550
+ const wf = new workflows.Workflow({
551
+ id: opts.id,
552
+ inputSchema: workflow.inputSchema,
553
+ outputSchema: workflow.outputSchema,
554
+ steps: workflow.stepDefs,
555
+ mastra: workflow.mastra
556
+ });
557
+ wf.setStepFlow(workflow.stepGraph);
558
+ wf.commit();
559
+ return wf;
560
+ }
289
561
  };
290
562
  }
291
- var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
563
+ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
292
564
  inngestStep;
293
565
  inngestAttempts;
294
- constructor(mastra, inngestStep, inngestAttempts = 0) {
295
- super({ mastra });
566
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
567
+ super({ mastra, options });
296
568
  this.inngestStep = inngestStep;
297
569
  this.inngestAttempts = inngestAttempts;
298
570
  }
571
+ async execute(params) {
572
+ await params.emitter.emit("watch-v2", {
573
+ type: "workflow-start",
574
+ payload: { runId: params.runId }
575
+ });
576
+ const result = await super.execute(params);
577
+ await params.emitter.emit("watch-v2", {
578
+ type: "workflow-finish",
579
+ payload: { runId: params.runId }
580
+ });
581
+ return result;
582
+ }
299
583
  async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
300
584
  const base = {
301
585
  status: lastOutput.status,
@@ -353,28 +637,179 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
353
637
  executionSpan?.end();
354
638
  return base;
355
639
  }
356
- async superExecuteStep({
640
+ // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
641
+ // await this.inngestStep.sleep(id, duration);
642
+ // }
643
+ async executeSleep({
357
644
  workflowId,
358
645
  runId,
359
- step,
646
+ entry,
647
+ prevOutput,
360
648
  stepResults,
649
+ emitter,
650
+ abortController,
651
+ runtimeContext,
361
652
  executionContext,
362
- resume,
653
+ writableStream,
654
+ tracingContext
655
+ }) {
656
+ let { duration, fn } = entry;
657
+ const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
658
+ type: aiTracing.AISpanType.WORKFLOW_SLEEP,
659
+ name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
660
+ attributes: {
661
+ durationMs: duration,
662
+ sleepType: fn ? "dynamic" : "fixed"
663
+ },
664
+ tracingPolicy: this.options?.tracingPolicy
665
+ });
666
+ if (fn) {
667
+ const stepCallId = crypto.randomUUID();
668
+ duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
669
+ return await fn({
670
+ runId,
671
+ workflowId,
672
+ mastra: this.mastra,
673
+ runtimeContext,
674
+ inputData: prevOutput,
675
+ runCount: -1,
676
+ tracingContext: {
677
+ currentSpan: sleepSpan
678
+ },
679
+ getInitData: () => stepResults?.input,
680
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
681
+ // TODO: this function shouldn't have suspend probably?
682
+ suspend: async (_suspendPayload) => {
683
+ },
684
+ bail: () => {
685
+ },
686
+ abort: () => {
687
+ abortController?.abort();
688
+ },
689
+ [_constants.EMITTER_SYMBOL]: emitter,
690
+ // TODO: add streamVNext support
691
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
692
+ engine: { step: this.inngestStep },
693
+ abortSignal: abortController?.signal,
694
+ writer: new tools.ToolStream(
695
+ {
696
+ prefix: "workflow-step",
697
+ callId: stepCallId,
698
+ name: "sleep",
699
+ runId
700
+ },
701
+ writableStream
702
+ )
703
+ });
704
+ });
705
+ sleepSpan?.update({
706
+ attributes: {
707
+ durationMs: duration
708
+ }
709
+ });
710
+ }
711
+ try {
712
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
713
+ sleepSpan?.end();
714
+ } catch (e) {
715
+ sleepSpan?.error({ error: e });
716
+ throw e;
717
+ }
718
+ }
719
+ async executeSleepUntil({
720
+ workflowId,
721
+ runId,
722
+ entry,
363
723
  prevOutput,
724
+ stepResults,
364
725
  emitter,
365
- runtimeContext
726
+ abortController,
727
+ runtimeContext,
728
+ executionContext,
729
+ writableStream,
730
+ tracingContext
366
731
  }) {
367
- return super.executeStep({
368
- workflowId,
369
- runId,
370
- step,
371
- stepResults,
372
- executionContext,
373
- resume,
374
- prevOutput,
375
- emitter,
376
- runtimeContext
732
+ let { date, fn } = entry;
733
+ const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
734
+ type: aiTracing.AISpanType.WORKFLOW_SLEEP,
735
+ name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
736
+ attributes: {
737
+ untilDate: date,
738
+ durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
739
+ sleepType: fn ? "dynamic" : "fixed"
740
+ },
741
+ tracingPolicy: this.options?.tracingPolicy
742
+ });
743
+ if (fn) {
744
+ date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
745
+ const stepCallId = crypto.randomUUID();
746
+ return await fn({
747
+ runId,
748
+ workflowId,
749
+ mastra: this.mastra,
750
+ runtimeContext,
751
+ inputData: prevOutput,
752
+ runCount: -1,
753
+ tracingContext: {
754
+ currentSpan: sleepUntilSpan
755
+ },
756
+ getInitData: () => stepResults?.input,
757
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
758
+ // TODO: this function shouldn't have suspend probably?
759
+ suspend: async (_suspendPayload) => {
760
+ },
761
+ bail: () => {
762
+ },
763
+ abort: () => {
764
+ abortController?.abort();
765
+ },
766
+ [_constants.EMITTER_SYMBOL]: emitter,
767
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
768
+ // TODO: add streamVNext support
769
+ engine: { step: this.inngestStep },
770
+ abortSignal: abortController?.signal,
771
+ writer: new tools.ToolStream(
772
+ {
773
+ prefix: "workflow-step",
774
+ callId: stepCallId,
775
+ name: "sleep",
776
+ runId
777
+ },
778
+ writableStream
779
+ )
780
+ });
781
+ });
782
+ if (date && !(date instanceof Date)) {
783
+ date = new Date(date);
784
+ }
785
+ const time = !date ? 0 : date.getTime() - Date.now();
786
+ sleepUntilSpan?.update({
787
+ attributes: {
788
+ durationMs: Math.max(0, time)
789
+ }
790
+ });
791
+ }
792
+ if (!(date instanceof Date)) {
793
+ sleepUntilSpan?.end();
794
+ return;
795
+ }
796
+ try {
797
+ await this.inngestStep.sleepUntil(entry.id, date);
798
+ sleepUntilSpan?.end();
799
+ } catch (e) {
800
+ sleepUntilSpan?.error({ error: e });
801
+ throw e;
802
+ }
803
+ }
804
+ async executeWaitForEvent({ event, timeout }) {
805
+ const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
806
+ event: `user-event-${event}`,
807
+ timeout: timeout ?? 5e3
377
808
  });
809
+ if (eventData === null) {
810
+ throw "Timeout waiting for event";
811
+ }
812
+ return eventData?.data;
378
813
  }
379
814
  async executeStep({
380
815
  step,
@@ -383,11 +818,30 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
383
818
  resume,
384
819
  prevOutput,
385
820
  emitter,
386
- runtimeContext
821
+ abortController,
822
+ runtimeContext,
823
+ tracingContext,
824
+ writableStream,
825
+ disableScorers
387
826
  }) {
388
- await this.inngestStep.run(
827
+ const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
828
+ name: `workflow step: '${step.id}'`,
829
+ type: aiTracing.AISpanType.WORKFLOW_STEP,
830
+ input: prevOutput,
831
+ attributes: {
832
+ stepId: step.id
833
+ },
834
+ tracingPolicy: this.options?.tracingPolicy
835
+ });
836
+ const { inputData, validationError } = await workflows.validateStepInput({
837
+ prevOutput,
838
+ step,
839
+ validateInputs: this.options?.validateInputs ?? false
840
+ });
841
+ const startedAt = await this.inngestStep.run(
389
842
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
390
843
  async () => {
844
+ const startedAt2 = Date.now();
391
845
  await emitter.emit("watch", {
392
846
  type: "watch",
393
847
  payload: {
@@ -409,6 +863,16 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
409
863
  },
410
864
  eventTimestamp: Date.now()
411
865
  });
866
+ await emitter.emit("watch-v2", {
867
+ type: "workflow-step-start",
868
+ payload: {
869
+ id: step.id,
870
+ status: "running",
871
+ payload: inputData,
872
+ startedAt: startedAt2
873
+ }
874
+ });
875
+ return startedAt2;
412
876
  }
413
877
  );
414
878
  if (step instanceof InngestWorkflow) {
@@ -424,7 +888,7 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
424
888
  const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
425
889
  function: step.getFunction(),
426
890
  data: {
427
- inputData: prevOutput,
891
+ inputData,
428
892
  runId,
429
893
  resume: {
430
894
  runId,
@@ -442,7 +906,7 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
442
906
  const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
443
907
  function: step.getFunction(),
444
908
  data: {
445
- inputData: prevOutput
909
+ inputData
446
910
  }
447
911
  });
448
912
  result = invokeResp.result;
@@ -469,6 +933,15 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
469
933
  },
470
934
  eventTimestamp: Date.now()
471
935
  });
936
+ await emitter.emit("watch-v2", {
937
+ type: "workflow-step-result",
938
+ payload: {
939
+ id: step.id,
940
+ status: "failed",
941
+ error: result?.error,
942
+ payload: prevOutput
943
+ }
944
+ });
472
945
  return { executionContext, result: { status: "failed", error: result?.error } };
473
946
  } else if (result.status === "suspended") {
474
947
  const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
@@ -495,6 +968,13 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
495
968
  },
496
969
  eventTimestamp: Date.now()
497
970
  });
971
+ await emitter.emit("watch-v2", {
972
+ type: "workflow-step-suspended",
973
+ payload: {
974
+ id: step.id,
975
+ status: "suspended"
976
+ }
977
+ });
498
978
  return {
499
979
  executionContext,
500
980
  result: {
@@ -545,6 +1025,21 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
545
1025
  },
546
1026
  eventTimestamp: Date.now()
547
1027
  });
1028
+ await emitter.emit("watch-v2", {
1029
+ type: "workflow-step-result",
1030
+ payload: {
1031
+ id: step.id,
1032
+ status: "success",
1033
+ output: result?.result
1034
+ }
1035
+ });
1036
+ await emitter.emit("watch-v2", {
1037
+ type: "workflow-step-finish",
1038
+ payload: {
1039
+ id: step.id,
1040
+ metadata: {}
1041
+ }
1042
+ });
548
1043
  return { executionContext, result: { status: "success", output: result?.result } };
549
1044
  }
550
1045
  );
@@ -554,42 +1049,81 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
554
1049
  const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
555
1050
  let execResults;
556
1051
  let suspended;
1052
+ let bailed;
557
1053
  try {
1054
+ if (validationError) {
1055
+ throw validationError;
1056
+ }
558
1057
  const result = await step.execute({
1058
+ runId: executionContext.runId,
559
1059
  mastra: this.mastra,
560
1060
  runtimeContext,
561
- inputData: prevOutput,
1061
+ writableStream,
1062
+ inputData,
562
1063
  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;
568
- }
569
- return null;
1064
+ tracingContext: {
1065
+ currentSpan: stepAISpan
570
1066
  },
1067
+ getInitData: () => stepResults?.input,
1068
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
571
1069
  suspend: async (suspendPayload) => {
572
1070
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
573
1071
  suspended = { payload: suspendPayload };
574
1072
  },
1073
+ bail: (result2) => {
1074
+ bailed = { payload: result2 };
1075
+ },
575
1076
  resume: {
576
1077
  steps: resume?.steps?.slice(1) || [],
577
1078
  resumePayload: resume?.resumePayload,
578
1079
  // @ts-ignore
579
1080
  runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
580
1081
  },
581
- emitter
1082
+ [_constants.EMITTER_SYMBOL]: emitter,
1083
+ engine: {
1084
+ step: this.inngestStep
1085
+ },
1086
+ abortSignal: abortController.signal
582
1087
  });
583
- execResults = { status: "success", output: result };
1088
+ const endedAt = Date.now();
1089
+ execResults = {
1090
+ status: "success",
1091
+ output: result,
1092
+ startedAt,
1093
+ endedAt,
1094
+ payload: inputData,
1095
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1096
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1097
+ };
584
1098
  } catch (e) {
585
- execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
1099
+ execResults = {
1100
+ status: "failed",
1101
+ payload: inputData,
1102
+ error: e instanceof Error ? e.message : String(e),
1103
+ endedAt: Date.now(),
1104
+ startedAt,
1105
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1106
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1107
+ };
586
1108
  }
587
1109
  if (suspended) {
588
- execResults = { status: "suspended", payload: suspended.payload };
1110
+ execResults = {
1111
+ status: "suspended",
1112
+ suspendedPayload: suspended.payload,
1113
+ payload: inputData,
1114
+ suspendedAt: Date.now(),
1115
+ startedAt,
1116
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1117
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1118
+ };
1119
+ } else if (bailed) {
1120
+ execResults = { status: "bailed", output: bailed.payload, payload: inputData, endedAt: Date.now(), startedAt };
589
1121
  }
590
1122
  if (execResults.status === "failed") {
591
1123
  if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
592
- throw execResults.error;
1124
+ const error = new Error(execResults.error);
1125
+ stepAISpan?.error({ error });
1126
+ throw error;
593
1127
  }
594
1128
  }
595
1129
  await emitter.emit("watch", {
@@ -597,20 +1131,61 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
597
1131
  payload: {
598
1132
  currentStep: {
599
1133
  id: step.id,
600
- status: execResults.status,
601
- output: execResults.output
1134
+ ...execResults
602
1135
  },
603
1136
  workflowState: {
604
1137
  status: "running",
605
- steps: stepResults,
1138
+ steps: { ...stepResults, [step.id]: execResults },
606
1139
  result: null,
607
1140
  error: null
608
1141
  }
609
1142
  },
610
1143
  eventTimestamp: Date.now()
611
1144
  });
1145
+ if (execResults.status === "suspended") {
1146
+ await emitter.emit("watch-v2", {
1147
+ type: "workflow-step-suspended",
1148
+ payload: {
1149
+ id: step.id,
1150
+ ...execResults
1151
+ }
1152
+ });
1153
+ } else {
1154
+ await emitter.emit("watch-v2", {
1155
+ type: "workflow-step-result",
1156
+ payload: {
1157
+ id: step.id,
1158
+ ...execResults
1159
+ }
1160
+ });
1161
+ await emitter.emit("watch-v2", {
1162
+ type: "workflow-step-finish",
1163
+ payload: {
1164
+ id: step.id,
1165
+ metadata: {}
1166
+ }
1167
+ });
1168
+ }
1169
+ stepAISpan?.end({ output: execResults });
612
1170
  return { result: execResults, executionContext, stepResults };
613
1171
  });
1172
+ if (disableScorers !== false) {
1173
+ await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1174
+ if (step.scorers) {
1175
+ await this.runScorers({
1176
+ scorers: step.scorers,
1177
+ runId: executionContext.runId,
1178
+ input: inputData,
1179
+ output: stepRes.result,
1180
+ workflowId: executionContext.workflowId,
1181
+ stepId: step.id,
1182
+ runtimeContext,
1183
+ disableScorers,
1184
+ tracingContext: { currentSpan: stepAISpan }
1185
+ });
1186
+ }
1187
+ });
1188
+ }
614
1189
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
615
1190
  Object.assign(stepResults, stepRes.stepResults);
616
1191
  return stepRes.result;
@@ -619,7 +1194,12 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
619
1194
  workflowId,
620
1195
  runId,
621
1196
  stepResults,
622
- executionContext
1197
+ resourceId,
1198
+ executionContext,
1199
+ serializedStepGraph,
1200
+ workflowStatus,
1201
+ result,
1202
+ error
623
1203
  }) {
624
1204
  await this.inngestStep.run(
625
1205
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
@@ -627,12 +1207,18 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
627
1207
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
628
1208
  workflowName: workflowId,
629
1209
  runId,
1210
+ resourceId,
630
1211
  snapshot: {
631
1212
  runId,
632
1213
  value: {},
633
1214
  context: stepResults,
634
1215
  activePaths: [],
635
1216
  suspendedPaths: executionContext.suspendedPaths,
1217
+ waitingPaths: {},
1218
+ serializedStepGraph,
1219
+ status: workflowStatus,
1220
+ result,
1221
+ error,
636
1222
  // @ts-ignore
637
1223
  timestamp: Date.now()
638
1224
  }
@@ -647,50 +1233,109 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
647
1233
  prevOutput,
648
1234
  prevStep,
649
1235
  stepResults,
1236
+ serializedStepGraph,
650
1237
  resume,
651
1238
  executionContext,
652
1239
  emitter,
653
- runtimeContext
1240
+ abortController,
1241
+ runtimeContext,
1242
+ writableStream,
1243
+ disableScorers,
1244
+ tracingContext
654
1245
  }) {
1246
+ const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1247
+ type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
1248
+ name: `conditional: '${entry.conditions.length} conditions'`,
1249
+ input: prevOutput,
1250
+ attributes: {
1251
+ conditionCount: entry.conditions.length
1252
+ },
1253
+ tracingPolicy: this.options?.tracingPolicy
1254
+ });
655
1255
  let execResults;
656
1256
  const truthyIndexes = (await Promise.all(
657
1257
  entry.conditions.map(
658
1258
  (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1259
+ const evalSpan = conditionalSpan?.createChildSpan({
1260
+ type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1261
+ name: `condition: '${index}'`,
1262
+ input: prevOutput,
1263
+ attributes: {
1264
+ conditionIndex: index
1265
+ },
1266
+ tracingPolicy: this.options?.tracingPolicy
1267
+ });
659
1268
  try {
660
1269
  const result = await cond({
1270
+ runId,
1271
+ workflowId,
661
1272
  mastra: this.mastra,
662
1273
  runtimeContext,
1274
+ runCount: -1,
663
1275
  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;
672
- }
673
- return null;
1276
+ tracingContext: {
1277
+ currentSpan: evalSpan
674
1278
  },
1279
+ getInitData: () => stepResults?.input,
1280
+ getStepResult: workflows.getStepResult.bind(this, stepResults),
675
1281
  // TODO: this function shouldn't have suspend probably?
676
1282
  suspend: async (_suspendPayload) => {
677
1283
  },
678
- emitter
1284
+ bail: () => {
1285
+ },
1286
+ abort: () => {
1287
+ abortController.abort();
1288
+ },
1289
+ [_constants.EMITTER_SYMBOL]: emitter,
1290
+ [_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
1291
+ // TODO: add streamVNext support
1292
+ engine: {
1293
+ step: this.inngestStep
1294
+ },
1295
+ abortSignal: abortController.signal,
1296
+ writer: new tools.ToolStream(
1297
+ {
1298
+ prefix: "workflow-step",
1299
+ callId: crypto.randomUUID(),
1300
+ name: "conditional",
1301
+ runId
1302
+ },
1303
+ writableStream
1304
+ )
1305
+ });
1306
+ evalSpan?.end({
1307
+ output: result,
1308
+ attributes: {
1309
+ result: !!result
1310
+ }
679
1311
  });
680
1312
  return result ? index : null;
681
1313
  } catch (e) {
1314
+ evalSpan?.error({
1315
+ error: e instanceof Error ? e : new Error(String(e)),
1316
+ attributes: {
1317
+ result: false
1318
+ }
1319
+ });
682
1320
  return null;
683
1321
  }
684
1322
  })
685
1323
  )
686
1324
  )).filter((index) => index !== null);
687
1325
  const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1326
+ conditionalSpan?.update({
1327
+ attributes: {
1328
+ truthyIndexes,
1329
+ selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1330
+ }
1331
+ });
688
1332
  const results = await Promise.all(
689
1333
  stepsToRun.map(
690
1334
  (step, index) => this.executeEntry({
691
1335
  workflowId,
692
1336
  runId,
693
1337
  entry: step,
1338
+ serializedStepGraph,
694
1339
  prevStep,
695
1340
  stepResults,
696
1341
  resume,
@@ -703,27 +1348,42 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
703
1348
  executionSpan: executionContext.executionSpan
704
1349
  },
705
1350
  emitter,
706
- runtimeContext
1351
+ abortController,
1352
+ runtimeContext,
1353
+ writableStream,
1354
+ disableScorers,
1355
+ tracingContext: {
1356
+ currentSpan: conditionalSpan
1357
+ }
707
1358
  })
708
1359
  )
709
1360
  );
710
- const hasFailed = results.find((result) => result.status === "failed");
711
- const hasSuspended = results.find((result) => result.status === "suspended");
1361
+ const hasFailed = results.find((result) => result.result.status === "failed");
1362
+ const hasSuspended = results.find((result) => result.result.status === "suspended");
712
1363
  if (hasFailed) {
713
- execResults = { status: "failed", error: hasFailed.error };
1364
+ execResults = { status: "failed", error: hasFailed.result.error };
714
1365
  } else if (hasSuspended) {
715
- execResults = { status: "suspended", payload: hasSuspended.payload };
1366
+ execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
716
1367
  } else {
717
1368
  execResults = {
718
1369
  status: "success",
719
1370
  output: results.reduce((acc, result, index) => {
720
- if (result.status === "success") {
1371
+ if (result.result.status === "success") {
721
1372
  acc[stepsToRun[index].step.id] = result.output;
722
1373
  }
723
1374
  return acc;
724
1375
  }, {})
725
1376
  };
726
1377
  }
1378
+ if (execResults.status === "failed") {
1379
+ conditionalSpan?.error({
1380
+ error: new Error(execResults.error)
1381
+ });
1382
+ } else {
1383
+ conditionalSpan?.end({
1384
+ output: execResults.output || execResults
1385
+ });
1386
+ }
727
1387
  return execResults;
728
1388
  }
729
1389
  };
@@ -731,5 +1391,8 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
731
1391
  exports.InngestExecutionEngine = InngestExecutionEngine;
732
1392
  exports.InngestRun = InngestRun;
733
1393
  exports.InngestWorkflow = InngestWorkflow;
1394
+ exports.createStep = createStep;
734
1395
  exports.init = init;
735
1396
  exports.serve = serve;
1397
+ //# sourceMappingURL=index.cjs.map
1398
+ //# sourceMappingURL=index.cjs.map