@mastra/core
Version:
Mastra is a framework for building AI-powered applications and agents with a modern TypeScript stack.
224 lines (221 loc) • 9.77 kB
JavaScript
'use strict';
var chunkBKPEQNQF_cjs = require('./chunk-BKPEQNQF.cjs');
var chunkM5AKMHS2_cjs = require('./chunk-M5AKMHS2.cjs');
var zod = require('zod');
var inputSchema = zod.z.object({ taskId: zod.z.string() });
var attemptOutcomeSchema = zod.z.enum(["success", "retry", "cancelled", "timed_out"]);
var attemptOutputSchema = zod.z.object({
taskId: zod.z.string(),
outcome: attemptOutcomeSchema,
result: zod.z.unknown().optional(),
error: zod.z.any().optional()
});
var bodyIOSchema = zod.z.object({
taskId: zod.z.string(),
done: zod.z.boolean().optional(),
result: zod.z.unknown().optional()
});
var bodyOutputSchema = zod.z.object({
taskId: zod.z.string(),
done: zod.z.boolean(),
result: zod.z.unknown().optional()
});
var WORKFLOW_STATUS_TO_PERSIST = ["suspended", "pending", "paused", "waiting"];
function buildBackgroundTaskWorkflow(manager) {
const runAttemptStep = chunkBKPEQNQF_cjs.createStep({
id: "run-attempt",
inputSchema: bodyIOSchema,
outputSchema: attemptOutputSchema,
execute: async ({ inputData, abortSignal: workflowAbortSignal, suspend, resumeData }) => {
const { taskId } = inputData;
const storage = await manager.getStorage();
const task = await storage.getTask(taskId);
if (!task || task.status === "cancelled") {
manager.deregisterTaskContext(taskId);
return { taskId, outcome: "cancelled" };
}
const ctx = manager.taskContexts.get(taskId);
const executor = ctx?.executor ?? manager.getStaticExecutor(task.toolName);
if (!executor) {
const errorInfo = {
message: `No executor registered for tool "${task.toolName}". Register the tool on Mastra (so workers can resolve it cross-process) or run the task in the same process as the producer.`
};
await storage.updateTask(taskId, { status: "failed", error: errorInfo, completedAt: /* @__PURE__ */ new Date() });
const failedTask = await storage.getTask(taskId);
if (failedTask) {
await manager.runLocalCompletionHooks(failedTask, "failed", { error: errorInfo });
await manager.publishLifecycleEvent("task.failed", failedTask);
}
manager.deregisterTaskContext(taskId);
throw new Error(errorInfo.message);
}
const progressThrottleMs = manager.config.progressThrottleMs;
const shouldThrottleProgress = typeof progressThrottleMs === "number" && Number.isFinite(progressThrottleMs) && progressThrottleMs > 0;
let lastProgressEmitMs;
const onProgress = async (chunk) => {
if (shouldThrottleProgress) {
const now = Date.now();
if (lastProgressEmitMs !== void 0 && now - lastProgressEmitMs < progressThrottleMs) return;
lastProgressEmitMs = now;
}
await manager.publishLifecycleEvent("task.output", { ...task, chunk });
};
const abortController = new AbortController();
manager.activeAbortControllers.set(taskId, abortController);
const onWorkflowAbort = () => abortController.abort(new Error("Task cancelled"));
if (workflowAbortSignal.aborted) {
abortController.abort(new Error("Task cancelled"));
} else {
workflowAbortSignal.addEventListener("abort", onWorkflowAbort, { once: true });
}
const timeoutHandle = setTimeout(() => {
abortController.abort(new Error(`Task timed out after ${task.timeoutMs}ms`));
}, task.timeoutMs);
let pendingSuspend;
const wrappedSuspend = async (data, suspendOptions) => {
await storage.updateTask(taskId, {
status: "suspended",
suspendPayload: data,
suspendedAt: /* @__PURE__ */ new Date()
});
const suspendedTask = await storage.getTask(taskId);
if (suspendedTask) {
await manager.runLocalSuspendHooks(suspendedTask);
await manager.publishLifecycleEvent("task.suspended", suspendedTask);
}
pendingSuspend = { data, suspendOptions };
};
try {
const result = await executor.execute(task.args, {
abortSignal: abortController.signal,
onProgress,
suspend: wrappedSuspend,
// On resume the runtime populates `resumeData`; undefined on
// the initial run.
resumeData
});
if (pendingSuspend) {
return suspend(pendingSuspend.data, pendingSuspend.suspendOptions);
}
return { taskId, outcome: "success", result };
} catch (error) {
const currentTask = await storage.getTask(taskId);
if (!currentTask || currentTask.status === "cancelled") {
manager.deregisterTaskContext(taskId);
return { taskId, outcome: "cancelled" };
}
if (abortController.signal.aborted || error?.name === "AbortError" || error?.message === "Task cancelled" || error?.message?.startsWith("Task timed out after ")) {
return { taskId, outcome: "timed_out" };
}
return {
taskId,
outcome: "retry",
error: { message: error?.message ?? "Unknown error", stack: error?.stack }
};
} finally {
clearTimeout(timeoutHandle);
workflowAbortSignal.removeEventListener("abort", onWorkflowAbort);
manager.activeAbortControllers.delete(taskId);
}
}
});
const classifyOutcomeStep = chunkBKPEQNQF_cjs.createStep({
id: "classify-outcome",
inputSchema: attemptOutputSchema,
outputSchema: bodyOutputSchema,
execute: async ({ inputData }) => {
const { taskId, outcome, result, error } = inputData;
const storage = await manager.getStorage();
const task = await storage.getTask(taskId);
if (!task) return { taskId, done: true };
if (outcome === "cancelled") {
manager.deregisterTaskContext(taskId);
return { taskId, done: true };
}
if (outcome === "timed_out") {
const status = task.status;
if (status !== "timed_out" && status !== "cancelled") {
await storage.updateTask(taskId, {
status: "timed_out",
error: { message: `Task timed out after ${task.timeoutMs}ms` },
completedAt: /* @__PURE__ */ new Date()
});
const timedOutTask = await storage.getTask(taskId);
if (timedOutTask) await manager.publishLifecycleEvent("task.failed", timedOutTask);
}
return { taskId, done: true };
}
if (outcome === "success") {
if (task.status === "cancelled") {
manager.deregisterTaskContext(taskId);
return { taskId, done: true };
}
await storage.updateTask(taskId, { status: "completed", result, completedAt: /* @__PURE__ */ new Date() });
const completedTask = await storage.getTask(taskId);
if (completedTask) {
await manager.runLocalCompletionHooks(completedTask, "completed", { result });
await manager.publishLifecycleEvent("task.completed", completedTask);
}
return { taskId, done: true, result };
}
if (task.retryCount < task.maxRetries) {
await storage.updateTask(taskId, {
retryCount: task.retryCount + 1,
error: void 0,
startedAt: /* @__PURE__ */ new Date()
});
return { taskId, done: false };
}
const errorInfo = error ?? { message: "Unknown error" };
await storage.updateTask(taskId, { status: "failed", error: errorInfo, completedAt: /* @__PURE__ */ new Date() });
const failedTask = await storage.getTask(taskId);
if (failedTask) {
await manager.runLocalCompletionHooks(failedTask, "failed", { error: errorInfo });
await manager.publishLifecycleEvent("task.failed", failedTask);
}
const thrown = new Error(errorInfo.message);
if (errorInfo.stack) thrown.stack = errorInfo.stack;
throw thrown;
}
});
const attemptBodyWorkflow = chunkBKPEQNQF_cjs.createWorkflow({
id: `${chunkM5AKMHS2_cjs.BACKGROUND_TASK_WORKFLOW_ID}__attempt`,
inputSchema: bodyIOSchema,
outputSchema: bodyOutputSchema,
steps: [runAttemptStep, classifyOutcomeStep],
options: {
// `dountil` feeds the prior iteration's output back in as input. The
// body's actual entry point only needs `taskId`, but the loop's
// feedback shape includes `done`/`result`/etc. Skip validation rather
// than widen every step's input schema.
validateInputs: false,
shouldPersistSnapshot: ({ workflowStatus }) => WORKFLOW_STATUS_TO_PERSIST.includes(workflowStatus),
// Internal scheduler plumbing — hide workflow spans from exported
// traces. The task body itself runs as user code and keeps its own
// spans.
tracingPolicy: {
internal: 1 /* WORKFLOW */
}
}
}).then(runAttemptStep).then(classifyOutcomeStep).commit();
return chunkBKPEQNQF_cjs.createWorkflow({
id: chunkM5AKMHS2_cjs.BACKGROUND_TASK_WORKFLOW_ID,
inputSchema,
outputSchema: bodyOutputSchema,
steps: [attemptBodyWorkflow],
options: {
shouldPersistSnapshot: ({ workflowStatus }) => WORKFLOW_STATUS_TO_PERSIST.includes(workflowStatus),
// Internal scheduler plumbing — see the inner workflow comment.
tracingPolicy: {
internal: 1 /* WORKFLOW */
}
}
}).dountil(attemptBodyWorkflow, async ({ inputData }) => inputData?.done === true).commit();
}
Object.defineProperty(exports, "BACKGROUND_TASK_WORKFLOW_ID", {
enumerable: true,
get: function () { return chunkM5AKMHS2_cjs.BACKGROUND_TASK_WORKFLOW_ID; }
});
exports.buildBackgroundTaskWorkflow = buildBackgroundTaskWorkflow;
//# sourceMappingURL=workflow-NQFS7R77.cjs.map
//# sourceMappingURL=workflow-NQFS7R77.cjs.map