@mastra/inngest 0.0.0-fix-request-context-as-query-key-20251209130646 → 0.0.0-fix-zod4-schema-validation-20251212180638
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 +180 -3
- package/dist/execution-engine.d.ts +8 -5
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/index.cjs +372 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +367 -105
- 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 +7 -7
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
|
-
import { ReadableStream, WritableStream } from 'stream/web';
|
|
7
6
|
import { RequestContext } from '@mastra/core/di';
|
|
8
7
|
import { RetryAfterError, NonRetriableError } from 'inngest';
|
|
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
|
});
|
|
@@ -132,7 +144,7 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
132
144
|
if (!(params.step instanceof InngestWorkflow)) {
|
|
133
145
|
return null;
|
|
134
146
|
}
|
|
135
|
-
const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData,
|
|
147
|
+
const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData, pubsub, startedAt } = params;
|
|
136
148
|
const isResume = !!resume?.steps?.length;
|
|
137
149
|
let result;
|
|
138
150
|
let runId;
|
|
@@ -221,13 +233,17 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
221
233
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
222
234
|
async () => {
|
|
223
235
|
if (result.status === "failed") {
|
|
224
|
-
await
|
|
225
|
-
type: "
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
236
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
237
|
+
type: "watch",
|
|
238
|
+
runId: executionContext.runId,
|
|
239
|
+
data: {
|
|
240
|
+
type: "workflow-step-result",
|
|
241
|
+
payload: {
|
|
242
|
+
id: step.id,
|
|
243
|
+
status: "failed",
|
|
244
|
+
error: result?.error,
|
|
245
|
+
payload: prevOutput
|
|
246
|
+
}
|
|
231
247
|
}
|
|
232
248
|
});
|
|
233
249
|
return { executionContext, result: { status: "failed", error: result?.error } };
|
|
@@ -239,11 +255,15 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
239
255
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
240
256
|
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
241
257
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
242
|
-
await
|
|
243
|
-
type: "
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
258
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
259
|
+
type: "watch",
|
|
260
|
+
runId: executionContext.runId,
|
|
261
|
+
data: {
|
|
262
|
+
type: "workflow-step-suspended",
|
|
263
|
+
payload: {
|
|
264
|
+
id: step.id,
|
|
265
|
+
status: "suspended"
|
|
266
|
+
}
|
|
247
267
|
}
|
|
248
268
|
});
|
|
249
269
|
return {
|
|
@@ -265,20 +285,49 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
265
285
|
payload: {}
|
|
266
286
|
}
|
|
267
287
|
};
|
|
288
|
+
} else if (result.status === "tripwire") {
|
|
289
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
290
|
+
type: "watch",
|
|
291
|
+
runId: executionContext.runId,
|
|
292
|
+
data: {
|
|
293
|
+
type: "workflow-step-result",
|
|
294
|
+
payload: {
|
|
295
|
+
id: step.id,
|
|
296
|
+
status: "tripwire",
|
|
297
|
+
error: result?.tripwire?.reason,
|
|
298
|
+
payload: prevOutput
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
return {
|
|
303
|
+
executionContext,
|
|
304
|
+
result: {
|
|
305
|
+
status: "tripwire",
|
|
306
|
+
tripwire: result?.tripwire
|
|
307
|
+
}
|
|
308
|
+
};
|
|
268
309
|
}
|
|
269
|
-
await
|
|
270
|
-
type: "
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
310
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
311
|
+
type: "watch",
|
|
312
|
+
runId: executionContext.runId,
|
|
313
|
+
data: {
|
|
314
|
+
type: "workflow-step-result",
|
|
315
|
+
payload: {
|
|
316
|
+
id: step.id,
|
|
317
|
+
status: "success",
|
|
318
|
+
output: result?.result
|
|
319
|
+
}
|
|
275
320
|
}
|
|
276
321
|
});
|
|
277
|
-
await
|
|
278
|
-
type: "
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
322
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
323
|
+
type: "watch",
|
|
324
|
+
runId: executionContext.runId,
|
|
325
|
+
data: {
|
|
326
|
+
type: "workflow-step-finish",
|
|
327
|
+
payload: {
|
|
328
|
+
id: step.id,
|
|
329
|
+
metadata: {}
|
|
330
|
+
}
|
|
282
331
|
}
|
|
283
332
|
});
|
|
284
333
|
return { executionContext, result: { status: "success", output: result?.result } };
|
|
@@ -295,6 +344,118 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
295
344
|
};
|
|
296
345
|
}
|
|
297
346
|
};
|
|
347
|
+
var InngestPubSub = class extends PubSub {
|
|
348
|
+
inngest;
|
|
349
|
+
workflowId;
|
|
350
|
+
publishFn;
|
|
351
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
352
|
+
constructor(inngest, workflowId, publishFn) {
|
|
353
|
+
super();
|
|
354
|
+
this.inngest = inngest;
|
|
355
|
+
this.workflowId = workflowId;
|
|
356
|
+
this.publishFn = publishFn;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Publish an event to Inngest's realtime system.
|
|
360
|
+
*
|
|
361
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
362
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
363
|
+
*/
|
|
364
|
+
async publish(topic, event) {
|
|
365
|
+
if (!this.publishFn) {
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
369
|
+
if (!match) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const runId = match[1];
|
|
373
|
+
try {
|
|
374
|
+
await this.publishFn({
|
|
375
|
+
channel: `workflow:${this.workflowId}:${runId}`,
|
|
376
|
+
topic: "watch",
|
|
377
|
+
data: event.data
|
|
378
|
+
});
|
|
379
|
+
} catch (err) {
|
|
380
|
+
console.error("InngestPubSub publish error:", err?.message ?? err);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Subscribe to events from Inngest's realtime system.
|
|
385
|
+
*
|
|
386
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
387
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
388
|
+
*/
|
|
389
|
+
async subscribe(topic, cb) {
|
|
390
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
391
|
+
if (!match || !match[1]) {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const runId = match[1];
|
|
395
|
+
if (this.subscriptions.has(topic)) {
|
|
396
|
+
this.subscriptions.get(topic).callbacks.add(cb);
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
const callbacks = /* @__PURE__ */ new Set([cb]);
|
|
400
|
+
const channel = `workflow:${this.workflowId}:${runId}`;
|
|
401
|
+
const streamPromise = subscribe(
|
|
402
|
+
{
|
|
403
|
+
channel,
|
|
404
|
+
topics: ["watch"],
|
|
405
|
+
app: this.inngest
|
|
406
|
+
},
|
|
407
|
+
(message) => {
|
|
408
|
+
const event = {
|
|
409
|
+
id: crypto.randomUUID(),
|
|
410
|
+
type: "watch",
|
|
411
|
+
runId,
|
|
412
|
+
data: message.data,
|
|
413
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
414
|
+
};
|
|
415
|
+
for (const callback of callbacks) {
|
|
416
|
+
callback(event);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
this.subscriptions.set(topic, {
|
|
421
|
+
unsubscribe: () => {
|
|
422
|
+
streamPromise.then((stream) => stream.cancel()).catch((err) => {
|
|
423
|
+
console.error("InngestPubSub unsubscribe error:", err);
|
|
424
|
+
});
|
|
425
|
+
},
|
|
426
|
+
callbacks
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Unsubscribe a callback from a topic.
|
|
431
|
+
* If no callbacks remain, the underlying Inngest subscription is cancelled.
|
|
432
|
+
*/
|
|
433
|
+
async unsubscribe(topic, cb) {
|
|
434
|
+
const sub = this.subscriptions.get(topic);
|
|
435
|
+
if (!sub) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
sub.callbacks.delete(cb);
|
|
439
|
+
if (sub.callbacks.size === 0) {
|
|
440
|
+
sub.unsubscribe();
|
|
441
|
+
this.subscriptions.delete(topic);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Flush any pending operations. No-op for Inngest.
|
|
446
|
+
*/
|
|
447
|
+
async flush() {
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Clean up all subscriptions during graceful shutdown.
|
|
451
|
+
*/
|
|
452
|
+
async close() {
|
|
453
|
+
for (const [, sub] of this.subscriptions) {
|
|
454
|
+
sub.unsubscribe();
|
|
455
|
+
}
|
|
456
|
+
this.subscriptions.clear();
|
|
457
|
+
}
|
|
458
|
+
};
|
|
298
459
|
var InngestRun = class extends Run {
|
|
299
460
|
inngest;
|
|
300
461
|
serializedStepGraph;
|
|
@@ -306,27 +467,77 @@ var InngestRun = class extends Run {
|
|
|
306
467
|
this.#mastra = params.mastra;
|
|
307
468
|
}
|
|
308
469
|
async getRuns(eventId) {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
470
|
+
const maxRetries = 3;
|
|
471
|
+
let lastError = null;
|
|
472
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
473
|
+
try {
|
|
474
|
+
const response = await fetch(
|
|
475
|
+
`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
|
|
476
|
+
{
|
|
477
|
+
headers: {
|
|
478
|
+
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
if (response.status === 429) {
|
|
483
|
+
const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
|
|
484
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
if (!response.ok) {
|
|
488
|
+
throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
|
|
489
|
+
}
|
|
490
|
+
const text = await response.text();
|
|
491
|
+
if (!text) {
|
|
492
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
const json = JSON.parse(text);
|
|
496
|
+
return json.data;
|
|
497
|
+
} catch (error) {
|
|
498
|
+
lastError = error;
|
|
499
|
+
if (attempt < maxRetries - 1) {
|
|
500
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
|
|
501
|
+
}
|
|
312
502
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return json.data;
|
|
503
|
+
}
|
|
504
|
+
throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
|
|
316
505
|
}
|
|
317
|
-
async getRunOutput(eventId) {
|
|
318
|
-
|
|
506
|
+
async getRunOutput(eventId, maxWaitMs = 3e5) {
|
|
507
|
+
const startTime = Date.now();
|
|
319
508
|
const storage = this.#mastra?.getStorage();
|
|
320
|
-
while (
|
|
321
|
-
|
|
322
|
-
|
|
509
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
510
|
+
let runs;
|
|
511
|
+
try {
|
|
512
|
+
runs = await this.getRuns(eventId);
|
|
513
|
+
} catch (error) {
|
|
514
|
+
if (error instanceof NonRetriableError) {
|
|
515
|
+
throw error;
|
|
516
|
+
}
|
|
517
|
+
throw new NonRetriableError(
|
|
518
|
+
`Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
|
|
522
|
+
return runs[0];
|
|
523
|
+
}
|
|
323
524
|
if (runs?.[0]?.status === "Failed") {
|
|
324
525
|
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
325
526
|
workflowName: this.workflowId,
|
|
326
527
|
runId: this.runId
|
|
327
528
|
});
|
|
529
|
+
if (snapshot?.context) {
|
|
530
|
+
snapshot.context = hydrateSerializedStepErrors(snapshot.context);
|
|
531
|
+
}
|
|
328
532
|
return {
|
|
329
|
-
output: {
|
|
533
|
+
output: {
|
|
534
|
+
result: {
|
|
535
|
+
steps: snapshot?.context,
|
|
536
|
+
status: "failed",
|
|
537
|
+
// Get the original error from NonRetriableError's cause (which contains the workflow result)
|
|
538
|
+
error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
|
|
539
|
+
}
|
|
540
|
+
}
|
|
330
541
|
};
|
|
331
542
|
}
|
|
332
543
|
if (runs?.[0]?.status === "Cancelled") {
|
|
@@ -336,8 +547,9 @@ var InngestRun = class extends Run {
|
|
|
336
547
|
});
|
|
337
548
|
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
338
549
|
}
|
|
550
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
|
|
339
551
|
}
|
|
340
|
-
|
|
552
|
+
throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
|
|
341
553
|
}
|
|
342
554
|
async cancel() {
|
|
343
555
|
const storage = this.#mastra?.getStorage();
|
|
@@ -367,6 +579,51 @@ var InngestRun = class extends Run {
|
|
|
367
579
|
async start(params) {
|
|
368
580
|
return this._start(params);
|
|
369
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Starts the workflow execution without waiting for completion (fire-and-forget).
|
|
584
|
+
* Returns immediately with the runId after sending the event to Inngest.
|
|
585
|
+
* The workflow executes independently in Inngest.
|
|
586
|
+
* Use this when you don't need to wait for the result or want to avoid polling failures.
|
|
587
|
+
*/
|
|
588
|
+
async startAsync(params) {
|
|
589
|
+
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
590
|
+
workflowName: this.workflowId,
|
|
591
|
+
runId: this.runId,
|
|
592
|
+
resourceId: this.resourceId,
|
|
593
|
+
snapshot: {
|
|
594
|
+
runId: this.runId,
|
|
595
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
596
|
+
status: "running",
|
|
597
|
+
value: {},
|
|
598
|
+
context: {},
|
|
599
|
+
activePaths: [],
|
|
600
|
+
suspendedPaths: {},
|
|
601
|
+
activeStepsPath: {},
|
|
602
|
+
resumeLabels: {},
|
|
603
|
+
waitingPaths: {},
|
|
604
|
+
timestamp: Date.now()
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
const inputDataToUse = await this._validateInput(params.inputData);
|
|
608
|
+
const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
|
|
609
|
+
const eventOutput = await this.inngest.send({
|
|
610
|
+
name: `workflow.${this.workflowId}`,
|
|
611
|
+
data: {
|
|
612
|
+
inputData: inputDataToUse,
|
|
613
|
+
initialState: initialStateToUse,
|
|
614
|
+
runId: this.runId,
|
|
615
|
+
resourceId: this.resourceId,
|
|
616
|
+
outputOptions: params.outputOptions,
|
|
617
|
+
tracingOptions: params.tracingOptions,
|
|
618
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
const eventId = eventOutput.ids[0];
|
|
622
|
+
if (!eventId) {
|
|
623
|
+
throw new Error("Event ID is not set");
|
|
624
|
+
}
|
|
625
|
+
return { runId: this.runId };
|
|
626
|
+
}
|
|
370
627
|
async _start({
|
|
371
628
|
inputData,
|
|
372
629
|
initialState,
|
|
@@ -414,9 +671,7 @@ var InngestRun = class extends Run {
|
|
|
414
671
|
}
|
|
415
672
|
const runOutput = await this.getRunOutput(eventId);
|
|
416
673
|
const result = runOutput?.output?.result;
|
|
417
|
-
|
|
418
|
-
result.error = new Error(result.error);
|
|
419
|
-
}
|
|
674
|
+
this.hydrateFailedResult(result);
|
|
420
675
|
if (result.status !== "suspended") {
|
|
421
676
|
this.cleanup?.();
|
|
422
677
|
}
|
|
@@ -475,9 +730,7 @@ var InngestRun = class extends Run {
|
|
|
475
730
|
}
|
|
476
731
|
const runOutput = await this.getRunOutput(eventId);
|
|
477
732
|
const result = runOutput?.output?.result;
|
|
478
|
-
|
|
479
|
-
result.error = new Error(result.error);
|
|
480
|
-
}
|
|
733
|
+
this.hydrateFailedResult(result);
|
|
481
734
|
return result;
|
|
482
735
|
}
|
|
483
736
|
async timeTravel(params) {
|
|
@@ -567,9 +820,7 @@ var InngestRun = class extends Run {
|
|
|
567
820
|
}
|
|
568
821
|
const runOutput = await this.getRunOutput(eventId);
|
|
569
822
|
const result = runOutput?.output?.result;
|
|
570
|
-
|
|
571
|
-
result.error = new Error(result.error);
|
|
572
|
-
}
|
|
823
|
+
this.hydrateFailedResult(result);
|
|
573
824
|
return result;
|
|
574
825
|
}
|
|
575
826
|
watch(cb) {
|
|
@@ -790,6 +1041,18 @@ var InngestRun = class extends Run {
|
|
|
790
1041
|
});
|
|
791
1042
|
return this.streamOutput;
|
|
792
1043
|
}
|
|
1044
|
+
/**
|
|
1045
|
+
* Hydrates errors in a failed workflow result back to proper Error instances.
|
|
1046
|
+
* This ensures error.cause chains and custom properties are preserved.
|
|
1047
|
+
*/
|
|
1048
|
+
hydrateFailedResult(result) {
|
|
1049
|
+
if (result.status === "failed") {
|
|
1050
|
+
result.error = getErrorFromUnknown(result.error, { serializeStack: false });
|
|
1051
|
+
if (result.steps) {
|
|
1052
|
+
hydrateSerializedStepErrors(result.steps);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
793
1056
|
};
|
|
794
1057
|
|
|
795
1058
|
// src/workflow.ts
|
|
@@ -827,6 +1090,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
827
1090
|
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
828
1091
|
}
|
|
829
1092
|
__registerMastra(mastra) {
|
|
1093
|
+
super.__registerMastra(mastra);
|
|
830
1094
|
this.#mastra = mastra;
|
|
831
1095
|
this.executionEngine.__registerMastra(mastra);
|
|
832
1096
|
const updateNested = (step) => {
|
|
@@ -913,28 +1177,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
913
1177
|
return randomUUID();
|
|
914
1178
|
});
|
|
915
1179
|
}
|
|
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
|
-
};
|
|
1180
|
+
const pubsub = new InngestPubSub(this.inngest, this.id, publish);
|
|
938
1181
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
939
1182
|
const result = await engine.execute({
|
|
940
1183
|
workflowId: this.id,
|
|
@@ -944,7 +1187,7 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
944
1187
|
serializedStepGraph: this.serializedStepGraph,
|
|
945
1188
|
input: inputData,
|
|
946
1189
|
initialState,
|
|
947
|
-
|
|
1190
|
+
pubsub,
|
|
948
1191
|
retryConfig: this.retryConfig,
|
|
949
1192
|
requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
|
|
950
1193
|
resume,
|
|
@@ -953,12 +1196,17 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
953
1196
|
abortController: new AbortController(),
|
|
954
1197
|
// currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
|
|
955
1198
|
outputOptions,
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1199
|
+
outputWriter: async (chunk) => {
|
|
1200
|
+
try {
|
|
1201
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1202
|
+
type: "watch",
|
|
1203
|
+
runId,
|
|
1204
|
+
data: chunk
|
|
959
1205
|
});
|
|
1206
|
+
} catch (err) {
|
|
1207
|
+
this.logger.debug?.("Failed to publish watch event:", err);
|
|
960
1208
|
}
|
|
961
|
-
}
|
|
1209
|
+
}
|
|
962
1210
|
});
|
|
963
1211
|
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
964
1212
|
if (result.status === "failed") {
|
|
@@ -1033,6 +1281,7 @@ function createStep(params, agentOptions) {
|
|
|
1033
1281
|
return params;
|
|
1034
1282
|
}
|
|
1035
1283
|
if (isAgent(params)) {
|
|
1284
|
+
const outputSchema = agentOptions?.structuredOutput?.schema ?? z.object({ text: z.string() });
|
|
1036
1285
|
return {
|
|
1037
1286
|
id: params.name,
|
|
1038
1287
|
description: params.getDescription(),
|
|
@@ -1041,12 +1290,11 @@ function createStep(params, agentOptions) {
|
|
|
1041
1290
|
// resourceId: z.string().optional(),
|
|
1042
1291
|
// threadId: z.string().optional(),
|
|
1043
1292
|
}),
|
|
1044
|
-
outputSchema
|
|
1045
|
-
text: z.string()
|
|
1046
|
-
}),
|
|
1293
|
+
outputSchema,
|
|
1047
1294
|
execute: async ({
|
|
1048
1295
|
inputData,
|
|
1049
|
-
|
|
1296
|
+
runId,
|
|
1297
|
+
[PUBSUB_SYMBOL]: pubsub,
|
|
1050
1298
|
[STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
1051
1299
|
requestContext,
|
|
1052
1300
|
tracingContext,
|
|
@@ -1059,6 +1307,7 @@ function createStep(params, agentOptions) {
|
|
|
1059
1307
|
streamPromise.resolve = resolve;
|
|
1060
1308
|
streamPromise.reject = reject;
|
|
1061
1309
|
});
|
|
1310
|
+
let structuredResult = null;
|
|
1062
1311
|
const toolData = {
|
|
1063
1312
|
name: params.name,
|
|
1064
1313
|
args: inputData
|
|
@@ -1072,6 +1321,10 @@ function createStep(params, agentOptions) {
|
|
|
1072
1321
|
requestContext,
|
|
1073
1322
|
tracingContext,
|
|
1074
1323
|
onFinish: (result) => {
|
|
1324
|
+
const resultWithObject = result;
|
|
1325
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1326
|
+
structuredResult = resultWithObject.object;
|
|
1327
|
+
}
|
|
1075
1328
|
streamPromise.resolve(result.text);
|
|
1076
1329
|
void agentOptions?.onFinish?.(result);
|
|
1077
1330
|
},
|
|
@@ -1084,6 +1337,10 @@ function createStep(params, agentOptions) {
|
|
|
1084
1337
|
requestContext,
|
|
1085
1338
|
tracingContext,
|
|
1086
1339
|
onFinish: (result) => {
|
|
1340
|
+
const resultWithObject = result;
|
|
1341
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1342
|
+
structuredResult = resultWithObject.object;
|
|
1343
|
+
}
|
|
1087
1344
|
streamPromise.resolve(result.text);
|
|
1088
1345
|
void agentOptions?.onFinish?.(result);
|
|
1089
1346
|
},
|
|
@@ -1092,22 +1349,24 @@ function createStep(params, agentOptions) {
|
|
|
1092
1349
|
stream = modelOutput.fullStream;
|
|
1093
1350
|
}
|
|
1094
1351
|
if (streamFormat === "legacy") {
|
|
1095
|
-
await
|
|
1096
|
-
type: "
|
|
1097
|
-
|
|
1352
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1353
|
+
type: "watch",
|
|
1354
|
+
runId,
|
|
1355
|
+
data: { type: "tool-call-streaming-start", ...toolData ?? {} }
|
|
1098
1356
|
});
|
|
1099
1357
|
for await (const chunk of stream) {
|
|
1100
1358
|
if (chunk.type === "text-delta") {
|
|
1101
|
-
await
|
|
1102
|
-
type: "
|
|
1103
|
-
|
|
1104
|
-
argsTextDelta: chunk.textDelta
|
|
1359
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1360
|
+
type: "watch",
|
|
1361
|
+
runId,
|
|
1362
|
+
data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
|
|
1105
1363
|
});
|
|
1106
1364
|
}
|
|
1107
1365
|
}
|
|
1108
|
-
await
|
|
1109
|
-
type: "
|
|
1110
|
-
|
|
1366
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1367
|
+
type: "watch",
|
|
1368
|
+
runId,
|
|
1369
|
+
data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
|
|
1111
1370
|
});
|
|
1112
1371
|
} else {
|
|
1113
1372
|
for await (const chunk of stream) {
|
|
@@ -1117,6 +1376,9 @@ function createStep(params, agentOptions) {
|
|
|
1117
1376
|
if (abortSignal.aborted) {
|
|
1118
1377
|
return abort();
|
|
1119
1378
|
}
|
|
1379
|
+
if (structuredResult !== null) {
|
|
1380
|
+
return structuredResult;
|
|
1381
|
+
}
|
|
1120
1382
|
return {
|
|
1121
1383
|
text: await streamPromise.promise
|
|
1122
1384
|
};
|
|
@@ -1216,6 +1478,6 @@ function init(inngest) {
|
|
|
1216
1478
|
};
|
|
1217
1479
|
}
|
|
1218
1480
|
|
|
1219
|
-
export { InngestExecutionEngine, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1481
|
+
export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1220
1482
|
//# sourceMappingURL=index.js.map
|
|
1221
1483
|
//# sourceMappingURL=index.js.map
|