@mastra/inngest 0.0.0-redis-cloud-transporter-20250508194049 → 0.0.0-refactor-agent-information-for-recomposable-ui-20251112151814

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,34 +1,53 @@
1
1
  import { randomUUID } from 'crypto';
2
+ import { ReadableStream } from 'stream/web';
2
3
  import { subscribe } from '@inngest/realtime';
3
- import { RuntimeContext } from '@mastra/core/di';
4
- import { Run, NewWorkflow, cloneStep, createStep, DefaultExecutionEngine } from '@mastra/core/workflows/vNext';
4
+ import { RequestContext } from '@mastra/core/di';
5
+ import { wrapMastra, SpanType } from '@mastra/core/observability';
6
+ import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
7
+ import { ToolStream, Tool } from '@mastra/core/tools';
8
+ import { Run, Workflow, DefaultExecutionEngine, createDeprecationProxy, getStepResult, runCountDeprecationMessage, validateStepInput } from '@mastra/core/workflows';
9
+ import { EMITTER_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
10
+ import { NonRetriableError, RetryAfterError } from 'inngest';
5
11
  import { serve as serve$1 } from 'inngest/hono';
12
+ import { z } from 'zod';
6
13
 
7
14
  // src/index.ts
8
- function serve({ mastra, inngest }) {
9
- const wfs = mastra.vnext_getWorkflows();
10
- const functions = Object.values(wfs).flatMap((wf) => {
11
- if (wf instanceof InngestWorkflow) {
12
- wf.__registerMastra(mastra);
13
- return wf.getFunctions();
14
- }
15
- return [];
16
- });
15
+ function serve({
16
+ mastra,
17
+ inngest,
18
+ functions: userFunctions = [],
19
+ registerOptions
20
+ }) {
21
+ const wfs = mastra.listWorkflows();
22
+ const workflowFunctions = Array.from(
23
+ new Set(
24
+ Object.values(wfs).flatMap((wf) => {
25
+ if (wf instanceof InngestWorkflow) {
26
+ wf.__registerMastra(mastra);
27
+ return wf.getFunctions();
28
+ }
29
+ return [];
30
+ })
31
+ )
32
+ );
17
33
  return serve$1({
34
+ ...registerOptions,
18
35
  client: inngest,
19
- functions
36
+ functions: [...workflowFunctions, ...userFunctions]
20
37
  });
21
38
  }
22
39
  var InngestRun = class extends Run {
23
40
  inngest;
41
+ serializedStepGraph;
24
42
  #mastra;
25
43
  constructor(params, inngest) {
26
44
  super(params);
27
45
  this.inngest = inngest;
46
+ this.serializedStepGraph = params.serializedStepGraph;
28
47
  this.#mastra = params.mastra;
29
48
  }
30
49
  async getRuns(eventId) {
31
- const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
50
+ const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
32
51
  headers: {
33
52
  Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
34
53
  }
@@ -38,35 +57,94 @@ var InngestRun = class extends Run {
38
57
  }
39
58
  async getRunOutput(eventId) {
40
59
  let runs = await this.getRuns(eventId);
41
- while (runs?.[0]?.status !== "Completed") {
60
+ const storage = this.#mastra?.getStorage();
61
+ while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
42
62
  await new Promise((resolve) => setTimeout(resolve, 1e3));
43
63
  runs = await this.getRuns(eventId);
44
- if (runs?.[0]?.status === "Failed" || runs?.[0]?.status === "Cancelled") {
45
- throw new Error(`Function run ${runs?.[0]?.status}`);
64
+ if (runs?.[0]?.status === "Failed") {
65
+ const snapshot = await storage?.loadWorkflowSnapshot({
66
+ workflowName: this.workflowId,
67
+ runId: this.runId
68
+ });
69
+ return {
70
+ output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
71
+ };
72
+ }
73
+ if (runs?.[0]?.status === "Cancelled") {
74
+ const snapshot = await storage?.loadWorkflowSnapshot({
75
+ workflowName: this.workflowId,
76
+ runId: this.runId
77
+ });
78
+ return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
46
79
  }
47
80
  }
48
81
  return runs?.[0];
49
82
  }
50
- async start({
51
- inputData
83
+ async cancel() {
84
+ const storage = this.#mastra?.getStorage();
85
+ await this.inngest.send({
86
+ name: `cancel.workflow.${this.workflowId}`,
87
+ data: {
88
+ runId: this.runId
89
+ }
90
+ });
91
+ const snapshot = await storage?.loadWorkflowSnapshot({
92
+ workflowName: this.workflowId,
93
+ runId: this.runId
94
+ });
95
+ if (snapshot) {
96
+ await storage?.persistWorkflowSnapshot({
97
+ workflowName: this.workflowId,
98
+ runId: this.runId,
99
+ resourceId: this.resourceId,
100
+ snapshot: {
101
+ ...snapshot,
102
+ status: "canceled",
103
+ value: snapshot.value
104
+ }
105
+ });
106
+ }
107
+ }
108
+ async start(params) {
109
+ return this._start(params);
110
+ }
111
+ async _start({
112
+ inputData,
113
+ initialState,
114
+ outputOptions,
115
+ tracingOptions,
116
+ format
52
117
  }) {
53
118
  await this.#mastra.getStorage()?.persistWorkflowSnapshot({
54
119
  workflowName: this.workflowId,
55
120
  runId: this.runId,
121
+ resourceId: this.resourceId,
56
122
  snapshot: {
57
123
  runId: this.runId,
124
+ serializedStepGraph: this.serializedStepGraph,
125
+ status: "running",
58
126
  value: {},
59
127
  context: {},
60
128
  activePaths: [],
61
129
  suspendedPaths: {},
130
+ activeStepsPath: {},
131
+ resumeLabels: {},
132
+ waitingPaths: {},
62
133
  timestamp: Date.now()
63
134
  }
64
135
  });
136
+ const inputDataToUse = await this._validateInput(inputData);
137
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
65
138
  const eventOutput = await this.inngest.send({
66
139
  name: `workflow.${this.workflowId}`,
67
140
  data: {
68
- inputData,
69
- runId: this.runId
141
+ inputData: inputDataToUse,
142
+ initialState: initialStateToUse,
143
+ runId: this.runId,
144
+ resourceId: this.resourceId,
145
+ outputOptions,
146
+ tracingOptions,
147
+ format
70
148
  }
71
149
  });
72
150
  const eventId = eventOutput.ids[0];
@@ -78,27 +156,45 @@ var InngestRun = class extends Run {
78
156
  if (result.status === "failed") {
79
157
  result.error = new Error(result.error);
80
158
  }
81
- this.cleanup?.();
159
+ if (result.status !== "suspended") {
160
+ this.cleanup?.();
161
+ }
82
162
  return result;
83
163
  }
84
164
  async resume(params) {
165
+ const p = this._resume(params).then((result) => {
166
+ if (result.status !== "suspended") {
167
+ this.closeStreamAction?.().catch(() => {
168
+ });
169
+ }
170
+ return result;
171
+ });
172
+ this.executionResults = p;
173
+ return p;
174
+ }
175
+ async _resume(params) {
176
+ const storage = this.#mastra?.getStorage();
85
177
  const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
86
178
  (step) => typeof step === "string" ? step : step?.id
87
179
  );
88
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
180
+ const snapshot = await storage?.loadWorkflowSnapshot({
89
181
  workflowName: this.workflowId,
90
182
  runId: this.runId
91
183
  });
184
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
185
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
92
186
  const eventOutput = await this.inngest.send({
93
187
  name: `workflow.${this.workflowId}`,
94
188
  data: {
95
- inputData: params.resumeData,
189
+ inputData: resumeDataToUse,
190
+ initialState: snapshot?.value ?? {},
96
191
  runId: this.runId,
192
+ workflowId: this.workflowId,
97
193
  stepResults: snapshot?.context,
98
194
  resume: {
99
195
  steps,
100
196
  stepResults: snapshot?.context,
101
- resumePayload: params.resumeData,
197
+ resumePayload: resumeDataToUse,
102
198
  // @ts-ignore
103
199
  resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
104
200
  }
@@ -116,6 +212,7 @@ var InngestRun = class extends Run {
116
212
  return result;
117
213
  }
118
214
  watch(cb) {
215
+ let active = true;
119
216
  const streamPromise = subscribe(
120
217
  {
121
218
  channel: `workflow:${this.workflowId}:${this.runId}`,
@@ -123,40 +220,176 @@ var InngestRun = class extends Run {
123
220
  app: this.inngest
124
221
  },
125
222
  (message) => {
126
- cb(message.data);
223
+ if (active) {
224
+ cb(message.data);
225
+ }
127
226
  }
128
227
  );
129
228
  return () => {
130
- streamPromise.then((stream) => {
131
- stream.cancel();
229
+ active = false;
230
+ streamPromise.then(async (stream) => {
231
+ return stream.cancel();
132
232
  }).catch((err) => {
133
233
  console.error(err);
134
234
  });
135
235
  };
136
236
  }
237
+ streamLegacy({ inputData, requestContext } = {}) {
238
+ const { readable, writable } = new TransformStream();
239
+ const writer = writable.getWriter();
240
+ const unwatch = this.watch(async (event) => {
241
+ try {
242
+ await writer.write({
243
+ // @ts-ignore
244
+ type: "start",
245
+ // @ts-ignore
246
+ payload: { runId: this.runId }
247
+ });
248
+ const e = {
249
+ ...event,
250
+ type: event.type.replace("workflow-", "")
251
+ };
252
+ if (e.type === "step-output") {
253
+ e.type = e.payload.output.type;
254
+ e.payload = e.payload.output.payload;
255
+ }
256
+ await writer.write(e);
257
+ } catch {
258
+ }
259
+ });
260
+ this.closeStreamAction = async () => {
261
+ await writer.write({
262
+ type: "finish",
263
+ // @ts-ignore
264
+ payload: { runId: this.runId }
265
+ });
266
+ unwatch();
267
+ try {
268
+ await writer.close();
269
+ } catch (err) {
270
+ console.error("Error closing stream:", err);
271
+ } finally {
272
+ writer.releaseLock();
273
+ }
274
+ };
275
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
276
+ if (result.status !== "suspended") {
277
+ this.closeStreamAction?.().catch(() => {
278
+ });
279
+ }
280
+ return result;
281
+ });
282
+ return {
283
+ stream: readable,
284
+ getWorkflowState: () => this.executionResults
285
+ };
286
+ }
287
+ stream({
288
+ inputData,
289
+ requestContext,
290
+ tracingOptions,
291
+ closeOnSuspend = true,
292
+ initialState,
293
+ outputOptions
294
+ } = {}) {
295
+ if (this.closeStreamAction && this.streamOutput) {
296
+ return this.streamOutput;
297
+ }
298
+ this.closeStreamAction = async () => {
299
+ };
300
+ const self = this;
301
+ const stream = new ReadableStream({
302
+ async start(controller) {
303
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
304
+ controller.enqueue({
305
+ type,
306
+ runId: self.runId,
307
+ from,
308
+ payload: {
309
+ stepName: payload?.id,
310
+ ...payload
311
+ }
312
+ });
313
+ });
314
+ self.closeStreamAction = async () => {
315
+ unwatch();
316
+ try {
317
+ await controller.close();
318
+ } catch (err) {
319
+ console.error("Error closing stream:", err);
320
+ }
321
+ };
322
+ const executionResultsPromise = self._start({
323
+ inputData,
324
+ requestContext,
325
+ // tracingContext, // We are not able to pass a reference to a span here, what to do?
326
+ initialState,
327
+ tracingOptions,
328
+ outputOptions,
329
+ format: "vnext"
330
+ });
331
+ let executionResults;
332
+ try {
333
+ executionResults = await executionResultsPromise;
334
+ if (closeOnSuspend) {
335
+ self.closeStreamAction?.().catch(() => {
336
+ });
337
+ } else if (executionResults.status !== "suspended") {
338
+ self.closeStreamAction?.().catch(() => {
339
+ });
340
+ }
341
+ if (self.streamOutput) {
342
+ self.streamOutput.updateResults(
343
+ executionResults
344
+ );
345
+ }
346
+ } catch (err) {
347
+ self.streamOutput?.rejectResults(err);
348
+ self.closeStreamAction?.().catch(() => {
349
+ });
350
+ }
351
+ }
352
+ });
353
+ this.streamOutput = new WorkflowRunOutput({
354
+ runId: this.runId,
355
+ workflowId: this.workflowId,
356
+ stream
357
+ });
358
+ return this.streamOutput;
359
+ }
360
+ streamVNext(args = {}) {
361
+ return this.stream(args);
362
+ }
137
363
  };
138
- var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
364
+ var InngestWorkflow = class _InngestWorkflow extends Workflow {
139
365
  #mastra;
140
366
  inngest;
141
367
  function;
368
+ flowControlConfig;
142
369
  constructor(params, inngest) {
143
- super(params);
370
+ const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
371
+ super(workflowParams);
372
+ this.engineType = "inngest";
373
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
374
+ ([_, value]) => value !== void 0
375
+ );
376
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
144
377
  this.#mastra = params.mastra;
145
378
  this.inngest = inngest;
146
379
  }
147
- async getWorkflowRuns(args) {
380
+ async listWorkflowRuns(args) {
148
381
  const storage = this.#mastra?.getStorage();
149
382
  if (!storage) {
150
383
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
151
384
  return { runs: [], total: 0 };
152
385
  }
153
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
386
+ return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
154
387
  }
155
388
  async getWorkflowRunById(runId) {
156
389
  const storage = this.#mastra?.getStorage();
157
390
  if (!storage) {
158
391
  this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
159
- return null;
392
+ return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
160
393
  }
161
394
  const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
162
395
  return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
@@ -179,21 +412,53 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
179
412
  }
180
413
  }
181
414
  }
182
- createRun(options) {
415
+ async createRun(options) {
183
416
  const runIdToUse = options?.runId || randomUUID();
184
417
  const run = this.runs.get(runIdToUse) ?? new InngestRun(
185
418
  {
186
419
  workflowId: this.id,
187
420
  runId: runIdToUse,
421
+ resourceId: options?.resourceId,
188
422
  executionEngine: this.executionEngine,
189
423
  executionGraph: this.executionGraph,
424
+ serializedStepGraph: this.serializedStepGraph,
190
425
  mastra: this.#mastra,
191
426
  retryConfig: this.retryConfig,
192
- cleanup: () => this.runs.delete(runIdToUse)
427
+ cleanup: () => this.runs.delete(runIdToUse),
428
+ workflowSteps: this.steps,
429
+ workflowEngineType: this.engineType
193
430
  },
194
431
  this.inngest
195
432
  );
196
433
  this.runs.set(runIdToUse, run);
434
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
435
+ workflowStatus: run.workflowRunStatus,
436
+ stepResults: {}
437
+ });
438
+ const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
439
+ if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
440
+ await this.mastra?.getStorage()?.persistWorkflowSnapshot({
441
+ workflowName: this.id,
442
+ runId: runIdToUse,
443
+ resourceId: options?.resourceId,
444
+ snapshot: {
445
+ runId: runIdToUse,
446
+ status: "pending",
447
+ value: {},
448
+ context: {},
449
+ activePaths: [],
450
+ activeStepsPath: {},
451
+ waitingPaths: {},
452
+ serializedStepGraph: this.serializedStepGraph,
453
+ suspendedPaths: {},
454
+ resumeLabels: {},
455
+ result: void 0,
456
+ error: void 0,
457
+ // @ts-ignore
458
+ timestamp: Date.now()
459
+ }
460
+ });
461
+ }
197
462
  return run;
198
463
  }
199
464
  getFunction() {
@@ -201,11 +466,17 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
201
466
  return this.function;
202
467
  }
203
468
  this.function = this.inngest.createFunction(
204
- // @ts-ignore
205
- { id: `workflow.${this.id}`, retries: this.retryConfig?.attempts ?? 0 },
469
+ {
470
+ id: `workflow.${this.id}`,
471
+ // @ts-ignore
472
+ retries: this.retryConfig?.attempts ?? 0,
473
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
474
+ // Spread flow control configuration
475
+ ...this.flowControlConfig
476
+ },
206
477
  { event: `workflow.${this.id}` },
207
478
  async ({ event, step, attempt, publish }) => {
208
- let { inputData, runId, resume } = event.data;
479
+ let { inputData, initialState, runId, resourceId, resume, outputOptions, format } = event.data;
209
480
  if (!runId) {
210
481
  runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
211
482
  return randomUUID();
@@ -219,25 +490,52 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
219
490
  try {
220
491
  await publish({
221
492
  channel: `workflow:${this.id}:${runId}`,
222
- topic: "watch",
493
+ topic: event2,
223
494
  data
224
495
  });
225
496
  } catch (err) {
226
497
  this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
227
498
  }
499
+ },
500
+ on: (_event, _callback) => {
501
+ },
502
+ off: (_event, _callback) => {
503
+ },
504
+ once: (_event, _callback) => {
228
505
  }
229
506
  };
230
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
507
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
231
508
  const result = await engine.execute({
232
509
  workflowId: this.id,
233
510
  runId,
511
+ resourceId,
234
512
  graph: this.executionGraph,
513
+ serializedStepGraph: this.serializedStepGraph,
235
514
  input: inputData,
515
+ initialState,
236
516
  emitter,
237
517
  retryConfig: this.retryConfig,
238
- runtimeContext: new RuntimeContext(),
518
+ requestContext: new RequestContext(),
239
519
  // TODO
240
- resume
520
+ resume,
521
+ format,
522
+ abortController: new AbortController(),
523
+ // currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
524
+ outputOptions,
525
+ writableStream: new WritableStream({
526
+ write(chunk) {
527
+ void emitter.emit("watch", chunk).catch(() => {
528
+ });
529
+ }
530
+ })
531
+ });
532
+ await step.run(`workflow.${this.id}.finalize`, async () => {
533
+ if (result.status === "failed") {
534
+ throw new NonRetriableError(`Workflow failed`, {
535
+ cause: result
536
+ });
537
+ }
538
+ return result;
241
539
  });
242
540
  return { result, runId };
243
541
  }
@@ -261,118 +559,393 @@ var InngestWorkflow = class _InngestWorkflow extends NewWorkflow {
261
559
  return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
262
560
  }
263
561
  };
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;
562
+ function isAgent(params) {
563
+ return params?.component === "AGENT";
564
+ }
565
+ function isTool(params) {
566
+ return params instanceof Tool;
567
+ }
568
+ function createStep(params, agentOptions) {
569
+ if (isAgent(params)) {
570
+ return {
571
+ id: params.name,
572
+ description: params.getDescription(),
573
+ // @ts-ignore
574
+ inputSchema: z.object({
575
+ prompt: z.string()
576
+ // resourceId: z.string().optional(),
577
+ // threadId: z.string().optional(),
578
+ }),
579
+ // @ts-ignore
580
+ outputSchema: z.object({
581
+ text: z.string()
582
+ }),
583
+ execute: async ({
584
+ inputData,
585
+ [EMITTER_SYMBOL]: emitter,
586
+ [STREAM_FORMAT_SYMBOL]: streamFormat,
587
+ requestContext,
588
+ tracingContext,
589
+ abortSignal,
590
+ abort,
591
+ writer
592
+ }) => {
593
+ let streamPromise = {};
594
+ streamPromise.promise = new Promise((resolve, reject) => {
595
+ streamPromise.resolve = resolve;
596
+ streamPromise.reject = reject;
597
+ });
598
+ const toolData = {
599
+ name: params.name,
600
+ args: inputData
601
+ };
602
+ let stream;
603
+ if ((await params.getModel()).specificationVersion === "v1") {
604
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
605
+ ...agentOptions ?? {},
606
+ // resourceId: inputData.resourceId,
607
+ // threadId: inputData.threadId,
608
+ requestContext,
609
+ tracingContext,
610
+ onFinish: (result) => {
611
+ streamPromise.resolve(result.text);
612
+ void agentOptions?.onFinish?.(result);
613
+ },
614
+ abortSignal
615
+ });
616
+ stream = fullStream;
617
+ } else {
618
+ const modelOutput = await params.stream(inputData.prompt, {
619
+ ...agentOptions ?? {},
620
+ requestContext,
621
+ tracingContext,
622
+ onFinish: (result) => {
623
+ streamPromise.resolve(result.text);
624
+ void agentOptions?.onFinish?.(result);
625
+ },
626
+ abortSignal
627
+ });
628
+ stream = modelOutput.fullStream;
629
+ }
630
+ if (streamFormat === "legacy") {
631
+ await emitter.emit("watch", {
632
+ type: "tool-call-streaming-start",
633
+ ...toolData ?? {}
634
+ });
635
+ for await (const chunk of stream) {
636
+ if (chunk.type === "text-delta") {
637
+ await emitter.emit("watch", {
638
+ type: "tool-call-delta",
639
+ ...toolData ?? {},
640
+ argsTextDelta: chunk.textDelta
641
+ });
642
+ }
643
+ }
644
+ await emitter.emit("watch", {
645
+ type: "tool-call-streaming-finish",
646
+ ...toolData ?? {}
647
+ });
648
+ } else {
649
+ for await (const chunk of stream) {
650
+ await writer.write(chunk);
651
+ }
652
+ }
653
+ if (abortSignal.aborted) {
654
+ return abort();
655
+ }
656
+ return {
657
+ text: await streamPromise.promise
658
+ };
659
+ },
660
+ component: params.component
661
+ };
662
+ }
663
+ if (isTool(params)) {
664
+ if (!params.inputSchema || !params.outputSchema) {
665
+ throw new Error("Tool must have input and output schemas defined");
666
+ }
667
+ return {
668
+ // TODO: tool probably should have strong id type
669
+ // @ts-ignore
670
+ id: params.id,
671
+ description: params.description,
672
+ inputSchema: params.inputSchema,
673
+ outputSchema: params.outputSchema,
674
+ execute: async ({ inputData, mastra, requestContext, tracingContext, suspend, resumeData }) => {
675
+ return params.execute({
676
+ context: inputData,
677
+ mastra: wrapMastra(mastra, tracingContext),
678
+ requestContext,
679
+ tracingContext,
680
+ suspend,
681
+ resumeData
682
+ });
683
+ },
684
+ component: "TOOL"
685
+ };
686
+ }
687
+ return {
688
+ id: params.id,
689
+ description: params.description,
690
+ inputSchema: params.inputSchema,
691
+ outputSchema: params.outputSchema,
692
+ resumeSchema: params.resumeSchema,
693
+ suspendSchema: params.suspendSchema,
694
+ execute: params.execute
695
+ };
278
696
  }
279
697
  function init(inngest) {
280
698
  return {
281
699
  createWorkflow(params) {
282
- return new InngestWorkflow(params, inngest);
700
+ return new InngestWorkflow(
701
+ params,
702
+ inngest
703
+ );
283
704
  },
284
705
  createStep,
285
- cloneStep,
286
- cloneWorkflow
706
+ cloneStep(step, opts) {
707
+ return {
708
+ id: opts.id,
709
+ description: step.description,
710
+ inputSchema: step.inputSchema,
711
+ outputSchema: step.outputSchema,
712
+ resumeSchema: step.resumeSchema,
713
+ suspendSchema: step.suspendSchema,
714
+ stateSchema: step.stateSchema,
715
+ execute: step.execute,
716
+ component: step.component
717
+ };
718
+ },
719
+ cloneWorkflow(workflow, opts) {
720
+ const wf = new Workflow({
721
+ id: opts.id,
722
+ inputSchema: workflow.inputSchema,
723
+ outputSchema: workflow.outputSchema,
724
+ steps: workflow.stepDefs,
725
+ mastra: workflow.mastra
726
+ });
727
+ wf.setStepFlow(workflow.stepGraph);
728
+ wf.commit();
729
+ return wf;
730
+ }
287
731
  };
288
732
  }
289
733
  var InngestExecutionEngine = class extends DefaultExecutionEngine {
290
734
  inngestStep;
291
735
  inngestAttempts;
292
- constructor(mastra, inngestStep, inngestAttempts = 0) {
293
- super({ mastra });
736
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
737
+ super({ mastra, options });
294
738
  this.inngestStep = inngestStep;
295
739
  this.inngestAttempts = inngestAttempts;
296
740
  }
297
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
741
+ async fmtReturnValue(emitter, stepResults, lastOutput, error) {
298
742
  const base = {
299
743
  status: lastOutput.status,
300
744
  steps: stepResults
301
745
  };
302
746
  if (lastOutput.status === "success") {
303
- await emitter.emit("watch", {
304
- type: "watch",
305
- payload: {
306
- workflowState: {
307
- status: lastOutput.status,
308
- steps: stepResults,
309
- result: lastOutput.output
310
- }
311
- },
312
- eventTimestamp: Date.now()
313
- });
314
747
  base.result = lastOutput.output;
315
748
  } else if (lastOutput.status === "failed") {
316
749
  base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
317
- await emitter.emit("watch", {
318
- type: "watch",
319
- payload: {
320
- workflowState: {
321
- status: lastOutput.status,
322
- steps: stepResults,
323
- result: null,
324
- error: base.error
325
- }
326
- },
327
- eventTimestamp: Date.now()
328
- });
329
750
  } else if (lastOutput.status === "suspended") {
330
- await emitter.emit("watch", {
331
- type: "watch",
332
- payload: {
333
- workflowState: {
334
- status: lastOutput.status,
335
- steps: stepResults,
336
- result: null,
337
- error: null
338
- }
339
- },
340
- eventTimestamp: Date.now()
341
- });
342
751
  const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
343
752
  if (stepResult?.status === "suspended") {
344
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
753
+ const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
345
754
  return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
346
755
  }
347
756
  return [];
348
757
  });
349
758
  base.suspended = suspendedStepIds;
350
759
  }
351
- executionSpan?.end();
352
760
  return base;
353
761
  }
