Highlights
Per-request Workspace sandboxes (multi-tenant + isolation)
@mastra/core Workspace sandbox can now be a resolver function, enabling per-request sandbox routing for multi-tenant deployments with isolated working directories/permissions; includes prompt-safe “stable placeholder” instructions by default with an opt-in instructions.dynamicSandbox mode.
Sandbox continuity for background processes via sandboxCacheKey
When using dynamic sandboxes, sandboxCacheKey lets background process tools (execute_command with background: true, get_process_output, kill_process) reliably attach to the same sandbox across follow-up requests, with cache management via workspace.clearSandboxCache().
Unified “stream until idle” behavior via untilIdle option (core + server + client SDKs)
stream()/resumeStream() now support untilIdle (boolean or { maxIdleMs }) to keep streams open across background-task continuations; the same untilIdle request field is supported on server endpoints and client SDKs.
Deprecation: dedicated *UntilIdle methods/endpoints
streamUntilIdle() and resumeStreamUntilIdle() (and the dedicated /stream-until-idle and /resume-stream-until-idle endpoints) are deprecated in favor of stream(..., { untilIdle: true }) / resumeStream(..., { untilIdle: true }).
Breaking Changes
- None noted in this changelog.
Changelog
@mastra/core@1.41.0
Minor Changes
-
Workspace
sandboxnow accepts a resolver function for per-request sandboxes. (#16048)Before:
sandbox: WorkspaceSandbox(static, same sandbox for every request)
After:sandbox: WorkspaceSandbox | (({ requestContext }) => WorkspaceSandbox)(static or per-request)This enables per-request sandbox routing from a single Workspace — useful for multi-tenant deployments where each user/role needs an isolated working directory or different execution permissions.
const workspace = new Workspace({ sandbox: ({ requestContext }) => { const userId = requestContext.get('user-id') as string; return new LocalSandbox({ workingDirectory: `/workspaces/${userId}` }); }, });
When using a resolver, the caller owns the returned sandbox's lifecycle — the Workspace will not call
start()ordestroy()on it.mountsthrows anINVALID_CONFIGerror with a resolver, andlsp: trueis disabled with a warning because both require a concrete sandbox instance up front.Stable prompts by default
Building workspace instructions no longer calls a sandbox resolver. Resolver-backed sandboxes contribute stable placeholder text to the agent's system message, so constructing the prompt never provisions a caller-owned sandbox and the prompt stays cache-friendly. Opt into concrete per-request instructions with
instructions.dynamicSandbox:const workspace = new Workspace({ sandbox: ({ requestContext }) => resolveSandbox(requestContext), instructions: { dynamicSandbox: 'resolve' }, // or a ({ requestContext }) => string function });
Background process continuity
Set
sandboxCacheKeyto keepexecute_command({ background: true }),get_process_output, andkill_processon the same sandbox across follow-up requests — continuity is keyed by a stable id rather than theRequestContextinstance:const workspace = new Workspace({ sandbox: ({ requestContext }) => resolveSandbox(requestContext), sandboxCacheKey: ({ requestContext }) => requestContext.get('thread-id') as string, });
Failed sandbox resolver calls are removed from the cache so later calls can retry. Use
workspace.clearSandboxCache(cacheKey)to drop a keyed sandbox reference when your own lifecycle code has destroyed or replaced that sandbox.When background process tools cannot find a PID on a dynamic sandbox without
sandboxCacheKey, the tool output now points tosandboxCacheKeyso callers can fix continuity across follow-up requests.
Patch Changes
-
Added
untilIdleoption tostream()andresumeStream()methods. PassuntilIdle: true(oruntilIdle: { maxIdleMs: 60_000 }) to keep the stream open across background-task continuations — same behavior as the now-deprecatedstreamUntilIdle()method. (#17536)Example:
const result = await agent.stream('Research solana for me', { untilIdle: true, memory: { thread: 't1', resource: 'u1' }, });
Deprecated
streamUntilIdle()andresumeStreamUntilIdle()— they still work but now delegate internally tostream({ untilIdle: true }).
@mastra/acp@0.2.1
Patch Changes
- Fixed ACP tools to keep their default session alive across executions. (#17516)
@mastra/client-js@1.23.2
Patch Changes
-
The
/agents/:agentId/streamand/agents/:agentId/resume-streamendpoints now accept anuntilIdlefield in the request body. When set, the stream stays open across background-task continuations (same behavior as the/stream-until-idleendpoint). The dedicated/stream-until-idleand/resume-stream-until-idleendpoints remain available but are deprecated. (#17536)Server example:
// POST /api/agents/:agentId/stream fetch(`/api/agents/${agentId}/stream`, { method: 'POST', body: JSON.stringify({ messages: [{ role: 'user', content: 'Research solana for me' }], untilIdle: true, // or { maxIdleMs: 60000 } }), });
Client SDK:
streamUntilIdle()andresumeStreamUntilIdle()are deprecated — usestream(messages, { untilIdle: true })instead.
@mastra/playground-ui@32.0.2
Patch Changes
-
Fixed dropdown, combobox, select, and popover popups silently failing to render when opened outside a side panel (most visibly the model and provider pickers in the chat composer). The shared portal-container resolver now always falls back to the document body instead of leaking an unrenderable value. (#17560)
-
Fixed combobox popups so model picker dropdowns open correctly outside side dialogs. (#17556)
@mastra/react@0.5.2
Patch Changes
-
The
/agents/:agentId/streamand/agents/:agentId/resume-streamendpoints now accept anuntilIdlefield in the request body. When set, the stream stays open across background-task continuations (same behavior as the/stream-until-idleendpoint). The dedicated/stream-until-idleand/resume-stream-until-idleendpoints remain available but are deprecated. (#17536)Server example:
// POST /api/agents/:agentId/stream fetch(`/api/agents/${agentId}/stream`, { method: 'POST', body: JSON.stringify({ messages: [{ role: 'user', content: 'Research solana for me' }], untilIdle: true, // or { maxIdleMs: 60000 } }), });
Client SDK:
streamUntilIdle()andresumeStreamUntilIdle()are deprecated — usestream(messages, { untilIdle: true })instead.
@mastra/server@1.41.0
Minor Changes
-
The
/agents/:agentId/streamand/agents/:agentId/resume-streamendpoints now accept anuntilIdlefield in the request body. When set, the stream stays open across background-task continuations (same behavior as the/stream-until-idleendpoint). The dedicated/stream-until-idleand/resume-stream-until-idleendpoints remain available but are deprecated. (#17536)Server example:
// POST /api/agents/:agentId/stream fetch(`/api/agents/${agentId}/stream`, { method: 'POST', body: JSON.stringify({ messages: [{ role: 'user', content: 'Research solana for me' }], untilIdle: true, // or { maxIdleMs: 60000 } }), });
Client SDK:
streamUntilIdle()andresumeStreamUntilIdle()are deprecated — usestream(messages, { untilIdle: true })instead.
Patch Changes
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/deployer@1.41.0
- @mastra/deployer-cloud@1.41.0
- @mastra/deployer-cloudflare@1.1.42
- @mastra/deployer-netlify@1.1.18
- @mastra/deployer-vercel@1.1.36
- @mastra/express@1.3.29
- @mastra/fastify@1.3.29
- @mastra/hono@1.4.24
- @mastra/koa@1.5.12
- @mastra/longmemeval@1.0.48
- @mastra/mcp-docs-server@1.1.45
- @mastra/nestjs@0.1.13
- @mastra/opencode@0.0.45
- @mastra/temporal@0.1.12