@mastra/inngest 1.0.0-beta.6 → 1.0.0-beta.9
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 +186 -0
- package/dist/execution-engine.d.ts +29 -5
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/index.cjs +364 -112
- 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 +359 -108
- 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 +24 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/package.json +4 -5
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,7 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
132
156
|
if (!(params.step instanceof InngestWorkflow)) {
|
|
133
157
|
return null;
|
|
134
158
|
}
|
|
135
|
-
const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData,
|
|
159
|
+
const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData, pubsub, startedAt } = params;
|
|
136
160
|
const isResume = !!resume?.steps?.length;
|
|
137
161
|
let result;
|
|
138
162
|
let runId;
|
|
@@ -221,13 +245,17 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
221
245
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
222
246
|
async () => {
|
|
223
247
|
if (result.status === "failed") {
|
|
224
|
-
await
|
|
225
|
-
type: "
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
248
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
249
|
+
type: "watch",
|
|
250
|
+
runId: executionContext.runId,
|
|
251
|
+
data: {
|
|
252
|
+
type: "workflow-step-result",
|
|
253
|
+
payload: {
|
|
254
|
+
id: step.id,
|
|
255
|
+
status: "failed",
|
|
256
|
+
error: result?.error,
|
|
257
|
+
payload: prevOutput
|
|
258
|
+
}
|
|
231
259
|
}
|
|
232
260
|
});
|
|
233
261
|
return { executionContext, result: { status: "failed", error: result?.error } };
|
|
@@ -239,11 +267,15 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
239
267
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
240
268
|
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
241
269
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
242
|
-
await
|
|
243
|
-
type: "
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
270
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
271
|
+
type: "watch",
|
|
272
|
+
runId: executionContext.runId,
|
|
273
|
+
data: {
|
|
274
|
+
type: "workflow-step-suspended",
|
|
275
|
+
payload: {
|
|
276
|
+
id: step.id,
|
|
277
|
+
status: "suspended"
|
|
278
|
+
}
|
|
247
279
|
}
|
|
248
280
|
});
|
|
249
281
|
return {
|
|
@@ -266,13 +298,17 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
266
298
|
}
|
|
267
299
|
};
|
|
268
300
|
} else if (result.status === "tripwire") {
|
|
269
|
-
await
|
|
270
|
-
type: "
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
301
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
302
|
+
type: "watch",
|
|
303
|
+
runId: executionContext.runId,
|
|
304
|
+
data: {
|
|
305
|
+
type: "workflow-step-result",
|
|
306
|
+
payload: {
|
|
307
|
+
id: step.id,
|
|
308
|
+
status: "tripwire",
|
|
309
|
+
error: result?.tripwire?.reason,
|
|
310
|
+
payload: prevOutput
|
|
311
|
+
}
|
|
276
312
|
}
|
|
277
313
|
});
|
|
278
314
|
return {
|
|
@@ -283,19 +319,27 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
283
319
|
}
|
|
284
320
|
};
|
|
285
321
|
}
|
|
286
|
-
await
|
|
287
|
-
type: "
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
322
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
323
|
+
type: "watch",
|
|
324
|
+
runId: executionContext.runId,
|
|
325
|
+
data: {
|
|
326
|
+
type: "workflow-step-result",
|
|
327
|
+
payload: {
|
|
328
|
+
id: step.id,
|
|
329
|
+
status: "success",
|
|
330
|
+
output: result?.result
|
|
331
|
+
}
|
|
292
332
|
}
|
|
293
333
|
});
|
|
294
|
-
await
|
|
295
|
-
type: "
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
334
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
335
|
+
type: "watch",
|
|
336
|
+
runId: executionContext.runId,
|
|
337
|
+
data: {
|
|
338
|
+
type: "workflow-step-finish",
|
|
339
|
+
payload: {
|
|
340
|
+
id: step.id,
|
|
341
|
+
metadata: {}
|
|
342
|
+
}
|
|
299
343
|
}
|
|
300
344
|
});
|
|
301
345
|
return { executionContext, result: { status: "success", output: result?.result } };
|
|
@@ -312,6 +356,118 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
312
356
|
};
|
|
313
357
|
}
|
|
314
358
|
};
|
|
359
|
+
var InngestPubSub = class extends PubSub {
|
|
360
|
+
inngest;
|
|
361
|
+
workflowId;
|
|
362
|
+
publishFn;
|
|
363
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
364
|
+
constructor(inngest, workflowId, publishFn) {
|
|
365
|
+
super();
|
|
366
|
+
this.inngest = inngest;
|
|
367
|
+
this.workflowId = workflowId;
|
|
368
|
+
this.publishFn = publishFn;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Publish an event to Inngest's realtime system.
|
|
372
|
+
*
|
|
373
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
374
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
375
|
+
*/
|
|
376
|
+
async publish(topic, event) {
|
|
377
|
+
if (!this.publishFn) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
381
|
+
if (!match) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const runId = match[1];
|
|
385
|
+
try {
|
|
386
|
+
await this.publishFn({
|
|
387
|
+
channel: `workflow:${this.workflowId}:${runId}`,
|
|
388
|
+
topic: "watch",
|
|
389
|
+
data: event.data
|
|
390
|
+
});
|
|
391
|
+
} catch (err) {
|
|
392
|
+
console.error("InngestPubSub publish error:", err?.message ?? err);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Subscribe to events from Inngest's realtime system.
|
|
397
|
+
*
|
|
398
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
399
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
400
|
+
*/
|
|
401
|
+
async subscribe(topic, cb) {
|
|
402
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
403
|
+
if (!match || !match[1]) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
const runId = match[1];
|
|
407
|
+
if (this.subscriptions.has(topic)) {
|
|
408
|
+
this.subscriptions.get(topic).callbacks.add(cb);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
const callbacks = /* @__PURE__ */ new Set([cb]);
|
|
412
|
+
const channel = `workflow:${this.workflowId}:${runId}`;
|
|
413
|
+
const streamPromise = subscribe(
|
|
414
|
+
{
|
|
415
|
+
channel,
|
|
416
|
+
topics: ["watch"],
|
|
417
|
+
app: this.inngest
|
|
418
|
+
},
|
|
419
|
+
(message) => {
|
|
420
|
+
const event = {
|
|
421
|
+
id: crypto.randomUUID(),
|
|
422
|
+
type: "watch",
|
|
423
|
+
runId,
|
|
424
|
+
data: message.data,
|
|
425
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
426
|
+
};
|
|
427
|
+
for (const callback of callbacks) {
|
|
428
|
+
callback(event);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
this.subscriptions.set(topic, {
|
|
433
|
+
unsubscribe: () => {
|
|
434
|
+
streamPromise.then((stream) => stream.cancel()).catch((err) => {
|
|
435
|
+
console.error("InngestPubSub unsubscribe error:", err);
|
|
436
|
+
});
|
|
437
|
+
},
|
|
438
|
+
callbacks
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Unsubscribe a callback from a topic.
|
|
443
|
+
* If no callbacks remain, the underlying Inngest subscription is cancelled.
|
|
444
|
+
*/
|
|
445
|
+
async unsubscribe(topic, cb) {
|
|
446
|
+
const sub = this.subscriptions.get(topic);
|
|
447
|
+
if (!sub) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
sub.callbacks.delete(cb);
|
|
451
|
+
if (sub.callbacks.size === 0) {
|
|
452
|
+
sub.unsubscribe();
|
|
453
|
+
this.subscriptions.delete(topic);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Flush any pending operations. No-op for Inngest.
|
|
458
|
+
*/
|
|
459
|
+
async flush() {
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Clean up all subscriptions during graceful shutdown.
|
|
463
|
+
*/
|
|
464
|
+
async close() {
|
|
465
|
+
for (const [, sub] of this.subscriptions) {
|
|
466
|
+
sub.unsubscribe();
|
|
467
|
+
}
|
|
468
|
+
this.subscriptions.clear();
|
|
469
|
+
}
|
|
470
|
+
};
|
|
315
471
|
var InngestRun = class extends Run {
|
|
316
472
|
inngest;
|
|
317
473
|
serializedStepGraph;
|
|
@@ -323,27 +479,77 @@ var InngestRun = class extends Run {
|
|
|
323
479
|
this.#mastra = params.mastra;
|
|
324
480
|
}
|
|
325
481
|
async getRuns(eventId) {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
482
|
+
const maxRetries = 3;
|
|
483
|
+
let lastError = null;
|
|
484
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
485
|
+
try {
|
|
486
|
+
const response = await fetch(
|
|
487
|
+
`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
|
|
488
|
+
{
|
|
489
|
+
headers: {
|
|
490
|
+
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
);
|
|
494
|
+
if (response.status === 429) {
|
|
495
|
+
const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
|
|
496
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (!response.ok) {
|
|
500
|
+
throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
|
|
501
|
+
}
|
|
502
|
+
const text = await response.text();
|
|
503
|
+
if (!text) {
|
|
504
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
const json = JSON.parse(text);
|
|
508
|
+
return json.data;
|
|
509
|
+
} catch (error) {
|
|
510
|
+
lastError = error;
|
|
511
|
+
if (attempt < maxRetries - 1) {
|
|
512
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
|
|
513
|
+
}
|
|
329
514
|
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
return json.data;
|
|
515
|
+
}
|
|
516
|
+
throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
|
|
333
517
|
}
|
|
334
|
-
async getRunOutput(eventId) {
|
|
335
|
-
|
|
518
|
+
async getRunOutput(eventId, maxWaitMs = 3e5) {
|
|
519
|
+
const startTime = Date.now();
|
|
336
520
|
const storage = this.#mastra?.getStorage();
|
|
337
|
-
while (
|
|
338
|
-
|
|
339
|
-
|
|
521
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
522
|
+
let runs;
|
|
523
|
+
try {
|
|
524
|
+
runs = await this.getRuns(eventId);
|
|
525
|
+
} catch (error) {
|
|
526
|
+
if (error instanceof NonRetriableError) {
|
|
527
|
+
throw error;
|
|
528
|
+
}
|
|
529
|
+
throw new NonRetriableError(
|
|
530
|
+
`Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
|
|
534
|
+
return runs[0];
|
|
535
|
+
}
|
|
340
536
|
if (runs?.[0]?.status === "Failed") {
|
|
341
537
|
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
342
538
|
workflowName: this.workflowId,
|
|
343
539
|
runId: this.runId
|
|
344
540
|
});
|
|
541
|
+
if (snapshot?.context) {
|
|
542
|
+
snapshot.context = hydrateSerializedStepErrors(snapshot.context);
|
|
543
|
+
}
|
|
345
544
|
return {
|
|
346
|
-
output: {
|
|
545
|
+
output: {
|
|
546
|
+
result: {
|
|
547
|
+
steps: snapshot?.context,
|
|
548
|
+
status: "failed",
|
|
549
|
+
// Get the original error from NonRetriableError's cause (which contains the workflow result)
|
|
550
|
+
error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
|
|
551
|
+
}
|
|
552
|
+
}
|
|
347
553
|
};
|
|
348
554
|
}
|
|
349
555
|
if (runs?.[0]?.status === "Cancelled") {
|
|
@@ -353,8 +559,9 @@ var InngestRun = class extends Run {
|
|
|
353
559
|
});
|
|
354
560
|
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
355
561
|
}
|
|
562
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
|
|
356
563
|
}
|
|
357
|
-
|
|
564
|
+
throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
|
|
358
565
|
}
|
|
359
566
|
async cancel() {
|
|
360
567
|
const storage = this.#mastra?.getStorage();
|
|
@@ -384,6 +591,51 @@ var InngestRun = class extends Run {
|
|
|
384
591
|
async start(params) {
|
|
385
592
|
return this._start(params);
|
|
386
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Starts the workflow execution without waiting for completion (fire-and-forget).
|
|
596
|
+
* Returns immediately with the runId after sending the event to Inngest.
|
|
597
|
+
* The workflow executes independently in Inngest.
|
|
598
|
+
* Use this when you don't need to wait for the result or want to avoid polling failures.
|
|
599
|
+
*/
|
|
600
|
+
async startAsync(params) {
|
|
601
|
+
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
602
|
+
workflowName: this.workflowId,
|
|
603
|
+
runId: this.runId,
|
|
604
|
+
resourceId: this.resourceId,
|
|
605
|
+
snapshot: {
|
|
606
|
+
runId: this.runId,
|
|
607
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
608
|
+
status: "running",
|
|
609
|
+
value: {},
|
|
610
|
+
context: {},
|
|
611
|
+
activePaths: [],
|
|
612
|
+
suspendedPaths: {},
|
|
613
|
+
activeStepsPath: {},
|
|
614
|
+
resumeLabels: {},
|
|
615
|
+
waitingPaths: {},
|
|
616
|
+
timestamp: Date.now()
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
const inputDataToUse = await this._validateInput(params.inputData);
|
|
620
|
+
const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
|
|
621
|
+
const eventOutput = await this.inngest.send({
|
|
622
|
+
name: `workflow.${this.workflowId}`,
|
|
623
|
+
data: {
|
|
624
|
+
inputData: inputDataToUse,
|
|
625
|
+
initialState: initialStateToUse,
|
|
626
|
+
runId: this.runId,
|
|
627
|
+
resourceId: this.resourceId,
|
|
628
|
+
outputOptions: params.outputOptions,
|
|
629
|
+
tracingOptions: params.tracingOptions,
|
|
630
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
const eventId = eventOutput.ids[0];
|
|
634
|
+
if (!eventId) {
|
|
635
|
+
throw new Error("Event ID is not set");
|
|
636
|
+
}
|
|
637
|
+
return { runId: this.runId };
|
|
638
|
+
}
|
|
387
639
|
async _start({
|
|
388
640
|
inputData,
|
|
389
641
|
initialState,
|
|
@@ -431,9 +683,7 @@ var InngestRun = class extends Run {
|
|
|
431
683
|
}
|
|
432
684
|
const runOutput = await this.getRunOutput(eventId);
|
|
433
685
|
const result = runOutput?.output?.result;
|
|
434
|
-
|
|
435
|
-
result.error = new Error(result.error);
|
|
436
|
-
}
|
|
686
|
+
this.hydrateFailedResult(result);
|
|
437
687
|
if (result.status !== "suspended") {
|
|
438
688
|
this.cleanup?.();
|
|
439
689
|
}
|
|
@@ -492,9 +742,7 @@ var InngestRun = class extends Run {
|
|
|
492
742
|
}
|
|
493
743
|
const runOutput = await this.getRunOutput(eventId);
|
|
494
744
|
const result = runOutput?.output?.result;
|
|
495
|
-
|
|
496
|
-
result.error = new Error(result.error);
|
|
497
|
-
}
|
|
745
|
+
this.hydrateFailedResult(result);
|
|
498
746
|
return result;
|
|
499
747
|
}
|
|
500
748
|
async timeTravel(params) {
|
|
@@ -584,9 +832,7 @@ var InngestRun = class extends Run {
|
|
|
584
832
|
}
|
|
585
833
|
const runOutput = await this.getRunOutput(eventId);
|
|
586
834
|
const result = runOutput?.output?.result;
|
|
587
|
-
|
|
588
|
-
result.error = new Error(result.error);
|
|
589
|
-
}
|
|
835
|
+
this.hydrateFailedResult(result);
|
|
590
836
|
return result;
|
|
591
837
|
}
|
|
592
838
|
watch(cb) {
|
|
@@ -807,6 +1053,18 @@ var InngestRun = class extends Run {
|
|
|
807
1053
|
});
|
|
808
1054
|
return this.streamOutput;
|
|
809
1055
|
}
|
|
1056
|
+
/**
|
|
1057
|
+
* Hydrates errors in a failed workflow result back to proper Error instances.
|
|
1058
|
+
* This ensures error.cause chains and custom properties are preserved.
|
|
1059
|
+
*/
|
|
1060
|
+
hydrateFailedResult(result) {
|
|
1061
|
+
if (result.status === "failed") {
|
|
1062
|
+
result.error = getErrorFromUnknown(result.error, { serializeStack: false });
|
|
1063
|
+
if (result.steps) {
|
|
1064
|
+
hydrateSerializedStepErrors(result.steps);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
810
1068
|
};
|
|
811
1069
|
|
|
812
1070
|
// src/workflow.ts
|
|
@@ -844,6 +1102,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
844
1102
|
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
845
1103
|
}
|
|
846
1104
|
__registerMastra(mastra) {
|
|
1105
|
+
super.__registerMastra(mastra);
|
|
847
1106
|
this.#mastra = mastra;
|
|
848
1107
|
this.executionEngine.__registerMastra(mastra);
|
|
849
1108
|
const updateNested = (step) => {
|
|
@@ -885,7 +1144,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
885
1144
|
workflowStatus: run.workflowRunStatus,
|
|
886
1145
|
stepResults: {}
|
|
887
1146
|
});
|
|
888
|
-
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse,
|
|
1147
|
+
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
|
|
1148
|
+
withNestedWorkflows: false
|
|
1149
|
+
});
|
|
889
1150
|
if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
|
|
890
1151
|
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
891
1152
|
workflowName: this.id,
|
|
@@ -930,28 +1191,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
930
1191
|
return randomUUID();
|
|
931
1192
|
});
|
|
932
1193
|
}
|
|
933
|
-
const
|
|
934
|
-
emit: async (event2, data) => {
|
|
935
|
-
if (!publish) {
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
try {
|
|
939
|
-
await publish({
|
|
940
|
-
channel: `workflow:${this.id}:${runId}`,
|
|
941
|
-
topic: event2,
|
|
942
|
-
data
|
|
943
|
-
});
|
|
944
|
-
} catch (err) {
|
|
945
|
-
this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
|
|
946
|
-
}
|
|
947
|
-
},
|
|
948
|
-
on: (_event, _callback) => {
|
|
949
|
-
},
|
|
950
|
-
off: (_event, _callback) => {
|
|
951
|
-
},
|
|
952
|
-
once: (_event, _callback) => {
|
|
953
|
-
}
|
|
954
|
-
};
|
|
1194
|
+
const pubsub = new InngestPubSub(this.inngest, this.id, publish);
|
|
955
1195
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
956
1196
|
const result = await engine.execute({
|
|
957
1197
|
workflowId: this.id,
|
|
@@ -961,7 +1201,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
961
1201
|
serializedStepGraph: this.serializedStepGraph,
|
|
962
1202
|
input: inputData,
|
|
963
1203
|
initialState,
|
|
964
|
-
|
|
1204
|
+
pubsub,
|
|
965
1205
|
retryConfig: this.retryConfig,
|
|
966
1206
|
requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
|
|
967
1207
|
resume,
|
|
@@ -971,11 +1211,19 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
971
1211
|
// currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
|
|
972
1212
|
outputOptions,
|
|
973
1213
|
outputWriter: async (chunk) => {
|
|
974
|
-
|
|
975
|
-
|
|
1214
|
+
try {
|
|
1215
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1216
|
+
type: "watch",
|
|
1217
|
+
runId,
|
|
1218
|
+
data: chunk
|
|
1219
|
+
});
|
|
1220
|
+
} catch (err) {
|
|
1221
|
+
this.logger.debug?.("Failed to publish watch event:", err);
|
|
1222
|
+
}
|
|
976
1223
|
}
|
|
977
1224
|
});
|
|
978
1225
|
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
1226
|
+
await engine.invokeLifecycleCallbacksInternal(result);
|
|
979
1227
|
if (result.status === "failed") {
|
|
980
1228
|
throw new NonRetriableError(`Workflow failed`, {
|
|
981
1229
|
cause: result
|
|
@@ -1060,7 +1308,8 @@ function createStep(params, agentOptions) {
|
|
|
1060
1308
|
outputSchema,
|
|
1061
1309
|
execute: async ({
|
|
1062
1310
|
inputData,
|
|
1063
|
-
|
|
1311
|
+
runId,
|
|
1312
|
+
[PUBSUB_SYMBOL]: pubsub,
|
|
1064
1313
|
[STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
1065
1314
|
requestContext,
|
|
1066
1315
|
tracingContext,
|
|
@@ -1115,22 +1364,24 @@ function createStep(params, agentOptions) {
|
|
|
1115
1364
|
stream = modelOutput.fullStream;
|
|
1116
1365
|
}
|
|
1117
1366
|
if (streamFormat === "legacy") {
|
|
1118
|
-
await
|
|
1119
|
-
type: "
|
|
1120
|
-
|
|
1367
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1368
|
+
type: "watch",
|
|
1369
|
+
runId,
|
|
1370
|
+
data: { type: "tool-call-streaming-start", ...toolData ?? {} }
|
|
1121
1371
|
});
|
|
1122
1372
|
for await (const chunk of stream) {
|
|
1123
1373
|
if (chunk.type === "text-delta") {
|
|
1124
|
-
await
|
|
1125
|
-
type: "
|
|
1126
|
-
|
|
1127
|
-
argsTextDelta: chunk.textDelta
|
|
1374
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1375
|
+
type: "watch",
|
|
1376
|
+
runId,
|
|
1377
|
+
data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
|
|
1128
1378
|
});
|
|
1129
1379
|
}
|
|
1130
1380
|
}
|
|
1131
|
-
await
|
|
1132
|
-
type: "
|
|
1133
|
-
|
|
1381
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1382
|
+
type: "watch",
|
|
1383
|
+
runId,
|
|
1384
|
+
data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
|
|
1134
1385
|
});
|
|
1135
1386
|
} else {
|
|
1136
1387
|
for await (const chunk of stream) {
|
|
@@ -1242,6 +1493,6 @@ function init(inngest) {
|
|
|
1242
1493
|
};
|
|
1243
1494
|
}
|
|
1244
1495
|
|
|
1245
|
-
export { InngestExecutionEngine, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1496
|
+
export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1246
1497
|
//# sourceMappingURL=index.js.map
|
|
1247
1498
|
//# sourceMappingURL=index.js.map
|