@mastra/core@1.30.0
mastra-ai/mastra@mastra/core@1.30.0May 1, 2026by PaulieScanlon
AI Summary
This release introduces Durable Agents with resumable streams and workflow-backed execution strategies (Evented and Inngest). It also adds pluggable PubSub and Cache infrastructure, improves A2A streaming artifacts, and reduces observability noise by default.
Key Highlights
- Durable Agents + Resumable Streams (crash/disconnect resilient execution)
- Workflow-backed “Durable Execution” (Evented + Inngest strategies)
- Pluggable PubSub + Cache infrastructure (Redis/Upstash-ready)
- Improved A2A streaming artifacts in @mastra/server
- Observability noise/volume reduction by default
New Features
- DurableAgent, EventedAgent, and InngestAgent classes
- observe() method for resuming streams with offset
- streamUntilIdle() method for background task integration
- resumeStream() endpoint and client method
- A2A streaming artifact improvements
- CloudExporter filtering of model chunk spans
Full Release Notes
## Highlights
### Durable Agents + Resumable Streams (crash/disconnect resilient execution)
New `DurableAgent` support lets agent streams resume after client disconnects and continue through server crashes/restarts by caching stream events and enabling reconnection via `observe(runId, { offset })`.
### Workflow-backed “Durable Execution” (Evented + Inngest strategies)
Agents can now run outside the HTTP request using workflow execution (`createEventedAgent` for built-in evented engine, `createInngestAgent` for Inngest), enabling reliable long-running tool loops while clients subscribe to progress.
### Pluggable PubSub + Cache infrastructure (Redis/Upstash-ready)
Durable streaming is backed by a PubSub + ServerCache layer (defaults: `EventEmitterPubSub` + `InMemoryServerCache`), with recommended production configs using Redis-backed implementations so any instance can serve reconnect/replay.
### Improved A2A streaming artifacts in `@mastra/server`
A2A streaming now emits incremental artifact updates during the full agent stream while still preserving final structured output artifacts.
### Observability noise/volume reduction by default
Cloud observability uploads now filter model chunk spans by default and raise the default observability log level to `warn`, reducing data volume and chatter.
### Breaking Changes
- None called out in this changelog.
## Changelog
### [@mastra/core@1.30.0](https://github.com/mastra-ai/mastra/blob/@mastra/core@1.30.0/packages/core/CHANGELOG.md)
#### Minor Changes
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
#### Patch Changes
- Fixed a regression in 1.29.0 where configuring an agent with channel adapters (e.g. `channels.adapters.slack`) caused server startup to crash with a "Custom API route ... must not start with /api" error. The custom-route prefix validation now skips framework-generated webhook routes. ([#15952](https://github.com/mastra-ai/mastra/pull/15952))
- Update provider registry and model documentation with latest models and providers ([`d587199`](https://github.com/mastra-ai/mastra/commit/d5871993c0371bde2b0717d6b47194755baa1443))
- Fix MCP client support in the agent editor: ([#15945](https://github.com/mastra-ai/mastra/pull/15945))
- MCP client form dirty state: Save button now enables after adding/removing MCP clients
- MCP tool name matching: Both bare and namespaced tool names are matched correctly
- Auth token forwarding: Token from cookie or header is forwarded to auth-protected MCP servers
- String interpolation: Request context variables in system prompts now resolve correctly
### [@mastra/client-js@1.15.2](https://github.com/mastra-ai/mastra/blob/@mastra/client-js@1.15.2/client-sdks/client-js/CHANGELOG.md)
#### Patch Changes
- Remove incorrect deprecation markers from `getTask()` and `cancelTask()` in the Mastra A2A client. ([#15941](https://github.com/mastra-ai/mastra/pull/15941))
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
### [@mastra/editor@0.7.22](https://github.com/mastra-ai/mastra/blob/@mastra/editor@0.7.22/packages/editor/CHANGELOG.md)
#### Patch Changes
- Fix MCP client support in the agent editor: ([#15945](https://github.com/mastra-ai/mastra/pull/15945))
- MCP client form dirty state: Save button now enables after adding/removing MCP clients
- MCP tool name matching: Both bare and namespaced tool names are matched correctly
- Auth token forwarding: Token from cookie or header is forwarded to auth-protected MCP servers
- String interpolation: Request context variables in system prompts now resolve correctly
### [@mastra/hono@1.4.10](https://github.com/mastra-ai/mastra/blob/@mastra/hono@1.4.10/server-adapters/hono/CHANGELOG.md)
#### Patch Changes
- Fixed a regression in 1.29.0 where configuring an agent with channel adapters (e.g. `channels.adapters.slack`) caused server startup to crash with a "Custom API route ... must not start with /api" error. The custom-route prefix validation now skips framework-generated webhook routes. ([#15952](https://github.com/mastra-ai/mastra/pull/15952))
### [@mastra/inngest@1.3.0](https://github.com/mastra-ai/mastra/blob/@mastra/inngest@1.3.0/workflows/inngest/CHANGELOG.md)
#### Minor Changes
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
- Update peer dependencies to match core package version bump (1.0.5) ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
#### Patch Changes
### [@mastra/memory@1.17.4](https://github.com/mastra-ai/mastra/blob/@mastra/memory@1.17.4/packages/memory/CHANGELOG.md)
#### Patch Changes
- Fixed idle timeout and provider-change observation activations blocking on in-progress reflection buffering. These triggers now return immediately, letting the background reflection complete asynchronously. ([#15937](https://github.com/mastra-ai/mastra/pull/15937))
### [@mastra/observability@1.10.3](https://github.com/mastra-ai/mastra/blob/@mastra/observability@1.10.3/observability/mastra/CHANGELOG.md)
#### Patch Changes
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
- Reduced default cloud observability volume by filtering model chunk spans from CloudExporter uploads by default and raising the default observability log level to `warn`. ([#15815](https://github.com/mastra-ai/mastra/pull/15815))
### [@mastra/playground-ui@24.0.2](https://github.com/mastra-ai/mastra/blob/@mastra/playground-ui@24.0.2/packages/playground-ui/CHANGELOG.md)
#### Patch Changes
- Updated the look and motion of `Dialog`. The surface is now lighter and translucent with a subtle backdrop blur, the typography is tighter, and the open/close animation feels snappier. `SideDialog` and `AlertDialog` pick up the refined ambient shadow as well, since they share the same shadow style. ([#15958](https://github.com/mastra-ai/mastra/pull/15958))
- Polished DataList visuals: removed the trailing "No more data to load" message and dropped the bottom border on the last row for a cleaner end-of-list appearance. ([#15959](https://github.com/mastra-ai/mastra/pull/15959))
- Refined the DataPanel loading state with a smaller spinner and tightened layout for a less prominent appearance. ([#15965](https://github.com/mastra-ai/mastra/pull/15965))
### [@mastra/redis@1.1.0](https://github.com/mastra-ai/mastra/blob/@mastra/redis@1.1.0/stores/redis/CHANGELOG.md)
#### Minor Changes
- Update peer dependencies to match core package version bump (1.0.5) ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
#### Patch Changes
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
### [@mastra/server@1.30.0](https://github.com/mastra-ai/mastra/blob/@mastra/server@1.30.0/packages/server/CHANGELOG.md)
#### Minor Changes
- Update peer dependencies to match core package version bump (1.0.5) ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
#### Patch Changes
- Fixed a regression in 1.29.0 where configuring an agent with channel adapters (e.g. `channels.adapters.slack`) caused server startup to crash with a "Custom API route ... must not start with /api" error. The custom-route prefix validation now skips framework-generated webhook routes. ([#15952](https://github.com/mastra-ai/mastra/pull/15952))
- Fix MCP client support in the agent editor: ([#15945](https://github.com/mastra-ai/mastra/pull/15945))
- MCP client form dirty state: Save button now enables after adding/removing MCP clients
- MCP tool name matching: Both bare and namespaced tool names are matched correctly
- Auth token forwarding: Token from cookie or header is forwarded to auth-protected MCP servers
- String interpolation: Request context variables in system prompts now resolve correctly
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
- Fix A2A streaming to emit incremental artifact updates from the agent full stream while preserving final structured output artifacts. ([#15941](https://github.com/mastra-ai/mastra/pull/15941))
### [@mastra/upstash@1.1.0](https://github.com/mastra-ai/mastra/blob/@mastra/upstash@1.1.0/stores/upstash/CHANGELOG.md)
#### Minor Changes
- Update peer dependencies to match core package version bump (1.0.5) ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
#### Patch Changes
- Add durable agents with resumable streams ([#12557](https://github.com/mastra-ai/mastra/pull/12557))
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
### The Problem
Standard agent streaming has two fragility points:
1. **Connection drops** - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
2. **Long-running operations** - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
### The Solution
**Resumable streams** solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.
**Durable execution** solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.
### Usage
Wrap any existing `Agent` with durability using factory functions:
```typescript
import { Agent } from '@mastra/core/agent';
import { createDurableAgent } from '@mastra/core/agent/durable';
const agent = new Agent({
id: 'my-agent',
model: openai('gpt-4'),
instructions: 'You are helpful',
});
const durableAgent = createDurableAgent({ agent });
```
**Factory functions for different execution strategies:**
| Factory | Execution | Use Case |
| ---------------------------------------- | ----------------------------------- | ------------------------------- |
| `createDurableAgent({ agent })` | Local, synchronous | Development, simple deployments |
| `createEventedAgent({ agent })` | Fire-and-forget via workflow engine | Long-running operations |
| `createInngestAgent({ agent, inngest })` | Inngest-powered | Production, distributed systems |
### Resumable Streams
```typescript
// Start streaming
const { runId, output } = await durableAgent.stream('Analyze this data...');
// Client disconnects at event 5...
// Reconnect and resume from where we left off
const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
// Receives events 6, 7, 8... from cache, then continues with live events
```
### PubSub and Cache
Durable agents use two infrastructure components:
| Component | Purpose | Default |
| ---------- | ----------------------------------------- | --------------------- |
| **PubSub** | Real-time event delivery during streaming | `EventEmitterPubSub` |
| **Cache** | Stores events for replay on reconnection | `InMemoryServerCache` |
When `stream()` is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When `observe()` is called, missed events replay from cache before continuing with live events.
**Configure via Mastra instance (recommended):**
```typescript
const mastra = new Mastra({
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
agents: {
// Inherits cache and pubsub from Mastra
myAgent: createDurableAgent({ agent }),
},
});
```
**Configure per-agent (overrides Mastra):**
```typescript
const durableAgent = createDurableAgent({
agent,
cache: new RedisServerCache({ url: 'redis://...' }),
pubsub: new RedisPubSub({ url: 'redis://...' }),
});
```
**Disable caching (streams won't be resumable):**
```typescript
const durableAgent = createDurableAgent({ agent, cache: false });
```
For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.
### Class Hierarchy
- `DurableAgent` extends `Agent` - base class with resumable streams
- `EventedAgent` extends `DurableAgent` - fire-and-forget execution
- `InngestAgent` extends `DurableAgent` - Inngest-powered execution
### Other updated packages
The following packages were updated with dependency changes only:
- [@mastra/agent-builder@1.0.32](https://github.com/mastra-ai/mastra/blob/@mastra/agent-builder@1.0.32/packages/agent-builder/CHANGELOG.md)
- [@mastra/arize@1.0.21](https://github.com/mastra-ai/mastra/blob/@mastra/arize@1.0.21/observability/arize/CHANGELOG.md)
- [@mastra/arthur@0.2.7](https://github.com/mastra-ai/mastra/blob/@mastra/arthur@0.2.7/observability/arthur/CHANGELOG.md)
- [@mastra/braintrust@1.0.21](https://github.com/mastra-ai/mastra/blob/@mastra/braintrust@1.0.21/observability/braintrust/CHANGELOG.md)
- [@mastra/datadog@1.0.21](https://github.com/mastra-ai/mastra/blob/@mastra/datadog@1.0.21/observability/datadog/CHANGELOG.md)
- [@mastra/deployer@1.30.0](https://github.com/mastra-ai/mastra/blob/@mastra/deployer@1.30.0/packages/deployer/CHANGELOG.md)
- [@mastra/deployer-cloud@1.30.0](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-cloud@1.30.0/deployers/cloud/CHANGELOG.md)
- [@mastra/deployer-cloudflare@1.1.29](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-cloudflare@1.1.29/deployers/cloudflare/CHANGELOG.md)
- [@mastra/deployer-netlify@1.1.5](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-netlify@1.1.5/deployers/netlify/CHANGELOG.md)
- [@mastra/deployer-vercel@1.1.23](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-vercel@1.1.23/deployers/vercel/CHANGELOG.md)
- [@mastra/express@1.3.15](https://github.com/mastra-ai/mastra/blob/@mastra/express@1.3.15/server-adapters/express/CHANGELOG.md)
- [@mastra/fastify@1.3.15](https://github.com/mastra-ai/mastra/blob/@mastra/fastify@1.3.15/server-adapters/fastify/CHANGELOG.md)
- [@mastra/koa@1.4.15](https://github.com/mastra-ai/mastra/blob/@mastra/koa@1.4.15/server-adapters/koa/CHANGELOG.md)
- [@mastra/laminar@1.0.20](https://github.com/mastra-ai/mastra/blob/@mastra/laminar@1.0.20/observability/laminar/CHANGELOG.md)
- [@mastra/langfuse@1.2.3](https://github.com/mastra-ai/mastra/blob/@mastra/langfuse@1.2.3/observability/langfuse/CHANGELOG.md)
- [@mastra/langsmith@1.1.18](https://github.com/mastra-ai/mastra/blob/@mastra/langsmith@1.1.18/observability/langsmith/CHANGELOG.md)
- [@mastra/longmemeval@1.0.34](https://github.com/mastra-ai/mastra/blob/@mastra/longmemeval@1.0.34/explorations/longmemeval/CHANGELOG.md)
- [@mastra/mcp-docs-server@1.1.31](https://github.com/mastra-ai/mastra/blob/@mastra/mcp-docs-server@1.1.31/packages/mcp-docs-server/CHANGELOG.md)
- [@mastra/opencode@0.0.31](https://github.com/mastra-ai/mastra/blob/@mastra/opencode@0.0.31/integrations/opencode/CHANGELOG.md)
- [@mastra/otel-bridge@1.0.20](https://github.com/mastra-ai/mastra/blob/@mastra/otel-bridge@1.0.20/observability/otel-bridge/CHANGELOG.md)
- [@mastra/otel-exporter@1.0.20](https://github.com/mastra-ai/mastra/blob/@mastra/otel-exporter@1.0.20/observability/otel-exporter/CHANGELOG.md)
- [@mastra/posthog@1.0.21](https://github.com/mastra-ai/mastra/blob/@mastra/posthog@1.0.21/observability/posthog/CHANGELOG.md)
- [@mastra/react@0.2.32](https://github.com/mastra-ai/mastra/blob/@mastra/react@0.2.32/client-sdks/react/CHANGELOG.md)
- [@mastra/sentry@1.0.20](https://github.com/mastra-ai/mastra/blob/@mastra/sentry@1.0.20/observability/sentry/CHANGELOG.md)