@mastra/core
Version:
297 lines (180 loc) • 14.3 kB
Markdown
> Discover all available pages from the documentation index: https://mastra.ai/llms.txt
# DurableAgent
`DurableAgent` wraps an existing [`Agent`](https://mastra.ai/reference/agents/agent) with durable execution and resumable streams. It runs the agentic loop so that a client can disconnect and reconnect without missing events, and it streams those events over [PubSub](https://mastra.ai/docs/server/pubsub). Use it when a run must outlive a single request or survive a dropped connection.
Create one with the [`createDurableAgent`](#createdurableagentoptions) factory, or use [`createEventedAgent`](#createeventedagentoptions) for fire-and-forget execution on the built-in workflow engine. For Inngest-powered execution, use [`createInngestAgent`](https://mastra.ai/reference/agents/inngest-agent) from `@mastra/inngest`.
## Usage example
```typescript
import { Mastra } from '@mastra/core'
import { Agent } from '@mastra/core/agent'
import { createDurableAgent } from '@mastra/core/agent/durable'
const agent = new Agent({
id: 'my-agent',
name: 'My Agent',
instructions: 'You are a helpful assistant',
model: 'openai/gpt-5.5',
})
const durableAgent = createDurableAgent({ agent })
export const mastra = new Mastra({
agents: { myAgent: durableAgent },
})
```
Stream a response and read the result. The `cleanup` function unsubscribes from PubSub when you are done with the run:
```typescript
const { output, runId, cleanup } = await durableAgent.stream('Hello!')
const text = await output.text
cleanup()
```
## `createDurableAgent(options)`
Wraps an `Agent` with durable execution and resumable streams. This is the recommended way to create a `DurableAgent`.
```typescript
import { createDurableAgent } from '@mastra/core/agent/durable'
const durableAgent = createDurableAgent({ agent })
```
Returns: `DurableAgent`
### Parameters
**agent** (`Agent`): The Agent to wrap with durable execution capabilities. Agent methods delegate to this agent.
**id** (`string`): ID override. (Default: `agent.id`)
**name** (`string`): Name override. (Default: `agent.name`)
**cache** (`MastraServerCache | false`): Cache for stored stream events, which enables resumable streams. If omitted, the agent inherits the cache from the Mastra instance, or uses an InMemoryServerCache. Set to false to disable caching, which makes streams non-resumable.
**pubsub** (`PubSub`): PubSub instance for streaming events. (Default: `EventEmitterPubSub`)
**maxSteps** (`number`): Maximum number of steps for the agentic loop.
## `createEventedAgent(options)`
Wraps an `Agent` with fire-and-forget durable execution on the built-in workflow engine. Like `createDurableAgent`, it returns a result you stream from, but the underlying workflow runs non-blocking (via `startAsync`) instead of running to completion before the stream is wired up. Use it when you want the run to progress independently of the caller. It doesn't accept `id` or `name` overrides.
```typescript
import { createEventedAgent } from '@mastra/core/agent/durable'
const eventedAgent = createEventedAgent({ agent })
```
Returns: `EventedAgent` (a subclass of `DurableAgent`)
### Parameters
**agent** (`Agent`): The Agent to wrap with evented durable execution capabilities.
**cache** (`MastraServerCache | false`): Cache for stored stream events, which enables resumable streams. If omitted, the agent inherits the cache from the Mastra instance, or uses an InMemoryServerCache. Set to false to disable caching.
**pubsub** (`PubSub`): PubSub instance for streaming events. (Default: `EventEmitterPubSub`)
**maxSteps** (`number`): Maximum number of steps for the agentic loop.
## Constructor parameters
The `DurableAgent` class accepts the same options as `createDurableAgent`, plus `cleanupTimeoutMs`. Prefer the factory unless you need to subclass.
**agent** (`Agent`): The Agent to wrap with durable execution capabilities.
**id** (`string`): ID override. (Default: `agent.id`)
**name** (`string`): Name override. (Default: `agent.name`)
**cache** (`MastraServerCache | false`): Cache for stored stream events. If omitted, inherits from the Mastra instance or uses an InMemoryServerCache. Set to false to disable caching.
**pubsub** (`PubSub`): PubSub instance for streaming events. (Default: `EventEmitterPubSub`)
**maxSteps** (`number`): Maximum number of steps for the agentic loop.
**cleanupTimeoutMs** (`number`): Grace period in milliseconds before registry entries are cleaned up automatically after a stream finishes or errors. Set to 0 to disable auto-cleanup and require a manual cleanup() call. Auto-cleanup does not fire on suspended events. (Default: `30000`)
## Methods
### Execution
#### `stream(messages, options?)`
Streams a response using durable execution. Returns immediately with a result whose `output` produces events as the run progresses.
```typescript
const { output, runId, cleanup } = await durableAgent.stream('Hello!', {
onChunk: chunk => console.log(chunk),
onFinish: result => console.log('done', result),
})
const text = await output.text
cleanup()
```
Returns: [`Promise<DurableAgentStreamResult>`](#durableagentstreamresult)
#### `resume(runId, resumeData, options?)`
Resumes a suspended run, for example after a tool approval. Pass the `runId` from the original stream and the data the run was waiting on. Throws if no registry entry exists for the run.
```typescript
const { output, cleanup } = await durableAgent.resume(runId, {
approved: true,
})
await output.text
cleanup()
```
Returns: [`Promise<DurableAgentStreamResult>`](#durableagentstreamresult)
#### `observe(runId, options?)`
Reconnects to an existing run, replaying cached events before delivering live ones. Use this after a network disconnection. Pass `offset` to start replay from a known position.
```typescript
const { output, cleanup } = await durableAgent.observe(runId, {
offset: 0,
onChunk: chunk => console.log(chunk),
})
await output.text
```
Returns: `Promise<DurableAgentStreamResult>`
> **Warning:** The `cleanup()` returned by `observe()` destroys the run's registry entries and cached events. Only call it when you are done with the run. If the run is suspended and you intend to resume later, don't call `cleanup()` — let the auto-cleanup timer handle it after the run finishes or errors. Auto-cleanup doesn't fire on suspended events.
#### `prepare(messages, options?)`
Prepares a run for durable execution without starting it. Registers the run in the internal registry and returns the serialized workflow input. Use this when you need to control when and how the workflow is triggered.
```typescript
const { runId, messageId, workflowInput, threadId, resourceId } = await durableAgent.prepare(
'Summarize the document',
{
memory: { threadId: 'thread-1', resourceId: 'user-1' },
},
)
```
Returns:
```typescript
interface PrepareResult {
runId: string
messageId: string
workflowInput: any
registryEntry: object
threadId?: string
resourceId?: string
}
```
## Stream options
`stream()` accepts a `DurableAgentStreamOptions` object. It supports the agent execution options below, plus lifecycle callbacks.
**runId** (`string`): Unique identifier for this run. Use it later with resume() or observe().
**instructions** (`AgentExecutionOptions['instructions']`): Overrides the agent's default instructions for this run. Accepts a static string or the same dynamic instructions value the agent supports.
**context** (`ModelMessage[]`): Additional context messages to provide to the agent.
**memory** (`object`): Memory configuration for conversation persistence and retrieval.
**requestContext** (`RequestContext`): Request context carrying dynamic configuration and state for this run.
**maxSteps** (`number`): Maximum number of steps to run for this stream.
**toolsets** (`object`): Additional tool sets available for this run.
**clientTools** (`object`): Client-side tools available during execution.
**toolChoice** (`'auto' | 'none' | 'required' | { type: 'tool'; toolName: string }`): Tool selection strategy.
**activeTools** (`string[]`): Restricts execution to the named subset of the agent's tools.
**modelSettings** (`object`): Model-specific settings such as temperature. Credential-bearing headers (Authorization, X-Api-Key, and similar) are stripped from the serialized snapshot before it crosses process boundaries.
**stopWhen** (`AgentExecutionOptions['stopWhen']`): Predicate or composition that ends the agentic loop early. The closure rides on the in-process run registry; cross-process resumes degrade to maxSteps only.
**system** (`string | string[]`): Additional system message appended after the agent instructions and before user messages.
**requireToolApproval** (`boolean | ((args: { toolName: string; args: unknown; requestContext: RequestContext; workspace?: string }) => boolean | Promise<boolean>)`): Require approval for tool calls. Pass true or false to gate all or none, or a function for per-call policy. Function-form policies live on the in-process run registry; cross-process resumes fall back to a true shadow.
**autoResumeSuspendedTools** (`boolean`): Automatically resume tools that suspended, instead of waiting for an external resume() call.
**toolCallConcurrency** (`number`): Maximum number of tool calls to execute concurrently.
**includeRawChunks** (`boolean`): Include raw provider chunks in the stream output.
**maxProcessorRetries** (`number`): Maximum number of processor retries per generation.
**structuredOutput** (`object`): Structured output configuration.
**untilIdle** (`boolean | { maxIdleMs?: number }`): When set, keeps the stream open across background-task continuations until the agent is idle. Pass true for the default 5-minute idle timeout, or { maxIdleMs } to customise. Equivalent to the deprecated streamUntilIdle() method. Also supported on resume().
**disableBackgroundTasks** (`boolean`): Disable background-task dispatch for this run. Background-eligible tools execute inline instead.
**tracingOptions** (`AgentExecutionOptions['tracingOptions']`): Tracing metadata, tags, trace ID, parent span ID, and requestContextKeys forwarded to the agent and model spans. Fully JSON-serializable.
**actor** (`AgentExecutionOptions['actor']`): Per-call actor signal forwarded to FGA checks and tool execution.
**transform** (`AgentExecutionOptions['transform']`): Per-invocation tool payload transform policy. The transformToolPayload closure lives on the in-process run registry; only the JSON-safe targets shadow is serialized.
**prepareStep** (`AgentExecutionOptions['prepareStep']`): Per-step preparation hook invoked as a PrepareStepProcessor at the start of every iteration. Closure-only — stored on the in-process run registry. Cross-process resumes lose the hook.
**isTaskComplete** (`AgentExecutionOptions['isTaskComplete']`): Per-call completion policy. Scorer instances and onComplete live on the in-process run registry; the JSON-safe primitives (strategy, timeout, parallel, suppressFeedback, scorerNames) are serialized for cross-process observability.
**delegation** (`AgentExecutionOptions['delegation']`): Sub-agent delegation hooks (onDelegationStart, onDelegationComplete, messageFilter). Callbacks are baked into the sub-agent tool wrappers at prepare time. Cross-process resumes lose the callbacks.
**versions** (`object`): Version overrides for sub-agent delegation.
**abortSignal** (`AbortSignal`): External abort signal. Forwarded to the durable run's internal AbortController, so either source can cancel the run. Cross-process resumes cannot recover the signal — pass a fresh one to resume() if you need post-resume abortability.
**onChunk** (`(chunk: ChunkType) => void | Promise<void>`): Called for each streamed chunk.
**onStepFinish** (`(result: AgentStepFinishEventData) => void | Promise<void>`): Called when a step in the agentic loop finishes.
**onFinish** (`(result: AgentFinishEventData) => void | Promise<void>`): Called when the run finishes.
**onError** (`(error: Error) => void | Promise<void>`): Called when the run errors.
**onSuspended** (`(data: AgentSuspendedEventData) => void | Promise<void>`): Called when the run suspends, for example for tool approval.
**onAbort** (`AgentExecutionOptions['onAbort']`): Called when the run is aborted via abortSignal or result.abort().
**onIterationComplete** (`AgentExecutionOptions['onIterationComplete']`): Called after every agentic-loop iteration with the latest messageList, finishReason, and isFinal flag. Observation-only on durable agents: returning continue: false or feedback does not influence the loop.
`resume()` and `observe()` accept the same lifecycle callbacks (`onChunk`, `onStepFinish`, `onFinish`, `onError`, `onSuspended`). `observe()` also accepts an `offset` to control where replay starts.
## DurableAgentStreamResult
The object returned by `stream()`, `resume()`, and `observe()`.
```typescript
interface DurableAgentStreamResult<OUTPUT = undefined> {
output: MastraModelOutput<OUTPUT>
readonly fullStream: ReadableStream<any>
runId: string
threadId?: string
resourceId?: string
cleanup: () => void
abort: () => void
}
```
**output** (`MastraModelOutput`): The streaming output. Await output.text for the full text, or consume output.fullStream.
**fullStream** (`ReadableStream`): The full event stream, delegating to output.fullStream.
**runId** (`string`): The unique run ID. Pass it to resume() or observe() to reconnect.
**threadId** (`string`): Thread ID when using memory.
**resourceId** (`string`): Resource ID when using memory.
**cleanup** (`() => void`): Unsubscribes from PubSub and clears registry entries for the run. Call it when done with the run.
**abort** (`() => void`): Aborts the run by flipping the internal AbortController. Surfaces as an AbortError inside the durable LLM-execution step and fires the onAbort callback. Safe to call after the run has finished — a no-op in that case.
## Related
- [`createInngestAgent()`](https://mastra.ai/reference/agents/inngest-agent)
- [Agent class](https://mastra.ai/reference/agents/agent)
- [PubSub](https://mastra.ai/docs/server/pubsub)
- [`.getMemory()`](https://mastra.ai/reference/agents/getMemory)