@mastra/inngest 0.0.0-cloud-transporter-20250513033346 → 0.0.0-consolidate-changesets-20250904042643

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