@mastra/inngest 0.0.0-generate-message-id-20250512171942 → 0.0.0-interpolate-reporter-url-20250910180021

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, 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
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,192 @@ 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,
656
+ emitter,
657
+ abortController,
658
+ runtimeContext,
359
659
  executionContext,
360
- resume,
660
+ writableStream,
661
+ tracingContext
662
+ }) {
663
+ let { duration, fn } = entry;
664
+ const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
665
+ type: AISpanType.WORKFLOW_SLEEP,
666
+ name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
667
+ attributes: {
668
+ durationMs: duration,
669
+ sleepType: fn ? "dynamic" : "fixed"
670
+ }
671
+ });
672
+ if (fn) {
673
+ const stepCallId = randomUUID();
674
+ duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
675
+ return await fn({
676
+ runId,
677
+ workflowId,
678
+ mastra: this.mastra,
679
+ runtimeContext,
680
+ inputData: prevOutput,
681
+ runCount: -1,
682
+ tracingContext: {
683
+ currentSpan: sleepSpan
684
+ },
685
+ getInitData: () => stepResults?.input,
686
+ getStepResult: (step) => {
687
+ if (!step?.id) {
688
+ return null;
689
+ }
690
+ const result = stepResults[step.id];
691
+ if (result?.status === "success") {
692
+ return result.output;
693
+ }
694
+ return null;
695
+ },
696
+ // TODO: this function shouldn't have suspend probably?
697
+ suspend: async (_suspendPayload) => {
698
+ },
699
+ bail: () => {
700
+ },
701
+ abort: () => {
702
+ abortController?.abort();
703
+ },
704
+ [EMITTER_SYMBOL]: emitter,
705
+ // TODO: add streamVNext support
706
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
707
+ engine: { step: this.inngestStep },
708
+ abortSignal: abortController?.signal,
709
+ writer: new ToolStream(
710
+ {
711
+ prefix: "workflow-step",
712
+ callId: stepCallId,
713
+ name: "sleep",
714
+ runId
715
+ },
716
+ writableStream
717
+ )
718
+ });
719
+ });
720
+ sleepSpan?.update({
721
+ attributes: {
722
+ durationMs: duration
723
+ }
724
+ });
725
+ }
726
+ try {
727
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
728
+ sleepSpan?.end();
729
+ } catch (e) {
730
+ sleepSpan?.error({ error: e });
731
+ throw e;
732
+ }
733
+ }
734
+ async executeSleepUntil({
735
+ workflowId,
736
+ runId,
737
+ entry,
361
738
  prevOutput,
739
+ stepResults,
362
740
  emitter,
363
- runtimeContext
741
+ abortController,
742
+ runtimeContext,
743
+ executionContext,
744
+ writableStream,
745
+ tracingContext
364
746
  }) {
365
- return super.executeStep({
366
- workflowId,
367
- runId,
368
- step,
369
- stepResults,
370
- executionContext,
371
- resume,
372
- prevOutput,
373
- emitter,
374
- runtimeContext
747
+ let { date, fn } = entry;
748
+ const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
749
+ type: AISpanType.WORKFLOW_SLEEP,
750
+ name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
751
+ attributes: {
752
+ untilDate: date,
753
+ durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
754
+ sleepType: fn ? "dynamic" : "fixed"
755
+ }
375
756
  });
757
+ if (fn) {
758
+ date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
759
+ const stepCallId = randomUUID();
760
+ return await fn({
761
+ runId,
762
+ workflowId,
763
+ mastra: this.mastra,
764
+ runtimeContext,
765
+ inputData: prevOutput,
766
+ runCount: -1,
767
+ tracingContext: {
768
+ currentSpan: sleepUntilSpan
769
+ },
770
+ getInitData: () => stepResults?.input,
771
+ getStepResult: (step) => {
772
+ if (!step?.id) {
773
+ return null;
774
+ }
775
+ const result = stepResults[step.id];
776
+ if (result?.status === "success") {
777
+ return result.output;
778
+ }
779
+ return null;
780
+ },
781
+ // TODO: this function shouldn't have suspend probably?
782
+ suspend: async (_suspendPayload) => {
783
+ },
784
+ bail: () => {
785
+ },
786
+ abort: () => {
787
+ abortController?.abort();
788
+ },
789
+ [EMITTER_SYMBOL]: emitter,
790
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
791
+ // TODO: add streamVNext support
792
+ engine: { step: this.inngestStep },
793
+ abortSignal: abortController?.signal,
794
+ writer: new ToolStream(
795
+ {
796
+ prefix: "workflow-step",
797
+ callId: stepCallId,
798
+ name: "sleep",
799
+ runId
800
+ },
801
+ writableStream
802
+ )
803
+ });
804
+ });
805
+ const time = !date ? 0 : date.getTime() - Date.now();
806
+ sleepUntilSpan?.update({
807
+ attributes: {
808
+ durationMs: Math.max(0, time)
809
+ }
810
+ });
811
+ }
812
+ if (!(date instanceof Date)) {
813
+ sleepUntilSpan?.end();
814
+ return;
815
+ }
816
+ try {
817
+ await this.inngestStep.sleepUntil(entry.id, date);
818
+ sleepUntilSpan?.end();
819
+ } catch (e) {
820
+ sleepUntilSpan?.error({ error: e });
821
+ throw e;
822
+ }
823
+ }
824
+ async executeWaitForEvent({ event, timeout }) {
825
+ const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
826
+ event: `user-event-${event}`,
827
+ timeout: timeout ?? 5e3
828
+ });
829
+ if (eventData === null) {
830
+ throw "Timeout waiting for event";
831
+ }
832
+ return eventData?.data;
376
833
  }
