UNPKG

@mastra/core

Version:
1,437 lines 263 kB
const require_rolldown_runtime = require("./rolldown-runtime-uwYp4b74.cjs"); const require_logger = require("./logger-BPclhj7J.cjs"); const require_inmemory = require("./inmemory-CVHnncRp.cjs"); const require_error = require("./error-B-e62x-A.cjs"); const require_event_emitter = require("./event-emitter-80LrFIBS.cjs"); const require_caching_pubsub = require("./caching-pubsub-DBB7kAgu.cjs"); const require_observability = require("./observability-BzV5axz0.cjs"); const require_utils = require("./utils-CNiGU0Uf.cjs"); require("./tracing-BUrUJwCM.cjs"); const require_request_context = require("./request-context-ByoZMp-j.cjs"); const require_llm = require("./llm-CmflaXHA.cjs"); const require_background_tasks = require("./background-tasks-lifNqs9M.cjs"); const require_toolchecks = require("./toolchecks-Rfz17G5p.cjs"); const require_utils$1 = require("./utils-Bw7FoAI3.cjs"); const require_utils_safe_stringify = require("./utils/safe-stringify.cjs"); const require_trip_wire = require("./trip-wire-dxd_uCHj.cjs"); const require_agent = require("./agent-DCD4MApC.cjs"); const require_payload_transform = require("./payload-transform-DJzDdfpE.cjs"); const require_signals = require("./signals-D2CulJo3.cjs"); const require_message_list = require("./message-list-BM7m-E-v.cjs"); const require_task_state_processor = require("./task-state-processor-BB7omHO3.cjs"); const require_workflows_constants = require("./workflows/constants.cjs"); const require_storage = require("./storage-C4FD5U8Z.cjs"); let _isaacs_ttlcache = require("@isaacs/ttlcache"); let stream_web = require("stream/web"); let zod = require("zod"); let _mastra_schema_compat_schema = require("@mastra/schema-compat/schema"); //#region src/agent/durable/durable-stream-until-idle.ts /** * Run `DurableAgent.streamUntilIdle` (or `DurableAgent.stream({ untilIdle })`). * Initial turn invokes `agent.stream(messages, ...)`; continuations triggered * by background-task completions run as fresh `agent.stream([], ...)` calls * against the same memory thread. */ async function runDurableStreamUntilIdle(agent, messages, streamOptions, deps) { const innerCleanups = []; const innerAborts = []; return require_agent.runIdleLoop(agent, streamOptions, deps, (opts) => agent.stream(messages, opts), (opts) => agent.stream([], opts), (first, ctx) => { if (!ctx) return first; return { output: new Proxy(first.output, { get(target, prop) { if (prop === "fullStream") return ctx.combinedStream; const value = Reflect.get(target, prop, target); return typeof value === "function" ? value.bind(target) : value; } }), get fullStream() { return ctx.combinedStream; }, runId: first.runId, threadId: ctx.threadId, resourceId: ctx.resourceId, cleanup: ctx.forceClose, abort: (reason) => { for (const innerAbort of innerAborts) try { innerAbort(reason); } catch {} ctx.forceClose(); } }; }, { onInnerResult: (inner) => { if (typeof inner.cleanup === "function") innerCleanups.push(inner.cleanup); if (typeof inner.abort === "function") innerAborts.push(inner.abort); }, onForceClose: () => { for (const fn of innerCleanups) try { fn(); } catch {} } }); } /** * Run `DurableAgent.resume(..., { untilIdle })`. Same idle-loop semantics as * `runDurableStreamUntilIdle` — initial turn calls `agent.resume(runId, * resumeData, ...)` against the existing run snapshot, and subsequent * continuations triggered by background-task completions use * `agent.stream([], continuationOpts)` (a normal multi-turn agent stream) * since the resume completes and we're back in regular conversation flow. */ async function runResumeDurableStreamUntilIdle(agent, runId, resumeData, streamOptions, deps) { const innerCleanups = []; const innerAborts = []; return require_agent.runIdleLoop(agent, streamOptions, deps, (opts) => agent.resume(runId, resumeData, opts), (opts) => agent.stream([], opts), (first, ctx) => { if (!ctx) return first; return { output: new Proxy(first.output, { get(target, prop) { if (prop === "fullStream") return ctx.combinedStream; const value = Reflect.get(target, prop, target); return typeof value === "function" ? value.bind(target) : value; } }), get fullStream() { return ctx.combinedStream; }, runId: first.runId, threadId: ctx.threadId, resourceId: ctx.resourceId, cleanup: ctx.forceClose, abort: (reason) => { for (const innerAbort of innerAborts) try { innerAbort(reason); } catch {} ctx.forceClose(); } }; }, { onInnerResult: (inner) => { if (typeof inner.cleanup === "function") innerCleanups.push(inner.cleanup); if (typeof inner.abort === "function") innerAborts.push(inner.abort); }, onForceClose: () => { for (const fn of innerCleanups) try { fn(); } catch {} } }); } //#endregion //#region src/agent/durable/utils/serialize-state.ts /** * Extract serializable metadata from a CoreTool * This strips out the execute function and converts the schema to JSON Schema */ function serializeToolMetadata(name, tool) { let inputSchema = { type: "object" }; if (tool.parameters) { if ("type" in tool.parameters && typeof tool.parameters.type === "string") inputSchema = tool.parameters; else if ("jsonSchema" in tool.parameters) inputSchema = tool.parameters.jsonSchema; else if ("_def" in tool.parameters) inputSchema = { type: "object" }; } return { id: "id" in tool && typeof tool.id === "string" ? tool.id : name, name, description: tool.description, inputSchema, requireApproval: tool.requireApproval, hasSuspendSchema: tool.hasSuspendSchema }; } /** * Extract serializable metadata from all tools */ function serializeToolsMetadata(tools) { return Object.entries(tools).map(([name, tool]) => serializeToolMetadata(name, tool)); } /** * Extract serializable model configuration */ function serializeModelConfig(model) { return { provider: model.provider, modelId: model.modelId, specificationVersion: model.specificationVersion, originalConfig: `${model.provider}/${model.modelId}` }; } /** * Extract serializable model list entry from AgentModelManagerConfig */ function serializeModelListEntry(entry) { const model = entry.model; return { id: entry.id, config: { provider: model.provider, modelId: model.modelId, specificationVersion: model.specificationVersion, originalConfig: `${model.provider}/${model.modelId}`, providerOptions: entry.providerOptions }, maxRetries: entry.maxRetries, enabled: entry.enabled }; } /** * Serialize an array of model configs into a model list. * Filters out disabled models since they shouldn't be included in durable execution. */ function serializeModelList(models) { return models.filter((m) => m.enabled !== false).map(serializeModelListEntry); } /** * Serialize scorers configuration for durable execution. * * This extracts the scorer name (for resolution at runtime) and sampling config. * The actual scorer objects are resolved from Mastra at step execution time. * * @param scorers The agent's scorers configuration (from agent.scorers or options.scorers) * @returns Serializable scorer configuration */ function serializeScorersConfig(scorers) { const result = {}; for (const [key, entry] of Object.entries(scorers)) { const scorerEntry = { scorerName: typeof entry.scorer === "string" ? entry.scorer : entry.scorer.name }; if (entry.sampling) scorerEntry.sampling = entry.sampling; result[key] = scorerEntry; } return result; } /** * Extract serializable state from _internal-like objects */ function serializeDurableState(params) { return { memoryConfig: params.memoryConfig, threadId: params.threadId, resourceId: params.resourceId, threadExists: params.threadExists, savePerStep: params.savePerStep, observationalMemory: params.observationalMemory }; } /** * Pick the JSON-safe call settings out of an arbitrary `modelSettings` input. * Drops any field that is not a primitive value of the expected type so that * non-serializable fields (functions, AbortSignal, etc.) never reach the * workflow input. */ function serializeModelSettings(settings) { if (!settings || typeof settings !== "object") return void 0; const source = settings; const out = {}; const pickNumber = (key) => { const value = source[key]; if (typeof value === "number" && Number.isFinite(value)) out[key] = value; }; pickNumber("maxOutputTokens"); pickNumber("temperature"); pickNumber("topP"); pickNumber("topK"); pickNumber("presencePenalty"); pickNumber("frequencyPenalty"); pickNumber("seed"); pickNumber("maxRetries"); if (Array.isArray(source.stopSequences) && source.stopSequences.every((v) => typeof v === "string")) out.stopSequences = source.stopSequences; return Object.keys(out).length > 0 ? out : void 0; } /** * Extract serializable options from agent execution options */ function serializeDurableOptions(options) { let serializedToolChoice; if (options.toolChoice) { if (typeof options.toolChoice === "string") serializedToolChoice = options.toolChoice; else if (typeof options.toolChoice === "object" && "type" in options.toolChoice) { if (options.toolChoice.type === "tool" && "toolName" in options.toolChoice) serializedToolChoice = { type: "tool", toolName: options.toolChoice.toolName }; } } return { maxSteps: options.maxSteps, toolChoice: serializedToolChoice, activeTools: options.activeTools, modelSettings: serializeModelSettings(options.modelSettings), requireToolApproval: options.requireToolApproval, toolCallConcurrency: options.toolCallConcurrency, autoResumeSuspendedTools: options.autoResumeSuspendedTools, maxProcessorRetries: options.maxProcessorRetries, includeRawChunks: options.includeRawChunks, returnScorerData: options.returnScorerData, hasErrorProcessors: options.hasErrorProcessors, providerOptions: options.providerOptions, structuredOutput: options.structuredOutput, skipBgTaskWait: options.skipBgTaskWait, disableBackgroundTasks: options.disableBackgroundTasks, tracingOptions: options.tracingOptions, actor: options.actor, instructionsOverride: options.instructionsOverride, systemMessage: options.systemMessage, transform: options.transform, isTaskComplete: options.isTaskComplete }; } /** * Create the full workflow input from all components */ function createWorkflowInput(params) { return { __workflowKind: "durable-agent", runId: params.runId, agentId: params.agentId, agentName: params.agentName, messageListState: params.messageList.serialize(), toolsMetadata: serializeToolsMetadata(params.tools), modelConfig: serializeModelConfig(params.model), modelList: params.modelList ? serializeModelList(params.modelList) : void 0, scorers: params.scorers ? serializeScorersConfig(params.scorers) : void 0, options: serializeDurableOptions(params.options), state: serializeDurableState(params.state), messageId: params.messageId, agentSpanData: params.agentSpanData, modelSpanData: params.modelSpanData, requestContextEntries: params.requestContextEntries }; } /** * Serialize an error for workflow state */ function serializeError(error) { if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack }; return { name: "Error", message: String(error) }; } //#endregion //#region src/agent/durable/preparation.ts /** * JSON-safe snapshot of `requestContext.entries()` so durable steps (e.g. * is-task-complete scorers) can see the same `customContext` the non-durable * path passes. Best-effort: entries that fail a JSON round-trip are skipped * so a single non-serializable value can't break the workflow input. */ function snapshotRequestContextEntries(requestContext) { if (!requestContext) return void 0; const out = {}; let any = false; for (const [key, value] of requestContext.entries()) { const json = require_utils_safe_stringify.boundedStringify(value); if (json === void 0) continue; out[key] = JSON.parse(json); any = true; } return any ? out : void 0; } /** * Mirror of Agent#convertInstructionsToString — used for the AGENT_RUN span * `attributes.instructions` field so durable runs publish the same shape as * non-durable runs. Kept local to avoid promoting the private method. */ function convertInstructionsToString(instructions) { if (!instructions) return ""; if (typeof instructions === "string") return instructions; if (Array.isArray(instructions)) return instructions.map((msg) => typeof msg === "string" ? msg : typeof msg.content === "string" ? msg.content : "").filter(Boolean).join("\n\n"); return typeof instructions.content === "string" ? instructions.content : ""; } /** * Extract signal messages already present in the messageList at run start * (from persisted history) so they can be echoed as data-signal stream parts * on the first LLM step. Mirrors `prepare-memory-step.ts#getInitialSignalEchoes`. */ function getInitialSignalEchoes(messageList) { const inputMessageIds = messageList.makeMessageSourceChecker().input; return messageList.get.all.db().filter((message) => message.role === "signal" && inputMessageIds.has(message.id)).map(require_signals.mastraDBMessageToSignal); } /** * Prepare for durable agent execution. * * This function performs the non-durable preparation phase: * 1. Generates run ID and message ID * 2. Resolves thread/memory context * 3. Creates MessageList with instructions and messages * 4. Converts tools to CoreTool format * 5. Gets the model configuration * 6. Creates serialized workflow input * 7. Creates run registry entry for non-serializable state * * The result includes both the serialized workflow input (for the durable * workflow) and the run registry entry (for non-serializable state). */ async function prepareForDurableExecution(options) { const { agent, messages, options: rawExecOptions, optionsAreResolved = false, runId: providedRunId, requestContext: providedRequestContext, logger, mastra, methodType = "stream", durableAgentId, durableAgentName } = options; const publicAgentId = durableAgentId ?? agent.id; const publicAgentName = durableAgentName ?? agent.name ?? agent.id; const typedAgent = agent; const runId = providedRunId ?? crypto.randomUUID(); const messageId = crypto.randomUUID(); const requestContext = providedRequestContext ?? new require_request_context.RequestContext(); const requestContextEntriesSnapshot = snapshotRequestContextEntries(requestContext); const execOptions = optionsAreResolved ? rawExecOptions ?? {} : require_utils$1.deepMerge(await typedAgent.getDefaultOptions({ requestContext }) ?? {}, rawExecOptions ?? {}); const requestVersions = requestContext.get(require_request_context.MASTRA_VERSIONS_KEY); let mergedVersions = require_request_context.mergeVersionOverrides(mastra?.getVersionOverrides?.(), requestVersions); if (execOptions?.versions) mergedVersions = require_request_context.mergeVersionOverrides(mergedVersions, execOptions.versions); if (mergedVersions) requestContext.set(require_request_context.MASTRA_VERSIONS_KEY, mergedVersions); const thread = typeof execOptions?.memory?.thread === "string" ? { id: execOptions.memory.thread } : execOptions?.memory?.thread; const threadId = thread?.id; const resourceId = execOptions?.memory?.resource; let threadObject; let threadExists = false; const messageList = new require_message_list.MessageList({ threadId, resourceId }); const instructions = execOptions?.instructions || await typedAgent.getInstructions({ requestContext }); if (instructions) if (typeof instructions === "string") messageList.addSystem(instructions); else if (Array.isArray(instructions)) for (const inst of instructions) messageList.addSystem(inst); else messageList.addSystem(instructions); const workspace = await typedAgent.getWorkspace({ requestContext }); if (workspace) { const hasFs = typeof workspace.hasFilesystemConfig === "function" ? workspace.hasFilesystemConfig() : !!workspace.filesystem; const hasSb = typeof workspace.hasSandboxConfig === "function" ? workspace.hasSandboxConfig() : !!workspace.sandbox; if (hasFs || hasSb) { const wsInstructions = typeof workspace.getInstructionsAsync === "function" ? await workspace.getInstructionsAsync({ requestContext }) : workspace.getInstructions({ requestContext }); if (wsInstructions) messageList.addSystem({ role: "system", content: wsInstructions }); } } if (execOptions?.context) messageList.add(execOptions.context, "context"); if (execOptions?.system) { const sys = execOptions.system; if (typeof sys === "string") messageList.addSystem(sys); else if (Array.isArray(sys)) for (const s of sys) messageList.addSystem(s); else messageList.addSystem(sys); } messageList.add(messages, "input"); const memory = await typedAgent.getMemory({ requestContext }); const memoryConfig = execOptions?.memory?.options; if (memory && threadId && resourceId) { threadObject = await memory.getThreadById({ threadId }) ?? await memory.createThread({ threadId, metadata: thread?.metadata, title: thread?.title, memoryConfig, resourceId, saveThread: true }); threadExists = true; requestContext.set("MastraMemory", { thread: threadObject, resourceId, memoryConfig }); } else requestContext.delete("MastraMemory"); const processorStates = /* @__PURE__ */ new Map(); let inputProcessors = []; let llmRequestInputProcessors = []; let outputProcessors = []; let errorProcessors = []; try { inputProcessors = await typedAgent.listInputProcessors(requestContext); llmRequestInputProcessors = await typedAgent.__listLLMRequestProcessors(requestContext); outputProcessors = execOptions?.outputProcessors ? execOptions.outputProcessors : await typedAgent.listOutputProcessors(requestContext); errorProcessors = await typedAgent.listErrorProcessors(requestContext); } catch (error) { logger?.warn?.(`[DurableAgent] Error resolving processors: ${error}`); } const resolvedVersionId = (typeof agent.toRawConfig === "function" ? agent.toRawConfig() : void 0)?.resolvedVersionId; const agentTracingPolicy = typeof agent.getTracingPolicy === "function" ? agent.getTracingPolicy() : void 0; const agentSpan = require_utils.getOrCreateSpan({ type: "agent_run", name: `agent run: '${publicAgentId}'`, entityType: require_utils.EntityType.AGENT, entityId: publicAgentId, entityName: publicAgentName, input: messages, attributes: { conversationId: threadId, instructions: convertInstructionsToString(instructions), ...resolvedVersionId ? { resolvedVersionId } : {} }, metadata: { runId, resourceId, threadId, ...resolvedVersionId ? { entityVersionId: resolvedVersionId } : {} }, tracingPolicy: agentTracingPolicy, tracingContext: execOptions?.tracingContext, tracingOptions: execOptions?.tracingOptions, requestContext, mastra }); let tripwireData; if (inputProcessors.length > 0) try { const { ProcessorRunner } = await Promise.resolve().then(() => require("./trip-wire-dxd_uCHj.cjs")).then((n) => n.runner_exports); await new ProcessorRunner({ inputProcessors, outputProcessors, errorProcessors, logger, agentName: publicAgentName, processorStates }).runInputProcessors(messageList, require_observability.createObservabilityContext({ currentSpan: agentSpan }), requestContext, 0); } catch (error) { if (error instanceof require_trip_wire.TripWire) { tripwireData = { reason: error.message, retry: error.options?.retry, metadata: error.options?.metadata, processorId: error.processorId }; logger?.warn?.("Input processor tripwire triggered", { agent: publicAgentName, reason: error.message, processorId: error.processorId, retry: error.options?.retry }); } else logger?.warn?.(`[DurableAgent] Error running input processors: ${error}`); } let tools = {}; try { tools = await typedAgent.getToolsForExecution({ toolsets: execOptions?.toolsets, clientTools: execOptions?.clientTools, threadId, resourceId, runId, requestContext, memoryConfig: execOptions?.memory?.options, autoResumeSuspendedTools: execOptions?.autoResumeSuspendedTools, hooks: execOptions?.hooks, delegation: execOptions?.delegation, methodType }); } catch (error) { logger?.warn?.(`[DurableAgent] Error converting tools: ${error}`); } const model = await typedAgent.getModel({ requestContext }); if (!model) throw new Error("Agent model not available"); const modelList = await typedAgent.getModelList(requestContext); const overrideScorers = execOptions?.scorers; let scorers; if (overrideScorers) scorers = overrideScorers; else try { const agentScorers = await typedAgent.listScorers({ requestContext }); if (agentScorers && Object.keys(agentScorers).length > 0) scorers = agentScorers; } catch (error) { logger?.debug?.(`[DurableAgent] Error getting scorers: ${error}`); } const saveQueueManager = memory ? new require_agent.SaveQueueManager({ logger, memory }) : void 0; let serializedStructuredOutput; if (execOptions?.structuredOutput) { const so = execOptions.structuredOutput; if (so.schema) { serializedStructuredOutput = { jsonPromptInjection: so.jsonPromptInjection, useAgent: so.useAgent }; if (typeof so.schema === "object" && "type" in so.schema) serializedStructuredOutput.schema = so.schema; else if (typeof so.schema === "object" && "jsonSchema" in so.schema) serializedStructuredOutput.schema = so.schema.jsonSchema; } } const backgroundTasksConfig = typedAgent.getBackgroundTasksConfig?.(); const backgroundTaskManager = execOptions?.disableBackgroundTasks ? void 0 : mastra?.backgroundTaskManager; const toolPayloadTransform = require_payload_transform.normalizeToolPayloadTransformPolicy(execOptions?.transform) ?? typedAgent.getToolPayloadTransform?.() ?? require_payload_transform.normalizeToolPayloadTransformPolicy(mastra?.getToolPayloadTransform?.() ?? mastra?.getToolPayloadProjection?.()); const savePerStep = execOptions?.savePerStep; const observationalMemory = !!memoryConfig?.observationalMemory; const modelSpan = agentSpan?.createChildSpan({ type: "model_generation", name: `llm: '${model.modelId}'`, attributes: { model: model.modelId, provider: model.provider, streaming: true }, metadata: { runId, threadId, resourceId }, requestContext }); return { runId, messageId, workflowInput: createWorkflowInput({ runId, agentId: publicAgentId, agentName: publicAgentName, messageList, tools, model, modelList: modelList ?? void 0, scorers, options: { maxSteps: execOptions?.maxSteps, toolChoice: execOptions?.toolChoice, activeTools: execOptions?.activeTools, modelSettings: execOptions?.modelSettings, requireToolApproval: typeof execOptions?.requireToolApproval === "function" ? true : execOptions?.requireToolApproval, toolCallConcurrency: execOptions?.toolCallConcurrency, autoResumeSuspendedTools: execOptions?.autoResumeSuspendedTools, maxProcessorRetries: execOptions?.maxProcessorRetries, includeRawChunks: execOptions?.includeRawChunks, returnScorerData: execOptions?.returnScorerData, hasErrorProcessors: errorProcessors.length > 0, providerOptions: execOptions?.providerOptions, structuredOutput: serializedStructuredOutput, skipBgTaskWait: execOptions?._skipBgTaskWait, disableBackgroundTasks: execOptions?.disableBackgroundTasks, tracingOptions: execOptions?.tracingOptions, actor: execOptions?.actor, instructionsOverride: execOptions?.instructions, systemMessage: execOptions?.system, transform: toolPayloadTransform?.targets ? { targets: toolPayloadTransform.targets } : void 0, isTaskComplete: execOptions?.isTaskComplete ? { scorerNames: execOptions.isTaskComplete.scorers?.map((s) => s.name).filter((n) => !!n), strategy: execOptions.isTaskComplete.strategy, timeout: execOptions.isTaskComplete.timeout, parallel: execOptions.isTaskComplete.parallel, suppressFeedback: execOptions.isTaskComplete.suppressFeedback } : void 0 }, state: { memoryConfig, threadId, resourceId, threadExists, savePerStep, observationalMemory }, messageId, agentSpanData: agentSpan?.exportSpan(), modelSpanData: modelSpan?.exportSpan(), requestContextEntries: requestContextEntriesSnapshot }), registryEntry: { tools, saveQueueManager, memory, model, modelList: modelList ? modelList.map((entry) => ({ id: entry.id, model: entry.model, maxRetries: entry.maxRetries ?? 0, enabled: entry.enabled ?? true, headers: entry.headers })) : void 0, workspace, requestContext, inputProcessors, llmRequestInputProcessors, outputProcessors, errorProcessors, processorStates, backgroundTaskManager, backgroundTasksConfig, agentSpan, modelSpan, stopWhen: execOptions?.stopWhen, onIterationComplete: execOptions?.onIterationComplete, prepareStep: execOptions?.prepareStep, toolPayloadTransform, isTaskComplete: execOptions?.isTaskComplete, requireToolApproval: execOptions?.requireToolApproval, drainPendingSignals: (scope) => typedAgent.__getDrainPendingSignals()(runId, scope), generateThreadTitle: memory ? async ({ threadId, resourceId, memoryConfig, messageListState, requestContext: rc, tracingContext }) => { const thread = await memory.getThreadById?.({ threadId }); const mergedConfig = memory.getMergedThreadConfig?.(memoryConfig); const { shouldGenerate, model, instructions, minMessages } = agent.resolveTitleGenerationConfig(mergedConfig?.generateTitle); if (!shouldGenerate || thread?.title) return; const titleMessageList = new require_message_list.MessageList().deserialize(messageListState); const uiMessages = agent.filterUiMessagesByThread(titleMessageList, threadId, titleMessageList.get.all.ui()); if (uiMessages.length < (minMessages ?? 1)) return; const userMessage = agent.getMostRecentUserMessage(uiMessages); if (!userMessage) return; const title = await agent.genTitle(userMessage, rc ?? new require_request_context.RequestContext(), require_observability.createObservabilityContext(tracingContext), model, instructions, uiMessages); if (!title) return; if (thread) await memory.updateThread({ id: threadId, title, metadata: thread.metadata ?? {}, memoryConfig }); else await memory.createThread({ threadId, resourceId, memoryConfig, title }); } : void 0, initialSignalEchoes: getInitialSignalEchoes(messageList), goal: agent.__getGoalConfig(), tripwire: tripwireData, callTimeHeaders: extractCallTimeHeaders(execOptions?.modelSettings), structuredOutput: execOptions?.structuredOutput?.schema ? { ...execOptions.structuredOutput, schema: (0, _mastra_schema_compat_schema.toStandardSchema)(execOptions.structuredOutput.schema) } : void 0, cleanup: () => {} }, messageList, threadId, resourceId }; } /** * Extract string-valued headers from `modelSettings.headers` for storage on the * in-process `RunRegistryEntry`. Returns `undefined` when no valid headers are * present so the registry slot stays empty rather than carrying an empty object. */ function extractCallTimeHeaders(modelSettings) { const raw = modelSettings?.headers; if (!raw || typeof raw !== "object" || Array.isArray(raw)) return void 0; const headers = {}; for (const [key, value] of Object.entries(raw)) if (typeof value === "string") headers[key] = value; return Object.keys(headers).length > 0 ? headers : void 0; } //#endregion //#region src/agent/durable/run-registry.ts /** * Global registry for accessing run entries from workflow steps. * This is necessary because workflow steps don't have direct access to * the DurableAgent instance's registry. * * Entries are keyed by runId (which are unique UUIDs). * * Uses TTLCache to prevent unbounded memory growth: entries auto-expire * after 10 minutes (refreshed on access) and the registry is hard-capped * at 1000 concurrent entries. */ const globalRunRegistry = new _isaacs_ttlcache.TTLCache({ max: 1e3, ttl: 600 * 1e3, updateAgeOnGet: true, dispose: (entry) => { entry?.cleanup?.(); }, noDisposeOnSet: true }); /** * End a run's root spans (MODEL_GENERATION then AGENT_RUN) with an error so the trace * still exports — stores persist only span-end events. After a resume the fresh resume * spans are the active root, so prefer them. Ending an already-ended span is a no-op, * so the duplicate error paths (workflow failure + emitError) are safe. Never throws. */ function endRunSpansWithError(runId, error) { try { const entry = globalRunRegistry.get(runId); (entry?.resumeModelSpan ?? entry?.modelSpan)?.error({ error, endSpan: true }); (entry?.resumeAgentSpan ?? entry?.agentSpan)?.error({ error, endSpan: true }); } catch {} } /** * Registry for per-run non-serializable state. * * During durable execution, the DurableAgent needs to store non-serializable * objects (tools with execute functions, SaveQueueManager, etc.) that can't * flow through workflow state. This registry provides a way to store and * retrieve these objects keyed by runId. * * The registry is scoped to a single DurableAgent instance and entries are * cleaned up when a run completes. */ var RunRegistry = class { #entries = /* @__PURE__ */ new Map(); /** * Register non-serializable state for a run * @param runId - The unique run identifier * @param entry - The registry entry containing tools, saveQueueManager, etc. */ register(runId, entry) { this.cleanup(runId); this.#entries.set(runId, entry); } /** * Get the registry entry for a run * @param runId - The unique run identifier * @returns The registry entry or undefined if not found */ get(runId) { return this.#entries.get(runId); } /** * Get tools for a specific run * @param runId - The unique run identifier * @returns The tools record or an empty object if not found */ getTools(runId) { return this.#entries.get(runId)?.tools ?? {}; } /** * Get SaveQueueManager for a specific run * @param runId - The unique run identifier * @returns The SaveQueueManager or undefined if not found */ getSaveQueueManager(runId) { return this.#entries.get(runId)?.saveQueueManager; } /** * Get the language model for a specific run * @param runId - The unique run identifier * @returns The MastraLanguageModel or undefined if not found */ getModel(runId) { return this.#entries.get(runId)?.model; } /** * Check if a run is registered * @param runId - The unique run identifier * @returns True if the run is registered */ has(runId) { return this.#entries.has(runId); } /** * Cleanup and remove a run's entry from the registry * @param runId - The unique run identifier */ cleanup(runId) { const entry = this.#entries.get(runId); if (entry) { entry.cleanup?.(); this.#entries.delete(runId); } } /** * Get the number of active runs in the registry */ get size() { return this.#entries.size; } /** * Get all active run IDs */ get runIds() { return Array.from(this.#entries.keys()); } /** * Clear all entries from the registry * Calls cleanup on each entry before removing */ clear() { for (const runId of this.#entries.keys()) this.cleanup(runId); } }; /** * Extended run registry that also stores MessageList references and memory info */ var ExtendedRunRegistry = class extends RunRegistry { #messageLists = /* @__PURE__ */ new Map(); #memoryInfo = /* @__PURE__ */ new Map(); /** * Register non-serializable state for a run including MessageList */ registerWithMessageList(runId, entry, messageList, memoryInfo) { this.register(runId, entry); this.#messageLists.set(runId, messageList); if (memoryInfo) this.#memoryInfo.set(runId, memoryInfo); } /** * Get MessageList for a specific run */ getMessageList(runId) { return this.#messageLists.get(runId); } /** * Get memory info for a specific run */ getMemoryInfo(runId) { return this.#memoryInfo.get(runId); } /** * Override cleanup to also remove MessageList and memory info */ cleanup(runId) { super.cleanup(runId); this.#messageLists.delete(runId); this.#memoryInfo.delete(runId); } /** * Override clear to also clear MessageLists and memory info */ clear() { super.clear(); this.#messageLists.clear(); this.#memoryInfo.clear(); } }; //#endregion //#region src/agent/durable/stream-adapter.ts /** * Map workflow usage (which may use legacy promptTokens/completionTokens) to * the canonical LanguageModelUsage shape (inputTokens/outputTokens). */ function normalizeUsage(raw) { if (!raw) return { inputTokens: 0, outputTokens: 0, totalTokens: 0 }; const inputTokens = raw.inputTokens ?? raw.promptTokens ?? 0; const outputTokens = raw.outputTokens ?? raw.completionTokens ?? 0; return { inputTokens, outputTokens, totalTokens: raw.totalTokens ?? inputTokens + outputTokens }; } /** * Create a MastraModelOutput that streams from pubsub events. * * This adapter subscribes to the agent stream pubsub channel and converts * pubsub events into a ReadableStream that MastraModelOutput can consume. * Callbacks are invoked as events arrive. */ function createDurableAgentStream(options) { const { pubsub, runId, messageId, model, threadId, resourceId, offset, idleTimeoutMs, isAlive, onChunk, onStepFinish, onFinish, onStreamFinished, onError, onSuspended, onAbort, onIterationComplete, logger, closeOnSuspend = false, structuredOutput, outputProcessors, experimentalTransform, messageList: externalMessageList } = options; const logError = (message, error) => { if (logger) logger.error(message, error); else console.error(message, error); }; const messageList = externalMessageList ?? new require_message_list.MessageList({ threadId, resourceId }); let isSubscribed = false; let cancelled = false; let terminated = false; let controller = null; let resolveReady; let rejectReady; const ready = new Promise((resolve, reject) => { resolveReady = resolve; rejectReady = reject; }); let lastErrorMessage; let idleTimer; let idleGeneration = 0; const clearIdleTimer = () => { idleGeneration += 1; if (idleTimer) { clearTimeout(idleTimer); idleTimer = void 0; } }; const markTerminated = () => { terminated = true; clearIdleTimer(); }; const onIdleTimeout = async (generation) => { idleTimer = void 0; if (cancelled || !controller || generation !== idleGeneration) return; if (isAlive) { let alive = true; try { alive = await isAlive(); } catch { alive = true; } if (cancelled || !controller || generation !== idleGeneration) return; if (alive) { armIdleTimer(); return; } } const error = /* @__PURE__ */ new Error(`Durable agent stream idle for ${idleTimeoutMs}ms with no live producer`); require_trip_wire.safeEnqueue(controller, { type: "error", payload: { error } }); require_trip_wire.safeClose(controller); markTerminated(); try { await onError?.({ error }); } catch (callbackError) { logError(`[DurableAgentStream] onError callback error:`, callbackError); } finally { cleanup(); } }; const armIdleTimer = () => { if (idleTimeoutMs === void 0 || idleTimeoutMs <= 0 || cancelled || terminated || !isSubscribed || !controller) return; clearIdleTimer(); const generation = idleGeneration; idleTimer = setTimeout(() => { onIdleTimeout(generation); }, idleTimeoutMs); }; const handleEvent = async (event) => { if (!controller) return; armIdleTimer(); const streamEvent = event; try { switch (streamEvent.type) { case require_agent.AgentStreamEventTypes.CHUNK: { const chunk = streamEvent.data; if (chunk.type === "error") { const errPayload = chunk.payload; lastErrorMessage = errPayload?.error?.message || errPayload?.message || "LLM execution error"; } require_trip_wire.safeEnqueue(controller, chunk); await onChunk?.(chunk); break; } case require_agent.AgentStreamEventTypes.STEP_START: { const chunk = streamEvent.data; if (chunk && "type" in chunk) require_trip_wire.safeEnqueue(controller, chunk); break; } case require_agent.AgentStreamEventTypes.STEP_FINISH: { const data = streamEvent.data; await onStepFinish?.(data); break; } case require_agent.AgentStreamEventTypes.FINISH: { const data = streamEvent.data; const finishChunk = { type: "finish", payload: { output: data.output, stepResult: data.stepResult } }; require_trip_wire.safeEnqueue(controller, finishChunk); require_trip_wire.safeClose(controller); markTerminated(); if (onFinish) try { const steps = data.output?.steps ?? []; const allToolResults = steps.flatMap((s) => s?.toolResults ?? []); const allToolCalls = steps.flatMap((s) => s?.toolCalls ?? []); await onFinish({ text: data.output?.text ?? "", steps, toolResults: allToolResults, toolCalls: allToolCalls, dynamicToolCalls: [], dynamicToolResults: [], staticToolCalls: [], staticToolResults: [], files: [], sources: [], reasoning: [], content: [], finishReason: data.stepResult?.reason ?? "stop", usage: normalizeUsage(data.output?.usage), totalUsage: normalizeUsage(data.output?.usage), warnings: data.stepResult?.warnings ?? [], request: { body: void 0 }, response: {}, reasoningText: void 0, providerMetadata: void 0 }); } catch (callbackError) { logError(`[DurableAgentStream] onFinish callback error:`, callbackError); } if (onAbort && data.stepResult?.reason === "abort") try { await onAbort({ steps: data.output?.steps ?? [] }); } catch (callbackError) { logError(`[DurableAgentStream] onAbort (from FINISH) callback error:`, callbackError); } if (onError && data.stepResult?.reason === "error") try { await onError({ error: new Error(lastErrorMessage || "LLM execution error") }); } catch (callbackError) { logError(`[DurableAgentStream] onError (from FINISH) callback error:`, callbackError); } try { await onStreamFinished?.(); } catch (callbackError) { logError(`[DurableAgentStream] onStreamFinished callback error:`, callbackError); } break; } case require_agent.AgentStreamEventTypes.ERROR: { const data = streamEvent.data; const error = new Error(data.error.message); error.name = data.error.name; if (data.error.stack) error.stack = data.error.stack; require_trip_wire.safeEnqueue(controller, { type: "error", payload: { error } }); require_trip_wire.safeClose(controller); markTerminated(); try { await onError?.({ error }); } catch (callbackError) { logError(`[DurableAgentStream] onError callback error:`, callbackError); } break; } case require_agent.AgentStreamEventTypes.SUSPENDED: { const data = streamEvent.data; if (closeOnSuspend) { markTerminated(); try { await onSuspended?.(data); } finally { require_trip_wire.safeClose(controller); } } else await onSuspended?.(data); break; } case require_agent.AgentStreamEventTypes.ABORT: { const data = streamEvent.data; markTerminated(); try { await onAbort?.(data); } catch (callbackError) { logError(`[DurableAgentStream] onAbort callback error:`, callbackError); } require_trip_wire.safeClose(controller); break; } case require_agent.AgentStreamEventTypes.ITERATION_COMPLETE: { const data = streamEvent.data; try { await onIterationComplete?.(data); } catch (callbackError) { logError(`[DurableAgentStream] onIterationComplete callback error:`, callbackError); } break; } default: break; } } catch (error) { logError(`[DurableAgentStream] Error handling event ${streamEvent.type}:`, error); } }; const stream = new stream_web.ReadableStream({ start(ctrl) { controller = ctrl; const topic = require_agent.AGENT_STREAM_TOPIC(runId); (offset !== void 0 ? pubsub.subscribeFromOffset(topic, offset, handleEvent) : pubsub.subscribeWithReplay(topic, handleEvent)).then(() => { if (cancelled) { pubsub.unsubscribe(topic, handleEvent).catch((error) => { logError(`[DurableAgentStream] Failed to unsubscribe from ${topic}:`, error); }); resolveReady(); return; } isSubscribed = true; armIdleTimer(); resolveReady(); }).catch((error) => { logError(`[DurableAgentStream] Failed to subscribe to ${topic}:`, error); rejectReady(error); ctrl.error(error); }); }, cancel() { cleanup(); } }); const cleanup = () => { markTerminated(); cancelled = true; if (isSubscribed) { isSubscribed = false; const topic = require_agent.AGENT_STREAM_TOPIC(runId); pubsub.unsubscribe(topic, handleEvent).catch((error) => { logError(`[DurableAgentStream] Failed to unsubscribe from ${topic}:`, error); }); } controller = null; }; return { output: new require_trip_wire.MastraModelOutput({ model, stream, messageList, messageId, options: { runId, onStepFinish, structuredOutput, isLLMExecutionStep: true, resolveFinalPromises: true, outputProcessors, experimentalTransform } }), cleanup, ready }; } /** * Helper to emit a chunk event to pubsub */ async function emitChunkEvent(pubsub, runId, chunk) { const topic = require_agent.AGENT_STREAM_TOPIC(runId); await pubsub.publish(topic, { type: require_agent.AgentStreamEventTypes.CHUNK, runId, data: chunk }); } /** * Helper to emit a step start event to pubsub. * The `data` payload must include `type: 'step-start'` so the stream-adapter * consumer recognises it as a `ChunkType` and enqueues it onto the client stream. */ async function emitStepStartEvent(pubsub, runId, data) { await pubsub.publish(require_agent.AGENT_STREAM_TOPIC(runId), { type: require_agent.AgentStreamEventTypes.STEP_START, runId, data: { type: "step-start", ...data } }); } /** * Helper to emit a step finish event to pubsub */ async function emitStepFinishEvent(pubsub, runId, data) { await pubsub.publish(require_agent.AGENT_STREAM_TOPIC(runId), { type: require_agent.AgentStreamEventTypes.STEP_FINISH, runId, data }); } /** * Helper to emit a finish event to pubsub */ async function emitFinishEvent(pubsub, runId, data) { await pubsub.publish(require_agent.AGENT_STREAM_TOPIC(runId), { type: require_agent.AgentStreamEventTypes.FINISH, runId, data }); } /** * Helper to emit an error event to pubsub */ async function emitErrorEvent(pubsub, runId, error) { await pubsub.publish(require_agent.AGENT_STREAM_TOPIC(runId), { type: require_agent.AgentStreamEventTypes.ERROR, runId, data: { error: { name: error.name, message: error.message } } }); } /** * Helper to emit a suspended event to pubsub */ async function emitSuspendedEvent(pubsub, runId, data) { await pubsub.publish(require_agent.AGENT_STREAM_TOPIC(runId), { type: require_agent.AgentStreamEventTypes.SUSPENDED, runId, data }); } /** * Helper to emit an iteration-complete event to pubsub */ async function emitIterationCompleteEvent(pubsub, runId, data) { await pubsub.publish(require_agent.AGENT_STREAM_TOPIC(runId), { type: require_agent.AgentStreamEventTypes.ITERATION_COMPLETE, runId, data }); } //#endregion //#region src/agent/durable/workflows/shared/schemas.ts /** * Shared Zod schemas for durable agentic workflows. * * These schemas are used by: * - Core DurableAgent workflow * - Inngest durable agent workflow * - Evented durable agent workflow (future) */ /** * Schema for model configuration */ const modelConfigSchema = zod.z.object({ provider: zod.z.string(), modelId: zod.z.string(), specificationVersion: zod.z.string().optional(), settings: zod.z.record(zod.z.string(), zod.z.any()).optional(), providerOptions: zod.z.record(zod.z.string(), zod.z.any()).optional() }); /** * Schema for model list entry (fallback support) */ const modelListEntrySchema = zod.z.object({ id: zod.z.string(), config: zod.z.object({ provider: zod.z.string(), modelId: zod.z.string(), specificationVersion: zod.z.string().optional(), originalConfig: zod.z.union([zod.z.string(), zod.z.record(zod.z.string(), zod.z.any())]).optional(), providerOptions: zod.z.record(zod.z.string(), zod.z.any()).optional() }), maxRetries: zod.z.number(), enabled: zod.z.boolean() }); /** * Schema for accumulated usage across iterations */ const accumulatedUsageSchema = zod.z.object({ inputTokens: zod.z.number(), outputTokens: zod.z.number(), totalTokens: zod.z.number() }); /** * Schema for output from the durable agentic workflow */ const durableAgenticOutputSchema = zod.z.object({ messageListState: zod.z.any(), messageId: zod.z.string(), stepResult: zod.z.any(), output: zod.z.object({ text: zod.z.string().optional(), usage: zod.z.any(), steps: zod.z.array(zod.z.any()) }), state: zod.z.any() }); /** * Base schema for durable agentic workflow input. * Implementations can extend this with additional fields. */ const baseDurableAgenticInputSchema = zod.z.object({ runId: zod.z.string(), agentId: zod.z.string(), agentName: zod.z.string().optional(), messageListState: zod.z.any(), toolsMetadata: zod.z.array(zod.z.any()), modelConfig: modelConfigSchema, options: zod.z.any(), state: zod.z.any(), messageId: zod.z.string() }); /** * Base schema for iteration state. * Implementations can extend this with additional fields. */ const baseIterationStateSchema = zod.z.object({ runId: zod.z.string(), agentId: zod.z.string(), agentName: zod.z.string().optional(), messageListState: zod.z.any(), toolsMetadata: zod.z.array(zod.z.any()), modelConfig: zod.z.any(), options: zod.z.any(), state: zod.z.any(), messageId: zod.z.string(), iterationCount: zod.z.number(), accumulatedSteps: zod.z.array(zod.z.any()), accumulatedUsage: accumulatedUsageSchema, lastStepResult: zod.z.any().optional(), backgroundTaskPending: zod.z.boolean().optional(), delegationBailed: zod.z.boolean().optional(), pendingFeedbackStop: zod.z.boolean().optional(), agentSpanData: zod.z.any().optional(), modelSpanData: zod.z.any().optional() }); //#endregion //#region src/agent/durable/workflows/shared/iteration-state.ts /** * Calculate accumulated usage from current state and new execution output. */ function calculateAccumulatedUsage(currentUsage, executionUsage) { return { inputTokens: currentUsage.inputTokens + (executionUsage?.inputTokens || 0), outputTokens: currentUsage.outputTokens + (executionUsage?.outputTokens || 0), totalTokens: currentUsage.totalTokens + (executionUsage?.totalTokens || 0) }; } /** * Build a step record from execution output. */ function buildStepRecord(executionOutput) { return { text: executionOutput.output.text, toolCalls: executionOutput.output.toolCalls, toolResults: executionOutput.toolResults, usage: executionOutput.output.usage, finishReason: executionOutput.stepResult.reason }; } /** * Create the base iteration state update. * * This returns the common fields for iteration state updates. * Implementations can extend this with their specific fields. * * @example * ```typescript * const baseUpdate = createBaseIterationStateUpdate({ * currentState: initData, * executionOutput, * }); * * // Core extends with modelList * const coreState = { ...baseUpdate, modelList: initData.modelList }; * * // Inngest extends with observability * const inngestState = { * ...baseUpdate, * agentSpanData: initData.agentSpanData, * modelSpanData: initData.modelSpanData, * stepIndex: initData.stepIndex + 1, * }; * ``` */ function createBaseIterationStateUpdate(input) { const { currentState, executionOutput } = input; const newUsage = calculateAccumulatedUsage(currentState.accumulatedUsage, executionOutput.output.usage); const stepRecord = buildStepRecord(executionOutput); return { runId: currentState.runId, agentId: currentState.agentId, agentName: currentState.agentName, messageListState: executionOutput.messageListState, toolsMetadata: currentState.toolsMetadata, modelConfig: currentState.modelConfig, options: currentState.options, state: executionOutput.state, messageId: executionOutput.messageId, iterationCount: currentState.iterationCount + 1, accumulatedSteps: [...currentState.accumulatedSteps, stepRecord], accumulatedUsage: newUsage, lastStepResult: executionOutput.stepResult, backgroundTaskPending: executionOutput.backgroundTaskPending, delegationBailed: executionOutput.delegationBailed, pendingFeedbackStop: currentState.pendingFeedbackStop, agentSpanData: currentState.agentSpanData, modelSpanData: currentState.modelSpanData }; } //#endregion //#region src/agent/durable/workflows/shared/tool-call-concurrency.ts /** * Resolves the effective tool-call foreach concurrency for a durable agentic * workflow from the serialized workflow input (iteration state) and the * step's tool calls. * * Mirrors @mastra/core's non-durable loop semantics * (loop/workflows/agentic-execution/tool-call-concurrency.ts): * - Global `requireToolApproval` forces sequential execution. The serialized * boolean shadow is `true` for function-form policies, so those degrade * safely to sequential as well. * - Any tool in the step's *effective active tool set* with `requireApproval` * or `hasSuspendSchema` forces sequential execution so approval/suspension * flows never race with concurrent tool calls. The check is against the * active tool set, NOT the tools the m