@mastra/inngest 0.0.0-fix-prompt-enhance-route-20251210210827 → 0.0.0-fix-11329-windows-path-20251222155941
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/CHANGELOG.md +410 -12
- package/dist/execution-engine.d.ts +30 -5
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/index.cjs +441 -120
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +436 -116
- package/dist/index.js.map +1 -1
- package/dist/pubsub.d.ts +56 -0
- package/dist/pubsub.d.ts.map +1 -0
- package/dist/run.d.ts +37 -4
- package/dist/run.d.ts.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/package.json +7 -8
package/dist/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Tool } from '@mastra/core/tools';
|
|
2
|
-
import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, Workflow } from '@mastra/core/workflows';
|
|
3
|
-
import {
|
|
2
|
+
import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, hydrateSerializedStepErrors, Workflow } from '@mastra/core/workflows';
|
|
3
|
+
import { PUBSUB_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { randomUUID } from 'crypto';
|
|
6
6
|
import { RequestContext } from '@mastra/core/di';
|
|
7
7
|
import { RetryAfterError, NonRetriableError } from 'inngest';
|
|
8
|
-
import {
|
|
8
|
+
import { getErrorFromUnknown } from '@mastra/core/error';
|
|
9
9
|
import { subscribe } from '@inngest/realtime';
|
|
10
|
+
import { PubSub } from '@mastra/core/events';
|
|
11
|
+
import { ReadableStream } from 'stream/web';
|
|
10
12
|
import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
|
|
11
13
|
import { serve as serve$1 } from 'inngest/hono';
|
|
12
14
|
|
|
@@ -23,17 +25,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
23
25
|
// Hook Overrides
|
|
24
26
|
// =============================================================================
|
|
25
27
|
/**
|
|
26
|
-
* Format errors
|
|
28
|
+
* Format errors while preserving Error instances and their custom properties.
|
|
29
|
+
* Uses getErrorFromUnknown to ensure all error properties are preserved.
|
|
27
30
|
*/
|
|
28
31
|
formatResultError(error, lastOutput) {
|
|
29
|
-
if (error instanceof Error) {
|
|
30
|
-
return error.stack ?? error.message;
|
|
31
|
-
}
|
|
32
32
|
const outputError = lastOutput?.error;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
const errorSource = error || outputError;
|
|
34
|
+
const errorInstance = getErrorFromUnknown(errorSource, {
|
|
35
|
+
serializeStack: true,
|
|
36
|
+
// Include stack in JSON for better debugging in Inngest
|
|
37
|
+
fallbackMessage: "Unknown workflow error"
|
|
38
|
+
});
|
|
39
|
+
return errorInstance.toJSON();
|
|
37
40
|
}
|
|
38
41
|
/**
|
|
39
42
|
* Detect InngestWorkflow instances for special nested workflow handling
|
|
@@ -65,18 +68,24 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
65
68
|
error: e,
|
|
66
69
|
attributes: { status: "failed" }
|
|
67
70
|
});
|
|
71
|
+
if (cause.error && !(cause.error instanceof Error)) {
|
|
72
|
+
cause.error = getErrorFromUnknown(cause.error, { serializeStack: false });
|
|
73
|
+
}
|
|
68
74
|
return { ok: false, error: cause };
|
|
69
75
|
}
|
|
70
|
-
const
|
|
76
|
+
const errorInstance = getErrorFromUnknown(e, {
|
|
77
|
+
serializeStack: false,
|
|
78
|
+
fallbackMessage: "Unknown step execution error"
|
|
79
|
+
});
|
|
71
80
|
params.stepSpan?.error({
|
|
72
|
-
error:
|
|
81
|
+
error: errorInstance,
|
|
73
82
|
attributes: { status: "failed" }
|
|
74
83
|
});
|
|
75
84
|
return {
|
|
76
85
|
ok: false,
|
|
77
86
|
error: {
|
|
78
87
|
status: "failed",
|
|
79
|
-
error:
|
|
88
|
+
error: errorInstance,
|
|
80
89
|
endedAt: Date.now()
|
|
81
90
|
}
|
|
82
91
|
};
|
|
@@ -105,11 +114,14 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
105
114
|
return await operationFn();
|
|
106
115
|
} catch (e) {
|
|
107
116
|
if (retryConfig) {
|
|
108
|
-
const
|
|
109
|
-
|
|
117
|
+
const errorInstance = getErrorFromUnknown(e, {
|
|
118
|
+
serializeStack: false,
|
|
119
|
+
fallbackMessage: "Unknown step execution error"
|
|
120
|
+
});
|
|
121
|
+
throw new RetryAfterError(errorInstance.message, retryConfig.delay, {
|
|
110
122
|
cause: {
|
|
111
123
|
status: "failed",
|
|
112
|
-
error:
|
|
124
|
+
error: errorInstance,
|
|
113
125
|
endedAt: Date.now()
|
|
114
126
|
}
|
|
115
127
|
});
|
|
@@ -124,6 +136,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
124
136
|
getEngineContext() {
|
|
125
137
|
return { step: this.inngestStep };
|
|
126
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
|
|
141
|
+
* (wrapped in step.run for durability), not in execute(). Override to skip.
|
|
142
|
+
*/
|
|
143
|
+
async invokeLifecycleCallbacks(_result) {
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
|
|
147
|
+
*/
|
|
148
|
+
async invokeLifecycleCallbacksInternal(result) {
|
|
149
|
+
return super.invokeLifecycleCallbacks(result);
|
|
150
|
+
}
|
|
127
151
|
/**
|
|
128
152
|
* Execute nested InngestWorkflow using inngestStep.invoke() for durability.
|
|
129
153
|
* This MUST be called directly (not inside step.run()) due to Inngest constraints.
|
|
@@ -132,7 +156,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
132
156
|
if (!(params.step instanceof InngestWorkflow)) {
|
|
133
157
|
return null;
|
|
134
158
|
}
|
|
135
|
-
const {
|
|
159
|
+
const {
|
|
160
|
+
step,
|
|
161
|
+
stepResults,
|
|
162
|
+
executionContext,
|
|
163
|
+
resume,
|
|
164
|
+
timeTravel,
|
|
165
|
+
prevOutput,
|
|
166
|
+
inputData,
|
|
167
|
+
pubsub,
|
|
168
|
+
startedAt,
|
|
169
|
+
perStep
|
|
170
|
+
} = params;
|
|
136
171
|
const isResume = !!resume?.steps?.length;
|
|
137
172
|
let result;
|
|
138
173
|
let runId;
|
|
@@ -157,7 +192,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
157
192
|
resumePayload: resume.resumePayload,
|
|
158
193
|
resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
|
|
159
194
|
},
|
|
160
|
-
outputOptions: { includeState: true }
|
|
195
|
+
outputOptions: { includeState: true },
|
|
196
|
+
perStep
|
|
161
197
|
}
|
|
162
198
|
});
|
|
163
199
|
result = invokeResp.result;
|
|
@@ -183,7 +219,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
183
219
|
timeTravel: timeTravelParams,
|
|
184
220
|
initialState: executionContext.state ?? {},
|
|
185
221
|
runId: executionContext.runId,
|
|
186
|
-
outputOptions: { includeState: true }
|
|
222
|
+
outputOptions: { includeState: true },
|
|
223
|
+
perStep
|
|
187
224
|
}
|
|
188
225
|
});
|
|
189
226
|
result = invokeResp.result;
|
|
@@ -195,7 +232,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
195
232
|
data: {
|
|
196
233
|
inputData,
|
|
197
234
|
initialState: executionContext.state ?? {},
|
|
198
|
-
outputOptions: { includeState: true }
|
|
235
|
+
outputOptions: { includeState: true },
|
|
236
|
+
perStep
|
|
199
237
|
}
|
|
200
238
|
});
|
|
201
239
|
result = invokeResp.result;
|
|
@@ -221,16 +259,20 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
221
259
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
222
260
|
async () => {
|
|
223
261
|
if (result.status === "failed") {
|
|
224
|
-
await
|
|
225
|
-
type: "
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
262
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
263
|
+
type: "watch",
|
|
264
|
+
runId: executionContext.runId,
|
|
265
|
+
data: {
|
|
266
|
+
type: "workflow-step-result",
|
|
267
|
+
payload: {
|
|
268
|
+
id: step.id,
|
|
269
|
+
status: "failed",
|
|
270
|
+
error: result?.error,
|
|
271
|
+
payload: prevOutput
|
|
272
|
+
}
|
|
231
273
|
}
|
|
232
274
|
});
|
|
233
|
-
return { executionContext, result: { status: "failed", error: result?.error } };
|
|
275
|
+
return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
|
|
234
276
|
} else if (result.status === "suspended") {
|
|
235
277
|
const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
|
|
236
278
|
const stepRes = stepResult;
|
|
@@ -239,17 +281,22 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
239
281
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
240
282
|
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
241
283
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
242
|
-
await
|
|
243
|
-
type: "
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
284
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
285
|
+
type: "watch",
|
|
286
|
+
runId: executionContext.runId,
|
|
287
|
+
data: {
|
|
288
|
+
type: "workflow-step-suspended",
|
|
289
|
+
payload: {
|
|
290
|
+
id: step.id,
|
|
291
|
+
status: "suspended"
|
|
292
|
+
}
|
|
247
293
|
}
|
|
248
294
|
});
|
|
249
295
|
return {
|
|
250
296
|
executionContext,
|
|
251
297
|
result: {
|
|
252
298
|
status: "suspended",
|
|
299
|
+
suspendedAt: Date.now(),
|
|
253
300
|
payload: stepResult.payload,
|
|
254
301
|
suspendPayload: {
|
|
255
302
|
...stepResult?.suspendPayload,
|
|
@@ -262,39 +309,205 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
262
309
|
executionContext,
|
|
263
310
|
result: {
|
|
264
311
|
status: "suspended",
|
|
312
|
+
suspendedAt: Date.now(),
|
|
265
313
|
payload: {}
|
|
266
314
|
}
|
|
267
315
|
};
|
|
316
|
+
} else if (result.status === "tripwire") {
|
|
317
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
318
|
+
type: "watch",
|
|
319
|
+
runId: executionContext.runId,
|
|
320
|
+
data: {
|
|
321
|
+
type: "workflow-step-result",
|
|
322
|
+
payload: {
|
|
323
|
+
id: step.id,
|
|
324
|
+
status: "tripwire",
|
|
325
|
+
error: result?.tripwire?.reason,
|
|
326
|
+
payload: prevOutput
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
return {
|
|
331
|
+
executionContext,
|
|
332
|
+
result: {
|
|
333
|
+
status: "tripwire",
|
|
334
|
+
tripwire: result?.tripwire,
|
|
335
|
+
endedAt: Date.now()
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
} else if (perStep || result.status === "paused") {
|
|
339
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
340
|
+
type: "watch",
|
|
341
|
+
runId: executionContext.runId,
|
|
342
|
+
data: {
|
|
343
|
+
type: "workflow-step-result",
|
|
344
|
+
payload: {
|
|
345
|
+
id: step.id,
|
|
346
|
+
status: "paused"
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
351
|
+
type: "watch",
|
|
352
|
+
runId: executionContext.runId,
|
|
353
|
+
data: {
|
|
354
|
+
type: "workflow-step-finish",
|
|
355
|
+
payload: {
|
|
356
|
+
id: step.id,
|
|
357
|
+
metadata: {}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
return { executionContext, result: { status: "paused" } };
|
|
268
362
|
}
|
|
269
|
-
await
|
|
270
|
-
type: "
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
363
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
364
|
+
type: "watch",
|
|
365
|
+
runId: executionContext.runId,
|
|
366
|
+
data: {
|
|
367
|
+
type: "workflow-step-result",
|
|
368
|
+
payload: {
|
|
369
|
+
id: step.id,
|
|
370
|
+
status: "success",
|
|
371
|
+
output: result?.result
|
|
372
|
+
}
|
|
275
373
|
}
|
|
276
374
|
});
|
|
277
|
-
await
|
|
278
|
-
type: "
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
375
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
376
|
+
type: "watch",
|
|
377
|
+
runId: executionContext.runId,
|
|
378
|
+
data: {
|
|
379
|
+
type: "workflow-step-finish",
|
|
380
|
+
payload: {
|
|
381
|
+
id: step.id,
|
|
382
|
+
metadata: {}
|
|
383
|
+
}
|
|
282
384
|
}
|
|
283
385
|
});
|
|
284
|
-
return { executionContext, result: { status: "success", output: result?.result } };
|
|
386
|
+
return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
|
|
285
387
|
}
|
|
286
388
|
);
|
|
287
389
|
Object.assign(executionContext, res.executionContext);
|
|
288
390
|
return {
|
|
289
391
|
...res.result,
|
|
290
392
|
startedAt,
|
|
291
|
-
endedAt: Date.now(),
|
|
292
393
|
payload: inputData,
|
|
293
394
|
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
294
395
|
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
295
396
|
};
|
|
296
397
|
}
|
|
297
398
|
};
|
|
399
|
+
var InngestPubSub = class extends PubSub {
|
|
400
|
+
inngest;
|
|
401
|
+
workflowId;
|
|
402
|
+
publishFn;
|
|
403
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
404
|
+
constructor(inngest, workflowId, publishFn) {
|
|
405
|
+
super();
|
|
406
|
+
this.inngest = inngest;
|
|
407
|
+
this.workflowId = workflowId;
|
|
408
|
+
this.publishFn = publishFn;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Publish an event to Inngest's realtime system.
|
|
412
|
+
*
|
|
413
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
414
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
415
|
+
*/
|
|
416
|
+
async publish(topic, event) {
|
|
417
|
+
if (!this.publishFn) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
421
|
+
if (!match) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const runId = match[1];
|
|
425
|
+
try {
|
|
426
|
+
await this.publishFn({
|
|
427
|
+
channel: `workflow:${this.workflowId}:${runId}`,
|
|
428
|
+
topic: "watch",
|
|
429
|
+
data: event.data
|
|
430
|
+
});
|
|
431
|
+
} catch (err) {
|
|
432
|
+
console.error("InngestPubSub publish error:", err?.message ?? err);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Subscribe to events from Inngest's realtime system.
|
|
437
|
+
*
|
|
438
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
439
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
440
|
+
*/
|
|
441
|
+
async subscribe(topic, cb) {
|
|
442
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
443
|
+
if (!match || !match[1]) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
const runId = match[1];
|
|
447
|
+
if (this.subscriptions.has(topic)) {
|
|
448
|
+
this.subscriptions.get(topic).callbacks.add(cb);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const callbacks = /* @__PURE__ */ new Set([cb]);
|
|
452
|
+
const channel = `workflow:${this.workflowId}:${runId}`;
|
|
453
|
+
const streamPromise = subscribe(
|
|
454
|
+
{
|
|
455
|
+
channel,
|
|
456
|
+
topics: ["watch"],
|
|
457
|
+
app: this.inngest
|
|
458
|
+
},
|
|
459
|
+
(message) => {
|
|
460
|
+
const event = {
|
|
461
|
+
id: crypto.randomUUID(),
|
|
462
|
+
type: "watch",
|
|
463
|
+
runId,
|
|
464
|
+
data: message.data,
|
|
465
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
466
|
+
};
|
|
467
|
+
for (const callback of callbacks) {
|
|
468
|
+
callback(event);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
);
|
|
472
|
+
this.subscriptions.set(topic, {
|
|
473
|
+
unsubscribe: () => {
|
|
474
|
+
streamPromise.then((stream) => stream.cancel()).catch((err) => {
|
|
475
|
+
console.error("InngestPubSub unsubscribe error:", err);
|
|
476
|
+
});
|
|
477
|
+
},
|
|
478
|
+
callbacks
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Unsubscribe a callback from a topic.
|
|
483
|
+
* If no callbacks remain, the underlying Inngest subscription is cancelled.
|
|
484
|
+
*/
|
|
485
|
+
async unsubscribe(topic, cb) {
|
|
486
|
+
const sub = this.subscriptions.get(topic);
|
|
487
|
+
if (!sub) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
sub.callbacks.delete(cb);
|
|
491
|
+
if (sub.callbacks.size === 0) {
|
|
492
|
+
sub.unsubscribe();
|
|
493
|
+
this.subscriptions.delete(topic);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Flush any pending operations. No-op for Inngest.
|
|
498
|
+
*/
|
|
499
|
+
async flush() {
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Clean up all subscriptions during graceful shutdown.
|
|
503
|
+
*/
|
|
504
|
+
async close() {
|
|
505
|
+
for (const [, sub] of this.subscriptions) {
|
|
506
|
+
sub.unsubscribe();
|
|
507
|
+
}
|
|
508
|
+
this.subscriptions.clear();
|
|
509
|
+
}
|
|
510
|
+
};
|
|
298
511
|
var InngestRun = class extends Run {
|
|
299
512
|
inngest;
|
|
300
513
|
serializedStepGraph;
|
|
@@ -306,27 +519,77 @@ var InngestRun = class extends Run {
|
|
|
306
519
|
this.#mastra = params.mastra;
|
|
307
520
|
}
|
|
308
521
|
async getRuns(eventId) {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
522
|
+
const maxRetries = 3;
|
|
523
|
+
let lastError = null;
|
|
524
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
525
|
+
try {
|
|
526
|
+
const response = await fetch(
|
|
527
|
+
`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
|
|
528
|
+
{
|
|
529
|
+
headers: {
|
|
530
|
+
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
);
|
|
534
|
+
if (response.status === 429) {
|
|
535
|
+
const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
|
|
536
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
if (!response.ok) {
|
|
540
|
+
throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
|
|
541
|
+
}
|
|
542
|
+
const text = await response.text();
|
|
543
|
+
if (!text) {
|
|
544
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const json = JSON.parse(text);
|
|
548
|
+
return json.data;
|
|
549
|
+
} catch (error) {
|
|
550
|
+
lastError = error;
|
|
551
|
+
if (attempt < maxRetries - 1) {
|
|
552
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
|
|
553
|
+
}
|
|
312
554
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return json.data;
|
|
555
|
+
}
|
|
556
|
+
throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
|
|
316
557
|
}
|
|
317
|
-
async getRunOutput(eventId) {
|
|
318
|
-
|
|
558
|
+
async getRunOutput(eventId, maxWaitMs = 3e5) {
|
|
559
|
+
const startTime = Date.now();
|
|
319
560
|
const storage = this.#mastra?.getStorage();
|
|
320
|
-
while (
|
|
321
|
-
|
|
322
|
-
|
|
561
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
562
|
+
let runs;
|
|
563
|
+
try {
|
|
564
|
+
runs = await this.getRuns(eventId);
|
|
565
|
+
} catch (error) {
|
|
566
|
+
if (error instanceof NonRetriableError) {
|
|
567
|
+
throw error;
|
|
568
|
+
}
|
|
569
|
+
throw new NonRetriableError(
|
|
570
|
+
`Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
|
|
574
|
+
return runs[0];
|
|
575
|
+
}
|
|
323
576
|
if (runs?.[0]?.status === "Failed") {
|
|
324
577
|
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
325
578
|
workflowName: this.workflowId,
|
|
326
579
|
runId: this.runId
|
|
327
580
|
});
|
|
581
|
+
if (snapshot?.context) {
|
|
582
|
+
snapshot.context = hydrateSerializedStepErrors(snapshot.context);
|
|
583
|
+
}
|
|
328
584
|
return {
|
|
329
|
-
output: {
|
|
585
|
+
output: {
|
|
586
|
+
result: {
|
|
587
|
+
steps: snapshot?.context,
|
|
588
|
+
status: "failed",
|
|
589
|
+
// Get the original error from NonRetriableError's cause (which contains the workflow result)
|
|
590
|
+
error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
|
|
591
|
+
}
|
|
592
|
+
}
|
|
330
593
|
};
|
|
331
594
|
}
|
|
332
595
|
if (runs?.[0]?.status === "Cancelled") {
|
|
@@ -336,8 +599,9 @@ var InngestRun = class extends Run {
|
|
|
336
599
|
});
|
|
337
600
|
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
338
601
|
}
|
|
602
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
|
|
339
603
|
}
|
|
340
|
-
|
|
604
|
+
throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
|
|
341
605
|
}
|
|
342
606
|
async cancel() {
|
|
343
607
|
const storage = this.#mastra?.getStorage();
|
|
@@ -367,13 +631,60 @@ var InngestRun = class extends Run {
|
|
|
367
631
|
async start(params) {
|
|
368
632
|
return this._start(params);
|
|
369
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Starts the workflow execution without waiting for completion (fire-and-forget).
|
|
636
|
+
* Returns immediately with the runId after sending the event to Inngest.
|
|
637
|
+
* The workflow executes independently in Inngest.
|
|
638
|
+
* Use this when you don't need to wait for the result or want to avoid polling failures.
|
|
639
|
+
*/
|
|
640
|
+
async startAsync(params) {
|
|
641
|
+
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
642
|
+
workflowName: this.workflowId,
|
|
643
|
+
runId: this.runId,
|
|
644
|
+
resourceId: this.resourceId,
|
|
645
|
+
snapshot: {
|
|
646
|
+
runId: this.runId,
|
|
647
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
648
|
+
status: "running",
|
|
649
|
+
value: {},
|
|
650
|
+
context: {},
|
|
651
|
+
activePaths: [],
|
|
652
|
+
suspendedPaths: {},
|
|
653
|
+
activeStepsPath: {},
|
|
654
|
+
resumeLabels: {},
|
|
655
|
+
waitingPaths: {},
|
|
656
|
+
timestamp: Date.now()
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
const inputDataToUse = await this._validateInput(params.inputData);
|
|
660
|
+
const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
|
|
661
|
+
const eventOutput = await this.inngest.send({
|
|
662
|
+
name: `workflow.${this.workflowId}`,
|
|
663
|
+
data: {
|
|
664
|
+
inputData: inputDataToUse,
|
|
665
|
+
initialState: initialStateToUse,
|
|
666
|
+
runId: this.runId,
|
|
667
|
+
resourceId: this.resourceId,
|
|
668
|
+
outputOptions: params.outputOptions,
|
|
669
|
+
tracingOptions: params.tracingOptions,
|
|
670
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
|
|
671
|
+
perStep: params.perStep
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
const eventId = eventOutput.ids[0];
|
|
675
|
+
if (!eventId) {
|
|
676
|
+
throw new Error("Event ID is not set");
|
|
677
|
+
}
|
|
678
|
+
return { runId: this.runId };
|
|
679
|
+
}
|
|
370
680
|
async _start({
|
|
371
681
|
inputData,
|
|
372
682
|
initialState,
|
|
373
683
|
outputOptions,
|
|
374
684
|
tracingOptions,
|
|
375
685
|
format,
|
|
376
|
-
requestContext
|
|
686
|
+
requestContext,
|
|
687
|
+
perStep
|
|
377
688
|
}) {
|
|
378
689
|
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
379
690
|
workflowName: this.workflowId,
|
|
@@ -405,7 +716,8 @@ var InngestRun = class extends Run {
|
|
|
405
716
|
outputOptions,
|
|
406
717
|
tracingOptions,
|
|
407
718
|
format,
|
|
408
|
-
requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {}
|
|
719
|
+
requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
|
|
720
|
+
perStep
|
|
409
721
|
}
|
|
410
722
|
});
|
|
411
723
|
const eventId = eventOutput.ids[0];
|
|
@@ -414,9 +726,7 @@ var InngestRun = class extends Run {
|
|
|
414
726
|
}
|
|
415
727
|
const runOutput = await this.getRunOutput(eventId);
|
|
416
728
|
const result = runOutput?.output?.result;
|
|
417
|
-
|
|
418
|
-
result.error = new Error(result.error);
|
|
419
|
-
}
|
|
729
|
+
this.hydrateFailedResult(result);
|
|
420
730
|
if (result.status !== "suspended") {
|
|
421
731
|
this.cleanup?.();
|
|
422
732
|
}
|
|
@@ -466,7 +776,8 @@ var InngestRun = class extends Run {
|
|
|
466
776
|
resumePayload: resumeDataToUse,
|
|
467
777
|
resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
|
|
468
778
|
},
|
|
469
|
-
requestContext: mergedRequestContext
|
|
779
|
+
requestContext: mergedRequestContext,
|
|
780
|
+
perStep: params.perStep
|
|
470
781
|
}
|
|
471
782
|
});
|
|
472
783
|
const eventId = eventOutput.ids[0];
|
|
@@ -475,9 +786,7 @@ var InngestRun = class extends Run {
|
|
|
475
786
|
}
|
|
476
787
|
const runOutput = await this.getRunOutput(eventId);
|
|
477
788
|
const result = runOutput?.output?.result;
|
|
478
|
-
|
|
479
|
-
result.error = new Error(result.error);
|
|
480
|
-
}
|
|
789
|
+
this.hydrateFailedResult(result);
|
|
481
790
|
return result;
|
|
482
791
|
}
|
|
483
792
|
async timeTravel(params) {
|
|
@@ -558,7 +867,8 @@ var InngestRun = class extends Run {
|
|
|
558
867
|
timeTravel: timeTravelData,
|
|
559
868
|
tracingOptions: params.tracingOptions,
|
|
560
869
|
outputOptions: params.outputOptions,
|
|
561
|
-
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
|
|
870
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
|
|
871
|
+
perStep: params.perStep
|
|
562
872
|
}
|
|
563
873
|
});
|
|
564
874
|
const eventId = eventOutput.ids[0];
|
|
@@ -567,9 +877,7 @@ var InngestRun = class extends Run {
|
|
|
567
877
|
}
|
|
568
878
|
const runOutput = await this.getRunOutput(eventId);
|
|
569
879
|
const result = runOutput?.output?.result;
|
|
570
|
-
|
|
571
|
-
result.error = new Error(result.error);
|
|
572
|
-
}
|
|
880
|
+
this.hydrateFailedResult(result);
|
|
573
881
|
return result;
|
|
574
882
|
}
|
|
575
883
|
watch(cb) {
|
|
@@ -651,7 +959,8 @@ var InngestRun = class extends Run {
|
|
|
651
959
|
tracingOptions,
|
|
652
960
|
closeOnSuspend = true,
|
|
653
961
|
initialState,
|
|
654
|
-
outputOptions
|
|
962
|
+
outputOptions,
|
|
963
|
+
perStep
|
|
655
964
|
} = {}) {
|
|
656
965
|
if (this.closeStreamAction && this.streamOutput) {
|
|
657
966
|
return this.streamOutput;
|
|
@@ -687,7 +996,8 @@ var InngestRun = class extends Run {
|
|
|
687
996
|
initialState,
|
|
688
997
|
tracingOptions,
|
|
689
998
|
outputOptions,
|
|
690
|
-
format: "vnext"
|
|
999
|
+
format: "vnext",
|
|
1000
|
+
perStep
|
|
691
1001
|
});
|
|
692
1002
|
let executionResults;
|
|
693
1003
|
try {
|
|
@@ -730,7 +1040,8 @@ var InngestRun = class extends Run {
|
|
|
730
1040
|
nestedStepsContext,
|
|
731
1041
|
requestContext,
|
|
732
1042
|
tracingOptions,
|
|
733
|
-
outputOptions
|
|
1043
|
+
outputOptions,
|
|
1044
|
+
perStep
|
|
734
1045
|
}) {
|
|
735
1046
|
this.closeStreamAction = async () => {
|
|
736
1047
|
};
|
|
@@ -765,7 +1076,8 @@ var InngestRun = class extends Run {
|
|
|
765
1076
|
initialState,
|
|
766
1077
|
requestContext,
|
|
767
1078
|
tracingOptions,
|
|
768
|
-
outputOptions
|
|
1079
|
+
outputOptions,
|
|
1080
|
+
perStep
|
|
769
1081
|
});
|
|
770
1082
|
self.executionResults = executionResultsPromise;
|
|
771
1083
|
let executionResults;
|
|
@@ -790,6 +1102,18 @@ var InngestRun = class extends Run {
|
|
|
790
1102
|
});
|
|
791
1103
|
return this.streamOutput;
|
|
792
1104
|
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Hydrates errors in a failed workflow result back to proper Error instances.
|
|
1107
|
+
* This ensures error.cause chains and custom properties are preserved.
|
|
1108
|
+
*/
|
|
1109
|
+
hydrateFailedResult(result) {
|
|
1110
|
+
if (result.status === "failed") {
|
|
1111
|
+
result.error = getErrorFromUnknown(result.error, { serializeStack: false });
|
|
1112
|
+
if (result.steps) {
|
|
1113
|
+
hydrateSerializedStepErrors(result.steps);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
793
1117
|
};
|
|
794
1118
|
|
|
795
1119
|
// src/workflow.ts
|
|
@@ -827,6 +1151,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
827
1151
|
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
828
1152
|
}
|
|
829
1153
|
__registerMastra(mastra) {
|
|
1154
|
+
super.__registerMastra(mastra);
|
|
830
1155
|
this.#mastra = mastra;
|
|
831
1156
|
this.executionEngine.__registerMastra(mastra);
|
|
832
1157
|
const updateNested = (step) => {
|
|
@@ -868,7 +1193,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
868
1193
|
workflowStatus: run.workflowRunStatus,
|
|
869
1194
|
stepResults: {}
|
|
870
1195
|
});
|
|
871
|
-
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse,
|
|
1196
|
+
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
|
|
1197
|
+
withNestedWorkflows: false
|
|
1198
|
+
});
|
|
872
1199
|
if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
|
|
873
1200
|
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
874
1201
|
workflowName: this.id,
|
|
@@ -907,34 +1234,13 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
907
1234
|
},
|
|
908
1235
|
{ event: `workflow.${this.id}` },
|
|
909
1236
|
async ({ event, step, attempt, publish }) => {
|
|
910
|
-
let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
|
|
1237
|
+
let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel, perStep } = event.data;
|
|
911
1238
|
if (!runId) {
|
|
912
1239
|
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
913
1240
|
return randomUUID();
|
|
914
1241
|
});
|
|
915
1242
|
}
|
|
916
|
-
const
|
|
917
|
-
emit: async (event2, data) => {
|
|
918
|
-
if (!publish) {
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
try {
|
|
922
|
-
await publish({
|
|
923
|
-
channel: `workflow:${this.id}:${runId}`,
|
|
924
|
-
topic: event2,
|
|
925
|
-
data
|
|
926
|
-
});
|
|
927
|
-
} catch (err) {
|
|
928
|
-
this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
|
|
929
|
-
}
|
|
930
|
-
},
|
|
931
|
-
on: (_event, _callback) => {
|
|
932
|
-
},
|
|
933
|
-
off: (_event, _callback) => {
|
|
934
|
-
},
|
|
935
|
-
once: (_event, _callback) => {
|
|
936
|
-
}
|
|
937
|
-
};
|
|
1243
|
+
const pubsub = new InngestPubSub(this.inngest, this.id, publish);
|
|
938
1244
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
939
1245
|
const result = await engine.execute({
|
|
940
1246
|
workflowId: this.id,
|
|
@@ -944,21 +1250,32 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
944
1250
|
serializedStepGraph: this.serializedStepGraph,
|
|
945
1251
|
input: inputData,
|
|
946
1252
|
initialState,
|
|
947
|
-
|
|
1253
|
+
pubsub,
|
|
948
1254
|
retryConfig: this.retryConfig,
|
|
949
1255
|
requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
|
|
950
1256
|
resume,
|
|
951
1257
|
timeTravel,
|
|
1258
|
+
perStep,
|
|
952
1259
|
format,
|
|
953
1260
|
abortController: new AbortController(),
|
|
954
1261
|
// currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
|
|
955
1262
|
outputOptions,
|
|
956
1263
|
outputWriter: async (chunk) => {
|
|
957
|
-
|
|
958
|
-
|
|
1264
|
+
try {
|
|
1265
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1266
|
+
type: "watch",
|
|
1267
|
+
runId,
|
|
1268
|
+
data: chunk
|
|
1269
|
+
});
|
|
1270
|
+
} catch (err) {
|
|
1271
|
+
this.logger.debug?.("Failed to publish watch event:", err);
|
|
1272
|
+
}
|
|
959
1273
|
}
|
|
960
1274
|
});
|
|
961
1275
|
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
1276
|
+
if (result.status !== "paused") {
|
|
1277
|
+
await engine.invokeLifecycleCallbacksInternal(result);
|
|
1278
|
+
}
|
|
962
1279
|
if (result.status === "failed") {
|
|
963
1280
|
throw new NonRetriableError(`Workflow failed`, {
|
|
964
1281
|
cause: result
|
|
@@ -1043,7 +1360,8 @@ function createStep(params, agentOptions) {
|
|
|
1043
1360
|
outputSchema,
|
|
1044
1361
|
execute: async ({
|
|
1045
1362
|
inputData,
|
|
1046
|
-
|
|
1363
|
+
runId,
|
|
1364
|
+
[PUBSUB_SYMBOL]: pubsub,
|
|
1047
1365
|
[STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
1048
1366
|
requestContext,
|
|
1049
1367
|
tracingContext,
|
|
@@ -1098,22 +1416,24 @@ function createStep(params, agentOptions) {
|
|
|
1098
1416
|
stream = modelOutput.fullStream;
|
|
1099
1417
|
}
|
|
1100
1418
|
if (streamFormat === "legacy") {
|
|
1101
|
-
await
|
|
1102
|
-
type: "
|
|
1103
|
-
|
|
1419
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1420
|
+
type: "watch",
|
|
1421
|
+
runId,
|
|
1422
|
+
data: { type: "tool-call-streaming-start", ...toolData ?? {} }
|
|
1104
1423
|
});
|
|
1105
1424
|
for await (const chunk of stream) {
|
|
1106
1425
|
if (chunk.type === "text-delta") {
|
|
1107
|
-
await
|
|
1108
|
-
type: "
|
|
1109
|
-
|
|
1110
|
-
argsTextDelta: chunk.textDelta
|
|
1426
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1427
|
+
type: "watch",
|
|
1428
|
+
runId,
|
|
1429
|
+
data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
|
|
1111
1430
|
});
|
|
1112
1431
|
}
|
|
1113
1432
|
}
|
|
1114
|
-
await
|
|
1115
|
-
type: "
|
|
1116
|
-
|
|
1433
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1434
|
+
type: "watch",
|
|
1435
|
+
runId,
|
|
1436
|
+
data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
|
|
1117
1437
|
});
|
|
1118
1438
|
} else {
|
|
1119
1439
|
for await (const chunk of stream) {
|
|
@@ -1225,6 +1545,6 @@ function init(inngest) {
|
|
|
1225
1545
|
};
|
|
1226
1546
|
}
|
|
1227
1547
|
|
|
1228
|
-
export { InngestExecutionEngine, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1548
|
+
export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1229
1549
|
//# sourceMappingURL=index.js.map
|
|
1230
1550
|
//# sourceMappingURL=index.js.map
|