377
834
  async executeStep({
378
835
  step,
@@ -381,11 +838,24 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
381
838
  resume,
382
839
  prevOutput,
383
840
  emitter,
384
- runtimeContext
841
+ abortController,
842
+ runtimeContext,
843
+ tracingContext,
844
+ writableStream,
845
+ disableScorers
385
846
  }) {
386
- await this.inngestStep.run(
847
+ const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
848
+ name: `workflow step: '${step.id}'`,
849
+ type: AISpanType.WORKFLOW_STEP,
850
+ input: prevOutput,
851
+ attributes: {
852
+ stepId: step.id
853
+ }
854
+ });
855
+ const startedAt = await this.inngestStep.run(
387
856
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
388
857
  async () => {
858
+ const startedAt2 = Date.now();
389
859
  await emitter.emit("watch", {
390
860
  type: "watch",
391
861
  payload: {
@@ -407,6 +877,16 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
407
877
  },
408
878
  eventTimestamp: Date.now()
409
879
  });
880
+ await emitter.emit("watch-v2", {
881
+ type: "workflow-step-start",
882
+ payload: {
883
+ id: step.id,
884
+ status: "running",
885
+ payload: prevOutput,
886
+ startedAt: startedAt2
887
+ }
888
+ });
889
+ return startedAt2;
410
890
  }
411
891
  );
412
892
  if (step instanceof InngestWorkflow) {
@@ -467,6 +947,15 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
467
947
  },
468
948
  eventTimestamp: Date.now()
469
949
  });
950
+ await emitter.emit("watch-v2", {
951
+ type: "workflow-step-result",
952
+ payload: {
953
+ id: step.id,
954
+ status: "failed",
955
+ error: result?.error,
956
+ payload: prevOutput
957
+ }
958
+ });
470
959
  return { executionContext, result: { status: "failed", error: result?.error } };
471
960
  } else if (result.status === "suspended") {
472
961
  const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
@@ -493,6 +982,13 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
493
982
  },
494
983
  eventTimestamp: Date.now()
495
984
  });
985
+ await emitter.emit("watch-v2", {
986
+ type: "workflow-step-suspended",
987
+ payload: {
988
+ id: step.id,
989
+ status: "suspended"
990
+ }
991
+ });
496
992
  return {
497
993
  executionContext,
498
994
  result: {
@@ -543,6 +1039,21 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
543
1039
  },
544
1040
  eventTimestamp: Date.now()
545
1041
  });