354
- async superExecuteStep({
762
+ // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
763
+ // await this.inngestStep.sleep(id, duration);
764
+ // }
765
+ async executeSleep({
355
766
  workflowId,
356
767
  runId,
357
- step,
768
+ entry,
769
+ prevOutput,
358
770
  stepResults,
771
+ emitter,
772
+ abortController,
773
+ requestContext,
359
774
  executionContext,
360
- resume,
775
+ writableStream,
776
+ tracingContext
777
+ }) {
778
+ let { duration, fn } = entry;
779
+ const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
780
+ type: SpanType.WORKFLOW_SLEEP,
781
+ name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
782
+ attributes: {
783
+ durationMs: duration,
784
+ sleepType: fn ? "dynamic" : "fixed"
785
+ },
786
+ tracingPolicy: this.options?.tracingPolicy
787
+ });
788
+ if (fn) {
789
+ const stepCallId = randomUUID();
790
+ duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
791
+ return await fn(
792
+ createDeprecationProxy(
793
+ {
794
+ runId,
795
+ workflowId,
796
+ mastra: this.mastra,
797
+ requestContext,
798
+ inputData: prevOutput,
799
+ state: executionContext.state,
800
+ setState: (state) => {
801
+ executionContext.state = state;
802
+ },
803
+ retryCount: -1,
804
+ tracingContext: {
805
+ currentSpan: sleepSpan
806
+ },
807
+ getInitData: () => stepResults?.input,
808
+ getStepResult: getStepResult.bind(this, stepResults),
809
+ // TODO: this function shouldn't have suspend probably?
810
+ suspend: async (_suspendPayload) => {
811
+ },
812
+ bail: () => {
813
+ },
814
+ abort: () => {
815
+ abortController?.abort();
816
+ },
817
+ [EMITTER_SYMBOL]: emitter,
818
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
819
+ engine: { step: this.inngestStep },
820
+ abortSignal: abortController?.signal,
821
+ writer: new ToolStream(
822
+ {
823
+ prefix: "workflow-step",
824
+ callId: stepCallId,
825
+ name: "sleep",
826
+ runId
827
+ },
828
+ writableStream
829
+ )
830
+ },
831
+ {
832
+ paramName: "runCount",
833
+ deprecationMessage: runCountDeprecationMessage,
834
+ logger: this.logger
835
+ }
836
+ )
837
+ );
838
+ });
839
+ sleepSpan?.update({
840
+ attributes: {
841
+ durationMs: duration
842
+ }
843
+ });
844
+ }
845
+ try {
846
+ await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
847
+ sleepSpan?.end();
848
+ } catch (e) {
849
+ sleepSpan?.error({ error: e });
850
+ throw e;
851
+ }
852
+ }
853
+ async executeSleepUntil({
854
+ workflowId,
855
+ runId,
856
+ entry,
361
857
  prevOutput,
858
+ stepResults,
362
859
  emitter,
363
- runtimeContext
860
+ abortController,
861
+ requestContext,
862
+ executionContext,
863
+ writableStream,
864
+ tracingContext
364
865
  }) {
365
- return super.executeStep({
366
- workflowId,
367
- runId,
368
- step,
369
- stepResults,
370
- executionContext,
371
- resume,
372
- prevOutput,
373
- emitter,
374
- runtimeContext
866
+ let { date, fn } = entry;
867
+ const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
868
+ type: SpanType.WORKFLOW_SLEEP,
869
+ name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
870
+ attributes: {
871
+ untilDate: date,
872
+ durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
873
+ sleepType: fn ? "dynamic" : "fixed"
874
+ },
875
+ tracingPolicy: this.options?.tracingPolicy
375
876
  });
877
+ if (fn) {
878
+ date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
879
+ const stepCallId = randomUUID();
880
+ return await fn(
881
+ createDeprecationProxy(
882
+ {
883
+ runId,
884
+ workflowId,
885
+ mastra: this.mastra,
886
+ requestContext,
887
+ inputData: prevOutput,
888
+ state: executionContext.state,
889
+ setState: (state) => {
890
+ executionContext.state = state;
891
+ },
892
+ retryCount: -1,
893
+ tracingContext: {
894
+ currentSpan: sleepUntilSpan
895
+ },
896
+ getInitData: () => stepResults?.input,
897
+ getStepResult: getStepResult.bind(this, stepResults),
898
+ // TODO: this function shouldn't have suspend probably?
899
+ suspend: async (_suspendPayload) => {
900
+ },
901
+ bail: () => {
902
+ },
903
+ abort: () => {
904
+ abortController?.abort();
905
+ },
906
+ [EMITTER_SYMBOL]: emitter,
907
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
908
+ engine: { step: this.inngestStep },
909
+ abortSignal: abortController?.signal,
910
+ writer: new ToolStream(
911
+ {
912
+ prefix: "workflow-step",
913
+ callId: stepCallId,
914
+ name: "sleep",
915
+ runId
916
+ },
917
+ writableStream
918
+ )
919
+ },
920
+ {
921
+ paramName: "runCount",
922
+ deprecationMessage: runCountDeprecationMessage,
923
+ logger: this.logger
924
+ }
925
+ )
926
+ );
927
+ });
928
+ if (date && !(date instanceof Date)) {
929
+ date = new Date(date);
930
+ }
931
+ const time = !date ? 0 : date.getTime() - Date.now();
932
+ sleepUntilSpan?.update({
933
+ attributes: {
934
+ durationMs: Math.max(0, time)
935
+ }
936
+ });
937
+ }
938
+ if (!(date instanceof Date)) {
939
+ sleepUntilSpan?.end();
940
+ return;
941
+ }
942
+ try {
943
+ await this.inngestStep.sleepUntil(entry.id, date);
944
+ sleepUntilSpan?.end();
945
+ } catch (e) {
946
+ sleepUntilSpan?.error({ error: e });
947
+ throw e;
948
+ }
376
949
  }
