@mastra/inngest 0.0.0-roamin-openaivoice-speak-options-passing-20250926163614 → 0.0.0-salesman-20260127182805

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,885 +1,292 @@
1
+ import { MessageList, Agent, TripWire } from '@mastra/core/agent';
2
+ import { getErrorFromUnknown, MastraError, ErrorDomain, ErrorCategory } from '@mastra/core/error';
3
+ import { EntityType, SpanType } from '@mastra/core/observability';
4
+ import { ProcessorStepOutputSchema, ProcessorStepSchema, ProcessorRunner } from '@mastra/core/processors';
5
+ import { Tool } from '@mastra/core/tools';
6
+ import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, hydrateSerializedStepErrors, Workflow } from '@mastra/core/workflows';
7
+ import { PUBSUB_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
8
+ import { z } from 'zod';
1
9
  import { randomUUID } from 'crypto';
10
+ import { RequestContext } from '@mastra/core/di';
11
+ import { NonRetriableError } from 'inngest';
2
12
  import { subscribe } from '@inngest/realtime';
3
- import { wrapMastra, AISpanType } from '@mastra/core/ai-tracing';
4
- import { RuntimeContext } from '@mastra/core/di';
5
- import { ToolStream, Tool } from '@mastra/core/tools';
6
- import { Run, Workflow, DefaultExecutionEngine, getStepResult, validateStepInput } from '@mastra/core/workflows';
7
- import { EMITTER_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
13
+ import { PubSub } from '@mastra/core/events';
14
+ import { ReadableStream } from 'stream/web';
15
+ import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
8
16
  import { serve as serve$1 } from 'inngest/hono';
9
- import { z } from 'zod';
10
17
 
11
18
  // src/index.ts
12
- function serve({
13
- mastra,
14
- inngest,
15
- functions: userFunctions = [],
16
- registerOptions
17
- }) {
18
- const wfs = mastra.getWorkflows();
19
- const workflowFunctions = Array.from(
20
- new Set(
21
- Object.values(wfs).flatMap((wf) => {
22
- if (wf instanceof InngestWorkflow) {
23
- wf.__registerMastra(mastra);
24
- return wf.getFunctions();
25
- }
26
- return [];
27
- })
28
- )
29
- );
30
- return serve$1({
31
- ...registerOptions,
32
- client: inngest,
33
- functions: [...workflowFunctions, ...userFunctions]
34
- });
35
- }
36
- var InngestRun = class extends Run {
37
- inngest;
38
- serializedStepGraph;
39
- #mastra;
40
- constructor(params, inngest) {
41
- super(params);
42
- this.inngest = inngest;
43
- this.serializedStepGraph = params.serializedStepGraph;
44
- this.#mastra = params.mastra;
19
+ var InngestExecutionEngine = class extends DefaultExecutionEngine {
20
+ inngestStep;
21
+ inngestAttempts;
22
+ constructor(mastra, inngestStep, inngestAttempts = 0, options) {
23
+ super({ mastra, options });
24
+ this.inngestStep = inngestStep;
25
+ this.inngestAttempts = inngestAttempts;
45
26
  }
46
- async getRuns(eventId) {
47
- const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
48
- headers: {
49
- Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
50
- }
27
+ // =============================================================================
28
+ // Hook Overrides
29
+ // =============================================================================
30
+ /**
31
+ * Format errors while preserving Error instances and their custom properties.
32
+ * Uses getErrorFromUnknown to ensure all error properties are preserved.
33
+ */
34
+ formatResultError(error, lastOutput) {
35
+ const outputError = lastOutput?.error;
36
+ const errorSource = error || outputError;
37
+ const errorInstance = getErrorFromUnknown(errorSource, {
38
+ serializeStack: true,
39
+ // Include stack in JSON for better debugging in Inngest
40
+ fallbackMessage: "Unknown workflow error"
51
41
  });
52
- const json = await response.json();
53
- return json.data;
42
+ return errorInstance.toJSON();
54
43
  }
55
- async getRunOutput(eventId) {
56
- let runs = await this.getRuns(eventId);
57
- while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
58
- await new Promise((resolve) => setTimeout(resolve, 1e3));
59
- runs = await this.getRuns(eventId);
60
- if (runs?.[0]?.status === "Failed") {
61
- throw new Error(`Function run ${runs?.[0]?.status}`);
62
- } else if (runs?.[0]?.status === "Cancelled") {
63
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
64
- workflowName: this.workflowId,
65
- runId: this.runId
66
- });
67
- return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
68
- }
69
- }
70
- return runs?.[0];
44
+ /**
45
+ * Detect InngestWorkflow instances for special nested workflow handling
46
+ */
47
+ isNestedWorkflowStep(step) {
48
+ return step instanceof InngestWorkflow;
71
49
  }
72
- async sendEvent(event, data) {
73
- await this.inngest.send({
74
- name: `user-event-${event}`,
75
- data
76
- });
50
+ /**
51
+ * Inngest requires requestContext serialization for memoization.
52
+ * When steps are replayed, the original function doesn't re-execute,
53
+ * so requestContext modifications must be captured and restored.
54
+ */
55
+ requiresDurableContextSerialization() {
56
+ return true;
77
57
  }
78
- async cancel() {
79
- await this.inngest.send({
80
- name: `cancel.workflow.${this.workflowId}`,
81
- data: {
82
- runId: this.runId
58
+ /**
59
+ * Execute a step with retry logic for Inngest.
60
+ * Retries are handled via step-level retry (RetryAfterError thrown INSIDE step.run()).
61
+ * After retries exhausted, error propagates here and we return a failed result.
62
+ */
63
+ async executeStepWithRetry(stepId, runStep, params) {
64
+ for (let i = 0; i < params.retries + 1; i++) {
65
+ if (i > 0 && params.delay) {
66
+ await new Promise((resolve) => setTimeout(resolve, params.delay));
83
67
  }
84
- });
85
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
86
- workflowName: this.workflowId,
87
- runId: this.runId
88
- });
89
- if (snapshot) {
90
- await this.#mastra?.storage?.persistWorkflowSnapshot({
91
- workflowName: this.workflowId,
92
- runId: this.runId,
93
- resourceId: this.resourceId,
94
- snapshot: {
95
- ...snapshot,
96
- status: "canceled"
68
+ try {
69
+ const result = await this.wrapDurableOperation(stepId, runStep);
70
+ return { ok: true, result };
71
+ } catch (e) {
72
+ if (i === params.retries) {
73
+ const cause = e?.cause;
74
+ if (cause?.status === "failed") {
75
+ params.stepSpan?.error({
76
+ error: e,
77
+ attributes: { status: "failed" }
78
+ });
79
+ if (cause.error && !(cause.error instanceof Error)) {
80
+ cause.error = getErrorFromUnknown(cause.error, { serializeStack: false });
81
+ }
82
+ return { ok: false, error: cause };
83
+ }
84
+ const errorInstance = getErrorFromUnknown(e, {
85
+ serializeStack: false,
86
+ fallbackMessage: "Unknown step execution error"
87
+ });
88
+ params.stepSpan?.error({
89
+ error: errorInstance,
90
+ attributes: { status: "failed" }
91
+ });
92
+ return {
93
+ ok: false,
94
+ error: {
95
+ status: "failed",
96
+ error: errorInstance,
97
+ endedAt: Date.now()
98
+ }
99
+ };
97
100
  }
98
- });
99
- }
100
- }
101
- async start({
102
- inputData
103
- }) {
104
- await this.#mastra.getStorage()?.persistWorkflowSnapshot({
105
- workflowName: this.workflowId,
106
- runId: this.runId,
107
- resourceId: this.resourceId,
108
- snapshot: {
109
- runId: this.runId,
110
- serializedStepGraph: this.serializedStepGraph,
111
- value: {},
112
- context: {},
113
- activePaths: [],
114
- suspendedPaths: {},
115
- waitingPaths: {},
116
- timestamp: Date.now(),
117
- status: "running"
118
- }
119
- });
120
- const inputDataToUse = await this._validateInput(inputData);
121
- const eventOutput = await this.inngest.send({
122
- name: `workflow.${this.workflowId}`,
123
- data: {
124
- inputData: inputDataToUse,
125
- runId: this.runId,
126
- resourceId: this.resourceId
127
101
  }
128
- });
129
- const eventId = eventOutput.ids[0];
130
- if (!eventId) {
131
- throw new Error("Event ID is not set");
132
- }
133
- const runOutput = await this.getRunOutput(eventId);
134
- const result = runOutput?.output?.result;
135
- if (result.status === "failed") {
136
- result.error = new Error(result.error);
137
- }
138
- if (result.status !== "suspended") {
139
- this.cleanup?.();
140
102
  }
141
- return result;
142
- }
143
- async resume(params) {
144
- const p = this._resume(params).then((result) => {
145
- if (result.status !== "suspended") {
146
- this.closeStreamAction?.().catch(() => {
147
- });
148
- }
149
- return result;
150
- });
151
- this.executionResults = p;
152
- return p;
103
+ return { ok: false, error: { status: "failed", error: new Error("Unknown error"), endedAt: Date.now() } };
153
104
  }
154
- async _resume(params) {
155
- const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
156
- (step) => typeof step === "string" ? step : step?.id
157
- );
158
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
159
- workflowName: this.workflowId,
160
- runId: this.runId
161
- });
162
- const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
163
- const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
164
- const eventOutput = await this.inngest.send({
165
- name: `workflow.${this.workflowId}`,
166
- data: {
167
- inputData: resumeDataToUse,
168
- runId: this.runId,
169
- workflowId: this.workflowId,
170
- stepResults: snapshot?.context,
171
- resume: {
172
- steps,
173
- stepResults: snapshot?.context,
174
- resumePayload: resumeDataToUse,
175
- // @ts-ignore
176
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
177
- }
178
- }
179
- });
180
- const eventId = eventOutput.ids[0];
181
- if (!eventId) {
182
- throw new Error("Event ID is not set");
183
- }
184
- const runOutput = await this.getRunOutput(eventId);
185
- const result = runOutput?.output?.result;
186
- if (result.status === "failed") {
187
- result.error = new Error(result.error);
188
- }
189
- return result;
105
+ /**
106
+ * Use Inngest's sleep primitive for durability
107
+ */
108
+ async executeSleepDuration(duration, sleepId, workflowId) {
109
+ await this.inngestStep.sleep(`workflow.${workflowId}.sleep.${sleepId}`, duration < 0 ? 0 : duration);
190
110
  }
191
- watch(cb, type = "watch") {
192
- let active = true;
193
- const streamPromise = subscribe(
194
- {
195
- channel: `workflow:${this.workflowId}:${this.runId}`,
196
- topics: [type],
197
- app: this.inngest
198
- },
199
- (message) => {
200
- if (active) {
201
- cb(message.data);
202
- }
203
- }
204
- );
205
- return () => {
206
- active = false;
207
- streamPromise.then(async (stream) => {
208
- return stream.cancel();
209
- }).catch((err) => {
210
- console.error(err);
211
- });
212
- };
111
+ /**
112
+ * Use Inngest's sleepUntil primitive for durability
113
+ */
114
+ async executeSleepUntilDate(date, sleepUntilId, workflowId) {
115
+ await this.inngestStep.sleepUntil(`workflow.${workflowId}.sleepUntil.${sleepUntilId}`, date);
213
116
  }
214
- stream({ inputData, runtimeContext } = {}) {
215
- const { readable, writable } = new TransformStream();
216
- const writer = writable.getWriter();
217
- const unwatch = this.watch(async (event) => {
218
- try {
219
- const e = {
220
- ...event,
221
- type: event.type.replace("workflow-", "")
222
- };
223
- await writer.write(e);
224
- } catch {
225
- }
226
- }, "watch-v2");
227
- this.closeStreamAction = async () => {
228
- unwatch();
117
+ /**
118
+ * Wrap durable operations in Inngest step.run() for durability.
119
+ *
120
+ * IMPORTANT: Errors are wrapped with a cause structure before throwing.
121
+ * This is necessary because Inngest's error serialization (serialize-error-cjs)
122
+ * only captures standard Error properties (message, name, stack, code, cause).
123
+ * Custom properties like statusCode, responseHeaders from AI SDK errors would
124
+ * be lost. By putting our serialized error (via getErrorFromUnknown with toJSON())
125
+ * in the cause property, we ensure custom properties survive serialization.
126
+ * The cause property is in serialize-error-cjs's allowlist, and when the cause
127
+ * object is finally JSON.stringify'd, our error's toJSON() is called.
128
+ */
129
+ async wrapDurableOperation(operationId, operationFn) {
130
+ return this.inngestStep.run(operationId, async () => {
229
131
  try {
230
- await writer.close();
231
- } catch (err) {
232
- console.error("Error closing stream:", err);
233
- } finally {
234
- writer.releaseLock();
235
- }
236
- };
237
- this.executionResults = this.start({ inputData, runtimeContext }).then((result) => {
238
- if (result.status !== "suspended") {
239
- this.closeStreamAction?.().catch(() => {
132
+ return await operationFn();
133
+ } catch (e) {
134
+ const errorInstance = getErrorFromUnknown(e, {
135
+ serializeStack: false,
136
+ fallbackMessage: "Unknown step execution error"
137
+ });
138
+ throw new Error(errorInstance.message, {
139
+ cause: {
140
+ status: "failed",
141
+ error: errorInstance,
142
+ endedAt: Date.now()
143
+ }
240
144
  });
241
145
  }
242
- return result;
243
146
  });
244
- return {
245
- stream: readable,
246
- getWorkflowState: () => this.executionResults
247
- };
248
147
  }
249
- };
250
- var InngestWorkflow = class _InngestWorkflow extends Workflow {
251
- #mastra;
252
- inngest;
253
- function;
254
- flowControlConfig;
255
- constructor(params, inngest) {
256
- const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
257
- super(workflowParams);
258
- const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
259
- ([_, value]) => value !== void 0
260
- );
261
- this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
262
- this.#mastra = params.mastra;
263
- this.inngest = inngest;
148
+ /**
149
+ * Provide Inngest step primitive in engine context
150
+ */
151
+ getEngineContext() {
152
+ return { step: this.inngestStep };
264
153
  }
265
- async getWorkflowRuns(args) {
266
- const storage = this.#mastra?.getStorage();
267
- if (!storage) {
268
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
269
- return { runs: [], total: 0 };
270
- }
271
- return storage.getWorkflowRuns({ workflowName: this.id, ...args ?? {} });
154
+ /**
155
+ * For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
156
+ * (wrapped in step.run for durability), not in execute(). Override to skip.
157
+ */
158
+ async invokeLifecycleCallbacks(_result) {
272
159
  }
273
- async getWorkflowRunById(runId) {
274
- const storage = this.#mastra?.getStorage();
275
- if (!storage) {
276
- this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
277
- return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
278
- }
279
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
280
- return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
160
+ /**
161
+ * Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
162
+ */
163
+ async invokeLifecycleCallbacksInternal(result) {
164
+ return super.invokeLifecycleCallbacks(result);
281
165
  }
282
- __registerMastra(mastra) {
283
- this.#mastra = mastra;
284
- this.executionEngine.__registerMastra(mastra);
285
- const updateNested = (step) => {
286
- if ((step.type === "step" || step.type === "loop" || step.type === "foreach") && step.step instanceof _InngestWorkflow) {
287
- step.step.__registerMastra(mastra);
288
- } else if (step.type === "parallel" || step.type === "conditional") {
289
- for (const subStep of step.steps) {
290
- updateNested(subStep);
291
- }
292
- }
293
- };
294
- if (this.executionGraph.steps.length) {
295
- for (const step of this.executionGraph.steps) {
296
- updateNested(step);
297
- }
166
+ // =============================================================================
167
+ // Durable Span Lifecycle Hooks
168
+ // =============================================================================
169
+ /**
170
+ * Create a step span durably - on first execution, creates and exports span.
171
+ * On replay, returns cached span data without re-creating.
172
+ */
173
+ async createStepSpan(params) {
174
+ const { executionContext, operationId, options, parentSpan } = params;
175
+ const parentSpanId = parentSpan?.id ?? executionContext.tracingIds?.workflowSpanId;
176
+ const exportedSpan = await this.wrapDurableOperation(operationId, async () => {
177
+ const observability = this.mastra?.observability?.getSelectedInstance({});
178
+ if (!observability) return void 0;
179
+ const span = observability.startSpan({
180
+ ...options,
181
+ entityType: options.entityType,
182
+ traceId: executionContext.tracingIds?.traceId,
183
+ parentSpanId
184
+ });
185
+ return span?.exportSpan();
186
+ });
187
+ if (exportedSpan) {
188
+ const observability = this.mastra?.observability?.getSelectedInstance({});
189
+ return observability?.rebuildSpan(exportedSpan);
298
190
  }
191
+ return void 0;
299
192
  }
300
193
  /**
301
- * @deprecated Use createRunAsync() instead.
302
- * @throws {Error} Always throws an error directing users to use createRunAsync()
194
+ * End a step span durably.
303
195
  */
304
- createRun(_options) {
305
- throw new Error(
306
- "createRun() has been deprecated. Please use createRunAsync() instead.\n\nMigration guide:\n Before: const run = workflow.createRun();\n After: const run = await workflow.createRunAsync();\n\nNote: createRunAsync() is an async method, so make sure your calling function is async."
307
- );
308
- }
309
- async createRunAsync(options) {
310
- const runIdToUse = options?.runId || randomUUID();
311
- const run = this.runs.get(runIdToUse) ?? new InngestRun(
312
- {
313
- workflowId: this.id,
314
- runId: runIdToUse,
315
- resourceId: options?.resourceId,
316
- executionEngine: this.executionEngine,
317
- executionGraph: this.executionGraph,
318
- serializedStepGraph: this.serializedStepGraph,
319
- mastra: this.#mastra,
320
- retryConfig: this.retryConfig,
321
- cleanup: () => this.runs.delete(runIdToUse),
322
- workflowSteps: this.steps
323
- },
324
- this.inngest
325
- );
326
- this.runs.set(runIdToUse, run);
327
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
328
- if (!workflowSnapshotInStorage) {
329
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
330
- workflowName: this.id,
331
- runId: runIdToUse,
332
- resourceId: options?.resourceId,
333
- snapshot: {
334
- runId: runIdToUse,
335
- status: "pending",
336
- value: {},
337
- context: {},
338
- activePaths: [],
339
- waitingPaths: {},
340
- serializedStepGraph: this.serializedStepGraph,
341
- suspendedPaths: {},
342
- result: void 0,
343
- error: void 0,
344
- // @ts-ignore
345
- timestamp: Date.now()
346
- }
347
- });
348
- }
349
- return run;
350
- }
351
- getFunction() {
352
- if (this.function) {
353
- return this.function;
354
- }
355
- this.function = this.inngest.createFunction(
356
- {
357
- id: `workflow.${this.id}`,
358
- // @ts-ignore
359
- retries: this.retryConfig?.attempts ?? 0,
360
- cancelOn: [{ event: `cancel.workflow.${this.id}` }],
361
- // Spread flow control configuration
362
- ...this.flowControlConfig
363
- },
364
- { event: `workflow.${this.id}` },
365
- async ({ event, step, attempt, publish }) => {
366
- let { inputData, runId, resourceId, resume } = event.data;
367
- if (!runId) {
368
- runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
369
- return randomUUID();
370
- });
371
- }
372
- const emitter = {
373
- emit: async (event2, data) => {
374
- if (!publish) {
375
- return;
376
- }
377
- try {
378
- await publish({
379
- channel: `workflow:${this.id}:${runId}`,
380
- topic: event2,
381
- data
382
- });
383
- } catch (err) {
384
- this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
385
- }
386
- },
387
- on: (_event, _callback) => {
388
- },
389
- off: (_event, _callback) => {
390
- },
391
- once: (_event, _callback) => {
392
- }
393
- };
394
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
395
- const result = await engine.execute({
396
- workflowId: this.id,
397
- runId,
398
- resourceId,
399
- graph: this.executionGraph,
400
- serializedStepGraph: this.serializedStepGraph,
401
- input: inputData,
402
- emitter,
403
- retryConfig: this.retryConfig,
404
- runtimeContext: new RuntimeContext(),
405
- // TODO
406
- resume,
407
- abortController: new AbortController(),
408
- currentSpan: void 0
409
- // TODO: Pass actual parent AI span from workflow execution context
410
- });
411
- return { result, runId };
412
- }
413
- );
414
- return this.function;
415
- }
416
- getNestedFunctions(steps) {
417
- return steps.flatMap((step) => {
418
- if (step.type === "step" || step.type === "loop" || step.type === "foreach") {
419
- if (step.step instanceof _InngestWorkflow) {
420
- return [step.step.getFunction(), ...step.step.getNestedFunctions(step.step.executionGraph.steps)];
421
- }
422
- return [];
423
- } else if (step.type === "parallel" || step.type === "conditional") {
424
- return this.getNestedFunctions(step.steps);
425
- }
426
- return [];
196
+ async endStepSpan(params) {
197
+ const { span, operationId, endOptions } = params;
198
+ if (!span) return;
199
+ await this.wrapDurableOperation(operationId, async () => {
200
+ span.end(endOptions);
427
201
  });
428
202
  }
429
- getFunctions() {
430
- return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
431
- }
432
- };
433
- function isAgent(params) {
434
- return params?.component === "AGENT";
435
- }
436
- function isTool(params) {
437
- return params instanceof Tool;
438
- }
439
- function createStep(params) {
440
- if (isAgent(params)) {
441
- return {
442
- id: params.name,
443
- description: params.getDescription(),
444
- // @ts-ignore
445
- inputSchema: z.object({
446
- prompt: z.string()
447
- }),
448
- // @ts-ignore
449
- outputSchema: z.object({
450
- text: z.string()
451
- }),
452
- execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
453
- let streamPromise = {};
454
- streamPromise.promise = new Promise((resolve, reject) => {
455
- streamPromise.resolve = resolve;
456
- streamPromise.reject = reject;
457
- });
458
- const toolData = {
459
- name: params.name,
460
- args: inputData
461
- };
462
- const { fullStream } = await params.stream(inputData.prompt, {
463
- runtimeContext,
464
- tracingContext,
465
- onFinish: (result) => {
466
- streamPromise.resolve(result.text);
467
- },
468
- abortSignal
469
- });
470
- if (abortSignal.aborted) {
471
- return abort();
472
- }
473
- await emitter.emit("watch-v2", {
474
- type: "tool-call-streaming-start",
475
- ...toolData ?? {}
476
- });
477
- for await (const chunk of fullStream) {
478
- if (chunk.type === "text-delta") {
479
- await emitter.emit("watch-v2", {
480
- type: "tool-call-delta",
481
- ...toolData ?? {},
482
- argsTextDelta: chunk.textDelta
483
- });
484
- }
485
- }
486
- await emitter.emit("watch-v2", {
487
- type: "tool-call-streaming-finish",
488
- ...toolData ?? {}
489
- });
490
- return {
491
- text: await streamPromise.promise
492
- };
493
- },
494
- component: params.component
495
- };
496
- }
497
- if (isTool(params)) {
498
- if (!params.inputSchema || !params.outputSchema) {
499
- throw new Error("Tool must have input and output schemas defined");
500
- }
501
- return {
502
- // TODO: tool probably should have strong id type
503
- // @ts-ignore
504
- id: params.id,
505
- description: params.description,
506
- inputSchema: params.inputSchema,
507
- outputSchema: params.outputSchema,
508
- execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
509
- return params.execute({
510
- context: inputData,
511
- mastra: wrapMastra(mastra, tracingContext),
512
- runtimeContext,
513
- tracingContext,
514
- suspend,
515
- resumeData
516
- });
517
- },
518
- component: "TOOL"
519
- };
520
- }
521
- return {
522
- id: params.id,
523
- description: params.description,
524
- inputSchema: params.inputSchema,
525
- outputSchema: params.outputSchema,
526
- resumeSchema: params.resumeSchema,
527
- suspendSchema: params.suspendSchema,
528
- execute: params.execute
529
- };
530
- }
531
- function init(inngest) {
532
- return {
533
- createWorkflow(params) {
534
- return new InngestWorkflow(params, inngest);
535
- },
536
- createStep,
537
- cloneStep(step, opts) {
538
- return {
539
- id: opts.id,
540
- description: step.description,
541
- inputSchema: step.inputSchema,
542
- outputSchema: step.outputSchema,
543
- execute: step.execute,
544
- component: step.component
545
- };
546
- },
547
- cloneWorkflow(workflow, opts) {
548
- const wf = new Workflow({
549
- id: opts.id,
550
- inputSchema: workflow.inputSchema,
551
- outputSchema: workflow.outputSchema,
552
- steps: workflow.stepDefs,
553
- mastra: workflow.mastra
554
- });
555
- wf.setStepFlow(workflow.stepGraph);
556
- wf.commit();
557
- return wf;
558
- }
559
- };
560
- }
561
- var InngestExecutionEngine = class extends DefaultExecutionEngine {
562
- inngestStep;
563
- inngestAttempts;
564
- constructor(mastra, inngestStep, inngestAttempts = 0, options) {
565
- super({ mastra, options });
566
- this.inngestStep = inngestStep;
567
- this.inngestAttempts = inngestAttempts;
568
- }
569
- async execute(params) {
570
- await params.emitter.emit("watch-v2", {
571
- type: "workflow-start",
572
- payload: { runId: params.runId }
573
- });
574
- const result = await super.execute(params);
575
- await params.emitter.emit("watch-v2", {
576
- type: "workflow-finish",
577
- payload: { runId: params.runId }
203
+ /**
204
+ * Record error on step span durably.
205
+ */
206
+ async errorStepSpan(params) {
207
+ const { span, operationId, errorOptions } = params;
208
+ if (!span) return;
209
+ await this.wrapDurableOperation(operationId, async () => {
210
+ span.error(errorOptions);
578
211
  });
579
- return result;
580
212
  }
581
- async fmtReturnValue(executionSpan, emitter, stepResults, lastOutput, error) {
582
- const base = {
583
- status: lastOutput.status,
584
- steps: stepResults
585
- };
586
- if (lastOutput.status === "success") {
587
- await emitter.emit("watch", {
588
- type: "watch",
589
- payload: {
590
- workflowState: {
591
- status: lastOutput.status,
592
- steps: stepResults,
593
- result: lastOutput.output
594
- }
595
- },
596
- eventTimestamp: Date.now()
597
- });
598
- base.result = lastOutput.output;
599
- } else if (lastOutput.status === "failed") {
600
- base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
601
- await emitter.emit("watch", {
602
- type: "watch",
603
- payload: {
604
- workflowState: {
605
- status: lastOutput.status,
606
- steps: stepResults,
607
- result: null,
608
- error: base.error
609
- }
610
- },
611
- eventTimestamp: Date.now()
612
- });
613
- } else if (lastOutput.status === "suspended") {
614
- await emitter.emit("watch", {
615
- type: "watch",
616
- payload: {
617
- workflowState: {
618
- status: lastOutput.status,
619
- steps: stepResults,
620
- result: null,
621
- error: null
622
- }
623
- },
624
- eventTimestamp: Date.now()
625
- });
626
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
627
- if (stepResult?.status === "suspended") {
628
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
629
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
630
- }
631
- return [];
213
+ /**
214
+ * Create a generic child span durably (for control-flow operations).
215
+ * On first execution, creates and exports span. On replay, returns cached span data.
216
+ */
217
+ async createChildSpan(params) {
218
+ const { executionContext, operationId, options, parentSpan } = params;
219
+ const parentSpanId = parentSpan?.id ?? executionContext.tracingIds?.workflowSpanId;
220
+ const exportedSpan = await this.wrapDurableOperation(operationId, async () => {
221
+ const observability = this.mastra?.observability?.getSelectedInstance({});
222
+ if (!observability) return void 0;
223
+ const span = observability.startSpan({
224
+ ...options,
225
+ traceId: executionContext.tracingIds?.traceId,
226
+ parentSpanId
632
227
  });
633
- base.suspended = suspendedStepIds;
634
- }
635
- executionSpan?.end();
636
- return base;
637
- }
638
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
639
- // await this.inngestStep.sleep(id, duration);
640
- // }
641
- async executeSleep({
642
- workflowId,
643
- runId,
644
- entry,
645
- prevOutput,
646
- stepResults,
647
- emitter,
648
- abortController,
649
- runtimeContext,
650
- executionContext,
651
- writableStream,
652
- tracingContext
653
- }) {
654
- let { duration, fn } = entry;
655
- const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
656
- type: AISpanType.WORKFLOW_SLEEP,
657
- name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
658
- attributes: {
659
- durationMs: duration,
660
- sleepType: fn ? "dynamic" : "fixed"
661
- },
662
- tracingPolicy: this.options?.tracingPolicy
228
+ return span?.exportSpan();
663
229
  });
664
- if (fn) {
665
- const stepCallId = randomUUID();
666
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
667
- return await fn({
668
- runId,
669
- workflowId,
670
- mastra: this.mastra,
671
- runtimeContext,
672
- inputData: prevOutput,
673
- runCount: -1,
674
- tracingContext: {
675
- currentSpan: sleepSpan
676
- },
677
- getInitData: () => stepResults?.input,
678
- getStepResult: getStepResult.bind(this, stepResults),
679
- // TODO: this function shouldn't have suspend probably?
680
- suspend: async (_suspendPayload) => {
681
- },
682
- bail: () => {
683
- },
684
- abort: () => {
685
- abortController?.abort();
686
- },
687
- [EMITTER_SYMBOL]: emitter,
688
- // TODO: add streamVNext support
689
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
690
- engine: { step: this.inngestStep },
691
- abortSignal: abortController?.signal,
692
- writer: new ToolStream(
693
- {
694
- prefix: "workflow-step",
695
- callId: stepCallId,
696
- name: "sleep",
697
- runId
698
- },
699
- writableStream
700
- )
701
- });
702
- });
703
- sleepSpan?.update({
704
- attributes: {
705
- durationMs: duration
706
- }
707
- });
708
- }
709
- try {
710
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
711
- sleepSpan?.end();
712
- } catch (e) {
713
- sleepSpan?.error({ error: e });
714
- throw e;
230
+ if (exportedSpan) {
231
+ const observability = this.mastra?.observability?.getSelectedInstance({});
232
+ return observability?.rebuildSpan(exportedSpan);
715
233
  }
234
+ return void 0;
716
235
  }
717
- async executeSleepUntil({
718
- workflowId,
719
- runId,
720
- entry,
721
- prevOutput,
722
- stepResults,
723
- emitter,
724
- abortController,
725
- runtimeContext,
726
- executionContext,
727
- writableStream,
728
- tracingContext
729
- }) {
730
- let { date, fn } = entry;
731
- const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
732
- type: AISpanType.WORKFLOW_SLEEP,
733
- name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
734
- attributes: {
735
- untilDate: date,
736
- durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
737
- sleepType: fn ? "dynamic" : "fixed"
738
- },
739
- tracingPolicy: this.options?.tracingPolicy
236
+ /**
237
+ * End a generic child span durably (for control-flow operations).
238
+ */
239
+ async endChildSpan(params) {
240
+ const { span, operationId, endOptions } = params;
241
+ if (!span) return;
242
+ await this.wrapDurableOperation(operationId, async () => {
243
+ span.end(endOptions);
740
244
  });
741
- if (fn) {
742
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
743
- const stepCallId = randomUUID();
744
- return await fn({
745
- runId,
746
- workflowId,
747
- mastra: this.mastra,
748
- runtimeContext,
749
- inputData: prevOutput,
750
- runCount: -1,
751
- tracingContext: {
752
- currentSpan: sleepUntilSpan
753
- },
754
- getInitData: () => stepResults?.input,
755
- getStepResult: getStepResult.bind(this, stepResults),
756
- // TODO: this function shouldn't have suspend probably?
757
- suspend: async (_suspendPayload) => {
758
- },
759
- bail: () => {
760
- },
761
- abort: () => {
762
- abortController?.abort();
763
- },
764
- [EMITTER_SYMBOL]: emitter,
765
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
766
- // TODO: add streamVNext support
767
- engine: { step: this.inngestStep },
768
- abortSignal: abortController?.signal,
769
- writer: new ToolStream(
770
- {
771
- prefix: "workflow-step",
772
- callId: stepCallId,
773
- name: "sleep",
774
- runId
775
- },
776
- writableStream
777
- )
778
- });
779
- });
780
- if (date && !(date instanceof Date)) {
781
- date = new Date(date);
782
- }
783
- const time = !date ? 0 : date.getTime() - Date.now();
784
- sleepUntilSpan?.update({
785
- attributes: {
786
- durationMs: Math.max(0, time)
787
- }
788
- });
789
- }
790
- if (!(date instanceof Date)) {
791
- sleepUntilSpan?.end();
792
- return;
793
- }
794
- try {
795
- await this.inngestStep.sleepUntil(entry.id, date);
796
- sleepUntilSpan?.end();
797
- } catch (e) {
798
- sleepUntilSpan?.error({ error: e });
799
- throw e;
800
- }
801
245
  }
802
- async executeWaitForEvent({ event, timeout }) {
803
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
804
- event: `user-event-${event}`,
805
- timeout: timeout ?? 5e3
246
+ /**
247
+ * Record error on a generic child span durably (for control-flow operations).
248
+ */
249
+ async errorChildSpan(params) {
250
+ const { span, operationId, errorOptions } = params;
251
+ if (!span) return;
252
+ await this.wrapDurableOperation(operationId, async () => {
253
+ span.error(errorOptions);
806
254
  });
807
- if (eventData === null) {
808
- throw "Timeout waiting for event";
809
- }
810
- return eventData?.data;
811
255
  }
812
- async executeStep({
813
- step,
814
- stepResults,
815
- executionContext,
816
- resume,
817
- prevOutput,
818
- emitter,
819
- abortController,
820
- runtimeContext,
821
- tracingContext,
822
- writableStream,
823
- disableScorers
824
- }) {
825
- const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
826
- name: `workflow step: '${step.id}'`,
827
- type: AISpanType.WORKFLOW_STEP,
828
- input: prevOutput,
829
- attributes: {
830
- stepId: step.id
831
- },
832
- tracingPolicy: this.options?.tracingPolicy
833
- });
834
- const { inputData, validationError } = await validateStepInput({
835
- prevOutput,
256
+ /**
257
+ * Execute nested InngestWorkflow using inngestStep.invoke() for durability.
258
+ * This MUST be called directly (not inside step.run()) due to Inngest constraints.
259
+ */
260
+ async executeWorkflowStep(params) {
261
+ if (!(params.step instanceof InngestWorkflow)) {
262
+ return null;
263
+ }
264
+ const {
836
265
  step,
837
- validateInputs: this.options?.validateInputs ?? false
838
- });
839
- const startedAt = await this.inngestStep.run(
840
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
841
- async () => {
842
- const startedAt2 = Date.now();
843
- await emitter.emit("watch", {
844
- type: "watch",
845
- payload: {
846
- currentStep: {
847
- id: step.id,
848
- status: "running"
849
- },
850
- workflowState: {
851
- status: "running",
852
- steps: {
853
- ...stepResults,
854
- [step.id]: {
855
- status: "running"
856
- }
857
- },
858
- result: null,
859
- error: null
860
- }
861
- },
862
- eventTimestamp: Date.now()
863
- });
864
- await emitter.emit("watch-v2", {
865
- type: "workflow-step-start",
866
- payload: {
867
- id: step.id,
868
- status: "running",
869
- payload: inputData,
870
- startedAt: startedAt2
871
- }
872
- });
873
- return startedAt2;
874
- }
875
- );
876
- if (step instanceof InngestWorkflow) {
877
- const isResume = !!resume?.steps?.length;
878
- let result;
879
- let runId;
266
+ stepResults,
267
+ executionContext,
268
+ resume,
269
+ timeTravel,
270
+ prevOutput,
271
+ inputData,
272
+ pubsub,
273
+ startedAt,
274
+ perStep,
275
+ stepSpan
276
+ } = params;
277
+ const nestedTracingContext = executionContext.tracingIds?.traceId ? {
278
+ traceId: executionContext.tracingIds.traceId,
279
+ parentSpanId: stepSpan?.id
280
+ } : void 0;
281
+ const isResume = !!resume?.steps?.length;
282
+ let result;
283
+ let runId;
284
+ const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
285
+ try {
880
286
  if (isResume) {
881
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
882
- const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
287
+ runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
288
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
289
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
883
290
  workflowName: step.id,
884
291
  runId
885
292
  });
@@ -887,51 +294,90 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
887
294
  function: step.getFunction(),
888
295
  data: {
889
296
  inputData,
297
+ initialState: executionContext.state ?? snapshot?.value ?? {},
890
298
  runId,
891
299
  resume: {
892
300
  runId,
893
301
  steps: resume.steps.slice(1),
894
302
  stepResults: snapshot?.context,
895
303
  resumePayload: resume.resumePayload,
896
- // @ts-ignore
897
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
898
- }
304
+ resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
305
+ },
306
+ outputOptions: { includeState: true },
307
+ perStep,
308
+ tracingOptions: nestedTracingContext
899
309
  }
900
310
  });
901
311
  result = invokeResp.result;
902
312
  runId = invokeResp.runId;
903
- } else {
904
- const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
313
+ executionContext.state = invokeResp.result.state;
314
+ } else if (isTimeTravel) {
315
+ const workflowsStoreForTimeTravel = await this.mastra?.getStorage()?.getStore("workflows");
316
+ const snapshot = await workflowsStoreForTimeTravel?.loadWorkflowSnapshot({
317
+ workflowName: step.id,
318
+ runId: executionContext.runId
319
+ }) ?? { context: {} };
320
+ const timeTravelParams = createTimeTravelExecutionParams({
321
+ steps: timeTravel.steps.slice(1),
322
+ inputData: timeTravel.inputData,
323
+ resumeData: timeTravel.resumeData,
324
+ context: timeTravel.nestedStepResults?.[step.id] ?? {},
325
+ nestedStepsContext: timeTravel.nestedStepResults ?? {},
326
+ snapshot,
327
+ graph: step.buildExecutionGraph()
328
+ });
329
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
330
+ function: step.getFunction(),
331
+ data: {
332
+ timeTravel: timeTravelParams,
333
+ initialState: executionContext.state ?? {},
334
+ runId: executionContext.runId,
335
+ outputOptions: { includeState: true },
336
+ perStep,
337
+ tracingOptions: nestedTracingContext
338
+ }
339
+ });
340
+ result = invokeResp.result;
341
+ runId = invokeResp.runId;
342
+ executionContext.state = invokeResp.result.state;
343
+ } else {
344
+ const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
905
345
  function: step.getFunction(),
906
346
  data: {
907
- inputData
347
+ inputData,
348
+ initialState: executionContext.state ?? {},
349
+ outputOptions: { includeState: true },
350
+ perStep,
351
+ tracingOptions: nestedTracingContext
908
352
  }
909
353
  });
910
354
  result = invokeResp.result;
911
355
  runId = invokeResp.runId;
356
+ executionContext.state = invokeResp.result.state;
912
357
  }
913
- const res = await this.inngestStep.run(
914
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
915
- async () => {
916
- if (result.status === "failed") {
917
- await emitter.emit("watch", {
918
- type: "watch",
919
- payload: {
920
- currentStep: {
921
- id: step.id,
922
- status: "failed",
923
- error: result?.error
924
- },
925
- workflowState: {
926
- status: "running",
927
- steps: stepResults,
928
- result: null,
929
- error: null
930
- }
931
- },
932
- eventTimestamp: Date.now()
933
- });
934
- await emitter.emit("watch-v2", {
358
+ } catch (e) {
359
+ const errorCause = e?.cause;
360
+ if (errorCause && typeof errorCause === "object") {
361
+ result = errorCause;
362
+ runId = errorCause.runId || randomUUID();
363
+ } else {
364
+ runId = randomUUID();
365
+ result = {
366
+ status: "failed",
367
+ error: e instanceof Error ? e : new Error(String(e)),
368
+ steps: {},
369
+ input: inputData
370
+ };
371
+ }
372
+ }
373
+ const res = await this.inngestStep.run(
374
+ `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
375
+ async () => {
376
+ if (result.status === "failed") {
377
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
378
+ type: "watch",
379
+ runId: executionContext.runId,
380
+ data: {
935
381
  type: "workflow-step-result",
936
382
  payload: {
937
383
  id: step.id,
@@ -939,453 +385,1862 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
939
385
  error: result?.error,
940
386
  payload: prevOutput
941
387
  }
942
- });
943
- return { executionContext, result: { status: "failed", error: result?.error } };
944
- } else if (result.status === "suspended") {
945
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
946
- const stepRes2 = stepResult;
947
- return stepRes2?.status === "suspended";
948
- });
949
- for (const [stepName, stepResult] of suspendedSteps) {
950
- const suspendPath = [stepName, ...stepResult?.payload?.__workflow_meta?.path ?? []];
951
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
952
- await emitter.emit("watch", {
953
- type: "watch",
954
- payload: {
955
- currentStep: {
956
- id: step.id,
957
- status: "suspended",
958
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
959
- },
960
- workflowState: {
961
- status: "running",
962
- steps: stepResults,
963
- result: null,
964
- error: null
965
- }
966
- },
967
- eventTimestamp: Date.now()
968
- });
969
- await emitter.emit("watch-v2", {
388
+ }
389
+ });
390
+ return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
391
+ } else if (result.status === "suspended") {
392
+ const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
393
+ const stepRes = stepResult;
394
+ return stepRes?.status === "suspended";
395
+ });
396
+ for (const [stepName, stepResult] of suspendedSteps) {
397
+ const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
398
+ executionContext.suspendedPaths[step.id] = executionContext.executionPath;
399
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
400
+ type: "watch",
401
+ runId: executionContext.runId,
402
+ data: {
970
403
  type: "workflow-step-suspended",
971
404
  payload: {
972
405
  id: step.id,
973
406
  status: "suspended"
974
407
  }
975
- });
976
- return {
977
- executionContext,
978
- result: {
979
- status: "suspended",
980
- payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
981
- }
982
- };
983
- }
984
- await emitter.emit("watch", {
985
- type: "watch",
986
- payload: {
987
- currentStep: {
988
- id: step.id,
989
- status: "suspended",
990
- payload: {}
991
- },
992
- workflowState: {
993
- status: "running",
994
- steps: stepResults,
995
- result: null,
996
- error: null
997
- }
998
- },
999
- eventTimestamp: Date.now()
408
+ }
1000
409
  });
1001
410
  return {
1002
411
  executionContext,
1003
412
  result: {
1004
413
  status: "suspended",
1005
- payload: {}
414
+ suspendedAt: Date.now(),
415
+ payload: stepResult.payload,
416
+ suspendPayload: {
417
+ ...stepResult?.suspendPayload,
418
+ __workflow_meta: { runId, path: suspendPath }
419
+ }
1006
420
  }
1007
421
  };
1008
422
  }
1009
- await emitter.emit("watch", {
423
+ return {
424
+ executionContext,
425
+ result: {
426
+ status: "suspended",
427
+ suspendedAt: Date.now(),
428
+ payload: {}
429
+ }
430
+ };
431
+ } else if (result.status === "tripwire") {
432
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
1010
433
  type: "watch",
1011
- payload: {
1012
- currentStep: {
434
+ runId: executionContext.runId,
435
+ data: {
436
+ type: "workflow-step-result",
437
+ payload: {
1013
438
  id: step.id,
1014
- status: "success",
1015
- output: result?.result
1016
- },
1017
- workflowState: {
1018
- status: "running",
1019
- steps: stepResults,
1020
- result: null,
1021
- error: null
439
+ status: "tripwire",
440
+ error: result?.tripwire?.reason,
441
+ payload: prevOutput
1022
442
  }
1023
- },
1024
- eventTimestamp: Date.now()
443
+ }
444
+ });
445
+ return {
446
+ executionContext,
447
+ result: {
448
+ status: "tripwire",
449
+ tripwire: result?.tripwire,
450
+ endedAt: Date.now()
451
+ }
452
+ };
453
+ } else if (perStep || result.status === "paused") {
454
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
455
+ type: "watch",
456
+ runId: executionContext.runId,
457
+ data: {
458
+ type: "workflow-step-result",
459
+ payload: {
460
+ id: step.id,
461
+ status: "paused"
462
+ }
463
+ }
1025
464
  });
1026
- await emitter.emit("watch-v2", {
465
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
466
+ type: "watch",
467
+ runId: executionContext.runId,
468
+ data: {
469
+ type: "workflow-step-finish",
470
+ payload: {
471
+ id: step.id,
472
+ metadata: {}
473
+ }
474
+ }
475
+ });
476
+ return { executionContext, result: { status: "paused" } };
477
+ }
478
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
479
+ type: "watch",
480
+ runId: executionContext.runId,
481
+ data: {
1027
482
  type: "workflow-step-result",
1028
483
  payload: {
1029
484
  id: step.id,
1030
485
  status: "success",
1031
486
  output: result?.result
1032
487
  }
1033
- });
1034
- await emitter.emit("watch-v2", {
488
+ }
489
+ });
490
+ await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
491
+ type: "watch",
492
+ runId: executionContext.runId,
493
+ data: {
1035
494
  type: "workflow-step-finish",
1036
495
  payload: {
1037
496
  id: step.id,
1038
497
  metadata: {}
1039
498
  }
1040
- });
1041
- return { executionContext, result: { status: "success", output: result?.result } };
499
+ }
500
+ });
501
+ return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
502
+ }
503
+ );
504
+ Object.assign(executionContext, res.executionContext);
505
+ return {
506
+ ...res.result,
507
+ startedAt,
508
+ payload: inputData,
509
+ resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
510
+ resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
511
+ };
512
+ }
513
+ };
514
+ var InngestPubSub = class extends PubSub {
515
+ inngest;
516
+ workflowId;
517
+ publishFn;
518
+ subscriptions = /* @__PURE__ */ new Map();
519
+ constructor(inngest, workflowId, publishFn) {
520
+ super();
521
+ this.inngest = inngest;
522
+ this.workflowId = workflowId;
523
+ this.publishFn = publishFn;
524
+ }
525
+ /**
526
+ * Publish an event to Inngest's realtime system.
527
+ *
528
+ * Topic format: "workflow.events.v2.{runId}"
529
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
530
+ */
531
+ async publish(topic, event) {
532
+ if (!this.publishFn) {
533
+ return;
534
+ }
535
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
536
+ if (!match) {
537
+ return;
538
+ }
539
+ const runId = match[1];
540
+ try {
541
+ await this.publishFn({
542
+ channel: `workflow:${this.workflowId}:${runId}`,
543
+ topic: "watch",
544
+ data: event.data
545
+ });
546
+ } catch (err) {
547
+ console.error("InngestPubSub publish error:", err?.message ?? err);
548
+ }
549
+ }
550
+ /**
551
+ * Subscribe to events from Inngest's realtime system.
552
+ *
553
+ * Topic format: "workflow.events.v2.{runId}"
554
+ * Maps to Inngest channel: "workflow:{workflowId}:{runId}"
555
+ */
556
+ async subscribe(topic, cb) {
557
+ const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
558
+ if (!match || !match[1]) {
559
+ return;
560
+ }
561
+ const runId = match[1];
562
+ if (this.subscriptions.has(topic)) {
563
+ this.subscriptions.get(topic).callbacks.add(cb);
564
+ return;
565
+ }
566
+ const callbacks = /* @__PURE__ */ new Set([cb]);
567
+ const channel = `workflow:${this.workflowId}:${runId}`;
568
+ const streamPromise = subscribe(
569
+ {
570
+ channel,
571
+ topics: ["watch"],
572
+ app: this.inngest
573
+ },
574
+ (message) => {
575
+ const event = {
576
+ id: crypto.randomUUID(),
577
+ type: "watch",
578
+ runId,
579
+ data: message.data,
580
+ createdAt: /* @__PURE__ */ new Date()
581
+ };
582
+ for (const callback of callbacks) {
583
+ callback(event);
1042
584
  }
1043
- );
1044
- Object.assign(executionContext, res.executionContext);
1045
- return res.result;
585
+ }
586
+ );
587
+ this.subscriptions.set(topic, {
588
+ unsubscribe: () => {
589
+ streamPromise.then((stream) => stream.cancel()).catch((err) => {
590
+ console.error("InngestPubSub unsubscribe error:", err);
591
+ });
592
+ },
593
+ callbacks
594
+ });
595
+ }
596
+ /**
597
+ * Unsubscribe a callback from a topic.
598
+ * If no callbacks remain, the underlying Inngest subscription is cancelled.
599
+ */
600
+ async unsubscribe(topic, cb) {
601
+ const sub = this.subscriptions.get(topic);
602
+ if (!sub) {
603
+ return;
604
+ }
605
+ sub.callbacks.delete(cb);
606
+ if (sub.callbacks.size === 0) {
607
+ sub.unsubscribe();
608
+ this.subscriptions.delete(topic);
1046
609
  }
1047
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1048
- let execResults;
1049
- let suspended;
1050
- let bailed;
610
+ }
611
+ /**
612
+ * Flush any pending operations. No-op for Inngest.
613
+ */
614
+ async flush() {
615
+ }
616
+ /**
617
+ * Clean up all subscriptions during graceful shutdown.
618
+ */
619
+ async close() {
620
+ for (const [, sub] of this.subscriptions) {
621
+ sub.unsubscribe();
622
+ }
623
+ this.subscriptions.clear();
624
+ }
625
+ };
626
+ var InngestRun = class extends Run {
627
+ inngest;
628
+ serializedStepGraph;
629
+ #mastra;
630
+ constructor(params, inngest) {
631
+ super(params);
632
+ this.inngest = inngest;
633
+ this.serializedStepGraph = params.serializedStepGraph;
634
+ this.#mastra = params.mastra;
635
+ }
636
+ async getRuns(eventId) {
637
+ const maxRetries = 3;
638
+ let lastError = null;
639
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
1051
640
  try {
1052
- if (validationError) {
1053
- throw validationError;
641
+ const response = await fetch(
642
+ `${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
643
+ {
644
+ headers: {
645
+ Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
646
+ }
647
+ }
648
+ );
649
+ if (response.status === 429) {
650
+ const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
651
+ await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
652
+ continue;
653
+ }
654
+ if (!response.ok) {
655
+ throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
656
+ }
657
+ const text = await response.text();
658
+ if (!text) {
659
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
660
+ continue;
661
+ }
662
+ const json = JSON.parse(text);
663
+ return json.data;
664
+ } catch (error) {
665
+ lastError = error;
666
+ if (attempt < maxRetries - 1) {
667
+ await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
1054
668
  }
1055
- const result = await step.execute({
1056
- runId: executionContext.runId,
1057
- mastra: this.mastra,
1058
- runtimeContext,
1059
- writableStream,
1060
- inputData,
1061
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
1062
- tracingContext: {
1063
- currentSpan: stepAISpan
1064
- },
1065
- getInitData: () => stepResults?.input,
1066
- getStepResult: getStepResult.bind(this, stepResults),
1067
- suspend: async (suspendPayload) => {
1068
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1069
- suspended = { payload: suspendPayload };
1070
- },
1071
- bail: (result2) => {
1072
- bailed = { payload: result2 };
1073
- },
1074
- resume: {
1075
- steps: resume?.steps?.slice(1) || [],
1076
- resumePayload: resume?.resumePayload,
1077
- // @ts-ignore
1078
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
1079
- },
1080
- [EMITTER_SYMBOL]: emitter,
1081
- engine: {
1082
- step: this.inngestStep
1083
- },
1084
- abortSignal: abortController.signal
1085
- });
1086
- const endedAt = Date.now();
1087
- execResults = {
1088
- status: "success",
1089
- output: result,
1090
- startedAt,
1091
- endedAt,
1092
- payload: inputData,
1093
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1094
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1095
- };
1096
- } catch (e) {
1097
- execResults = {
1098
- status: "failed",
1099
- payload: inputData,
1100
- error: e instanceof Error ? e.message : String(e),
1101
- endedAt: Date.now(),
1102
- startedAt,
1103
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1104
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1105
- };
1106
- }
1107
- if (suspended) {
1108
- execResults = {
1109
- status: "suspended",
1110
- suspendedPayload: suspended.payload,
1111
- payload: inputData,
1112
- suspendedAt: Date.now(),
1113
- startedAt,
1114
- resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
1115
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
1116
- };
1117
- } else if (bailed) {
1118
- execResults = { status: "bailed", output: bailed.payload, payload: inputData, endedAt: Date.now(), startedAt };
1119
669
  }
1120
- if (execResults.status === "failed") {
1121
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1122
- const error = new Error(execResults.error);
1123
- stepAISpan?.error({ error });
670
+ }
671
+ throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
672
+ }
673
+ async getRunOutput(eventId, maxWaitMs = 3e5) {
674
+ const startTime = Date.now();
675
+ const storage = this.#mastra?.getStorage();
676
+ const workflowsStore = await storage?.getStore("workflows");
677
+ while (Date.now() - startTime < maxWaitMs) {
678
+ let runs;
679
+ try {
680
+ runs = await this.getRuns(eventId);
681
+ } catch (error) {
682
+ if (error instanceof NonRetriableError) {
1124
683
  throw error;
1125
684
  }
685
+ throw new NonRetriableError(
686
+ `Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
687
+ );
1126
688
  }
1127
- await emitter.emit("watch", {
1128
- type: "watch",
1129
- payload: {
1130
- currentStep: {
1131
- id: step.id,
1132
- ...execResults
1133
- },
1134
- workflowState: {
1135
- status: "running",
1136
- steps: { ...stepResults, [step.id]: execResults },
1137
- result: null,
1138
- error: null
1139
- }
1140
- },
1141
- eventTimestamp: Date.now()
1142
- });
1143
- if (execResults.status === "suspended") {
1144
- await emitter.emit("watch-v2", {
1145
- type: "workflow-step-suspended",
1146
- payload: {
1147
- id: step.id,
1148
- ...execResults
1149
- }
1150
- });
1151
- } else {
1152
- await emitter.emit("watch-v2", {
1153
- type: "workflow-step-result",
1154
- payload: {
1155
- id: step.id,
1156
- ...execResults
1157
- }
689
+ if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
690
+ return runs[0];
691
+ }
692
+ if (runs?.[0]?.status === "Failed") {
693
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
694
+ workflowName: this.workflowId,
695
+ runId: this.runId
1158
696
  });
1159
- await emitter.emit("watch-v2", {
1160
- type: "workflow-step-finish",
1161
- payload: {
1162
- id: step.id,
1163
- metadata: {}
697
+ if (snapshot?.context) {
698
+ snapshot.context = hydrateSerializedStepErrors(snapshot.context);
699
+ }
700
+ return {
701
+ output: {
702
+ result: {
703
+ steps: snapshot?.context,
704
+ status: "failed",
705
+ // Get the original error from NonRetriableError's cause (which contains the workflow result)
706
+ error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
707
+ }
1164
708
  }
709
+ };
710
+ }
711
+ if (runs?.[0]?.status === "Cancelled") {
712
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
713
+ workflowName: this.workflowId,
714
+ runId: this.runId
1165
715
  });
716
+ return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
717
+ }
718
+ await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
719
+ }
720
+ throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
721
+ }
722
+ async cancel() {
723
+ const storage = this.#mastra?.getStorage();
724
+ await this.inngest.send({
725
+ name: `cancel.workflow.${this.workflowId}`,
726
+ data: {
727
+ runId: this.runId
1166
728
  }
1167
- stepAISpan?.end({ output: execResults });
1168
- return { result: execResults, executionContext, stepResults };
1169
729
  });
1170
- if (disableScorers !== false) {
1171
- await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1172
- if (step.scorers) {
1173
- await this.runScorers({
1174
- scorers: step.scorers,
1175
- runId: executionContext.runId,
1176
- input: inputData,
1177
- output: stepRes.result,
1178
- workflowId: executionContext.workflowId,
1179
- stepId: step.id,
1180
- runtimeContext,
1181
- disableScorers,
1182
- tracingContext: { currentSpan: stepAISpan }
1183
- });
730
+ const workflowsStore = await storage?.getStore("workflows");
731
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
732
+ workflowName: this.workflowId,
733
+ runId: this.runId
734
+ });
735
+ if (snapshot) {
736
+ await workflowsStore?.persistWorkflowSnapshot({
737
+ workflowName: this.workflowId,
738
+ runId: this.runId,
739
+ resourceId: this.resourceId,
740
+ snapshot: {
741
+ ...snapshot,
742
+ status: "canceled",
743
+ value: snapshot.value
1184
744
  }
1185
745
  });
1186
746
  }
1187
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1188
- Object.assign(stepResults, stepRes.stepResults);
1189
- return stepRes.result;
1190
747
  }
1191
- async persistStepUpdate({
1192
- workflowId,
1193
- runId,
1194
- stepResults,
1195
- resourceId,
1196
- executionContext,
1197
- serializedStepGraph,
1198
- workflowStatus,
1199
- result,
1200
- error
1201
- }) {
1202
- await this.inngestStep.run(
1203
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1204
- async () => {
1205
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1206
- workflowName: workflowId,
1207
- runId,
1208
- resourceId,
1209
- snapshot: {
1210
- runId,
1211
- value: {},
1212
- context: stepResults,
1213
- activePaths: [],
1214
- suspendedPaths: executionContext.suspendedPaths,
1215
- waitingPaths: {},
1216
- serializedStepGraph,
1217
- status: workflowStatus,
1218
- result,
1219
- error,
1220
- // @ts-ignore
1221
- timestamp: Date.now()
1222
- }
1223
- });
1224
- }
1225
- );
748
+ async start(args) {
749
+ return this._start(args);
1226
750
  }
1227
- async executeConditional({
1228
- workflowId,
1229
- runId,
1230
- entry,
1231
- prevOutput,
1232
- prevStep,
1233
- stepResults,
1234
- serializedStepGraph,
1235
- resume,
1236
- executionContext,
1237
- emitter,
1238
- abortController,
1239
- runtimeContext,
1240
- writableStream,
1241
- disableScorers,
1242
- tracingContext
1243
- }) {
1244
- const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1245
- type: AISpanType.WORKFLOW_CONDITIONAL,
1246
- name: `conditional: '${entry.conditions.length} conditions'`,
1247
- input: prevOutput,
1248
- attributes: {
1249
- conditionCount: entry.conditions.length
1250
- },
1251
- tracingPolicy: this.options?.tracingPolicy
751
+ /**
752
+ * Starts the workflow execution without waiting for completion (fire-and-forget).
753
+ * Returns immediately with the runId after sending the event to Inngest.
754
+ * The workflow executes independently in Inngest.
755
+ * Use this when you don't need to wait for the result or want to avoid polling failures.
756
+ */
757
+ async startAsync(args) {
758
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
759
+ await workflowsStore?.persistWorkflowSnapshot({
760
+ workflowName: this.workflowId,
761
+ runId: this.runId,
762
+ resourceId: this.resourceId,
763
+ snapshot: {
764
+ runId: this.runId,
765
+ serializedStepGraph: this.serializedStepGraph,
766
+ status: "running",
767
+ value: {},
768
+ context: {},
769
+ activePaths: [],
770
+ suspendedPaths: {},
771
+ activeStepsPath: {},
772
+ resumeLabels: {},
773
+ waitingPaths: {},
774
+ timestamp: Date.now()
775
+ }
1252
776
  });
1253
- let execResults;
1254
- const truthyIndexes = (await Promise.all(
1255
- entry.conditions.map(
1256
- (cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1257
- const evalSpan = conditionalSpan?.createChildSpan({
1258
- type: AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1259
- name: `condition: '${index}'`,
1260
- input: prevOutput,
1261
- attributes: {
1262
- conditionIndex: index
1263
- },
1264
- tracingPolicy: this.options?.tracingPolicy
1265
- });
1266
- try {
1267
- const result = await cond({
1268
- runId,
1269
- workflowId,
1270
- mastra: this.mastra,
1271
- runtimeContext,
1272
- runCount: -1,
1273
- inputData: prevOutput,
1274
- tracingContext: {
1275
- currentSpan: evalSpan
1276
- },
1277
- getInitData: () => stepResults?.input,
1278
- getStepResult: getStepResult.bind(this, stepResults),
1279
- // TODO: this function shouldn't have suspend probably?
1280
- suspend: async (_suspendPayload) => {
1281
- },
1282
- bail: () => {
1283
- },
1284
- abort: () => {
1285
- abortController.abort();
1286
- },
1287
- [EMITTER_SYMBOL]: emitter,
1288
- [STREAM_FORMAT_SYMBOL]: executionContext.format,
1289
- // TODO: add streamVNext support
1290
- engine: {
1291
- step: this.inngestStep
1292
- },
1293
- abortSignal: abortController.signal,
1294
- writer: new ToolStream(
1295
- {
1296
- prefix: "workflow-step",
1297
- callId: randomUUID(),
1298
- name: "conditional",
1299
- runId
1300
- },
1301
- writableStream
1302
- )
1303
- });
1304
- evalSpan?.end({
1305
- output: result,
1306
- attributes: {
1307
- result: !!result
1308
- }
1309
- });
1310
- return result ? index : null;
1311
- } catch (e) {
1312
- evalSpan?.error({
1313
- error: e instanceof Error ? e : new Error(String(e)),
1314
- attributes: {
1315
- result: false
1316
- }
1317
- });
1318
- return null;
1319
- }
1320
- })
1321
- )
1322
- )).filter((index) => index !== null);
1323
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1324
- conditionalSpan?.update({
1325
- attributes: {
1326
- truthyIndexes,
1327
- selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
777
+ const inputDataToUse = await this._validateInput(args.inputData);
778
+ const initialStateToUse = await this._validateInitialState(args.initialState ?? {});
779
+ const eventOutput = await this.inngest.send({
780
+ name: `workflow.${this.workflowId}`,
781
+ data: {
782
+ inputData: inputDataToUse,
783
+ initialState: initialStateToUse,
784
+ runId: this.runId,
785
+ resourceId: this.resourceId,
786
+ outputOptions: args.outputOptions,
787
+ tracingOptions: args.tracingOptions,
788
+ requestContext: args.requestContext ? Object.fromEntries(args.requestContext.entries()) : {},
789
+ perStep: args.perStep
1328
790
  }
1329
791
  });
1330
- const results = await Promise.all(
1331
- stepsToRun.map(
1332
- (step, index) => this.executeEntry({
1333
- workflowId,
1334
- runId,
1335
- entry: step,
1336
- serializedStepGraph,
1337
- prevStep,
1338
- stepResults,
792
+ const eventId = eventOutput.ids[0];
793
+ if (!eventId) {
794
+ throw new Error("Event ID is not set");
795
+ }
796
+ return { runId: this.runId };
797
+ }
798
+ async _start({
799
+ inputData,
800
+ initialState,
801
+ outputOptions,
802
+ tracingOptions,
803
+ format,
804
+ requestContext,
805
+ perStep
806
+ }) {
807
+ const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
808
+ await workflowsStore?.persistWorkflowSnapshot({
809
+ workflowName: this.workflowId,
810
+ runId: this.runId,
811
+ resourceId: this.resourceId,
812
+ snapshot: {
813
+ runId: this.runId,
814
+ serializedStepGraph: this.serializedStepGraph,
815
+ status: "running",
816
+ value: {},
817
+ context: {},
818
+ activePaths: [],
819
+ suspendedPaths: {},
820
+ activeStepsPath: {},
821
+ resumeLabels: {},
822
+ waitingPaths: {},
823
+ timestamp: Date.now()
824
+ }
825
+ });
826
+ const inputDataToUse = await this._validateInput(inputData);
827
+ const initialStateToUse = await this._validateInitialState(initialState ?? {});
828
+ const eventOutput = await this.inngest.send({
829
+ name: `workflow.${this.workflowId}`,
830
+ data: {
831
+ inputData: inputDataToUse,
832
+ initialState: initialStateToUse,
833
+ runId: this.runId,
834
+ resourceId: this.resourceId,
835
+ outputOptions,
836
+ tracingOptions,
837
+ format,
838
+ requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
839
+ perStep
840
+ }
841
+ });
842
+ const eventId = eventOutput.ids[0];
843
+ if (!eventId) {
844
+ throw new Error("Event ID is not set");
845
+ }
846
+ const runOutput = await this.getRunOutput(eventId);
847
+ const result = runOutput?.output?.result;
848
+ this.hydrateFailedResult(result);
849
+ if (result.status !== "suspended") {
850
+ this.cleanup?.();
851
+ }
852
+ return result;
853
+ }
854
+ async resume(params) {
855
+ const p = this._resume(params).then((result) => {
856
+ if (result.status !== "suspended") {
857
+ this.closeStreamAction?.().catch(() => {
858
+ });
859
+ }
860
+ return result;
861
+ });
862
+ this.executionResults = p;
863
+ return p;
864
+ }
865
+ async _resume(params) {
866
+ const storage = this.#mastra?.getStorage();
867
+ let steps = [];
868
+ if (typeof params.step === "string") {
869
+ steps = params.step.split(".");
870
+ } else {
871
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
872
+ (step) => typeof step === "string" ? step : step?.id
873
+ );
874
+ }
875
+ const workflowsStore = await storage?.getStore("workflows");
876
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
877
+ workflowName: this.workflowId,
878
+ runId: this.runId
879
+ });
880
+ const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
881
+ const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
882
+ const persistedRequestContext = snapshot?.requestContext ?? {};
883
+ const newRequestContext = params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {};
884
+ const mergedRequestContext = { ...persistedRequestContext, ...newRequestContext };
885
+ const eventOutput = await this.inngest.send({
886
+ name: `workflow.${this.workflowId}`,
887
+ data: {
888
+ inputData: resumeDataToUse,
889
+ initialState: snapshot?.value ?? {},
890
+ runId: this.runId,
891
+ workflowId: this.workflowId,
892
+ stepResults: snapshot?.context,
893
+ resume: {
894
+ steps,
895
+ stepResults: snapshot?.context,
896
+ resumePayload: resumeDataToUse,
897
+ resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
898
+ },
899
+ requestContext: mergedRequestContext,
900
+ perStep: params.perStep
901
+ }
902
+ });
903
+ const eventId = eventOutput.ids[0];
904
+ if (!eventId) {
905
+ throw new Error("Event ID is not set");
906
+ }
907
+ const runOutput = await this.getRunOutput(eventId);
908
+ const result = runOutput?.output?.result;
909
+ this.hydrateFailedResult(result);
910
+ return result;
911
+ }
912
+ async timeTravel(params) {
913
+ const p = this._timeTravel(params).then((result) => {
914
+ if (result.status !== "suspended") {
915
+ this.closeStreamAction?.().catch(() => {
916
+ });
917
+ }
918
+ return result;
919
+ });
920
+ this.executionResults = p;
921
+ return p;
922
+ }
923
+ async _timeTravel(params) {
924
+ if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
925
+ throw new Error("Step is required and must be a valid step or array of steps");
926
+ }
927
+ let steps = [];
928
+ if (typeof params.step === "string") {
929
+ steps = params.step.split(".");
930
+ } else {
931
+ steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
932
+ (step) => typeof step === "string" ? step : step?.id
933
+ );
934
+ }
935
+ if (steps.length === 0) {
936
+ throw new Error("No steps provided to timeTravel");
937
+ }
938
+ const storage = this.#mastra?.getStorage();
939
+ const workflowsStore = await storage?.getStore("workflows");
940
+ const snapshot = await workflowsStore?.loadWorkflowSnapshot({
941
+ workflowName: this.workflowId,
942
+ runId: this.runId
943
+ });
944
+ if (!snapshot) {
945
+ await workflowsStore?.persistWorkflowSnapshot({
946
+ workflowName: this.workflowId,
947
+ runId: this.runId,
948
+ resourceId: this.resourceId,
949
+ snapshot: {
950
+ runId: this.runId,
951
+ serializedStepGraph: this.serializedStepGraph,
952
+ status: "pending",
953
+ value: {},
954
+ context: {},
955
+ activePaths: [],
956
+ suspendedPaths: {},
957
+ activeStepsPath: {},
958
+ resumeLabels: {},
959
+ waitingPaths: {},
960
+ timestamp: Date.now()
961
+ }
962
+ });
963
+ }
964
+ if (snapshot?.status === "running") {
965
+ throw new Error("This workflow run is still running, cannot time travel");
966
+ }
967
+ let inputDataToUse = params.inputData;
968
+ if (inputDataToUse && steps.length === 1) {
969
+ inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
970
+ }
971
+ const timeTravelData = createTimeTravelExecutionParams({
972
+ steps,
973
+ inputData: inputDataToUse,
974
+ resumeData: params.resumeData,
975
+ context: params.context,
976
+ nestedStepsContext: params.nestedStepsContext,
977
+ snapshot: snapshot ?? { context: {} },
978
+ graph: this.executionGraph,
979
+ initialState: params.initialState,
980
+ perStep: params.perStep
981
+ });
982
+ const eventOutput = await this.inngest.send({
983
+ name: `workflow.${this.workflowId}`,
984
+ data: {
985
+ initialState: timeTravelData.state,
986
+ runId: this.runId,
987
+ workflowId: this.workflowId,
988
+ stepResults: timeTravelData.stepResults,
989
+ timeTravel: timeTravelData,
990
+ tracingOptions: params.tracingOptions,
991
+ outputOptions: params.outputOptions,
992
+ requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
993
+ perStep: params.perStep
994
+ }
995
+ });
996
+ const eventId = eventOutput.ids[0];
997
+ if (!eventId) {
998
+ throw new Error("Event ID is not set");
999
+ }
1000
+ const runOutput = await this.getRunOutput(eventId);
1001
+ const result = runOutput?.output?.result;
1002
+ this.hydrateFailedResult(result);
1003
+ return result;
1004
+ }
1005
+ watch(cb) {
1006
+ let active = true;
1007
+ const streamPromise = subscribe(
1008
+ {
1009
+ channel: `workflow:${this.workflowId}:${this.runId}`,
1010
+ topics: ["watch"],
1011
+ app: this.inngest
1012
+ },
1013
+ (message) => {
1014
+ if (active) {
1015
+ cb(message.data);
1016
+ }
1017
+ }
1018
+ );
1019
+ return () => {
1020
+ active = false;
1021
+ streamPromise.then(async (stream) => {
1022
+ return stream.cancel();
1023
+ }).catch((err) => {
1024
+ console.error(err);
1025
+ });
1026
+ };
1027
+ }
1028
+ streamLegacy({ inputData, requestContext } = {}) {
1029
+ const { readable, writable } = new TransformStream();
1030
+ const writer = writable.getWriter();
1031
+ void writer.write({
1032
+ // @ts-expect-error - stream event type mismatch
1033
+ type: "start",
1034
+ payload: { runId: this.runId }
1035
+ });
1036
+ const unwatch = this.watch(async (event) => {
1037
+ try {
1038
+ const e = {
1039
+ ...event,
1040
+ type: event.type.replace("workflow-", "")
1041
+ };
1042
+ if (e.type === "step-output") {
1043
+ e.type = e.payload.output.type;
1044
+ e.payload = e.payload.output.payload;
1045
+ }
1046
+ await writer.write(e);
1047
+ } catch {
1048
+ }
1049
+ });
1050
+ this.closeStreamAction = async () => {
1051
+ await writer.write({
1052
+ type: "finish",
1053
+ // @ts-expect-error - stream event type mismatch
1054
+ payload: { runId: this.runId }
1055
+ });
1056
+ unwatch();
1057
+ try {
1058
+ await writer.close();
1059
+ } catch (err) {
1060
+ console.error("Error closing stream:", err);
1061
+ } finally {
1062
+ writer.releaseLock();
1063
+ }
1064
+ };
1065
+ this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
1066
+ if (result.status !== "suspended") {
1067
+ this.closeStreamAction?.().catch(() => {
1068
+ });
1069
+ }
1070
+ return result;
1071
+ });
1072
+ return {
1073
+ stream: readable,
1074
+ getWorkflowState: () => this.executionResults
1075
+ };
1076
+ }
1077
+ stream({
1078
+ inputData,
1079
+ requestContext,
1080
+ tracingOptions,
1081
+ closeOnSuspend = true,
1082
+ initialState,
1083
+ outputOptions,
1084
+ perStep
1085
+ } = {}) {
1086
+ if (this.closeStreamAction && this.streamOutput) {
1087
+ return this.streamOutput;
1088
+ }
1089
+ this.closeStreamAction = async () => {
1090
+ };
1091
+ const self = this;
1092
+ const stream = new ReadableStream({
1093
+ async start(controller) {
1094
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
1095
+ controller.enqueue({
1096
+ type,
1097
+ runId: self.runId,
1098
+ from,
1099
+ payload: {
1100
+ stepName: payload?.id,
1101
+ ...payload
1102
+ }
1103
+ });
1104
+ });
1105
+ self.closeStreamAction = async () => {
1106
+ unwatch();
1107
+ try {
1108
+ await controller.close();
1109
+ } catch (err) {
1110
+ console.error("Error closing stream:", err);
1111
+ }
1112
+ };
1113
+ const executionResultsPromise = self._start({
1114
+ inputData,
1115
+ requestContext,
1116
+ // tracingContext, // We are not able to pass a reference to a span here, what to do?
1117
+ initialState,
1118
+ tracingOptions,
1119
+ outputOptions,
1120
+ format: "vnext",
1121
+ perStep
1122
+ });
1123
+ let executionResults;
1124
+ try {
1125
+ executionResults = await executionResultsPromise;
1126
+ if (closeOnSuspend) {
1127
+ self.closeStreamAction?.().catch(() => {
1128
+ });
1129
+ } else if (executionResults.status !== "suspended") {
1130
+ self.closeStreamAction?.().catch(() => {
1131
+ });
1132
+ }
1133
+ if (self.streamOutput) {
1134
+ self.streamOutput.updateResults(
1135
+ executionResults
1136
+ );
1137
+ }
1138
+ } catch (err) {
1139
+ self.streamOutput?.rejectResults(err);
1140
+ self.closeStreamAction?.().catch(() => {
1141
+ });
1142
+ }
1143
+ }
1144
+ });
1145
+ this.streamOutput = new WorkflowRunOutput({
1146
+ runId: this.runId,
1147
+ workflowId: this.workflowId,
1148
+ stream
1149
+ });
1150
+ return this.streamOutput;
1151
+ }
1152
+ timeTravelStream({
1153
+ inputData,
1154
+ resumeData,
1155
+ initialState,
1156
+ step,
1157
+ context,
1158
+ nestedStepsContext,
1159
+ requestContext,
1160
+ // tracingContext,
1161
+ tracingOptions,
1162
+ outputOptions,
1163
+ perStep
1164
+ }) {
1165
+ this.closeStreamAction = async () => {
1166
+ };
1167
+ const self = this;
1168
+ const stream = new ReadableStream({
1169
+ async start(controller) {
1170
+ const unwatch = self.watch(async ({ type, from = ChunkFrom.WORKFLOW, payload }) => {
1171
+ controller.enqueue({
1172
+ type,
1173
+ runId: self.runId,
1174
+ from,
1175
+ payload: {
1176
+ stepName: payload?.id,
1177
+ ...payload
1178
+ }
1179
+ });
1180
+ });
1181
+ self.closeStreamAction = async () => {
1182
+ unwatch();
1183
+ try {
1184
+ controller.close();
1185
+ } catch (err) {
1186
+ console.error("Error closing stream:", err);
1187
+ }
1188
+ };
1189
+ const executionResultsPromise = self._timeTravel({
1190
+ inputData,
1191
+ step,
1192
+ context,
1193
+ nestedStepsContext,
1194
+ resumeData,
1195
+ initialState,
1196
+ requestContext,
1197
+ tracingOptions,
1198
+ outputOptions,
1199
+ perStep
1200
+ });
1201
+ self.executionResults = executionResultsPromise;
1202
+ let executionResults;
1203
+ try {
1204
+ executionResults = await executionResultsPromise;
1205
+ self.closeStreamAction?.().catch(() => {
1206
+ });
1207
+ if (self.streamOutput) {
1208
+ self.streamOutput.updateResults(executionResults);
1209
+ }
1210
+ } catch (err) {
1211
+ self.streamOutput?.rejectResults(err);
1212
+ self.closeStreamAction?.().catch(() => {
1213
+ });
1214
+ }
1215
+ }
1216
+ });
1217
+ this.streamOutput = new WorkflowRunOutput({
1218
+ runId: this.runId,
1219
+ workflowId: this.workflowId,
1220
+ stream
1221
+ });
1222
+ return this.streamOutput;
1223
+ }
1224
+ /**
1225
+ * Hydrates errors in a failed workflow result back to proper Error instances.
1226
+ * This ensures error.cause chains and custom properties are preserved.
1227
+ */
1228
+ hydrateFailedResult(result) {
1229
+ if (result.status === "failed") {
1230
+ result.error = getErrorFromUnknown(result.error, { serializeStack: false });
1231
+ if (result.steps) {
1232
+ hydrateSerializedStepErrors(result.steps);
1233
+ }
1234
+ }
1235
+ }
1236
+ };
1237
+
1238
+ // src/workflow.ts
1239
+ var InngestWorkflow = class _InngestWorkflow extends Workflow {
1240
+ #mastra;
1241
+ inngest;
1242
+ function;
1243
+ cronFunction;
1244
+ flowControlConfig;
1245
+ cronConfig;
1246
+ constructor(params, inngest) {
1247
+ const { concurrency, rateLimit, throttle, debounce, priority, cron, inputData, initialState, ...workflowParams } = params;
1248
+ super(workflowParams);
1249
+ this.engineType = "inngest";
1250
+ const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
1251
+ ([_, value]) => value !== void 0
1252
+ );
1253
+ this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
1254
+ this.#mastra = params.mastra;
1255
+ this.inngest = inngest;
1256
+ if (cron) {
1257
+ this.cronConfig = { cron, inputData, initialState };
1258
+ }
1259
+ }
1260
+ async listWorkflowRuns(args) {
1261
+ const storage = this.#mastra?.getStorage();
1262
+ if (!storage) {
1263
+ this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
1264
+ return { runs: [], total: 0 };
1265
+ }
1266
+ const workflowsStore = await storage.getStore("workflows");
1267
+ if (!workflowsStore) {
1268
+ return { runs: [], total: 0 };
1269
+ }
1270
+ return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
1271
+ }
1272
+ __registerMastra(mastra) {
1273
+ super.__registerMastra(mastra);
1274
+ this.#mastra = mastra;
1275
+ this.executionEngine.__registerMastra(mastra);
1276
+ const updateNested = (step) => {
1277
+ if ((step.type === "step" || step.type === "loop" || step.type === "foreach") && step.step instanceof _InngestWorkflow) {
1278
+ step.step.__registerMastra(mastra);
1279
+ } else if (step.type === "parallel" || step.type === "conditional") {
1280
+ for (const subStep of step.steps) {
1281
+ updateNested(subStep);
1282
+ }
1283
+ }
1284
+ };
1285
+ if (this.executionGraph.steps.length) {
1286
+ for (const step of this.executionGraph.steps) {
1287
+ updateNested(step);
1288
+ }
1289
+ }
1290
+ }
1291
+ async createRun(options) {
1292
+ const runIdToUse = options?.runId || randomUUID();
1293
+ const existingInMemoryRun = this.runs.get(runIdToUse);
1294
+ const newRun = new InngestRun(
1295
+ {
1296
+ workflowId: this.id,
1297
+ runId: runIdToUse,
1298
+ resourceId: options?.resourceId,
1299
+ executionEngine: this.executionEngine,
1300
+ executionGraph: this.executionGraph,
1301
+ serializedStepGraph: this.serializedStepGraph,
1302
+ mastra: this.#mastra,
1303
+ retryConfig: this.retryConfig,
1304
+ cleanup: () => this.runs.delete(runIdToUse),
1305
+ workflowSteps: this.steps,
1306
+ workflowEngineType: this.engineType,
1307
+ validateInputs: this.options.validateInputs
1308
+ },
1309
+ this.inngest
1310
+ );
1311
+ const run = existingInMemoryRun ?? newRun;
1312
+ this.runs.set(runIdToUse, run);
1313
+ const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
1314
+ workflowStatus: run.workflowRunStatus,
1315
+ stepResults: {}
1316
+ });
1317
+ const existingStoredRun = await this.getWorkflowRunById(runIdToUse, {
1318
+ withNestedWorkflows: false
1319
+ });
1320
+ const existsInStorage = existingStoredRun && !existingStoredRun.isFromInMemory;
1321
+ if (!existsInStorage && shouldPersistSnapshot) {
1322
+ const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
1323
+ await workflowsStore?.persistWorkflowSnapshot({
1324
+ workflowName: this.id,
1325
+ runId: runIdToUse,
1326
+ resourceId: options?.resourceId,
1327
+ snapshot: {
1328
+ runId: runIdToUse,
1329
+ status: "pending",
1330
+ value: {},
1331
+ context: {},
1332
+ activePaths: [],
1333
+ activeStepsPath: {},
1334
+ waitingPaths: {},
1335
+ serializedStepGraph: this.serializedStepGraph,
1336
+ suspendedPaths: {},
1337
+ resumeLabels: {},
1338
+ result: void 0,
1339
+ error: void 0,
1340
+ timestamp: Date.now()
1341
+ }
1342
+ });
1343
+ }
1344
+ return run;
1345
+ }
1346
+ //createCronFunction is only called if cronConfig.cron is defined.
1347
+ createCronFunction() {
1348
+ if (this.cronFunction) {
1349
+ return this.cronFunction;
1350
+ }
1351
+ this.cronFunction = this.inngest.createFunction(
1352
+ {
1353
+ id: `workflow.${this.id}.cron`,
1354
+ retries: 0,
1355
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1356
+ ...this.flowControlConfig
1357
+ },
1358
+ { cron: this.cronConfig?.cron ?? "" },
1359
+ async () => {
1360
+ const run = await this.createRun();
1361
+ const result = await run.start({
1362
+ inputData: this.cronConfig?.inputData,
1363
+ initialState: this.cronConfig?.initialState
1364
+ });
1365
+ return { result, runId: run.runId };
1366
+ }
1367
+ );
1368
+ return this.cronFunction;
1369
+ }
1370
+ getFunction() {
1371
+ if (this.function) {
1372
+ return this.function;
1373
+ }
1374
+ this.function = this.inngest.createFunction(
1375
+ {
1376
+ id: `workflow.${this.id}`,
1377
+ retries: 0,
1378
+ cancelOn: [{ event: `cancel.workflow.${this.id}` }],
1379
+ // Spread flow control configuration
1380
+ ...this.flowControlConfig
1381
+ },
1382
+ { event: `workflow.${this.id}` },
1383
+ async ({ event, step, attempt, publish }) => {
1384
+ let {
1385
+ inputData,
1386
+ initialState,
1387
+ runId,
1388
+ resourceId,
1339
1389
  resume,
1340
- executionContext: {
1341
- workflowId,
1390
+ outputOptions,
1391
+ format,
1392
+ timeTravel,
1393
+ perStep,
1394
+ tracingOptions
1395
+ } = event.data;
1396
+ if (!runId) {
1397
+ runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
1398
+ return randomUUID();
1399
+ });
1400
+ }
1401
+ const pubsub = new InngestPubSub(this.inngest, this.id, publish);
1402
+ const requestContext = new RequestContext(Object.entries(event.data.requestContext ?? {}));
1403
+ const mastra = this.#mastra;
1404
+ const tracingPolicy = this.options.tracingPolicy;
1405
+ const workflowSpanData = await step.run(`workflow.${this.id}.span.start`, async () => {
1406
+ const observability = mastra?.observability?.getSelectedInstance({ requestContext });
1407
+ if (!observability) return void 0;
1408
+ const span = observability.startSpan({
1409
+ type: SpanType.WORKFLOW_RUN,
1410
+ name: `workflow run: '${this.id}'`,
1411
+ entityType: EntityType.WORKFLOW_RUN,
1412
+ entityId: this.id,
1413
+ input: inputData,
1414
+ metadata: {
1415
+ resourceId,
1416
+ runId
1417
+ },
1418
+ tracingPolicy,
1419
+ tracingOptions,
1420
+ requestContext
1421
+ });
1422
+ return span?.exportSpan();
1423
+ });
1424
+ const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
1425
+ let result;
1426
+ try {
1427
+ result = await engine.execute({
1428
+ workflowId: this.id,
1342
1429
  runId,
1343
- executionPath: [...executionContext.executionPath, index],
1344
- suspendedPaths: executionContext.suspendedPaths,
1345
- retryConfig: executionContext.retryConfig,
1346
- executionSpan: executionContext.executionSpan
1347
- },
1348
- emitter,
1349
- abortController,
1350
- runtimeContext,
1351
- writableStream,
1352
- disableScorers,
1353
- tracingContext: {
1354
- currentSpan: conditionalSpan
1430
+ resourceId,
1431
+ graph: this.executionGraph,
1432
+ serializedStepGraph: this.serializedStepGraph,
1433
+ input: inputData,
1434
+ initialState,
1435
+ pubsub,
1436
+ retryConfig: this.retryConfig,
1437
+ requestContext,
1438
+ resume,
1439
+ timeTravel,
1440
+ perStep,
1441
+ format,
1442
+ abortController: new AbortController(),
1443
+ // For Inngest, we don't pass workflowSpan - step spans use tracingIds instead
1444
+ workflowSpan: void 0,
1445
+ // Pass tracing IDs for durable span operations
1446
+ tracingIds: workflowSpanData ? {
1447
+ traceId: workflowSpanData.traceId,
1448
+ workflowSpanId: workflowSpanData.id
1449
+ } : void 0,
1450
+ outputOptions,
1451
+ outputWriter: async (chunk) => {
1452
+ try {
1453
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1454
+ type: "watch",
1455
+ runId,
1456
+ data: chunk
1457
+ });
1458
+ } catch (err) {
1459
+ this.logger.debug?.("Failed to publish watch event:", err);
1460
+ }
1461
+ }
1462
+ });
1463
+ } catch (error) {
1464
+ throw error;
1465
+ }
1466
+ await step.run(`workflow.${this.id}.finalize`, async () => {
1467
+ if (result.status !== "paused") {
1468
+ await engine.invokeLifecycleCallbacksInternal({
1469
+ status: result.status,
1470
+ result: "result" in result ? result.result : void 0,
1471
+ error: "error" in result ? result.error : void 0,
1472
+ steps: result.steps,
1473
+ tripwire: "tripwire" in result ? result.tripwire : void 0,
1474
+ runId,
1475
+ workflowId: this.id,
1476
+ resourceId,
1477
+ input: inputData,
1478
+ requestContext,
1479
+ state: result.state ?? initialState ?? {}
1480
+ });
1481
+ }
1482
+ if (workflowSpanData) {
1483
+ const observability = mastra?.observability?.getSelectedInstance({ requestContext });
1484
+ if (observability) {
1485
+ const workflowSpan = observability.rebuildSpan(workflowSpanData);
1486
+ if (result.status === "failed") {
1487
+ workflowSpan.error({
1488
+ error: result.error instanceof Error ? result.error : new Error(String(result.error)),
1489
+ attributes: { status: "failed" }
1490
+ });
1491
+ } else {
1492
+ workflowSpan.end({
1493
+ output: result.status === "success" ? result.result : void 0,
1494
+ attributes: { status: result.status }
1495
+ });
1496
+ }
1497
+ }
1498
+ }
1499
+ if (result.status === "failed") {
1500
+ throw new NonRetriableError(`Workflow failed`, {
1501
+ cause: result
1502
+ });
1355
1503
  }
1356
- })
1357
- )
1504
+ return result;
1505
+ });
1506
+ return { result, runId };
1507
+ }
1358
1508
  );
1359
- const hasFailed = results.find((result) => result.result.status === "failed");
1360
- const hasSuspended = results.find((result) => result.result.status === "suspended");
1361
- if (hasFailed) {
1362
- execResults = { status: "failed", error: hasFailed.result.error };
1363
- } else if (hasSuspended) {
1364
- execResults = { status: "suspended", payload: hasSuspended.result.suspendPayload };
1365
- } else {
1366
- execResults = {
1367
- status: "success",
1368
- output: results.reduce((acc, result, index) => {
1369
- if (result.result.status === "success") {
1370
- acc[stepsToRun[index].step.id] = result.output;
1509
+ return this.function;
1510
+ }
1511
+ getNestedFunctions(steps) {
1512
+ return steps.flatMap((step) => {
1513
+ if (step.type === "step" || step.type === "loop" || step.type === "foreach") {
1514
+ if (step.step instanceof _InngestWorkflow) {
1515
+ return [step.step.getFunction(), ...step.step.getNestedFunctions(step.step.executionGraph.steps)];
1516
+ }
1517
+ return [];
1518
+ } else if (step.type === "parallel" || step.type === "conditional") {
1519
+ return this.getNestedFunctions(step.steps);
1520
+ }
1521
+ return [];
1522
+ });
1523
+ }
1524
+ getFunctions() {
1525
+ return [
1526
+ this.getFunction(),
1527
+ ...this.cronConfig?.cron ? [this.createCronFunction()] : [],
1528
+ ...this.getNestedFunctions(this.executionGraph.steps)
1529
+ ];
1530
+ }
1531
+ };
1532
+ function prepareServeOptions({ mastra, inngest, functions: userFunctions = [], registerOptions }) {
1533
+ const wfs = mastra.listWorkflows();
1534
+ const workflowFunctions = Array.from(
1535
+ new Set(
1536
+ Object.values(wfs).flatMap((wf) => {
1537
+ if (wf instanceof InngestWorkflow) {
1538
+ wf.__registerMastra(mastra);
1539
+ return wf.getFunctions();
1540
+ }
1541
+ return [];
1542
+ })
1543
+ )
1544
+ );
1545
+ return {
1546
+ ...registerOptions,
1547
+ client: inngest,
1548
+ functions: [...workflowFunctions, ...userFunctions]
1549
+ };
1550
+ }
1551
+ function createServe(adapter) {
1552
+ return (options) => {
1553
+ const serveOptions = prepareServeOptions(options);
1554
+ return adapter(serveOptions);
1555
+ };
1556
+ }
1557
+ var serve = createServe(serve$1);
1558
+
1559
+ // src/types.ts
1560
+ var _compatibilityCheck = true;
1561
+
1562
+ // src/index.ts
1563
+ function isInngestWorkflow(input) {
1564
+ return input instanceof InngestWorkflow;
1565
+ }
1566
+ function isAgent(input) {
1567
+ return input instanceof Agent;
1568
+ }
1569
+ function isToolStep(input) {
1570
+ return input instanceof Tool;
1571
+ }
1572
+ function isStepParams(input) {
1573
+ return input !== null && typeof input === "object" && "id" in input && "execute" in input && !(input instanceof Agent) && !(input instanceof Tool) && !(input instanceof InngestWorkflow);
1574
+ }
1575
+ function isProcessor(obj) {
1576
+ return obj !== null && typeof obj === "object" && "id" in obj && typeof obj.id === "string" && !(obj instanceof Agent) && !(obj instanceof Tool) && !(obj instanceof InngestWorkflow) && (typeof obj.processInput === "function" || typeof obj.processInputStep === "function" || typeof obj.processOutputStream === "function" || typeof obj.processOutputResult === "function" || typeof obj.processOutputStep === "function");
1577
+ }
1578
+ function createStep(params, agentOrToolOptions) {
1579
+ if (isInngestWorkflow(params)) {
1580
+ return params;
1581
+ }
1582
+ if (isAgent(params)) {
1583
+ return createStepFromAgent(params, agentOrToolOptions);
1584
+ }
1585
+ if (isToolStep(params)) {
1586
+ return createStepFromTool(params, agentOrToolOptions);
1587
+ }
1588
+ if (isStepParams(params)) {
1589
+ return createStepFromParams(params);
1590
+ }
1591
+ if (isProcessor(params)) {
1592
+ return createStepFromProcessor(params);
1593
+ }
1594
+ throw new Error("Invalid input: expected StepParams, Agent, ToolStep, Processor, or InngestWorkflow");
1595
+ }
1596
+ function createStepFromParams(params) {
1597
+ return {
1598
+ id: params.id,
1599
+ description: params.description,
1600
+ inputSchema: params.inputSchema,
1601
+ stateSchema: params.stateSchema,
1602
+ outputSchema: params.outputSchema,
1603
+ resumeSchema: params.resumeSchema,
1604
+ suspendSchema: params.suspendSchema,
1605
+ scorers: params.scorers,
1606
+ retries: params.retries,
1607
+ execute: params.execute.bind(params)
1608
+ };
1609
+ }
1610
+ function createStepFromAgent(params, agentOrToolOptions) {
1611
+ const options = agentOrToolOptions ?? {};
1612
+ const outputSchema = options?.structuredOutput?.schema ?? z.object({ text: z.string() });
1613
+ const { retries, scorers, ...agentOptions } = options ?? {};
1614
+ return {
1615
+ id: params.name,
1616
+ description: params.getDescription(),
1617
+ inputSchema: z.object({
1618
+ prompt: z.string()
1619
+ }),
1620
+ outputSchema,
1621
+ retries,
1622
+ scorers,
1623
+ execute: async ({
1624
+ inputData,
1625
+ runId,
1626
+ [PUBSUB_SYMBOL]: pubsub,
1627
+ [STREAM_FORMAT_SYMBOL]: streamFormat,
1628
+ requestContext,
1629
+ tracingContext,
1630
+ abortSignal,
1631
+ abort,
1632
+ writer
1633
+ }) => {
1634
+ let streamPromise = {};
1635
+ streamPromise.promise = new Promise((resolve, reject) => {
1636
+ streamPromise.resolve = resolve;
1637
+ streamPromise.reject = reject;
1638
+ });
1639
+ let structuredResult = null;
1640
+ const toolData = {
1641
+ name: params.name,
1642
+ args: inputData
1643
+ };
1644
+ let stream;
1645
+ if ((await params.getModel()).specificationVersion === "v1") {
1646
+ const { fullStream } = await params.streamLegacy(inputData.prompt, {
1647
+ ...agentOptions ?? {},
1648
+ requestContext,
1649
+ tracingContext,
1650
+ onFinish: (result) => {
1651
+ const resultWithObject = result;
1652
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1653
+ structuredResult = resultWithObject.object;
1654
+ }
1655
+ streamPromise.resolve(result.text);
1656
+ void agentOptions?.onFinish?.(result);
1657
+ },
1658
+ abortSignal
1659
+ });
1660
+ stream = fullStream;
1661
+ } else {
1662
+ const modelOutput = await params.stream(inputData.prompt, {
1663
+ ...agentOptions ?? {},
1664
+ requestContext,
1665
+ tracingContext,
1666
+ onFinish: (result) => {
1667
+ const resultWithObject = result;
1668
+ if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
1669
+ structuredResult = resultWithObject.object;
1670
+ }
1671
+ streamPromise.resolve(result.text);
1672
+ void agentOptions?.onFinish?.(result);
1673
+ },
1674
+ abortSignal
1675
+ });
1676
+ stream = modelOutput.fullStream;
1677
+ }
1678
+ if (streamFormat === "legacy") {
1679
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1680
+ type: "watch",
1681
+ runId,
1682
+ data: { type: "tool-call-streaming-start", ...toolData ?? {} }
1683
+ });
1684
+ for await (const chunk of stream) {
1685
+ if (chunk.type === "text-delta") {
1686
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1687
+ type: "watch",
1688
+ runId,
1689
+ data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
1690
+ });
1371
1691
  }
1372
- return acc;
1373
- }, {})
1692
+ }
1693
+ await pubsub.publish(`workflow.events.v2.${runId}`, {
1694
+ type: "watch",
1695
+ runId,
1696
+ data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
1697
+ });
1698
+ } else {
1699
+ for await (const chunk of stream) {
1700
+ await writer.write(chunk);
1701
+ }
1702
+ }
1703
+ if (abortSignal.aborted) {
1704
+ return abort();
1705
+ }
1706
+ if (structuredResult !== null) {
1707
+ return structuredResult;
1708
+ }
1709
+ return {
1710
+ text: await streamPromise.promise
1711
+ };
1712
+ },
1713
+ component: params.component
1714
+ };
1715
+ }
1716
+ function createStepFromTool(params, agentOrToolOptions) {
1717
+ const toolOpts = agentOrToolOptions;
1718
+ if (!params.inputSchema || !params.outputSchema) {
1719
+ throw new Error("Tool must have input and output schemas defined");
1720
+ }
1721
+ return {
1722
+ id: params.id,
1723
+ description: params.description,
1724
+ inputSchema: params.inputSchema,
1725
+ outputSchema: params.outputSchema,
1726
+ resumeSchema: params.resumeSchema,
1727
+ suspendSchema: params.suspendSchema,
1728
+ retries: toolOpts?.retries,
1729
+ scorers: toolOpts?.scorers,
1730
+ execute: async ({
1731
+ inputData,
1732
+ mastra,
1733
+ requestContext,
1734
+ tracingContext,
1735
+ suspend,
1736
+ resumeData,
1737
+ runId,
1738
+ workflowId,
1739
+ state,
1740
+ setState
1741
+ }) => {
1742
+ const toolContext = {
1743
+ mastra,
1744
+ requestContext,
1745
+ tracingContext,
1746
+ workflow: {
1747
+ runId,
1748
+ resumeData,
1749
+ suspend,
1750
+ workflowId,
1751
+ state,
1752
+ setState
1753
+ }
1374
1754
  };
1755
+ return params.execute(inputData, toolContext);
1756
+ },
1757
+ component: "TOOL"
1758
+ };
1759
+ }
1760
+ function createStepFromProcessor(processor) {
1761
+ const getProcessorEntityType = (phase) => {
1762
+ switch (phase) {
1763
+ case "input":
1764
+ return EntityType.INPUT_PROCESSOR;
1765
+ case "inputStep":
1766
+ return EntityType.INPUT_STEP_PROCESSOR;
1767
+ case "outputStream":
1768
+ case "outputResult":
1769
+ return EntityType.OUTPUT_PROCESSOR;
1770
+ case "outputStep":
1771
+ return EntityType.OUTPUT_STEP_PROCESSOR;
1772
+ default:
1773
+ return EntityType.OUTPUT_PROCESSOR;
1774
+ }
1775
+ };
1776
+ const getSpanNamePrefix = (phase) => {
1777
+ switch (phase) {
1778
+ case "input":
1779
+ return "input processor";
1780
+ case "inputStep":
1781
+ return "input step processor";
1782
+ case "outputStream":
1783
+ return "output stream processor";
1784
+ case "outputResult":
1785
+ return "output processor";
1786
+ case "outputStep":
1787
+ return "output step processor";
1788
+ default:
1789
+ return "processor";
1790
+ }
1791
+ };
1792
+ const hasPhaseMethod = (phase) => {
1793
+ switch (phase) {
1794
+ case "input":
1795
+ return !!processor.processInput;
1796
+ case "inputStep":
1797
+ return !!processor.processInputStep;
1798
+ case "outputStream":
1799
+ return !!processor.processOutputStream;
1800
+ case "outputResult":
1801
+ return !!processor.processOutputResult;
1802
+ case "outputStep":
1803
+ return !!processor.processOutputStep;
1804
+ default:
1805
+ return false;
1375
1806
  }
1376
- if (execResults.status === "failed") {
1377
- conditionalSpan?.error({
1378
- error: new Error(execResults.error)
1807
+ };
1808
+ return {
1809
+ id: `processor:${processor.id}`,
1810
+ description: processor.name ?? `Processor ${processor.id}`,
1811
+ inputSchema: ProcessorStepSchema,
1812
+ outputSchema: ProcessorStepOutputSchema,
1813
+ execute: async ({ inputData, requestContext, tracingContext }) => {
1814
+ const input = inputData;
1815
+ const {
1816
+ phase,
1817
+ messages,
1818
+ messageList,
1819
+ stepNumber,
1820
+ systemMessages,
1821
+ part,
1822
+ streamParts,
1823
+ state,
1824
+ finishReason,
1825
+ toolCalls,
1826
+ text,
1827
+ retryCount,
1828
+ // inputStep phase fields for model/tools configuration
1829
+ model,
1830
+ tools,
1831
+ toolChoice,
1832
+ activeTools,
1833
+ providerOptions,
1834
+ modelSettings,
1835
+ structuredOutput,
1836
+ steps
1837
+ } = input;
1838
+ const abort = (reason, options) => {
1839
+ throw new TripWire(reason || `Tripwire triggered by ${processor.id}`, options, processor.id);
1840
+ };
1841
+ if (!hasPhaseMethod(phase)) {
1842
+ return input;
1843
+ }
1844
+ const currentSpan = tracingContext?.currentSpan;
1845
+ const parentSpan = phase === "inputStep" || phase === "outputStep" ? currentSpan?.findParent(SpanType.MODEL_STEP) || currentSpan : currentSpan?.findParent(SpanType.AGENT_RUN) || currentSpan;
1846
+ const processorSpan = phase !== "outputStream" ? parentSpan?.createChildSpan({
1847
+ type: SpanType.PROCESSOR_RUN,
1848
+ name: `${getSpanNamePrefix(phase)}: ${processor.id}`,
1849
+ entityType: getProcessorEntityType(phase),
1850
+ entityId: processor.id,
1851
+ entityName: processor.name ?? processor.id,
1852
+ input: { phase, messageCount: messages?.length },
1853
+ attributes: {
1854
+ processorExecutor: "workflow",
1855
+ // Read processorIndex from processor (set in combineProcessorsIntoWorkflow)
1856
+ processorIndex: processor.processorIndex
1857
+ }
1858
+ }) : void 0;
1859
+ const processorTracingContext = processorSpan ? { currentSpan: processorSpan } : tracingContext;
1860
+ const baseContext = {
1861
+ abort,
1862
+ retryCount: retryCount ?? 0,
1863
+ requestContext,
1864
+ tracingContext: processorTracingContext
1865
+ };
1866
+ const passThrough = {
1867
+ phase,
1868
+ // Auto-create MessageList from messages if not provided
1869
+ // This enables running processor workflows from the UI where messageList can't be serialized
1870
+ messageList: messageList ?? (Array.isArray(messages) ? new MessageList().add(messages, "input").addSystem(systemMessages ?? []) : void 0),
1871
+ stepNumber,
1872
+ systemMessages,
1873
+ streamParts,
1874
+ state,
1875
+ finishReason,
1876
+ toolCalls,
1877
+ text,
1878
+ retryCount,
1879
+ // inputStep phase fields for model/tools configuration
1880
+ model,
1881
+ tools,
1882
+ toolChoice,
1883
+ activeTools,
1884
+ providerOptions,
1885
+ modelSettings,
1886
+ structuredOutput,
1887
+ steps
1888
+ };
1889
+ const executePhaseWithSpan = async (fn) => {
1890
+ try {
1891
+ const result = await fn();
1892
+ processorSpan?.end({ output: result });
1893
+ return result;
1894
+ } catch (error) {
1895
+ if (error instanceof TripWire) {
1896
+ processorSpan?.end({ output: { tripwire: error.message } });
1897
+ } else {
1898
+ processorSpan?.error({ error, endSpan: true });
1899
+ }
1900
+ throw error;
1901
+ }
1902
+ };
1903
+ return executePhaseWithSpan(async () => {
1904
+ switch (phase) {
1905
+ case "input": {
1906
+ if (processor.processInput) {
1907
+ if (!passThrough.messageList) {
1908
+ throw new MastraError({
1909
+ category: ErrorCategory.USER,
1910
+ domain: ErrorDomain.MASTRA_WORKFLOW,
1911
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
1912
+ text: `Processor ${processor.id} requires messageList or messages for processInput phase`
1913
+ });
1914
+ }
1915
+ const idsBeforeProcessing = messages.map((m) => m.id);
1916
+ const check = passThrough.messageList.makeMessageSourceChecker();
1917
+ const result = await processor.processInput({
1918
+ ...baseContext,
1919
+ messages,
1920
+ messageList: passThrough.messageList,
1921
+ systemMessages: systemMessages ?? []
1922
+ });
1923
+ if (result instanceof MessageList) {
1924
+ if (result !== passThrough.messageList) {
1925
+ throw new MastraError({
1926
+ category: ErrorCategory.USER,
1927
+ domain: ErrorDomain.MASTRA_WORKFLOW,
1928
+ id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
1929
+ text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
1930
+ });
1931
+ }
1932
+ return {
1933
+ ...passThrough,
1934
+ messages: result.get.all.db(),
1935
+ systemMessages: result.getAllSystemMessages()
1936
+ };
1937
+ } else if (Array.isArray(result)) {
1938
+ ProcessorRunner.applyMessagesToMessageList(
1939
+ result,
1940
+ passThrough.messageList,
1941
+ idsBeforeProcessing,
1942
+ check,
1943
+ "input"
1944
+ );
1945
+ return { ...passThrough, messages: result };
1946
+ } else if (result && "messages" in result && "systemMessages" in result) {
1947
+ const typedResult = result;
1948
+ ProcessorRunner.applyMessagesToMessageList(
1949
+ typedResult.messages,
1950
+ passThrough.messageList,
1951
+ idsBeforeProcessing,
1952
+ check,
1953
+ "input"
1954
+ );
1955
+ passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
1956
+ return {
1957
+ ...passThrough,
1958
+ messages: typedResult.messages,
1959
+ systemMessages: typedResult.systemMessages
1960
+ };
1961
+ }
1962
+ return { ...passThrough, messages };
1963
+ }
1964
+ return { ...passThrough, messages };
1965
+ }
1966
+ case "inputStep": {
1967
+ if (processor.processInputStep) {
1968
+ if (!passThrough.messageList) {
1969
+ throw new MastraError({
1970
+ category: ErrorCategory.USER,
1971
+ domain: ErrorDomain.MASTRA_WORKFLOW,
1972
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
1973
+ text: `Processor ${processor.id} requires messageList or messages for processInputStep phase`
1974
+ });
1975
+ }
1976
+ const idsBeforeProcessing = messages.map((m) => m.id);
1977
+ const check = passThrough.messageList.makeMessageSourceChecker();
1978
+ const result = await processor.processInputStep({
1979
+ ...baseContext,
1980
+ messages,
1981
+ messageList: passThrough.messageList,
1982
+ stepNumber: stepNumber ?? 0,
1983
+ systemMessages: systemMessages ?? [],
1984
+ // Pass model/tools configuration fields - types match ProcessInputStepArgs
1985
+ model,
1986
+ tools,
1987
+ toolChoice,
1988
+ activeTools,
1989
+ providerOptions,
1990
+ modelSettings,
1991
+ structuredOutput,
1992
+ steps: steps ?? []
1993
+ });
1994
+ const validatedResult = await ProcessorRunner.validateAndFormatProcessInputStepResult(result, {
1995
+ messageList: passThrough.messageList,
1996
+ processor,
1997
+ stepNumber: stepNumber ?? 0
1998
+ });
1999
+ if (validatedResult.messages) {
2000
+ ProcessorRunner.applyMessagesToMessageList(
2001
+ validatedResult.messages,
2002
+ passThrough.messageList,
2003
+ idsBeforeProcessing,
2004
+ check
2005
+ );
2006
+ }
2007
+ if (validatedResult.systemMessages) {
2008
+ passThrough.messageList.replaceAllSystemMessages(validatedResult.systemMessages);
2009
+ }
2010
+ return { ...passThrough, messages, ...validatedResult };
2011
+ }
2012
+ return { ...passThrough, messages };
2013
+ }
2014
+ case "outputStream": {
2015
+ if (processor.processOutputStream) {
2016
+ const spanKey = `__outputStreamSpan_${processor.id}`;
2017
+ const mutableState = state ?? {};
2018
+ let processorSpan2 = mutableState[spanKey];
2019
+ if (!processorSpan2 && parentSpan) {
2020
+ processorSpan2 = parentSpan.createChildSpan({
2021
+ type: SpanType.PROCESSOR_RUN,
2022
+ name: `output stream processor: ${processor.id}`,
2023
+ entityType: EntityType.OUTPUT_PROCESSOR,
2024
+ entityId: processor.id,
2025
+ entityName: processor.name ?? processor.id,
2026
+ input: { phase, streamParts: [] },
2027
+ attributes: {
2028
+ processorExecutor: "workflow",
2029
+ processorIndex: processor.processorIndex
2030
+ }
2031
+ });
2032
+ mutableState[spanKey] = processorSpan2;
2033
+ }
2034
+ if (processorSpan2) {
2035
+ processorSpan2.input = {
2036
+ phase,
2037
+ streamParts: streamParts ?? [],
2038
+ totalChunks: (streamParts ?? []).length
2039
+ };
2040
+ }
2041
+ const processorTracingContext2 = processorSpan2 ? { currentSpan: processorSpan2 } : baseContext.tracingContext;
2042
+ let result;
2043
+ try {
2044
+ result = await processor.processOutputStream({
2045
+ ...baseContext,
2046
+ tracingContext: processorTracingContext2,
2047
+ part,
2048
+ streamParts: streamParts ?? [],
2049
+ state: mutableState,
2050
+ messageList: passThrough.messageList
2051
+ // Optional for stream processing
2052
+ });
2053
+ if (part && part.type === "finish") {
2054
+ processorSpan2?.end({ output: result });
2055
+ delete mutableState[spanKey];
2056
+ }
2057
+ } catch (error) {
2058
+ if (error instanceof TripWire) {
2059
+ processorSpan2?.end({ output: { tripwire: error.message } });
2060
+ } else {
2061
+ processorSpan2?.error({ error, endSpan: true });
2062
+ }
2063
+ delete mutableState[spanKey];
2064
+ throw error;
2065
+ }
2066
+ return { ...passThrough, state: mutableState, part: result };
2067
+ }
2068
+ return { ...passThrough, part };
2069
+ }
2070
+ case "outputResult": {
2071
+ if (processor.processOutputResult) {
2072
+ if (!passThrough.messageList) {
2073
+ throw new MastraError({
2074
+ category: ErrorCategory.USER,
2075
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2076
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
2077
+ text: `Processor ${processor.id} requires messageList or messages for processOutputResult phase`
2078
+ });
2079
+ }
2080
+ const idsBeforeProcessing = messages.map((m) => m.id);
2081
+ const check = passThrough.messageList.makeMessageSourceChecker();
2082
+ const result = await processor.processOutputResult({
2083
+ ...baseContext,
2084
+ messages,
2085
+ messageList: passThrough.messageList
2086
+ });
2087
+ if (result instanceof MessageList) {
2088
+ if (result !== passThrough.messageList) {
2089
+ throw new MastraError({
2090
+ category: ErrorCategory.USER,
2091
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2092
+ id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
2093
+ text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
2094
+ });
2095
+ }
2096
+ return {
2097
+ ...passThrough,
2098
+ messages: result.get.all.db(),
2099
+ systemMessages: result.getAllSystemMessages()
2100
+ };
2101
+ } else if (Array.isArray(result)) {
2102
+ ProcessorRunner.applyMessagesToMessageList(
2103
+ result,
2104
+ passThrough.messageList,
2105
+ idsBeforeProcessing,
2106
+ check,
2107
+ "response"
2108
+ );
2109
+ return { ...passThrough, messages: result };
2110
+ } else if (result && "messages" in result && "systemMessages" in result) {
2111
+ const typedResult = result;
2112
+ ProcessorRunner.applyMessagesToMessageList(
2113
+ typedResult.messages,
2114
+ passThrough.messageList,
2115
+ idsBeforeProcessing,
2116
+ check,
2117
+ "response"
2118
+ );
2119
+ passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
2120
+ return {
2121
+ ...passThrough,
2122
+ messages: typedResult.messages,
2123
+ systemMessages: typedResult.systemMessages
2124
+ };
2125
+ }
2126
+ return { ...passThrough, messages };
2127
+ }
2128
+ return { ...passThrough, messages };
2129
+ }
2130
+ case "outputStep": {
2131
+ if (processor.processOutputStep) {
2132
+ if (!passThrough.messageList) {
2133
+ throw new MastraError({
2134
+ category: ErrorCategory.USER,
2135
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2136
+ id: "PROCESSOR_MISSING_MESSAGE_LIST",
2137
+ text: `Processor ${processor.id} requires messageList or messages for processOutputStep phase`
2138
+ });
2139
+ }
2140
+ const idsBeforeProcessing = messages.map((m) => m.id);
2141
+ const check = passThrough.messageList.makeMessageSourceChecker();
2142
+ const result = await processor.processOutputStep({
2143
+ ...baseContext,
2144
+ messages,
2145
+ messageList: passThrough.messageList,
2146
+ stepNumber: stepNumber ?? 0,
2147
+ finishReason,
2148
+ toolCalls,
2149
+ text,
2150
+ systemMessages: systemMessages ?? [],
2151
+ steps: steps ?? []
2152
+ });
2153
+ if (result instanceof MessageList) {
2154
+ if (result !== passThrough.messageList) {
2155
+ throw new MastraError({
2156
+ category: ErrorCategory.USER,
2157
+ domain: ErrorDomain.MASTRA_WORKFLOW,
2158
+ id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
2159
+ text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
2160
+ });
2161
+ }
2162
+ return {
2163
+ ...passThrough,
2164
+ messages: result.get.all.db(),
2165
+ systemMessages: result.getAllSystemMessages()
2166
+ };
2167
+ } else if (Array.isArray(result)) {
2168
+ ProcessorRunner.applyMessagesToMessageList(
2169
+ result,
2170
+ passThrough.messageList,
2171
+ idsBeforeProcessing,
2172
+ check,
2173
+ "response"
2174
+ );
2175
+ return { ...passThrough, messages: result };
2176
+ } else if (result && "messages" in result && "systemMessages" in result) {
2177
+ const typedResult = result;
2178
+ ProcessorRunner.applyMessagesToMessageList(
2179
+ typedResult.messages,
2180
+ passThrough.messageList,
2181
+ idsBeforeProcessing,
2182
+ check,
2183
+ "response"
2184
+ );
2185
+ passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
2186
+ return {
2187
+ ...passThrough,
2188
+ messages: typedResult.messages,
2189
+ systemMessages: typedResult.systemMessages
2190
+ };
2191
+ }
2192
+ return { ...passThrough, messages };
2193
+ }
2194
+ return { ...passThrough, messages };
2195
+ }
2196
+ default:
2197
+ return { ...passThrough, messages };
2198
+ }
1379
2199
  });
1380
- } else {
1381
- conditionalSpan?.end({
1382
- output: execResults.output || execResults
2200
+ },
2201
+ component: "PROCESSOR"
2202
+ };
2203
+ }
2204
+ function init(inngest) {
2205
+ return {
2206
+ createWorkflow(params) {
2207
+ return new InngestWorkflow(
2208
+ params,
2209
+ inngest
2210
+ );
2211
+ },
2212
+ createStep,
2213
+ cloneStep(step, opts) {
2214
+ return {
2215
+ id: opts.id,
2216
+ description: step.description,
2217
+ inputSchema: step.inputSchema,
2218
+ outputSchema: step.outputSchema,
2219
+ resumeSchema: step.resumeSchema,
2220
+ suspendSchema: step.suspendSchema,
2221
+ stateSchema: step.stateSchema,
2222
+ execute: step.execute,
2223
+ retries: step.retries,
2224
+ scorers: step.scorers,
2225
+ component: step.component
2226
+ };
2227
+ },
2228
+ cloneWorkflow(workflow, opts) {
2229
+ const wf = new Workflow({
2230
+ id: opts.id,
2231
+ inputSchema: workflow.inputSchema,
2232
+ outputSchema: workflow.outputSchema,
2233
+ steps: workflow.stepDefs,
2234
+ mastra: workflow.mastra,
2235
+ options: workflow.options
1383
2236
  });
2237
+ wf.setStepFlow(workflow.stepGraph);
2238
+ wf.commit();
2239
+ return wf;
1384
2240
  }
1385
- return execResults;
1386
- }
1387
- };
2241
+ };
2242
+ }
1388
2243
 
1389
- export { InngestExecutionEngine, InngestRun, InngestWorkflow, createStep, init, serve };
2244
+ export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createServe, createStep, init, serve };
1390
2245
  //# sourceMappingURL=index.js.map
1391
2246
  //# sourceMappingURL=index.js.map