1042
+ await emitter.emit("watch-v2", {
1043
+ type: "workflow-step-result",
1044
+ payload: {
1045
+ id: step.id,
1046
+ status: "success",
1047
+ output: result?.result
1048
+ }
1049
+ });
1050
+ await emitter.emit("watch-v2", {
1051
+ type: "workflow-step-finish",
1052
+ payload: {
1053
+ id: step.id,
1054
+ metadata: {}
1055
+ }
1056
+ });
546
1057
  return { executionContext, result: { status: "success", output: result?.result } };
547
1058
  }
548
1059
  );
@@ -552,12 +1063,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
552
1063
  const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
553
1064
  let execResults;
554
1065
  let suspended;
1066
+ let bailed;
555
1067
  try {
556
1068
  const result = await step.execute({
1069
+ runId: executionContext.runId,
557
1070
  mastra: this.mastra,
558
1071
  runtimeContext,
1072
+ writableStream,
559
1073
  inputData: prevOutput,
560
1074
  resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1075
+ tracingContext: {
1076
+ currentSpan: stepAISpan
1077
+ },
561
1078
  getInitData: () => stepResults?.input,
562
1079
  getStepResult: (step2) => {
563
1080
  const result2 = stepResults[step2.id];
@@ -570,24 +1087,60 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
570
1087
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
571
1088
  suspended = { payload: suspendPayload };
572
1089
  },
1090
+ bail: (result2) => {
1091
+ bailed = { payload: result2 };
1092
+ },
573
1093
  resume: {
574
1094
  steps: resume?.steps?.slice(1) || [],
575
1095
  resumePayload: resume?.resumePayload,
576
1096
  // @ts-ignore
577
1097
  runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
578
1098
  },
579
- emitter
1099
+ [EMITTER_SYMBOL]: emitter,
1100
+ engine: {
1101
+ step: this.inngestStep
1102
+ },
1103
+ abortSignal: abortController.signal
580
1104
  });
581
- execResults = { status: "success", output: result };
1105
+ const endedAt = Date.now();
1106
+ execResults = {
1107
+ status: "success",
1108
+ output: result,
1109
+ startedAt,
1110
+ endedAt,
1111
+ payload: prevOutput,
1112
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1113
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1114
+ };
582
1115
  } catch (e) {
583
- execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
1116
+ execResults = {
1117
+ status: "failed",
1118
+ payload: prevOutput,
1119
+ error: e instanceof Error ? e.message : String(e),
1120
+ endedAt: Date.now(),
1121
+ startedAt,
1122
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1123
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1124
+ };
584
1125
  }
585
1126
  if (suspended) {
586
- execResults = { status: "suspended", payload: suspended.payload };
1127
+ execResults = {
1128
+ status: "suspended",
1129
+ suspendedPayload: suspended.payload,
1130
+ payload: prevOutput,
1131
+ suspendedAt: Date.now(),
1132
+ startedAt,
1133
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1134
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1135
+ };
1136
+ } else if (bailed) {
1137
+ execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
587
1138
  }
588
1139
  if (execResults.status === "failed") {
589
1140
  if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
590
- throw execResults.error;
1141
+ const error = new Error(execResults.error);
1142
+ stepAISpan?.error({ error });
1143
+ throw error;
591
1144
  }
592
1145
  }
593
1146
  await emitter.emit("watch", {
@@ -595,20 +1148,61 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
595
1148
  payload: {
596
1149
  currentStep: {
597
1150
  id: step.id,
598
- status: execResults.status,
599
- output: execResults.output
1151
+ ...execResults
600
1152
  },
601
1153
  workflowState: {
602
1154
  status: "running",
603
- steps: stepResults,
1155
+ steps: { ...stepResults, [step.id]: execResults },
604
1156
  result: null,
605
1157
  error: null
606
1158
  }
607
1159
  },
608
1160
  eventTimestamp: Date.now()
609
1161
  });