377
950
  async executeStep({
378
951
  step,
@@ -381,91 +954,113 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
381
954
  resume,
382
955
  prevOutput,
383
956
  emitter,
384
- runtimeContext
957
+ abortController,
958
+ requestContext,
959
+ tracingContext,
960
+ writableStream,
961
+ disableScorers
385
962
  }) {
386
- await this.inngestStep.run(
963
+ const stepSpan = tracingContext?.currentSpan?.createChildSpan({
964
+ name: `workflow step: '${step.id}'`,
965
+ type: SpanType.WORKFLOW_STEP,
966
+ input: prevOutput,
967
+ attributes: {
968
+ stepId: step.id
969
+ },
970
+ tracingPolicy: this.options?.tracingPolicy
971
+ });
972
+ const { inputData, validationError } = await validateStepInput({
973
+ prevOutput,
974
+ step,
975
+ validateInputs: this.options?.validateInputs ?? false
976
+ });
977
+ const startedAt = await this.inngestStep.run(
387
978
  `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
388
979
  async () => {
980
+ const startedAt2 = Date.now();
389
981
  await emitter.emit("watch", {
390
- type: "watch",
982
+ type: "workflow-step-start",
391
983
  payload: {
392
- currentStep: {
393
- id: step.id,
394
- status: "running"
395
- },
396
- workflowState: {
397
- status: "running",
398
- steps: {
399
- ...stepResults,
400
- [step.id]: {
401
- status: "running"
402
- }
403
- },
404
- result: null,
405
- error: null
406
- }
407
- },
408
- eventTimestamp: Date.now()
984
+ id: step.id,
985
+ status: "running",
986
+ payload: inputData,
987
+ startedAt: startedAt2
988
+ }
409
989
  });
990
+ return startedAt2;
410
991
  }
411
992
  );
412
993
  if (step instanceof InngestWorkflow) {
413
994
  const isResume = !!resume?.steps?.length;
414
995
  let result;
415
996
  let runId;
416
- if (isResume) {
417
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
418
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
419
- workflowName: step.id,
420
- runId
421
- });
422
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
423
- function: step.getFunction(),
424
- data: {
425
- inputData: prevOutput,
426
- runId,
427
- resume: {
997
+ try {
998
+ if (isResume) {
999
+ runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
1000
+ const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1001
+ workflowName: step.id,
1002
+ runId
1003
+ });
1004
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1005
+ function: step.getFunction(),
1006
+ data: {
1007
+ inputData,
1008
+ initialState: executionContext.state ?? snapshot?.value ?? {},
428
1009
  runId,
429
- steps: resume.steps.slice(1),
430
- stepResults: snapshot?.context,
431
- resumePayload: resume.resumePayload,
432
- // @ts-ignore
433
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1010
+ resume: {
1011
+ runId,
1012
+ steps: resume.steps.slice(1),
1013
+ stepResults: snapshot?.context,
1014
+ resumePayload: resume.resumePayload,
1015
+ // @ts-ignore
1016
+ resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
1017
+ },
1018
+ outputOptions: { includeState: true }
434
1019
  }
435
- }
436
- });
437
- result = invokeResp.result;
438
- runId = invokeResp.runId;
439
- } else {
440
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
441
- function: step.getFunction(),
442
- data: {
443
- inputData: prevOutput
444
- }
445
- });
446
- result = invokeResp.result;
447
- runId = invokeResp.runId;
1020
+ });
1021
+ result = invokeResp.result;
1022
+ runId = invokeResp.runId;
1023
+ executionContext.state = invokeResp.result.state;
1024
+ } else {
1025
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1026
+ function: step.getFunction(),
1027
+ data: {
1028
+ inputData,
1029
+ initialState: executionContext.state ?? {},
1030
+ outputOptions: { includeState: true }
1031
+ }
1032
+ });
1033
+ result = invokeResp.result;
1034
+ runId = invokeResp.runId;
1035
+ executionContext.state = invokeResp.result.state;
1036
+ }
1037
+ } catch (e) {
1038
+ const errorCause = e?.cause;
1039
+ if (errorCause && typeof errorCause === "object") {
1040
+ result = errorCause;
1041
+ runId = errorCause.runId || randomUUID();
1042
+ } else {
1043
+ runId = randomUUID();
1044
+ result = {
1045
+ status: "failed",
1046
+ error: e instanceof Error ? e : new Error(String(e)),
1047
+ steps: {},
1048
+ input: inputData
1049
+ };
1050
+ }
448
1051
  }
449
1052
  const res = await this.inngestStep.run(
450
1053
  `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
451
1054
  async () => {
452
1055
  if (result.status === "failed") {
453
1056
  await emitter.emit("watch", {
454
- type: "watch",
1057
+ type: "workflow-step-result",
455
1058
  payload: {
456
- currentStep: {
457
- id: step.id,
458
- status: "failed",
459
- error: result?.error
460
- },
461
- workflowState: {
462
- status: "running",
463
- steps: stepResults,
464
- result: null,
465
- error: null
466
- }
467
- },
468
- eventTimestamp: Date.now()
1059
+ id: step.id,
1060
+ status: "failed",
1061
+ error: result?.error,
1062
+ payload: prevOutput
1063
+ }
469
1064
  });
470
1065
  return { executionContext, result: { status: "failed", error: result?.error } };
471
1066
  } else if (result.status === "suspended") {
@@ -474,50 +1069,27 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
474
1069
  return stepRes2?.status === "suspended";
475
1070
  });
476
1071
  for (const [stepName, stepResult] of suspendedSteps) {
477
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
1072
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
478
1073
  executionContext.suspendedPaths[step.id] = executionContext.executionPath;
479
1074
  await emitter.emit("watch", {
480
- type: "watch",
1075
+ type: "workflow-step-suspended",
481
1076
  payload: {
482
- currentStep: {
483
- id: step.id,
484
- status: "suspended",
485
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
486
- },
487
- workflowState: {
488
- status: "running",
489
- steps: stepResults,
490
- result: null,
491
- error: null
492
- }
493
- },
494
- eventTimestamp: Date.now()
1077
+ id: step.id,
1078
+ status: "suspended"
1079
+ }
495
1080
  });
496
1081
  return {
497
1082
  executionContext,
498
1083
  result: {
499
1084
  status: "suspended",
500
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
1085
+ payload: stepResult.payload,
1086
+ suspendPayload: {
1087
+ ...stepResult?.suspendPayload,
1088
+ __workflow_meta: { runId, path: suspendPath }
1089
+ }
501
1090
  }
502
1091
  };
503
1092
  }
504
- await emitter.emit("watch", {
505
- type: "watch",
506
- payload: {
507
- currentStep: {
508
- id: step.id,
509
- status: "suspended",
510
- payload: {}
511
- },
512
- workflowState: {
513
- status: "running",
514
- steps: stepResults,
515
- result: null,
516
- error: null
517
- }
518
- },
519
- eventTimestamp: Date.now()
520
- });
521
1093
  return {
522
1094
  executionContext,
523
1095
  result: {
@@ -527,110 +1099,245 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
527
1099
  };
528
1100
  }
529
1101
  await emitter.emit("watch", {
530
- type: "watch",
1102
+ type: "workflow-step-result",
531
1103
  payload: {
532
- currentStep: {
533
- id: step.id,
534
- status: "success",
535
- output: result?.result
536
- },
537
- workflowState: {
538
- status: "running",
539
- steps: stepResults,
540
- result: null,
541
- error: null
542
- }
543
- },
544
- eventTimestamp: Date.now()
1104
+ id: step.id,
1105
+ status: "success",
1106
+ output: result?.result
1107
+ }
1108
+ });
1109
+ await emitter.emit("watch", {
1110
+ type: "workflow-step-finish",
1111
+ payload: {
1112
+ id: step.id,
1113
+ metadata: {}
1114
+ }
545
1115
  });
