@mastra/inngest 0.0.0-course-20250527170450 → 0.0.0-createToolOptions-20250926094418

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