1162
+ if (execResults.status === "suspended") {
1163
+ await emitter.emit("watch-v2", {
1164
+ type: "workflow-step-suspended",
1165
+ payload: {
1166
+ id: step.id,
1167
+ ...execResults
1168
+ }
1169
+ });
1170
+ } else {
1171
+ await emitter.emit("watch-v2", {
1172
+ type: "workflow-step-result",
1173
+ payload: {
1174
+ id: step.id,
1175
+ ...execResults
1176
+ }
1177
+ });
1178
+ await emitter.emit("watch-v2", {
1179
+ type: "workflow-step-finish",
1180
+ payload: {
1181
+ id: step.id,
1182
+ metadata: {}
1183
+ }
1184
+ });
1185
+ }
1186
+ stepAISpan?.end({ output: execResults });
610
1187
  return { result: execResults, executionContext, stepResults };
611
1188
  });
1189
+ if (disableScorers !== false) {
1190
+ await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1191
+ if (step.scorers) {
1192
+ await this.runScorers({
1193
+ scorers: step.scorers,
1194
+ runId: executionContext.runId,
1195
+ input: prevOutput,
1196
+ output: stepRes.result,
1197
+ workflowId: executionContext.workflowId,
1198
+ stepId: step.id,
1199
+ runtimeContext,
1200
+ disableScorers,
1201
+ tracingContext: { currentSpan: stepAISpan }
1202
+ });
1203
+ }
1204
+ });
1205
+ }
612
1206
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
613
1207
  Object.assign(stepResults, stepRes.stepResults);
614
1208
  return stepRes.result;
@@ -617,7 +1211,11 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
617
1211
  workflowId,
618
1212
  runId,
619
1213
  stepResults,
620
- executionContext
1214
+ executionContext,
1215
+ serializedStepGraph,
1216
+ workflowStatus,
1217
+ result,
1218
+ error
621
1219
  }) {
622
1220
  await this.inngestStep.run(
623
1221
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
@@ -631,6 +1229,11 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
631
1229
  context: stepResults,
632
1230
  activePaths: [],
633
1231
  suspendedPaths: executionContext.suspendedPaths,
1232
+ waitingPaths: {},
1233
+ serializedStepGraph,
1234
+ status: workflowStatus,
1235
+ result,
1236
+ error,
634
1237
  // @ts-ignore
635
1238
  timestamp: Date.now()
636
1239
  }
@@ -645,20 +1248,47 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
645
1248
  prevOutput,
646
1249
  prevStep,
647
1250
  stepResults,
1251
+ serializedStepGraph,
648
1252
  resume,
649
1253
  executionContext,
650
1254
  emitter,
651
- runtimeContext
1255
+ abortController,
1256
+ runtimeContext,
1257
+ writableStream,
1258
+ disableScorers,
1259
+ tracingContext
652
1260
  }) {
1261
+ const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1262
+ type: AISpanType.WORKFLOW_CONDITIONAL,
1263
+ name: `conditional: '${entry.conditions.length} conditions'`,
1264
+ input: prevOutput,
1265
+ attributes: {
1266
+ conditionCount: entry.conditions.length
1267
+ }
1268
+ });
653
1269
  let execResults;