546
1116
  return { executionContext, result: { status: "success", output: result?.result } };
547
1117
  }
548
1118
  );
549
1119
  Object.assign(executionContext, res.executionContext);
550
- return res.result;
1120
+ return {
1121
+ ...res.result,
1122
+ startedAt,
1123
+ endedAt: Date.now(),
1124
+ payload: inputData,
1125
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1126
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1127
+ };
551
1128
  }
552
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
553
- let execResults;
554
- let suspended;
555
- try {
556
- const result = await step.execute({
557
- mastra: this.mastra,
558
- runtimeContext,
559
- inputData: prevOutput,
560
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
561
- getInitData: () => stepResults?.input,
562
- getStepResult: (step2) => {
563
- const result2 = stepResults[step2.id];
564
- if (result2?.status === "success") {
565
- return result2.output;
1129
+ const stepCallId = randomUUID();
1130
+ let stepRes;
1131
+ try {
1132
+ stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1133
+ let execResults;
1134
+ let suspended;
1135
+ let bailed;
1136
+ try {
1137
+ if (validationError) {
1138
+ throw validationError;
1139
+ }
1140
+ const result = await step.execute({
1141
+ runId: executionContext.runId,
1142
+ mastra: this.mastra,
1143
+ requestContext,
1144
+ writer: new ToolStream(
1145
+ {
1146
+ prefix: "workflow-step",
1147
+ callId: stepCallId,
1148
+ name: step.id,
1149
+ runId: executionContext.runId
1150
+ },
1151
+ writableStream
1152
+ ),
1153
+ state: executionContext?.state ?? {},
1154
+ setState: (state) => {
1155
+ executionContext.state = state;
1156
+ },
1157
+ inputData,
1158
+ resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1159
+ tracingContext: {
1160
+ currentSpan: stepSpan
1161
+ },
1162
+ getInitData: () => stepResults?.input,
1163
+ getStepResult: getStepResult.bind(this, stepResults),
1164
+ suspend: async (suspendPayload, suspendOptions) => {
1165
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1166
+ if (suspendOptions?.resumeLabel) {
1167
+ const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
1168
+ for (const label of resumeLabel) {
1169
+ executionContext.resumeLabels[label] = {
1170
+ stepId: step.id,
1171
+ foreachIndex: executionContext.foreachIndex
1172
+ };
1173
+ }
1174
+ }
1175
+ suspended = { payload: suspendPayload };
1176
+ },
1177
+ bail: (result2) => {
1178
+ bailed = { payload: result2 };
1179
+ },
1180
+ resume: {
1181
+ steps: resume?.steps?.slice(1) || [],
1182
+ resumePayload: resume?.resumePayload,
1183
+ // @ts-ignore
1184
+ runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
1185
+ },
1186
+ [EMITTER_SYMBOL]: emitter,
1187
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
1188
+ engine: {
1189
+ step: this.inngestStep
1190
+ },
1191
+ abortSignal: abortController.signal
1192
+ });
1193
+ const endedAt = Date.now();
1194
+ execResults = {
1195
+ status: "success",
1196
+ output: result,
1197
+ startedAt,
1198
+ endedAt,
1199
+ payload: inputData,
1200
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1201
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1202
+ };
1203
+ } catch (e) {
1204
+ const stepFailure = {
1205
+ status: "failed",
1206
+ payload: inputData,
1207
+ error: e instanceof Error ? e.message : String(e),
1208
+ endedAt: Date.now(),
1209
+ startedAt,
1210
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1211
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1212
+ };
1213
+ execResults = stepFailure;
1214
+ const fallbackErrorMessage = `Step ${step.id} failed`;
1215
+ stepSpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
1216
+ throw new RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
1217
+ cause: execResults
1218
+ });
1219
+ }
1220
+ if (suspended) {
1221
+ execResults = {
1222
+ status: "suspended",
1223
+ suspendPayload: suspended.payload,
1224
+ ...execResults.output ? { suspendOutput: execResults.output } : {},
1225
+ payload: inputData,
1226
+ suspendedAt: Date.now(),
1227
+ startedAt,
1228
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1229
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1230
+ };
1231
+ } else if (bailed) {
1232
+ execResults = {
1233
+ status: "bailed",
1234
+ output: bailed.payload,
1235
+ payload: inputData,
1236
+ endedAt: Date.now(),
1237
+ startedAt
1238
+ };
1239
+ }
1240
+ if (execResults.status === "suspended") {
1241
+ await emitter.emit("watch", {
1242
+ type: "workflow-step-suspended",
1243
+ payload: {
1244
+ id: step.id,
1245
+ ...execResults
566
1246
  }
567
- return null;
568
- },
569
- suspend: async (suspendPayload) => {
570
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
571
- suspended = { payload: suspendPayload };
572
- },
573
- resume: {
574
- steps: resume?.steps?.slice(1) || [],
575
- resumePayload: resume?.resumePayload,
576
- // @ts-ignore
577
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
578
- },
579
- emitter
580
- });
581
- execResults = { status: "success", output: result };
582
- } catch (e) {
583
- execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
584
- }
585
- if (suspended) {
586
- execResults = { status: "suspended", payload: suspended.payload };
587
- }
588
- if (execResults.status === "failed") {
589
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
590
- throw execResults.error;
1247
+ });
1248
+ } else {
1249
+ await emitter.emit("watch", {
1250
+ type: "workflow-step-result",
1251
+ payload: {
1252
+ id: step.id,
1253
+ ...execResults
1254
+ }
1255
+ });
1256
+ await emitter.emit("watch", {
1257
+ type: "workflow-step-finish",
1258
+ payload: {
1259
+ id: step.id,
1260
+ metadata: {}
1261
+ }
1262
+ });
591
1263
  }
592
- }
593
- await emitter.emit("watch", {
594
- type: "watch",
595
- payload: {
596
- currentStep: {
597
- id: step.id,
598
- status: execResults.status,
599
- output: execResults.output
600
- },
601
- workflowState: {
602
- status: "running",
603
- steps: stepResults,
604
- result: null,
605
- error: null
606
- }
607
- },
608
- eventTimestamp: Date.now()
1264
+ stepSpan?.end({ output: execResults });
1265
+ return { result: execResults, executionContext, stepResults };
609
1266
  });
