We’ve been busy smoothing out a few sharp edges across storage, filesystems, tracing, and agent execution, plus making sandboxes and hosted filesystems easier to extend when you need to drop down to provider SDKs.
Release: @mastra/core@1.12.0
We prepared automated codemods for most breaking changes. Run all v1 codemods at once:
1npx @mastra/codemod@latest v1See the migration guide for detailed instructions.
Let’s dive in:
Cloudflare Durable Objects Storage Adapter
If you deploy Mastra on Cloudflare and need stronger stateful persistence than KV, you now have a new option: a Durable Objects backed storage adapter with SQLite persistence, batch operations, and schema validation.
That combination is a big deal for production apps, because it gives you more predictable durability and query behavior while keeping things in the Cloudflare-native ecosystem.
At a high level, you can now choose between KV and Durable Objects depending on your consistency and persistence needs, and the DO adapter brings:
- SQL backed persistence via Durable Objects SQLite storage
- Batch operations for efficiency
- Table and column validation to catch schema issues early
Workspace Filesystem Path Resolution Now Matches Real Filesystem Semantics
LocalFilesystem path resolution now behaves like the real filesystem instead of using a “virtual root” convention. Previously, in contained mode, absolute paths like /file.txt were treated as shorthand for basePath/file.txt, which could silently point at surprising locations.
Now the rules are explicit and match typical OS semantics:
- Absolute paths (
/…) resolve to real filesystem locations (and still go through containment checks) - Relative paths (
src/index.ts) resolve againstbasePath - Tilde paths (
~/…) expand to your home directory
That makes it much easier to reason about what a path means, and avoids accidental reads and writes to the wrong place when you pass in a path that “looks” absolute.
Migration example:
1// Before (treated as workspace-relative)
2await filesystem.readFile("/src/index.ts");
3
4// After (explicitly workspace-relative)
5await filesystem.readFile("src/index.ts");A few related fixes landed alongside the semantic change:
allowedPathsnow resolve againstbasePath(not the working directory), preventing unexpected permission errors whenbasePath !== cwd- Permission behavior is more robust when
allowedPathsdirectories do not exist yet (useful during skills discovery) - Server-side permission errors now correctly return HTTP 403 instead of 500, which prevents pointless client retries
Improved Observability for MCP Tooling and Agent/Workflow Execution
Tracing got a meaningful upgrade across MCP, processors, and workflows, so you can understand what your system is doing without guessing.
MCP tool calls get a dedicated span type
MCP tool calls are now traced using a dedicated MCP_TOOL_CALL span type (instead of TOOL_CALL). CoreToolBuilder detects mcpMetadata on tools and emits spans with MCP server name, version, and tool description attached as attributes. The MCP client also attaches that metadata automatically to the tools it creates, so most users get this without any code changes.
On the UI side, Studio now renders MCP spans with MCP-specific timeline styling (icon and color), which makes tool boundaries much easier to spot when you’re scanning traces.
Processor-triggered aborts are visible in traces
If a guardrail or processor aborts a run, you now see it clearly in your tracing dashboard. Processor spans include abort details (reason, retry flag, metadata), and agent-level spans capture the same information when an abort short-circuits the run.
This makes debugging “why did my agent stop?” much less of a forensic exercise.
Workflow suspend/resume keeps trace continuity
Workflows run by the default engine now preserve trace continuity across suspend and resume. Resumed work appears as children of the original span in tracing tools, so long-running workflows keep a coherent trace tree.
More Reliable Agent Loops and Token Budgeting in Multi-step Runs
Multi-step agent runs are where tiny control flow or budgeting issues tend to compound. This release focuses on making those runs more predictable and cheaper.
Agent loop control flow is fixed
The agent loop now correctly continues when onIterationComplete returns continue: true. If you were relying on that hook to keep iterating, it will now behave as expected.
Token pruning now runs every step (including tool continuations)
A nasty exponential token growth issue during multi-step agent workflows is fixed. The TokenLimiterProcessor now prunes token-based message history at every step of the loop, including tool call continuations, so the in-memory message list stays within budget before each LLM call.
Under the hood, this was implemented by adding processInputStep and removing a redundant processInput, and the Tiktoken encoder was refactored to use the shared global singleton from getTiktoken() instead of creating a new encoder instance per processor.
Sandbox & Workspace Extensibility via Provider-specific Getters and String PIDs
If you’ve ever wanted “the nice Mastra abstraction” but also the escape hatch to access provider-specific features, this update is for you.
Process IDs are now strings across providers
ProcessHandle.pid is now a string to support providers that use non-numeric identifiers (for example, session IDs). This also applies to APIs like sandbox.processes.get(...).
1const handle = await sandbox.processes.spawn("node server.js");
2
3handle.pid; // string (e.g., '1234' for local, 'session-abc' for Daytona)
4
5await sandbox.processes.get(handle.pid);This change is especially important for Daytona and Blaxel, where provider-native IDs are now used directly (no more parseInt() workarounds).
Provider-specific getters for direct SDK access
Sandboxes and filesystems now expose their underlying provider SDK instances via explicit, discoverable getters. This keeps the default API simple, while giving you an easy path to advanced features that aren’t (and maybe shouldn’t be) part of the generic interface.
Examples:
1// Sandboxes
2const daytonaSandbox = sandbox.daytona;
3const blaxelSandbox = sandbox.blaxel;
4
5// Filesystems
6const s3Client = filesystem.client;
7
8const gcsStorage = filesystem.storage;
9const gcsBucket = filesystem.bucket;This also deprecates the generic sandbox.instance getter in favor of provider-named getters for better IDE discoverability and consistency.
Breaking Changes
LocalFilesystem absolute path semantics changed (PR #13804): LocalFilesystem no longer treats absolute paths (for example, /src/index.ts) as basePath-relative. Absolute paths now resolve to real filesystem locations (still subject to containment checks). To target the workspace, pass relative paths instead.
1// Before (workspace-relative via "virtual root")
2await filesystem.readFile("/src/index.ts");
3
4// After (explicitly workspace-relative)
5await filesystem.readFile("src/index.ts");ProcessHandle.pid is now a string (PR #13591): sandbox process IDs now use string to support providers with non-numeric identifiers. Update any code that assumes numeric PIDs, including calls like processes.get(...).
1// Before
2const handle = await sandbox.processes.spawn("node server.js");
3handle.pid; // number
4await sandbox.processes.get(42);
5
6// After
7const handle = await sandbox.processes.spawn("node server.js");
8handle.pid; // string
9await sandbox.processes.get("1234");Other Notable Updates
- Provider request attribution: All provider API requests now include a
mastra/<version>User-Agent header to improve traffic attribution across gateways and providers (PR #13087) - Processor inspection helpers:
listConfiguredInputProcessors()andlistConfiguredOutputProcessors()now return the intended flat array of processors (instead of a combined workflow), making inspection and lookup by ID possible again (PR #14158) - Retry backoff tuning:
fetchWithRetrynow backs off 2s → 4s → 8s, then caps at 10s (PR #14159) - Sub-agent memory config merging: Sub-agents with
defaultOptions.memoryno longer have their memory settings overridden when called as tools by a parent agent (no consumer code changes required) (PR #11561) - MCP stdio server config: MCP stdio server configuration now supports
stderrandcwdso you can control error output and working directory (PR #13959) - Studio performance: Studio static assets are now served compressed in dev and deploy for faster load times, and gzip is scoped to static assets only to avoid breaking JSON API responses (PR #13945, PR #14190)
- Schema conversion fix: Fixed Zod v4 schema conversion edge case that could cause
type: "None"errors with the Anthropic API when thezod/v4compat layer is used (PR #14157) - Cloudflare D1 semantic recall: Fixed
listMessagesreturning empty results in a specific semantic recall case by batchingUNION ALLqueries to avoid SQLite compound SELECT limits (PR #14117) - Daytona reliability: Improved sandbox reconnection when a Daytona sandbox is externally stopped or times out due to inactivity (PR #14175)
- Model registry/docs: Updated provider registry and model documentation with the latest models and providers (PR #9cede11)
That's all for @mastra/core@1.12.0!
Happy building! 🚀
