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