610
- return { result: execResults, executionContext, stepResults };
611
- });
1267
+ } catch (e) {
1268
+ const stepFailure = e instanceof Error ? e?.cause : {
1269
+ status: "failed",
1270
+ error: e instanceof Error ? e.message : String(e),
1271
+ payload: inputData,
1272
+ startedAt,
1273
+ endedAt: Date.now()
1274
+ };
1275
+ stepRes = {
1276
+ result: stepFailure,
1277
+ executionContext,
1278
+ stepResults: {
1279
+ ...stepResults,
1280
+ [step.id]: stepFailure
1281
+ }
1282
+ };
1283
+ }
1284
+ if (disableScorers !== false && stepRes.result.status === "success") {
1285
+ await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1286
+ if (step.scorers) {
1287
+ await this.runScorers({
1288
+ scorers: step.scorers,
1289
+ runId: executionContext.runId,
1290
+ input: inputData,
1291
+ output: stepRes.result,
1292
+ workflowId: executionContext.workflowId,
1293
+ stepId: step.id,
1294
+ requestContext,
1295
+ disableScorers,
1296
+ tracingContext: { currentSpan: stepSpan }
1297
+ });
1298
+ }
1299
+ });
1300
+ }
612
1301
  Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
613
1302
  Object.assign(stepResults, stepRes.stepResults);
