@mastra/core
Version:
907 lines • 43 kB
TypeScript
import type { MastraServerCache } from '../../cache/base.js';
import type { PubSub } from '../../events/pubsub.js';
import type { Mastra } from '../../mastra/index.js';
import type { FullOutput, MastraModelOutput } from '../../stream/base/output.js';
import type { ChunkType, MastraOnFinishCallback, MastraStreamTransformOptions } from '../../stream/types.js';
import type { WorkflowRunStatus } from '../../workflows/types.js';
import { Agent } from '../agent.js';
import type { AgentExecutionOptions } from '../agent.types.js';
import type { MessageListInput } from '../message-list/index.js';
import type { ToolsInput } from '../types.js';
import { ExtendedRunRegistry } from './run-registry.js';
import type { AgentStepFinishEventData, AgentSuspendedEventData, DurableAgenticWorkflowInput } from './types.js';
import { createDurableAgenticWorkflow } from './workflows/index.js';
/**
* Options for DurableAgent.stream()
*/
export interface DurableAgentStreamOptions<OUTPUT = undefined> {
/** Custom instructions that override the agent's default instructions for this execution */
instructions?: AgentExecutionOptions<OUTPUT>['instructions'];
/** Additional context messages to provide to the agent */
context?: AgentExecutionOptions<OUTPUT>['context'];
/** Memory configuration for conversation persistence and retrieval */
memory?: AgentExecutionOptions<OUTPUT>['memory'];
/** Unique identifier for this execution run */
runId?: string;
/** Request Context containing dynamic configuration and state */
requestContext?: AgentExecutionOptions<OUTPUT>['requestContext'];
/** Maximum number of steps to run */
maxSteps?: number;
/**
* Conditions for stopping execution (e.g., step count, token limit).
*
* The predicate is non-serializable, so it's parked on the in-process run
* registry and evaluated by the durable loop on every iteration. Cross-process
* durable engines (e.g. Inngest after a worker restart) cannot recover the
* closure and degrade to `maxSteps` only.
*/
stopWhen?: AgentExecutionOptions<OUTPUT>['stopWhen'];
/** Additional tool sets that can be used for this execution */
toolsets?: AgentExecutionOptions<OUTPUT>['toolsets'];
/** Client-side tools available during execution */
clientTools?: AgentExecutionOptions<OUTPUT>['clientTools'];
/** Tool selection strategy */
toolChoice?: AgentExecutionOptions<OUTPUT>['toolChoice'];
/** Tool names enabled for this execution */
activeTools?: AgentExecutionOptions<OUTPUT>['activeTools'];
/** Model-specific settings like temperature */
modelSettings?: AgentExecutionOptions<OUTPUT>['modelSettings'];
/** Require approval for tool calls. Boolean (gate all / none) or a per-call function policy. */
requireToolApproval?: AgentExecutionOptions<OUTPUT>['requireToolApproval'];
/** Automatically resume suspended tools */
autoResumeSuspendedTools?: boolean;
/** Maximum number of tool calls to execute concurrently */
toolCallConcurrency?: number;
/** Whether to include raw chunks in the stream output */
includeRawChunks?: boolean;
/** Experimental transforms applied whenever `fullStream` is consumed. */
experimentalTransform?: MastraStreamTransformOptions<OUTPUT>;
/** Maximum processor retries */
maxProcessorRetries?: number;
/** Structured output configuration */
structuredOutput?: AgentExecutionOptions<OUTPUT>['structuredOutput'];
/** Version overrides for sub-agent delegation */
versions?: AgentExecutionOptions<OUTPUT>['versions'];
/** Callback when chunk is received */
onChunk?: (chunk: ChunkType<OUTPUT>) => void | Promise<void>;
/** Callback when step finishes */
onStepFinish?: (result: AgentStepFinishEventData) => void | Promise<void>;
/** Callback when execution finishes — receives rich step data (text, steps, toolResults) */
onFinish?: MastraOnFinishCallback<OUTPUT>;
/** Callback on error */
onError?: ({ error }: {
error: Error | string;
}) => void | Promise<void>;
/** Callback when workflow suspends (e.g., for tool approval) */
onSuspended?: (data: AgentSuspendedEventData) => void | Promise<void>;
/** Callback when execution is aborted via abortSignal */
onAbort?: AgentExecutionOptions<OUTPUT>['onAbort'];
/** Callback fired after each agentic-loop iteration */
onIterationComplete?: AgentExecutionOptions<OUTPUT>['onIterationComplete'];
/** Additional system message appended after context but before user messages. */
system?: AgentExecutionOptions<OUTPUT>['system'];
/** When true, background tasks are disabled for this run. */
disableBackgroundTasks?: AgentExecutionOptions<OUTPUT>['disableBackgroundTasks'];
/** Tracing options forwarded to the agent/model spans. */
tracingOptions?: AgentExecutionOptions<OUTPUT>['tracingOptions'];
/** Per-call actor signal forwarded to FGA checks and tool execution. */
actor?: AgentExecutionOptions<OUTPUT>['actor'];
/**
* Per-invocation tool payload transform policy. The closure rides on the
* in-process run registry; only the JSON-safe `targets` shadow is serialized
* for cross-process engines.
*/
transform?: AgentExecutionOptions<OUTPUT>['transform'];
/**
* Per-step preparation hook. Closure-only: stored on the in-process run
* registry and invoked as a `PrepareStepProcessor` at the start of every
* iteration. Cross-process resumes lose the hook.
*/
prepareStep?: AgentExecutionOptions<OUTPUT>['prepareStep'];
/**
* Per-call `isTaskComplete` policy. Scorer instances and `onComplete` are
* closure-only and live on the in-process run registry; the JSON-safe
* primitives (`strategy`, `timeout`, `parallel`, `suppressFeedback`,
* `scorerNames`) are serialized for cross-process observability.
*/
isTaskComplete?: AgentExecutionOptions<OUTPUT>['isTaskComplete'];
/**
* Sub-agent delegation hooks (`onDelegationStart`, `onDelegationComplete`,
* `messageFilter`, etc.). The callbacks are forwarded into `convertTools`
* at prepare time and burned into the sub-agent `CoreTool` wrappers on the
* in-process run registry. Cross-process resumes lose the callbacks (only
* `includeSubAgentToolResultsInModelContext` would be JSON-safe), so a
* fresh worker degrades to default delegation behaviour.
*/
delegation?: AgentExecutionOptions<OUTPUT>['delegation'];
/**
* When set, `stream()` delegates to the idle-loop wrapper that keeps the
* outer stream open across background-task continuations — the same
* behaviour as the now-deprecated `streamUntilIdle()`.
*
* Pass `true` for default idle timeout (5 min), or `{ maxIdleMs }` to
* customise.
*
* @example
* ```typescript
* const { output, cleanup } = await durableAgent.stream('Research topic', {
* untilIdle: true,
* memory: { thread: 't1', resource: 'u1' },
* });
* ```
*/
untilIdle?: boolean | {
maxIdleMs?: number;
};
/** When true, the in-loop background task check step skips waiting (streamUntilIdle sets this) */
_skipBgTaskWait?: boolean;
/**
* External abort signal. The durable agent always installs its own internal
* `AbortController` for the run; when this signal is provided, its `abort`
* event is forwarded to the internal controller so either source can cancel
* the run.
*
* Cross-process resumes (e.g. Inngest after a worker restart) cannot
* recover the signal — call `resume(runId, ..., { abortSignal })` with a
* fresh signal on each segment if you need abortability post-resume.
*/
abortSignal?: AbortSignal;
}
type DurableAgentResumeOptions<OUTPUT = undefined> = DurableAgentStreamOptions<OUTPUT> & {
toolCallId?: string;
};
/**
* Result from DurableAgent.stream()
*/
export interface DurableAgentStreamResult<OUTPUT = undefined> {
/** The streaming output */
output: MastraModelOutput<OUTPUT>;
/** The full stream - delegates to output.fullStream for server compatibility */
readonly fullStream: ReadableStream<any>;
/** The unique run ID for this execution */
runId: string;
/** Thread ID if using memory */
threadId?: string;
/** Resource ID if using memory */
resourceId?: string;
/** Cleanup function to call when done (unsubscribes from pubsub) */
cleanup: () => void;
/**
* Abort the run. Flips the internal `AbortController` for this run, which
* surfaces as an `AbortError` inside the durable LLM-execution step and
* is bridged to the user's `onAbort` callback via the run's pubsub topic.
*
* Safe to call after the run has already finished — it's a no-op in that
* case.
*/
abort: (reason?: unknown) => void;
}
/**
* Configuration for DurableAgent - wraps an existing Agent with durable execution
*/
export interface DurableAgentConfig<TAgentId extends string = string, TTools extends ToolsInput = ToolsInput, TOutput = undefined> {
/**
* The Agent to wrap with durable execution capabilities.
* All agent methods (getModel, listTools, etc.) delegate to this agent.
*/
agent: Agent<TAgentId, TTools, TOutput>;
/**
* Optional ID override. Defaults to agent.id.
*/
id?: TAgentId;
/**
* Optional name override. Defaults to agent.name.
*/
name?: string;
/**
* PubSub instance for streaming events.
* Optional - if not provided, defaults to EventEmitterPubSub.
*/
pubsub?: PubSub;
/**
* Cache instance for storing stream events.
* Enables resumable streams - clients can disconnect and reconnect
* without missing events.
*
* - If not provided: Inherits from Mastra instance, or uses InMemoryServerCache
* - If provided: Uses the provided cache backend (e.g., Redis)
* - If set to `false`: Disables caching (streams are not resumable)
*/
cache?: MastraServerCache | false;
/**
* Maximum steps for the agentic loop.
* Defaults to the workflow default if not specified.
*/
maxSteps?: number;
/**
* Timeout in milliseconds before automatic cleanup of registry entries
* after a stream finishes or errors. This provides a grace period for
* late observers to access the stream.
*
* Defaults to 30000 (30 seconds).
* Set to 0 to disable auto-cleanup (manual cleanup() required).
*/
cleanupTimeoutMs?: number;
}
/**
* DurableAgent wraps an existing Agent with durable execution capabilities.
*
* Key features:
* 1. Resumable streams - clients can disconnect and reconnect without missing events
* 2. Serializable workflow inputs - works with durable execution engines
* 3. PubSub-based streaming - events flow through pubsub for distribution
*
* DurableAgent extends Agent, delegating most methods to the wrapped agent.
* It overrides stream() to use durable execution with the agentic workflow.
*
* Subclasses (EventedAgent, InngestAgent) override executeWorkflow() to
* customize how the workflow is executed.
*
* @example
* ```typescript
* import { Agent } from '@mastra/core/agent';
* import { DurableAgent } from '@mastra/core/agent/durable';
*
* const agent = new Agent({
* id: 'my-agent',
* instructions: 'You are a helpful assistant',
* model: openai('gpt-4'),
* });
*
* const durableAgent = new DurableAgent({ agent });
*
* const { output, runId, cleanup } = await durableAgent.stream('Hello!');
* const text = await output.text;
* cleanup();
* ```
*/
/**
* Statuses of durable agent runs discoverable via {@link DurableAgent.listActiveRuns}.
*
* `running` is the status reported by the workflow engine while the durable
* agent's agentic loop is actively executing (i.e. between suspend
* boundaries). Persisted `running` snapshots are the recovery source for runs
* orphaned by a process restart.
*/
export type DurableAgentActiveRunStatus = Extract<WorkflowRunStatus, 'running'>;
/**
* Filters for {@link DurableAgent.listActiveRuns}. Mirrors the
* `listWorkflowRuns` filter contract, plus the agent-level `threadId` /
* `resourceId` filters used by the base {@link Agent.listSuspendedRuns}.
*/
export interface DurableAgentListActiveRunsOptions {
/** Only return runs that belong to this memory thread. */
threadId?: string;
/** Only return runs that belong to this memory resource. */
resourceId?: string;
/** Only return runs created at or after this date. */
fromDate?: Date;
/** Only return runs created at or before this date. */
toDate?: Date;
/**
* Number of items per page. Pagination is applied when both `perPage` and
* `page` are provided; otherwise all matching runs are returned.
*/
perPage?: number;
/** Zero-indexed page number. */
page?: number;
}
/**
* A durable agent run currently reported as `running` in workflow snapshot
* storage. These are the runs that a boot-time or operator-initiated
* recovery would re-drive after a process restart.
*/
export interface DurableAgentActiveRun {
/** Run ID accepted by {@link DurableAgent.recoverActiveRuns} and workflow `restart`. */
runId: string;
status: DurableAgentActiveRunStatus;
threadId?: string;
resourceId?: string;
/** When the run's snapshot was last persisted while running. */
updatedAt: Date;
}
export interface DurableAgentListActiveRunsResult {
runs: DurableAgentActiveRun[];
/** Total number of matching runs, before pagination. */
total: number;
}
/**
* Outcome of a single run restart attempted by
* {@link DurableAgent.recoverActiveRuns}. `success` means `run.restart()`
* returned; `failed` means it threw and the error was captured so recovery
* of remaining runs could proceed.
*/
export interface DurableAgentRecoveredRun {
runId: string;
status: 'success' | 'failed';
/** Populated only when `status === 'failed'`. */
error?: Error;
}
/**
* Filters for {@link DurableAgent.recoverActiveRuns}. Reuses the
* {@link DurableAgentListActiveRunsOptions} discovery filters and adds an
* escape hatch for targeting a specific run ID.
*/
export interface DurableAgentRecoverActiveRunsOptions extends DurableAgentListActiveRunsOptions {
/**
* Recover a specific run by ID. When set, the discovery filters and
* pagination fields are ignored. Useful when the caller already knows the
* run ID from another source (e.g. their own bookkeeping).
*/
runId?: string;
}
export interface DurableAgentRecoverActiveRunsResult {
recovered: DurableAgentRecoveredRun[];
/** Number of runs that restarted successfully. */
succeeded: number;
/** Number of runs whose restart threw. */
failed: number;
}
/**
* Options for {@link DurableAgent.recover}, a single-run streamable recovery
* counterpart to {@link DurableAgent.resume}.
*
* `recover()` rebuilds the run's non-serializable state from the persisted
* workflow snapshot (message list, model, tools, memory, saveQueueManager,
* request context, agent span) and returns a fresh {@link DurableAgentStreamResult}
* whose `fullStream` observes the recovered run through pubsub. Callbacks
* mirror `stream()` / `resume()`.
*/
export interface DurableAgentRecoverOptions<OUTPUT = undefined> {
/** Callback when chunk is received */
onChunk?: (chunk: ChunkType<OUTPUT>) => void | Promise<void>;
/** Experimental transforms applied whenever `fullStream` is consumed. */
experimentalTransform?: MastraStreamTransformOptions<OUTPUT>;
/** Callback when a step finishes */
onStepFinish?: (result: AgentStepFinishEventData) => void | Promise<void>;
/** Callback when the recovered run finishes */
onFinish?: MastraOnFinishCallback<OUTPUT>;
/** Callback when the recovered run errors */
onError?: ({ error }: {
error: Error | string;
}) => void | Promise<void>;
/** Callback when the recovered run suspends again */
onSuspended?: (data: AgentSuspendedEventData) => void | Promise<void>;
/**
* Optional abort signal for the recovered segment. Forwarded onto a fresh
* internal `AbortController` installed on the run's registry entry, so
* `result.abort()` and the external signal can both cancel the recovered run.
*/
abortSignal?: AbortSignal;
}
export declare class DurableAgent<TAgentId extends string = string, TTools extends ToolsInput = ToolsInput, TOutput = undefined> extends Agent<TAgentId, TTools, TOutput> {
#private;
/**
* Create a new DurableAgent that wraps an existing Agent
*/
constructor(config: DurableAgentConfig<TAgentId, TTools, TOutput>);
/**
* Get the resolved cache instance.
* Lazily initialized to allow inheriting from Mastra.
*/
get cache(): MastraServerCache | null;
/**
* Get the PubSub instance.
* Returns CachingPubSub if caching is enabled, otherwise the inner pubsub.
*/
get pubsub(): PubSub;
/**
* Get the wrapped agent instance.
*/
get agent(): Agent<TAgentId, TTools, TOutput>;
/**
* Get the run registry (for testing and advanced usage)
*/
get runRegistry(): ExtendedRunRegistry;
/**
* Get the max steps configured for this agent
*/
get maxSteps(): number | undefined;
/**
* Get the cleanup timeout in milliseconds.
* Returns 0 if auto-cleanup is disabled.
*/
get cleanupTimeoutMs(): number;
getModel(options?: any): import("../../_types/@internal_ai-sdk-v4/dist/index.d.ts").LanguageModelV1 | import("..").MastraLanguageModel | Promise<import("../../_types/@internal_ai-sdk-v4/dist/index.d.ts").LanguageModelV1 | import("..").MastraLanguageModel>;
getLLM(options?: any): import("..").MastraLLM | Promise<import("..").MastraLLM>;
getModelList(requestContext?: any): Promise<import("..").AgentModelManagerConfig[] | null>;
getInstructions(options?: any): import("../../llm").SystemMessage | Promise<import("../../llm").SystemMessage>;
getDescription(): string;
getMetadata(options?: any): Record<string, unknown> | Promise<Record<string, unknown> | undefined> | undefined;
getTracingPolicy(): import("../../observability").TracingPolicy | undefined;
listTools(options?: any): Promise<TTools>;
getConfiguredToolHooks(): import("../../tools").ToolHooks<unknown, unknown, unknown, Record<string, unknown>> | undefined;
getDefaultOptions(options?: any): AgentExecutionOptions<TOutput> | Promise<AgentExecutionOptions<TOutput>>;
getDefaultGenerateOptionsLegacy(options?: any): import("..").AgentGenerateOptions | Promise<import("..").AgentGenerateOptions>;
getDefaultStreamOptionsLegacy(options?: any): import("..").AgentStreamOptions | Promise<import("..").AgentStreamOptions>;
getDefaultNetworkOptions(options?: any): import("..").NetworkOptions | Promise<import("..").NetworkOptions>;
getMemory(options?: any): Promise<import("../../memory").MastraMemory | undefined>;
hasOwnMemory(): boolean;
getWorkspace(options?: any): Promise<import("../../workspace").AnyWorkspace | undefined>;
hasOwnWorkspace(): boolean;
getVoice(options?: any): Promise<import("../../_types/@internal_voice/dist/index.d.ts").MastraVoice<unknown, unknown, unknown, import("../../_types/@internal_voice/dist/index.d.ts").ToolsInput, import("../../_types/@internal_voice/dist/index.d.ts").VoiceEventMap, unknown>>;
get voice(): import("../../_types/@internal_voice/dist/index.d.ts").MastraVoice<unknown, unknown, unknown, import("../../_types/@internal_voice/dist/index.d.ts").ToolsInput, import("../../_types/@internal_voice/dist/index.d.ts").VoiceEventMap, unknown>;
get requestContextSchema(): import("@mastra/schema-compat").StandardSchemaWithJSON<unknown> | undefined;
getConfiguredProcessorWorkflows(): Promise<import("../../processors").ProcessorWorkflow[]>;
listInputProcessors(requestContext?: any): Promise<import("../../processors").InputProcessorOrWorkflow[]>;
listOutputProcessors(requestContext?: any): Promise<import("../../processors").OutputProcessorOrWorkflow[]>;
listErrorProcessors(requestContext?: any): Promise<import("../../processors").ErrorProcessorOrWorkflow[]>;
resolveProcessorById<TId extends string = string>(processorId: TId, requestContext?: any): Promise<import("../../processors").Processor<TId, unknown> | null>;
listConfiguredInputProcessors(requestContext?: any): Promise<import("../../processors").InputProcessorOrWorkflow[]>;
listConfiguredOutputProcessors(requestContext?: any): Promise<import("../../processors").OutputProcessorOrWorkflow[]>;
getConfiguredProcessorIds(requestContext?: any): Promise<{
inputProcessorIds: string[];
outputProcessorIds: string[];
errorProcessorIds: string[];
}>;
listAgents(options?: any): Record<string, import("..").SubAgent<string, unknown>> | Promise<Record<string, import("..").SubAgent<string, unknown>>>;
__getStaticAgents(): Record<string, import("..").SubAgent<string, unknown>> | undefined;
__hasSubAgentsConfigured(): boolean;
listWorkflows(options?: any): Promise<Record<string, import("../../workflows").AnyWorkflow>>;
getSkill(skillName: string, options?: any): Promise<import("../../workspace").Skill | null>;
listSkills(options?: any): Promise<import("../../workspace").SkillMetadata[]>;
listScorers(options?: any): Promise<import("../../evals").MastraScorers>;
getBackgroundTasksConfig(): import("../../background-tasks").AgentBackgroundConfig | undefined;
disableBackgroundTasks(): void;
enableBackgroundTasks(): void;
getToolPayloadTransform(): import("../../tools").ToolPayloadTransformPolicy | undefined;
__getGoalConfig(): import("..").GoalConfig | undefined;
get browser(): import("..").MastraBrowser | undefined;
setBrowser(browser: any): void;
hasOwnBrowser(): boolean;
getChannels(): import("../../channels").AgentChannels | null;
setChannels(agentChannels: any): void;
hasOwnPubSub(): boolean;
__setMemory(memory: any): void;
__setPubSub(pubsub: any): void;
__setWorkspace(workspace: any): void;
__getEditorConfig(): import("..").AgentEditorConfig | undefined;
__getOverridableFields(): {
instructions: import("../../types").DynamicArgument<import("../../llm").SystemMessage, unknown>;
model: {
id: string;
model: import("../../types").DynamicArgument<import("../../llm").MastraModelConfig>;
maxRetries: number;
enabled: boolean;
modelSettings?: import("../../types").DynamicArgument<import("..").ModelFallbackSettings>;
providerOptions?: import("../../types").DynamicArgument<import("../../llm/model/provider-options").ProviderOptions>;
headers?: import("../../types").DynamicArgument<Record<string, string>>;
}[] | import("../../types").DynamicArgument<import("../../llm").MastraModelConfig | import("..").ModelWithRetries[], unknown>;
tools: import("../../types").DynamicArgument<TTools, unknown>;
workspace: import("../../types").DynamicArgument<import("../../workspace").AnyWorkspace | undefined, unknown>;
};
__updateInstructions(instructions: Parameters<Agent<TAgentId, TTools, TOutput>['__updateInstructions']>[0]): void;
__updateModel(config: Parameters<Agent<TAgentId, TTools, TOutput>['__updateModel']>[0]): void;
__setTools(tools: Parameters<Agent<TAgentId, TTools, TOutput>['__setTools']>[0]): void;
/**
* Create a per-request clone for applying stored editor overrides.
*
* The base `Agent.__fork()` builds a bare `new Agent(...)`, which for a
* DurableAgent would drop the wrapped agent and every delegating override
* (tools, model, memory, voice, durable streaming) — the served fork ends up a
* plain `Agent` with no tools. Instead, fork the wrapped agent (so overrides
* applied to this fork don't mutate the singleton) and re-wrap it in the same
* durable subclass, preserving pubsub/cache/run configuration.
*
* @internal
*/
__fork(): Agent<TAgentId, TTools, TOutput>;
/**
* Get the PubSub instance for use by subclasses.
* @internal
*/
protected get pubsubInternal(): PubSub;
/**
* Get the run registry for use by subclasses.
* @internal
*/
protected get runRegistryInternal(): ExtendedRunRegistry;
/**
* Execute the durable workflow.
*
* Subclasses override this method to customize how the workflow is executed:
* - DurableAgent (this): Runs the workflow directly via createRun + start
* - EventedAgent: Uses run.startAsync() for fire-and-forget execution
* - InngestAgent: Uses inngest.send() to trigger Inngest function
*
* @param runId - The unique run ID
* @param workflowInput - The serialized workflow input
* @internal
*/
protected executeWorkflow(runId: string, workflowInput: DurableAgenticWorkflowInput): Promise<void>;
/**
* Create the durable workflow for this agent.
*
* Subclasses can override this method to use a different workflow implementation:
* - DurableAgent (this): Uses createDurableAgenticWorkflow()
* - InngestAgent: Uses createInngestDurableAgenticWorkflow()
*
* @internal
*/
protected createWorkflow(): ReturnType<typeof createDurableAgenticWorkflow>;
/**
* Emit an error event to pubsub.
*
* @param runId - The run ID
* @param error - The error to emit
* @internal
*/
protected emitError(runId: string, error: Error): Promise<void>;
/**
* Delete the persisted workflow snapshot rows for a completed durable run.
*
* A durable agent write two rows per run: one for the outer `AGENTIC_LOOP`
* workflow and one for the nested `AGENTIC_EXECUTION` workflow (persisted
* under the same `runId`). Once the run reaches a non-suspended terminal
* state neither row is needed again — leaving them behind fills snapshot
* storage with stale `pending`/`running` rows for every completed run and
* pollutes `listActiveRuns` / `recoverActiveRuns` on the next boot.
*
* Best-effort: a cleanup failure must never turn a finished run into an
* error — a stale row is preferable to a broken exit path.
*
* @internal
*/
protected deleteRunSnapshots(runId: string): Promise<void>;
/**
* Stream a response from the agent using durable execution.
*/
stream(messages: MessageListInput, options?: DurableAgentStreamOptions<TOutput>): Promise<DurableAgentStreamResult<TOutput>>;
/**
* Resume a suspended workflow execution.
*/
resume(runId: string, resumeData: unknown, options?: DurableAgentResumeOptions<TOutput>): Promise<DurableAgentStreamResult<TOutput>>;
/**
* Recover a single durable run whose in-process agentic loop was orphaned by
* a process restart. Streamable counterpart to
* {@link DurableAgent.recoverActiveRuns} — where the bulk API only re-drives
* the workflow and returns counts, `recover()` rebuilds the run's
* non-serializable state (message list, model, tools, memory,
* saveQueueManager, request context, agent span) from the persisted workflow
* snapshot and returns a fresh {@link DurableAgentStreamResult} whose
* `fullStream` observes the recovered run through pubsub.
*
* Because the rebuilt registry entry carries `memory` + `saveQueueManager`,
* the durable agentic workflow's terminal step will flush new messages to
* memory just like a fresh `stream()` call would. The single-run form is
* useful when operators want to attach listeners to a specific recovered
* run; for boot-time bulk recovery of every orphaned run, use
* `recoverActiveRuns()`.
*
* @example
* ```typescript
* const { fullStream, output, cleanup } = await durableAgent.recover(runId, {
* onChunk: chunk => process.stdout.write(chunk.payload?.text ?? ''),
* });
* for await (const chunk of fullStream) {
* // ...
* }
* cleanup();
* ```
*/
recover(runId: string, options?: DurableAgentRecoverOptions<TOutput>): Promise<DurableAgentStreamResult<TOutput>>;
/**
* Override the inherited `resumeStream()` so that callers using the base
* `Agent` API (including `approveToolCall` / `declineToolCall`) are routed
* through the durable `resume()` path instead of the regular Agent's
* snapshot-based resume.
*
* Returns just the `MastraModelOutput` (matching the base Agent's return
* type) while internally delegating to `this.resume()`.
*/
resumeStream(resumeData: any, streamOptions?: any): Promise<MastraModelOutput<TOutput>>;
/**
* Override the inherited `approveToolCall()` to route through the durable
* `resume()` path.
*/
approveToolCall(options: {
runId: string;
toolCallId?: string;
} & Record<string, any>): Promise<MastraModelOutput<any>>;
/**
* Override the inherited `declineToolCall()` to route through the durable
* `resume()` path.
*/
declineToolCall(options: {
runId: string;
toolCallId?: string;
} & Record<string, any>): Promise<MastraModelOutput<any>>;
approveToolCallGenerate<OUTPUT = undefined>(options: AgentExecutionOptions<OUTPUT> & {
runId: string;
toolCallId?: string;
}): Promise<Awaited<ReturnType<MastraModelOutput<OUTPUT>['getFullOutput']>>>;
declineToolCallGenerate<OUTPUT = undefined>(options: AgentExecutionOptions<OUTPUT> & {
runId: string;
toolCallId?: string;
}): Promise<Awaited<ReturnType<MastraModelOutput<OUTPUT>['getFullOutput']>>>;
/**
* Generate a complete response from the agent using durable execution.
*
* Drains the underlying durable stream to completion and returns the same
* {@link FullOutput} shape as non-durable `Agent.generate`. The underlying
* workflow is identical to `stream()` — it just collects the final result
* for callers that don't want to consume chunks themselves.
*
* This method intentionally re-implements the `stream()` setup rather than
* delegating to `this.stream(...)` so that `prepareForDurableExecution` (and
* downstream `convertTools`) receives `methodType: 'generate'`. Tool
* factories that vary their `CoreTool` output based on the calling method
* (e.g. `clientTools` vs server-side tools) rely on this signal — calling
* `stream()` here would silently pass `methodType: 'stream'`.
*
* If the run suspends (e.g. tool approval or `suspend()` from a tool), the
* returned output's `finishReason` will be `'suspended'` and
* `suspendPayload` will be populated. Use {@link DurableAgent.resumeGenerate}
* to continue.
*
* Note on suspend persistence: for the base `DurableAgent`, the workflow
* engine's `run.start()` only resolves after the suspend snapshot is
* persisted, so awaiting `workflowExecution` on suspend is sufficient for
* a subsequent `resumeGenerate()` to find the snapshot. Subclasses like
* `EventedAgent` use a fire-and-forget `run.startAsync()` and therefore
* cannot rely on this await for snapshot durability — see the
* `EventedAgent` docs for the recommended pattern.
*/
generate(messages: MessageListInput, options?: DurableAgentStreamOptions<TOutput>): Promise<FullOutput<TOutput>>;
/**
* Resume a suspended durable run and drain it to a single
* {@link FullOutput}. Mirrors {@link Agent.resumeGenerate} on top of
* {@link DurableAgent.resume}.
*
* Unlike `generate()`, this delegates to `resume()` because resume reads
* its tools from the existing run-registry entry rather than running
* `prepareForDurableExecution` again — there is no `methodType` to thread
* through. The same `EventedAgent` caveat about fire-and-forget snapshot
* persistence noted on `generate()` applies if the resumed turn suspends.
*/
resumeGenerate(runId: string, resumeData: unknown, options?: Parameters<DurableAgent<TAgentId, TTools, TOutput>['resume']>[2]): Promise<FullOutput<TOutput>>;
/**
* List durable agent runs currently reported as `running` in workflow
* snapshot storage.
*
* A `running` snapshot is a durable agent run whose agentic loop was
* mid-execution the last time the workflow engine persisted its state. On a
* healthy process these transition to `suspended` (waiting on
* tool approval / resume) or a terminal status. On a crashed / restarted
* process they are orphaned in the `running` state with no in-process
* driver — this is the discovery API used to enumerate them for recovery
* (see {@link DurableAgent.recoverActiveRuns} and workflow `restart`).
*
* Requires persistent workflow storage. Filters `agentId` against the
* persisted `DurableAgenticWorkflowInput.agentId`, so runs started by other
* durable agents sharing the same storage are not surfaced.
*
* @example
* ```typescript
* const { runs } = await durableAgent.listActiveRuns({ resourceId });
* for (const run of runs) {
* await durableAgent.recoverActiveRuns({ runId: run.runId });
* }
* ```
*/
listActiveRuns(options?: DurableAgentListActiveRunsOptions): Promise<DurableAgentListActiveRunsResult>;
/**
* Bulk recover durable agent runs whose in-process agentic loop was orphaned
* by a process restart. This is the recovery half of the discovery API
* paired with {@link DurableAgent.listActiveRuns} and is the typical
* boot-time hook.
*
* Each targeted run is delegated to {@link DurableAgent.recover}, which
* rebuilds the run's non-serializable state (message list, model, memory,
* save-queue manager, request context, agent span), re-subscribes to the
* run's pubsub topic, and restarts the workflow in the background. Because
* `recover()` registers `memory` + `saveQueueManager` on the run entry, the
* durable agentic workflow's terminal step flushes new messages to memory
* just like a fresh `stream()` call would.
*
* The per-run stream returned by `recover()` is discarded — this method
* awaits each run's workflow settlement and reports summary counts instead
* of surfacing live event streams. Callers who want to observe a specific
* recovered run's events should use {@link DurableAgent.recover} directly
* (or {@link DurableAgent.observe} with the returned `runId`).
*
* Failures are captured per-run so a single bad run does not block
* recovery of the rest.
*
* @example
* ```typescript
* // Recover every orphaned run for this agent (typical boot-time hook).
* const { recovered, succeeded, failed } = await durableAgent.recoverActiveRuns();
* logger.info('Recovered durable agent runs', { succeeded, failed });
*
* // Recover a single run by ID.
* await durableAgent.recoverActiveRuns({ runId });
* ```
*/
recoverActiveRuns(options?: DurableAgentRecoverActiveRunsOptions): Promise<DurableAgentRecoverActiveRunsResult>;
/**
* Observe an existing stream.
* Use this to reconnect to a stream after a network disconnection.
*
* **Warning:** The returned `cleanup()` function destroys the run's registry
* entries and cached PubSub events. Only call it when you are done with the
* run entirely. If the workflow is suspended and you intend to resume later,
* do not call cleanup — let the auto-cleanup timer handle it after
* FINISH/ERROR. Auto-cleanup does not fire on SUSPENDED events.
*
* Pass `idleTimeoutMs` to bound how long the stream waits on a silent topic:
* a durable run whose driving process crashed stops emitting chunks but never
* publishes a terminal event, so without this `observe()` hangs forever on a
* producerless topic. When the idle timeout fires, the optional `isAlive`
* probe is consulted first — returning true (e.g. a live run-liveness
* heartbeat, or a suspended HITL gate) re-arms the timer and keeps waiting,
* while false/absent terminates the stream with an error chunk. Both options
* are opt-in; omit them for the current unbounded behavior.
*/
observe(runId: string, options?: {
offset?: number;
idleTimeoutMs?: number;
isAlive?: () => boolean | Promise<boolean>;
onChunk?: (chunk: ChunkType<TOutput>) => void | Promise<void>;
experimentalTransform?: MastraStreamTransformOptions<TOutput>;
onStepFinish?: (result: AgentStepFinishEventData) => void | Promise<void>;
onFinish?: MastraOnFinishCallback<TOutput>;
onError?: ({ error }: {
error: Error | string;
}) => void | Promise<void>;
onSuspended?: (data: AgentSuspendedEventData) => void | Promise<void>;
}): Promise<Omit<DurableAgentStreamResult<TOutput>, 'runId'> & {
runId: string;
}>;
/**
* Get the workflow instance for direct execution.
* Lazily creates the workflow and registers Mastra on it (needed for
* getAgentById in execution steps).
*/
getWorkflow(): import("../../workflows").Workflow<import("../../workflows").DefaultEngineType, import("../../workflows").Step<string, unknown, unknown, unknown, unknown, unknown, any, unknown>[], "durable-agentic-loop", unknown, {
__workflowKind: "durable-agent";
runId: string;
agentId: string;
messageListState: any;
toolsMetadata: any[];
modelConfig: {
provider: string;
modelId: string;
specificationVersion?: string | undefined;
settings?: Record<string, any> | undefined;
providerOptions?: Record<string, any> | undefined;
};
options: any;
state: any;
messageId: string;
agentName?: string | undefined;
modelList?: {
id: string;
config: {
provider: string;
modelId: string;
specificationVersion?: string | undefined;
originalConfig?: string | Record<string, any> | undefined;
providerOptions?: Record<string, any> | undefined;
};
maxRetries: number;
enabled: boolean;
}[] | undefined;
agentSpanData?: any;
modelSpanData?: any;
requestContextEntries?: Record<string, any> | undefined;
}, {
messageListState: any;
messageId: string;
stepResult: any;
output: {
usage: any;
steps: any[];
text?: string | undefined;
};
state: any;
}, {
messageListState: any;
messageId: string;
stepResult: any;
output: {
usage: any;
steps: any[];
text?: string | undefined;
};
state: any;
}, unknown>;
/**
* @deprecated Use `stream(messages, { untilIdle: true })` instead.
*
* Stream until all background tasks complete and the agent is idle.
* Mirrors the regular Agent's streamUntilIdle but adapted for durable execution.
*/
streamUntilIdle<OUTPUT = TOutput>(messages: MessageListInput, streamOptions?: DurableAgentStreamOptions<OUTPUT> & {
maxIdleMs?: number;
}): Promise<DurableAgentStreamResult<OUTPUT>>;
/**
* Prepare for durable execution without starting it.
*/
prepare(messages: MessageListInput, options?: AgentExecutionOptions<TOutput>): Promise<{
runId: string;
messageId: string;
workflowInput: DurableAgenticWorkflowInput;
registryEntry: import("./types").RunRegistryEntry;
threadId: string | undefined;
resourceId: string | undefined;
}>;
/**
* Get the durable workflows required by this agent.
* Called by Mastra during agent registration.
* @internal
*/
getDurableWorkflows(): import("../../workflows").Workflow<import("../../workflows").DefaultEngineType, import("../../workflows").Step<string, unknown, unknown, unknown, unknown, unknown, any, unknown>[], "durable-agentic-loop", unknown, {
__workflowKind: "durable-agent";
runId: string;
agentId: string;
messageListState: any;
toolsMetadata: any[];
modelConfig: {
provider: string;
modelId: string;
specificationVersion?: string | undefined;
settings?: Record<string, any> | undefined;
providerOptions?: Record<string, any> | undefined;
};
options: any;
state: any;
messageId: string;
agentName?: string | undefined;
modelList?: {
id: string;
config: {
provider: string;
modelId: string;
specificationVersion?: string | undefined;
originalConfig?: string | Record<string, any> | undefined;
providerOptions?: Record<string, any> | undefined;
};
maxRetries: number;
enabled: boolean;
}[] | undefined;
agentSpanData?: any;
modelSpanData?: any;
requestContextEntries?: Record<string, any> | undefined;
}, {
messageListState: any;
messageId: string;
stepResult: any;
output: {
usage: any;
steps: any[];
text?: string | undefined;
};
state: any;
}, {
messageListState: any;
messageId: string;
stepResult: any;
output: {
usage: any;
steps: any[];
text?: string | undefined;
};
state: any;
}, unknown>[];
/**
* Set the Mastra instance.
* Called by the durable agent registration path in addAgent().
* Delegates to __registerMastra so the pubsub wiring and agent
* registration happen regardless of which entry point is called first.
* @internal
*/
__setMastra(mastra: Mastra): void;
/**
* Register the Mastra instance.
* Called by Mastra during agent registration (normal Agent path).
*
* Also wires mastra.pubsub as the inner pubsub (if the user didn't provide
* a custom one), so that the OBSERVE_AGENT_STREAM_ROUTE handler can subscribe
* to the same PubSub instance that this agent publishes to.
* @internal
*/
__registerMastra(mastra: Mastra): void;
}
export {};
//# sourceMappingURL=durable-agent.d.ts.map