@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.cjs
CHANGED
|
@@ -4,11 +4,13 @@ var tools = require('@mastra/core/tools');
|
|
|
4
4
|
var workflows = require('@mastra/core/workflows');
|
|
5
5
|
var _constants = require('@mastra/core/workflows/_constants');
|
|
6
6
|
var zod = require('zod');
|
|
7
|
-
var crypto = require('crypto');
|
|
8
|
-
var web = require('stream/web');
|
|
7
|
+
var crypto$1 = require('crypto');
|
|
9
8
|
var di = require('@mastra/core/di');
|
|
10
9
|
var inngest = require('inngest');
|
|
10
|
+
var error = require('@mastra/core/error');
|
|
11
11
|
var realtime = require('@inngest/realtime');
|
|
12
|
+
var events = require('@mastra/core/events');
|
|
13
|
+
var web = require('stream/web');
|
|
12
14
|
var stream = require('@mastra/core/stream');
|
|
13
15
|
var hono = require('inngest/hono');
|
|
14
16
|
|
|
@@ -25,17 +27,18 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
25
27
|
// Hook Overrides
|
|
26
28
|
// =============================================================================
|
|
27
29
|
/**
|
|
28
|
-
* Format errors
|
|
30
|
+
* Format errors while preserving Error instances and their custom properties.
|
|
31
|
+
* Uses getErrorFromUnknown to ensure all error properties are preserved.
|
|
29
32
|
*/
|
|
30
|
-
formatResultError(error, lastOutput) {
|
|
31
|
-
if (error instanceof Error) {
|
|
32
|
-
return error.stack ?? error.message;
|
|
33
|
-
}
|
|
33
|
+
formatResultError(error$1, lastOutput) {
|
|
34
34
|
const outputError = lastOutput?.error;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const errorSource = error$1 || outputError;
|
|
36
|
+
const errorInstance = error.getErrorFromUnknown(errorSource, {
|
|
37
|
+
serializeStack: true,
|
|
38
|
+
// Include stack in JSON for better debugging in Inngest
|
|
39
|
+
fallbackMessage: "Unknown workflow error"
|
|
40
|
+
});
|
|
41
|
+
return errorInstance.toJSON();
|
|
39
42
|
}
|
|
40
43
|
/**
|
|
41
44
|
* Detect InngestWorkflow instances for special nested workflow handling
|
|
@@ -67,18 +70,24 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
67
70
|
error: e,
|
|
68
71
|
attributes: { status: "failed" }
|
|
69
72
|
});
|
|
73
|
+
if (cause.error && !(cause.error instanceof Error)) {
|
|
74
|
+
cause.error = error.getErrorFromUnknown(cause.error, { serializeStack: false });
|
|
75
|
+
}
|
|
70
76
|
return { ok: false, error: cause };
|
|
71
77
|
}
|
|
72
|
-
const
|
|
78
|
+
const errorInstance = error.getErrorFromUnknown(e, {
|
|
79
|
+
serializeStack: false,
|
|
80
|
+
fallbackMessage: "Unknown step execution error"
|
|
81
|
+
});
|
|
73
82
|
params.stepSpan?.error({
|
|
74
|
-
error:
|
|
83
|
+
error: errorInstance,
|
|
75
84
|
attributes: { status: "failed" }
|
|
76
85
|
});
|
|
77
86
|
return {
|
|
78
87
|
ok: false,
|
|
79
88
|
error: {
|
|
80
89
|
status: "failed",
|
|
81
|
-
error:
|
|
90
|
+
error: errorInstance,
|
|
82
91
|
endedAt: Date.now()
|
|
83
92
|
}
|
|
84
93
|
};
|
|
@@ -107,11 +116,14 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
107
116
|
return await operationFn();
|
|
108
117
|
} catch (e) {
|
|
109
118
|
if (retryConfig) {
|
|
110
|
-
const
|
|
111
|
-
|
|
119
|
+
const errorInstance = error.getErrorFromUnknown(e, {
|
|
120
|
+
serializeStack: false,
|
|
121
|
+
fallbackMessage: "Unknown step execution error"
|
|
122
|
+
});
|
|
123
|
+
throw new inngest.RetryAfterError(errorInstance.message, retryConfig.delay, {
|
|
112
124
|
cause: {
|
|
113
125
|
status: "failed",
|
|
114
|
-
error:
|
|
126
|
+
error: errorInstance,
|
|
115
127
|
endedAt: Date.now()
|
|
116
128
|
}
|
|
117
129
|
});
|
|
@@ -134,14 +146,14 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
134
146
|
if (!(params.step instanceof InngestWorkflow)) {
|
|
135
147
|
return null;
|
|
136
148
|
}
|
|
137
|
-
const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData,
|
|
149
|
+
const { step, stepResults, executionContext, resume, timeTravel, prevOutput, inputData, pubsub, startedAt } = params;
|
|
138
150
|
const isResume = !!resume?.steps?.length;
|
|
139
151
|
let result;
|
|
140
152
|
let runId;
|
|
141
153
|
const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
|
|
142
154
|
try {
|
|
143
155
|
if (isResume) {
|
|
144
|
-
runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
|
|
156
|
+
runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? crypto$1.randomUUID();
|
|
145
157
|
const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
|
|
146
158
|
workflowName: step.id,
|
|
147
159
|
runId
|
|
@@ -208,9 +220,9 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
208
220
|
const errorCause = e?.cause;
|
|
209
221
|
if (errorCause && typeof errorCause === "object") {
|
|
210
222
|
result = errorCause;
|
|
211
|
-
runId = errorCause.runId || crypto.randomUUID();
|
|
223
|
+
runId = errorCause.runId || crypto$1.randomUUID();
|
|
212
224
|
} else {
|
|
213
|
-
runId = crypto.randomUUID();
|
|
225
|
+
runId = crypto$1.randomUUID();
|
|
214
226
|
result = {
|
|
215
227
|
status: "failed",
|
|
216
228
|
error: e instanceof Error ? e : new Error(String(e)),
|
|
@@ -223,13 +235,17 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
223
235
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
224
236
|
async () => {
|
|
225
237
|
if (result.status === "failed") {
|
|
226
|
-
await
|
|
227
|
-
type: "
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
238
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
239
|
+
type: "watch",
|
|
240
|
+
runId: executionContext.runId,
|
|
241
|
+
data: {
|
|
242
|
+
type: "workflow-step-result",
|
|
243
|
+
payload: {
|
|
244
|
+
id: step.id,
|
|
245
|
+
status: "failed",
|
|
246
|
+
error: result?.error,
|
|
247
|
+
payload: prevOutput
|
|
248
|
+
}
|
|
233
249
|
}
|
|
234
250
|
});
|
|
235
251
|
return { executionContext, result: { status: "failed", error: result?.error } };
|
|
@@ -241,11 +257,15 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
241
257
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
242
258
|
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
243
259
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
244
|
-
await
|
|
245
|
-
type: "
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
261
|
+
type: "watch",
|
|
262
|
+
runId: executionContext.runId,
|
|
263
|
+
data: {
|
|
264
|
+
type: "workflow-step-suspended",
|
|
265
|
+
payload: {
|
|
266
|
+
id: step.id,
|
|
267
|
+
status: "suspended"
|
|
268
|
+
}
|
|
249
269
|
}
|
|
250
270
|
});
|
|
251
271
|
return {
|
|
@@ -267,20 +287,49 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
267
287
|
payload: {}
|
|
268
288
|
}
|
|
269
289
|
};
|
|
290
|
+
} else if (result.status === "tripwire") {
|
|
291
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
292
|
+
type: "watch",
|
|
293
|
+
runId: executionContext.runId,
|
|
294
|
+
data: {
|
|
295
|
+
type: "workflow-step-result",
|
|
296
|
+
payload: {
|
|
297
|
+
id: step.id,
|
|
298
|
+
status: "tripwire",
|
|
299
|
+
error: result?.tripwire?.reason,
|
|
300
|
+
payload: prevOutput
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
return {
|
|
305
|
+
executionContext,
|
|
306
|
+
result: {
|
|
307
|
+
status: "tripwire",
|
|
308
|
+
tripwire: result?.tripwire
|
|
309
|
+
}
|
|
310
|
+
};
|
|
270
311
|
}
|
|
271
|
-
await
|
|
272
|
-
type: "
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
312
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
313
|
+
type: "watch",
|
|
314
|
+
runId: executionContext.runId,
|
|
315
|
+
data: {
|
|
316
|
+
type: "workflow-step-result",
|
|
317
|
+
payload: {
|
|
318
|
+
id: step.id,
|
|
319
|
+
status: "success",
|
|
320
|
+
output: result?.result
|
|
321
|
+
}
|
|
277
322
|
}
|
|
278
323
|
});
|
|
279
|
-
await
|
|
280
|
-
type: "
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
324
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
325
|
+
type: "watch",
|
|
326
|
+
runId: executionContext.runId,
|
|
327
|
+
data: {
|
|
328
|
+
type: "workflow-step-finish",
|
|
329
|
+
payload: {
|
|
330
|
+
id: step.id,
|
|
331
|
+
metadata: {}
|
|
332
|
+
}
|
|
284
333
|
}
|
|
285
334
|
});
|
|
286
335
|
return { executionContext, result: { status: "success", output: result?.result } };
|
|
@@ -297,6 +346,118 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
297
346
|
};
|
|
298
347
|
}
|
|
299
348
|
};
|
|
349
|
+
var InngestPubSub = class extends events.PubSub {
|
|
350
|
+
inngest;
|
|
351
|
+
workflowId;
|
|
352
|
+
publishFn;
|
|
353
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
354
|
+
constructor(inngest, workflowId, publishFn) {
|
|
355
|
+
super();
|
|
356
|
+
this.inngest = inngest;
|
|
357
|
+
this.workflowId = workflowId;
|
|
358
|
+
this.publishFn = publishFn;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Publish an event to Inngest's realtime system.
|
|
362
|
+
*
|
|
363
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
364
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
365
|
+
*/
|
|
366
|
+
async publish(topic, event) {
|
|
367
|
+
if (!this.publishFn) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
371
|
+
if (!match) {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
const runId = match[1];
|
|
375
|
+
try {
|
|
376
|
+
await this.publishFn({
|
|
377
|
+
channel: `workflow:${this.workflowId}:${runId}`,
|
|
378
|
+
topic: "watch",
|
|
379
|
+
data: event.data
|
|
380
|
+
});
|
|
381
|
+
} catch (err) {
|
|
382
|
+
console.error("InngestPubSub publish error:", err?.message ?? err);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Subscribe to events from Inngest's realtime system.
|
|
387
|
+
*
|
|
388
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
389
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
390
|
+
*/
|
|
391
|
+
async subscribe(topic, cb) {
|
|
392
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
393
|
+
if (!match || !match[1]) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
const runId = match[1];
|
|
397
|
+
if (this.subscriptions.has(topic)) {
|
|
398
|
+
this.subscriptions.get(topic).callbacks.add(cb);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const callbacks = /* @__PURE__ */ new Set([cb]);
|
|
402
|
+
const channel = `workflow:${this.workflowId}:${runId}`;
|
|
403
|
+
const streamPromise = realtime.subscribe(
|
|
404
|
+
{
|
|
405
|
+
channel,
|
|
406
|
+
topics: ["watch"],
|
|
407
|
+
app: this.inngest
|
|
408
|
+
},
|
|
409
|
+
(message) => {
|
|
410
|
+
const event = {
|
|
411
|
+
id: crypto.randomUUID(),
|
|
412
|
+
type: "watch",
|
|
413
|
+
runId,
|
|
414
|
+
data: message.data,
|
|
415
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
416
|
+
};
|
|
417
|
+
for (const callback of callbacks) {
|
|
418
|
+
callback(event);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
this.subscriptions.set(topic, {
|
|
423
|
+
unsubscribe: () => {
|
|
424
|
+
streamPromise.then((stream) => stream.cancel()).catch((err) => {
|
|
425
|
+
console.error("InngestPubSub unsubscribe error:", err);
|
|
426
|
+
});
|
|
427
|
+
},
|
|
428
|
+
callbacks
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Unsubscribe a callback from a topic.
|
|
433
|
+
* If no callbacks remain, the underlying Inngest subscription is cancelled.
|
|
434
|
+
*/
|
|
435
|
+
async unsubscribe(topic, cb) {
|
|
436
|
+
const sub = this.subscriptions.get(topic);
|
|
437
|
+
if (!sub) {
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
sub.callbacks.delete(cb);
|
|
441
|
+
if (sub.callbacks.size === 0) {
|
|
442
|
+
sub.unsubscribe();
|
|
443
|
+
this.subscriptions.delete(topic);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Flush any pending operations. No-op for Inngest.
|
|
448
|
+
*/
|
|
449
|
+
async flush() {
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Clean up all subscriptions during graceful shutdown.
|
|
453
|
+
*/
|
|
454
|
+
async close() {
|
|
455
|
+
for (const [, sub] of this.subscriptions) {
|
|
456
|
+
sub.unsubscribe();
|
|
457
|
+
}
|
|
458
|
+
this.subscriptions.clear();
|
|
459
|
+
}
|
|
460
|
+
};
|
|
300
461
|
var InngestRun = class extends workflows.Run {
|
|
301
462
|
inngest;
|
|
302
463
|
serializedStepGraph;
|
|
@@ -308,27 +469,77 @@ var InngestRun = class extends workflows.Run {
|
|
|
308
469
|
this.#mastra = params.mastra;
|
|
309
470
|
}
|
|
310
471
|
async getRuns(eventId) {
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
472
|
+
const maxRetries = 3;
|
|
473
|
+
let lastError = null;
|
|
474
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
475
|
+
try {
|
|
476
|
+
const response = await fetch(
|
|
477
|
+
`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
|
|
478
|
+
{
|
|
479
|
+
headers: {
|
|
480
|
+
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
if (response.status === 429) {
|
|
485
|
+
const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
|
|
486
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (!response.ok) {
|
|
490
|
+
throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
|
|
491
|
+
}
|
|
492
|
+
const text = await response.text();
|
|
493
|
+
if (!text) {
|
|
494
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
const json = JSON.parse(text);
|
|
498
|
+
return json.data;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
lastError = error;
|
|
501
|
+
if (attempt < maxRetries - 1) {
|
|
502
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
|
|
503
|
+
}
|
|
314
504
|
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return json.data;
|
|
505
|
+
}
|
|
506
|
+
throw new inngest.NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
|
|
318
507
|
}
|
|
319
|
-
async getRunOutput(eventId) {
|
|
320
|
-
|
|
508
|
+
async getRunOutput(eventId, maxWaitMs = 3e5) {
|
|
509
|
+
const startTime = Date.now();
|
|
321
510
|
const storage = this.#mastra?.getStorage();
|
|
322
|
-
while (
|
|
323
|
-
|
|
324
|
-
|
|
511
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
512
|
+
let runs;
|
|
513
|
+
try {
|
|
514
|
+
runs = await this.getRuns(eventId);
|
|
515
|
+
} catch (error) {
|
|
516
|
+
if (error instanceof inngest.NonRetriableError) {
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
throw new inngest.NonRetriableError(
|
|
520
|
+
`Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
|
|
524
|
+
return runs[0];
|
|
525
|
+
}
|
|
325
526
|
if (runs?.[0]?.status === "Failed") {
|
|
326
527
|
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
327
528
|
workflowName: this.workflowId,
|
|
328
529
|
runId: this.runId
|
|
329
530
|
});
|
|
531
|
+
if (snapshot?.context) {
|
|
532
|
+
snapshot.context = workflows.hydrateSerializedStepErrors(snapshot.context);
|
|
533
|
+
}
|
|
330
534
|
return {
|
|
331
|
-
output: {
|
|
535
|
+
output: {
|
|
536
|
+
result: {
|
|
537
|
+
steps: snapshot?.context,
|
|
538
|
+
status: "failed",
|
|
539
|
+
// Get the original error from NonRetriableError's cause (which contains the workflow result)
|
|
540
|
+
error: error.getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
|
|
541
|
+
}
|
|
542
|
+
}
|
|
332
543
|
};
|
|
333
544
|
}
|
|
334
545
|
if (runs?.[0]?.status === "Cancelled") {
|
|
@@ -338,8 +549,9 @@ var InngestRun = class extends workflows.Run {
|
|
|
338
549
|
});
|
|
339
550
|
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
340
551
|
}
|
|
552
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
|
|
341
553
|
}
|
|
342
|
-
|
|
554
|
+
throw new inngest.NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
|
|
343
555
|
}
|
|
344
556
|
async cancel() {
|
|
345
557
|
const storage = this.#mastra?.getStorage();
|
|
@@ -369,6 +581,51 @@ var InngestRun = class extends workflows.Run {
|
|
|
369
581
|
async start(params) {
|
|
370
582
|
return this._start(params);
|
|
371
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Starts the workflow execution without waiting for completion (fire-and-forget).
|
|
586
|
+
* Returns immediately with the runId after sending the event to Inngest.
|
|
587
|
+
* The workflow executes independently in Inngest.
|
|
588
|
+
* Use this when you don't need to wait for the result or want to avoid polling failures.
|
|
589
|
+
*/
|
|
590
|
+
async startAsync(params) {
|
|
591
|
+
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
592
|
+
workflowName: this.workflowId,
|
|
593
|
+
runId: this.runId,
|
|
594
|
+
resourceId: this.resourceId,
|
|
595
|
+
snapshot: {
|
|
596
|
+
runId: this.runId,
|
|
597
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
598
|
+
status: "running",
|
|
599
|
+
value: {},
|
|
600
|
+
context: {},
|
|
601
|
+
activePaths: [],
|
|
602
|
+
suspendedPaths: {},
|
|
603
|
+
activeStepsPath: {},
|
|
604
|
+
resumeLabels: {},
|
|
605
|
+
waitingPaths: {},
|
|
606
|
+
timestamp: Date.now()
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
const inputDataToUse = await this._validateInput(params.inputData);
|
|
610
|
+
const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
|
|
611
|
+
const eventOutput = await this.inngest.send({
|
|
612
|
+
name: `workflow.${this.workflowId}`,
|
|
613
|
+
data: {
|
|
614
|
+
inputData: inputDataToUse,
|
|
615
|
+
initialState: initialStateToUse,
|
|
616
|
+
runId: this.runId,
|
|
617
|
+
resourceId: this.resourceId,
|
|
618
|
+
outputOptions: params.outputOptions,
|
|
619
|
+
tracingOptions: params.tracingOptions,
|
|
620
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
const eventId = eventOutput.ids[0];
|
|
624
|
+
if (!eventId) {
|
|
625
|
+
throw new Error("Event ID is not set");
|
|
626
|
+
}
|
|
627
|
+
return { runId: this.runId };
|
|
628
|
+
}
|
|
372
629
|
async _start({
|
|
373
630
|
inputData,
|
|
374
631
|
initialState,
|
|
@@ -416,9 +673,7 @@ var InngestRun = class extends workflows.Run {
|
|
|
416
673
|
}
|
|
417
674
|
const runOutput = await this.getRunOutput(eventId);
|
|
418
675
|
const result = runOutput?.output?.result;
|
|
419
|
-
|
|
420
|
-
result.error = new Error(result.error);
|
|
421
|
-
}
|
|
676
|
+
this.hydrateFailedResult(result);
|
|
422
677
|
if (result.status !== "suspended") {
|
|
423
678
|
this.cleanup?.();
|
|
424
679
|
}
|
|
@@ -477,9 +732,7 @@ var InngestRun = class extends workflows.Run {
|
|
|
477
732
|
}
|
|
478
733
|
const runOutput = await this.getRunOutput(eventId);
|
|
479
734
|
const result = runOutput?.output?.result;
|
|
480
|
-
|
|
481
|
-
result.error = new Error(result.error);
|
|
482
|
-
}
|
|
735
|
+
this.hydrateFailedResult(result);
|
|
483
736
|
return result;
|
|
484
737
|
}
|
|
485
738
|
async timeTravel(params) {
|
|
@@ -569,9 +822,7 @@ var InngestRun = class extends workflows.Run {
|
|
|
569
822
|
}
|
|
570
823
|
const runOutput = await this.getRunOutput(eventId);
|
|
571
824
|
const result = runOutput?.output?.result;
|
|
572
|
-
|
|
573
|
-
result.error = new Error(result.error);
|
|
574
|
-
}
|
|
825
|
+
this.hydrateFailedResult(result);
|
|
575
826
|
return result;
|
|
576
827
|
}
|
|
577
828
|
watch(cb) {
|
|
@@ -792,6 +1043,18 @@ var InngestRun = class extends workflows.Run {
|
|
|
792
1043
|
});
|
|
793
1044
|
return this.streamOutput;
|
|
794
1045
|
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Hydrates errors in a failed workflow result back to proper Error instances.
|
|
1048
|
+
* This ensures error.cause chains and custom properties are preserved.
|
|
1049
|
+
*/
|
|
1050
|
+
hydrateFailedResult(result) {
|
|
1051
|
+
if (result.status === "failed") {
|
|
1052
|
+
result.error = error.getErrorFromUnknown(result.error, { serializeStack: false });
|
|
1053
|
+
if (result.steps) {
|
|
1054
|
+
workflows.hydrateSerializedStepErrors(result.steps);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
795
1058
|
};
|
|
796
1059
|
|
|
797
1060
|
// src/workflow.ts
|
|
@@ -829,6 +1092,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
829
1092
|
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
830
1093
|
}
|
|
831
1094
|
__registerMastra(mastra) {
|
|
1095
|
+
super.__registerMastra(mastra);
|
|
832
1096
|
this.#mastra = mastra;
|
|
833
1097
|
this.executionEngine.__registerMastra(mastra);
|
|
834
1098
|
const updateNested = (step) => {
|
|
@@ -847,7 +1111,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
847
1111
|
}
|
|
848
1112
|
}
|
|
849
1113
|
async createRun(options) {
|
|
850
|
-
const runIdToUse = options?.runId || crypto.randomUUID();
|
|
1114
|
+
const runIdToUse = options?.runId || crypto$1.randomUUID();
|
|
851
1115
|
const run = this.runs.get(runIdToUse) ?? new InngestRun(
|
|
852
1116
|
{
|
|
853
1117
|
workflowId: this.id,
|
|
@@ -912,31 +1176,10 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
912
1176
|
let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
|
|
913
1177
|
if (!runId) {
|
|
914
1178
|
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
915
|
-
return crypto.randomUUID();
|
|
1179
|
+
return crypto$1.randomUUID();
|
|
916
1180
|
});
|
|
917
1181
|
}
|
|
918
|
-
const
|
|
919
|
-
emit: async (event2, data) => {
|
|
920
|
-
if (!publish) {
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
try {
|
|
924
|
-
await publish({
|
|
925
|
-
channel: `workflow:${this.id}:${runId}`,
|
|
926
|
-
topic: event2,
|
|
927
|
-
data
|
|
928
|
-
});
|
|
929
|
-
} catch (err) {
|
|
930
|
-
this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
|
|
931
|
-
}
|
|
932
|
-
},
|
|
933
|
-
on: (_event, _callback) => {
|
|
934
|
-
},
|
|
935
|
-
off: (_event, _callback) => {
|
|
936
|
-
},
|
|
937
|
-
once: (_event, _callback) => {
|
|
938
|
-
}
|
|
939
|
-
};
|
|
1182
|
+
const pubsub = new InngestPubSub(this.inngest, this.id, publish);
|
|
940
1183
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
941
1184
|
const result = await engine.execute({
|
|
942
1185
|
workflowId: this.id,
|
|
@@ -946,7 +1189,7 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
946
1189
|
serializedStepGraph: this.serializedStepGraph,
|
|
947
1190
|
input: inputData,
|
|
948
1191
|
initialState,
|
|
949
|
-
|
|
1192
|
+
pubsub,
|
|
950
1193
|
retryConfig: this.retryConfig,
|
|
951
1194
|
requestContext: new di.RequestContext(Object.entries(event.data.requestContext ?? {})),
|
|
952
1195
|
resume,
|
|
@@ -955,12 +1198,17 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
955
1198
|
abortController: new AbortController(),
|
|
956
1199
|
// currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
|
|
957
1200
|
outputOptions,
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1201
|
+
outputWriter: async (chunk) => {
|
|
1202
|
+
try {
|
|
1203
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1204
|
+
type: "watch",
|
|
1205
|
+
runId,
|
|
1206
|
+
data: chunk
|
|
961
1207
|
});
|
|
1208
|
+
} catch (err) {
|
|
1209
|
+
this.logger.debug?.("Failed to publish watch event:", err);
|
|
962
1210
|
}
|
|
963
|
-
}
|
|
1211
|
+
}
|
|
964
1212
|
});
|
|
965
1213
|
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
966
1214
|
if (result.status === "failed") {
|
|
@@ -1035,6 +1283,7 @@ function createStep(params, agentOptions) {
|
|
|
1035
1283
|
return params;
|
|
1036
1284
|
}
|
|
1037
1285
|
if (isAgent(params)) {
|
|
1286
|
+
const outputSchema = agentOptions?.structuredOutput?.schema ?? zod.z.object({ text: zod.z.string() });
|
|
1038
1287
|
return {
|
|
1039
1288
|
id: params.name,
|
|
1040
1289
|
description: params.getDescription(),
|
|
@@ -1043,12 +1292,11 @@ function createStep(params, agentOptions) {
|
|
|
1043
1292
|
// resourceId: z.string().optional(),
|
|
1044
1293
|
// threadId: z.string().optional(),
|
|
1045
1294
|
}),
|
|
1046
|
-
outputSchema
|
|
1047
|
-
text: zod.z.string()
|
|
1048
|
-
}),
|
|
1295
|
+
outputSchema,
|
|
1049
1296
|
execute: async ({
|
|
1050
1297
|
inputData,
|
|
1051
|
-
|
|
1298
|
+
runId,
|
|
1299
|
+
[_constants.PUBSUB_SYMBOL]: pubsub,
|
|
1052
1300
|
[_constants.STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
1053
1301
|
requestContext,
|
|
1054
1302
|
tracingContext,
|
|
@@ -1061,6 +1309,7 @@ function createStep(params, agentOptions) {
|
|
|
1061
1309
|
streamPromise.resolve = resolve;
|
|
1062
1310
|
streamPromise.reject = reject;
|
|
1063
1311
|
});
|
|
1312
|
+
let structuredResult = null;
|
|
1064
1313
|
const toolData = {
|
|
1065
1314
|
name: params.name,
|
|
1066
1315
|
args: inputData
|
|
@@ -1074,6 +1323,10 @@ function createStep(params, agentOptions) {
|
|
|
1074
1323
|
requestContext,
|
|
1075
1324
|
tracingContext,
|
|
1076
1325
|
onFinish: (result) => {
|
|
1326
|
+
const resultWithObject = result;
|
|
1327
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1328
|
+
structuredResult = resultWithObject.object;
|
|
1329
|
+
}
|
|
1077
1330
|
streamPromise.resolve(result.text);
|
|
1078
1331
|
void agentOptions?.onFinish?.(result);
|
|
1079
1332
|
},
|
|
@@ -1086,6 +1339,10 @@ function createStep(params, agentOptions) {
|
|
|
1086
1339
|
requestContext,
|
|
1087
1340
|
tracingContext,
|
|
1088
1341
|
onFinish: (result) => {
|
|
1342
|
+
const resultWithObject = result;
|
|
1343
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1344
|
+
structuredResult = resultWithObject.object;
|
|
1345
|
+
}
|
|
1089
1346
|
streamPromise.resolve(result.text);
|
|
1090
1347
|
void agentOptions?.onFinish?.(result);
|
|
1091
1348
|
},
|
|
@@ -1094,22 +1351,24 @@ function createStep(params, agentOptions) {
|
|
|
1094
1351
|
stream = modelOutput.fullStream;
|
|
1095
1352
|
}
|
|
1096
1353
|
if (streamFormat === "legacy") {
|
|
1097
|
-
await
|
|
1098
|
-
type: "
|
|
1099
|
-
|
|
1354
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1355
|
+
type: "watch",
|
|
1356
|
+
runId,
|
|
1357
|
+
data: { type: "tool-call-streaming-start", ...toolData ?? {} }
|
|
1100
1358
|
});
|
|
1101
1359
|
for await (const chunk of stream) {
|
|
1102
1360
|
if (chunk.type === "text-delta") {
|
|
1103
|
-
await
|
|
1104
|
-
type: "
|
|
1105
|
-
|
|
1106
|
-
argsTextDelta: chunk.textDelta
|
|
1361
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1362
|
+
type: "watch",
|
|
1363
|
+
runId,
|
|
1364
|
+
data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
|
|
1107
1365
|
});
|
|
1108
1366
|
}
|
|
1109
1367
|
}
|
|
1110
|
-
await
|
|
1111
|
-
type: "
|
|
1112
|
-
|
|
1368
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1369
|
+
type: "watch",
|
|
1370
|
+
runId,
|
|
1371
|
+
data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
|
|
1113
1372
|
});
|
|
1114
1373
|
} else {
|
|
1115
1374
|
for await (const chunk of stream) {
|
|
@@ -1119,6 +1378,9 @@ function createStep(params, agentOptions) {
|
|
|
1119
1378
|
if (abortSignal.aborted) {
|
|
1120
1379
|
return abort();
|
|
1121
1380
|
}
|
|
1381
|
+
if (structuredResult !== null) {
|
|
1382
|
+
return structuredResult;
|
|
1383
|
+
}
|
|
1122
1384
|
return {
|
|
1123
1385
|
text: await streamPromise.promise
|
|
1124
1386
|
};
|
|
@@ -1219,6 +1481,7 @@ function init(inngest) {
|
|
|
1219
1481
|
}
|
|
1220
1482
|
|
|
1221
1483
|
exports.InngestExecutionEngine = InngestExecutionEngine;
|
|
1484
|
+
exports.InngestPubSub = InngestPubSub;
|
|
1222
1485
|
exports.InngestRun = InngestRun;
|
|
1223
1486
|
exports.InngestWorkflow = InngestWorkflow;
|
|
1224
1487
|
exports._compatibilityCheck = _compatibilityCheck;
|