1303
+ executionContext.state = stepRes.executionContext.state;
614
1304
  return stepRes.result;
615
1305
  }
616
1306
  async persistStepUpdate({
617
1307
  workflowId,
618
1308
  runId,
619
1309
  stepResults,
620
- executionContext
1310
+ resourceId,
1311
+ executionContext,
1312
+ serializedStepGraph,
1313
+ workflowStatus,
1314
+ result,
1315
+ error
621
1316
  }) {
622
1317
  await this.inngestStep.run(
623
1318
  `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
624
1319
  async () => {
1320
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
1321
+ if (!shouldPersistSnapshot) {
1322
+ return;
1323
+ }
625
1324
  await this.mastra?.getStorage()?.persistWorkflowSnapshot({
626
1325
  workflowName: workflowId,
627
1326
  runId,
1327
+ resourceId,
628
1328
  snapshot: {
629
1329
  runId,
630
- value: {},
1330
+ status: workflowStatus,
1331
+ value: executionContext.state,
631
1332
  context: stepResults,
632
- activePaths: [],
1333
+ activePaths: executionContext.executionPath,
1334
+ activeStepsPath: executionContext.activeStepsPath,
633
1335
  suspendedPaths: executionContext.suspendedPaths,
1336
+ resumeLabels: executionContext.resumeLabels,
1337
+ waitingPaths: {},
1338
+ serializedStepGraph,
1339
+ result,
1340
+ error,
634
1341
  // @ts-ignore
635
1342
  timestamp: Date.now()
636
1343
  }
@@ -643,74 +1350,158 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
643
1350
  runId,
644
1351
  entry,
645
1352
  prevOutput,
646
- prevStep,
647
1353
  stepResults,
648
1354
  resume,
649
1355
  executionContext,
650
1356
  emitter,
651
- runtimeContext
1357
+ abortController,
1358
+ requestContext,
1359
+ writableStream,
1360
+ disableScorers,
1361
+ tracingContext
652
1362
  }) {
1363
+ const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1364
+ type: SpanType.WORKFLOW_CONDITIONAL,
1365
+ name: `conditional: '${entry.conditions.length} conditions'`,
1366
+ input: prevOutput,
1367
+ attributes: {
1368
+ conditionCount: entry.conditions.length
1369
+ },
1370
+ tracingPolicy: this.options?.tracingPolicy
1371
+ });
653
1372
  let execResults;
654
1373
  const truthyIndexes = (await Promise.all(
655
1374
  entry.conditions.map(
656
1375
  (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1376
+ const evalSpan = conditionalSpan?.createChildSpan({
1377
+ type: SpanType.WORKFLOW_CONDITIONAL_EVAL,
1378
+ name: `condition: '${index}'`,
1379
+ input: prevOutput,
1380
+ attributes: {
1381
+ conditionIndex: index
1382
+ },
1383
+ tracingPolicy: this.options?.tracingPolicy
1384
+ });
657
1385
  try {
658
- const result = await cond({
659
- mastra: this.mastra,
660
- runtimeContext,
661
- inputData: prevOutput,
662
- getInitData: () => stepResults?.input,
663
- getStepResult: (step) => {
664
- if (!step?.id) {
665
- return null;
666
- }
667
- const result2 = stepResults[step.id];
668
- if (result2?.status === "success") {
669
- return result2.output;
1386
+ const result = await cond(
1387
+ createDeprecationProxy(
1388
+ {
1389
+ runId,
1390
+ workflowId,
1391
+ mastra: this.mastra,
1392
+ requestContext,
1393
+ retryCount: -1,
1394
+ inputData: prevOutput,
1395
+ state: executionContext.state,
1396
+ setState: (state) => {
1397
+ executionContext.state = state;
1398
+ },
1399
+ tracingContext: {
1400
+ currentSpan: evalSpan
1401
+ },
1402
+ getInitData: () => stepResults?.input,
1403
+ getStepResult: getStepResult.bind(this, stepResults),
1404
+ // TODO: this function shouldn't have suspend probably?
1405
+ suspend: async (_suspendPayload) => {
1406
+ },
1407
+ bail: () => {
1408
+ },
1409
+ abort: () => {
1410
+ abortController.abort();
1411
+ },
1412
+ [EMITTER_SYMBOL]: emitter,
1413
+ [STREAM_FORMAT_SYMBOL]: executionContext.format,
1414
+ engine: {
1415
+ step: this.inngestStep
1416
+ },
1417
+ abortSignal: abortController.signal,
1418
+ writer: new ToolStream(
1419
+ {
1420
+ prefix: "workflow-step",
1421
+ callId: randomUUID(),
1422
+ name: "conditional",
1423
+ runId
1424
+ },
1425
+ writableStream
1426
+ )
1427
+ },
1428
+ {
1429
+ paramName: "runCount",
1430
+ deprecationMessage: runCountDeprecationMessage,
1431
+ logger: this.logger
670
1432
  }
671
- return null;
672
- },
673
- // TODO: this function shouldn't have suspend probably?
674
- suspend: async (_suspendPayload) => {
675
- },
676
- emitter
1433
+ )
1434
+ );
1435
+ evalSpan?.end({
1436
+ output: result,
1437
+ attributes: {
1438
+ result: !!result
1439
+ }
677
1440
  });
678
1441
  return result ? index : null;
679
1442
  } catch (e) {
1443
+ evalSpan?.error({
1444
+ error: e instanceof Error ? e : new Error(String(e)),
1445
+ attributes: {
1446
+ result: false
1447
+ }
1448
+ });
680
1449
  return null;
681
1450
  }
682
1451
  })
683
1452
  )
684
1453
  )).filter((index) => index !== null);
685
1454
  const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1455
+ conditionalSpan?.update({
1456
+ attributes: {
1457
+ truthyIndexes,
1458
+ selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
1459
+ }
1460
+ });
686
1461
  const results = await Promise.all(
687
- stepsToRun.map(
688
- (step, index) => this.executeEntry({
689
- workflowId,
690
- runId,
691
- entry: step,
692
- prevStep,
1462
+ stepsToRun.map(async (step, index) => {
1463
+ const currStepResult = stepResults[step.step.id];
1464
+ if (currStepResult && currStepResult.status === "success") {
1465
+ return currStepResult;
1466
+ }
1467
+ const result = await this.executeStep({
1468
+ step: step.step,
1469
+ prevOutput,
693
1470
  stepResults,
694
1471
  resume,
695
1472
  executionContext: {
696
1473
  workflowId,
697
1474
  runId,
698
1475
  executionPath: [...executionContext.executionPath, index],
1476
+ activeStepsPath: executionContext.activeStepsPath,
699
1477
  suspendedPaths: executionContext.suspendedPaths,
1478
+ resumeLabels: executionContext.resumeLabels,
700
1479
  retryConfig: executionContext.retryConfig,
701
- executionSpan: executionContext.executionSpan
1480
+ state: executionContext.state
702
1481
  },
703
1482
  emitter,
704
- runtimeContext
705
- })
706
- )
1483
+ abortController,
1484
+ requestContext,
1485
+ writableStream,
1486
+ disableScorers,
1487
+ tracingContext: {
1488
+ currentSpan: conditionalSpan
1489
+ }
1490
+ });
1491
+ stepResults[step.step.id] = result;
1492
+ return result;
1493
+ })
707
1494
  );
708
1495
  const hasFailed = results.find((result) => result.status === "failed");
709
1496
  const hasSuspended = results.find((result) => result.status === "suspended");
710
1497
  if (hasFailed) {
711
1498
  execResults = { status: "failed", error: hasFailed.error };
712
1499
  } else if (hasSuspended) {
713
- execResults = { status: "suspended", payload: hasSuspended.payload };
1500
+ execResults = {
1501
+ status: "suspended",
1502
+ suspendPayload: hasSuspended.suspendPayload,
1503
+ ...hasSuspended.suspendOutput ? { suspendOutput: hasSuspended.suspendOutput } : {}
1504
+ };
714
1505
  } else {
715
1506
  execResults = {
716
1507
  status: "success",
@@ -722,8 +1513,19 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
722
1513
  }, {})
723
1514
  };
724
1515
  }
1516
+ if (execResults.status === "failed") {
1517
+ conditionalSpan?.error({
1518
+ error: new Error(execResults.error)
1519
+ });
1520
+ } else {
1521
+ conditionalSpan?.end({
1522
+ output: execResults.output || execResults
1523
+ });
1524
+ }
725
1525
  return execResults;
726
1526
  }
727
1527
  };
728
1528
 
729
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, init, serve };
1529
+ export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
1530
+ //# sourceMappingURL=index.js.map
1531
+ //# sourceMappingURL=index.js.map