@mastra/inngest 0.0.0-experimental-agent-builder-20250815195917 → 0.0.0-export-agent-memory-from-local-studio-20251112153946

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