654
1270
  const truthyIndexes = (await Promise.all(
655
1271
  entry.conditions.map(
656
1272
  (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1273
+ const evalSpan = conditionalSpan?.createChildSpan({
1274
+ type: AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1275
+ name: `condition: '${index}'`,
1276
+ input: prevOutput,
1277
+ attributes: {
1278
+ conditionIndex: index
1279
+ }
1280
+ });
657
1281
  try {
658
1282
  const result = await cond({
1283
+ runId,
1284
+ workflowId,
659
1285
  mastra: this.mastra,
660
1286
  runtimeContext,
1287
+ runCount: -1,
661
1288
  inputData: prevOutput,
1289
+ tracingContext: {
1290
+ currentSpan: evalSpan
1291
+ },
662
1292
  getInitData: () => stepResults?.input,
663
1293
  getStepResult: (step) => {
664
1294
  if (!step?.id) {
@@ -673,22 +1303,61 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
673
1303
  // TODO: this function shouldn't have suspend probably?
674
1304
  suspend: async (_suspendPayload) => {
675
1305
  },
676
- emitter
1306
+ bail: () => {
1307
+ },
1308
+ abort: () => {
1309
+ abortController.abort();
1310
+ },
1311
+ [EMITTER_SYMBOL]: emitter,
1312
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
1313
+ // TODO: add streamVNext support
1314
+ engine: {
1315
+ step: this.inngestStep
1316
+ },
1317
+ abortSignal: abortController.signal,
1318
+ writer: new ToolStream(
1319
+ {
1320
+ prefix: "workflow-step",
1321
+ callId: randomUUID(),
1322
+ name: "conditional",
1323
+ runId
1324
+ },
1325
+ writableStream
1326
+ )
1327
+ });
1328
+ evalSpan?.end({
1329
+ output: result,
1330
+ attributes: {
1331
+ result: !!result
1332
+ }
677
1333
  });
678
1334
  return result ? index : null;
679
1335
  } catch (e) {
1336
+ evalSpan?.error({
1337
+ error: e instanceof Error ? e : new Error(String(e)),
1338
+ attributes: {
1339
+ result: false
1340
+ }
1341
+ });
680
1342
  return null;
681
1343
  }
682
1344
  })
683
1345
  )
684
1346
  )).filter((index) => index !== null);
685
1347
  const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1348
+ conditionalSpan?.update({
1349
+ attributes: {
1350
+ truthyIndexes,
1351
+ selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1352
+ }
1353
+ });
686
1354
  const results = await Promise.all(
687
1355
  stepsToRun.map(
688
1356
  (step, index) => this.executeEntry({
689
1357
  workflowId,
690
1358
  runId,
691
1359
  entry: step,
1360
+ serializedStepGraph,
692
1361
  prevStep,
693
1362
  stepResults,
694
1363
  resume,
@@ -701,29 +1370,46 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
701
1370
  executionSpan: executionContext.executionSpan
702
1371
  },
703
1372
  emitter,
704
- runtimeContext
1373
+ abortController,
1374
+ runtimeContext,
1375
+ writableStream,
1376
+ disableScorers,
1377
+ tracingContext: {
1378
+ currentSpan: conditionalSpan
1379
+ }
705
1380
  })
706
1381
  )
707
1382
  );
708
- const hasFailed = results.find((result) => result.status === "failed");
709
- const hasSuspended = results.find((result) => result.status === "suspended");
1383
+ const hasFailed = results.find((result) => result.result.status === "failed");
1384
+ const hasSuspended = results.find((result) => result.result.status === "suspended");
710
1385
  if (hasFailed) {
711
- execResults = { status: "failed", error: hasFailed.error };
1386
+ execResults = { status: "failed", error: hasFailed.result.error };
712
1387
  } else if (hasSuspended) {
713
- execResults = { status: "suspended", payload: hasSuspended.payload };
1388
+ execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
714
1389
  } else {
715
1390
  execResults = {
716
1391
  status: "success",
717
1392
  output: results.reduce((acc, result, index) => {
718
- if (result.status === "success") {
1393
+ if (result.result.status === "success") {
719
1394
  acc[stepsToRun[index].step.id] = result.output;
720
1395
  }
721
1396
  return acc;
722
1397
  }, {})
723
1398
  };
724
1399
  }
1400
+ if (execResults.status === "failed") {
1401
+ conditionalSpan?.error({
1402
+ error: new Error(execResults.error)
1403
+ });
1404
+ } else {
1405
+ conditionalSpan?.end({
1406
+ output: execResults.output || execResults
1407
+ });
1408
+ }
725
1409
  return execResults;
726
1410
  }
727
1411
  };
728
1412
 
729
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, init, serve };
1413
+ export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1414
+ //# sourceMappingURL=index.js.map
1415
+ //# sourceMappingURL=index.js.map