Mastra
Thread-scoped notifications; processor state signals for thread context
↗This release5 featuresNew capabilitiesAI-tallied from the release notes@mastra/core@1.39.0
Highlights
Thread-Scoped Notification Signals + Persisted Inbox (Experimental)
New record-first notification signals add a thread-scoped inbox with agent.sendNotificationSignal(), priority-aware delivery (including due-notification dispatch and low-priority rollups), a structured metadata.notification contract, and a notification_inbox tool to list/read/dismiss/archive notifications.
Notification Inbox Storage Support Across Backends
Notification inbox persistence is now supported in multiple stores—Postgres (@mastra/pg), MongoDB (@mastra/mongodb), and LibSQL (@mastra/libsql)—so notification signals can reliably persist inbox records regardless of your storage choice.
Processor State Signals for Memory-Backed Thread Context
Processors can publish replayable, deduped state “lanes” via computeStateSignal() and external producers can update them with agent.sendStateSignal(), enabling efficient snapshot/delta state history and making browser context flow through the same thread state mechanism.
Working Memory Delivered as State Signals (Experimental Opt-in)
A new workingMemory.useStateSignals option sends working memory as state signals (instead of system-message injection), with automatic WorkingMemoryStateProcessor attachment, cache-key dedupe, and unified-diff deltas when smaller than snapshots (markdown mode).
Type-Safe Message Rendering with @mastra/react MessageFactory + Canonical Stream Types
@mastra/react adds a fully type-safe MessageFactory for per-part rendering (including dynamic tool parts) plus strongly-typed status slots (Tripwire/Warning/Error/Task), while @mastra/core/stream now exports canonical IsTaskCompletePayload and TripwirePayload types for consistent UI typing.
Breaking Changes
- None noted in this changelog.
Changelog
@mastra/core@1.39.0
Minor Changes
-
Add a type-safe
MessageFactorycomponent to@mastra/reactfor rendering aMastraDBMessagewith your own per-part components, and align its tripwire/task-verdict types with@mastra/core. (#17514)@mastra/react
MessageFactoryprovides optional, fully type-safe render functions for each kind of message part. Only the renderer matching a part's type runs, and each receives correctly narrowed props; missing renderers fall back gracefully. Runtime-onlydynamic-tooland AI SDK v5tool-${string}parts are covered by a dedicatedDynamicToolrenderer, and optional role wrappers let you frame parts per message role.import { MessageFactory } from '@mastra/react'; <MessageFactory message={message} Text={part => <p>{part.text}</p>} ToolInvocation={part => <ToolCard name={part.toolInvocation.toolName} />} DynamicTool={part => <ToolCard name={part.toolName} state={part.state} />} Data={part => <DataView type={part.type} data={part.data} />} roles={{ Signal: ({ children }) => <SignalFrame>{children}</SignalFrame> }} />;It also accepts an optional
statusprop with four strongly-typed slots that render from a message's metadata while keeping part renderers pure.Tripwire,Warning, andErrorare replacement slots (rendered instead of the parts whenmetadata.statusmatches);Taskis an adjacent slot (rendered alongside the parts when a task-completion verdict exists). The factory only surfaces metadata to the slots and never filters it (for example, it still invokesTaskwhensuppressFeedbackis true) — the consumer decides what to render or skip. Existing behavior is unchanged whenstatusis omitted.<MessageFactory message={message} status={{ Error: ({ text }) => <ErrorNotice>{text}</ErrorNotice>, Task: ({ passed, suppressFeedback }) => (suppressFeedback ? null : <TaskVerdict passed={passed} />), }} {...renderers} />The narrowed part types used by the renderers are exported so consumers can type their own components:
TextPart,ReasoningPart,FilePart,StepStartPart,ToolInvocationPart,SourceDocumentPart, andSourceUrlPart, plusMessageFactoryPart(the exact union of part shapesMessageFactorycan dispatch — the typed accumulator parts plus the runtime-onlydynamic-tool/tool-${string}parts) for typing part arrays precisely instead ofunknown[].MastraDBMessageMetadata.isTaskCompleteResultis now typed as the{ passed?, suppressFeedback? }completion-verdict shape (matchingcompletionResult) instead ofboolean, so theTaskslot resolves verdicts from either field without a cast.TripwireMetadatais now an alias of core'sTripwirePayload, and the message accumulator persists the canonical shape. Two behavioral changes to persistedmetadata.tripwire:- The tripwire
reasonis now persisted astripwire.reason(previously it was only stored in the message text part). - The processor metadata field was renamed from
tripwire.tripwirePayloadtotripwire.metadatato match the canonical type.
The
MessageFactoryTripwireslot receivesreasonthroughprops.tripwire.@mastra/core
Exported the canonical
IsTaskCompletePayloadandTripwirePayloadtypes from@mastra/core/streamso consumers can type their own task/completion and tripwire UI against them instead of redeclaring the shapes.import type { IsTaskCompletePayload, TripwirePayload } from '@mastra/core/stream'; - The tripwire
-
Add experimental record-first notification signals with thread-scoped inbox storage,
agent.sendNotificationSignal(), priority-aware notification delivery policies, due-notification dispatch, summary rollups for low-priority notifications, a structuredmetadata.notificationsignal contract, and a flexible notification inbox tool. (#17241)await agent.sendNotificationSignal( { source: 'github', kind: 'ci-status', priority: 'high', summary: 'CI failed on main', }, { resourceId: 'user-1', threadId: 'thread-1' }, );Agents can then use the
notification_inboxtool to list, read, dismiss, or archive persisted inbox records. -
Add processor state signals for memory-backed thread context. (#17240)
Processors can now publish named state lanes with
computeStateSignal(), and external producers can update the same lanes withagent.sendStateSignal(). The runtime tracks each lane byid,cacheKey, andmode, so unchanged state is deduped and snapshot/delta history can be replayed efficiently.Browser context now uses this state-signal path, so browser state is represented as thread state instead of being injected as ad hoc context.
// External producer: push browser state outside a model run. await agent.sendStateSignal( { id: 'browser', cacheKey: 'tab-42:https://app.example.com/dashboard', mode: 'snapshot', contents: 'Browser is open on https://app.example.com/dashboard', value: { url: 'https://app.example.com/dashboard', title: 'Dashboard' }, }, { resourceId, threadId, ifIdle: { behavior: 'persist' } }, ); // Processor-owned state: compute state before the model request. const browserProcessor = { id: 'browser-state', stateId: 'browser', async computeStateSignal({ lastSnapshot }) { const nextBrowserState = await getBrowserState(); return { cacheKey: nextBrowserState.url, mode: 'snapshot', contents: `Browser is open on ${nextBrowserState.url}`, value: nextBrowserState, }; }, };
Patch Changes
-
Fixed CostGuardProcessor thread and resource scope resolution when running without auth middleware (e.g. Studio dev mode). The processor now falls back to the MastraMemory context on RequestContext to resolve threadId and resourceId, matching the pattern used by other processor helpers. (#17522)
-
Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
-
Improved notification delivery targeting and inbox search so notification records can be found and delivered to the intended thread. (#17447)
-
Fixed subscribed client tools so browser-executed tool results continue through the existing thread subscription instead of opening and canceling a second stream. This prevents closed-stream errors in apps like Agent Builder when multiple client tools run during one response. (#17532)
-
Added experimental
workingMemory.useStateSignalsopt-in. When set totrue, working memory is delivered to the model as astatesignal (via the new state-signals API) instead of being folded into the system message.Memoryauto-attaches aWorkingMemoryStateProcessorthat emits a signal withstateId: 'working-memory'and dedups viacacheKey. Subsequent turns emit unified-diff deltas against the prior snapshot when the diff is smaller than the snapshot (markdown mode only); schema mode and the fallback path always emit a full snapshot. The working-memory tool is registered assetWorkingMemoryinstead ofupdateWorkingMemoryunder this opt-in so legacy persistence/prompt strip filters naturally bypass it. The default (false) preserves the existing system-message behavior.useStateSignalsis not supported with template working memoryversion: 'vnext'. (#17497)import { Memory } from '@mastra/memory'; const memory = new Memory({ options: { workingMemory: { enabled: true, useStateSignals: true, }, }, });
@mastra/auth-better-auth@1.0.3
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/auth-cloud@1.1.3
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/auth-okta@0.0.4
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/auth-studio@1.2.3
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/auth-workos@1.5.1
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/client-js@1.23.0
Minor Changes
-
Added
GET /stored/agents/:storedAgentId/dependentsendpoint that lists agents (#17183) referencing a stored agent as a sub-agent.const { dependents, hiddenCount } = await client.getStoredAgent(id).dependents(); // { dependents: [{ id: 'parent-1', name: 'Triager' }], hiddenCount: 2 }dependents— caller-readable agents (public agents and the caller's own private agents) withid+name.hiddenCount— cross-workspace dependents the caller cannot read, only surfaced when the target agent is public.
Access mirrors
GET /stored/agents/:storedAgentId— 404 when the caller cannot read the target. -
Add experimental
agent.sendMessage()andagent.queueMessage()helpers for the new message-first server routes. (#17238)
Patch Changes
-
Improved agent message, stream, and observational-memory type handling across the client SDKs and playground UI. (#17208)
-
Fixed subscribed client tools so browser-executed tool results continue through the existing thread subscription instead of opening and canceling a second stream. This prevents closed-stream errors in apps like Agent Builder when multiple client tools run during one response. (#17532)
@mastra/deployer@1.39.0
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/libsql@1.12.1
Patch Changes
-
Added LibSQL support for the notifications storage domain so notification signals can persist thread-scoped inbox records. (#17241)
import { LibSQLStore } from '@mastra/libsql'; const storage = new LibSQLStore({ url: 'file:./mastra.db' }); -
Fixed LibSQL memory cleanup so in-memory stores initialize their tables before clearing data. This prevents reset flows from failing with missing table errors such as mastra_resources. (#17532)
@mastra/mcp@1.9.1
Patch Changes
- Removed Hono from @mastra/core and auth package runtime dependencies. Auth providers now receive framework-agnostic request types that support standard Request objects and Hono-compatible request shapes. MCP and deployer avoid relying on core-bundled Hono context types at package boundaries. (#17410)
@mastra/memory@1.20.2
Patch Changes
-
Improved agent message, stream, and observational-memory type handling across the client SDKs and playground UI. (#17208)
-
Added experimental
workingMemory.useStateSignalsopt-in. When set totrue, working memory is delivered to the model as astatesignal (via the new state-signals API) instead of being folded into the system message.Memoryauto-attaches aWorkingMemoryStateProcessorthat emits a signal withstateId: 'working-memory'and dedups viacacheKey. Subsequent turns emit unified-diff deltas against the prior snapshot when the diff is smaller than the snapshot (markdown mode only); schema mode and the fallback path always emit a full snapshot. The working-memory tool is registered assetWorkingMemoryinstead ofupdateWorkingMemoryunder this opt-in so legacy persistence/prompt strip filters naturally bypass it. The default (false) preserves the existing system-message behavior.useStateSignalsis not supported with template working memoryversion: 'vnext'. (#17497)import { Memory } from '@mastra/memory'; const memory = new Memory({ options: { workingMemory: { enabled: true, useStateSignals: true, }, }, });
@mastra/mongodb@1.9.1
Patch Changes
-
Added notification inbox storage support for MongoDB stores. (#17241)
import { MongoDBStore } from '@mastra/mongodb'; const storage = new MongoDBStore({ url: process.env.MONGODB_URI!, dbName: 'mastra' });Agents using this store can persist thread-scoped notification inbox records for notification signals.
@mastra/pg@1.12.1
Patch Changes
-
Added notification inbox storage support for Postgres stores. (#17241)
import { PostgresStore } from '@mastra/pg'; const storage = new PostgresStore({ connectionString: process.env.POSTGRES_URL! });Agents using this store can persist thread-scoped notification inbox records for notification signals.
@mastra/playground-ui@32.0.0
Patch Changes
- Improved agent message, stream, and observational-memory type handling across the client SDKs and playground UI. (#17208)
@mastra/react@0.5.0
Minor Changes
-
Add a type-safe
MessageFactorycomponent to@mastra/reactfor rendering aMastraDBMessagewith your own per-part components, and align its tripwire/task-verdict types with@mastra/core. (#17514)@mastra/react
MessageFactoryprovides optional, fully type-safe render functions for each kind of message part. Only the renderer matching a part's type runs, and each receives correctly narrowed props; missing renderers fall back gracefully. Runtime-onlydynamic-tooland AI SDK v5tool-${string}parts are covered by a dedicatedDynamicToolrenderer, and optional role wrappers let you frame parts per message role.import { MessageFactory } from '@mastra/react'; <MessageFactory message={message} Text={part => <p>{part.text}</p>} ToolInvocation={part => <ToolCard name={part.toolInvocation.toolName} />} DynamicTool={part => <ToolCard name={part.toolName} state={part.state} />} Data={part => <DataView type={part.type} data={part.data} />} roles={{ Signal: ({ children }) => <SignalFrame>{children}</SignalFrame> }} />;It also accepts an optional
statusprop with four strongly-typed slots that render from a message's metadata while keeping part renderers pure.Tripwire,Warning, andErrorare replacement slots (rendered instead of the parts whenmetadata.statusmatches);Taskis an adjacent slot (rendered alongside the parts when a task-completion verdict exists). The factory only surfaces metadata to the slots and never filters it (for example, it still invokesTaskwhensuppressFeedbackis true) — the consumer decides what to render or skip. Existing behavior is unchanged whenstatusis omitted.<MessageFactory message={message} status={{ Error: ({ text }) => <ErrorNotice>{text}</ErrorNotice>, Task: ({ passed, suppressFeedback }) => (suppressFeedback ? null : <TaskVerdict passed={passed} />), }} {...renderers} />The narrowed part types used by the renderers are exported so consumers can type their own components:
TextPart,ReasoningPart,FilePart,StepStartPart,ToolInvocationPart,SourceDocumentPart, andSourceUrlPart, plusMessageFactoryPart(the exact union of part shapesMessageFactorycan dispatch — the typed accumulator parts plus the runtime-onlydynamic-tool/tool-${string}parts) for typing part arrays precisely instead ofunknown[].MastraDBMessageMetadata.isTaskCompleteResultis now typed as the{ passed?, suppressFeedback? }completion-verdict shape (matchingcompletionResult) instead ofboolean, so theTaskslot resolves verdicts from either field without a cast.TripwireMetadatais now an alias of core'sTripwirePayload, and the message accumulator persists the canonical shape. Two behavioral changes to persistedmetadata.tripwire:- The tripwire
reasonis now persisted astripwire.reason(previously it was only stored in the message text part). - The processor metadata field was renamed from
tripwire.tripwirePayloadtotripwire.metadatato match the canonical type.
The
MessageFactoryTripwireslot receivesreasonthroughprops.tripwire.@mastra/core
Exported the canonical
IsTaskCompletePayloadandTripwirePayloadtypes from@mastra/core/streamso consumers can type their own task/completion and tripwire UI against them instead of redeclaring the shapes.import type { IsTaskCompletePayload, TripwirePayload } from '@mastra/core/stream'; - The tripwire
-
Use the message-first client helper for user-authored thread input while preserving fallback behavior for older servers that only support the legacy signal route. (#17238)
Patch Changes
- Improved agent message, stream, and observational-memory type handling across the client SDKs and playground UI. (#17208)
@mastra/server@1.39.0
Minor Changes
-
Added
GET /stored/agents/:storedAgentId/dependentsendpoint that lists agents (#17183) referencing a stored agent as a sub-agent.const { dependents, hiddenCount } = await client.getStoredAgent(id).dependents(); // { dependents: [{ id: 'parent-1', name: 'Triager' }], hiddenCount: 2 }dependents— caller-readable agents (public agents and the caller's own private agents) withid+name.hiddenCount— cross-workspace dependents the caller cannot read, only surfaced when the target agent is public.
Access mirrors
GET /stored/agents/:storedAgentId— 404 when the caller cannot read the target.
Patch Changes
-
Fixed memory status incorrectly reporting memory as enabled for agents without memory configured. The /api/memory/status endpoint now returns false for a resolved agent that has no memory, even when the Mastra instance has storage configured. Previously, Studio would render memory UI for such agents. (#17506)
-
Fixed subscribed client tools so browser-executed tool results continue through the existing thread subscription instead of opening and canceling a second stream. This prevents closed-stream errors in apps like Agent Builder when multiple client tools run during one response. (#17532)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.40
- @mastra/deployer-cloud@1.39.0
- @mastra/deployer-cloudflare@1.1.40
- @mastra/deployer-netlify@1.1.16
- @mastra/deployer-vercel@1.1.34
- @mastra/editor@0.11.1
- @mastra/express@1.3.27
- @mastra/fastify@1.3.27
- @mastra/hono@1.4.22
- @mastra/koa@1.5.10
- @mastra/longmemeval@1.0.46
- @mastra/mcp-docs-server@1.1.43
- @mastra/nestjs@0.1.11
- @mastra/opencode@0.0.43
- @mastra/temporal@0.1.10
@mastra/core@1.38.0
Highlights
OAuth ToolProviders runtime for stored agents (connections + toolkit-scoped tool resolution)
Mastra now includes the v1 ToolProvider runtime plus server routes, editor wiring, and client SDK methods to manage OAuth-backed connections and let stored agents pin specific connections per toolkit at execution time (including per-author vs shared vs caller-supplied scopes).
New MySQL storage adapter
@mastra/mysql adds a first-party MySQL storage backend with broad domain coverage (memory, threads, workflows, observability, agents, etc.), including reliable fresh-DB initialization and index creation fixes.
New cloud sandbox and browser deployment options (Vercel MicroVM + Firecrawl Browser)
@mastra/vercel introduces VercelMicroVMSandbox (ephemeral Firecracker MicroVMs with persistent in-session FS, ports, and background processes), and @mastra/browser-firecrawl ships a Firecrawl-hosted Chrome sandbox while keeping the same @mastra/agent-browser tool surface.
Code Mode (experimental): TypeScript orchestration tool for agents
createCodeMode() lets agents generate and run a single TypeScript program to orchestrate multiple tool calls (batching with Promise.all, aggregations, real computation) while tools still execute host-side with validation and tracing.
Message-first + approval-first thread APIs (send/queue messages, subscription-native tool approval, fire-and-forget resumes)
New agent.sendMessage() / agent.queueMessage() and matching server routes enable higher-level “user message into thread” flows (vs low-level signals), tool approvals can now resume via the active thread subscription (with message queueing during approval), and workflows add resumeNoWait/resumeAsync() fire-and-forget semantics returning { runId } immediately.
Breaking Changes
- None called out in this changelog.
Changelog
@mastra/core@1.38.0
Minor Changes
-
Added
channels.threadContext.addSystemMessageto opt out of the built-in channel system message. By default,AgentChannelsinjects a short system message telling the agent which channel/platform a request came from (DM vs public, bot identity, etc.). SetaddSystemMessage: falseto skip it: (#17171)new Agent({ channels: { adapters: { slack: createSlackAdapter() }, threadContext: { addSystemMessage: false, }, }, }); -
Added
agent.sendMessage()andagent.queueMessage()APIs for sending user-authored input into agent threads. These are intended to be used withagent.subscribeToThread()and replace lower-levelagent.sendSignal()calls for regular user messages. (#17191)await agent.sendMessage('Continue with the latest user input', { resourceId, threadId }); await agent.queueMessage('Follow up after the active turn finishes', { resourceId, threadId }); await agent.sendMessage( { contents: [ { type: 'text', text: 'What is in this image?' }, { type: 'file', data: imageBase64, mediaType: 'image/png', filename: 'screenshot.png' }, ], }, { resourceId, threadId }, ); -
Support conditional, function-based tool approvals. (#17337)
- MCP tools that wrap a server-level
requireToolApprovalfunction are now honored end-to-end. The per-tool approval function was previously dropped when the agent converted MCP tools (it kept only the boolean flag), so conditional approval silently fell back to always-on.CoreToolBuildernow preserves aneedsApprovalFnattached directly to a tool instance. - The global
requireToolApprovaloption onagent.stream/agent.generatenow accepts a function in addition to a boolean. It is evaluated per tool call with the tool name, arguments, and request context, enabling policies such as regex allowlists on tool names. Returningtruerequires approval for that call;falseallows it. On error the call defaults to requiring approval. When a function policy is set, tool calls run sequentially so approval suspensions don't race. Durable agents and stored agents continue to accept only a boolean (a function degrades to requiring approval for every call, since their options must be serializable).
// Approve only tool calls whose name is not on an allowlist. const allowlist = /^(get|list|search)_/; await agent.generate('...', { requireToolApproval: ({ toolName }) => !allowlist.test(toolName), });- Precedence is unchanged from before: a per-tool approval function (
createTool({ requireApproval: fn })or an MCP-derivedneedsApprovalFn) is authoritative for that tool and overrides the global setting, so a tool can still opt out of approval by returningfalseeven when the global option is on. The only new behavior is that the global option may now be a function in addition to a boolean. - The previously implicit, runtime-attached per-tool approval predicate is now a typed contract:
@mastra/coreexportsNeedsApprovalFnand declares the optionalneedsApprovalFnproperty on theToolclass. The MCP client and the agent runtime now share this typed contract instead of reaching throughany. This is additive — no public API changes.
- MCP tools that wrap a server-level
-
Added harness events for session lifecycle updates, mode changes, model changes, and cloned threads. (#17290)
Users can now subscribe to harness events to observe harness activity.
Example
const unsubscribe = harness.subscribe(event => { console.log(event.id, event.type); }); -
Added agent override support to the agent and editor APIs. (#17227)
Code-defined agents can now declare which fields Studio may edit with the
editoroption:new Agent({ name: 'Weather Agent', model, editor: { instructions: true, tools: { description: true }, }, });The editor applies stored overrides only for fields the
editorconfig owns, so locked fields keep their code-defined values. Per-agenteditor: falselocks an agent entirely.MastraEditoraccepts asourcesetting that picks the editing experience:new MastraEditor({ source: 'code' });source: 'code'— the editor auto-wires aFilesystemStore(defaulting to./mastra/editor/, overridable withcodePath) when no editor storage is supplied, and persists overrides as deterministic per-agent JSON files.source: 'db'(default) — keeps the existing storage-backed flow against whatever storage the project has configured.
-
Added the
tool_provider_connectionsstorage domain. Stored agents can now persist per-agent ToolProvider config that round-trips on read/write/create. Runtime connection resolution (per-author, shared, caller-supplied) ships in a follow-up PR. (#17247)What you can do
- Pin a connection on a stored agent's config and have it round-trip on read/write/create.
- Persist multiple connections per toolkit so a follow-up runtime PR can fan-out to the right one at execution time.
Example
import { LibSQLStore } from '@mastra/libsql'; const storage = new LibSQLStore({ url: process.env.DATABASE_URL }); // Persist an OAuth connection that an agent can pin later await storage.toolProviders.upsertConnection({ authorId: 'user-123', providerId: 'composio', connectionId: 'auth_abc', toolkit: 'gmail', label: 'Work inbox', scope: 'per-author', }); // List a user's own connections (admin can omit authorId to list across users) const { items } = await storage.toolProviders.listConnectionsByAuthor({ authorId: 'user-123', providerId: 'composio', });Additive — existing stored agents continue to work unchanged. The runtime that consumes this domain ships in a follow-up PR.
PR 1 of 3 split from #17224.
-
Added opt-in MCP server instructions forwarding into agent system prompts. (#17155)
When an MCP server advertises instructions during initialization, you can now forward that guidance into the system prompt of agents that use the server's tools. This is opt-in — set
forwardInstructions: trueper server to enable it. Forwarded instructions are injected into the agent's system prompt, so only enable this for servers you trust.const mcp = new MCPClient({ servers: { db: { url: new URL('http://localhost:4111/mcp'), forwardInstructions: true, // opt in; defaults to false instructionsMaxLength: 512, // max chars forwarded per server }, }, }); const agent = new Agent({ id: 'db-agent', name: 'DB Agent', instructions: 'Help with database changes.', model, tools: await mcp.listTools(), });You can always inspect cached instructions without forwarding them:
const instructions = mcp.getServerInstructions(); // => { db: 'Always validate before migrating.', other: undefined } -
Add fire-and-forget workflow resume that returns immediately with
{ runId }without awaiting the run output. (#17230)For
@mastra/inngest, this skips thegetRunOutput()polling that previously raced a realtime subscription against the Inngest runs API and could surface spurious 404s even though the durable workflow was running fine.Run.resumeAsync()added to core: dispatches the resume in the background and returns{ runId }immediately. Engines that poll for results (Inngest) override it to skip polling entirely.InngestRun.resumeAsync()sends the resume event and returns{ runId }, skipping polling. Send-time failures (bad payload, event send failure) still reject synchronously and roll back the snapshot.- New
POST /workflows/:workflowId/resume-no-waitandPOST /agent-builder/:actionId/resume-no-waitroutes return{ runId }immediately. - New client SDK
run.resumeNoWait()resolves with{ runId }.
The existing
resumeAsync()client/server surface is unchanged and still resolves with the full workflow result, so there is no breaking change.resumeNoWaitis intentionally additive in v1. In Mastra v2 the fire-and-forget behavior is planned to become the default behavior ofresumeAsync()(mirroringstart/resumesemantics), at which pointresumeNoWaitand theresume-no-waitroutes will be removed. The code paths carryTODO(v2)comments documenting this consolidation. -
Added experimental Code Mode for agents.
createCodeModereturns anexecute_typescripttool plus generated instructions that let an agent write one TypeScript program to orchestrate your tools (batch withPromise.all, aggregate, and do math in a real runtime) instead of calling tools one at a time. Tools still run on the host with full validation and tracing; only the orchestration code runs in a workspace sandbox. (#17324)import { createCodeMode, createTool } from '@mastra/core/tools'; const { tool, instructions } = createCodeMode({ tools: { getTopProducts, getProductRatings }, }); const agent = new Agent({ instructions: ['You are a helpful assistant.', instructions], tools: { execute_typescript: tool }, }); -
Add per-entity file persistence and per-entity git history to
FilesystemVersionedHelpers. (#17225)FilesystemVersionedHelpersnow accepts three optional hooks that let a storage domain split a published entity across many per-entity JSON files (e.g.agents/<id>.json) instead of one shared map file:perEntityFilesDir— directory (under the FilesystemDB root) for per-entity files.shouldPersistToPerEntityFile(entity)— decide per published entity whether to write its snapshot to a per-entity file.perEntitySnapshotFilter(snapshot, entity)— filter the snapshot before writing it to the per-entity file (e.g. drop fields the user does not own).
When configured, the helper:
- Reads per-entity files on hydrate (alongside the shared map file).
- Writes published snapshots to per-entity files with stable alphabetical key ordering for friendly diffs.
- Walks per-entity file git history and surfaces each commit as a read-only
version in
listVersions(in addition to the existing shared-file git history). - Skips writing an empty shared map file when every published entity is persisted to per-entity files, so a code-only project does not end up with an empty stub committed to git.
Also adds
FilesystemDB.listDomainFiles,domainFileExists, andremoveDomainFilehelpers, and broadensGitHistory.getFileAtCommitto be generic so callers can request a per-entity snapshot type rather than the shared-map shape.Example — configure a domain to persist each entity to its own file:
import { FilesystemDB, FilesystemVersionedHelpers } from '@mastra/core/storage'; const db = new FilesystemDB('./mastra/editor'); // Entities that should be persisted as their own files. const codeModeEntityIds = new Set(['support-bot']); const agents = new FilesystemVersionedHelpers({ db, entitiesFile: 'agents.json', parentIdField: 'agentId', name: 'agents', versionMetadataFields: ['id', 'agentId', 'versionNumber', 'createdAt'], // New per-entity hooks: perEntityFilesDir: 'agents', // Decide per entity whether it gets its own file (vs. the shared agents.json). shouldPersistToPerEntityFile: entity => codeModeEntityIds.has(entity.id), // Drop fields that should not live in the per-entity file. perEntitySnapshotFilter: snapshot => { const { model, ...userOwned } = snapshot; return userOwned; }, }); // Published snapshots are now written to ./mastra/editor/agents/<id>.json, // and each git commit to those files shows up as a read-only version. const versions = await agents.listVersions({ agentId: 'support-bot' }, 'agentId'); -
Added support for resolving an agent's
voiceper request. (#17345)You can now pass
voiceas a resolver, just likeinstructions,tools, andmodel. Mastra runs the resolver on eachgetVoice()call and returns a fresh, session-owned voice instance. This fixes concurrent realtime and speech-to-speech sessions on a single deployed agent, where a shared voice instance previously let one session overwrite another session's WebSocket, tools, and instructions.A static
voicekeeps its existing shared behavior, so this change is backward compatible.Before
const agent = new Agent({ name: 'support-line', voice: new GeminiLiveVoice({ apiKey: KEY }), // shared across every session });After
const agent = new Agent({ name: 'support-line', voice: ({ requestContext }) => new GeminiLiveVoice({ apiKey: requestContext.get('apiKey') }), }); const voice = await agent.getVoice({ requestContext }); // owns its own ws/tools/instructions await voice.connect();The caller owns the lifecycle of a resolver instance and should call
disconnect()when the session ends. Theagent.voicegetter throws whenvoiceis a resolver because it has no request context; useagent.getVoice({ requestContext })instead. -
Workflows that suspend during dataset experiments now resume automatically. Provide resume data in your dataset items and the workflow will continue execution through multiple suspend/resume cycles. (#17378)
For multi-step workflows, use
resumeStepskeyed by step ID:const item = { input: { prompt: 'Draft a blog post' }, resumeSteps: { 'approval-step': { approved: true } }, };For single-step workflows, use flat
resumeData:const item = { input: { prompt: 'Draft a blog post' }, resumeData: { approved: true }, };Storage-backed items can use
metadata.resumeStepsormetadata.resumeDataas fallback. When no resume data is provided, the suspend payload is returned as output with guidance on how to add it. (#15382) -
Added request-aware filtering for ToolSearchProcessor search, load, and active tools. The filter hook receives the resolved tool ID as
toolName. (#16088)new ToolSearchProcessor({ tools, filter: ({ toolName, requestContext }) => { const plan = requestContext?.get('plan'); return plan === 'pro' || !toolName.startsWith('premium_'); }, }); -
Added
channels.resolveResourceIdto control whichresourceIdowns a channel thread's memory, separately from who sent the message. Useful for SSO apps that want a user's memory shared across web and a Feishu/Lark DM, or group chats scoped to the conversation instead of the sender. Only affects newly-created threads; return the provided default to keep current behavior. (#17471)new Agent({ // ... channels: { adapters: { slack: createSlackAdapter() }, resolveResourceId: async ({ thread, message }) => { if (thread.isDM) return resolveSsoUserId(message); // shared with web return thread.channelId; // group owns the memory }, }, }); -
Added the
disableInitoption to theMastraVectorbase class. When set totrue, vector stores skip creating schemas, extensions, tables, and indexes at application startup. This matches the existingdisableInitbehavior on storage adapters and is useful for deployments where schemas and indexes are created ahead of time by a privileged database role, while the application runs with a least-privilege role. (#17272)Usage
const vector = new PgVector({ id: 'vectors', connectionString: process.env.DATABASE_URL, disableInit: true, });The
MASTRA_DISABLE_STORAGE_INITenvironment variable also disables vector init, so a single flag prevents both storage and vector stores from creating schemas, tables, or indexes at startup. -
Storage adapters can now receive a narrow back-pointer to the Mastra instance (#17226) via
MastraCompositeStore.__registerMastra. This mirrors the existing registration pattern on agents, workflows, memory, scorers and processors, and lets a storage domain look up agents and editor config without pulling the full Mastra type into the storage layer (which would create a circular import).The reference is cascaded automatically to any parent composites and owned domain stores, and is wired both during Mastra construction and via
setStorage. The editor is registered after storage so editor-driven storage overlays observe the assigned storage.A new
StorageMastraRefinterface exposes only the methods storage needs today (getAgentById,getEditor).// Inside a domain store, read the registered reference after Mastra wires it up: class MyAgentsStore extends AgentsStorage { protected getEditorConfig(agentId: string) { // `this.mastra` is populated by MastraCompositeStore.__registerMastra, // which runs during Mastra construction and on setStorage(). const agent = this.mastra?.getAgentById?.(agentId); if (agent?.source !== 'code') return undefined; return agent.__getEditorConfig?.(); } } -
Added the v1 ToolProvider runtime, server routes, client SDK methods, and editor wiring that power OAuth-backed integrations on stored agents. (#17248)
Stored agents can now pin OAuth connections per toolkit
A stored agent's config accepts a new
toolProvidersshape that tells the runtime which connection to bind for each toolkit at execution time. Connections can be scoped per-author, shared across an org, or supplied by the caller.{ toolProviders: { composio: { connections: { gmail: [{ kind: 'author', toolkit: 'gmail', connectionId: 'auth_abc', scope: 'per-author' }], }, tools: { GMAIL_FETCH_EMAILS: { toolkit: 'gmail' }, }, }, }, }New client SDK surface for managing connections
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: '…' }); const composio = client.toolProvider('composio'); const { items } = await composio.listConnections({ toolkit: 'gmail' }); await composio.disconnectConnection('auth_abc');New
ToolProviderinterface for custom providersProviders implement a VNext surface (
listToolkitsVNext,listToolsVNext,resolveToolsVNext) plus the auth round-trip (authorize,getAuthStatus,listConnections,disconnectConnection,listConnectionFields,health). The Composio provider has been rewritten on this surface; the older catalog methods remain as@deprecatedshims for back-compat.Connections list responses use
page/perPagepagination, matching the rest of the server surface.Both stored agents (
editor.agent.getById(...)) and code-defined agents with stored overrides (editor.agent.applyStoredOverrides(...)) resolvetoolProvidersat request time, merging provider-resolved tools alongside code/registry/MCP/integration tools.Stored agents that don't set
toolProviderscontinue to work unchanged. The Studio/Builder UI ships separately.
Patch Changes
-
Improved per-message latency in channels by removing two awaited storage round-trips from the chat message dispatch path. (#17185)
-
Fixed Observational Memory and other tagged system context being lost when an agent uses Channels. Channel-specific context now adds itself alongside other processors' system messages instead of replacing them. (#17168)
-
Fixed output processors so they receive agent step lifecycle chunks during streaming. (#16687)
-
Fixed UnixSocketPubSub streaming so a slow or stuck subscriber no longer blocks active local streams or other subscribers. (#17302)
-
Added subscription-native tool approval APIs so approving or declining a tool call resumes through the active thread subscription instead of requiring a separate continuation stream. New messages are queued while a tool approval is waiting, preventing overlapping runs from duplicating approval requests. (#17311)
await agent.sendToolApproval({ resourceId: 'user-123', threadId: 'thread-123', toolCallId: 'tool-call-123', approved: true, }); -
Fixed
filterMessagesForPersistenceunconditionally trimming whitespace from text parts, which caused spaces between words to be lost when text parts were split by token boundaries in the streaming span-based persistence. The trim now only applies when working memory tags are actually stripped. (#17404) -
Fixed resumed agent observability spans so
agent.resumeStream()andagent.resumeGenerate()use the resume payload as theAGENT_RUNspan input instead of an empty array. (#17134)Resumed spans now also link back to the suspended trace when persisted tracing context is available, so human-in-the-loop approval flows show the decision payload and remain connected in tracing backends. Fixes #17075.
-
Fixed BatchPartsProcessor dropping the final stream part when a stopWhen condition stops the agent on a non-text part (such as a tool result). The processor batches text deltas and previously deferred the next non-text part to the following stream iteration; if the loop stopped on that part, it was lost. BatchPartsProcessor now returns the flushed batch and hands the non-text part back to the output processor runner, which re-drives it through the full output processor chain. As a result the final tool result always reaches the stream, and the flushed batch still passes through any downstream output processors (e.g. a moderation or PII processor configured after BatchPartsProcessor) instead of bypassing them. Fixes #17094. (#17342)
-
Fixed Convex workflow storage to save concurrent workflow updates atomically. (#16641)
-
Fixed processor-returned
systemMessageswiping tagged system messages owned by other processors (e.g. observational memory). Processorargs.systemMessagesnow exposes only the untagged system message bucket, so tagged messages owned by other processors are no longer round-tripped through the replacement API.MessageList.replaceAllSystemMessages()replaces only the untagged bucket and leaves tagged buckets intact. Final model input still receives both viamessageList.getAllSystemMessages(). (#16950) -
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility. -
Fixed
AgentExecutionOptions<undefined>(and its public/inner variants) incorrectly requiring astructuredOutputproperty. When the output type isundefinedornull— including a fully-nullish union such asundefined | null—structuredOutputis now correctly optional, regardless of thestrictNullCheckssetting. This is the shape produced byAgentConfig.defaultOptions, sodefaultOptions: { maxSteps: 50 }now type-checks without a spuriousstructuredOutputrequirement. Object output types still requirestructuredOutputas before. (#17131) -
Fixes a crash where OpenAI rejects resumed tool-approval requests with
AI_APICallError: Duplicate item found with id rs_*. (#17439)The same message stored in PostgreSQL's
jsonbcolumn (workflow snapshot) andtextcolumn (messages) could have different JSON key orders, causing the deduplication check to treat them as different messages and send the same reasoning item twice. Both representations now compare equal regardless of key order. -
Fixed a render crash when loading stored threads containing signal messages (such as system reminders). Non-user signal data parts are now merged onto the neighboring assistant message instead of becoming standalone system messages that break assistant-ui. (#17429)
-
Added Alibaba provider support for Qwen models. You can now use Qwen, DashScope, and other Alibaba models with automatic provider detection. (#17433)
Example usage:
import { Mastra } from '@mastra/core'; const mastra = new Mastra(); // Use any Alibaba variant - automatically detected const agent = mastra.getAgent('myAgent'); const result = await agent.generate({ model: '__GATEWAY_ALIBABA_MODEL__', messages: [{ role: 'user', content: 'Hello' }], });Works with all Alibaba variants (alibaba, alibaba-cn, alibaba-coding-plan, etc.) and future variants like alibaba-coding-plan-cn-v2.
-
Fixed observational memory replaying previously observed assistant responses during reprocessing, so past assistant messages no longer reappear in later turns. (#17338)
-
Use
queueMessage()for Harness follow-up scheduling while preserving queued follow-up display state. (#17191) -
Asset download errors now include the failing URL so callers can identify which media link broke and recover from it (e.g. drop the dead part on retry). The URL appears redacted (query string and fragment stripped) in the error message and in full on
error.details.url. (#17069) -
Fixed output processor state continuity for step lifecycle chunks during streaming. Lifecycle chunks (e.g.
step-finish) routed through the streamoutputWriternow share the same per-processor state map as the main model-output path, so state set inprocessOutputStreamwhile handling content chunks is visible when the lifecycle chunk is processed (and inprocessOutputResult). Previously these chunks were handled with an isolated, empty state. (#17370)Note: as part of routing lifecycle chunks through output processors, an aborted stream now surfaces content the model produced before the abort (e.g. a
text-startchunk and partialtext) instead of dropping it. Consumers that assumed an aborted result always had empty text may now observe partial output. -
Removed a stale AI SDK UI utils dependency from @mastra/core so projects using Zod 4 do not get Zod 3 peer dependency warnings from core. (#16994)
-
Fixed AGENT_RUN spans not closing when an agent stream is aborted mid-flight (e.g. browser disconnect or
AbortController.abort()). Aborted runs now end with{ status: 'aborted', reason: 'abort' }so traces are exported to observability backends. (#17203) -
Improved active workflow run listing latency. (#17374)
-
Mastra.shutdown()now releases storage resources automatically. Stores that expose aclose()lifecycle hook (such asLibSQLStore) are closed during shutdown, so file handles are freed and the storage directory can be removed cleanly afterward, including on Windows. (#17306)const mastra = new Mastra({ storage }); // Storage is closed for you — no manual cleanup needed await mastra.shutdown(); -
Add
updateThreadas an abstract method on theMastraMemorybase class and implement it inMockMemory. Previously the method existed only on the concreteMemorysubclass, so callingupdateThreadon a variable typed asMastraMemory(or any otherMastraMemorysubclass) produced a TypeScript error. Callers can now rename or re-title threads through the base class API without casting. (#17130) -
Fixed sub-agent version resolution in supervisor mode. Sub-agents now inherit the parent's draft/published version semantics — when chatting in the editor (draft mode), sub-agents resolve to their latest draft version; in the main agent chat (published mode), sub-agents resolve to their published version. Previously, sub-agents without explicit per-agent version overrides always fell back to the code-defined default, ignoring the parent's version context. (#17165)
-
Fixed read_file line ranges when offsets are past the end of a file (#17275)
-
Fixed FGA-enabled MCP servers so OAuth authInfo can be mapped to a Mastra user before tools/list and tools/call authorization. (#17475)
-
Fixed forEach workflow steps losing completed outputs and status during resume. (#17294)
-
Reduced overhead when streaming tool calls in agentic workflows (#17354)
-
Fixed noisy "Cannot get workflow run. Mastra storage is not initialized" debug logs that appeared on every
agent.generate()andagent.stream()call when the agent's Mastra instance had storage configured. (#17344)The internal workflow that runs each agent call never received the parent Mastra instance, so it could not see configured storage and logged the warning before falling back to in-memory state. It now receives the Mastra instance. It still does not write any of its own snapshots to your storage, so no extra rows are created.
-
Fixed Google model routing to accept GOOGLE_GENERATIVE_AI_API_KEY when GOOGLE_API_KEY is not set. (#17343)
-
Fixed grep context output so overlapping matches are shown once. (#17274)
-
Added
jsonPromptInjectionto the scorerjudgeconfig so users can opt out of nativeresponse_formatfor models that don't support it (e.g. some Groq Llama models). Previously, every scorer invocation made a wasted 400 API call before falling back to prompt injection. (#17046)import { createScorer } from '@mastra/core/evals'; const scorer = createScorer({ id: 'translation-quality', description: 'Evaluates translation quality', judge: { model: 'groq/llama-3.3-70b-versatile', instructions: 'You are an expert evaluator…', jsonPromptInjection: true, // skip the unsupported `response_format` attempt }, });Fixes #17040.
-
Added native multimodal tool-result support. Core now converts MCP-style tool results with image and audio
contentparts into model-native media output when building model prompts, without requiring MCP tools to persist duplicate media payloads inproviderMetadata.mastra.modelOutput. (#16866)return { content: [ { type: 'text', text: 'Screenshot captured' }, { type: 'image', data: base64Png, mimeType: 'image/png' }, ], }; -
Fixed
MASTRA_TELEMETRY_DISABLEDopt-out detection. The values1,true, andyes(case-insensitive, trimmed) now reliably disable telemetry in both@mastra/coreenterprise events and themastraCLI's PostHog analytics. (#16990)Previously,
@mastra/coreonly treated the literal string'1'as disabled, so common opt-out values likeMASTRA_TELEMETRY_DISABLED=truesilently kept telemetry on.The
mastraCLI'sPosthogAnalyticsconstructor now also short-circuits when telemetry is disabled — no disk I/O, no tracking ID generation, no PostHog client. Previously the config file (mastra-cli.json) was written even when telemetry was disabled.Example:
# .env — any of these now reliably disable telemetry MASTRA_TELEMETRY_DISABLED=true MASTRA_TELEMETRY_DISABLED=1 MASTRA_TELEMETRY_DISABLED=yes -
Improved PIIDetector streaming performance. (#17377)
- Removed per-chunk LLM calls during streaming PII checks.
- Added local regex detection for common PII types (email, phone, SSN, credit card, IP address, API keys, URLs, UUIDs, crypto wallets, and IBAN).
- Added regex carryover buffer across chunk boundaries to catch split PII patterns.
- Buffered context-dependent PII types (names, addresses, dates of birth) with periodic LLM calls at configurable thresholds.
- Added
bufferSizeoption (default: 200) to control LLM buffer flush threshold. - Reduced streaming API cost, latency, and rate-limit pressure.
Closes #16466.
-
Fixed a TypeScript TS2589 "type instantiation is excessively deep" error when using Mastra alongside deeply-generic libraries such as @hono/zod-openapi. (#17339)
-
Improved observability and error isolation in the v1 ToolProvider runtime. (#17248)
Better visibility into connection-scope misconfiguration
When an agent runs with a stored ToolProvider connection whose scope cannot be resolved from the request context, the runtime now logs a one-shot warning and falls back to a shared bucket instead of silently routing every caller to the same OAuth account. Multi-tenant deployments get a clear signal when their identity wiring isn't reaching the runtime.
One bad toolkit no longer disables sibling providers
If a provider returns more connections for a toolkit than its declared capabilities allow, the runtime now logs and skips that toolkit instead of throwing. Other providers and other toolkits on the same agent continue to resolve normally.
-
Workflows now support an optional
metadatafield for attaching custom key-value data such asdisplayName,author, orcategory. Metadata is preserved through serialization and returned in workflow info API responses. (#17355)// Define a workflow with metadata const myWorkflow = createWorkflow({ id: 'data-processing', metadata: { displayName: 'Data Processing Pipeline', category: 'ETL', }, inputSchema: z.object({ ... }), outputSchema: z.object({ ... }), }); // Retrieve workflow info with metadata via the Mastra Server API const workflowInfo = await mastraClient.getWorkflow('data-processing'); console.log(workflowInfo.metadata?.displayName); // "Data Processing Pipeline"
@mastra/agent-browser@0.3.0
Minor Changes
-
Add
waitUntilsupport tobrowser_click,browser_press, andbrowser_select. When provided, the tool waits for the page to reach the given load state (load,domcontentloaded, ornetworkidle) after the action completes, preventing the nextbrowser_snapshotfrom capturing stale DOM when the interaction triggers navigation. The parameter is optional and behaviour is unchanged when omitted. (#17426)Usage example:
await browser_click({ ref: '@e1', waitUntil: 'domcontentloaded', timeout: 5000 });Fixes #17397.
-
Added extensibility hooks for custom browser providers (e.g. Firecrawl Browser Sandbox). (#15724)
- New
createThreadManagerconfig option to inject a custom thread manager factory - Exported
AgentBrowserThreadManagerclass and related types (AgentBrowserSession,AgentBrowserThreadManagerConfig,CreateAgentBrowserThreadManager) - Changed several internal members from
privatetoprotectedto support subclassing
- New
Patch Changes
@mastra/agentcore@0.2.0
Minor Changes
-
Added AWS Bedrock AgentCore Runtime sandbox support. (#16642)
You can now run Workspace commands in AWS Bedrock AgentCore Runtime through a sandbox provider.
import { AgentCoreRuntimeSandbox } from '@mastra/agentcore'; const sandbox = new AgentCoreRuntimeSandbox({ region: 'us-west-2', agentRuntimeArn: process.env.AGENTCORE_RUNTIME_ARN!, }); const result = await sandbox.executeCommand('node', ['--version']);
Patch Changes
@mastra/ai-sdk@1.4.4
Patch Changes
- Fixed processor middleware so
args.systemMessagesonly contains untagged system messages. Tagged processor-owned system messages stay on the message list and are still included in the final model input. (#16950)
@mastra/blaxel@0.4.0
Minor Changes
- Added region support to Blaxel sandboxes:
new BlaxelSandbox({ region: 'eu-west-1' }). When omitted, Mastra falls back toBL_REGIONand thenauto. (#16555)
Patch Changes
@mastra/brightdata@0.2.1
Patch Changes
-
Fix Bright Data tools under Bun by replacing the SDK runtime client with fetch-based REST calls. (#16630)
-
Harden Bright Data search input handling. Country and language codes are now validated as alphabetic two-letter codes, the
getBrightDataClient().search.google()client validates and lowercase-normalizeslanguagebefore the request, and structured JSON (brd_json=1) is only requested when the searchformatisjsonso callers can obtain a true raw SERP response. (#17341)
@mastra/browser-firecrawl@0.1.0
Minor Changes
-
Initial release: Firecrawl Browser Sandbox integration for Mastra. (#15724)
FirecrawlBrowserextendsAgentBrowserto run the same deterministic browser tools (snapshot+refs, 16 tools, Playwright over CDP) against Firecrawl's cloud-hosted Chrome instances instead of local or self-hosted browsers.Features:
- Cloud-hosted Chrome via Firecrawl Browser Sandbox API
- Same tool surface as
@mastra/agent-browser(~16 browser automation tools) - Thread-scoped browser isolation (
scope: 'thread') - Automatic session cleanup on close
Usage:
import { FirecrawlBrowser } from '@mastra/browser-firecrawl'; const browser = new FirecrawlBrowser({ firecrawlApiKey: process.env.FIRECRAWL_API_KEY, scope: 'thread', }); const agent = mastra.getAgent('my-agent', { browser });
Patch Changes
@mastra/claude@0.1.0
Minor Changes
-
Added
@mastra/claude, a package for running Claude Agent SDK agents through Mastra. (#16906)Create a Claude SDK agent, register it with Mastra, and call
generate()orstream()with Mastra-compatible outputs. Runs keep Claude SDK usage, cost estimates, and observability data available to Mastra.import { ClaudeSDKAgent } from '@mastra/claude'; export const claudeAgent = new ClaudeSDKAgent({ id: 'claude-sdk-agent', description: 'Use Claude Agent SDK through Mastra.', sdkOptions: { model: process.env.CLAUDE_CODE_MODEL, cwd: process.cwd(), }, });
Patch Changes
@mastra/clickhouse@1.9.1
Patch Changes
-
Added the
tool_provider_connectionsstorage domain. Stored agents can now persist per-agent ToolProvider config that round-trips on read/write/create. Runtime connection resolution (per-author, shared, caller-supplied) ships in a follow-up PR. (#17247)What you can do
- Pin a connection on a stored agent's config and have it round-trip on read/write/create.
- Persist multiple connections per toolkit so a follow-up runtime PR can fan-out to the right one at execution time.
Example
import { LibSQLStore } from '@mastra/libsql'; const storage = new LibSQLStore({ url: process.env.DATABASE_URL }); // Persist an OAuth connection that an agent can pin later await storage.toolProviders.upsertConnection({ authorId: 'user-123', providerId: 'composio', connectionId: 'auth_abc', toolkit: 'gmail', label: 'Work inbox', scope: 'per-author', }); // List a user's own connections (admin can omit authorId to list across users) const { items } = await storage.toolProviders.listConnectionsByAuthor({ authorId: 'user-123', providerId: 'composio', });Additive — existing stored agents continue to work unchanged. The runtime that consumes this domain ships in a follow-up PR.
PR 1 of 3 split from #17224.
@mastra/client-js@1.22.0
Minor Changes
-
Added an agent override export API and server-side ownership enforcement. (#17228)
The server and client now expose an agent override export endpoint so Studio can download an agent's overrides as JSON for review or commit workflows. Saves are enforced server-side against each agent's
editorconfig, so only owned fields (instructions, tools, or tool descriptions) are persisted and fields locked by theeditorconfig are stripped.The system packages response also reports the active editor
sourceso clients can render the correct editing experience. -
Add fire-and-forget workflow resume that returns immediately with
{ runId }without awaiting the run output. (#17230)For
@mastra/inngest, this skips thegetRunOutput()polling that previously raced a realtime subscription against the Inngest runs API and could surface spurious 404s even though the durable workflow was running fine.Run.resumeAsync()added to core: dispatches the resume in the background and returns{ runId }immediately. Engines that poll for results (Inngest) override it to skip polling entirely.InngestRun.resumeAsync()sends the resume event and returns{ runId }, skipping polling. Send-time failures (bad payload, event send failure) still reject synchronously and roll back the snapshot.- New
POST /workflows/:workflowId/resume-no-waitandPOST /agent-builder/:actionId/resume-no-waitroutes return{ runId }immediately. - New client SDK
run.resumeNoWait()resolves with{ runId }.
The existing
resumeAsync()client/server surface is unchanged and still resolves with the full workflow result, so there is no breaking change.resumeNoWaitis intentionally additive in v1. In Mastra v2 the fire-and-forget behavior is planned to become the default behavior ofresumeAsync()(mirroringstart/resumesemantics), at which pointresumeNoWaitand theresume-no-waitroutes will be removed. The code paths carryTODO(v2)comments documenting this consolidation. -
Added a
PATCH /tool-providers/:providerId/connections/:connectionIdendpoint and matching client SDK method so authors can rename a connection's display label after creation. (#17249)Rename a connection from the client SDK
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: '…' }); await client.getToolProvider('composio').updateConnection('auth_abc', { label: 'Work inbox', });Pass
label: null(or an empty string) to clear the existing label. Labels are 1–32 characters and accept letters, digits, spaces, underscores, and hyphens ([A-Za-z0-9 _-]+).Ownership enforced server-side
Non-owners get a 403 unless they hold
tool-providers:admin. Shared connections are reachable by every author. The label is stored on the connection row itself, so the rename flows to every agent that pins the connection — no per-agent edit needed. -
Added the v1 ToolProvider runtime, server routes, client SDK methods, and editor wiring that power OAuth-backed integrations on stored agents. (#17248)
Stored agents can now pin OAuth connections per toolkit
A stored agent's config accepts a new
toolProvidersshape that tells the runtime which connection to bind for each toolkit at execution time. Connections can be scoped per-author, shared across an org, or supplied by the caller.{ toolProviders: { composio: { connections: { gmail: [{ kind: 'author', toolkit: 'gmail', connectionId: 'auth_abc', scope: 'per-author' }], }, tools: { GMAIL_FETCH_EMAILS: { toolkit: 'gmail' }, }, }, }, }New client SDK surface for managing connections
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: '…' }); const composio = client.toolProvider('composio'); const { items } = await composio.listConnections({ toolkit: 'gmail' }); await composio.disconnectConnection('auth_abc');New
ToolProviderinterface for custom providersProviders implement a VNext surface (
listToolkitsVNext,listToolsVNext,resolveToolsVNext) plus the auth round-trip (authorize,getAuthStatus,listConnections,disconnectConnection,listConnectionFields,health). The Composio provider has been rewritten on this surface; the older catalog methods remain as@deprecatedshims for back-compat.Connections list responses use
page/perPagepagination, matching the rest of the server surface.Both stored agents (
editor.agent.getById(...)) and code-defined agents with stored overrides (editor.agent.applyStoredOverrides(...)) resolvetoolProvidersat request time, merging provider-resolved tools alongside code/registry/MCP/integration tools.Stored agents that don't set
toolProviderscontinue to work unchanged. The Studio/Builder UI ships separately.
Patch Changes
-
Separated thread subscription cleanup from active-run aborts so closing or switching a listener only unsubscribes that listener, while explicit cancel still aborts the active run. (#17310)
-
Added subscription-native tool approval APIs so approving or declining a tool call resumes through the active thread subscription instead of requiring a separate continuation stream. New messages are queued while a tool approval is waiting, preventing overlapping runs from duplicating approval requests. (#17311)
await agent.sendToolApproval({ resourceId: 'user-123', threadId: 'thread-123', toolCallId: 'tool-call-123', approved: true, }); -
Fixed sub-agent version resolution in supervisor mode. Sub-agents now inherit the parent's draft/published version semantics — when chatting in the editor (draft mode), sub-agents resolve to their latest draft version; in the main agent chat (published mode), sub-agents resolve to their published version. Previously, sub-agents without explicit per-agent version overrides always fell back to the code-defined default, ignoring the parent's version context. (#17165)
-
Hardened v1 ToolProvider connection routes and SDK forwarding. (#17248)
Fail closed on unknown
connectionIdDELETE /tool-providers/:providerId/connections/:connectionIdandGET …/usagenow return403when storage is configured but no persisted row matches the suppliedconnectionIdand the caller isn't an admin. Previously these routes fell through to the caller's ownauthorId, which let non-admin callers probe (and trigger provider-siderevokeConnectionfor) IDs that didn't belong to them.Aligned authorize label validation with stored label rules
POST /tool-providers/:providerId/authorizenow enforces the same label rules the storedtoolProvidersconfig uses (min(1),max(32),/^[A-Za-z0-9 _-]+$/). Labels that passauthorizeare now guaranteed to pass downstream stored-agent validation.SDK forwards
toolkiton connection-scoped operations@mastra/client-js:await client.toolProviders.get('composio').disconnectConnection('ca_xxx', { toolkit: 'gmail', force: true, }); const usage = await client.toolProviders.get('composio').getConnectionUsage('ca_xxx', { toolkit: 'gmail' });disconnectConnectionnow forwardsparams.toolkit(previously dropped) andgetConnectionUsageaccepts an optional{ toolkit }parameter so toolkit-scoped connection lookups disambiguate correctly server-side. -
Improved observability and error isolation in the v1 ToolProvider runtime. (#17248)
Better visibility into connection-scope misconfiguration
When an agent runs with a stored ToolProvider connection whose scope cannot be resolved from the request context, the runtime now logs a one-shot warning and falls back to a shared bucket instead of silently routing every caller to the same OAuth account. Multi-tenant deployments get a clear signal when their identity wiring isn't reaching the runtime.
One bad toolkit no longer disables sibling providers
If a provider returns more connections for a toolkit than its declared capabilities allow, the runtime now logs and skips that toolkit instead of throwing. Other providers and other toolkits on the same agent continue to resolve normally.
-
Workflows now support an optional
metadatafield for attaching custom key-value data such asdisplayName,author, orcategory. Metadata is preserved through serialization and returned in workflow info API responses. (#17355)// Define a workflow with metadata const myWorkflow = createWorkflow({ id: 'data-processing', metadata: { displayName: 'Data Processing Pipeline', category: 'ETL', }, inputSchema: z.object({ ... }), outputSchema: z.object({ ... }), }); // Retrieve workflow info with metadata via the Mastra Server API const workflowInfo = await mastraClient.getWorkflow('data-processing'); console.log(workflowInfo.metadata?.displayName); // "Data Processing Pipeline"
@mastra/cloudflare@1.4.1
Patch Changes
-
Added the
tool_provider_connectionsstorage domain. Stored agents can now persist per-agent ToolProvider config that round-trips on read/write/create. Runtime connection resolution (per-author, shared, caller-supplied) ships in a follow-up PR. (#17247)What you can do
- Pin a connection on a stored agent's config and have it round-trip on read/write/create.
- Persist multiple connections per toolkit so a follow-up runtime PR can fan-out to the right one at execution time.
Example
import { LibSQLStore } from '@mastra/libsql'; const storage = new LibSQLStore({ url: process.env.DATABASE_URL }); // Persist an OAuth connection that an agent can pin later await storage.toolProviders.upsertConnection({ authorId: 'user-123', providerId: 'composio', connectionId: 'auth_abc', toolkit: 'gmail', label: 'Work inbox', scope: 'per-author', }); // List a user's own connections (admin can omit authorId to list across users) const { items } = await storage.toolProviders.listConnectionsByAuthor({ authorId: 'user-123', providerId: 'composio', });Additive — existing stored agents continue to work unchanged. The runtime that consumes this domain ships in a follow-up PR.
PR 1 of 3 split from #17224.
@mastra/convex@1.2.1
Patch Changes
-
Fixed Convex message lookups so they use indexed ids instead of a capped full-table scan. (#17409)
-
Fixed Convex workflow storage to save concurrent workflow updates atomically. (#16641)
-
Fixed concurrent Convex memory updates from overwriting each other (#17299)
-
Fixed forEach workflow steps losing completed outputs and status during resume. (#17294)
-
Fixed Convex JS vector scans to include all paginated vectors. (#17270)
@mastra/cursor@0.1.0
Minor Changes
-
Added
@mastra/cursor, a package for running Cursor SDK agents through Mastra. (#16906)Create a Cursor SDK agent, register it with Mastra, and call
generate()orstream()with Mastra-compatible outputs. Runs keep Cursor SDK usage and observability data available to Mastra.import { CursorSDKAgent } from '@mastra/cursor'; export const cursorAgent = new CursorSDKAgent({ id: 'cursor-sdk-agent', description: 'Use Cursor Agent SDK through Mastra.', sdkOptions: { apiKey: process.env.CURSOR_API_KEY, model: { id: process.env.CURSOR_MODEL_ID! }, local: { cwd: process.cwd(), }, }, });
Patch Changes
@mastra/daytona@0.4.1
Patch Changes
-
Fixed Daytona command execution to reject invalid environment variable names (#17279)
-
Fixed sandbox execution results to report killed and timed out commands. (#17281)
@mastra/deployer@1.38.0
Patch Changes
-
The server now installs SIGINT/SIGTERM handlers and runs
mastra.shutdown()before exiting, allowing storage backends to release resources cleanly instead of being terminated mid-flight. (#17413) -
Fixed Studio playground browser telemetry not respecting
MASTRA_TELEMETRY_DISABLED. The dev server was hardcoding an empty value into the servedindex.html, sowindow.MASTRA_TELEMETRY_DISABLEDwas always falsy in the browser and the playground React app initialized PostHog regardless of the user's.env. The dev server now propagatesprocess.env.MASTRA_TELEMETRY_DISABLEDto the browser, where the playground applies the same canonical opt-out parsing as the rest of the framework. (#16990)Before: Setting
MASTRA_TELEMETRY_DISABLED=truein.envhad no effect on playground network requests to PostHog.After:
# .env MASTRA_TELEMETRY_DISABLED=truePlayground analytics are now disabled.
-
Fixed false-positive LOCAL_STORAGE_PATH preflight errors caused by library code (e.g. Agent Builder prompt templates). Added a Rollup plugin (
mastra-local-storage-detector) to the deployer that detects host-local storage URLs during bundling — only user modules are inspected (node_modules excluded), and tree-shaken code is ignored. The CLI preflight check now reads this bundler-generated metadata instead of scanning raw bundle source. (#17286) -
Enabled Studio via the CLI and deployers to use agent signal subscriptions by default while preserving
MASTRA_AGENT_SIGNALS=false,enableThreadSignals: false, and explicit legacy Stream as opt-outs. The ReactuseChat()hook remains opt-in for SDK consumers viaenableThreadSignals: true. (#17313)
@mastra/deployer-vercel@1.1.33
Patch Changes
- Enabled Studio via the CLI and deployers to use agent signal subscriptions by default while preserving
MASTRA_AGENT_SIGNALS=false,enableThreadSignals: false, and explicit legacy Stream as opt-outs. The ReactuseChat()hook remains opt-in for SDK consumers viaenableThreadSignals: true. (#17313)
@mastra/docker@0.3.0
Minor Changes
-
Added
nameoption toDockerSandboxfor setting the container's display name. (#17266)Previously,
DockerSandboxdid not forward a container name to Docker, so containers were created with random names likegracious_tueven though the docs impliedidwas used for naming. The newnameoption (defaults toid) is now passed todocker run --nameand is sanitized to fit Docker's container-name rules ([a-zA-Z0-9_.-]).import { DockerSandbox } from '@mastra/docker'; // Before: container ended up with a random Docker-assigned name new DockerSandbox({ id: 'user-1001' }); // After: the id is used as the container name by default new DockerSandbox({ id: 'user-1001' }); // → docker ps shows 'user-1001' // Or override explicitly new DockerSandbox({ id: 'user-1001', name: 'tenant-acme-dev' });Closes #17263.
Patch Changes
- Fixed sandbox execution results to report killed and timed out commands. (#17281)
@mastra/duckdb@1.4.1
Patch Changes
- Fixed DuckDB "Conflicting lock is held" error on
mastra devhot reload.DuckDBStorenow releases its native file lock on shutdown so the restarted dev process can reopen the same database file. (#17413)
@mastra/e2b@0.3.2
Patch Changes
- Fix E2B sandbox creation failing with "Sandbox.betaCreate is not a function" on e2b SDK 2.24.0+. The adapter now uses the stable
Sandbox.create()API withlifecycle: { onTimeout: 'pause' }(replacing the removedbetaCreate/autoPause), and requirese2b >= 2.24.0. (#17261)
@mastra/editor@0.11.0
Minor Changes
-
Added agent override support to the agent and editor APIs. (#17227)
Code-defined agents can now declare which fields Studio may edit with the
editoroption:new Agent({ name: 'Weather Agent', model, editor: { instructions: true, tools: { description: true }, }, });The editor applies stored overrides only for fields the
editorconfig owns, so locked fields keep their code-defined values. Per-agenteditor: falselocks an agent entirely.MastraEditoraccepts asourcesetting that picks the editing experience:new MastraEditor({ source: 'code' });source: 'code'— the editor auto-wires aFilesystemStore(defaulting to./mastra/editor/, overridable withcodePath) when no editor storage is supplied, and persists overrides as deterministic per-agent JSON files.source: 'db'(default) — keeps the existing storage-backed flow against whatever storage the project has configured.
-
Added the v1 ToolProvider runtime, server routes, client SDK methods, and editor wiring that power OAuth-backed integrations on stored agents. (#17248)
Stored agents can now pin OAuth connections per toolkit
A stored agent's config accepts a new
toolProvidersshape that tells the runtime which connection to bind for each toolkit at execution time. Connections can be scoped per-author, shared across an org, or supplied by the caller.{ toolProviders: { composio: { connections: { gmail: [{ kind: 'author', toolkit: 'gmail', connectionId: 'auth_abc', scope: 'per-author' }], }, tools: { GMAIL_FETCH_EMAILS: { toolkit: 'gmail' }, }, }, }, }New client SDK surface for managing connections
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: '…' }); const composio = client.toolProvider('composio'); const { items } = await composio.listConnections({ toolkit: 'gmail' }); await composio.disconnectConnection('auth_abc');New
ToolProviderinterface for custom providersProviders implement a VNext surface (
listToolkitsVNext,listToolsVNext,resolveToolsVNext) plus the auth round-trip (authorize,getAuthStatus,listConnections,disconnectConnection,listConnectionFields,health). The Composio provider has been rewritten on this surface; the older catalog methods remain as@deprecatedshims for back-compat.Connections list responses use
page/perPagepagination, matching the rest of the server surface.Both stored agents (
editor.agent.getById(...)) and code-defined agents with stored overrides (editor.agent.applyStoredOverrides(...)) resolvetoolProvidersat request time, merging provider-resolved tools alongside code/registry/MCP/integration tools.Stored agents that don't set
toolProviderscontinue to work unchanged. The Studio/Builder UI ships separately.
Patch Changes
-
Improved observability and error isolation in the v1 ToolProvider runtime. (#17248)
Better visibility into connection-scope misconfiguration
When an agent runs with a stored ToolProvider connection whose scope cannot be resolved from the request context, the runtime now logs a one-shot warning and falls back to a shared bucket instead of silently routing every caller to the same OAuth account. Multi-tenant deployments get a clear signal when their identity wiring isn't reaching the runtime.
One bad toolkit no longer disables sibling providers
If a provider returns more connections for a toolkit than its declared capabilities allow, the runtime now logs and skips that toolkit instead of throwing. Other providers and other toolkits on the same agent continue to resolve normally.
-
Improved the Agent Builder system prompt so it produces more reliable agents from starter cards and freeform prompts. (#17424)
-
Agent Builder is now more resilient to transient and provider-specific stream errors out of the box. The built-in builder agent ships with three error processors enabled by default — automatic retry of transient OpenAI errors (such as
server_error,rate_limit, andoverloaded), recovery from Anthropic 400 prefill rejections, and per-provider history-shape fixes — so flaky LLM calls no longer end the conversation. You can still pass your ownerrorProcessorstocreateBuilderAgentto extend or replace these defaults. (#17481)
@mastra/evals@1.2.4
Patch Changes
- Fixed the hallucination and tool-usage scorers returning incorrect scores when observable memory is enabled. These scorers now detect tool calls in every message format, so responses are no longer wrongly scored as fully hallucinated or as using zero tools. (#17321)
@mastra/express@1.3.26
Patch Changes
-
Added sseFlushOnConnect route option to scope the SSE connected comment to subscribe endpoints only (#17158)
-
Fixed validation error responses on routes with
bodySchema,queryParamSchema, orpathParamSchemalosing field path information when consumers pinzod@^3. Responses now return the actual field name inissues[].field(e.g."agent_id") instead of"unknown"with the raw Zod issues serialized intoissues[0].message. Fixes #17167. (#17172) -
Scoped the SSE connected comment to subscribe routes only and added SSE comment passthrough for Fastify and NestJS adapters (#17158)
@mastra/fastembed@1.1.2
Patch Changes
- Fixed FastEmbed so repeated embedding calls reuse loaded models instead of loading a new model each time. (#17303)
@mastra/fastify@1.3.26
Patch Changes
-
Added sseFlushOnConnect route option to scope the SSE connected comment to subscribe endpoints only (#17158)
-
Fixed custom API route responses dropping headers set by Fastify plugins. Headers applied via Fastify hooks (e.g.
Access-Control-Allow-Originfrom@fastify/cors) were overwritten when the adapter hijacked the reply to stream the custom route response. The adapter now merges hook-set headers into the response before hijack — matching the behavior already implemented for streaming routes. (#15719) -
Fixed validation error responses on routes with
bodySchema,queryParamSchema, orpathParamSchemalosing field path information when consumers pinzod@^3. Responses now return the actual field name inissues[].field(e.g."agent_id") instead of"unknown"with the raw Zod issues serialized intoissues[0].message. Fixes #17167. (#17172) -
Scoped the SSE connected comment to subscribe routes only and added SSE comment passthrough for Fastify and NestJS adapters (#17158)
@mastra/hono@1.4.21
Patch Changes
-
Added sseFlushOnConnect route option to scope the SSE connected comment to subscribe endpoints only (#17158)
-
Fixed validation error responses on routes with
bodySchema,queryParamSchema, orpathParamSchemalosing field path information when consumers pinzod@^3. Responses now return the actual field name inissues[].field(e.g."agent_id") instead of"unknown"with the raw Zod issues serialized intoissues[0].message. Fixes #17167. (#17172) -
Scoped the SSE connected comment to subscribe routes only and added SSE comment passthrough for Fastify and NestJS adapters (#17158)
@mastra/inngest@1.5.0
Minor Changes
-
Add fire-and-forget workflow resume that returns immediately with
{ runId }without awaiting the run output. (#17230)For
@mastra/inngest, this skips thegetRunOutput()polling that previously raced a realtime subscription against the Inngest runs API and could surface spurious 404s even though the durable workflow was running fine.Run.resumeAsync()added to core: dispatches the resume in the background and returns{ runId }immediately. Engines that poll for results (Inngest) override it to skip polling entirely.InngestRun.resumeAsync()sends the resume event and returns{ runId }, skipping polling. Send-time failures (bad payload, event send failure) still reject synchronously and roll back the snapshot.- New
POST /workflows/:workflowId/resume-no-waitandPOST /agent-builder/:actionId/resume-no-waitroutes return{ runId }immediately. - New client SDK
run.resumeNoWait()resolves with{ runId }.
The existing
resumeAsync()client/server surface is unchanged and still resolves with the full workflow result, so there is no breaking change.resumeNoWaitis intentionally additive in v1. In Mastra v2 the fire-and-forget behavior is planned to become the default behavior ofresumeAsync()(mirroringstart/resumesemantics), at which pointresumeNoWaitand theresume-no-waitroutes will be removed. The code paths carryTODO(v2)comments documenting this consolidation. -
Added
connect()to support Inngest Connect for Mastra workflows. Use this when running workflow execution in a dedicated long-running worker process that should not expose an inbound HTTP endpoint: (#17064)import { connect } from '@mastra/inngest/connect'; await connect({ mastra, inngest, instanceId: 'worker-1', maxWorkerConcurrency: 10, });connect()uses the same Mastra workflow functions asserve(), including nested and cron workflows.serve()is unchanged.
Patch Changes
- Fixed processor workflow steps so
args.systemMessagesonly contains untagged system messages. Tagged processor-owned system messages stay on the message list and are still included in the final model input. (#16950)
@mastra/koa@1.5.9
Patch Changes
-
Added sseFlushOnConnect route option to scope the SSE connected comment to subscribe endpoints only (#17158)
-
Fixed validation error responses on routes with
bodySchema,queryParamSchema, orpathParamSchemalosing field path information when consumers pinzod@^3. Responses now return the actual field name inissues[].field(e.g."agent_id") instead of"unknown"with the raw Zod issues serialized intoissues[0].message. Fixes #17167. (#17172) -
Scoped the SSE connected comment to subscribe routes only and added SSE comment passthrough for Fastify and NestJS adapters (#17158)
@mastra/libsql@1.12.0
Minor Changes
-
Added the
tool_provider_connectionsstorage domain. Stored agents can now persist per-agent ToolProvider config that round-trips on read/write/create. Runtime connection resolution (per-author, shared, caller-supplied) ships in a follow-up PR. (#17247)What you can do
- Pin a connection on a stored agent's config and have it round-trip on read/write/create.
- Persist multiple connections per toolkit so a follow-up runtime PR can fan-out to the right one at execution time.
Example
import { LibSQLStore } from '@mastra/libsql'; const storage = new LibSQLStore({ url: process.env.DATABASE_URL }); // Persist an OAuth connection that an agent can pin later await storage.toolProviders.upsertConnection({ authorId: 'user-123', providerId: 'composio', connectionId: 'auth_abc', toolkit: 'gmail', label: 'Work inbox', scope: 'per-author', }); // List a user's own connections (admin can omit authorId to list across users) const { items } = await storage.toolProviders.listConnectionsByAuthor({ authorId: 'user-123', providerId: 'composio', });Additive — existing stored agents continue to work unchanged. The runtime that consumes this domain ships in a follow-up PR.
PR 1 of 3 split from #17224.
Patch Changes
-
Added a public
close()method toLibSQLStorethat releases SQLite file handles and cleans up the WAL/shm sidecar files. Previously these handles stayed open until the process exited, which on Windows causedEBUSYerrors when removing the storage directory after shutdown.Mastra.shutdown()now callsclose()automatically, so you no longer need to reach into private fields. (#17306)const storage = new LibSQLStore({ id: 'my-store', url: 'file:./dev.db' }); // Release all file handles, including WAL/shm sidecar files await storage.close(); // Now safe to remove the storage directory on all platforms, including Windows await fs.rm('./dev.db', { recursive: true, force: true });
@mastra/loggers@1.1.2
Patch Changes
- Added messageKey option to PinoLogger for compatibility with structured-log aggregators. Set messageKey: 'message' to emit log messages under the message field expected by Google Cloud Logging, Datadog, ECS, and AWS CloudWatch. (#17450)
@mastra/mcp@1.9.0
Minor Changes
-
Added opt-in MCP server instructions forwarding into agent system prompts. (#17155)
When an MCP server advertises instructions during initialization, you can now forward that guidance into the system prompt of agents that use the server's tools. This is opt-in — set
forwardInstructions: trueper server to enable it. Forwarded instructions are injected into the agent's system prompt, so only enable this for servers you trust.const mcp = new MCPClient({ servers: { db: { url: new URL('http://localhost:4111/mcp'), forwardInstructions: true, // opt in; defaults to false instructionsMaxLength: 512, // max chars forwarded per server }, }, }); const agent = new Agent({ id: 'db-agent', name: 'DB Agent', instructions: 'Help with database changes.', model, tools: await mcp.listTools(), });You can always inspect cached instructions without forwarding them:
const instructions = mcp.getServerInstructions(); // => { db: 'Always validate before migrating.', other: undefined } -
Added native multimodal tool-result support. Core now converts MCP-style tool results with image and audio
contentparts into model-native media output when building model prompts, without requiring MCP tools to persist duplicate media payloads inproviderMetadata.mastra.modelOutput. (#16866)return { content: [ { type: 'text', text: 'Screenshot captured' }, { type: 'image', data: base64Png, mimeType: 'image/png' }, ], };
Patch Changes
-
Support conditional, function-based tool approvals. (#17337)
- MCP tools that wrap a server-level
requireToolApprovalfunction are now honored end-to-end. The per-tool approval function was previously dropped when the agent converted MCP tools (it kept only the boolean flag), so conditional approval silently fell back to always-on.CoreToolBuildernow preserves aneedsApprovalFnattached directly to a tool instance. - The global
requireToolApprovaloption onagent.stream/agent.generatenow accepts a function in addition to a boolean. It is evaluated per tool call with the tool name, arguments, and request context, enabling policies such as regex allowlists on tool names. Returningtruerequires approval for that call;falseallows it. On error the call defaults to requiring approval. When a function policy is set, tool calls run sequentially so approval suspensions don't race. Durable agents and stored agents continue to accept only a boolean (a function degrades to requiring approval for every call, since their options must be serializable).
// Approve only tool calls whose name is not on an allowlist. const allowlist = /^(get|list|search)_/; await agent.generate('...', { requireToolApproval: ({ toolName }) => !allowlist.test(toolName), });- Precedence is unchanged from before: a per-tool approval function (
createTool({ requireApproval: fn })or an MCP-derivedneedsApprovalFn) is authoritative for that tool and overrides the global setting, so a tool can still opt out of approval by returningfalseeven when the global option is on. The only new behavior is that the global option may now be a function in addition to a boolean. - The previously implicit, runtime-attached per-tool approval predicate is now a typed contract:
@mastra/coreexportsNeedsApprovalFnand declares the optionalneedsApprovalFnproperty on theToolclass. The MCP client and the agent runtime now share this typed contract instead of reaching throughany. This is additive — no public API changes.
- MCP tools that wrap a server-level
-
Close the stale MCP transport before reconnecting so SSE connections no longer leak orphaned EventSource instances and accumulate server-side sessions on implicit reconnect. (#17326)
-
Fixed FGA-enabled MCP servers so OAuth authInfo can be mapped to a Mastra user before tools/list and tools/call authorization. (#17475)
@mastra/memory@1.20.1
Patch Changes
-
Fixed observational memory replaying previously observed assistant responses during reprocessing, so past assistant messages no longer reappear in later turns. (#17338)
-
Fixed a crash in Cloudflare Workers when using a Zod schema for working memory. Working-memory input is now validated directly by the provided schema validator, which avoids runtime restrictions in Cloudflare Workers. (#17327)
-
Preserve system-reminder filtering for normalized reactive signal metadata. (#17191)
-
Added
observation.bufferOnIdleto opt idle turns into background observation buffering and carry the signal sender needed for background notifications. (#17181) -
Fixed
Memory.saveMessagesnot populatingrole,content, andcreated_atin the vector store metadata. Calls toGET /api/memory/searchnow return matches with the full message shape regardless of whether messages were saved throughagent.generate/agent.streamor written directly viaMemory.saveMessages(for example through thePOST /api/memory/save-messagesHTTP route used by external agents). (#16381)
@mastra/modal@0.2.1
Patch Changes
- Fixed sandbox execution results to report killed and timed out commands. (#17281)
@mastra/mysql@0.1.0
Minor Changes
-
Added the MySQL storage adapter for Mastra. Use it as a storage backend with the same domain coverage as the other first-party adapters (memory, threads, workflows, observability, agents, and more). (#17446)
import { MySQLStore } from '@mastra/mysql'; const store = new MySQLStore({ connectionString: 'mysql://user:password@localhost:3306/mastra', });This release also makes table and index setup reliable on a brand-new database:
- Fixed store initialization failing on a fresh database. Idempotency for favorites is now enforced by the table's primary key instead of a separate index that MySQL rejected, which previously aborted setup and left the connection pool unusable.
- Fixed default performance indexes silently failing to be created. Indexes on text columns now include a key-length prefix so they are created instead of skipped.
Patch Changes
@mastra/nestjs@0.1.10
Patch Changes
-
Added sseFlushOnConnect route option to scope the SSE connected comment to subscribe endpoints only (#17158)
-
Fixed validation error responses on routes with
bodySchema,queryParamSchema, orpathParamSchemalosing field path information when consumers pin a differentzodmajor than the one bundled with this adapter. Responses now return the actual field name inissues[].field(e.g."agent_id") instead of"unknown"with the raw Zod issues serialized intoissues[0].message. (#17172)ValidationError.zodErroris now typed asZodErrorLike(a structural subset ofZodErrorexposingissues[]) so consumers pinning a differentzodmajor still type-check. The runtime value is unchanged; cast to your installedZodErrortype if you need its instance methods.Fixes #17167.
-
Scoped the SSE connected comment to subscribe routes only and added SSE comment passthrough for Fastify and NestJS adapters (#17158)
@mastra/observability@1.14.1
Patch Changes
-
Added documentation explaining how to query and retrieve metric data from Mastra's observability store. Developers can now learn how to aggregate metrics, break them down by labels, visualize time series, and calculate percentiles using the in-process store API, HTTP endpoints, or CLI commands. (#17178)
-
Fix null
estimatedCostfor OpenRouter models whose id carries a vendor prefix and a dotted version (e.g.google/gemini-2.5-flash). These previously failed to match the pricing data (gemini-2-5-flash), leaving cost unreported in Studio's "Total Model Cost". Cost is now estimated correctly for these routes. (#17140) -
Added support for costs supplied by external SDK agent integrations. (#16906)
When an SDK agent records an estimated cost on its model generation span, observability now carries that cost onto the auto-extracted model token metric. This lets storage-backed metric queries and dashboards display costs reported by external agent SDKs, even when Mastra cannot calculate the cost from its own pricing registry.
@mastra/pg@1.12.0
Minor Changes
-
Added the
disableInitoption to theMastraVectorbase class. When set totrue, vector stores skip creating schemas, extensions, tables, and indexes at application startup. This matches the existingdisableInitbehavior on storage adapters and is useful for deployments where schemas and indexes are created ahead of time by a privileged database role, while the application runs with a least-privilege role. (#17272)Usage
const vector = new PgVector({ id: 'vectors', connectionString: process.env.DATABASE_URL, disableInit: true, });The
MASTRA_DISABLE_STORAGE_INITenvironment variable also disables vector init, so a single flag prevents both storage and vector stores from creating schemas, tables, or indexes at startup.
Patch Changes
-
Fixed
PostgresStoreignoring an explicitssloption when theconnectionStringalso carries ansslmode=/ssl=query param. node-postgres re-parses the connection string andObject.assigns the URL-derivedsslover the explicit one, so a config like{ connectionString: '...?sslmode=require', ssl: { rejectUnauthorized: false } }silently droppedrejectUnauthorized: falseand failed withUNABLE_TO_GET_ISSUER_CERT_LOCALLYagainst self-signed CAs. The connection-string branch now parses the URL and applies the explicitssllast, while still honoring URL-driven SSL when nossloption is provided. Fixes #17307. (#17356) -
Improved Postgres memory message save performance (#17351)
@mastra/playground-ui@31.0.0
Minor Changes
-
Made
ButtonsGroupcompose joined controls (searchbar + dropdown pills, split buttons, steppers) cleanly, and improvedInputGroupso it drops straight into one. (#17259)ButtonsGroupwithspacing="close"fuses outline, filled andSelectsegments into one pill with a single clean divider, a complete focus ring (no missing side), and no consumer width classes.InputGroupfills a flex row on its own, matches a same-size sibling height, and propagates size viadata-size(no React context) — so an icon + input segment composes inside aButtonsGrouppill with no layout classes.
Use
InputGroup(icon as anInputGroupAddon, optional clear button as anInputGroupButton) to build an icon input — it owns the box, focus, hover and error states on the focusable wrapper:import { ButtonsGroup, InputGroup, InputGroupAddon, InputGroupInput, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, } from '@mastra/playground-ui'; <ButtonsGroup spacing="close"> <InputGroup variant="outline"> <InputGroupAddon align="inline-start"> <SearchIcon /> </InputGroupAddon> <InputGroupInput placeholder="Search projects..." /> </InputGroup> <Select value={sort} onValueChange={setSort}> <SelectTrigger className="rounded-full"> <SelectValue /> </SelectTrigger> <SelectContent align="end">{/* options */}</SelectContent> </Select> </ButtonsGroup>; -
Refined the focus state of form inputs in
@mastra/playground-ui. Applies toInput,InputGroup,Searchbar, andTextarea. (#17259)- Removed the green border and glow that appeared on focus.
- On focus, the field shows a subtle background shift and brightens its border to a neutral tone, so the focused field stays clearly visible on any underlying surface.
- Made single-line inputs fully rounded to match the design system. Multi-line surfaces (
Textarea, andInputGroupwith a block-style addon) keep a softerrounded-xlcorner. - Added
filledandoutlinevariants for consumers that need to choose between the new surface treatment and a quieter border-only treatment. - The
unstyledvariant ofInputandTextareano longer leaks the browser default focus outline.
Input,Textarea, andInputGroupdefault to thefilledsurface.SearchbarandListSearchdefault to theoutline(transparent) treatment. ForSearchbarthis matches its previous transparent look.ListSearchpreviously rendered a filled (bg-surface2),rounded-lgbox, so its search fields across the list pages now read as transparent, fully-rounded pills — passvariant="filled"to keep them on a filled surface:import { Input, InputGroup, InputGroupAddon, InputGroupInput, Searchbar } from '@mastra/playground-ui'; <Input placeholder="Name" /> <Input variant="outline" placeholder="Name" /> <InputGroup variant="outline"> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> <InputGroupInput placeholder="Email" /> </InputGroup> <Searchbar label="Search agents" placeholder="Search agents..." onSearch={handleSearch} /> <Searchbar variant="filled" label="Search agents" placeholder="Search agents..." onSearch={handleSearch} />
Patch Changes
-
Fixed syntax highlighting in Studio code blocks. Shiki tokens now render with per-token colors (keywords, strings, identifiers) instead of flat monochrome text, and Code Mode
execute_typescriptprograms display as a formatted, highlighted TypeScript block instead of a one-line JSON string. (#17324) -
Improved studio load time by only bundling the CodeMirror and Shiki languages the editor actually uses, and removed a redundant TypeScript pass from the playground-ui build. (#17406)
-
Improved RadioGroup styling with neutral selected states, cleaner focus outlines, and surface-aware disabled states. (#17401)
-
Added a
DataPanel.SectionHeadingcomponent for small-caps section labels (with an optional leading icon) inside aDataPanel.Content.DataCodeSectionnow renders through it, andDataPanel.Headerhides its bottom border when the panel is collapsed (header-only) so an empty panel no longer shows a stray divider. (#17464)<DataPanel.SectionHeading icon={<FileInputIcon />}>Input</DataPanel.SectionHeading> -
Pointer drags inside the
SideDialogbody now select text reliably instead of fighting with the close-swipe gesture. The popup chrome (header, edges) still closes the drawer on drag. (#16959)Drawer composition
DrawerContentis now the shadcn-style opinionated bundle (DrawerPortal+DrawerBackdrop+DrawerViewport+DrawerPopup, with a handle bar on top/bottom-anchored drawers and a fade-out when a nested drawer covers the parent). Most drawers can now be written as:<Drawer> <DrawerTrigger>…</DrawerTrigger> <DrawerContent> <DrawerHeader>…</DrawerHeader> <DrawerBody>…</DrawerBody> </DrawerContent> </Drawer>The low-level primitives (
DrawerPortal,DrawerBackdrop,DrawerViewport,DrawerPopup) remain exported for drawers that need a custom portal target, non-modal page behavior, or chrome outside the popup (see theSwipeToOpenandNonModalStorybook examples).Base UI's text-selectable region (the
Drawer.Contentpart — pointer drags inside it select text instead of closing the drawer) is now exported asDrawerInteractive. Migration:// Before import { DrawerContent } from '@mastra/playground-ui'; <DrawerContent render={<div>...</div>} />; // After import { DrawerInteractive } from '@mastra/playground-ui'; <DrawerInteractive render={<div>...</div>} />; -
Removed the unused
ElementSelectexport from@mastra/playground-ui. Use theSelectprimitives instead. (#17417)// Before import { ElementSelect } from '@mastra/playground-ui'; <ElementSelect name="status" value={status} onChange={setStatus} options={['Draft', 'Published']} />; // After import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@mastra/playground-ui'; <Select name="status" value={status} onValueChange={setStatus}> <SelectTrigger> <SelectValue placeholder="Select..." /> </SelectTrigger> <SelectContent> <SelectItem value="draft">Draft</SelectItem> <SelectItem value="published">Published</SelectItem> </SelectContent> </Select>; -
Changed
Spinnerto render the new compact loader by default and addedvariant="pulse"for longer data-loading states. Removed thecolorprop so the loader defaults to the neutral text token and color overrides go throughclassName. (#17455)Migration note
Before:
<Spinner color={Colors.neutral3} />After:
<Spinner className="text-neutral3" /> <Spinner variant="pulse" className="text-neutral1" /> -
Fixed dropdowns, menus, comboboxes, and popovers being unclickable when opened inside a SideDialog (for example the dataset selector in the "Save as Dataset Item" panel on the Traces tab). These popups now render inside the dialog so they stay interactive within the modal drawer. (#17479)
-
Agent Builder starter agents now use the admin-configured default model when the model policy has one set. Previously, the starter ignored the admin default and always picked the first entry from the picker allowlist, which surfaced as "default model gets over-written by agent builder" on agents created from starter cards or the freeform prompt. (#17424)
When no admin default is set, behavior is unchanged: the starter falls back to the first allowed model, then to the hardcoded fallback.
-
Improved
@mastra/playground-uistability by removing legacy runtime UI dependencies without changingSideDialog,MainSidebar, or accessibility behavior. NestedSideDialoglevels now stack consistently, so multi-level flows behave predictably. (#16959) -
Added an
is404NotFoundErrorhelper to detect 404 Not Found responses from the Mastra client, alongside the existingis401UnauthorizedErrorandis403ForbiddenErrorhelpers. Use it to show a clear not-found state when a resource no longer exists. (#17460)import { is404NotFoundError } from '@mastra/playground-ui'; try { await client.getDataset(id); } catch (error) { if (is404NotFoundError(error)) { // show a not-found state instead of a generic error } } -
Improved Checkbox styling with neutral selected states, cleaner focus outlines, and smoother state transitions. (#17400)
-
Improved switch focus, disabled, and motion states. (#17416)
@mastra/react@0.4.3
Patch Changes
-
Fixed canonical user signal echoes so messages sent through the agent-signals path appear in chat history when they move from pending to active. (#17309)
-
Separated thread subscription cleanup from active-run aborts so closing or switching a listener only unsubscribes that listener, while explicit cancel still aborts the active run. (#17310)
-
Added subscription-native tool approval APIs so approving or declining a tool call resumes through the active thread subscription instead of requiring a separate continuation stream. New messages are queued while a tool approval is waiting, preventing overlapping runs from duplicating approval requests. (#17311)
await agent.sendToolApproval({ resourceId: 'user-123', threadId: 'thread-123', toolCallId: 'tool-call-123', approved: true, }); -
Enabled Studio via the CLI and deployers to use agent signal subscriptions by default while preserving
MASTRA_AGENT_SIGNALS=false,enableThreadSignals: false, and explicit legacy Stream as opt-outs. The ReactuseChat()hook remains opt-in for SDK consumers viaenableThreadSignals: true. (#17313)
@mastra/schema-compat@1.2.11
Patch Changes
-
Fixed Gemini REST tool calls failing for
z.discriminatedUnion,z.lazy, andz.tupleinputs.GoogleSchemaCompatLayernow rewrites JSON Schema 2020-12 keywords into the OpenAPI 3.0 Schema Object subset that Gemini expects:oneOf→anyOf,const→enum, tupleitems: [array]→items: { anyOf: [...] }, nullableanyOfcollapse,$refinlining with recursive schema support, and stripping of$schema/additionalProperties/propertyNames. Fixes #17057. (#17179) -
Fixed Zod 4 schemas with
.transform()producing the wrong JSON Schema for structured output and tool calling. The generated schema now describes the pre-transform input the model must produce instead of the post-transform output, so a field likez.string().transform(JSON.parse)is advertised as astringrather thanstring | number | boolean | null. (#17357)
@mastra/server@1.38.0
Minor Changes
-
Added an agent override export API and server-side ownership enforcement. (#17228)
The server and client now expose an agent override export endpoint so Studio can download an agent's overrides as JSON for review or commit workflows. Saves are enforced server-side against each agent's
editorconfig, so only owned fields (instructions, tools, or tool descriptions) are persisted and fields locked by theeditorconfig are stripped.The system packages response also reports the active editor
sourceso clients can render the correct editing experience. -
Added
isZodErrorhelper andZodErrorLiketype, exported from@mastra/server/server-adapter(and@mastra/server/handlers/error). Use these instead ofinstanceof ZodErrorwhen handling validation errors in custom server adapters or middleware so the check survives consumers that pin a differentzodpackage instance than the one bundled with@mastra/server. (#17172)import { isZodError } from '@mastra/server/server-adapter'; try { await schema.parseAsync(input); } catch (error) { if (isZodError(error)) { // structural check — works across zod v3/v4 realms return formatValidationError(error); } throw error; }Underpins the fix for #17167.
-
Add fire-and-forget workflow resume that returns immediately with
{ runId }without awaiting the run output. (#17230)For
@mastra/inngest, this skips thegetRunOutput()polling that previously raced a realtime subscription against the Inngest runs API and could surface spurious 404s even though the durable workflow was running fine.Run.resumeAsync()added to core: dispatches the resume in the background and returns{ runId }immediately. Engines that poll for results (Inngest) override it to skip polling entirely.InngestRun.resumeAsync()sends the resume event and returns{ runId }, skipping polling. Send-time failures (bad payload, event send failure) still reject synchronously and roll back the snapshot.- New
POST /workflows/:workflowId/resume-no-waitandPOST /agent-builder/:actionId/resume-no-waitroutes return{ runId }immediately. - New client SDK
run.resumeNoWait()resolves with{ runId }.
The existing
resumeAsync()client/server surface is unchanged and still resolves with the full workflow result, so there is no breaking change.resumeNoWaitis intentionally additive in v1. In Mastra v2 the fire-and-forget behavior is planned to become the default behavior ofresumeAsync()(mirroringstart/resumesemantics), at which pointresumeNoWaitand theresume-no-waitroutes will be removed. The code paths carryTODO(v2)comments documenting this consolidation. -
Add experimental HTTP message routes for agent threads. Servers now expose
POST /agents/:agentId/send-messageandPOST /agents/:agentId/queue-messagefor message-first input while keeping/agents/:agentId/signalsavailable for lower-level signals and compatibility. (#17237) -
Added a
PATCH /tool-providers/:providerId/connections/:connectionIdendpoint and matching client SDK method so authors can rename a connection's display label after creation. (#17249)Rename a connection from the client SDK
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: '…' }); await client.getToolProvider('composio').updateConnection('auth_abc', { label: 'Work inbox', });Pass
label: null(or an empty string) to clear the existing label. Labels are 1–32 characters and accept letters, digits, spaces, underscores, and hyphens ([A-Za-z0-9 _-]+).Ownership enforced server-side
Non-owners get a 403 unless they hold
tool-providers:admin. Shared connections are reachable by every author. The label is stored on the connection row itself, so the rename flows to every agent that pins the connection — no per-agent edit needed. -
Added the v1 ToolProvider runtime, server routes, client SDK methods, and editor wiring that power OAuth-backed integrations on stored agents. (#17248)
Stored agents can now pin OAuth connections per toolkit
A stored agent's config accepts a new
toolProvidersshape that tells the runtime which connection to bind for each toolkit at execution time. Connections can be scoped per-author, shared across an org, or supplied by the caller.{ toolProviders: { composio: { connections: { gmail: [{ kind: 'author', toolkit: 'gmail', connectionId: 'auth_abc', scope: 'per-author' }], }, tools: { GMAIL_FETCH_EMAILS: { toolkit: 'gmail' }, }, }, }, }New client SDK surface for managing connections
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: '…' }); const composio = client.toolProvider('composio'); const { items } = await composio.listConnections({ toolkit: 'gmail' }); await composio.disconnectConnection('auth_abc');New
ToolProviderinterface for custom providersProviders implement a VNext surface (
listToolkitsVNext,listToolsVNext,resolveToolsVNext) plus the auth round-trip (authorize,getAuthStatus,listConnections,disconnectConnection,listConnectionFields,health). The Composio provider has been rewritten on this surface; the older catalog methods remain as@deprecatedshims for back-compat.Connections list responses use
page/perPagepagination, matching the rest of the server surface.Both stored agents (
editor.agent.getById(...)) and code-defined agents with stored overrides (editor.agent.applyStoredOverrides(...)) resolvetoolProvidersat request time, merging provider-resolved tools alongside code/registry/MCP/integration tools.Stored agents that don't set
toolProviderscontinue to work unchanged. The Studio/Builder UI ships separately.
Patch Changes
-
Separated thread subscription cleanup from active-run aborts so closing or switching a listener only unsubscribes that listener, while explicit cancel still aborts the active run. (#17310)
-
Added sseFlushOnConnect route option to scope the SSE connected comment to subscribe endpoints only (#17158)
-
Added subscription-native tool approval APIs so approving or declining a tool call resumes through the active thread subscription instead of requiring a separate continuation stream. New messages are queued while a tool approval is waiting, preventing overlapping runs from duplicating approval requests. (#17311)
await agent.sendToolApproval({ resourceId: 'user-123', threadId: 'thread-123', toolCallId: 'tool-call-123', approved: true, }); -
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility. -
Fix custom providers appearing twice in Studio's provider selector. (#17441)
In dev mode,
GatewayRegistryregisters custom gateways soPROVIDER_REGISTRYalready contains them with prefixed keys (e.g."melioffice/genai"). The/agents/providershandler then calledgateway.fetchProviders()again and re-added them, but the live call returns raw unprefixed keys (e.g."genai") which after prefixing produce the same key — however in some cases the keys differed, causing both entries to appear in the UI.The fix skips adding a provider from the live
fetchProviders()call if it is already present inallProvidersfromPROVIDER_REGISTRY. -
Fixed a startup crash that affected deployments pinning an older
@mastra/coreversion. The server now boots successfully even when the installed@mastra/coredoesn't include the Agent Builder runtime. (#17382)Symptom
Deployed servers failed to start with
ERR_MODULE_NOT_FOUNDpointing at@mastra/core/dist/agent-builder/ee/index.js, even on apps that never used the Agent Builder.What changed
The server no longer eagerly loads the Agent Builder runtime at boot. It's loaded on demand, only when a request actually needs it on an app that has configured a
MastraEditorwith builder support.No application code changes required.
-
Fixed sub-agent version resolution in supervisor mode. Sub-agents now inherit the parent's draft/published version semantics — when chatting in the editor (draft mode), sub-agents resolve to their latest draft version; in the main agent chat (published mode), sub-agents resolve to their published version. Previously, sub-agents without explicit per-agent version overrides always fell back to the code-defined default, ignoring the parent's version context. (#17165)
-
Stored agent and skill POST, PATCH, and skill publish responses now include
isFavorited, matching GET behavior. Clients can read favorite status from write responses without an extra GET request. (#17246)Under auth-off, write responses also omit
favoriteCountto match GET, so the response shape is consistent across all single-entity endpoints. -
Fixed memory status reporting for agents that do not support Mastra memory. The memory status endpoint now preserves storage fallback for regular agents while allowing integrations to opt out of memory UI. (#16906)
-
Scoped the SSE connected comment to subscribe routes only and added SSE comment passthrough for Fastify and NestJS adapters (#17158)
-
Hardened v1 ToolProvider connection routes and SDK forwarding. (#17248)
Fail closed on unknown
connectionIdDELETE /tool-providers/:providerId/connections/:connectionIdandGET …/usagenow return403when storage is configured but no persisted row matches the suppliedconnectionIdand the caller isn't an admin. Previously these routes fell through to the caller's ownauthorId, which let non-admin callers probe (and trigger provider-siderevokeConnectionfor) IDs that didn't belong to them.Aligned authorize label validation with stored label rules
POST /tool-providers/:providerId/authorizenow enforces the same label rules the storedtoolProvidersconfig uses (min(1),max(32),/^[A-Za-z0-9 _-]+$/). Labels that passauthorizeare now guaranteed to pass downstream stored-agent validation.SDK forwards
toolkiton connection-scoped operations@mastra/client-js:await client.toolProviders.get('composio').disconnectConnection('ca_xxx', { toolkit: 'gmail', force: true, }); const usage = await client.toolProviders.get('composio').getConnectionUsage('ca_xxx', { toolkit: 'gmail' });disconnectConnectionnow forwardsparams.toolkit(previously dropped) andgetConnectionUsageaccepts an optional{ toolkit }parameter so toolkit-scoped connection lookups disambiguate correctly server-side. -
Lazy-load
@mastra/core/tool-providerinside the tool-provider handler so (#17248)@mastra/serverevaluates under any peer-compatible@mastra/core(peer floor remains>=1.34.0-0). The handler no longer importsSHARED_BUCKET_IDorUnknownToolProviderErrorat module load —SHARED_BUCKET_IDis mirrored as a local literal (verified in lockstep with core via a regression test), andUnknownToolProviderErroris resolved via a cachedawait import(...)insideresolveProviderso the real class identity is preserved forinstanceof.OSS users running
Mastrawithout aMastraEditorare unaffected: every tool-provider route still short-circuits with HTTP 500 "Editor is not configured" viarequireEditor(...)before any core/tool-provider value is touched. Users with aMastraEditoralready pull a compatible core transitively through@mastra/editor. Tool-provider routes require the new core exports at request time only — older cores surface a clear runtime error instead of crashing the server at boot. -
Improved observability and error isolation in the v1 ToolProvider runtime. (#17248)
Better visibility into connection-scope misconfiguration
When an agent runs with a stored ToolProvider connection whose scope cannot be resolved from the request context, the runtime now logs a one-shot warning and falls back to a shared bucket instead of silently routing every caller to the same OAuth account. Multi-tenant deployments get a clear signal when their identity wiring isn't reaching the runtime.
One bad toolkit no longer disables sibling providers
If a provider returns more connections for a toolkit than its declared capabilities allow, the runtime now logs and skips that toolkit instead of throwing. Other providers and other toolkits on the same agent continue to resolve normally.
-
Workflows now support an optional
metadatafield for attaching custom key-value data such asdisplayName,author, orcategory. Metadata is preserved through serialization and returned in workflow info API responses. (#17355)// Define a workflow with metadata const myWorkflow = createWorkflow({ id: 'data-processing', metadata: { displayName: 'Data Processing Pipeline', category: 'ETL', }, inputSchema: z.object({ ... }), outputSchema: z.object({ ... }), }); // Retrieve workflow info with metadata via the Mastra Server API const workflowInfo = await mastraClient.getWorkflow('data-processing'); console.log(workflowInfo.metadata?.displayName); // "Data Processing Pipeline"
@mastra/spanner@1.1.0
Minor Changes
-
Added five new storage domains to the Google Cloud Spanner adapter: workspaces, datasets, experiments, favorites, and channels. The Spanner store now covers the full set of editor and evaluation domains. (#17472)
What's new
- Datasets versioned dataset items with historical snapshots and as-of reads (time-travel reads, per-item history, batched insert/delete).
- Experiments with per-item results, review-status aggregation, and pagination.
- Workspaces with versioned configuration snapshots (filesystem, sandbox, mounts, search, skills, tools), mirroring the existing thin-record + versions pattern.
- Favorites for agents and skills, maintaining a denormalized
favoriteCounton the parent record atomically. - Channels for multi-platform installations and per-platform configuration.
Enabling favorites also adds favorited-first ordering and
favoritedOnly/entityIdsfiltering toagents.list()andskills.list(), and surfacesfavoriteCounton skill records.const storage = new SpannerStore({ id: 'spanner-storage', projectId: process.env.SPANNER_PROJECT_ID!, instanceId: process.env.SPANNER_INSTANCE_ID!, databaseId: process.env.SPANNER_DATABASE_ID!, }); const datasets = await storage.getStore('datasets'); const ds = await datasets?.createDataset({ name: 'eval-set' }); const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'u1', entityType: 'agent', entityId: 'agent-1' });
Patch Changes
@mastra/vercel@0.2.0
Minor Changes
-
Added
VercelMicroVMSandbox, a new workspace sandbox provider backed by the Vercel Sandbox ephemeral Firecracker MicroVM product (@vercel/sandbox). It provides a persistent in-session filesystem,sudoaccess, exposed ports, command execution, and background processes via the process manager. This is distinct from the existingVercelSandbox, which runs commands as stateless Vercel serverless Functions and is unchanged. Also exportsVercelMicroVMProcessManagerand thevercelMicroVMSandboxProvidereditor descriptor (provider idvercel-microvm). Closes #16704. (#17332)import { Workspace } from '@mastra/core/workspace'; import { VercelMicroVMSandbox } from '@mastra/vercel'; const workspace = new Workspace({ sandbox: new VercelMicroVMSandbox({ runtime: 'node24', timeout: 600_000, ports: [3000], }), }); await workspace.init(); const result = await workspace.sandbox.executeCommand('node', ['--version']);
Patch Changes
@mastra/voice-aws-nova-sonic@0.1.2
Patch Changes
-
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility.
@mastra/voice-azure@0.11.1
Patch Changes
-
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility.
@mastra/voice-cloudflare@0.12.2
Patch Changes
-
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility.
@mastra/voice-deepgram@0.12.1
Patch Changes
-
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility.
@mastra/voice-elevenlabs@0.12.1
Patch Changes
-
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility.
@mastra/voice-gladia@0.12.1
Patch Changes
-
Moved shared voice primitives and route metadata into the new
@internal/voicepackage so voice providers no longer depend on@mastra/coreand server voice routes share the same route definitions. (#16725)@mastra/core/voicecontinues to re-export the voice APIs for backwards compatibility.
[@mastra/voice-google@0.12.1](https://github.com/mastra-ai/mas
@mastra/core@1.36.0
Highlights
Stored Entity HTTP APIs (Agents/Skills/Workspaces) + Builder/Registry Introspection
@mastra/server adds a full HTTP surface for stored entities (CRUD, versioning, activation/restore) including favorites metadata, plus builder introspection endpoints and an external skill-registry proxy that Studio and @mastra/client-js can consume.
Browser Automation & Screencast Session Probing (incl. Stored Agents)
New browser-provider/editor primitives wire browser automation into the editor, while deployers/adapters add a probeable GET /agents/:agentId/browser/session endpoint and client SDK methods (agent.browserSession() / agent.closeBrowser()) so UIs can safely open screencast WebSockets only when supported—now also working for stored agents via editor fallback lookup.
Delta Polling for Observability List APIs (Server/Client/Stores/UI)
Observability list endpoints now support mode: 'delta' with cursors across core + @mastra/server, with store support in DuckDB/ClickHouse (and in-memory alignment) and typed client support; Playground UI’s traces list updates live via delta polling instead of full-page refetches.
Harder Authorization Defaults: FGA Coverage + ANY-of Permissions + Safer Context Keys
FGA execution checks are now enforced consistently across agents/tools/memory/workflows (requiring authenticated requestContext when invoking protected APIs), and both FGA checks and route permissions accept permission arrays (ANY-of semantics); server auth middleware also writes namespaced request-context keys to avoid collisions.
Convex Production Upgrades: Native Vector Search + Durable Server Cache
@mastra/convex adds ConvexNativeVector backed by schema vector indexes/ctx.vectorSearch for production-grade retrieval, plus ConvexServerCache to persist durable stream replay and response-cache state in Convex.
Breaking Changes
AgentSignalContentsnarrowed tostring | (TextPart | FilePart)[](no longer accepts wrappedBaseMessageListInputshapes); updateagent.sendSignalcallers accordingly.@mastra/playground-uiButton variants changed:cta/contrast/linkremoved in favor ofdefault/primary/outline/ghost;ButtonWithTooltipremoved (useButtonwithtooltipprop).PopoverContentno longer forwardsonOpenAutoFocus/onCloseAutoFocus(useinitialFocus/finalFocusinstead).
Changelog
@mastra/core@1.36.0
Minor Changes
-
Added
activateAfterIdle: "auto"for Observational Memory early activation. (#16663)Mastra can now choose an idle activation timeout from the active model provider's prompt cache behavior. OpenAI also respects
providerOptions.openai.promptCacheRetentionwhen available.const memory = new Memory({ options: { observationalMemory: { model: 'google/gemini-2.5-flash', activateAfterIdle: 'auto', activateOnProviderChange: true, }, }, }); -
Added support for permission arrays in FGA checks and route configuration. When an array is provided, the user needs any one of the listed permissions (logical OR). (#16605)
Affected types
FGACheckParams.permissionFGARouteConfig.permissionFGARouteInfo.requiresPermissionFGADeniedError.permissionCheckFGAOptions.permission
Single-permission usage continues to work unchanged.
// Before — single permission only await fga.check({ resource: { type: 'agent', id: 'abc' }, permission: 'agents:read', }); // After — single permission or array (ANY-of) await fga.check({ resource: { type: 'agent', id: 'abc' }, permission: ['agents:read', 'agents:execute'], }); -
Added consistent FGA execution checks across agents, tools, memory, and workflows to prevent unauthenticated executions when FGA is configured. Pass an authenticated user through
requestContextwhen invoking protected APIs directly: (#16651)const requestContext = new RequestContext(); requestContext.set('user', user); await agent.generate('Summarize this thread', { requestContext, }); -
Added the
EditorFavorite*types and an optionalfavoritesnamespace onIMastraEditorso editor implementations can expose favoriting of stored agents and skills. (#16749)import type { IMastraEditor, IEditorFavoritesNamespace, EditorFavoriteTargetInput, EditorFavoriteToggleResult, } from '@mastra/core/editor'; interface IMastraEditor { // ...existing members... favorites?: IEditorFavoritesNamespace; }The
favoritesfield is optional — existing implementations ofIMastraEditorcontinue to work unchanged.@mastra/editorships a defaultEditorFavoritesNamespacethat wires this up against the storagefavoritesdomain.Also renamed
AgentFeatures.starstoAgentFeatures.favoritesin@mastra/core/agent-builder/eeso the feature flag aligns with the storage column (favoriteCount), HTTP routes (/favorite), and the editorfavoritesnamespace. The field had no functional consumers, so this is a name-only change. -
Enterprise edition now automatically captures PostHog telemetry for EE license checks and feature usage, including license validation status, RBAC access resolution, FGA authorization calls, and EE feature invocation metadata. Telemetry is enabled by default for EE customers and can be disabled with
MASTRA_TELEMETRY_DISABLED=1; community users are unaffected. (#16660) -
Added new editor configuration primitives for browser providers, agent builder integration, and stored-agent visibility. (#16778)
New:
BrowserProviderinterfaceImplement a browser provider to expose browser automation tools to agents via the editor. Each provider declares an id, name, and config schema, then returns a
MastraBrowserinstance fromcreateBrowser.import type { BrowserProvider } from '@mastra/core/editor'; const myProvider: BrowserProvider = { id: 'my-browser', name: 'My Browser', description: 'Custom browser automation', configSchema: z.object({ apiKey: z.string() }), createBrowser: async config => { return createMyBrowser(config.apiKey); }, };New:
MastraEditorConfig.browsersand.builderWire browser providers and agent-builder options into the editor:
new MastraEditor({ browsers: { 'my-browser': myProvider }, builder: { features: { agent: { favorites: true } } }, });New:
visibilityonupdateAgentMetaSet an agent's visibility (private or public) through the editor namespace:
await editor.agent.updateAgentMeta('agent-id', { visibility: 'public' }); -
publishSkillnow returns the full skill file tree so consumers can persist the UI-facing tree alongside storage blobs without re-walking the source. (#16666)import { publishSkill } from '@mastra/core/workspace'; const result = await publishSkill({ workspace, skillId, source }); // New: nested tree of folders + files; binary content base64-encoded. for (const node of result.files) { console.log(node.type, node.path); }Also added two optional capability methods to
IMastraEditorfor server-side gating of builder-aware behavior:interface IMastraEditor { // ...existing members... hasEnabledBuilderConfig?(): boolean; resolveBuilder?(): Promise<IAgentBuilder | undefined>; }Both methods are optional — existing implementations of
IMastraEditorcontinue to work unchanged. Servers that consume them treatundefined/ missing implementation as "no builder configured." -
Added route-specific CORS configuration so credentialed cross-origin access can be limited to selected custom routes and channel webhooks. (#16689)
registerApiRoute('/customer-webhook', { method: 'POST', cors: { origin: ['https://customer-saas.example'], credentials: true, }, handler: async c => c.json({ ok: true }), });new Agent({ id: 'support-agent', name: 'Support Agent', instructions: '...', model, channels: { adapters: { web: { adapter: createWebAdapter(), cors: { origin: ['https://customer-saas.example'], credentials: true, }, }, }, }, });Use
server.corsfor one global CORS policy across the server:new Mastra({ server: { cors: { origin: '*', }, }, }); -
Narrowed
AgentSignalContentsfromBaseMessageListInputtostring | (TextPart | FilePart)[]. (#16622)Fixed two signal-content bugs:
user-messagesignal attributes now reach the LLM- multimodal non-
user-messagesignals no longer lose file parts
Callers that previously passed wrapped message shapes to
agent.sendSignalshould now pass a bare string or a bare parts array.Before:
{ type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }After:
{ type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }Added an optional
providerOptionsfield toagent.sendSignalthat flows through to the resulting prompt turn (asproviderOptionson the LLM message) and is persisted on the stored signal message (ascontent.providerMetadata). -
publishSkillFromSource()(andcollectSkillForPublish()) now return afilesfield containing the full skill source as a tree ofStorageSkillFileNodeentries with base64-encoded blob content — handy for storing a UI-facing copy of a skill alongside its content-addressable tree: (#16673)const { snapshot, tree, files } = await publishSkillFromSource({ source }); // files: StorageSkillFileNode[] — name, mimeType, base64 content per nodeExisting callers that only destructure
{ snapshot, tree }are unaffected; the field is additive.Also adds
parseSkillSnapshotFromFiles()for parsing skill snapshot frontmatter from a flat file list (used by the registry install flow):import { parseSkillSnapshotFromFiles, type SkillSnapshotFile } from '@mastra/core/workspace'; const files: SkillSnapshotFile[] = [{ path: 'SKILL.md', content: '...' }, ...]; const snapshot = parseSkillSnapshotFromFiles(files); -
Added delta polling support for observability list APIs in core, DuckDB, and ClickHouse. (#16632)
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
452036a) -
Fixed task_update to auto-demote previously in_progress tasks instead of returning an error when moving another task to in_progress. (#16843)
-
Fixed durable agents to honor activeTools when streaming. (#16646)
-
Fixed type error when a tool calls
suspend(...)insideexecutewhile also declaring anoutputSchema. Theexecutereturn type now allowsvoidin addition to the declared output shape, so the idiomaticreturn await suspend(...)pattern type-checks correctly. (#16799) -
Fixed a startup bug in
MastraCompositeStore.init()when usingdefaultoreditor. (#16786)Before this fix, the composite initialized inner domains directly and could skip parent store initialization. That could skip adapter setup steps and cause missing-table errors during startup (most visibly with
LibSQLStoreon a local file).Now,
MastraCompositeStore.init()runs parentdefaultandeditorinitialization first, then initializes only domains not already covered by those parents. This preserves adapter-specific initialization behavior and prevents startup races.Fixes #16782.
-
Fixed CompositeAuth incorrectly advertising SSO, session, and user provider capabilities when no inner provider supports them. Studio would show an SSO login button even when no provider had SSO configured, leading to 401 errors on login attempts. The duck-typing check now verifies that interface methods are actual functions rather than just present on the prototype chain. (#16664)
-
Hide internal workflow spans from Mastra-owned plumbing in exported traces. (#16631)
-
Channels now serialize messages per thread to keep conversations in order, and the tool approval flow is fixed end-to-end: (#16517)
- Messages arriving while the agent is busy are delivered into the running agent loop instead of starting a new, conflicting stream on the same thread. Each Mastra thread shares one subscription, and channel/author facts (platform, message id, author name) are surfaced on the stored message under
providerMetadata.mastra.channels.<platform>. - Tool approval flow: approving now drains the resumed run so the card is updated with the tool result and any follow-up assistant text is posted. Denying now resumes the run via
declineToolCallinstead of leaving it suspended.agent.subscribeToThread()consumers also receive chunks from resumed runs (the subscription used to drop the second registration for a resumed run that kept its originalrunId). - The original
ChannelConfigis now exposed via the newAgentChannels.channelConfigfield so channel providers can merge with existing adapters instead of replacing them. - Bumped
chatto^4.29.0.
- Messages arriving while the agent is busy are delivered into the running agent loop instead of starting a new, conflicting stream on the same thread. Each Mastra thread shares one subscription, and channel/author facts (platform, message id, author name) are surfaced on the stored message under
-
Added the
internalUsage?: UsageStatsfield toAIBaseAttributes, so any span type can carry token usage rolled up from internal descendant spans. Populated automatically by@mastra/observabilitywhen an internalMODEL_GENERATIONends inside a non-internal ancestor. (#16434) -
Fixed trajectory scorers so tool calls stored only in V2 content.parts are included in extracted eval steps. (#15439)
-
Fixed thread metadata updates to merge with existing fields instead of replacing them. Previously, updating a thread's metadata would silently drop any fields not included in the update. Now existing metadata fields are preserved when updating. (#16846)
Fixed MockMemory working memory tool to support partial JSON updates when using schema-based working memory. Previously, sending a partial update would overwrite all existing data. Now for schema-based configs, unchanged fields are preserved automatically (matching @mastra/memory behavior).
Fixed MockMemory constructor to preserve workingMemory config options (like schema) when enableWorkingMemory is true.
-
Improved MastraCode quiet mode so terminal sessions are easier to scan. (#16771)
- Quiet mode is now the default for new installs, and existing classic users get a one-time prompt to choose whether to enable it.
- Added compact tool previews with a configurable preview-line limit, including an option to hide previews.
- Improved repeated tool-call rendering, path continuation handling, task wrapping, shell/error previews, and spacing between tools, messages, plans, and completed subagents.
- Added edited line ranges to workspace edit results so tool UIs can show where replacements happened.
-
Remove hardcoded
/api/prefix check fromregisterApiRoute(). The check incorrectly rejected custom routes starting with/api/even when users configured a differentapiPrefix. Reserved-path validation is already handled at the server adapter level using the actual configured prefix. (#16859) -
Fixed infinite recursion in
RequestContext.toJSON()when multiple (#16686)RequestContextinstances reference each other through stored values. Previously, serializing such cross-context cycles would cause a CPU hang. Cyclic references are now detected and omitted from the serialized output, consistent with how circular references within a single context are handled. -
Fixed crash in CacheKeyGenerator.fromAIV4Part when a tool-invocation part has undefined toolInvocation. This can happen when observational memory seals a partially-streamed assistant message. Also guarded MessageMerger against the same condition. (#16773)
-
Agent signals can now coordinate active thread runs across agents that share a PubSub instance, so thread subscribers and signal senders can observe the same run instead of being limited to one runtime instance. (#16665)
import { Agent } from '@mastra/core/agent'; import { EventEmitterPubSub } from '@mastra/core/events'; const pubsub = new EventEmitterPubSub(); const agent = new Agent({ id: 'agent', name: 'Agent', instructions: 'Help the user', model, pubsub, }); -
Exposed
formatSkillActivation(skill)from@mastra/core/workspace. It returns the activation payload — instructions plus references, scripts, and assets listings — that the built-inskilltool uses, so callers (e.g. an explicit/skill/<name>slash command) can produce the same output without duplicating the formatting logic. (#16618)Also preserves the
user-invocableskill frontmatter field in workspace skill metadata.import { formatSkillActivation } from '@mastra/core/workspace'; const content = formatSkillActivation(skill); -
Fixed sub-agent streams so nested tool input progress is emitted while tool arguments are still being generated. This lets UIs show delegated agents preparing tool calls before the final tool input is available. Fixes #16422. (#16553)
-
Fixed
backgroundTasks: { enabled: true }silently dispatching foreground-only tools to the background. (#16792)Previously, enabling
backgroundTaskson theMastrainstance injected a system prompt into every agent that taught the LLM to flip any tool to background by passing_background: { enabled: true }in its arguments, and the resolver honored that override unconditionally. Models would readily do this for short, deterministic tools — a plain calculator could return"Background task started…"instead of{ result: 42 }, breakingagent.generate()/agent.stream()for tool-using flows.The LLM
_backgroundoverride is now treated as a modifier on tools the developer has opted in at the tool or agent layer, not a standalone opt-in. If a tool hasn't been opted in via tool-levelbackground: { enabled: true }or agent-levelbackgroundTasks: { tools: { … } }(ortools: 'all'),_background.enabled: truefrom the model is ignored and the tool runs in the foreground. Opted-in tools continue to honor LLM overrides forenabled,timeoutMs, andmaxRetriesas documented. -
Fixed scheduler performance and correctness issues that could cause excessive Postgres CPU and noisy failed runs. (#16805)
- Added missing indexes on the schedules tables that the tick loop polls every 10 seconds:
(status, next_fire_at)onmastra_schedulesand(schedule_id, actual_fire_at)onmastra_schedule_triggers. Without these the scheduler performed a full sequential scan on every tick. - The scheduler tick loop is now only started when at least one workflow declares a schedule (or
scheduler.enabledis set explicitly), so deployments without scheduled workflows no longer poll the database at all. - The scheduler now validates that a schedule's target workflow is still registered before firing it. Schedules whose target workflow has been removed from the Mastra config are skipped for a short grace window and then deleted, so stale rows stop producing failed workflow runs forever.
- The scheduler is now lazily initialized inside
startWorkers(). Accessingmastra.schedulerbeforestartWorkers()runs throws a descriptive error instead of returning a half-initialized instance.
- Added missing indexes on the schedules tables that the tick loop polls every 10 seconds:
-
Fixed Mastra getting stuck after a storage startup failure. Previously, if storage couldn't start up (for example, because the database was briefly unreachable), Mastra would keep returning the same error for every operation until the process was restarted. Now the next storage operation tries to start storage again, so brief outages recover on their own. Storage startup failures are also logged, so the problem is visible even when a later retry succeeds. (#16427)
-
Restore MastraCode local command execution to inherit parent environment variables while redacting env-shaped and secret-looking workspace trace data. (#16691)
-
Added a Unix socket PubSub transport and wired the Mastra Code TUI through a per-resource socket so local sessions can coordinate thread streams across processes. Programmatic
createMastraCodeusage remains opt-in: (#16669)await createMastraCode({ unixSocketPubSub: true }); -
Fix in-memory observability storage to match the contract validated against DuckDB/ClickHouse vNext adapters. (#16808)
Previously, when running Mastra with the default in-memory storage, several observability operations behaved differently than they would against a production database:
getSpansthrew 'This storage provider does not support batch-fetching spans'. It now batch-fetches spans by id within a trace, enabling the optimizedgetBranchpath on in-memory storage.batchCreateLogs,batchCreateMetrics,createScore/batchCreateScores,createFeedback/batchCreateFeedbackappended duplicate records on retry. They now upsert by id, preserving the cursor id so delta polling does not re-emit the record. This makes client retries safe.- Discovery operations (
getEntityTypes,getEntityNames,getServiceNames,getEnvironments,getTags) only inspected spans. They now also scan logs and metrics, so dimensions emitted on those surfaces are surfaced in discovery results. getMetricTimeSeriesmerged grouped series whose label values contained the|character (e.g.{segmentA: 'a', segmentB: 'b|c'}collided with{segmentA: 'a|b', segmentB: 'c'}). Series are now keyed on the original label tuple, so colliding display names remain distinct series.
@mastra/ai-sdk@1.4.3
Patch Changes
- Fixed sub-agent streams so nested tool input progress is emitted while tool arguments are still being generated. This lets UIs show delegated agents preparing tool calls before the final tool input is available. Fixes #16422. (#16553)
@mastra/auth-okta@0.0.3
Patch Changes
-
Fix endpoint URL construction for Okta org authorization servers. (#15694)
MastraAuthOktaconcatenated/v1/authorize(and/token,/keys,/logout) directly ontoOKTA_ISSUER. That yields the right endpoint for a custom authorization server (https://{domain}/oauth2/default→.../oauth2/default/v1/authorize), but 404s on an Okta org authorization server (https://{domain}→.../v1/authorize, whereas the real org endpoint is.../oauth2/v1/authorize).An internal
endpointBaseis now derived from the issuer — verbatim when it already contains/oauth2/, otherwise${issuer}/oauth2— and used for the authorize, token, keys, and logout URLs. JWTiss-claim validation still uses the raw issuer so token validation stays correct on both server types. Trailing slashes on the issuer are also normalized soOKTA_ISSUER=https://{domain}/no longer produces.../oauth2//v1/....
@mastra/auth-workos@1.5.0
Minor Changes
-
FGA
check()andrequire()now accept an array of permissions and short-circuit on the first one that resolves to allow (ANY-of semantics). Single-permission usage continues to work unchanged. (#16605)// Before — one permission per call await fgaProvider.check({ user, resource: { type: 'agent', id: 'abc' }, permission: 'agents:read', }); // After — single permission or ANY-of array await fgaProvider.check({ user, resource: { type: 'agent', id: 'abc' }, permission: ['agents:read', 'agents:execute'], });When all permissions in the array are denied, the thrown
FGADeniedErrorlists them asany of [a, b, c]in its message.
Patch Changes
@mastra/clickhouse@1.9.0
Minor Changes
- Added delta polling support for observability list APIs in core, DuckDB, and ClickHouse. (#16632)
Patch Changes
- Fixed duplicate entries in the ClickHouse v-next observability discovery endpoints — tags, services, environments, entities, metric names, and metric labels now return each value once. Existing deployments are reconciled automatically on next startup; no manual migration required. (#16798)
@mastra/client-js@1.20.0
Minor Changes
-
Added
agent.browserSession(threadId?)andagent.closeBrowser(threadId?)to theAgentresource, plus aGetAgentBrowserSessionResponsetype. (#16668)browserSessionprobes the server's browser session state before opening a screencast WebSocket, so the connection is only made when the server has screencast support installed and an active session exists for the thread.closeBrowserends the agent's browser session (or a single thread's session ifthreadIdis passed). Both methods go through the configured clientbaseUrlandapiPrefix, so they work with servers mounted under a non-default API prefix.const probe = await client.getAgent('my-agent').browserSession(threadId); if (probe.screencastAvailable && probe.hasSession) { // safe to open the screencast WebSocket } await client.getAgent('my-agent').closeBrowser(threadId); -
Added typed client-side resources for the stored-entity HTTP surface so you no longer have to hand-roll
fetchcalls. (#16666)import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); // List/get with favorite metadata const { items } = await client.storedAgents.list({ page: 1, perPage: 20 }); const agent = await client.storedAgents.get(items[0].id); console.log(agent.favoriteCount, agent.isFavorited); // Favorite toggle await client.storedAgents.favorite(agent.id); await client.storedAgents.unfavorite(agent.id); // Versioning + publish const draft = await client.storedSkills.create({ /* ... */ }); const published = await client.storedSkills.publish(draft.id); await client.storedSkills.restore(draft.id, { version: 1 });Also regenerated
route-types.generated.tsto cover the new editor-builder introspection routes (/editor/builder/settings,/editor/builder/infrastructure) and the external skill-registry endpoints under/editor/builder/registries(list, search, popular, preview, install). -
Added delta polling support for observability list endpoints. (#16632)
const page = await client.observability.listTraces({ mode: 'page', filters: { entityName: 'agent-1' }, }); const delta = await client.observability.listTraces({ mode: 'delta', filters: { entityName: 'agent-1' }, after: page.deltaCursor, });Use
mode: 'delta'to fetch only new items after the last cursor.Page-mode responses include
paginationanddeltaCursorwhen delta polling is supported. Delta-mode responses includedeltaand do not includepagination.If you read these responses directly in typed code, note that
paginationis only included in page mode. -
Narrowed
AgentSignalContentsfromBaseMessageListInputtostring | (TextPart | FilePart)[]. (#16622)Fixed two signal-content bugs:
user-messagesignal attributes now reach the LLM- multimodal non-
user-messagesignals no longer lose file parts
Callers that previously passed wrapped message shapes to
agent.sendSignalshould now pass a bare string or a bare parts array.Before:
{ type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }After:
{ type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }Added an optional
providerOptionsfield toagent.sendSignalthat flows through to the resulting prompt turn (asproviderOptionson the LLM message) and is persisted on the stored signal message (ascontent.providerMetadata).
Patch Changes
-
Fixed provider and dataset item history response types to include fields returned by the API. (#16213)
-
Fixed an issue where recursive client-tool continuations after
resumeStream(andresumeStreamUntilIdle) incorrectly re-hit the one-shot resume endpoint instead of falling back to the regular stream endpoint. The resume routes consume server-sideresumeDataand cannot be replayed, so client-tool continuations now route to/streamand/stream-until-idlerespectively. (#16670) -
Fixed the playground memory configuration display for agents using observationalMemory: true. (#16213)
@mastra/convex@1.1.0
Minor Changes
-
Added native Convex vector search support for production workloads. The new
ConvexNativeVectoradapter uses (#16729) Convex schema-defined vector indexes andctx.vectorSearchinstead of loading vectors throughConvexVectorand scoring them in JavaScript.Define a native vector table in
convex/schema.ts:import { defineSchema } from 'convex/server'; import { defineMastraNativeVectorTable } from '@mastra/convex/schema'; export default defineSchema({ docs_vectors: defineMastraNativeVectorTable({ dimensions: 1536, }), });Export the native vector handlers:
import { mastraNativeVectorAction, mastraNativeVectorMutation, mastraNativeVectorQuery } from '@mastra/convex/server'; export const query = mastraNativeVectorAction; export const read = mastraNativeVectorQuery; export const write = mastraNativeVectorMutation;Then configure
ConvexNativeVectorin your Mastra app:import { ConvexNativeVector } from '@mastra/convex'; const vectorStore = new ConvexNativeVector({ id: 'convex-native-vectors', deploymentUrl: process.env.CONVEX_URL!, adminAuthToken: process.env.CONVEX_ADMIN_KEY!, indexes: { docs: { tableName: 'docs_vectors', vectorIndexName: 'by_embedding', dimension: 1536, }, }, }); -
Added
ConvexServerCacheso Convex-backed Mastra apps can keep durable stream replay and response cache state in Convex. (#16736)import { ConvexServerCache } from '@mastra/convex'; const cache = new ConvexServerCache({ deploymentUrl: process.env.CONVEX_URL!, adminAuthToken: process.env.CONVEX_ADMIN_KEY!, });The package also exports the Convex cache schema tables and server mutation for mounting the cache handler in a Convex app. Existing Convex users who adopt the cache must add
mastra_cacheandmastra_cache_list_itemsto their Convex schema, mount themastraCachehandler, and deploy the schema update.
Patch Changes
@mastra/datadog@1.2.1
Patch Changes
-
Fixed Datadog LLM output tool calls so they render as structured tool call blocks. (#16789)
-
Fixed Datadog LLM span input formatting to remove empty user messages. (#16785)
@mastra/deployer@1.36.0
Minor Changes
-
Added route-specific CORS configuration so credentialed cross-origin access can be limited to selected custom routes and channel webhooks. (#16689)
registerApiRoute('/customer-webhook', { method: 'POST', cors: { origin: ['https://customer-saas.example'], credentials: true, }, handler: async c => c.json({ ok: true }), });new Agent({ id: 'support-agent', name: 'Support Agent', instructions: '...', model, channels: { adapters: { web: { adapter: createWebAdapter(), cors: { origin: ['https://customer-saas.example'], credentials: true, }, }, }, }, });Use
server.corsfor one global CORS policy across the server:new Mastra({ server: { cors: { origin: '*', }, }, });
Patch Changes
-
Browser streaming now works for stored agents. The deployer's
getToolsetfirst checks the runtime agent registry, then falls back to the editor's stored-agent lookup, so agents created at runtime through the editor can stream browser sessions without being pre-registered in code. (#16778) -
When browser streaming is unavailable (the
wsand@hono/node-wspackages aren't installed, or the deployer is running in a serverless environment), the deployer now registers a fallbackGET /api/agents/:agentId/browser/sessionroute that returns{ hasSession: false, screencastAvailable: false }. This lets clients detect that screencast won't work and skip the WebSocket upgrade instead of triggering a noisy reconnect loop. (#16668) -
Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/deployer-cloud@1.36.0
Patch Changes
- Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/deployer-cloudflare@1.1.37
Patch Changes
- Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/deployer-netlify@1.1.13
Patch Changes
- Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/deployer-vercel@1.1.31
Patch Changes
- Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/duckdb@1.4.0
Minor Changes
- Added delta polling support for observability list APIs in core, DuckDB, and ClickHouse. (#16632)
Patch Changes
- Fixed DuckDB observability storage so dev server restarts no longer fail when replaying cursor migrations. (#16803)
@mastra/editor@0.9.0
Minor Changes
-
EditorWorkspaceNamespacecan now snapshot a liveWorkspacefor persistence — the reverse ofhydrateSnapshotToWorkspace(): (#16673)const snapshot = await editor.workspace.snapshotFromWorkspace(runtimeWorkspace); await editor.workspace.create({ id: 'my-workspace', ...snapshot });snapshotFromWorkspace()isasyncand awaitssandbox.getInfo()andfilesystem.getInfo()so async providers likeCompositeFilesystemkeep their mount metadata in the stored config.Also includes two smaller behavioral fixes:
EditorSkillNamespace.publishSkillFromSource()stores the newfilesfield on the published skill version and stripsundefinedkeys before callingupdate()(libsql/pg adapters rejectundefinedbind arguments).CrudEditorNamespace.clearCache(id)always callsonCacheEvict(id), even when the entity wasn't cached, so subclasses can clean up runtime registries for version-specific lookups that bypass the editor cache.
-
Added an
editor.favoritesnamespace so direct (non-HTTP) callers can favorite, unfavorite, and query favorited stored agents/skills through the editor instance. (#16749)import { MastraEditor } from '@mastra/editor'; const editor = new MastraEditor({ mastra }); // Toggle await editor.favorites.favorite({ userId, entityType: 'agent', entityId }); await editor.favorites.unfavorite({ userId, entityType: 'agent', entityId }); // Lookups const isFav = await editor.favorites.isFavorited({ userId, entityType: 'agent', entityId }); const favSet = await editor.favorites.isFavoritedBatch({ userId, entityType: 'agent', entityIds }); const ids = await editor.favorites.listFavoritedIds({ userId, entityType: 'agent' });The namespace performs the storage mutation only — visibility and ownership enforcement still belong to the caller (the HTTP route handlers in
@mastra/serveralready do this).
Patch Changes
@mastra/express@1.3.23
Patch Changes
-
Updated the adapter permission check to read user permissions from the new namespaced request-context key
mastra__userPermissions(wasuserPermissions). This matches the namespaced keys that@mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)No action needed for typical users — install the matching
@mastra/serverrelease and the adapter will continue to enforce route permissions exactly as before. -
Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/fastembed@1.1.0
Minor Changes
- Replace the abandoned
fastembednpm dependency with a maintained, vendored implementation. The public API and all embedding models remain unchanged — no migration needed. (#16772)
@mastra/fastify@1.3.23
Patch Changes
-
Updated the adapter permission check to read user permissions from the new namespaced request-context key
mastra__userPermissions(wasuserPermissions). This matches the namespaced keys that@mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)No action needed for typical users — install the matching
@mastra/serverrelease and the adapter will continue to enforce route permissions exactly as before. -
Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/hono@1.4.18
Patch Changes
-
Updated the adapter permission check to read user permissions from the new namespaced request-context key
mastra__userPermissions(wasuserPermissions). This matches the namespaced keys that@mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)No action needed for typical users — install the matching
@mastra/serverrelease and the adapter will continue to enforce route permissions exactly as before. -
Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666) -
Added
GET /agents/:agentId/browser/sessionendpoint (under the configuredapiPrefix, default/api) that reports whether a screencast WebSocket should be opened for an agent and thread. Clients can probe this before upgrading to a WebSocket to avoid idle connections and reconnect storms. (#16668)curl "http://localhost:4111/api/agents/my-agent/browser/session?threadId=thread-1" # {"hasSession":true,"screencastAvailable":true}The response shape is
{ hasSession: boolean, screencastAvailable: true }.screencastAvailableis alwaystruewhen this route is registered; the deployer registers a fallback that returns{ hasSession: false, screencastAvailable: false }when browser streaming packages aren't installed, so clients can use the same probe in both cases.setupBrowserStreamnow accepts an optionalapiPrefixso the probe and existingPOST /agents/:agentId/browser/closeroutes are mounted under the same prefix as the rest of the server. The deployer wires this frommastra.getServer().apiPrefixautomatically. -
The Hono adapter now awaits
getToolsetcalls in browser-stream routes, supporting deployers that resolve agents asynchronously (such as stored agents looked up via the editor). (#16778)
@mastra/koa@1.5.6
Patch Changes
-
Updated the adapter permission check to read user permissions from the new namespaced request-context key
mastra__userPermissions(wasuserPermissions). This matches the namespaced keys that@mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)No action needed for typical users — install the matching
@mastra/serverrelease and the adapter will continue to enforce route permissions exactly as before. -
Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/libsql@1.11.1
Patch Changes
- Fixed scheduler performance and correctness issues that could cause excessive Postgres CPU and noisy failed runs. (#16805)
- Added missing indexes on the schedules tables that the tick loop polls every 10 seconds:
(status, next_fire_at)onmastra_schedulesand(schedule_id, actual_fire_at)onmastra_schedule_triggers. Without these the scheduler performed a full sequential scan on every tick. - The scheduler tick loop is now only started when at least one workflow declares a schedule (or
scheduler.enabledis set explicitly), so deployments without scheduled workflows no longer poll the database at all. - The scheduler now validates that a schedule's target workflow is still registered before firing it. Schedules whose target workflow has been removed from the Mastra config are skipped for a short grace window and then deleted, so stale rows stop producing failed workflow runs forever.
- The scheduler is now lazily initialized inside
startWorkers(). Accessingmastra.schedulerbeforestartWorkers()runs throws a descriptive error instead of returning a half-initialized instance.
- Added missing indexes on the schedules tables that the tick loop polls every 10 seconds:
@mastra/mcp@1.8.0
Minor Changes
-
Added MCP tool annotations to the
requireToolApprovalcontext and exposed them on tools returned fromlistTools()/listToolsets(). (#16784)The
requireToolApprovalcallback now receives the server-advertisedannotations(title,readOnlyHint,destructiveHint,idempotentHint,openWorldHint) alongsidetoolNameandargs. This lets you write declarative approval policies instead of hardcoding tool name lists. Annotations are also propagated onto Mastra tools astool.mcp.annotationsso apps can render them in UI.Security caveat (per the MCP spec): annotations are hints, not guarantees. Clients MUST treat them as untrusted unless they come from a trusted server. Do not use annotations alone as a security boundary for servers you do not control — set
requireToolApproval: truefor those. When the server omits annotations entirely, this field isundefined, so policies can distinguish "no annotations" from "annotated as safe".import { MCPClient } from '@mastra/mcp'; // Before — hardcoded tool name lists, server-specific const mcp = new MCPClient({ servers: { github: { url: new URL('https://example.com/mcp'), requireToolApproval: ({ toolName }) => toolName === 'delete_repo', }, }, }); // After — annotation-driven, works across any trusted MCP server const mcp = new MCPClient({ servers: { github: { url: new URL('https://example.com/mcp'), requireToolApproval: ({ annotations }) => { if (!annotations) return true; if (annotations.readOnlyHint) return false; if (annotations.destructiveHint) return true; return false; }, }, }, }); // Annotations are also visible on tools returned by listTools() const tools = await mcp.listTools(); for (const tool of Object.values(tools)) { console.log(tool.mcp?.annotations); }Closes #16766.
Patch Changes
-
Fixed an issue where OAuth token requests dropped
client_idandclient_secretfor confidential clients. The provider previously shipped an emptyaddClientAuthenticationmethod that satisfied the MCP SDK's existence check and short-circuited its default credential attachment, causinginvalid_requesterrors on token exchange and refresh against confidential-client OAuth servers. The empty stub has been removed so the SDK's built-in client authentication runs again. See #16854. (#16862) -
Close previous SSE transport before accepting a new connection in
MCPServer.connectSSE(). Previously, sequential SSE connections to the same server would fail with "Already connected to a transport" because the underlying protocol was never closed when the previous client disconnected. (#16695)
@mastra/memory@1.19.0
Minor Changes
-
Added
activateAfterIdle: "auto"for Observational Memory early activation. (#16663)Mastra can now choose an idle activation timeout from the active model provider's prompt cache behavior. OpenAI also respects
providerOptions.openai.promptCacheRetentionwhen available.const memory = new Memory({ options: { observationalMemory: { model: 'google/gemini-2.5-flash', activateAfterIdle: 'auto', activateOnProviderChange: true, }, }, }); -
Add
observeAttachmentstoObservationConfigfor Observational Memory. Use it to control whether image/file parts on observed messages are forwarded to the Observer model alongside their placeholder text lines. (#16671)true(default) — forward all attachments (existing behavior).false— drop all attachments; placeholders still appear in the observer transcript.string[]— allowlist of mimeType patterns, e.g.['image/*']or['application/pdf']. Matching is case-insensitive and supports exact,type/*, and*patterns.
Useful when the Observer model is text-only (some DeepSeek endpoints, etc.) while the main agent uses a multimodal model. Tool-result attachments are filtered with the same rule.
new Memory({ options: { observationalMemory: { observation: { model: 'deepseek/deepseek-chat', // text-only observer observeAttachments: false, // or e.g. ['image/*', 'application/pdf'] }, }, }, });
Patch Changes
-
feat(memory): start background buffering of unobserved messages when agent goes idle (#16694)
In OM buffering mode, when the agent goes idle (turn.end()), any unobserved messages are now buffered in the background via a fire-and-forget buffer() call. This ensures observations are computed proactively rather than waiting for the next turn's step.prepare().
@mastra/nestjs@0.1.7
Patch Changes
- Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/observability@1.13.0
Minor Changes
-
Roll up token usage from internal MODEL_GENERATION spans onto the closest exported ancestor span. When
tracingPolicy.internalfilters a model call out of exported traces, its tokens used to disappear from both the trace UI and metrics. Now: (#16434)- The visible ancestor (e.g.
PROCESSOR_RUN,AGENT_RUN) gets aninternalUsageattribute summing the tokens consumed by its hidden descendants — so a Mastra-owned processor that runs an internal agent (moderation, PII detector, structured output, etc.) shows its aggregate cost on the visiblePROCESSOR_RUNspan. - Token / cost metrics still emit, but are attributed via labels to the visible ancestor instead of the hidden agent.
No action required — the rollup applies automatically whenever an internal
MODEL_GENERATIONends inside a non-internal ancestor. - The visible ancestor (e.g.
-
MastraStorageExporternow notifies custom exporters and connected integrations when it cannot persist observability events, such as unsupported storage or retries being exceeded. This matches the behavior already available onDefaultExporter. (#16755)Also fixed an issue in both exporters where span updates waiting on their parent span could be silently lost if a later flush in the same cycle failed.
Patch Changes
@mastra/pg@1.11.1
Patch Changes
- Fixed scheduler performance and correctness issues that could cause excessive Postgres CPU and noisy failed runs. (#16805)
- Added missing indexes on the schedules tables that the tick loop polls every 10 seconds:
(status, next_fire_at)onmastra_schedulesand(schedule_id, actual_fire_at)onmastra_schedule_triggers. Without these the scheduler performed a full sequential scan on every tick. - The scheduler tick loop is now only started when at least one workflow declares a schedule (or
scheduler.enabledis set explicitly), so deployments without scheduled workflows no longer poll the database at all. - The scheduler now validates that a schedule's target workflow is still registered before firing it. Schedules whose target workflow has been removed from the Mastra config are skipped for a short grace window and then deleted, so stale rows stop producing failed workflow runs forever.
- The scheduler is now lazily initialized inside
startWorkers(). Accessingmastra.schedulerbeforestartWorkers()runs throws a descriptive error instead of returning a half-initialized instance.
- Added missing indexes on the schedules tables that the tick loop polls every 10 seconds:
@mastra/playground-ui@29.0.0
Minor Changes
-
Added
ContextMenufor right-click interactions. Supports submenus, checkbox and radio items, keyboard shortcuts, and adestructivevariant for dangerous actions like delete. (#16791)import { ContextMenu } from '@mastra/playground-ui'; <ContextMenu> <ContextMenu.Trigger className="…">Right click here</ContextMenu.Trigger> <ContextMenu.Content> <ContextMenu.Item>Rename</ContextMenu.Item> <ContextMenu.Item variant="destructive">Delete</ContextMenu.Item> </ContextMenu.Content> </ContextMenu>; -
Removed
ButtonWithTooltipfrom@mastra/playground-ui. UseButtonwith thetooltipprop instead. (#16719)Migration
// before import { ButtonWithTooltip } from '@mastra/playground-ui'; <ButtonWithTooltip tooltipContent="Search"> <Search /> </ButtonWithTooltip>; // after import { Button } from '@mastra/playground-ui'; <Button tooltip="Search"> <Search /> </Button>;tooltipsupports the same values astooltipContent. Icon-only buttons that pass a stringtooltipnow also get it as theiraria-labelautomatically, matching how labelled controls have always behaved. Pass an explicitaria-labelto override. -
Added a custom date range option to the Metrics page date picker. You can now filter metrics by an arbitrary start and end date and time, matching the Traces page, alongside the existing relative presets (last 24 hours, 3, 7, 14, and 30 days). (#16832)
The selected range is reflected in the URL so it can be bookmarked or shared:
/metrics?period=custom&dateFrom=2026-05-01T00:00:00.000Z&dateTo=2026-05-07T23:59:59.999Z -
Refreshed Button + Card design system tokens. (#16769)
Button variants (breaking): consolidated to
default,primary,outline,ghost. Thecta,contrast, and unusedlinkvariants have been removed.primarynow uses a high-contrastneutral6fill instead ofsurface4, so it reads clearly as the form submit action in both themes.// Before <Button variant="cta">Save</Button> <Button variant="contrast">Done</Button> <Button variant="link">Open</Button> // After <Button variant="primary">Save</Button> // cta → primary (no brand green; theme-aware high contrast) <Button variant="primary">Done</Button> // contrast → primary (same recipe, renamed) <Button as="a" href="…" variant="ghost">Open</Button> // link → ghost (or plain <a> for inline text links)New tokens:
--surface-overlay-softand--surface-overlay-strong— alpha overlays of the opposite-theme color, used bySectionCardheader strip andDashboardCardfill so cards read consistently on any surface.Other:
- DashboardCard radius reduced to
rounded-xland padding tightened topx-4 py-3for better grid density. - SectionCard wrapper no longer fills its background — header strip + border carry definition.
- Dark
surface2/surface3darkened slightly (16.84% → 16%, 19.13% → 18%) so the main frame reads as a distinct surface. - Dark
border1/border2alphas bumped (6% → 7%, 10% → 11%) for closer dark/light parity. - Removed deprecated
--section-card-*tokens and their@utilityblocks.
- DashboardCard radius reduced to
Patch Changes
-
Added a
destructivevariant onDropdownMenu.Itemto highlight dangerous actions like delete. (#16791)<DropdownMenu.Item variant="destructive">Delete project</DropdownMenu.Item> -
PopoverContentno longer forwards the underlying library's auto-focus event handlers (onOpenAutoFocus,onCloseAutoFocus). To control focus when the popover opens or closes, useinitialFocusandfinalFocus. (#16791)// Before <PopoverContent onOpenAutoFocus={(e) => e.preventDefault()} /> // After <PopoverContent initialFocus={false} /> -
Fixed a crash in filter menus with nested submenus (such as the Filter on the Agent review page) that showed "
MenuPortalmust be used withinMenu". The submenu content now uses the design system'sDropdownMenu.SubContentinstead of the underlying library's portal directly. (#16829) -
Fixed type definitions and shared UI component types for
@mastra/playground-ui. (#16213) -
AlertDialog's API and behavior are unchanged —asChildonAlertDialog.Trigger, andAlertDialog.ActionandAlertDialog.Cancel, all work exactly as before. (Internally it now builds on Base UI primitives.) (#16824) -
Moved the
Collapsiblecomponent to Base UI, with a smoother height-based expand and collapse animation. The public API is unchanged —asChildonCollapsibleTriggerstill works. (#16825) -
Upgraded
@base-ui/reactto 1.5, making popups noticeably faster — components built on Base UI such asTooltip,Popover,DropdownMenuandContextMenunow open and close more quickly. (#16819) -
Moved the Level icon from its own column into the Name column, next to the trace name, on the Observability traces list. (#16712)
-
The Traces list now updates live via delta polling. Previously the list was refetched every 10 seconds, replacing the whole page with no signal about what changed; now new traces appear within a few seconds of being created, with a brief highlight to draw attention. Status changes on already-visible rows (running → success / error) also propagate without intervention, and returning to the tab after being idle re-syncs from a fresh cursor. (#16727)
New
useTracesreturn fieldsisRefetching— true while any meaningful refetch is in flight. Use it to drive a heartbeat indicator.autoRefetch/setAutoRefetch— pause / resume all automatic polling so the consumer can render an opt-out toggle.recentlyAddedKeys—Set<string>oftraceId:spanIdfor rows that just arrived via delta polling. Drives the temporary highlight inTracesListView.
New polling config
Every timing in the hook is tunable per-instance via a new
pollingoption:import { useTraces, type TracesPollingConfig } from '@mastra/playground-ui'; useTraces({ filters, listMode, polling: { deltaPollIntervalMs: 10_000, idleGuardThresholdMs: 5 * 60_000, }, });Omitted fields fall through to the defaults (delta poll every 5s, idle reset after 15 min, status refresh every 60s, etc).
TracesListView
New optional
recentlyAddedKeys?: Set<string>prop. Rows whosetraceId:spanIdis in the set get theanimate-row-highlightclass — a brief fade-out to transparent, added toindex.css.Compatibility
Requires
@mastra/serverand@mastra/client-jsat the versions that ship the observability delta-polling endpoints, and a store that opts into delta polling (@mastra/clickhouse,@mastra/duckdb, and the in-memory store today). When unavailable — older server or a store without delta capability — the hook silently falls back to page-mode interval refetching. No consumer changes required. -
Added
alignandstackvariants toPageLayout.Row. Usestack="responsive"for top bars that should collapse to a vertical stack on narrow viewports, andalign="center"to vertically center children. Applied the new variants to the Prompts and Workflows top bars so the search field and primary action share a single row on desktop and stack on mobile. (#16714)<PageLayout.Row align="center" stack="responsive"> <ListSearch ... /> <Button ...>Create</Button> </PageLayout.Row> -
Fixed the playground memory configuration display for agents using observationalMemory: true. (#16213)
-
Fixed double-counted cache token costs in the Metrics dashboard. The Model Usage & Cost table and the Token Usage by Agent table were summing cache read/write costs on top of the total input cost, which already includes them. (#16737)
-
Migrated the Tooltip primitive to Base UI while preserving the existing API. The popup explicitly sets
role="tooltip"so consumers can keep querying it viagetByRole('tooltip')(Base UI does not add this role automatically). Existing<TooltipTrigger asChild>usage continues to work unchanged, and Base UI's nativerenderprop is now also supported onTooltipTriggerso consumers wrapping anchors, custom router links, or icons can pass the element directly without anasChildadapter: (#16713)// Still supported <TooltipTrigger asChild> <Button>Save</Button> </TooltipTrigger> // New: pass the element via Base UI's native API <TooltipTrigger render={<Button>Save</Button>} />Also fixed the arrow rendering so the diagonal stroke meets the popup outline at the exact same pixel center on every side, removing the ~1px seam previously visible where the arrow joined the popup edge.
-
Migrated the Slider component to base-ui with a refined neutral visual design. (#16788)
What changed
- Replaced
@radix-ui/react-sliderwith@base-ui/react/slideras the underlying primitive - Refreshed visuals: thin rounded thumb with white border and neutral inside, opacity-based track that adapts to any surface, neutral filled indicator (no green/accent color)
- Larger click target via padded
Slider.Controland an invisible hit area on the thumb so it is easier to grab - Added
cursor-pointeron the control andcursor-not-allowedwhen disabled - Removed the now unused
@radix-ui/react-sliderand@radix-ui/react-tabsdependencies
API compatibility
The public API is preserved.
onValueChangeandonValueCommittedare wrapped so consumers always receivenumber[], even though base-ui returnsnumber | number[]internally. Existing call sites like<Slider value={[temperature]} onValueChange={value => setTemperature(value[0])} />continue to work without changes. - Replaced
-
Moved the
Dialogcomponent to Base UI. The public API is unchanged —asChildonDialogTriggerandDialogClosestill works the same way, and open/close animations behave as before. (#16821)
@mastra/react@0.4.0
Minor Changes
-
Added
clientToolsoption touseChat'sgenerate/streamcalls for forwarding browser-side tools to the agent on each invocation. (#16778)import { useChat } from '@mastra/react'; const { generate } = useChat({ agentId: 'my-agent' }); await generate({ messages: [{ role: 'user', content: 'Show a toast that says hi' }], clientTools: { showToast: { description: 'Show a toast to the user', inputSchema: z.object({ message: z.string() }), execute: ({ message }) => toast(message), }, }, });Client tools are forwarded as-is to the underlying
agent.generate()andagent.stream()calls. -
Narrowed
AgentSignalContentsfromBaseMessageListInputtostring | (TextPart | FilePart)[]. (#16622)Fixed two signal-content bugs:
user-messagesignal attributes now reach the LLM- multimodal non-
user-messagesignals no longer lose file parts
Callers that previously passed wrapped message shapes to
agent.sendSignalshould now pass a bare string or a bare parts array.Before:
{ type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }After:
{ type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }Added an optional
providerOptionsfield toagent.sendSignalthat flows through to the resulting prompt turn (asproviderOptionson the LLM message) and is persisted on the stored signal message (ascontent.providerMetadata).
Patch Changes
@mastra/server@1.36.0
Minor Changes
-
Added delta polling support for observability list endpoints. (#16632)
const page = await client.observability.listTraces({ mode: 'page', filters: { entityName: 'agent-1' }, }); const delta = await client.observability.listTraces({ mode: 'delta', filters: { entityName: 'agent-1' }, after: page.deltaCursor, });Use
mode: 'delta'to fetch only new items after the last cursor.Page-mode responses include
paginationanddeltaCursorwhen delta polling is supported. Delta-mode responses includedeltaand do not includepagination.If you read these responses directly in typed code, note that
paginationis only included in page mode. -
Added automatic FGA metadata for stored resource routes plus optional request scope isolation for stored resource APIs. Enable protected-route coverage with provider options: (#16651)
const fga = new MastraFGAWorkos({ resourceMapping, permissionMapping, requireForProtectedRoutes: true, auditProtectedRoutes: 'warn', }); -
Added an HTTP surface for stored agents/skills/workspaces, plus introspection endpoints for the agent-builder and an external skill-registry proxy. Studio and the client SDK use these endpoints to back the new "stored entity" management UI. (#16666)
# Browse + manage stored entities (responses include favoriteCount + isFavorited) GET /stored/agents?visibility=public&page=1&perPage=20 GET /stored/agents/:id POST /stored/agents PATCH /stored/agents/:id DELETE /stored/agents/:id # Versioning POST /stored/skills/:id/publish POST /stored/skills/:id/activate POST /stored/skills/:id/restore # Favorites PUT /stored/agents/:id/favorite DELETE /stored/agents/:id/favorite # Builder introspection GET /editor/builder/settings GET /editor/builder/infrastructure # External skill registry proxy (skills.sh) GET /editor/builder/registries GET /editor/builder/registries/:registryId/search GET /editor/builder/registries/:registryId/popular GET /editor/builder/registries/:registryId/skills/:owner/:repo/preview POST /editor/builder/registries/:registryId/skills/:owner/:repo/installHighlights:
- Visibility + authorship gating. Stored agents/skills now resolve a caller's author identity from the request context. Non-admin users only see their own + public entities. Admins see everything.
- Favorites. List/get responses include
favoriteCountand the caller'sisFavoritedflag.PUT/DELETE /stored/{agents|skills}/:id/favoritetoggle the favorite for the caller. - Avatar validation. Stored-agent/skill metadata avatars are validated through a new
validateMetadataAvatarUrlhelper (rejects payloads over the size limit or with malformed base64). - Model-policy enforcement. Stored-agent create/update routes invoke
assertModelAllowedvia the newresolveBuilderModelPolicyhelper. Disallowed models map to HTTP 422 with a structured body —{ code, attempted, offendingLabel, allowed }— viahandleError's newModelNotAllowedErrormapping. - Builder introspection.
GET /editor/builder/settingsreturns feature flags, configuration, picker visibility, and model policy.GET /editor/builder/infrastructurereports browser-provider and sandbox status. Both default to{ enabled: false }when noMastraEditoris configured. - External skill registry.
/editor/builder/registries/*proxies the public skills.sh catalog so the builder UI can browse and install registered skills.
This also bumps the
@mastra/corepeer dependency floor to>=1.34.0-0(see the separate changeset) because the new handlers and error mapping import runtime values from@mastra/core/agent-builder/ee. -
Narrowed
AgentSignalContentsfromBaseMessageListInputtostring | (TextPart | FilePart)[]. (#16622)Fixed two signal-content bugs:
user-messagesignal attributes now reach the LLM- multimodal non-
user-messagesignals no longer lose file parts
Callers that previously passed wrapped message shapes to
agent.sendSignalshould now pass a bare string or a bare parts array.Before:
{ type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }After:
{ type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }Added an optional
providerOptionsfield toagent.sendSignalthat flows through to the resulting prompt turn (asproviderOptionson the LLM message) and is persisted on the stored signal message (ascontent.providerMetadata). -
Routes can now require any one of multiple permissions by passing an array to
requiresPermission. When an array is provided, the request is allowed if the caller holds any of the listed permissions. Existing single-string usage continues to work. (#16605)// Before — single permission only { path: '/v1/things', method: 'GET', requiresPermission: 'things:read', handler, } // After — single permission or ANY-of array { path: '/v1/things/:id/stream', method: 'GET', requiresPermission: ['things:read', 'things:execute'], handler, }Denial messages now read
Missing required permission: a or b or cwhen an array is used.New endpoint
GET /api/auth/roles/:roleId/permissionsreturns the resolved permission list for a role. Useful for client-side gating and admin tooling.const res = await fetch('/api/auth/roles/admin/permissions', { credentials: 'include' }); // { "roleId": "admin", "permissions": ["*"] }Namespaced request-context keys (non-breaking)
coreAuthMiddlewarenow writes user state under namespaced keys (mastra__user,mastra__userPermissions,mastra__userRoles) in addition to the existing bare keys (user,userPermissions,userRoles). The bare keys are still written for backward compatibility, so existing middleware, integrations, and built-in handlers that readrequestContext.get('user')continue to work unchanged.New code should prefer the namespaced constants to avoid collisions with caller-supplied request-context entries:
import { MASTRA_USER_KEY, MASTRA_USER_PERMISSIONS_KEY, MASTRA_USER_ROLES_KEY } from '@mastra/server/auth'; const user = requestContext.get(MASTRA_USER_KEY); const permissions = requestContext.get(MASTRA_USER_PERMISSIONS_KEY) as string[] | undefined; const roles = requestContext.get(MASTRA_USER_ROLES_KEY) as string[] | undefined;The bare keys (
user,userPermissions,userRoles) remain populated and are considered the documented public surface for this release; a future major release may deprecate them.Route permission derivation
getEffectivePermission()now recognizes stored resource families (stored-agents,stored-skills,stored-prompt-blocks,stored-mcp-clients,stored-scorers,stored-workspaces) andpublish/activate/restoreaction suffixes on stored-resource routes. Return type widened tostring | string[] | nullto support routes that map to multiple permissions.
Patch Changes
-
Fixed stored resource updates to preserve existing metadata keys. (#16651)
-
Hardened the stored-agent and stored-skill favorite toggle endpoints (
PUT/DELETE /stored/{agents,skills}/:id/favorite) so callers can no longer favorite or unfavorite entities outside their tenant scope. (#16749)Deployments that configure
storedResources.scopenow get the same 404-on-mismatch protection on favorite toggles that already applied to read/update/delete. Single-tenant deployments are unaffected.Also corrected JSDoc on stored-agent and stored-skill handlers to reference the canonical resource/action names (
stored-agents:read,stored-skills:write). -
Fix DELETE custom routes forwarding JSON request bodies. (#16857)
-
Fixed CompositeAuth incorrectly advertising SSO, session, and user provider capabilities when no inner provider supports them. Studio would show an SSO login button even when no provider had SSO configured, leading to 401 errors on login attempts. The duck-typing check now verifies that interface methods are actual functions rather than just present on the prototype chain. (#16664)
-
Fixed provider and dataset item history response types to include fields returned by the API. (#16213)
-
Widened
BrowserStreamConfig.getToolsetto support async lookup. Existing synchronous implementations continue to work — the type now acceptsMastraBrowser | undefinedorPromise<MastraBrowser | undefined>. (#16778)This unblocks server-adapter implementations that need to resolve agents asynchronously (for example, hydrating stored agents from storage on first browser-stream connection).
import { setupBrowserStream } from '@mastra/server'; await setupBrowserStream(app, { getToolset: async agentId => { const agent = await resolveAgent(agentId); return agent?.browser; }, apiPrefix, }); -
Align stored-entity authorship checks with their RBAC resource names. Stored-agent and stored-skill handlers were calling
hasAdminBypass/assertReadAccess/assertWriteAccess/resolveAuthorFilterwithresource: 'agents'andresource: 'skills', but the routes are gated bystored-agents:*/stored-skills:*permissions. An admin grantedstored-agents:*(orstored-skills:*) without the global*wildcard would pass route authorization but be treated as a non-admin inside the handler, so they could not list, read, or update private records owned by other users. Handlers now usestored-agentsandstored-skillsas the authorship resource string, matching the permission strings emitted by the route layer. (#16666) -
Restore backwards compatibility for the legacy
GET /api/memory/threads?orderBy=<field>&sortDirection=<dir>query shape emitted by@mastra/client-js< 1.18 (and any hand-rolled HTTP clients written against pre-1.0 docs). Server 1.31.0 turned that shape into a hard 400 (Invalid input: expected object, received undefined); the legacy bare-string +sortDirectionpair is now transparently fused into the current{ orderBy: { field, direction } }object shape before validation, so pinned clients keep working. (#16745)Current callers using bracket notation (
orderBy[field]=...&orderBy[direction]=...) or a JSON-stringifiedorderByare unaffected. -
Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666) -
Fixed the playground memory configuration display for agents using observationalMemory: true. (#16213)
@mastra/slack@1.2.1
Patch Changes
-
SlackProvider.connect()now merges with existing channel adapters instead of replacing them, preserving adapters the agent author already configured (e.g. Discord). (#16517)- Slack interactive payloads (button clicks, modal submissions) no longer return
400 Malformed JSON body. The provider only JSON-parses the body for the events callback path and forwards form-urlencoded payloads to the adapter's webhook handler unchanged. - Bumped
chatto^4.29.0.
@mastra/stagehand@0.2.3
Patch Changes
- Silence Stagehand console logging by default and route logs through a logger hook to avoid corrupting host TUIs. (#16677)
@mastra/temporal@0.1.6
Patch Changes
- Bumped the
@mastra/corepeer dependency floor from>=1.32.0-0to>=1.34.0-0. (#16666)
@mastra/voice-openai-realtime@0.12.4
Patch Changes
-
Fixed OpenAI Realtime voice output streaming for current audio event names. (#16763)
-
Fixed user audio transcriptions missing or duplicating segments when OpenAI Realtime sends completion events. (#16759)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.37
- @mastra/arize@1.1.1
- @mastra/arthur@0.2.11
- @mastra/braintrust@1.1.1
- @mastra/laminar@1.1.1
- @mastra/langfuse@1.3.1
- @mastra/langsmith@1.2.1
- @mastra/longmemeval@1.0.42
- @mastra/mcp-docs-server@1.1.39
- @mastra/opencode@0.0.39
- @mastra/otel-bridge@1.1.1
- @mastra/otel-exporter@1.1.1
- @mastra/posthog@1.0.25
- @mastra/sentry@1.0.24
@mastra/core@1.37.0
Highlights
Google Cloud Spanner Storage Adapter (@mastra/spanner@1.0.0)
New first-party storage backend for Google Cloud Spanner (GoogleSQL) covering most Mastra storage domains (memory, workflows, schedules, observability, etc.), including persisting tracing spans for Studio and supporting the local Spanner emulator.
Agent Channels Streaming UX + Tool Rendering Controls (Core + Slack)
Major upgrades to channel adapters: opt-in streaming text deltas, adaptive typing indicators, and a new toolDisplay system (cards/text/timeline/grouped/hidden/function) to control how tool execution renders (including plan/timeline widgets and stable toolCallId correlation).
Client-Side Tool Observability End-to-End (@mastra/client-js + @mastra/observability)
Client-executed tools now show up in server traces via CLIENT_TOOL_CALL spans, with a shared observe helper for child spans/logs inside execute; observability exporters can ingest and forward this client telemetry (including duration metrics labeled toolType: 'client').
Agent Builder Routes Now Auto-Registered in Server Adapters
/agent-builder/* endpoints are now served automatically by any @mastra/server-based adapter (Express/Fastify/Hono/Koa/etc.) without manual wiring, while preserving lazy-loading to keep worker bundles small; new EE permissions gate access.
New Workspace Filesystem Provider via FilesSDK (@mastra/files-sdk@0.2.0)
Adds a unified WorkspaceFilesystem implementation backed by FilesSDK, letting you swap storage providers (S3/R2/GCS/Azure Blob/Vercel Blob/local FS, etc.) by changing the FilesSDK adapter without changing Mastra code.
Breaking Changes
@mastra/slack: removedcards: booleanandformatToolCallfrom Slack provider/channel config; migrate totoolDisplay: 'text'(forcards: false) ortoolDisplayfunction form (forformatToolCall).
Changelog
@mastra/core@1.37.0
Minor Changes
-
Improved agent channels UX: (#16937)
-
Streaming text — opt-in per-adapter
streamingflag (boolean | { updateIntervalMs?: number }) that pushes the agent's text deltas into the platform message progressively via the Chat SDK. -
Adaptive typing indicator — the platform's typing status now reflects what the agent is doing (
is working…at run start,is thinking…during reasoning,is typing…while generating text,is calling {toolName}…while a tool runs,is saving to memory…/is recalling memory…during memory work,is requesting approval for {toolName}…while a tool is suspended), coalesced so the platform API isn't called on every delta. Skipped while a streaming session is open since the widget itself conveys progress. -
toolDisplaymodes — newChannelAdapterConfig.toolDisplaycontrols how tool calls render:'cards'(default) — per-tool running/result cards in rich Block Kit form.'text'— per-tool running/result messages as plain text (replaces the oldcards: falseflag).'timeline'— every tool gets its own task row in a streaming widget with status icons and args.'grouped'— all tools in the run collapse into a single streaming widget; args fold inline into the title and successful results are suppressed for an at-a-glance summary (errors keep their full text).'hidden'— tools run silently; only the typing indicator shows work.- Function form (
ToolDisplayFn) — pass a function(event, ctx) => { kind: 'post', message } | { kind: 'stream', chunk } | undefinedto fully control how every tool event renders.'post'results post a discrete message (closing/reopening the streaming session when needed);'stream'results push atask_update/plan_updateinto the active streaming widget;undefinedskips the event. EveryToolDisplayEventvariant (running/result/error/approval) carries a stabletoolCallIdso user code can correlate events for the same tool invocation (e.g. as theidon a streamedtask_updateso the SDK updates a row in place rather than appending a new one).
'timeline'and'grouped'requirestreaming: trueand fall back to'cards'with a one-time warn if not enabled.'cards'/'text'work under both streaming modes — withstreaming: true, the driver closes the streaming session around each card, posts it, and reopens on the next chunk. Approve/deny prompts always render as a separate Block Kit card regardless of mode, since inline task entries and plain text can't carry interactive buttons.Deprecation (no breaking changes):
ChannelAdapterConfig.cards: booleanandChannelAdapterConfig.formatToolCallare now@deprecatedand surface in IDEs with a strikethrough. Both still work at runtime — they're mutually exclusive withtoolDisplayat the type level. WhentoolDisplayis not set:cards: trueresolves totoolDisplay: 'cards',cards: falseresolves totoolDisplay: 'text', andformatToolCallis shimmed into an equivalentToolDisplayFnthat only fires onresult/errorevents. Migrate at your leisure:cards: false→toolDisplay: 'text', andformatToolCall: ({ toolName, result }) => msg→toolDisplay: event => event.kind === 'result' ? { kind: 'post', message: msg } : undefined. -
typingStatuscustomization — newChannelAdapterConfig.typingStatus(boolean | (chunk, ctx) => string | false, defaulttrue). Set tofalseto suppress all typing indicators (useful when a live streaming widget already conveys progress), or pass a function to set custom copy per chunk. Compose with the exporteddefaultTypingStatushelper to fall back to built-in defaults for chunks you don't handle. -
Signal-aware message boundaries — when a
data-user-messagesignal echoes into the stream mid-reply, any in-flight text is flushed first so the agent's response renders as a new message after the user's signal instead of streaming into the prior reply. -
Stronger stay-silent prompt — the channel input processor's non-DM system message now explicitly calls out anti-patterns (bracketed status notes, "Got it"/"Noted" acknowledgments, apologizing for silence) and points the model at
add_reactionfor silent acknowledgments. Empty responses are framed as a first-class action rather than a fallback. -
Slack DM thread routing — each Slack thread (including top-level DMs) now maps to its own Mastra thread. Previously, replies and tool-approval clicks in a top-level DM could be routed into a sub-thread keyed by the bot's last message, causing follow-ups to thread under that message and tool approvals to fail to find the pending approval.
-
Parallel same-tool approval — fixed a bug where two parallel calls to the same tool with
requireApproval: trueclobbered each other's pending entry, so only the most recent could be approved. -
Tool error rendering — failing tools now emit a closing task update in
'timeline'/'grouped'modes (previously the row stayedin_progressand rendered as ⚠ at session close) and edit their card in'cards'/'hidden'modes. The error text is inlined into the taskdetails(with a ⚠ glyph) while the task itself staysstatus: 'complete'so a single tool failure doesn't flip the overall plan header to an error state. -
Observational-memory lifecycle in streaming widgets —
data-om-buffering-*anddata-om-activationchunks are routed into the active streaming session in'timeline'/'grouped'modes as their own task rows (e.g.Saved to memory (10x)with12.4k → 1.2k tokens), so memory work is visible alongside tool calls. Consecutive observation activations within a session coalesce into a singleRecalled memory (Nx)row with running totals instead of stacking — reflection runs often fire several activations back-to-back. The plan title is set toUpdating memoryon the first OM event so memory-only runs don't show the chat-SDK default ofThinking completed. OM buffering runs async in the background, so any still-in_progressOM task is optimistically markedcompletewhen the streaming session closes — without this, the chat-SDK plan widget would flip the "Saving to memory…" row to an error icon when the stream ends before the buffer flush resolves. In non-plan modes ('cards'/'text'/'hidden'/ToolDisplayFn) OM events are skipped entirely — a phantom Plan widget showing only memory rows would be inconsistent with the mode contract. Approvalplan_update/task_updaterows are also gated by'timeline'/'grouped'for the same reason; non-plan modes now post the approval card directly without flashing a single-row Plan widget that closes immediately after. -
Logger propagation — the Mastra logger is now propagated into
AgentChannelson register so channel-level logs flow through the configured logger. -
Internal refactor (no public API change) —
consumeAgentStreamnow dispatches to one of two focused drivers (streaming vs static) instead of switching ontoolDisplayinside a single 700-line loop. Tool-call correlation moved into aToolTrackerhelper and observational-memory rendering into a dedicatedrenderOmTaskUpdatehelper, both shared between drivers. Invalid combinations now warn and downgrade:streaming: false+'timeline'/'grouped'falls back to'cards'.streaming: true+'cards'/'text'is now valid and uses the streaming driver's close/post/reopen lifecycle.
import { Agent } from '@mastra/core/agent'; import { defaultTypingStatus } from '@mastra/core/channels'; import { createDiscordAdapter } from '@mastra/discord'; const agent = new Agent({ name: 'support-bot', channels: { adapters: { discord: { adapter: createDiscordAdapter(), streaming: true, toolDisplay: 'grouped', // 'cards' | 'text' | 'timeline' | 'grouped' | 'hidden' | ToolDisplayFn typingStatus: false, // suppress typing indicator when the widget already shows progress // Custom typing status per chunk; fall back to defaults for everything else. // typingStatus: (chunk, ctx) => { // if (chunk.type === 'tool-call' && chunk.payload.toolName === 'searchDocs') { // return 'is searching docs…'; // } // return defaultTypingStatus(chunk, ctx); // }, // Custom tool rendering via the function form: skip the running message, // post a single line on result. `undefined` skips rendering that event. // toolDisplay: event => { // if (event.kind !== 'result') return undefined; // return { kind: 'post', message: `🛠 ${event.toolName} → ${event.resultText}` }; // }, }, }, }, }); -
-
Client-side tools now appear in your traces when observability is configured. When an agent calls a tool that executes in the browser via
@mastra/client-js, aCLIENT_TOOL_CALLspan is recorded on the server trace so you can see which client tools were invoked, what arguments they received, and how they relate to the rest of the agent run. (#16425)Tools also gain an
observehelper on their execution context for recording child spans and logs from insideexecute:execute: async ({ userId }, { observe }) => { observe.log('info', 'fetching user', { userId }); return observe.span('fetch user', () => fetch(`/api/users/${userId}`)); }; -
Added signal delivery option attributes API that conditionally merges branch
attributesbased on whether a signal is delivered to an active agent run (ifActive.attributes) or an idle run (ifIdle.attributes). This enables contextual signal delivery — for example, tagging user messages aswhile-activewhen the agent is actively working. (#16923) -
Add per-provider capability files and
automode forobserveAttachments(#16922)- Generate per-provider capability files (e.g.
capabilities/openai.json) alongside the model router registry, sourced from models.dev API - Export
modelSupportsAttachments(modelRouterId)from@mastra/core/llmto check whether a model supports image/file attachments - Extend
observeAttachmentsconfig to accept'auto'in addition toboolean | string[] - When set to
'auto', the observer resolves the model (including function-based models) and checks the capability registry before deciding to forward or drop attachment parts
- Generate per-provider capability files (e.g.
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
cfa2e3a) -
Added sub-agent token usage to
onDelegationCompleteresults, so apps can track per-sub-agent token costs (#15825) -
Fixed thread subscription streams stalling or deadlocking when multiple consumers observe the same active run. Thread streams are now multicast to subscribers so each subscriber receives the run without competing for the underlying stream, and follow-up messages can continue while a subscription is active. (#16946)
-
Connecting or disconnecting an agent through a channel (e.g. Slack) now requires the same write permission as editing the underlying stored agent. The check runs on
POST /channels/:platform/connectandPOST /channels/:platform/:agentId/disconnectwhenever the target agent has a record in the stored-agents store. Callers without write access receive a404 Not found, matching the behavior of the stored-agent edit routes. Agents defined in code (no stored-agents record) are unaffected and continue to honor only the route's existing auth requirement. (#16949)The caller must either own the stored agent, have admin bypass, or hold
agents:edit(or a scopedagents:edit:<agentId>).POST /channels/slack/connect Authorization: Bearer <token-with-agents:edit> Content-Type: application/json { "agentId": "support-bot" }POST /channels/slack/support-bot/disconnect Authorization: Bearer <token-with-agents:edit>@mastra/coreis bumped as a patch to ship the regenerated permission definitions that back this check. -
Made provider capabilities loading lazy instead of bulk-syncing on every registry access. Removed subscription setup from harness switchMode() — subscriptions are now lazily ensured at send time. Added 10s TTL cache and invalidation method for listAvailableModels(). (#17008)
-
Preserve
thiswhen resolving permissions for the "View as role" picker inbuildCapabilities. Class-based RBAC providers (e.g.@mastra/auth-workos) read state fromthisinsidegetPermissionsForRole, but the method was being detached to a bare variable before invocation. This caused aTypeError: Cannot read properties of undefined (reading 'options')to be logged on every authenticated request and silently emptied the available roles list. The error was swallowed by a surrounding try/catch so admins saw an empty picker instead of a crash. (#17112) -
Fixed
workflow.parallel()type-checking for steps that declarerequestContextSchema. (#16989) Steps with matching request context now type-check correctly. Steps with mismatched request context still fail with a type error. Fixes #16975. -
Fixed split-brain broker election race in UnixSocketPubSub. When a broker process dies and multiple clients recover concurrently, an exclusive lock file now serializes the election so exactly one process becomes the new broker. (#16955)
-
Fixed a crash that could occur when background execution is enabled for tools with Zod v3 input schemas. (#16915)
Tools with Zod v3, Zod v4, and JSON Schema input definitions now work consistently with background execution.
-
Fixed processor workflow steps so
sendSignalis available when processors inject Agent signals, and updated Observational Memory temporal gap markers to use Agent signals. (#16927) -
Fixed an issue where thread subscriptions could appear idle after a run finished. Subsequent runs now stream promptly, and post-finish signals correctly start from an idle state. (#16928)
-
Fixed agent responses being ordered before the user message that triggered them in long conversations. This prevents duplicate tool calls in the next step. This regression started in 1.35.0. Fixes https://github.com/mastra-ai/mastra/issues/16893. (#16913)
-
Fixed agent crash when
backgroundTasks.enabled: trueis combined with a tool whoseinputSchemauses Zod's.refine()or.superRefine(). On Zod v4, such agents threwCannot overwrite keys on object schemas containing refinementson repeated invocations and became unusable. Tools with refined input schemas now work with background tasks. (#16966) -
UnixSocketPubSub: skip serialization when broker has 0 remote clients, lazily build ServerFrame only when a subscribed client exists, and automatically elect a new broker with resubscription when the active broker disconnects. (#16939)
-
Fixed v1 agent tools to preserve legacy tool result output while still allowing usage data in delegation hooks. (#17070)
-
Fixed thread stream subscriptions so streamed agent responses are saved to memory while still supporting multiple subscribers. (#16982)
-
Gate stored-workspace handlers by author. Previously any authenticated caller within a tenant could list, read, update, or delete another user's workspace. (#16974)
Behavior changes
POST /stored/workspaces— server stampsauthorIdfrom the authenticated caller; any body-providedauthorIdis ignored.GET /stored/workspaces/:id,PATCH /stored/workspaces/:id,DELETE /stored/workspaces/:id— return404 Not foundunless the caller is the owner, an admin (*), or holdsstored-workspaces:<action>[:<id>].GET /stored/workspaces— filters to the caller's own rows plus legacy unowned records; admins still see every row.- Legacy workspaces created before this change (no
authorId) remain accessible to any authenticated caller for backwards compatibility.
Example
// Client POST body — authorId is ignored if sent await fetch('/stored/workspaces', { method: 'POST', body: JSON.stringify({ name: 'My workspace', authorId: 'someone-else' }), }); // Stored row — authorId is stamped from the authenticated caller // { // id: 'my-workspace', // name: 'My workspace', // authorId: 'user_abc123', // from requestContext, NOT from body // ... // }Migration
- Existing rows with
authorId === null/undefinedremain readable/writable by any authenticated caller — no action required for backwards compatibility. - To lock down legacy rows, backfill
authorIddirectly in theworkspacestable with the original creator's id. - For service accounts or tooling that need cross-user access, grant
stored-workspaces:*(or per-idstored-workspaces:<action>:<id>) instead of relying on the legacy unowned bypass. - Admins (callers with
*) continue to see and mutate every row regardless ofauthorId.
The
@mastra/corepatch regeneratespermissions.generated.tsto include theauthandinfrastructureresources that already had routes onmain. -
Agent Builder action routes (
/agent-builder/*) are now registered automatically through the standard server route pipeline. Any adapter built on@mastra/server(Hono, Express, Fastify, Koa, etc.) serves the 15/agent-builder/*endpoints without consumers wiring them manually. (#17085)Example
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); // `/agent-builder/*` routes are now reachable out-of-the-box const actions = await client.getAgentBuilderActions(); const action = client.getAgentBuilderAction('generate-agent'); const { runId } = await action.createRun(); const result = await action.startAsync({ inputData: { prompt: 'Build me an agent' } }, runId);Why
Previously,
AGENT_BUILDER_ROUTESwas a type-only entry in the route registry to keep@mastra/agent-builderout of Cloudflare worker bundles. Consumers had to register the routes themselves to expose Agent Builder functionality. Lazy-loading of@mastra/agent-builderis preserved — handlers still resolve the workflow module on first request via dynamicimport(), so Cloudflare bundles are unaffected.New EE permissions
The following permissions are added to the EE registry. RBAC consumers with strict allowlists must grant these to retain access to builder action routes:
agent-builder:readagent-builder:writeagent-builder:execute
Two legacy stream routes (
STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE,OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE) are now registered through the standard pipeline as well. -
Fixed a race that could cause an immediate auto-resume of a suspended tool call to fail on some storage backends. Resume now succeeds reliably whether the underlying storage is fast or slow. (#17015)
Fixes #16158.
-
Fixed a race in Harness
sendMessagewhere a phantomagent_end: 'complete'event could fire before any chunks arrived. Subscribers, such as apps running on Cloudflare Workers or Durable Objects, will no longer miss text deltas, messages, or tool events when an agent run completes. (#17024)The cause was
AgentThreadStreamRuntime.subscribeToThread'sactiveRunId()returningnullduring the gap betweensendSignalreserving a runId andregisterRunpopulating the stream record, which madewaitForCurrentThreadStreamIdle()exit immediately andsendMessageemit a syntheticagent_end. The subscriber now treats both a reserved-but-not-yet-registered local run and an active remote run as live, matchingsendSignal's own behavior. -
Suppressed noisy gateway fetch errors when models.dev is unreachable. The registry no longer retries or logs errors on network failure since all model data is already bundled at publish time. (#16984)
@mastra/acp@0.2.0
Minor Changes
-
Added programmatic model selection for ACP agents using the
modeloption. (#17010)You can now set the model directly when creating
AcpAgentorcreateACPTool, instead of relying on environment variables.const codeAgent = new AcpAgent({ id: 'code-agent', description: 'ACP-compatible coding agent', command: 'claude', args: ['--acp'], model: 'claude-sonnet-4-20250514', });Discover available models with
getAvailableModels()and change the model at runtime withsetModel(). Invalid model IDs throw a descriptive error listing valid options.
Patch Changes
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/client-js@1.21.0
Minor Changes
-
Client-side tool tracing is now built in. When server-side observability is configured, the SDK automatically measures execution duration and ships it back to the server. To add child spans and structured logs from inside your tool's
execute(input, context)function, use theobservehelper on the execution context: (#16425)execute: async ({ userId }, { observe }) => { observe.log('info', 'fetching user', { userId }); return observe.span('fetch user', () => fetch(`/api/users/${userId}`)); };The
createTool()helper now callsexecute(input, context)so client tools receive the same execution context shape as core tools.
Patch Changes
-
Fixed client-side tools getting stuck in
input-availablestate in React'suseChatmessages. After a client tool finished executing, the React UI never observed a terminaltool-result(ortool-error) chunk for it, so the matchingdynamic-toolpart stayed atstate: 'input-available'indefinitely. The client now emits a synthetic Mastra-shaped terminal chunk into the streamed response right after the client tool resolves or rejects, so the React reducer correctly flips the part tooutput-available(oroutput-error) and renders the tool result. (#16916)Also fixed the client stream parser so final
tool-callchunks are not treated as partial streaming tool calls while preparing client-tool continuation messages. -
Port the
yj/magnificent-marquessfrontend stack ontorain-purpose. (#17105)@mastra/client-js: newToolProviderresource and agetModelPolicyaccessor on the root client. Route types regenerated for the new endpoints.@internal/playground: Agent Builder routes (agents, skills, infrastructure, favorite, library) wired into the router,RoutePermissionGuardandRoleImpersonationProviderapplied to the app shell, new login layout, role-impersonation banner,useRestoreFocushook,StudioIndexRedirecthome, and supporting tweaks across agents, browser view, LLM, and CMS surfaces.
Existing client-tools-on-signals work and the unrouted Agent Builder view/edit pages are preserved.
-
Made optional memory response fields optional in server schemas and generated client types. (#17070)
-
Add
StoredSkill.favorite()andStoredSkill.unfavorite()methods, mirroring the existingStoredAgentfavorite API. Both are idempotent and callPUT/DELETE /api/stored/skills/:id/favorite. (#17101) -
Agent Builder action routes (
/agent-builder/*) are now registered automatically through the standard server route pipeline. Any adapter built on@mastra/server(Hono, Express, Fastify, Koa, etc.) serves the 15/agent-builder/*endpoints without consumers wiring them manually. (#17085)Example
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); // `/agent-builder/*` routes are now reachable out-of-the-box const actions = await client.getAgentBuilderActions(); const action = client.getAgentBuilderAction('generate-agent'); const { runId } = await action.createRun(); const result = await action.startAsync({ inputData: { prompt: 'Build me an agent' } }, runId);Why
Previously,
AGENT_BUILDER_ROUTESwas a type-only entry in the route registry to keep@mastra/agent-builderout of Cloudflare worker bundles. Consumers had to register the routes themselves to expose Agent Builder functionality. Lazy-loading of@mastra/agent-builderis preserved — handlers still resolve the workflow module on first request via dynamicimport(), so Cloudflare bundles are unaffected.New EE permissions
The following permissions are added to the EE registry. RBAC consumers with strict allowlists must grant these to retain access to builder action routes:
agent-builder:readagent-builder:writeagent-builder:execute
Two legacy stream routes (
STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE,OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE) are now registered through the standard pipeline as well. -
Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)
Enable automatic reconnection with
subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }). -
Fixed
clientToolsbeing silently dropped — and never executed — on thread-backed chats. When a chat had athreadId, the ReactuseChathook routed messages through the new agent signals path but did not pass theclientToolsmap into the signal startup flow, so client-side tools were unavailable when the model requested them. (#16540)The signals path now carries
clientToolsand other per-send stream options onsendSignal. When the subscribed stream finishes withtool-calls, the client executes matching local tools with observability support, emits tool result chunks, and posts a continuation with the assistant tool-call messages plus tool-result messages so the run resumes on the same thread with the same per-send options.
@mastra/convex@1.2.0
Minor Changes
-
Convex can now persist channel installations and provider configuration. (#16718)
import { ConvexStore } from '@mastra/convex'; const storage = new ConvexStore({ id: 'app-storage', deploymentUrl: process.env.CONVEX_URL!, adminAuthToken: process.env.CONVEX_ADMIN_KEY!, }); const channels = await storage.getStore('channels'); await channels?.saveInstallation({ id: 'slack-agent-1', platform: 'slack', agentId: 'agent-1', status: 'active', webhookId: 'webhook-1', data: { teamId: 'T123', botUserId: 'U123' }, createdAt: new Date(), updatedAt: new Date(), }); -
Workflow schedules can now be stored in Convex. (#16710)
import { ConvexStore } from '@mastra/convex'; const storage = new ConvexStore({ id: 'app-storage', deploymentUrl: process.env.CONVEX_URL!, adminAuthToken: process.env.CONVEX_ADMIN_KEY!, }); const schedules = await storage.getStore('schedules'); await schedules?.createSchedule({ id: 'daily-summary', target: { type: 'workflow', workflowId: 'summary-workflow' }, cron: '0 9 * * *', status: 'active', nextFireAt: Date.now(), createdAt: Date.now(), updatedAt: Date.now(), });
Patch Changes
- Improved Convex background task reliability with safer lifecycle updates, faster filtering, and smoother upgrades from older storage rows. (#16724)
@mastra/deployer@1.37.0
Patch Changes
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/editor@0.10.0
Minor Changes
-
Ship
EditorAgentBuilderand Agent Builder runtime through the@mastra/editor/eesubpath. (#16948)- Adds
EditorAgentBuilderclass and supporting types under@mastra/editor/ee(dormant unlessMastraEditorConfig.builderis configured). - Wires builder resolution on
MastraEditor:hasEnabledBuilderConfig(),resolveBuilder(),ensureBuilderWorkspaces(), andreconcileBuilderWorkspaces(). - Adds builder defaults plumbing in the agent namespace (
applyBuilderDefaults,BUILDER_BASELINE_DEFAULTSenablingobservationalMemory: trueby default for Builder-created agents). - Adds a defense-in-depth license guard inside
MastraEditor.resolveBuilder()that mirrors the server-startup check inMastraServer.validateAgentBuilderLicense(). Dev environments bypass viaisEEEnabled(); production without a validMASTRA_EE_LICENSEthrows[mastra/auth-ee] Agent Builder is configured but no valid EE license was found. - Bumps the
@mastra/corepeer dependency to>=1.34.0-0 <2.0.0-0to cover the@mastra/core/agent-builder/eeand@mastra/core/auth/eesubpaths consumed by the builder runtime.
Opt-in usage:
import { Mastra } from '@mastra/core'; import { MastraEditor } from '@mastra/editor'; const editor = new MastraEditor({ builder: { enabled: true, configuration: { agent: { models: { default: { provider: 'openai', modelId: 'gpt-4o-mini' } }, }, }, }, }); new Mastra({ storage, editor }); // Later, on demand: const builder = await editor.resolveBuilder(); // `builder` is undefined when the builder is not configured/enabled. // In production it requires a valid MASTRA_EE_LICENSE; dev environments bypass.This is plumbing — no UI consumer ships in this release.
- Adds
Patch Changes
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/evals@1.2.3
Patch Changes
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/express@1.3.24
Patch Changes
-
Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)
registerApiRoute('/stream', { method: 'GET', handler: async c => { const stream = await agent.stream(prompt, { abortSignal: c.req.raw.signal, }); return stream.toTextStreamResponse(); }, }); -
Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)
Enable automatic reconnection with
subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).
@mastra/fastembed@1.1.1
Patch Changes
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/fastify@1.3.24
Patch Changes
-
Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)
registerApiRoute('/stream', { method: 'GET', handler: async c => { const stream = await agent.stream(prompt, { abortSignal: c.req.raw.signal, }); return stream.toTextStreamResponse(); }, });
@mastra/files-sdk@0.2.0
Minor Changes
-
Added @mastra/files-sdk workspace filesystem provider — a unified storage adapter backed by FilesSDK. Supports any FilesSDK adapter (S3, R2, GCS, Azure Blob, Vercel Blob, local filesystem, and more) through a single
FilesSDKFilesystemclass that implements theWorkspaceFilesysteminterface. (#17027)Usage
import { Files } from 'files-sdk'; import { s3 } from 'files-sdk/s3'; import { FilesSDKFilesystem } from '@mastra/files-sdk'; const files = new Files({ adapter: s3({ bucket: 'my-bucket', region: 'us-east-1' }) }); const filesystem = new FilesSDKFilesystem({ files });Swap adapters without changing code — just replace
s3()withr2(),gcs(),azure(),fs(), etc.
Patch Changes
@mastra/hono@1.4.19
Patch Changes
-
Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)
registerApiRoute('/stream', { method: 'GET', handler: async c => { const stream = await agent.stream(prompt, { abortSignal: c.req.raw.signal, }); return stream.toTextStreamResponse(); }, }); -
Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)
Enable automatic reconnection with
subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }). -
Fixed SSE streams so browser clients can connect to long-lived subscriptions before the first stream event is available. (#16540)
@mastra/koa@1.5.7
Patch Changes
-
Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)
registerApiRoute('/stream', { method: 'GET', handler: async c => { const stream = await agent.stream(prompt, { abortSignal: c.req.raw.signal, }); return stream.toTextStreamResponse(); }, }); -
Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)
Enable automatic reconnection with
subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).
@mastra/mcp@1.8.1
Patch Changes
- Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/memory@1.20.0
Minor Changes
- Add per-provider capability files and
automode forobserveAttachments(#16922)- Generate per-provider capability files (e.g.
capabilities/openai.json) alongside the model router registry, sourced from models.dev API - Export
modelSupportsAttachments(modelRouterId)from@mastra/core/llmto check whether a model supports image/file attachments - Extend
observeAttachmentsconfig to accept'auto'in addition toboolean | string[] - When set to
'auto', the observer resolves the model (including function-based models) and checks the capability registry before deciding to forward or drop attachment parts
- Generate per-provider capability files (e.g.
Patch Changes
-
Fixed observational memory so in-progress messages are not removed during observation cleanup. (#16913)
-
Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
-
Fixed processor workflow steps so
sendSignalis available when processors inject Agent signals, and updated Observational Memory temporal gap markers to use Agent signals. (#16927)
@mastra/observability@1.14.0
Minor Changes
- Support ingesting client-side tool telemetry. Spans, logs, and duration metrics captured by the client SDK during tool execution are forwarded through the observability bus to your existing exporters. Client tool durations are reported via the existing
mastra_tool_duration_msmetric with atoolType: 'client'label to distinguish them from server-side tool durations. (#16425)
Patch Changes
- Paused observability uploads after invalid credentials so exporters stop repeatedly sending unauthorized requests. (#16743)
@mastra/playground-ui@30.0.0
Minor Changes
-
Added a Drawer component — a panel that slides in from any edge of the screen with swipe-to-dismiss gestures. (#16958)
The Drawer can be anchored to any of the four screen edges and supports snap points, nested stacking, controlled state, non-modal mode, swipe-to-open areas, and detached triggers.
import { Drawer, DrawerTrigger, DrawerContent, DrawerHeader, DrawerTitle, DrawerDescription, DrawerFooter, DrawerClose, Button, } from '@mastra/playground-ui'; <Drawer side="right"> <DrawerTrigger asChild> <Button>Open</Button> </DrawerTrigger> <DrawerContent> <DrawerHeader> <DrawerTitle>Library</DrawerTitle> <DrawerDescription>A panel that slides in from the right edge.</DrawerDescription> </DrawerHeader> <DrawerFooter> <DrawerClose asChild> <Button variant="outline">Close</Button> </DrawerClose> </DrawerFooter> </DrawerContent> </Drawer>; -
Added a reusable
HoverCardcomponent (HoverCard,HoverCardTrigger,HoverCardContent) built on Base UI. You can now use these exported components to add hover card interactions anywhere in your UI. (#16919)import { HoverCard, HoverCardTrigger, HoverCardContent } from '@mastra/playground-ui'; <HoverCard> <HoverCardTrigger>Weather Agent</HoverCardTrigger> <HoverCardContent>Answers questions about current conditions and forecasts.</HoverCardContent> </HoverCard>;
Patch Changes
-
Migrated the Switch component to Base UI for smoother animations and consistent behavior. No API changes —
checked,defaultChecked,onCheckedChange, anddisabledwork exactly as before. (#16891) -
Improved the Select component by migrating it to Base UI for more reliable positioning and accessibility. The public API (
Select,SelectTrigger,SelectContent,SelectItem,SelectValue,SelectGroup) is unchanged, so no consumer updates are needed. (#16918) -
Added
DataList.RowStatic, a non-interactive row primitive. It renders a row that looks identical to other list rows but does not respond to clicks and shows no hover/focus state — use it alongsideDataList.RowButton/DataList.RowLinkwhen only some rows are clickable (e.g. error or placeholder entries in an otherwise navigable list). (#16970){ rows.map(row => row.href ? ( <DataList.RowLink key={row.id} to={row.href} LinkComponent={Link}> {row.cells} </DataList.RowLink> ) : ( <DataList.RowStatic key={row.id}>{row.cells}</DataList.RowStatic> ), ); } -
Added
DataListprimitives and props for building selection-aware, condensed list rows that match the Traces/Logs visual style. (#16820)New cells on
DataList:IdCell— compact mono cell that truncates long IDs to 8 chars.MonoCell— compact mono + truncate text cell (for input previews, JSON summaries, etc.).DateCell— compact date cell rendering "Today" or "MMM dd".TimeCell— compact mono time cell renderingHH:mm:ss.SSSwith the millisecond portion tinted.SelectCell— labelled checkbox cell with a shift-key range-select handler.TopSelectCell— header version withindeterminatesupport for "select all".TopCells— non-interactive header sibling ofRowButton, for hosting top cells beside a leading select cell.
New props on
DataList.RowButtonandDataList.RowLink:flushLeft— drops the default left margin when wrapped beside a leading cell.colStart— places the row starting at a column line (e.g.colStart={2}to leave column 1 for a leading cell).featured— applies the highlighted background to mark the active row.
New props on existing wrappers:
asonDataList.CellandDataList.TopCell— render the cell as any HTML element (e.g.<label>so the whole cell is clickable).hasLeadingCellonDataList.Top— drops default gap and left padding so a leading cell sits flush, mirroring howRow+RowButtoncompose.
Example — selection row with a checkbox in column 1 and an interactive button spanning the rest:
<DataList.Row> <DataList.SelectCell checked={isSelected} onToggle={shiftKey => toggle(id, shiftKey)} /> <DataList.RowButton flushLeft colStart={2} featured={isFeatured} onClick={onRowClick}> <DataList.IdCell id={item.id} /> <DataList.MonoCell>{item.input}</DataList.MonoCell> </DataList.RowButton> </DataList.Row>Internally the Traces and Logs list views now use the shared primitives — no behavior change for those consumers.
-
Added support for trailing cells in
DataListrows.DataList.RowButtonandDataList.RowLinknow acceptcolEndandflushRight(mirrors of the existingcolStart/flushLeft), so a row can sit beside a non-interactive trailing cell (e.g. an actions column) and stay aligned with the header. Rows wrapped inDataList.Rownow render a full-width separator that extends through the leading and trailing cells.DataList.MonoCellalso gained an optionalheightprop so non-compact lists can use it without forcing compact padding. (#16888)Usage
<DataList.Row> <DataList.RowButton flushLeft flushRight colEnd={-2} onClick={onClick}> {/* main row content */} </DataList.RowButton> <DataList.Cell> {/* trailing actions, e.g. icon buttons */} </DataList.Cell> </DataList.Row> <DataList.MonoCell height="default">long mono text…</DataList.MonoCell> -
Migrated the Label component off Radix UI. It now renders a native
<label>element with the same props and styling —htmlFor,className, and children behave exactly as before. (#16892) -
Fixed Studio Settings page (and other default-height
PageLayoutpages) clipping their content with no scrollbar on viewports shorter than the form. Users on short laptop screens (under ~991px tall) could not reach the Save button under the Mastra Connection headers form, making it impossible to apply changes. Default-heightPageLayoutpages now grow with their content and scroll through the studio chrome wrapper;height="full"pages (Logs, Traces, Metrics, etc.) are unchanged. (#16999) -
Restyled scrollbars across the studio UI to match the design system — thin, themed thumb on a transparent track — replacing the default OS scrollbars that clashed with dark and light surfaces. (#16918)
-
Exported ContextMenu from the package entry so it can be imported alongside other Base UI primitives. (#17062)
-
Design-system additions to support theming: (#17059)
Avatarnow accepts optionalcolorandtextColorprops for per-instance tinting, and falls back to the initial when the image fails to load.Searchbaraccepts an optionalclassNameto let consumers tune layout without forking.TabListaccepts astyleprop and the active-tab indicator now reads from the--tab-indicator-colorCSS variable, letting parents theme the indicator (e.g. per-agent accent color).stringToColornow accepts anynumberfor thelightnessargument and defaults to90instead of75for a lighter fallback chip.- Global
bodyrule enablesfont-smoothing/-webkit-font-smoothingfor crisper UI text.
-
Removed
EntryListand its sub-components (EntryList.Header,EntryList.Entries,EntryList.Entry,EntryList.EntryText,EntryList.Pagination,EntryList.NoMatch,EntryListSkeleton, etc.) from the public API. All in-repo list views have migrated toDataList, which is the recommended replacement. (#16910)Migration:
// Before import { EntryList, EntryListSkeleton } from '@mastra/playground-ui'; <EntryList> <EntryList.Trim> <EntryList.Header columns={columns} /> <EntryList.Entries> {items.map(item => ( <EntryList.Entry key={item.id} columns={columns} entry={item} onClick={…}> {columns.map(col => <EntryList.EntryText key={col.name}>{item[col.name]}</EntryList.EntryText>)} </EntryList.Entry> ))} </EntryList.Entries> </EntryList.Trim> <EntryList.Pagination …/> </EntryList> // After import { DataList } from '@mastra/playground-ui'; <DataList columns={gridColumns}> <DataList.Top> {columns.map(col => <DataList.TopCell key={col.name}>{col.label}</DataList.TopCell>)} </DataList.Top> {items.map(item => ( <DataList.RowButton key={item.id} onClick={…}> {columns.map(col => <DataList.Cell key={col.name}>{item[col.name]}</DataList.Cell>)} </DataList.RowButton> ))} <DataList.Pagination …/> </DataList> -
Improved the Checkbox component by migrating it to Base UI. The public API is unchanged —
checked(including the'indeterminate'value),defaultChecked,onCheckedChange, anddisabledall behave as before. (#16905) -
Fixed MetricsDataTable sticky header and column backgrounds to use surface3 token, matching DashboardCard surface (#16828)
-
Improved the RadioGroup component by migrating it to Base UI. The public API (
RadioGroup,RadioGroupItem,value,onValueChange,disabled) is unchanged. Also fixes the radio indicator sizing/centering — the control now stays square and the inner dot is properly centered. (#16904)
@mastra/posthog@1.0.26
Patch Changes
- Fixed negative PostHog cost calculations when cache tokens are present. (#16876)
@mastra/react@0.4.1
Patch Changes
-
Added support for reasoning-start and reasoning-end stream chunks so reasoning blocks are opened and closed with provider metadata preserved. (#17061)
-
Fixed
clientToolsbeing silently dropped — and never executed — on thread-backed chats. When a chat had athreadId, the ReactuseChathook routed messages through the new agent signals path but did not pass theclientToolsmap into the signal startup flow, so client-side tools were unavailable when the model requested them. (#16540)The signals path now carries
clientToolsand other per-send stream options onsendSignal. When the subscribed stream finishes withtool-calls, the client executes matching local tools with observability support, emits tool result chunks, and posts a continuation with the assistant tool-call messages plus tool-result messages so the run resumes on the same thread with the same per-send options.
@mastra/sentry@1.1.0
Minor Changes
-
Added the
gen_ai.conversation.idspan attribute to the Sentry exporter, sourced frommetadata.threadId. Spans from the same chat thread now group together in Sentry's Conversations view (part of AI Agent Monitoring). (#16925)const agent = mastra.getAgent('chat'); // Pass threadId as before — the Sentry exporter now emits it as gen_ai.conversation.id await agent.generate('Hello', { memory: { thread: 'thread-123', resource: 'user-1' }, });
Patch Changes
@mastra/server@1.37.0
Minor Changes
-
Connecting or disconnecting an agent through a channel (e.g. Slack) now requires the same write permission as editing the underlying stored agent. The check runs on
POST /channels/:platform/connectandPOST /channels/:platform/:agentId/disconnectwhenever the target agent has a record in the stored-agents store. Callers without write access receive a404 Not found, matching the behavior of the stored-agent edit routes. Agents defined in code (no stored-agents record) are unaffected and continue to honor only the route's existing auth requirement. (#16949)The caller must either own the stored agent, have admin bypass, or hold
agents:edit(or a scopedagents:edit:<agentId>).POST /channels/slack/connect Authorization: Bearer <token-with-agents:edit> Content-Type: application/json { "agentId": "support-bot" }POST /channels/slack/support-bot/disconnect Authorization: Bearer <token-with-agents:edit>@mastra/coreis bumped as a patch to ship the regenerated permission definitions that back this check. -
Gate stored-workspace handlers by author. Previously any authenticated caller within a tenant could list, read, update, or delete another user's workspace. (#16974)
Behavior changes
POST /stored/workspaces— server stampsauthorIdfrom the authenticated caller; any body-providedauthorIdis ignored.GET /stored/workspaces/:id,PATCH /stored/workspaces/:id,DELETE /stored/workspaces/:id— return404 Not foundunless the caller is the owner, an admin (*), or holdsstored-workspaces:<action>[:<id>].GET /stored/workspaces— filters to the caller's own rows plus legacy unowned records; admins still see every row.- Legacy workspaces created before this change (no
authorId) remain accessible to any authenticated caller for backwards compatibility.
Example
// Client POST body — authorId is ignored if sent await fetch('/stored/workspaces', { method: 'POST', body: JSON.stringify({ name: 'My workspace', authorId: 'someone-else' }), }); // Stored row — authorId is stamped from the authenticated caller // { // id: 'my-workspace', // name: 'My workspace', // authorId: 'user_abc123', // from requestContext, NOT from body // ... // }Migration
- Existing rows with
authorId === null/undefinedremain readable/writable by any authenticated caller — no action required for backwards compatibility. - To lock down legacy rows, backfill
authorIddirectly in theworkspacestable with the original creator's id. - For service accounts or tooling that need cross-user access, grant
stored-workspaces:*(or per-idstored-workspaces:<action>:<id>) instead of relying on the legacy unowned bypass. - Admins (callers with
*) continue to see and mutate every row regardless ofauthorId.
The
@mastra/corepatch regeneratespermissions.generated.tsto include theauthandinfrastructureresources that already had routes onmain. -
Agent Builder action routes (
/agent-builder/*) are now registered automatically through the standard server route pipeline. Any adapter built on@mastra/server(Hono, Express, Fastify, Koa, etc.) serves the 15/agent-builder/*endpoints without consumers wiring them manually. (#17085)Example
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); // `/agent-builder/*` routes are now reachable out-of-the-box const actions = await client.getAgentBuilderActions(); const action = client.getAgentBuilderAction('generate-agent'); const { runId } = await action.createRun(); const result = await action.startAsync({ inputData: { prompt: 'Build me an agent' } }, runId);Why
Previously,
AGENT_BUILDER_ROUTESwas a type-only entry in the route registry to keep@mastra/agent-builderout of Cloudflare worker bundles. Consumers had to register the routes themselves to expose Agent Builder functionality. Lazy-loading of@mastra/agent-builderis preserved — handlers still resolve the workflow module on first request via dynamicimport(), so Cloudflare bundles are unaffected.New EE permissions
The following permissions are added to the EE registry. RBAC consumers with strict allowlists must grant these to retain access to builder action routes:
agent-builder:readagent-builder:writeagent-builder:execute
Two legacy stream routes (
STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE,OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE) are now registered through the standard pipeline as well.
Patch Changes
-
Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)
registerApiRoute('/stream', { method: 'GET', handler: async c => { const stream = await agent.stream(prompt, { abortSignal: c.req.raw.signal, }); return stream.toTextStreamResponse(); }, }); -
Made optional memory response fields optional in server schemas and generated client types. (#17070)
-
Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)
Enable automatic reconnection with
subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).
@mastra/slack@1.3.0
Minor Changes
-
Improved Slack channel UX: (#16937)
- Flat adapter config —
SlackProvidernow accepts per-adapter options (formatError,streaming,typingStatus,toolDisplay) directly at the top level instead of nesting underadapterConfig. TheadapterConfigfield still works as a deprecated fallback. - Breaking change — the
cards: booleanandformatToolCallfields have been removed fromSlackProviderConfig/SlackAdapterChannelConfig. Migratecards: false→toolDisplay: 'text', andformatToolCall: (info) => msg→toolDisplay: (event) => event.kind === 'result' ? { kind: 'post', message: msg } : undefined. - Opinionated defaults —
SlackProvidernow defaultsstreaming: trueandtoolDisplay: 'grouped'since the grouped "Thinking Steps" widget renders well in Slack's AI Assistant UI. Whenstreaming: falseis explicitly set,toolDisplayfalls back to'cards'since'grouped'requires streaming. Override either per-config to restore other modes. - AI Assistant manifest —
assistant:writeis now part ofDEFAULT_BOT_SCOPESand the generated manifest declares the matchingassistant_viewfeature, so newly generated app manifests support the AI Assistant surface and thread context in DMs. - Slack DM tool-approval routing — clicks on tool-approval cards in Slack DMs now resume the correct Mastra thread.
import { SlackProvider } from '@mastra/slack'; const slack = new SlackProvider({ // Top-level options (preferred): streaming: true, toolDisplay: 'grouped', // or 'cards' | 'text' | 'timeline' | 'hidden' | ToolDisplayFn }); - Flat adapter config —
Patch Changes
@mastra/spanner@1.0.0
Major Changes
-
Added a Google Cloud Spanner storage adapter (
@mastra/spanner) targeting the GoogleSQL dialect. The adapter implements thememory,workflows,scores,backgroundTasks,agents,mcpClients,mcpServers,skills,blobs,promptBlocks,scorerDefinitions,schedules, andobservabilitystorage domains and works with both managed Cloud Spanner instances and the local Spanner emulator. Theschedulesdomain plugs into Mastra's built-inWorkflowSchedulerfor cron-driven workflow triggers, and theobservabilitydomain persists AI tracing spans inmastra_ai_spansto power the Studio traces UI (JSON containment filters compile to per-keyJSON_VALUEequality andEXISTSoverJSON_QUERY_ARRAYsince Spanner has no@>operator). (#15955)import { SpannerStore } from '@mastra/spanner'; const storage = new SpannerStore({ id: 'spanner-storage', projectId: process.env.SPANNER_PROJECT_ID!, instanceId: process.env.SPANNER_INSTANCE_ID!, databaseId: process.env.SPANNER_DATABASE_ID!, });
Patch Changes
@mastra/voice-murf@0.12.1
Patch Changes
- Replaced ky HTTP client with native fetch and built-in retry logic, removing the external dependency (#16960)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.38
- @mastra/arize@1.1.2
- @mastra/arthur@0.2.12
- @mastra/braintrust@1.1.2
- @mastra/datadog@1.2.2
- @mastra/deployer-cloud@1.37.0
- @mastra/deployer-cloudflare@1.1.38
- @mastra/deployer-netlify@1.1.14
- @mastra/deployer-vercel@1.1.32
- @mastra/laminar@1.1.2
- @mastra/langfuse@1.3.2
- @mastra/langsmith@1.2.2
- @mastra/longmemeval@1.0.43
- @mastra/mcp-docs-server@1.1.40
- @mastra/nestjs@0.1.8
- @mastra/opencode@0.0.40
- @mastra/otel-bridge@1.1.2
- @mastra/otel-exporter@1.1.2
- @mastra/temporal@0.1.7
FGA route policy coverage; favorites domain across storage backends
↗This release3 featuresNew capabilities1 enhancementImprovements to existing features4 fixesBug fixesAI-tallied from the release notes@mastra/core@1.35.0
Highlights
FGA Route Policy Coverage + Resolver Hooks (Core/Server/WorkOS)
New FGA route policy coverage controls add guardrails for protected routes, with built-in resource route metadata resolution and resolver hooks so you can centrally map route → {resourceType, resourceId, permission} while still overriding per-route config.
Favorites Storage Domain for Agents/Skills (Core + Storage Adapters)
A new favorites storage domain lets users favorite/unfavorite stored agents and skills, and adds visibility (private|public) and favoriteCount fields so listings can be filtered and ordered by favorite state (e.g., pinFavoritedFor, favoritedOnly).
Favorites Support Across Major Storage Backends
Favorites is implemented across multiple adapters (@mastra/pg, @mastra/libsql, @mastra/mongodb, @mastra/clickhouse, @mastra/cloudflare), enabling consistent favoriting/visibility queries regardless of your chosen persistence layer.
Observational Memory: Accurate Token Estimation for Large File Parts
@mastra/memory improves local token estimation for non-image file parts (notably large PDFs), preventing observational memory from missing thresholds and replaying excessive history when provider token-count endpoints aren’t available.
Reliability Fixes for Workspaces, Workflows, and Route Matching
Multiple fixes improve correctness: workspace PATCH no longer overwrites stored values with undefined, scheduled workflows created via @mastra/core/workflows apply schedules correctly, NestJS adapter prefix matching is stricter, and a circular ESM import crash in @mastra/core/workflows/workflow is resolved.
Breaking Changes
- None called out in this changelog.
Changelog
@mastra/core@1.35.0
Minor Changes
-
Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. (#16485)
For example:
import { MastraFGAWorkos } from '@mastra/auth-workos'; import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee'; import { createRoute } from '@mastra/server/server-adapter'; const routeFGA = { 'GET /billing/:accountId': { resourceType: 'account', resourceIdParam: 'accountId', permission: 'billing:read', }, } satisfies Record<string, FGARouteConfig>; const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`]; const fga: IFGAProvider = new MastraFGAWorkos({ apiKey: process.env.WORKOS_API_KEY!, clientId: process.env.WORKOS_CLIENT_ID!, requireForProtectedRoutes: true, auditProtectedRoutes: 'warn', resolveRouteFGA, validatePermissions: async permissions => { /* validate mappings */ }, }); export const getProjectRoute = createRoute({ method: 'GET', path: '/projects/:projectId', responseType: 'json', requiresAuth: true, fga: { resourceType: 'project', resourceIdParam: 'projectId', permission: 'projects:read', }, handler: async () => { return { project: null }; }, }); -
Added a favorites storage domain that lets users mark stored agents and skills as favorites, plus
visibility('private' | 'public') andfavoriteCountfields on stored agents and skills so callers can list, filter, and order by favorite state. (#16580)Existing rows without
visibilityorfavoriteCountcontinue to work; the new fields and APIs are opt-in.Example
const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'u1', entityType: 'agent', entityId: 'agent-123' }); const favoritedIds = await favorites?.listFavoritedIds({ userId: 'u1', entityType: 'agent' }); // List agents the user has favorited, surfaced first const { agents } = await storage.getStore('agents').list({ pinFavoritedFor: 'u1', favoritedOnly: true, });
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
b661349) -
Fixed a workspace PATCH bug in the inmemory workspace adapter: omitted config fields in a PATCH no longer overwrite previously-persisted values with
undefined. (#16580) -
Fixed active signals so they stay in the correct order when conversations are reloaded from memory. (#16623)
-
Fixed scheduled workflows created from the public @mastra/core/workflows entry point so declared schedules are applied correctly. (#16637)
-
Fixed a crash when importing
@mastra/core/workflows/workflowfrom tests or apps, which previously failed withTypeError: Class extends value undefined is not a constructor or null(caused by a circular ESM import through theworkflowsbarrel). (#16661)
@mastra/auth-workos@1.4.0
Minor Changes
-
Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. (#16485)
For example:
import { MastraFGAWorkos } from '@mastra/auth-workos'; import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee'; import { createRoute } from '@mastra/server/server-adapter'; const routeFGA = { 'GET /billing/:accountId': { resourceType: 'account', resourceIdParam: 'accountId', permission: 'billing:read', }, } satisfies Record<string, FGARouteConfig>; const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`]; const fga: IFGAProvider = new MastraFGAWorkos({ apiKey: process.env.WORKOS_API_KEY!, clientId: process.env.WORKOS_CLIENT_ID!, requireForProtectedRoutes: true, auditProtectedRoutes: 'warn', resolveRouteFGA, validatePermissions: async permissions => { /* validate mappings */ }, }); export const getProjectRoute = createRoute({ method: 'GET', path: '/projects/:projectId', responseType: 'json', requiresAuth: true, fga: { resourceType: 'project', resourceIdParam: 'projectId', permission: 'projects:read', }, handler: async () => { return { project: null }; }, });
Patch Changes
- Fixed WorkOS FGA missing resources so authorization checks deny access instead of surfacing provider errors. (#16485)
@mastra/clickhouse@1.8.0
Minor Changes
-
Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)
Example
const storage = new LibSQLStore({ /* config */ }); const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'user-1', entityType: 'agent', entityId: 'agent-42', });
Patch Changes
- Bumped
@mastra/corepeer dependency floor to>=1.34.0-0so the new@mastra/core/storage/domains/favoritessubpath is available. Older@mastra/coreversions don't ship theFavoritesStoragebase class these adapters now extend. (#16580)
@mastra/cloudflare@1.4.0
Minor Changes
-
Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)
Example
const storage = new LibSQLStore({ /* config */ }); const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'user-1', entityType: 'agent', entityId: 'agent-42', });
Patch Changes
- Bumped
@mastra/corepeer dependency floor to>=1.34.0-0so the new@mastra/core/storage/domains/favoritessubpath is available. Older@mastra/coreversions don't ship theFavoritesStoragebase class these adapters now extend. (#16580)
@mastra/libsql@1.11.0
Minor Changes
-
Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)
Example
const storage = new LibSQLStore({ /* config */ }); const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'user-1', entityType: 'agent', entityId: 'agent-42', });
Patch Changes
-
Bumped
@mastra/corepeer dependency floor to>=1.34.0-0so the new@mastra/core/storage/domains/favoritessubpath is available. Older@mastra/coreversions don't ship theFavoritesStoragebase class these adapters now extend. (#16580) -
Fixed a workspace PATCH bug: omitted config fields in a PATCH no longer overwrite previously-persisted values with
undefined. (#16580)
@mastra/memory@1.18.2
Patch Changes
-
Fixed thread deletion so it also clears thread-scoped observational memory. (#16628)
-
Fixed Observational Memory missing the observation threshold for messages with large file parts. Previously
TokenCounter's local/sync counting path only stringified the file descriptor (type,mimeType,filename) for non-image files, so a 100KB PDF looked like ~8 tokens to OM and the conversation kept replaying the full unobserved history past every reasonable threshold. (#16562)TokenCounternow estimates non-image file part tokens locally from the attachment's byte size and mime type using a per-provider heuristic, mirroring the existing local image-token estimator:- Anthropic PDFs ≈
bytes / 3(floor 1500) - Google PDFs ≈
bytes / 20(floor 258) - OpenAI / unknown PDFs ≈
bytes / 4(floor 500) - Text-ish mime types (
text/*, JSON, XML, YAML) ≈bytes / 4 - Other binary ≈
bytes / 4
URL-only file parts (no body to size) keep the previous descriptor-only local estimate.
countMessagesAsync()continues to prefer provider token-count endpoints for supported providers; this change only improves the local fallback used when no provider endpoint is available.// Before: this PDF counted as ~8 tokens locally regardless of size, so OM never triggered. const part = { type: 'file', data: largePdfBase64, mimeType: 'application/pdf', filename: 'report.pdf', }; // counter.countMessage(message) ≈ 8 // After: estimated locally from byte size on the active provider. // counter.countMessage(message) ≈ tens of thousands of tokens // → OM threshold trips as expected.The internal token-estimate cache version was bumped, which invalidates persisted estimates from older
@mastra/memoryreleases on the next read; entries are recomputed automatically. - Anthropic PDFs ≈
@mastra/mongodb@1.9.0
Minor Changes
-
Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)
Example
const storage = new LibSQLStore({ /* config */ }); const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'user-1', entityType: 'agent', entityId: 'agent-42', });
Patch Changes
- Bumped
@mastra/corepeer dependency floor to>=1.34.0-0so the new@mastra/core/storage/domains/favoritessubpath is available. Older@mastra/coreversions don't ship theFavoritesStoragebase class these adapters now extend. (#16580)
@mastra/nestjs@0.1.6
Patch Changes
- Fixed NestJS route matching so configured prefixes are enforced and partial prefix matches are ignored. For example, a prefix of
/apino longer matches/apiish/agents; only/apiand/api/*are treated as Mastra routes. (#16637)
@mastra/pg@1.11.0
Minor Changes
-
Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)
Example
const storage = new LibSQLStore({ /* config */ }); const favorites = await storage.getStore('favorites'); await favorites?.favorite({ userId: 'user-1', entityType: 'agent', entityId: 'agent-42', });
Patch Changes
-
Fixed a workspace PATCH bug: omitted config fields in a PATCH no longer overwrite previously-persisted values with
undefined. (#16580) -
Bumped
@mastra/corepeer dependency floor to>=1.34.0-0so the new@mastra/core/storage/domains/favoritessubpath is available. Older@mastra/coreversions don't ship theFavoritesStoragebase class these adapters now extend. (#16580)
@mastra/playground-ui@28.0.1
Patch Changes
-
Fixed the Observability traces page on storage providers that don't support the Branches list mode. The page now falls back to Traces mode, hides the Branches option in the filter, and shows a dismissible notice instead of a full-screen error. (#16601)
-
Improved the Observability traces list to make the with-subtraces view more discoverable. (#16643)
- Added: A Level column whose icon distinguishes top-level Trace rows from nested Subtrace rows.
- Added: A tooltip legend on the Level header showing both icons side by side.
- Added: A standalone Show subtraces toggle next to Add Filter — off keeps the default top-level view, on includes subtraces.
- Removed: The List mode entry from the Add Filter menu (now driven by the toggle).
Usage: Open Observability → Traces → switch Show subtraces on. The Level column updates: top-level rows keep the Trace icon, nested rows show the Subtrace (↳) icon. The toggle is hidden automatically when the active storage provider doesn't support subtraces.
-
Removed the "Group traces by thread" option from the Observability traces page. The list now always displays a flat view of traces, without thread-id subheaders. To narrow results to a specific thread, use the Thread ID property in the Add filter menu (open Observability → Traces → Add filter → Thread ID → paste the threadId). (#16633)
-
Polished the property filter dropdown so the chevron icon next to each option keeps the same shape when its side panel opens (#16635)
@mastra/server@1.35.0
Minor Changes
-
Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. (#16485)
For example:
import { MastraFGAWorkos } from '@mastra/auth-workos'; import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee'; import { createRoute } from '@mastra/server/server-adapter'; const routeFGA = { 'GET /billing/:accountId': { resourceType: 'account', resourceIdParam: 'accountId', permission: 'billing:read', }, } satisfies Record<string, FGARouteConfig>; const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`]; const fga: IFGAProvider = new MastraFGAWorkos({ apiKey: process.env.WORKOS_API_KEY!, clientId: process.env.WORKOS_CLIENT_ID!, requireForProtectedRoutes: true, auditProtectedRoutes: 'warn', resolveRouteFGA, validatePermissions: async permissions => { /* validate mappings */ }, }); export const getProjectRoute = createRoute({ method: 'GET', path: '/projects/:projectId', responseType: 'json', requiresAuth: true, fga: { resourceType: 'project', resourceIdParam: 'projectId', permission: 'projects:read', }, handler: async () => { return { project: null }; }, });
Patch Changes
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.36
- @mastra/client-js@1.19.1
- @mastra/deployer@1.35.0
- @mastra/deployer-cloud@1.35.0
- @mastra/deployer-cloudflare@1.1.36
- @mastra/deployer-netlify@1.1.12
- @mastra/deployer-vercel@1.1.30
- @mastra/editor@0.8.1
- @mastra/express@1.3.22
- @mastra/fastify@1.3.22
- @mastra/hono@1.4.17
- @mastra/koa@1.5.5
- @mastra/longmemeval@1.0.41
- @mastra/mcp-docs-server@1.1.38
- @mastra/opencode@0.0.38
- @mastra/react@0.3.3
- @mastra/temporal@0.1.5
@mastra/core@1.34.0
Highlights
ACP Coding Agents as Tools or Subagents (New @mastra/acp)
New @mastra/acp@0.1.0 lets you run ACP-compatible coding agents as Mastra tools (createACPTool) or lightweight subagents (AcpAgent) with incremental streaming, usable anywhere Mastra accepts SubAgent (supervisors, workflows, and the Inngest adapter).
Realtime xAI Voice Provider (New @mastra/voice-xai-realtime)
New @mastra/voice-xai-realtime@0.1.0 adds a realtime voice integration for the xAI Grok Voice Agent API, enabling Agents to connect, stream audio, and run voice turns via XAIRealtimeVoice.
Agent Metadata for Filtering, Cloning, and Dynamic Resolution
Agents can now include optional metadata (static or DynamicArgument) retrievable via agent.getMetadata(), surfaced in /agents responses for client-side filtering, and preserved when cloning agents via the editor (unless overridden).
Enterprise Foundations for Admin Policies + Role-Aware Capabilities
Two new entry points (@mastra/core/agent-builder/ee and @mastra/core/auth/ee) provide building blocks for model allowlists/admin model policies and RBAC enhancements (optional IRBACProvider.getAvailableRoles() / getPermissionsForRole()), with @mastra/auth-workos implementing these methods.
Better File Handling in workspace.read_file (Native Media Parts + Safer Binaries)
read_file now returns supported media (images/PDF) as native model file/image parts (with configurable mediaTypes and maxMediaBytes), keeps text files as text, and avoids dumping unsupported/oversized binaries as base64 by returning metadata unless an explicit encoding is requested.
Observability: Lightweight Trace Listing API + Improved Retry Visibility
New GET /observability/traces/light (plus ClickHouse/DuckDB/server/client support) enables paginated trace lists without span payloads, and background-task retries now appear as separate workflow steps in run history/event traces for easier debugging.
Breaking Changes
- None noted in this changelog.
Changelog
@mastra/core@1.34.0
Minor Changes
-
You can now run ACP-compatible coding agents as Mastra tools or lightweight subagents. ACP agents support incremental response streaming and can be used anywhere Mastra accepts a
SubAgent, including supervisor delegation and workflow steps. (#16423)import { createACPTool, AcpAgent } from '@mastra/acp'; export const codingTool = createACPTool({ id: 'coding-agent', command: 'my-acp-agent', }); export const codingAgent = new AcpAgent({ id: 'coding-agent', command: 'my-acp-agent', });You can also wire an
AcpAgentinto a supervisor or workflow as aSubAgent-compatible implementation:import { Agent } from '@mastra/core/agent'; export const supervisor = new Agent({ name: 'supervisor', instructions: 'Delegate coding tasks to the ACP agent.', model, agents: { codingAgent, }, });Workflows and the Inngest workflow adapter now recognize
SubAgent-compatible implementations when creating agent-backed workflow steps. -
Added optional
metadatato code-defined agents. Pass ametadatarecord tonew Agent({...}), read it back withagent.getMetadata(), and clients can filter on it from the existing/agentsand/agents/:agentIdresponses without encoding the data into IDs or names. (#16603)Metadata supports the same
DynamicArgumentform as other agent config fields, so it can also be resolved per request from the request context.Stored agents loaded via the editor also expose their metadata through
agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent viaeditor.agent.clone()now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.// Static const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'You help customers with support requests.', model: 'openai/gpt-5', metadata: { type: 'support' }, }); supportAgent.getMetadata(); // { type: 'support' } // Dynamic const tenantAgent = new Agent({ id: 'tenant-agent', name: 'Tenant Agent', instructions: 'You help customers with tenant-specific tasks.', model: 'openai/gpt-5', metadata: ({ requestContext }) => ({ type: 'support', tenant: requestContext.get('tenant'), }), }); await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' } -
Added an opt-in foundation for building agent-builder admin policies and role-aware capabilities, available under two new entry points. (#16578)
@mastra/core/agent-builder/eeExposes types, validators, and picker utilities for working with model allowlists and admin model policies on stored agents — for example normalizing model candidates, choosing a default from a configured allowlist, and producing typed errors when a request violates policy.
@mastra/core/auth/eeAdds optional methods on
IRBACProviderfor listing available roles and resolving the permissions for a given role:interface IRBACProvider { // existing methods... getAvailableRoles?(): Promise<RoleDescriptor[]>; getPermissionsForRole?(role: string): Promise<PermissionDescriptor[]>; }Static defaults, an expanded permissions catalog, and a capabilities helper that surfaces
availableRolesto clients when the provider supports it are also included. Providers that do not implement the new methods continue to work unchanged.Also adds a
StorageBrowserRefshape to@mastra/core/storagefor referencing a configured headless browser on stored agents. -
Improved how the workspace
read_filetool returns files to the model. Reads now branch on file type: (#16570)- Media files (default:
image/png,image/jpeg,image/webp,application/pdf) are surfaced as native file/image parts the model can directly view, instead of being dumped as base64 text. Capped at 10 MiB by default so large media don't get base64-encoded into context and persisted in storage — configurable viamaxMediaBytes. - Text-readable files (anything
text/*, common code/config mime types, or unknown extensions) are returned as text content as before. - Unsupported binaries (e.g.
image/pngwhenmediaTypesis disabled,application/zip, oversized media, etc.) now return a short metadata description (path, size, mime type) instead of dumping useless base64 into the conversation. Pass an explicitencodingto opt back into the raw base64/hex dump.
The set of mime types treated as native media parts and the inline size cap are configurable per workspace:
import { Workspace, WORKSPACE_TOOLS } from '@mastra/core/workspace'; const workspace = new Workspace({ filesystem, tools: { [WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: { // Broaden to any image (e.g. SVG, BMP, HEIC) — may fail on some providers mediaTypes: ['image/*', 'application/pdf'], // Raise the inline-media cap to 25 MiB maxMediaBytes: 25 * 1024 * 1024, // Or a custom predicate // mediaTypes: (mime) => mime.startsWith('image/'), // Or disable media parts entirely // mediaTypes: false, }, }, });The default
mediaTypesis intentionally the cross-provider-safe intersection — formats universally supported across Anthropic, OpenAI, and Gemini. - Media files (default:
-
Changed background process output retention. (#16574)
Before: Spawned process handles retained all stdout and stderr, which could grow without bound for long-running background processes.
After: Spawned process handles now retain the latest 1 MiB of stdout and stderr per stream by default. Pass
maxRetainedBytestoprocesses.spawn()to customize the limit, use0to disable retained polling output, or useInfinityto keep the previous retain-all behavior.const handle = await sandbox.processes.spawn('npm run dev', { maxRetainedBytes: 512 * 1024, });Streaming callbacks and reader streams still receive every chunk in full. Handles also expose truncation and dropped-byte counters so callers can detect when
stdout,stderr, orwait()results only include retained output.The built-in
executeCommand()implementation still retains full output by default; passmaxRetainedBytesthere only when you want bounded command results.
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
784ad98) -
Improved background-task observability. (#16590)
Retry attempts now appear as separate workflow steps in run history and event traces, making retry progression easier to debug. No public API changes.
-
Fixed approval resume for tools loaded by processor workflows. (#16365)
-
Fixed durable agents that could drop object-form system instructions when provider options like
cacheControlwere used. These instructions are now preserved so provider-specific options are respected. (#16599) -
Fixed approval resume for tools loaded with ToolSearchProcessor. (#16365)
-
Fixed listMessages perPage=0 behavior in the in-memory store to match other adapters. (#16602)
-
Fixed non-deterministic ordering of cross-thread semantic recall messages. (#16600)
When messages recalled from other threads shared the same timestamp, they were rendered into the system prompt in whatever order the vector query returned them — driven by similarity scores that can vary between equivalent runs. This made any test or evaluation that snapshots prompt output (or hashes the outbound LLM request) flaky.
Recalled cross-thread messages are now sorted by createdAt, then threadId, then role (user → assistant → tool → system), then id before formatting, so the same set of recalled messages always produces the same prompt.
-
Expose
GET /observability/traces/lightand storage support for fetching paginated trace-list rows without span payload data. (#16608) -
GET /api/observability/discovery/metric-namesandGET /api/observability/discovery/metric-label-valuesnow acceptlimitas a URL query parameter without pre-parsing. Previously, passing?limit=10was rejected as a validation error; callers can now use these endpoints directly from HTTP clients, consistent with other query endpoints (e.g. pagination). (#16489) -
Fixed agent signals so standalone agents coordinate thread streams through a shared runtime. (#16581)
@mastra/acp@0.1.0
Minor Changes
-
You can now run ACP-compatible coding agents as Mastra tools or lightweight subagents. ACP agents support incremental response streaming and can be used anywhere Mastra accepts a
SubAgent, including supervisor delegation and workflow steps. (#16423)import { createACPTool, AcpAgent } from '@mastra/acp'; export const codingTool = createACPTool({ id: 'coding-agent', command: 'my-acp-agent', }); export const codingAgent = new AcpAgent({ id: 'coding-agent', command: 'my-acp-agent', });You can also wire an
AcpAgentinto a supervisor or workflow as aSubAgent-compatible implementation:import { Agent } from '@mastra/core/agent'; export const supervisor = new Agent({ name: 'supervisor', instructions: 'Delegate coding tasks to the ACP agent.', model, agents: { codingAgent, }, });Workflows and the Inngest workflow adapter now recognize
SubAgent-compatible implementations when creating agent-backed workflow steps.
Patch Changes
@mastra/auth-workos@1.3.0
Minor Changes
-
Added optional
getAvailableRolesandgetPermissionsForRolemethods to the WorkOS RBAC provider, so consumers can list configured roles and inspect their permissions through@mastra/auth-workos. (#16578)import { MastraRBACWorkos } from '@mastra/auth-workos'; const rbac = new MastraRBACWorkos({ /* config */ }); // List all available roles const roles = await rbac.getAvailableRoles(); // [{ id: 'admin', name: 'Admin' }, { id: 'member', name: 'Member' }] // Get permissions for a specific role const permissions = await rbac.getPermissionsForRole('member'); // ['agents:read', 'workflows:read']
Patch Changes
@mastra/clickhouse@1.7.2
Patch Changes
- Expose
GET /observability/traces/lightand storage support for fetching paginated trace-list rows without span payload data. (#16608)
@mastra/client-js@1.19.0
Minor Changes
-
Added optional
metadatato code-defined agents. Pass ametadatarecord tonew Agent({...}), read it back withagent.getMetadata(), and clients can filter on it from the existing/agentsand/agents/:agentIdresponses without encoding the data into IDs or names. (#16603)Metadata supports the same
DynamicArgumentform as other agent config fields, so it can also be resolved per request from the request context.Stored agents loaded via the editor also expose their metadata through
agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent viaeditor.agent.clone()now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.// Static const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'You help customers with support requests.', model: 'openai/gpt-5', metadata: { type: 'support' }, }); supportAgent.getMetadata(); // { type: 'support' } // Dynamic const tenantAgent = new Agent({ id: 'tenant-agent', name: 'Tenant Agent', instructions: 'You help customers with tenant-specific tasks.', model: 'openai/gpt-5', metadata: ({ requestContext }) => ({ type: 'support', tenant: requestContext.get('tenant'), }), }); await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
Patch Changes
- Expose
GET /observability/traces/lightand storage support for fetching paginated trace-list rows without span payload data. (#16608)
@mastra/docker@0.2.0
Minor Changes
-
Docker sandbox containers now support resource limits and security hardening through Docker HostConfig options. Configure memory, CPU quota, process IDs, capabilities, security options, read-only root filesystems, and tmpfs mounts. (#16577)
const sandbox = new DockerSandbox({ memory: 512 * 1024 * 1024, memorySwap: 512 * 1024 * 1024, cpuQuota: 100_000, pidsLimit: 256, readonlyRootfs: true, capDrop: ['ALL'], securityOpt: ['no-new-privileges:true'], });
Patch Changes
@mastra/duckdb@1.3.2
Patch Changes
- Expose
GET /observability/traces/lightand storage support for fetching paginated trace-list rows without span payload data. (#16608)
@mastra/editor@0.8.0
Minor Changes
-
Added optional
metadatato code-defined agents. Pass ametadatarecord tonew Agent({...}), read it back withagent.getMetadata(), and clients can filter on it from the existing/agentsand/agents/:agentIdresponses without encoding the data into IDs or names. (#16603)Metadata supports the same
DynamicArgumentform as other agent config fields, so it can also be resolved per request from the request context.Stored agents loaded via the editor also expose their metadata through
agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent viaeditor.agent.clone()now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.// Static const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'You help customers with support requests.', model: 'openai/gpt-5', metadata: { type: 'support' }, }); supportAgent.getMetadata(); // { type: 'support' } // Dynamic const tenantAgent = new Agent({ id: 'tenant-agent', name: 'Tenant Agent', instructions: 'You help customers with tenant-specific tasks.', model: 'openai/gpt-5', metadata: ({ requestContext }) => ({ type: 'support', tenant: requestContext.get('tenant'), }), }); await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
Patch Changes
- Fixed stored agent tool overrides so conditional tool configuration no longer recursively calls the forked agent when merging code-defined tools. (#16544)
@mastra/inngest@1.4.1
Patch Changes
-
You can now run ACP-compatible coding agents as Mastra tools or lightweight subagents. ACP agents support incremental response streaming and can be used anywhere Mastra accepts a
SubAgent, including supervisor delegation and workflow steps. (#16423)import { createACPTool, AcpAgent } from '@mastra/acp'; export const codingTool = createACPTool({ id: 'coding-agent', command: 'my-acp-agent', }); export const codingAgent = new AcpAgent({ id: 'coding-agent', command: 'my-acp-agent', });You can also wire an
AcpAgentinto a supervisor or workflow as aSubAgent-compatible implementation:import { Agent } from '@mastra/core/agent'; export const supervisor = new Agent({ name: 'supervisor', instructions: 'Delegate coding tasks to the ACP agent.', model, agents: { codingAgent, }, });Workflows and the Inngest workflow adapter now recognize
SubAgent-compatible implementations when creating agent-backed workflow steps.
@mastra/memory@1.18.1
Patch Changes
-
Added a public escape hatch so callers can supply an authoritative token estimate for file parts whose binary payload has been stripped before persistence (for example, files uploaded to cloud storage with a hidden reference token left in
dataand re-hydrated by LLM middleware before inference). (#16565)For those pipelines TokenCounter has no on-device file size to measure, so Observational Memory thresholds and context budgets undercount large attachments. Callers can now stamp an estimate directly on the part:
part.providerMetadata = { mastra: { tokenEstimate: { v: 0, source: 'client', key: 'client', tokens: 25_000 }, }, };When present, TokenCounter returns those tokens from both the sync and async paths and skips provider fetches. Invalid entries (NaN, negative, non-numeric) fall through to the default estimator. Parts without a client estimate are unaffected.
@mastra/nestjs@0.1.5
Patch Changes
-
Fixed
@mastra/nestjscoercing query parameter values to booleans,null, numbers, and parsed JSON objects/arrays before route schema validation. A route declaringqueryParamSchema: z.object({ filter: z.string() })could reject a valid request like?filter={"a":1}because the adapter had already turned the string into an object. NestJS now forwards query values as the raw strings (or string arrays) the HTTP layer delivered — matching@mastra/hono,@mastra/express,@mastra/fastify, and@mastra/koa. (#16268)Routes that want type coercion should opt in via the schema, e.g.
z.coerce.boolean(),z.coerce.number(), or a JSON preprocessor on the field.Fixes #16114.
@mastra/playground-ui@28.0.0
Minor Changes
- Added a List mode filter to the Observability traces page for switching between Traces and Branches mode, and changed the default to Traces mode. Previously, the page opened in Branches mode; now it opens in Traces mode, and users can switch modes via the new "List mode" property in the Add filter menu. For example: open Observability → Traces (now defaults to Traces) → Add filter → List mode → pick Branches or Traces. (#16587)
Patch Changes
-
Removed the inset top gloss from the
shadow-dialogtoken. The gloss read as a faint highlight band along the top edge of dropdown menus, popovers, selects, comboboxes, dialogs, tooltips, side dialogs and the main app container in dark mode. The token now applies a drop-shadow only and is consistent across light and dark themes. (#16544) -
Restyled
MainSidebarand swapped sidebar icons to the new Figma design system set. (#16544)- Section titles are larger and medium-weight (
text-ui-sm,font-medium), lowercase, muted — replacing the previous uppercase + wide-tracking treatment. Underline divider beneath the title removed in both expanded and collapsed states. Active indicator bar on the left edge removed. - Nav items render flush: icons align horizontally with the section title, hover/active state now uses theme-aware sidebar surface tokens without item borders or shadows. The legacy
indentoption is still accepted but no longer changes layout. - New sidebar icons:
WorkspacesIcon,RequestContextIcon,ScorersIcon,DatasetsIcon,ExperimentsIcon,MetricsIcon. Existing iconsAgentIcon,PromptIcon,WorkflowIcon,ProcessorIcon,McpServerIcon,ToolsIcon,LogsIcon,TraceIconupdated to match the Figma artwork. All icons acceptReact.SVGProps<SVGSVGElement>and inherit color viacurrentColor.
- Section titles are larger and medium-weight (
-
Improved Studio main content framing, persistent page breadcrumbs, accessible page headings, panel layering, and theme-aware navigation/card contrast. (#16544)
-
Fixed a React/React DOM version mismatch that prevented Storybook from rendering in
@mastra/playground-ui. (#16529)
@mastra/server@1.34.0
Minor Changes
-
Added optional
metadatato code-defined agents. Pass ametadatarecord tonew Agent({...}), read it back withagent.getMetadata(), and clients can filter on it from the existing/agentsand/agents/:agentIdresponses without encoding the data into IDs or names. (#16603)Metadata supports the same
DynamicArgumentform as other agent config fields, so it can also be resolved per request from the request context.Stored agents loaded via the editor also expose their metadata through
agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent viaeditor.agent.clone()now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.// Static const supportAgent = new Agent({ id: 'support-agent', name: 'Support Agent', instructions: 'You help customers with support requests.', model: 'openai/gpt-5', metadata: { type: 'support' }, }); supportAgent.getMetadata(); // { type: 'support' } // Dynamic const tenantAgent = new Agent({ id: 'tenant-agent', name: 'Tenant Agent', instructions: 'You help customers with tenant-specific tasks.', model: 'openai/gpt-5', metadata: ({ requestContext }) => ({ type: 'support', tenant: requestContext.get('tenant'), }), }); await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
Patch Changes
-
Expose
GET /observability/traces/lightand storage support for fetching paginated trace-list rows without span payload data. (#16608) -
GET /api/observability/discovery/metric-namesandGET /api/observability/discovery/metric-label-valuesnow acceptlimitas a URL query parameter without pre-parsing. Previously, passing?limit=10was rejected as a validation error; callers can now use these endpoints directly from HTTP clients, consistent with other query endpoints (e.g. pagination). (#16489) -
Fixed agent signal wakeups so idle runs receive request context values while preserving server-owned resource identifiers. (#16557)
@mastra/voice-xai-realtime@0.1.0
Minor Changes
-
Added
@mastra/voice-xai-realtime, a realtime voice provider for the xAI Grok Voice Agent API. (#16507)Use
XAIRealtimeVoicewith Mastra'sAgentvoice primitive to connect, stream audio, and run xAI voice turns:import { Agent } from '@mastra/core/agent'; import { XAIRealtimeVoice } from '@mastra/voice-xai-realtime'; const voice = new XAIRealtimeVoice({ apiKey: process.env.XAI_API_KEY, model: 'grok-voice-think-fast-1.0', speaker: 'eve', turnDetection: { type: 'server_vad' }, }); const agent = new Agent({ id: 'voice-agent', name: 'Voice Agent', instructions: 'You are a helpful voice assistant.', model: 'xai/grok-4.3', voice, }); await agent.voice.connect(); agent.voice.on('speaker', audioStream => playAudio(audioStream)); await agent.voice.speak('How can I help you today?'); await agent.voice.send(microphoneStream);
Patch Changes
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.35
- @mastra/deployer@1.34.0
- @mastra/deployer-cloud@1.34.0
- @mastra/deployer-cloudflare@1.1.35
- @mastra/deployer-netlify@1.1.11
- @mastra/deployer-vercel@1.1.29
- @mastra/express@1.3.21
- @mastra/fastify@1.3.21
- @mastra/hono@1.4.16
- @mastra/koa@1.5.4
- @mastra/longmemeval@1.0.40
- @mastra/mcp-docs-server@1.1.37
- @mastra/opencode@0.0.37
- @mastra/react@0.3.2
- @mastra/temporal@0.1.4
@mastra/core@1.33.0
Highlights
Push-capable PubSub + HTTP workflow event ingestion
Mastra now supports push delivery for workflow events: PubSubs declare supportedModes, Mastra.handleWorkflowEvent(event) is the unified entry point, and servers expose POST /api/workflows/events so brokers like GCP Pub/Sub push/SNS/EventBridge can deliver events over HTTP (no pull worker required).
Response caching for identical LLM steps (ResponseCache)
A new opt-in ResponseCache input processor can skip model calls by replaying cached Responses per step in agent loops, with per-request overrides (key, scope, bust) via RequestContext. Adds InMemoryServerCache for local dev and per-entry TTL support in the cache interface (also implemented by @mastra/redis).
Agent Signals for “send while streaming” threaded chat
Agents can now accept contextual signals mid-run (agent.sendSignal) and clients can follow thread activity via agent.subscribeToThread, enabling reliable follow-ups while responses stream. This is supported end-to-end across @mastra/server, @mastra/client-js, and @mastra/react (stable signal IDs, dedupe, stop/abort behavior, and thread subscriptions).
Azure OpenAI Responses API (v1 routing) + WebSocket streaming transport
AzureOpenAIGateway now supports the Azure OpenAI Responses API with v1 routing controls (useResponsesAPI: true) and adds WebSocket transport for streaming agent/tool loops, improving streaming reliability and supporting real-time tool loop execution over WS.
New storage and integration options: Aurora DSQL + Bright Data tools
Introduces @mastra/dsql (Amazon Aurora DSQL storage with IAM auth) for persisting threads/workflows/observability, and @mastra/brightdata with brightdata-search/brightdata-fetch tools to bypass bot detection/CAPTCHAs for SERP + page fetching.
Observability upgrades: MODEL_INFERENCE spans, eval score unification, and OTEL logs
Adds MODEL_INFERENCE spans under MODEL_STEP to isolate pure provider latency, and routes Mastra Eval/scorer results through the unified score pipeline (and forwards them to multiple exporters). OTEL integrations now support log forwarding/export (@mastra/otel-bridge, @mastra/otel-exporter), and SensitiveDataFilter is applied by default to prevent secret leakage.
Breaking Changes
@mastra/inngestnow requiresinngest@^4(and Inngest Dev Serverv1.18.0+); remove@inngest/realtimeand import realtime helpers frominngest/realtime.@mastra/client-js: paginated list methods now requireorderBy: { field, direction }(flatsortDirectionremoved from affected param types);AgentBuilder.stream(...)now requiresrunId;createStoredSkill(...)now requiresdescription.@mastra/playground-ui:ScrollAreanow fades edges by default (opt out withmask={false}); removed exportsThreads,ThreadList,ThreadItem,ThreadLink,ThreadDeleteButton(no longer available downstream).
Changelog
@mastra/core@1.33.0
Minor Changes
-
Added
processLLMRequest, a processor hook that runs after messages are converted to the provider-facing prompt and before the model request is sent. The hook lets processors make temporary, model-aware prompt changes without mutating stored message history, memory, UI history, or later provider calls. (#16176)ProviderHistoryCompatnow uses this hook to prevent reasoning-history incompatibilities when switching providers. It strips reasoning parts from Cerebras-bound prompts that would otherwise be sent as rejectedreasoning_content, and strips non-Anthropic reasoning from Anthropic-bound prompts while preserving Anthropic-native thinking blocks. -
Improved Harness support for Agent thread signals. (#16231)
Harness thread subscriptions now own stream processing for followed runs, echo user-message signal data with stable IDs, and support idle signal starts without delaying optimistic rendering.
const { id, accepted } = harness.sendSignal({ type: 'user-message', contents: 'Follow up while the agent is still streaming', }); await accepted; -
Added target-aware tool payload transforms for display streams and transcript messages. Tool authors can transform tool input, output, errors, approval payloads, and suspension payloads without changing raw runtime behavior or toModelOutput. See https://github.com/mastra-ai/mastra/issues/16054. (#16103)
Use
transformon tools, agents, Mastra, or individual generation calls to configure these payload transforms. Runtime callers using the previoustoolPayloadProjectionshape continue to be normalized for compatibility.const lookupCustomer = createTool({ execute: async ({ customerId, internalPath }) => lookupCustomerRecord(customerId, internalPath), transform: { display: { input: ({ input }) => ({ customerId: input?.customerId }), output: ({ output }) => ({ displayName: output?.displayName }), }, transcript: { input: ({ input }) => ({ customerId: input?.customerId }), output: ({ output }) => ({ displayName: output?.displayName }), }, }, }); -
Added Azure OpenAI Responses API and v1 routing controls. (#16246)
Use
useResponsesAPI: trueto resolve Azure deployments through the Responses API with the Azure v1 route by default:new AzureOpenAIGateway({ resourceName: 'my-openai-resource', apiKey: process.env.AZURE_API_KEY!, useResponsesAPI: true, deployments: ['my-gpt-5-4-deployment'], });When
useDeploymentBasedUrls: falseis used directly, the gateway now defaultsapiVersionto"v1"to match the AI SDK Azure provider's v1 URL route. PassingapiVersion: "v1"by itself keeps the existing deployment-based URL default for compatibility. -
Mastra Eval results are now emitted once through the unified observability score pipeline. (#16185)
-
Added Azure OpenAI Responses WebSocket transport support for streaming agent and tool loops. (#16246)
Configure the Azure gateway with
useResponsesAPI: true, then opt into WebSocket streaming per request:const stream = await agent.stream('Review this task', { providerOptions: { azure: { transport: 'websocket', websocket: { closeOnFinish: false }, }, }, });Responses WebSocket streams now preserve transport handles through agent loops, reuse explicit API-key router connections safely, clean up cancelled streams, and reject overlapping
previous_response_idcontinuations on the same connection. -
Added
preserveModelOutputtoToolCallFilterso filtered tool history can keep compact model-facing output without raw tool args or results. (#16060)import { ToolCallFilter } from '@mastra/core/processors'; const filter = new ToolCallFilter({ preserveModelOutput: true, }); -
Added a SubAgent interface for custom supervisor subagents. (#16359)
-
Added
ResponseCacheinput processor (#16283)Cache identical LLM steps to skip the model call and replay a previously cached response. Useful for prompt templates, suggested-prompt buttons, agentic search re-asks, or guardrail LLMs that classify the same input over and over.
Caching is opt-in: register
ResponseCacheexplicitly oninputProcessors. There is no agent-level option — this keeps the surface small while we collect feedback on the processor API. Per-call overrides flow throughRequestContext.import { Agent } from '@mastra/core/agent'; import { InMemoryServerCache } from '@mastra/core/cache'; import { ResponseCache } from '@mastra/core/processors'; const cache = new InMemoryServerCache(); const agent = new Agent({ name: 'Search Agent', instructions: 'You answer questions concisely.', model: 'openai/gpt-5', inputProcessors: [new ResponseCache({ cache, ttl: 600 })], }); // First call: cache miss → LLM call await agent.generate('What is the capital of France?'); // Second identical call: cache hit → no LLM call await agent.generate('What is the capital of France?');Per-call overrides via
RequestContext:import { ResponseCache } from '@mastra/core/processors'; import { RequestContext } from '@mastra/core/request-context'; // Force a fresh call but still update the cache. await agent.stream(prompt, { requestContext: ResponseCache.context({ bust: true }), }); // Or merge into an existing context. const ctx = new RequestContext(); ResponseCache.applyContext(ctx, { key: 'custom-key' }); await agent.stream(prompt, { requestContext: ctx });Three fields are overridable per call:
key,scope,bust.cache,ttl, andagentIdstay on the constructor.A
keyfunction receives{ agentId, scope, model, prompt, stepNumber }and returns a string (orPromise<string>):await agent.stream(prompt, { requestContext: ResponseCache.context({ key: ({ model, prompt }) => `qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`, }), });The cache key is derived from the resolved prompt Mastra is about to send to the model — i.e. after memory loading and earlier input processors have run — so cached entries are tenant-isolated and don't leak context across users with shared prompts but different memory state. Each step in an agentic tool loop is independently cached. By default, the cache scope falls back to
MASTRA_RESOURCE_ID_KEYfrom the request context for automatic per-user isolation. Failed runs (errors, tripwire activations) are not cached. See Response caching for details.Also adds:
InMemoryServerCache(in@mastra/core/cache) for local development.ResponseCacheaccepts anyMastraServerCachedirectly — useRedisCachefrom@mastra/redisfor production.MastraServerCache.set()now accepts an optionalttlMsargument so implementations can override the configured default TTL on a per-entry basis.InMemoryServerCacheandRedisCache(in@mastra/redis) both honor this.- New paired processor hooks
processLLMRequestandprocessLLMResponse.ProcessLLMRequestResultmay return{ response }to short-circuit the LLM call with a cached payload.
-
Added workflow state reader helpers to inspect persisted workflow runs and recover suspended or long-running workflows. (#16091)
The reader exposes suspended steps, resume labels, step payloads, and step outputs from the public WorkflowState returned by workflow.getWorkflowRunById(), and WorkflowState step results now reflect foreach array entries.
-
Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)
Call
agent.sendSignal()to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. UseifActive.behaviorandifIdle.behaviorto deliver, persist, discard, or wake from a signal.Use
agent.subscribeToThread()to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.const subscription = await agent.subscribeToThread({ resourceId, threadId }); void (async () => { for await (const part of subscription.stream) { if (part.type === 'finish') { subscription.unsubscribe(); } } })(); agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId }); -
Add metadata filtering support to semantic recall. (#9256)
-
Fixed Azure and OpenAI Responses item handling so multi-step reasoning and tool-call histories round-trip correctly without item ID collisions. (#16246)
Added provider-neutral response item helpers to
@mastra/core/agent/message-list. Existing in-memory message cache entries are regenerated after upgrade. -
Improved foreach workflow execution to keep concurrency slots filled as iterations finish. (#12860)
-
Added processor
sendSignalsupport and routed built-in system reminders through signal messages. (#16438) -
Added structured drop event types and an
onDroppedEventhook so exporters and bridge integrations can observe events dropped by the observability pipeline. (#16111) -
Added stable IDs to Harness task items plus
task_updateandtask_completefor updating or completing one tracked task by ID. Task tools now return structured task snapshots, andtask_checkreturnssummaryandincompleteTasksfields so agents and UIs can restore and verify task state without parsing text. (#16254)Harness also exports
TaskItemSnapshot,assignTaskIds, andharness.restoreDisplayTasks()for UI history replay, serializes task reads and mutations against the latest task state snapshot, and returns task-tool errors inside forked subagents so sidecar work cannot mutate parent task state. -
Added new
MODEL_INFERENCEspan type underMODEL_STEP, covering only the model provider call. Use it to measure model latency separately from input/output processors and tool executions. (#16267) -
Added experimental support for using remote A2A agents as Mastra subagents. (#16348)
What changed
- Mastra agents can register remote A2A endpoints through
A2AAgentand delegate to them like other subagents. - Remote A2A subagents support
generate,resumeGenerate,stream, andresumeStreamso parent agents can use them in normal subagent flows. - Agent Cards can be cached and verified with pluggable verification hooks before remote execution begins.
- Browser environments can import shared A2A types and errors from
@mastra/core/a2a/client.
Example
import { Agent } from '@mastra/core/agent'; import { A2AAgent } from '@mastra/core/a2a'; const agent = new Agent({ name: 'Support Agent', instructions: 'Use the remote billing specialist for billing questions.', model: 'openai/gpt-4o-mini', agents: { billingSpecialist: new A2AAgent({ url: 'https://billing.example.com/.well-known/agent-card.json', }), }, }); const result = await agent.generate('Can you check the latest invoice status?');Why This lets Mastra agents compose with remote A2A agents without exposing those integrations as plain tools or depending directly on the client SDK.
- Mastra agents can register remote A2A endpoints through
-
Worker review fixes: (#16309)
- Step-execution endpoint (
POST /workflows/:id/runs/:runId/steps/execute) is now gated by Mastra's standardrequiresAuth: true+authenticateTokenpipeline rather than a parallel "worker secret" body field. The previously introducedworkerSecretconfig knob andMASTRA_WORKER_SECRETenv var have been removed (they were never released). To gate the endpoint on a standalone-worker deployment, configure an auth provider on the server'sMastrainstance — without one the framework currently treatsrequiresAuth: trueas a no-op for this route. HttpRemoteStrategynow sends credentials as a normalAuthorization: Bearer <token>header. The token comes from the newMASTRA_WORKER_AUTH_TOKENenv var or an explicitauthconstructor option.- Honor the caller's
abortSignalinHttpRemoteStrategyby combining it with the per-request timeout viaAbortSignal.any(with a manual fallback for runtimes that don't expose it). - Implement comma-separated name filtering for the
MASTRA_WORKERSenv var.MASTRA_WORKERS=scheduler,backgroundTasksnow boots only those named workers;MASTRA_WORKERS=falsestill disables all workers. - Restore
Mastra.startEventEngine/stopEventEngineas@deprecatedaliases for the renamedstartWorkers/stopWorkers. BackgroundTaskWorkernow subscribes to PubSub instart()instead ofinit(), matching the lifecycle of the other workers and makingisRunningaccurately reflect subscription state.RedisStreamsPubSubadds amaxDeliveryAttemptsoption (default 5) that drops events after the configured number of failed deliveries instead of redelivering forever, and replaces emptycatch {}blocks withlogger.warn/logger.debugcalls.RedisStreamsPubSub.unsubscribe(topic, cb)now honors the topic argument so the same callback can be subscribed to multiple topics independently.PullTransportguards the async router callback against unhandled promise rejections by attaching a.catchthat nacks the message.- Drop the dead
MASTRA_WORKER_NAMEenv var injection in the CLI worker spawn (the bundle entrypoint already passes the worker name directly). - Add a real cross-process e2e auth suite
(
pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong token, missing token, anonymous direct hits, and the no-auth-provider pin-down behavior. - Step-execution route now has a response schema, satisfying
schema-consistency.test.ts. - Internal type cleanups (drop several
as anycasts in worker strategies andBackgroundTaskWorker). RedisStreamsPubSub.maxDeliveryAttemptsnow rejects negative / NaN values at construction.0still means "no cap" for back-compat but emits a one-time warning; passInfinityto disable the cap explicitly.PullTransportaccepts a logger and uses it for unhandled router-callback rejections instead ofconsole.error.BackgroundTaskWorker.start()now throws ifinit()was not called, matching the contract of the other workers.- Cross-process integration tests now spawn a single user-owned project
(
test-fixtures/cli-project/src/mastra/index.ts) through two generic entries that mirror whatBuildBundlerandWorkerBundleremit. The previous one-offserver.entry.ts/worker.entry.ts/scheduler.entry.ts/background.entry.tsfiles have been deleted — they implied users hand-roll entry files, which they don't. Worker role is selected viaMASTRA_WORKERSexactly as in production.
Push-capable PubSub:
- The
PubSubabstract class now declares asupportedModesgetter (defaulting to['pull']for backward compatibility) so consumers can tell whether a broker delivers events through a pull loop, an in-process push, or an out-of-process HTTP push.EventEmitterPubSubreports['pull', 'push'](EventEmitter dispatches synchronously and works for either path),@mastra/redis-streamsreports['pull']. Mastranow exposes a publichandleWorkflowEvent(event)method backed by a sharedWorkflowEventProcessor. It is the single entry point used by the existing pull-modeOrchestrationWorker, by in-process push pubsubs (auto-wired duringstartWorkers()), and by the newPOST /api/workflows/eventsroute which lets push-mode brokers (GCP Pub/Sub push, SNS, EventBridge) deliver events over HTTP.- When the configured pubsub does not support
'pull', Mastra automatically skips creating anOrchestrationWorkerandOrchestrationWorker.init()throws a clear error if it is constructed against a push-only pubsub. WorkflowEventProcessorgains ahandle(event)method that returns a structured{ ok, retry }result. The originalprocess(event, ack?)method is preserved as a thin wrapper for back-compat.
Public-API example for a push-capable PubSub:
import { Mastra } from '@mastra/core/mastra'; import { EventEmitterPubSub } from '@mastra/core/pubsub'; const mastra = new Mastra({ // A push-capable broker (GCP Pub/Sub push, SNS, EventEmitter, …). // EventEmitterPubSub reports supportedModes = ['pull', 'push']. pubsub: new EventEmitterPubSub(), workflows: { myWorkflow }, }); // In-process push pubsubs are auto-wired here. For out-of-process // push (e.g. HTTP webhook from a cloud broker), POST the event to // /api/workflows/events on your Mastra server instead. await mastra.startWorkers(); // Direct invocation (e.g. inside an HTTP handler that bridges from a // cloud broker's push delivery): await mastra.handleWorkflowEvent({ id: 'evt-1', type: 'workflow.start', runId: 'run-1', createdAt: new Date(), data: { workflowId: 'myWorkflow', inputData: { name: 'world' } }, });CI follow-ups:
Mastraonly auto-registersSchedulerWorkerwhen storage is configured. Without storage the worker would crash on startup (deps.storage.getStoreon undefined); the scheduler now silently no-ops in that case, matching the pre-worker scheduler behavior.SchedulerWorker.initdefensively logs and returns when called without storage instead of throwing a TypeError.RECEIVE_WORKFLOW_EVENT_ROUTE(POST /workflows/events)createdAtis now a plainz.string()on the wire and the handler converts it to aDate(validating "Invalid Date" -> 400). The previousunion(...).transform().refine()schema couldn't be exercised by the shared adapter test suite because the generator didn't unwrap Zod 4'sZodPipe._test-utils/route-test-utilsrecognizes Zod 4'snumber_formatcheck (used forint()/safeint()), andgenerateContextualValuenow produces a valid ISO timestamp forcreatedAt/updatedAtfields.
- Step-execution endpoint (
-
Configured workflow step scorers now automatically emit scores through the observability score pipeline. (#16371)
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
ac47842) -
Fixed two bugs that affected scheduled workflows. (#16510)
Scheduled workflow with mismatched
idcould not be dispatched (#16471)When a workflow's
iddiffered from the key it was registered under, the scheduler published events the event processor could not resolve, causing the run to fail with "Workflow not found." The dispatcher now looks up workflows by.idfirst (falling back to the registration key), so the following now works as expected:const workflow = createWorkflow({ id: 'daily-report', schedule: { cron: '0 9 * * *' } }); new Mastra({ workflows: { dailyReport: workflow } });Deleted scheduled workflows caused infinite event redelivery
Removing a scheduled workflow from code used to leave its schedule row in storage. The scheduler kept firing for the missing workflow and the event processor kept telling the transport to redeliver the event forever. On boot, Mastra now cleans up declarative schedule rows (those it wrote itself, prefixed with
wf_) for workflows that are no longer registered. User-created schedules made via the schedules API are left untouched. The event processor also handles in-flight events for missing workflows by emitting a single terminalworkflow.failinstead of looping. -
Fixed processor-combined workflows to use the agent logger so processor step failures are logged through the configured logger instead of console output. (#16369)
-
Fixed plan approval so accepting a plan can switch modes after the waiting plan tool resolves, clears stale abort state before starting the approved goal, and injects the goal trigger directly instead of queueing a follow-up. (#16340)
-
Fixed guardrail processor schemas so structured output works with Anthropic models. (#15739)
-
Propagate cache metrics (
cachedInputTokens,cacheCreationInputTokens) through harness token usage. The step-finish handler now extractscachedInputTokensfrom AI SDK usage and propagates it throughusage_updateevents,getTokenUsage(), display state, and thread metadata persistence. (#14746) -
Fixed workflow resume to reuse suspended step input payloads when previous step output is stale. Fixes https://github.com/mastra-ai/mastra/issues/16051. (#16066)
-
A tool's
toModelOutputresult is now computed before thetool-resultchunk is emitted, so it travels with the chunk onproviderMetadata.mastra.modelOutput. PreviouslytoModelOutputonly ran later when the message list was updated, meaning live stream consumers couldn't see it without re-running the tool. (#16457)The harness now forwards that
providerMetadataontool_resultcontent (both streaming and replayed history) and ontool_endevents, so UIs can render rich tool output (e.g. screenshot images) inline.harness.on('tool_end', event => { const modelOutput = event.providerMetadata?.mastra?.modelOutput; // e.g. { type: 'content', value: [{ type: 'image-data', mediaType: 'image/png', data: '...' }] } }); -
Fixed ToolCallFilter so assistant messages with top-level text are preserved when tool parts are removed. (#16077)
-
Fixed AI SDK v6 dynamic tool UI messages so MessageList preserves tool state during history normalization. Fixes #16046. (#16062)
-
Fixed tool result media content not reaching the model. Tools using
toModelOutputto return images or files (e.g. screenshot tools) now work correctly with all AI SDK providers (Anthropic, OpenAI, Google). (#16449) -
Added A2A Agent Card signing config types for server configuration. (#16207)
Example
const mastra = new Mastra({ server: { a2a: { agentCardSigning: { privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!, protectedHeader: { alg: 'ES256', kid: 'agent-card-key', }, }, }, }, }); -
Fixed a Harness issue where reopening a thread could apply the wrong model for (#16278) the saved mode. Threads now reopen with the correct model for that mode, including when no explicit per-mode model was selected.
-
Fixed Workflow runs no longer fail to persist when request context contains non-serializable values (for example functions, circular references, or platform proxy objects). This prevents errors when saving workflow snapshots and scorer results. See #12301. (#12573)
-
Added
/goalto Mastra Code, a persistent autonomous task loop similar to the goal modes in Codex and Hermes Agent. (#16065)A user can start a goal with
/goal <objective>. Mastra Code saves that objective to the current thread, runs the normal assistant turn, then asks a separate judge model whether the goal isdone, shouldcontinue, or iswaitingon an explicit user checkpoint. When the judge says to continue, Mastra Code feeds the judge feedback back into the conversation and keeps working until the goal is complete, paused, cleared, or reaches the configured attempt limit.Use
/judgeto configure the default judge model and max attempts used by future goals.Approved plans can be selected as a goal from the inline plan approval UI, slash commands can opt into
/goal/<command>with top-levelgoal: true, and skills can opt into goal commands withmetadata.goal: true./goalobjectives can also span multiple lines. -
Fixed nested workflow runs retaining abort listeners after completion. (#16212)
-
- Fixed
foreachstate update andforeachbail in evented workflows (#16436) - Fixed suspend-resume in evented-workflows legacy stream.
- Fixed
-
Hide internal spans from Mastra-owned processors in exported traces. The
PROCESSOR_RUNspan still appears, but the agent, model, and tool spans that processors create under the hood are now marked internal and filtered out by default. (#16424)Affects the moderation, PII detector, language detector, prompt-injection detector, system-prompt scrubber, and structured-output processors.
To inspect the internals (e.g. for debugging a Mastra-owned processor's behavior), set
includeInternalSpans: trueon your Observability config and the full subtree will be exported. -
Fixed goal reminders in MastraCode to continue through signals without duplicating prompts. Updated core signal stream completion handling so idle-started reminder runs emit the expected lifecycle events. (#16231)
-
Fixed requestContext serialization in workflow suspend/resume. The snapshot persistence used instanceof check which fails across module boundaries, causing context values to be lost on resume. (#16082)
-
Fixed
timeTravel()on evented workflows so jumping to a.branch()step no longer returns empty results for branches that did not run. Branch results now include only the branch that ran, matching the default workflow engine. (#16428) -
Fixed an issue where trajectory and step scorer results from
runEvalswere not saved to storage. These scores now persist correctly and appear alongside agent and workflow scores in Studio's observability section. (#16249) -
Tool
toModelOutputinvocations now emit aMAPPINGtracing span, showing the transformed output the model receives. (#16347) -
Default top-level observational memory early activation settings to observations only, while allowing per-phase overrides under
observationandreflection. (#16367) -
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Fixed assistant message tracking when ObservationalMemory clears step-1 output to memory and step-2 text merges into the same assistant message, so merged text is not lost on the next response clear. (#15277)
-
Added subagent display names to Harness display state so UIs can render configured subagent names without duplicating agent type lookup. (#16237)
-
Fixed title generation to send plain text instead of JSON-serialized part objects to the title model. Previously, internal metadata like providerOptions and framework details leaked into the title prompt. Now formats messages using role-prefixed plain text (similar to observational memory formatting), supporting both single-message and multi-turn conversations. (#15798)
-
Fixed a bug where orphaned provider-executed tool calls (e.g. Anthropic
web_search, Geminicode_execution) could brick a thread. When a provider dropped the tool-result chunk (#15668) or a run aborted mid-stream (#14148), the unresolved call was replayed on every subsequent request — causing Gemini to return empty text and Anthropic to reject the tool-call/tool-result invariant. (#15682)sanitizeV5UIMessagesnow only keeps aninput-availableprovider-executed tool part on the most recent assistant message, and only when no user turn has followed it. Orphans on earlier assistant turns are dropped so the outgoing history always satisfies the tool-call/tool-result pairing required by provider APIs. -
Fixed TokenLimiterProcessor crashing when counting text that contains special token strings. (#16302)
-
Fix nested loops in evented workflow (#16312)
-
Fixed agent requests to Anthropic failing with
400 unexpected tool_use_idwhen recalled history contained an incompletetool_use/tool_resultpair. This most commonly hit agents using parallel tool calls when an earlier run was interrupted before every tool finished — pulling that broken exchange into a new prompt no longer crashes the next request. (#16201)Closes #16193
-
Stop logging auto-recoverable provider cache corruption warnings when
~/.cache/mastra/contains stale content from another Mastra version. Corrupted cache files are still deleted on read so they cannot propagate into a project'sdist/, and the next gateway sync rewrites valid files. (#16332) -
Fix evented workflow foreach timing out when payload is an empty array (#16358)
-
Fixed FGA EE license gating so production servers require an Enterprise license when FGA is configured. (#16362)
-
Fixed a bug where message-level
providerOptionscould be lost or applied to the wrong turn after tool calls. AnthropiccacheControlmarkers now stay attached to the intended message in tool-using conversations. (#16133) -
Fixed
MODEL_INFERENCEspan timing so it measures pure model latency. (#16357) -
Added extra defensive checks to prevent edge cases where system messages may have already been stored in message history. (#15787)
-
Fixes tool call args being lost when split across messages in client tools. When a tool invocation spans multiple messages (call with args, result with empty args), the
findToolCallArgsfunction now continues searching for non-empty args instead of returning the first match. (#12454) -
Replace
js-tiktokenwithtokenxin@mastra/coreto reduce bundle size by removing the bundled BPE rank tables. Token limiting and truncation now use heuristic token estimates, which is appropriate for output limiting and truncation. (#16326) -
Fix
abort()not cancelling evented workflows (#16416) -
Fixed suspend and resume for evented workflows that use parallel steps,
.branch(),dountil/dowhileloops, and nested workflows — previously these only worked reliably for simple linear flows. (#16476)Parallel &
.branch()steps — when more than one branch suspends at the same time (e.g. each branch waits on its own approval), every suspended branch can now be resumed, the workflow stays suspended until all of them have been resumed, and the branch outputs are merged correctly. Before, only the last branch to suspend was resumable, and resuming one branch could prematurely complete the run.dountil/dowhileloops — a loop body that callssuspend()now suspends the workflow instead of crashing the run. And after a resume, subsequent loop iterations run fresh instead of re-receiving the resume data — which previously made loops either run forever or skip their own suspend logic.Nested workflows — resuming a suspended step inside a nested workflow now gives it the correct input (the output of the step right before it, not the nested workflow's own input), so it produces correct results, even when workflows are nested several levels deep. The suspended-step path returned in a workflow result is also correct now, so you can pass it straight back into
resume({ step }). -
Added support for attaching a browser instance to the harness after initialization so consumers can defer browser creation until it is needed: (#16513)
const harness = new Harness({ agent, mastra }); await harness.init(); harness.setBrowser(browser); -
Added validation to evented workflows to ensure execution prerequisites are met.
EventedWorkflow.createRun()now throw clear error messages when the workflow execution flow is empty (missing.then(),.branch(), etc. calls) or when the step graph has uncommitted changes (missing.commit()call). This catches configuration errors early rather than failing during execution. (#16361) -
Background tasks now run as evented workflows. Each task is dispatched as a workflow run that owns executor invocation, retries, and suspend/resume; pubsub topics, lifecycle event shapes, concurrency gating, and the
BackgroundTaskManager.stream()contract are unchanged. (#16260)Add suspend/resume to background tasks. Tools can call
ctx.agent.suspend(data)fromexecuteto pause a task and release the concurrency slot; resume withmastra.backgroundTaskManager.resume(taskId, resumeData)oragent.resumeStreamUntilIdle(resumeData, { runId, toolCallId }). Surfacesbackground-task-suspended/background-task-resumedchunks onbackgroundTaskManager.stream()andagent.streamUntilIdle().fullStream. -
Fixed message part ordering in agent streaming responses. Message parts (text, reasoning, tool calls) now appear in the correct order they arrived in the stream, preventing incorrect step sequencing and agent loop behavior issues. (#16073)
@mastra/agent-browser@0.2.2
Patch Changes
-
Added
screenshottool to@mastra/stagehand(stagehand_screenshot) and@mastra/agent-browser(browser_screenshot). Captures a PNG screenshot and returns image content for vision-capable models. (#16074)Added
excludeToolsconfig option to opt out of specific tools:const browser = new StagehandBrowser({ excludeTools: ['stagehand_screenshot'], });
@mastra/ai-sdk@1.4.2
Patch Changes
-
Fixed cache write tokens not being set on the AI SDK v6 usage object.
inputTokenDetails.cacheWriteTokensnow reflects the prompt cache creation tokens reported by the provider instead of always beingundefined. Previously this value was only accessible viaproviderMetadata.anthropic.cacheCreationInputTokens. (#16354) -
Added support for showing different tool values to users than the values used internally during execution. AI SDK streams now read Mastra display values for tool call input, streamed input deltas, tool results, tool errors, approvals, and suspensions. (#16103)
const lookupCustomer = createTool({ // Runtime still receives the full input and returns the full output. execute: async ({ customerId, internalPath }) => lookupCustomerRecord(customerId, internalPath), transform: { display: { input: ({ input }) => ({ customerId: input?.customerId }), output: ({ output }) => ({ displayName: output?.displayName }), error: () => ({ message: 'Customer lookup failed' }), }, }, });This lets chat UIs show safe display values while runtime code keeps the original payloads. See https://github.com/mastra-ai/mastra/issues/16054.
@mastra/arize@1.1.0
Minor Changes
- Update PHOENIX_ENDPOINT to PHOENIX_COLLECTOR_ENDPOINT environment variable (#16341)
Patch Changes
@mastra/braintrust@1.1.0
Minor Changes
-
Added a Braintrust current span resolver option so eval traces can nest correctly when applications and Mastra resolve different installed Braintrust SDK copies. (#16396)
-
Mastra Eval results are now forwarded to Braintrust. (#16185)
Patch Changes
@mastra/brightdata@0.2.0
Minor Changes
-
Added
@mastra/brightdataintegration withbrightdata-searchandbrightdata-fetchtools backed by Bright Data's SERP API and Web Unlocker. The tools bypass bot detection and CAPTCHAs out of the box. (#16392)import { Agent } from '@mastra/core/agent'; import { createBrightDataTools } from '@mastra/brightdata'; const agent = new Agent({ id: 'research-agent', name: 'Research Agent', model: 'anthropic/claude-sonnet-4-6', instructions: 'Use brightdata-search to find pages and brightdata-fetch to read them.', tools: createBrightDataTools(), });Set
BRIGHTDATA_API_TOKENin your environment, or pass{ apiKey }explicitly.
Patch Changes
@mastra/clickhouse@1.7.1
Patch Changes
-
Fixed agent streams intermittently hanging when observability storage was backed by Replicated/Shared ClickHouse. Startup no longer re-applies no-op schema updates (e.g.
ADD COLUMN IF NOT EXISTS,ADD INDEX IF NOT EXISTS,MODIFY TTL), so it no longer triggers replica-lag retry errors that could leave storage in a stuck state. (#16420) -
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/client-js@1.18.0
Minor Changes
-
Added experimental A2A Agent Card signature verification to
getAgentCard. (#16207)Example
const card = await a2a.getAgentCard({ verifySignature: { algorithms: ['ES256'], keyProvider: async ({ kid, jku }) => { return fetchTrustedPublicJwk({ kid, jku }); }, }, });When verification is configured,
client-jsnow verifies signed Agent Cards when the server includessignatures. Unsigned cards are still returned unchanged.This also adds the new exported types
GetAgentCardOptions,VerifyAgentCardSignatureOptions,AgentCardVerificationKey, andAgentCardSignatureKeyProviderInput. -
Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)
const { sendMessage } = useChat({ agentId, resourceId, threadId }); await sendMessage({ message: 'Follow up while streaming', threadId }); -
Added streamed function-call argument events to
@mastra/client-jsResponses streams. You can now read finalized tool arguments directly from the stream: (#16285)for await (const event of stream) { if (event.type === 'response.function_call_arguments.done') { console.log(event.arguments); } } -
Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)
Call
agent.sendSignal()to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. UseifActive.behaviorandifIdle.behaviorto deliver, persist, discard, or wake from a signal.Use
agent.subscribeToThread()to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.const subscription = await agent.subscribeToThread({ resourceId, threadId }); void (async () => { for await (const part of subscription.stream) { if (part.type === 'finish') { subscription.unsubscribe(); } } })(); agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId }); -
Fix
orderByshape mismatch for paginated list methods. (#16323)The server expects
orderByas a structured object ({ field, direction }), but several SDK methods were sendingorderByandsortDirectionas flat strings, which caused server-side schema validation to fail.Affected methods:
MastraClient.listMemoryThreadsAgent.listVersionsStoredAgent.listVersionsStoredPromptBlock.listVersionsStoredScorer.listVersions
Before:
client.listMemoryThreads({ orderBy: 'createdAt', sortDirection: 'DESC' });After:
client.listMemoryThreads({ orderBy: { field: 'createdAt', direction: 'DESC' } });The flat
sortDirectionparameter has been removed from the affected param types in favor of the nestedorderBy.directionfield. -
Fix client-js bugs surfaced by the SDK ↔ server contract audit. (#16439)
MastraClient.getAgentBuilderActions()previously requested/agent-builder/(trailing slash) and 404'd. Now hits/agent-builder.AgentBuilder.stream(params, runId)now requiresrunId. The server route requires it; calls without it failed with a server-side validation error. The SDK now both typesrunIdas required and guards at runtime.MastraClient.createStoredSkill(...)now requiresdescriptionin its parameter type. The server schema has always required it; the SDK type used to mark it optional, so omitting it produced a runtime 400 instead of a compile error.
Migration:
// Before await agentBuilder.stream({ inputData }); // After await agentBuilder.stream({ inputData }, runId);// Before await client.createStoredSkill({ name, instructions }); // After await client.createStoredSkill({ name, description, instructions }); -
Add
agent.resumeStreamUntilIdle()to resume a suspended agent stream and keep the SSE connection open through the follow-up turn. (#16260)
Patch Changes
-
Fixed memory thread write methods (
update,delete,deleteMessages,clone) silently sending requests without the requiredagentId. The methods now resolveagentIdfrom a per-call argument first, then the constructor, and throw a clear error if neither is set — before any HTTP request is issued. Reads are unchanged. (#16310)// Either set agentId on the thread once... const thread = client.getMemoryThread({ threadId: 't1', agentId: 'a1' }); await thread.update({ title: 'Renamed' }); await thread.delete(); // ...or pass it per call. const thread = client.getMemoryThread({ threadId: 't1' }); await thread.update({ agentId: 'a1', title: 'Renamed' }); await thread.delete({ agentId: 'a1' });Fixed
MastraClient.deleteThread()issuingDELETE /api(an empty URL) when called withoutagentIdornetworkId. The method now requires exactly one of the two, enforced both at runtime and in the type signature.await client.deleteThread('t1', { agentId: 'a1' }); await client.deleteThread('t1', { networkId: 'n1' }); -
Regenerate route types to include
TRAJECTORYandSTEPentityType variants on the score response (follow-up to #16249). (#16288) -
Fix
MCPTool.executesending an empty/undefined request body when called withoutdataorrequestContext. The server's tool-execute endpoint expects an object body (with optionaldata), so calls likeclient.getMcpServerTool(serverId, toolId).execute({})would fail withInvalid request body. The SDK now always POSTs a JSON object body, defaulting to{}when no parameters are provided. (#16488) -
Removed Agent Builder routes from the default generated API route contracts. (#16499)
@mastra/cloudflare@1.3.3
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/cloudflare-d1@1.0.6
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/convex@1.0.11
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/datadog@1.2.0
Minor Changes
-
Mastra Eval results are now forwarded to Datadog. (#16185)
-
Mapped
MODEL_INFERENCEspans to Datadog'sllmkind (with token usage and model/provider attached) andMODEL_STEPtoworkflow. Falls back to the previous mapping when paired with an older@mastra/corethat does not emitMODEL_INFERENCE. (#16363)
Patch Changes
- Fixed double-encoded JSON on span input in Datadog. (#16256)
@mastra/deployer@1.33.0
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-cloud@1.33.0
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-cloudflare@1.1.33
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-netlify@1.1.9
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-vercel@1.1.27
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/dsql@1.0.0
Minor Changes
-
Added Amazon Aurora DSQL storage provider with IAM authentication support. (#10930)
Enables storing threads, messages, workflows, traces, and agent data in Amazon Aurora DSQL clusters.
import { DSQLStore } from '@mastra/dsql'; const storage = new DSQLStore({ id: 'my-dsql-store', host: 'abc123.dsql.us-east-1.on.aws', }); await storage.init();Related: #10929
Patch Changes
@mastra/duckdb@1.3.1
Patch Changes
- Improved DuckDB observability initialization by batching schema setup statements on one connection while preserving migration order. (#16239)
@mastra/dynamodb@1.0.7
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/e2b@0.3.1
Patch Changes
-
Fixed S3 mounts in E2B sandboxes by honoring the configured region and verifying that the FUSE mount attached successfully. (#16222)
Mount failures that previously appeared successful now surface a clear error, making region, credential, and endpoint compatibility problems easier to diagnose.
@mastra/editor@0.7.24
Patch Changes
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Fixed
@mastra/editorintegrations (Composio, Arcade) collapsing every tool call onto a shared'default'user. Tools resolved duringagent.generatenow scope to the authenticated resource from the request context, so per-user OAuth connections route to the correct account instead of a shared one. (#16122)
@mastra/express@1.3.19
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/fastify@1.3.19
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Fixed Fastify stream cleanup and route abort signals when clients disconnect before streamed responses finish. (#16308)
@mastra/google-cloud-pubsub@1.0.4
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/hono@1.4.14
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/inngest@1.4.0
Minor Changes
-
Updated
@mastra/inngestto use Inngest SDK v4. (#15377)Breaking: Requires
inngest@^4and Inngest Dev Serverv1.18.0or later. The@inngest/realtimepackage is no longer needed — its functionality is now included ininngestv4. Remove it from your dependencies and import realtime helpers frominngest/realtimeinstead.// package.json "dependencies": { - "@inngest/realtime": "^0.x", - "inngest": "^3.x" + "inngest": "^4.0.0" }- import { realtimeMiddleware } from '@inngest/realtime/middleware'; - import { subscribe } from '@inngest/realtime'; + import { subscribe } from 'inngest/realtime'; const inngest = new Inngest({ id: 'mastra', - middleware: [realtimeMiddleware()], });In v4,
subscribe()andrealtime.publish()are first-class methods on the client; the standalone middleware is no longer required.InngestPubSubpublishes viainngest.realtime.publish()instead of the function-contextpublishargument that no longer exists in v4, restoring realtime workflow events and agent stream events.Improved: Workflow result polling now uses snapshot-based polling, resulting in significantly faster retrieval (~83x).
Patch Changes
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Updated the
serveandcreateServeJSDoc adapter examples to register Inngest at/inngest/apiinstead of/api/inngest, matching the Inngest deployment guide and in-repo example projects. (#16186)Why
Mastra reserves the
/apiprefix for built-in routes (agents, workflows, memory). CustomapiRoutes[].pathvalues that start with the server'sapiPrefix(default/api) are rejected at startup, so the previous JSDoc snippets threwCustom API route "/api/inngest" must not start with "/api"when copy-pasted into a current Mastra project.Migration
If you registered Inngest with the previous guide or JSDoc example:
// Before apiRoutes: [ { path: '/api/inngest', method: 'ALL', createHandler: async ({ mastra }) => serve({ mastra, inngest }), }, ]; // After apiRoutes: [ { path: '/inngest/api', method: 'ALL', createHandler: async ({ mastra }) => serve({ mastra, inngest }), }, ];Update the dev server URL (
npx inngest-cli dev -u http://localhost:4111/inngest/api) and, in production, set the URL field on your Inngest app to match.If you cannot change the path, set
server.apiPrefix(for example/_mastra) to relocate the built-in routes and remember to updateserver.auth.protectedand anyMastraClientapiPrefixto match. See the Inngest deployment guide for the full walkthrough.
@mastra/koa@1.5.2
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Fixed
TypeError: Cannot read properties of undefined (reading 'length')thrown duringMastraServer.init()when a subclass forwards a non-Koa app-like object (for example akoa-routerinstance, a mounted sub-app, or a custom wrapper) tosuper.registerRoute(app, route, opts). The dispatcher-reuse optimization introduced in 1.5.0 now requires the target to expose anapp.middlewarearray; otherwise it falls back to registering a fresh dispatcher per route viaapp.use, matching the pre-1.5.0 per-route behavior. (#16484)Example (subclass that previously crashed):
import { MastraServer } from '@mastra/koa'; import Router from 'koa-router'; class CustomKoaMastraServer extends MastraServer { private router = new Router(); async registerCustomApiRoutes() { const routes = this.mastra.getServer()?.apiRoutes ?? []; for (const route of routes) { // The router has no `middleware` array — this used to throw at init. await super.registerRoute(this.router as any, route, { prefix: this.prefix }); } this.app.use(this.router.routes()); } }
@mastra/laminar@1.1.0
Minor Changes
- Mastra Eval results are now forwarded to Laminar. (#16185)
Patch Changes
@mastra/lance@1.0.6
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/langfuse@1.3.0
Minor Changes
- Mastra Eval results are now forwarded to Langfuse. (#16185)
Patch Changes
- Scope Langfuse traces to the agent or workflow that started them. See Scoping evaluators per agent for more info. (#16393)
@mastra/langsmith@1.2.0
Minor Changes
- Mastra Eval results are now forwarded to LangSmith. (#16185)
Patch Changes
@mastra/libsql@1.10.1
Patch Changes
-
Fixed Workflow run snapshots no longer lose fields when serialized for storage. The libsql
safeStringifycycle-detection treated any object that appeared more than once in a snapshot as a circular reference and dropped it. Becausesnapshot.resultand the final step'scontext[step].outputshare the same reference on success,snapshot.resultwas being silently stripped on every persist. This causedlistWorkflowRunsto return runs withsnapshot.result === undefinedand broke workflow resume when suspended-state fields were shared elsewhere in the snapshot. (#16368) -
Fixed Workflow runs no longer fail to persist when request context contains non-serializable values (for example functions, circular references, or platform proxy objects). This prevents errors when saving workflow snapshots and scorer results. See #12301. (#12573)
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Improved local LibSQL startup performance by applying conservative local SQLite performance settings before initialization, exposing local PRAGMA overrides, and reducing schema initialization contention. (#16513)
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260) -
Added LibSQL indexes for thread message history queries to speed up recent-message and observational-memory loading. (#16513)
@mastra/memory@1.18.0
Minor Changes
- Add metadata filtering support to semantic recall. (#9256)
Patch Changes
-
Fixed an issue where tool results containing AI SDK v5
image-datacontent blocks (returned viatoModelOutput) were stringified into the observational memory prompt as raw base64 text. The base64 data overflowed the observer's context, causing token-limit errors and degenerate output. (#16117)Image and file blocks (
image-data,image-url,file-data,file-url, andmedia) inside tool results are now hoisted into the observer's input as proper attachments, the same way image and file message parts already are. The text body shows a placeholder like[Image #1: image/png]so the observer keeps positional context without seeing the bytes. -
Default top-level observational memory early activation settings to observations only, while allowing per-phase overrides under
observationandreflection. (#16367) -
Auto-recover from transient transport errors (e.g. undici
terminated,fetch failed,UND_ERR_*, 5xx, 429) in the OM observer and reflector LLM calls. Adds an internal retry wrapper with exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s — per-attempt delay capped at 2 minutes, ~4 minute total budget per call) so a single network blip from any provider no longer kills the actor turn during long-running sessions. Non-transient errors (auth, validation, etc.) and user-initiated aborts still fail fast. No public API changes. (#16454) -
Added extra defensive checks to prevent edge cases where system messages may have already been stored in message history. (#15787)
-
Fixed read-only observational memory so existing context is still loaded. (#16059)
@mastra/mongodb@1.8.1
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/mssql@1.3.0
Minor Changes
-
Add agents storage domain to MSSQL adapter — brings @mastra/mssql to parity with @mastra/mongodb and @mastra/libsql for the agents domain. The Studio "Agents" tab and
mastra.getEditor()now work against MSSQL. (#16376)import { MSSQLStore } from '@mastra/mssql'; const store = new MSSQLStore({ id: 'mssql-storage', connectionString: process.env.MSSQL_URL!, }); const agents = await store.getStore('agents'); const agent = await agents?.getById('agent-id'); const page = await agents?.list({ status: 'published', perPage: 20 });
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/nestjs@0.1.3
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/observability@1.12.0
Minor Changes
-
DefaultExporternow notifies custom exporters and connected integrations when it cannot persist observability events, such as unsupported storage or retries being exceeded. (#16111) -
Renamed two built-in observability exporters to clearer names. The originals are still exported (now deprecated) and continue to work unchanged, including their existing exporter
namestrings and error IDs, so monitoring rules and dashboards keep matching until you migrate. (#16223)CloudExporter→MastraPlatformExporterDefaultExporter→MastraStorageExporter
Before
import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'my-app', exporters: [new DefaultExporter(), new CloudExporter()], spanOutputProcessors: [new SensitiveDataFilter()], }, }, });After
import { Observability, MastraStorageExporter, MastraPlatformExporter, SensitiveDataFilter, } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'my-app', exporters: [new MastraStorageExporter(), new MastraPlatformExporter()], spanOutputProcessors: [new SensitiveDataFilter()], }, }, }); -
Apply
SensitiveDataFilterby default (#16234)The
Observabilityregistry now auto-applies aSensitiveDataFilterspan output processor to every configured instance, so secrets (API keys, tokens, passwords, etc.) are redacted before they reach exporters such as the Mastra cloud exporter. This protects against accidentally exporting sensitive data when the filter was not added manually.A new top-level
sensitiveDataFilteroption on theObservabilityregistry config controls this behavior:true(default): applySensitiveDataFilterwith default options.false: opt out of auto-applied filtering.- a
SensitiveDataFilterOptionsobject: customize the filter (sensitive fields, redaction token, redaction style).
If a config already includes a
SensitiveDataFilterinspanOutputProcessors, the auto-applied filter is skipped to avoid double redaction. Pre-instantiatedObservabilityInstancevalues are not modified.Before:
import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'mastra', exporters: [new DefaultExporter(), new CloudExporter()], spanOutputProcessors: [new SensitiveDataFilter()], }, }, });After:
import { Observability, DefaultExporter, CloudExporter } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'mastra', exporters: [new DefaultExporter(), new CloudExporter()], }, }, // Optional: customize or disable the auto-applied filter. // sensitiveDataFilter: false, // sensitiveDataFilter: { sensitiveFields: ['myCustomSecret'] }, }); -
Added new
MODEL_INFERENCEspan type underMODEL_STEP, covering only the model provider call. Use it to measure model latency separately from input/output processors and tool executions. (#16267)
Patch Changes
-
Fixed cost estimation for OpenRouter models. The Model Usage & Cost panel now shows costs for OpenRouter
vendor/modelids (e.g.openai/gpt-5-mini-2025-08-07,xiaomi/mimo-v2-pro-20260318) that previously rendered an empty cost column. (#16206) -
Support
MASTRA_PLATFORM_ACCESS_TOKENas the preferred environment variable forMastraPlatformExporter, while retainingMASTRA_CLOUD_ACCESS_TOKENas a fallback for backward compatibility. (#16500) -
Score events now include scorer names and target entity types. (#16185)
-
Fixed
MODEL_INFERENCEspan timing so it measures pure model latency. (#16357) -
Refreshed the embedded pricing data snapshot used for cost estimation in observability metrics with the latest provider rates. (#16373)
@mastra/otel-bridge@1.1.0
Minor Changes
-
Added log forwarding to
@mastra/otel-bridge. The bridge now also subscribes to Mastra log events and forwards them to the globally-registered OpenTelemetryLoggerProvider, alongside the spans it already creates. (#13529)Logs that originate inside a Mastra span are emitted under that span's OTEL context, so backends like Datadog, Grafana, and Honeycomb correlate them with the surrounding trace automatically. Logs without trace context fall through to the currently active OTEL context.
To wire up logs alongside traces, register a
logRecordProcessoronNodeSDK:import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs'; const sdk = new NodeSDK({ // ...trace config as usual logRecordProcessor: new BatchLogRecordProcessor(new OTLPLogExporter()), });If no
LoggerProvideris registered, log emission is a silent no-op — traces continue to work as configured.
Patch Changes
@mastra/otel-exporter@1.1.0
Minor Changes
-
Added log export to
@mastra/otel-exporter. Logs emitted on the Mastra observability bus are now forwarded to the configured OTLP endpoint alongside traces, using the same provider configuration. (#13529)import { OtelExporter } from '@mastra/otel-exporter'; new OtelExporter({ provider: { custom: { endpoint: 'http://localhost:4318', protocol: 'http/json' }, }, // signals.logs defaults to true; set to false to disable. signals: { traces: true, logs: true }, });Requires the matching OTLP log exporter package to be installed (e.g.
@opentelemetry/exporter-logs-otlp-httpfor HTTP/JSON, or-proto/-grpcvariants).Trace correlation: Logs that carry
traceIdandspanIdare attached to the OTEL log record's native trace context, so backends like Datadog, Grafana, and Honeycomb auto-correlate logs to traces.Other improvements:
- Trace and log endpoints are always normalized to a single signal-path suffix, so
http://host:4318/,http://host:4318, andhttp://host:4318/v1/traces/all produce well-formed URLs instead of malformed variants like//v1/logs. - Calling
flush()orshutdown()immediately after init no longer drops telemetry — teardown waits for setup to finish before draining providers. - Debug log output no longer exposes credential fragments. Provider header values are fully redacted instead of printing prefix/suffix slices.
- When a dynamically-loaded OTLP exporter package is installed but does not expose the expected named export, Mastra now disables that signal with a clear error message instead of failing later with an opaque "X is not a constructor" error.
- Trace and log endpoints are always normalized to a single signal-path suffix, so
Patch Changes
@mastra/pg@1.10.1
Patch Changes
-
Fixed
@mastra/pglisting endpoints (agents, MCP clients, MCP servers, prompt blocks, scorer definitions, skills, and workspaces) so a single row with a malformed value no longer returns HTTP 500 and hides every other record in the Mastra Editor. Listings now tolerate the bad row and return the rest. (#16233)Fixes #16224.
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'. -
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/playground-ui@27.0.0
Minor Changes
-
Updated agent traces tab to use the rich observability traces UI (#16405)
The agent traces tab now shows the dense 7-column trace list with a side-panel detail view featuring colored timeline spans (Agent/Workflow/Model/Scorer), expandable nested spans, Evaluate Trace, and Save as Dataset Item.
Locked scope filter pills
When viewing agent-scoped traces, the Primitive Type and Primitive ID filter pills are now read-only — they display the agent context, show a lock icon and tooltip, and cannot be edited or removed. The Add Filter dropdown no longer lists scope-controlled fields so users cannot accidentally override the active scope.
PropertyFilterAppliedaccepts a newlockedFieldIds(and optionallockedTooltipContent) prop.PropertyFilterCreatoraccepts a newhiddenFieldIdsprop. Both are opt-in and unset by default, so existing usages are unaffected.// Before <PropertyFilterApplied fields={fields} tokens={tokens} onTokensChange={setTokens} /> // After — pills for the listed fields render locked with a tooltip <PropertyFilterApplied fields={fields} tokens={tokens} onTokensChange={setTokens} lockedFieldIds={['rootEntityType', 'entityId']} lockedTooltipContent="This filter is set by the current context." /> -
Added CodeBlock component with select/tabs switcher, optional shiki syntax highlighting, and Notion-style hover-only copy button (always visible on touch devices via media query). (#16202)
-
Improved
ScrollAreato use Base UI internally and added a richer mask API. Edges now fade by default based onorientation(top/bottom for vertical, left/right for horizontal, all four for both), so most scrollers get the polished fade-out automatically. (#16415)Heads up — default behavior change:
ScrollAreapreviously rendered without any edge fade unlessshowMaskwas passed. It now fades the edges that matchorientationby default. Passmask={false}on the callsites where you want to keep the old hard edges.New
maskprop. Accepts a boolean (falsedisables the fade entirely) or an object to override individual sides. Thexandykeys are shorthands for the matching axis.// Default — fades follow `orientation` <ScrollArea>...</ScrollArea> // Opt out entirely <ScrollArea mask={false}>...</ScrollArea> // Keep only the top fade <ScrollArea mask={{ bottom: false }}>...</ScrollArea> // Vertical fades only on a two-axis scroller <ScrollArea orientation="both" mask={{ x: false }}>...</ScrollArea>Migrating from
showMask. TheshowMaskboolean is now deprecated but still works —maskwins when both are set.// Before <ScrollArea showMask>...</ScrollArea> <ScrollArea showMask={false}>...</ScrollArea> // After <ScrollArea>...</ScrollArea> // default fade matches orientation <ScrollArea mask={false}>...</ScrollArea> // explicitly disable -
Added an "All traces, nested too" mode to the Observability → Traces page. (#16479)
The traces list now has a switcher in the toolbar to toggle between two views:
- Top-level traces only (default) — one row per top-level run, the existing behavior.
- All traces, nested too — one row per invocation, including every agent, workflow, tool, processor, scorer, and RAG ingestion that ran nested inside another run.
This makes it possible to find every invocation of a given entity (e.g. "every run of
recipe-makerworkflow") regardless of how it was triggered. Selecting a row in the new mode opens a detail panel showing just that branch's subtree.New hooks for consumers building their own observability UIs:
useBranch({ traceId, spanId, depth? })— fetches the span subtree rooted at an anchor span.useTraceOrBranchSpans({ traceId, spanId, listMode })— returns trace spans or a branch subtree depending on the active mode.
-
Added opt-in interactivity and per-page filter persistence support for observability UI components. (#15747)
MetricsLineChartaccepts anonPointClickcallback so chart points can drive drilldowns.HorizontalBarsaccepts row-level and segment-level hrefs for linked metric bars without nested anchors.MetricsDataTableacceptsgetRowHref(row)for linked rows with consistent hover and focus styling.MetricsCardexposes anActionsslot in the top bar for contextual icon links.- Observability filter helpers for Metrics, Traces, and Logs each keep their own saved-filters storage key so pages remember filters independently.
All additions are optional, so existing consumers continue to render the same way unless they pass the new props.
<MetricsLineChart data={points} series={series} onPointClick={point => navigate(`/observability?dateFrom=${point.from}&dateTo=${point.to}`)} /> <HorizontalBars data={[{ name: 'agent-a', values: [42, 3], href: '/observability?filterEntityName=agent-a' }]} /> <MetricsDataTable columns={cols} data={rows} getRowHref={row => `/observability?filterThreadId=${row.threadId}`} /> <MetricsCard> <MetricsCard.TopBar> <MetricsCard.TitleAndDescription title="Latency" /> <MetricsCard.Actions> <IconButton href="/observability" /> </MetricsCard.Actions> </MetricsCard.TopBar> </MetricsCard> -
Added a new
pillvariant onTabListwith an animated background indicator that slides behind the active trigger. The defaultlinevariant now animates its underline smoothly between tabs as well. Implemented by migrating the underlying Tabs component from Radix UI to Base UI. (#16414)// Before — only the line (underline) style was available <Tabs defaultTab="overview"> <TabList> <Tab value="overview">Overview</Tab> <Tab value="projects">Projects</Tab> </TabList> </Tabs> // After — opt into the new pill style via the `variant` prop on TabList <Tabs defaultTab="overview"> <TabList variant="pill"> <Tab value="overview">Overview</Tab> <Tab value="projects">Projects</Tab> </TabList> </Tabs>The public API (
Tabs,TabList,Tab,TabContent) is unchanged; existing call-sites keep the defaultlinevariant. -
Added new
pill-ghostvariant onTabsandstickyprop onTabListfor sticky tab headers. (#16433)<Tabs defaultTab="overview"> <TabList variant="pill-ghost" sticky> <Tab value="overview">Overview</Tab> <Tab value="settings">Settings</Tab> </TabList> </Tabs>Added
variantprop onCombobox(default,ghost,link) for flexible trigger styling. Note: this prop existed previously but was a no-op; it now actually drives the trigger appearance, so callers passingvariantwill see updated styles.// Bordered form input (default) <Combobox options={options} value={value} onValueChange={setValue} /> // Borderless, hover-only surface <Combobox options={options} value={value} onValueChange={setValue} variant="ghost" /> // Text-only trigger <Combobox options={options} value={value} onValueChange={setValue} variant="link" />Improved
EntityHeaderlayout — title and children now share a single row with wrapping, and padding is tighter for denser headers.Improved
ScrollAreato handle vertical/horizontal orientation correctly, preventing forced horizontal scroll when only vertical is needed.Improved
PanelSeparatorwith a pill-shaped handle that grows on hover/active for a clearer affordance.Removed
Threads,ThreadList,ThreadItem,ThreadLink,ThreadDeleteButtonexports. These had no consumers outside this repository. Downstream users relying on these exports should compose an equivalent list locally using the underlying DS primitives (Button,Icon,Txt) — theplaygroundpackage now contains a reference implementation undersrc/components/thread-list.- import { Threads, ThreadList, ThreadItem, ThreadLink, ThreadDeleteButton } from '@mastra/playground-ui'; + // Compose locally with @mastra/playground-ui primitives (Button, Icon, Txt) + // or copy the reference implementation from the playground package. -
Added
MainSidebar.NavLabel— collapse-aware label slot forasChildnav items. When the sidebar collapses to icon-only mode, the label hides viaVisuallyHidden(still announced by screen readers) instead of leaking outside the 36px icon rail. The defaultlink={...}path was already collapse-aware;asChildconsumers now have a matching primitive. (#16167)// Before: text leaked visually when the sidebar collapsed <MainSidebar.NavLink asChild> <button> <Bot /> Agents </button> </MainSidebar.NavLink> // After: wrap labels in MainSidebar.NavLabel <MainSidebar.NavLink asChild> <button> <Bot /> <MainSidebar.NavLabel>Agents</MainSidebar.NavLabel> </button> </MainSidebar.NavLink>Improved truncation handling for nav items and section headers. Long labels now clip with a single-line ellipsis instead of wrapping to a second line during the collapse/expand transition, eliminating layout jumps at narrow widths.
-
Added SettingsRow primitive for label/description + control rows in settings pages. Markup mirrors the existing platform settings row pattern (flex justify-between with title + optional description on the left, control on the right) so consumers can adopt it without visual regressions. (#16150)
Removed the redundant SelectField wrapper. Its only internal consumer (Studio settings) was migrated to SettingsRow + Select primitives. For form fields use SelectFieldBlock; for settings rows use SettingsRow.
Before
<SelectField name="theme" label="Theme mode" value={theme} onValueChange={setTheme} options={THEME_OPTIONS} />After
<SettingsRow label="Theme mode" htmlFor="theme"> <Select value={theme} onValueChange={setTheme}> <SelectTrigger id="theme" className="w-full sm:w-48"> <SelectValue /> </SelectTrigger> <SelectContent>{/* items */}</SelectContent> </Select> </SettingsRow> -
Added
InputGroupand extendedButtonsGroupin playground-ui design system. (#16417)New
InputGroupcomponentCompose inputs with leading or trailing icons, buttons, text labels, and keyboard hints. Supports inline (left/right) and block (top/bottom) addon alignment, and works with both inputs and textareas.
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupButton } from '@mastra/playground-ui'; import { SearchIcon, XIcon } from 'lucide-react'; <InputGroup> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> <InputGroupInput placeholder="Search..." /> <InputGroupAddon align="inline-end"> <InputGroupButton aria-label="Clear"> <XIcon /> </InputGroupButton> </InputGroupAddon> </InputGroup>;Extended
ButtonsGroupAdded
orientation(horizontal|vertical), and newButtonsGroupSeparatorandButtonsGroupTextslots. Existing API unchanged.<ButtonsGroup spacing="close"> <Button variant="outline">−</Button> <ButtonsGroupText>42</ButtonsGroupText> <Button variant="outline">+</Button> </ButtonsGroup> <ButtonsGroup orientation="vertical"> <Button variant="ghost">Copy</Button> <ButtonsGroupSeparator /> <Button variant="ghost">Cut</Button> </ButtonsGroup>Tweaked
Buttonghost variantAligned hover/active progression with the outline variant (
surface3→surface4) so click feedback is perceptible on transparent backgrounds. Existing ghost buttons throughout the playground will appear one shade lighter on hover and active.
Patch Changes
-
Improved Studio's Traces page to scale smoothly to many traces. The list now renders only the visible window, so scrolling stays responsive and memory usage stays bounded regardless of how many traces are loaded. (#16262)
-
Filter pills (
PropertyFilterApplied) now match the rest of the design system — single 1px border, consistent rounded segments, no custom styling. Labels stay on one line and no longer compress when long values are present. (#16426)ButtonsGroupTextsegments also no longer wrap to multiple lines or shrink under flex pressure, which makes them safer to drop into any tight layout. -
Fixed three issues on the Logs and Traces pages: (#16306)
- Column widths now stay stable while scrolling — they no longer jump as different rows scroll into view.
- Scrolling far down the Logs list no longer scrambles rows (duplicates, gaps, or empty rows after additional pages load).
- Changing a filter or the date range now scrolls the list back to the top, instead of leaving an empty band above the new data until you nudge the scroll. Logs and Traces now behave the same way on filter changes.
-
Added
NoTracesInfocomponent that informs the user there are no traces for the active date range. (#16303) -
Refreshed the visual style of form controls and popups for a softer, more consistent look: (#16150)
- Button: thinner border (
borderinstead ofborder-2); text-mode buttons userounded-full; icon-mode buttons are circular. - Combobox / Select / DropdownMenu / Command: triggers and items aligned on the form-input look —
rounded-lgborder, transparent background, soft hover/open states, consistenttext-ui-smdtypography. - Popups (Popover / Tooltip / Select / Dropdown content):
rounded-xlcontainers withshadow-dialog; inner itemsrounded-lginsidep-1. - Tokens: bumped the radius scale (
sm2→4px,md4→6px,lg6→10px,xl12→14px); replaced--shadow-dialog's outer 1px ring with an inset top gloss so the dialog shadow stops doubling up with each consumer's own border.
- Button: thinner border (
-
Polished Combobox dropdown items (#16411)
- Moved the selection check to the right of each item so unselected rows no longer carry an awkward left padding gap and the whole list aligns consistently.
- Tightened popup search/empty padding and softened the trigger hover for a calmer command-palette feel.
Added
ComboboxPrimitiveexport for advanced compositionsRe-exports the raw
@base-ui/react/comboboxparts (Root, Trigger, Input, List, Item, Chips, etc.) so callers needing virtualization, async status, chips, or creatable patterns can compose them directly with the sharedcomboboxStylestokens — without growing the monolithic<Combobox>prop surface.import { ComboboxPrimitive, comboboxStyles } from '@mastra/playground-ui'; <ComboboxPrimitive.Root items={items}> <ComboboxPrimitive.Input className={comboboxStyles.searchInput} /> <ComboboxPrimitive.Portal> <ComboboxPrimitive.Positioner> <ComboboxPrimitive.Popup className={comboboxStyles.popup}> <ComboboxPrimitive.List className={comboboxStyles.list}> {item => ( <ComboboxPrimitive.Item value={item} className={comboboxStyles.item}> {item.label} </ComboboxPrimitive.Item> )} </ComboboxPrimitive.List> </ComboboxPrimitive.Popup> </ComboboxPrimitive.Positioner> </ComboboxPrimitive.Portal> </ComboboxPrimitive.Root>; -
Aligned Badge variant colors with the Notice and Toast palette so the design system uses one consistent set of semantic colors. Default badges keep their neutral surface, while success, error, info and warning variants now use the same notice tokens as Notices and Toasts. Icons inside badges are sized down to match the badge height. (#16215)
-
Improved Studio's Logs page to scale smoothly to many log records. The list now renders only the visible window, so scrolling stays responsive and memory usage stays bounded regardless of how many logs are loaded. (#16263)
-
Fixed Studio streaming render behavior for interleaved reasoning and improved chat autoscroll during rapid output. (#16331)
-
Restored auto-refresh on the traces list page, polling every 10 seconds. Polling is paused while the request is forbidden (HTTP 403) to avoid flicker. (#16204)
-
Refined Combobox and Select trigger interactions with an active state and fixed value truncation when a leading badge is rendered. Refreshed PanelSeparator with a clearer hover/active affordance, an enlarged hit area, and a focus-visible accent. Removed the default
bg-surface2background fromThreadsso consumers can control the surface color. (#16269) -
Changed the default Observability list mode to branches (all traces, including nested). The query logic still recognizes
?listMode=tracesto opt back into the top-level-only view. (#16493)Before
/observability→ top-level traces onlyAfter
/observability→ branches (all traces, nested too)/observability?listMode=traces→ top-level traces only -
Fixed Notice component alignment with action: button now stays at default sm size, vertically aligns to first text line via negative margin compensation, and stacks below the message as a full-width button on narrow containers. (#16150)
@mastra/react@0.3.0
Minor Changes
-
Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)
const { sendMessage } = useChat({ agentId, resourceId, threadId }); await sendMessage({ message: 'Follow up while streaming', threadId });
Patch Changes
-
Fixed streaming chat messages so sending while an agent is running does not duplicate assistant output or leave the previous response marked as streaming. (#16338)
-
Fixed Studio streaming render behavior for interleaved reasoning and improved chat autoscroll during rapid output. (#16331)
-
Handle
background-task-suspendedchunks intoUIMessageso suspend metadata is restored after resume. (#16260)
@mastra/redis@1.1.1
Patch Changes
-
Per-key TTL support in
RedisCache(#16283)RedisCache.set()now accepts an optionalttlMsargument that overrides the configured default TTL for a single entry. Sub-second values are rounded up to one second (RedisEXPIREgranularity); a non-positive value persists the entry without expiry.const cache = new RedisCache({ url: 'redis://...' }); await cache.set('weather:nyc', payload, 60_000); // expires in 60s await cache.set('manifest', payload, 0); // persists indefinitely -
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
@mastra/redis-streams@0.0.2
Patch Changes
-
Worker review fixes: (#16309)
- Step-execution endpoint (
POST /workflows/:id/runs/:runId/steps/execute) is now gated by Mastra's standardrequiresAuth: true+authenticateTokenpipeline rather than a parallel "worker secret" body field. The previously introducedworkerSecretconfig knob andMASTRA_WORKER_SECRETenv var have been removed (they were never released). To gate the endpoint on a standalone-worker deployment, configure an auth provider on the server'sMastrainstance — without one the framework currently treatsrequiresAuth: trueas a no-op for this route. HttpRemoteStrategynow sends credentials as a normalAuthorization: Bearer <token>header. The token comes from the newMASTRA_WORKER_AUTH_TOKENenv var or an explicitauthconstructor option.- Honor the caller's
abortSignalinHttpRemoteStrategyby combining it with the per-request timeout viaAbortSignal.any(with a manual fallback for runtimes that don't expose it). - Implement comma-separated name filtering for the
MASTRA_WORKERSenv var.MASTRA_WORKERS=scheduler,backgroundTasksnow boots only those named workers;MASTRA_WORKERS=falsestill disables all workers. - Restore
Mastra.startEventEngine/stopEventEngineas@deprecatedaliases for the renamedstartWorkers/stopWorkers. BackgroundTaskWorkernow subscribes to PubSub instart()instead ofinit(), matching the lifecycle of the other workers and makingisRunningaccurately reflect subscription state.RedisStreamsPubSubadds amaxDeliveryAttemptsoption (default 5) that drops events after the configured number of failed deliveries instead of redelivering forever, and replaces emptycatch {}blocks withlogger.warn/logger.debugcalls.RedisStreamsPubSub.unsubscribe(topic, cb)now honors the topic argument so the same callback can be subscribed to multiple topics independently.PullTransportguards the async router callback against unhandled promise rejections by attaching a.catchthat nacks the message.- Drop the dead
MASTRA_WORKER_NAMEenv var injection in the CLI worker spawn (the bundle entrypoint already passes the worker name directly). - Add a real cross-process e2e auth suite
(
pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong token, missing token, anonymous direct hits, and the no-auth-provider pin-down behavior. - Step-execution route now has a response schema, satisfying
schema-consistency.test.ts. - Internal type cleanups (drop several
as anycasts in worker strategies andBackgroundTaskWorker). RedisStreamsPubSub.maxDeliveryAttemptsnow rejects negative / NaN values at construction.0still means "no cap" for back-compat but emits a one-time warning; passInfinityto disable the cap explicitly.PullTransportaccepts a logger and uses it for unhandled router-callback rejections instead ofconsole.error.BackgroundTaskWorker.start()now throws ifinit()was not called, matching the contract of the other workers.- Cross-process integration tests now spawn a single user-owned project
(
test-fixtures/cli-project/src/mastra/index.ts) through two generic entries that mirror whatBuildBundlerandWorkerBundleremit. The previous one-offserver.entry.ts/worker.entry.ts/scheduler.entry.ts/background.entry.tsfiles have been deleted — they implied users hand-roll entry files, which they don't. Worker role is selected viaMASTRA_WORKERSexactly as in production.
Push-capable PubSub:
- The
PubSubabstract class now declares asupportedModesgetter (defaulting to['pull']for backward compatibility) so consumers can tell whether a broker delivers events through a pull loop, an in-process push, or an out-of-process HTTP push.EventEmitterPubSubreports['pull', 'push'](EventEmitter dispatches synchronously and works for either path),@mastra/redis-streamsreports['pull']. Mastranow exposes a publichandleWorkflowEvent(event)method backed by a sharedWorkflowEventProcessor. It is the single entry point used by the existing pull-modeOrchestrationWorker, by in-process push pubsubs (auto-wired duringstartWorkers()), and by the newPOST /api/workflows/eventsroute which lets push-mode brokers (GCP Pub/Sub push, SNS, EventBridge) deliver events over HTTP.- When the configured pubsub does not support
'pull', Mastra automatically skips creating anOrchestrationWorkerandOrchestrationWorker.init()throws a clear error if it is constructed against a push-only pubsub. WorkflowEventProcessorgains ahandle(event)method that returns a structured{ ok, retry }result. The originalprocess(event, ack?)method is preserved as a thin wrapper for back-compat.
Public-API example for a push-capable PubSub:
import { Mastra } from '@mastra/core/mastra'; import { EventEmitterPubSub } from '@mastra/core/pubsub'; const mastra = new Mastra({ // A push-capable broker (GCP Pub/Sub push, SNS, EventEmitter, …). // EventEmitterPubSub reports supportedModes = ['pull', 'push']. pubsub: new EventEmitterPubSub(), workflows: { myWorkflow }, }); // In-process push pubsubs are auto-wired here. For out-of-process // push (e.g. HTTP webhook from a cloud broker), POST the event to // /api/workflows/events on your Mastra server instead. await mastra.startWorkers(); // Direct invocation (e.g. inside an HTTP handler that bridges from a // cloud broker's push delivery): await mastra.handleWorkflowEvent({ id: 'evt-1', type: 'workflow.start', runId: 'run-1', createdAt: new Date(), data: { workflowId: 'myWorkflow', inputData: { name: 'world' } }, });CI follow-ups:
Mastraonly auto-registersSchedulerWorkerwhen storage is configured. Without storage the worker would crash on startup (deps.storage.getStoreon undefined); the scheduler now silently no-ops in that case, matching the pre-worker scheduler behavior.SchedulerWorker.initdefensively logs and returns when called without storage instead of throwing a TypeError.RECEIVE_WORKFLOW_EVENT_ROUTE(POST /workflows/events)createdAtis now a plainz.string()on the wire and the handler converts it to aDate(validating "Invalid Date" -> 400). The previousunion(...).transform().refine()schema couldn't be exercised by the shared adapter test suite because the generator didn't unwrap Zod 4'sZodPipe._test-utils/route-test-utilsrecognizes Zod 4'snumber_formatcheck (used forint()/safeint()), andgenerateContextualValuenow produces a valid ISO timestamp forcreatedAt/updatedAtfields.
- Step-execution endpoint (
@mastra/schema-compat@1.2.10
Patch Changes
- Fixed Google-compatible schema conversion so Gemini accepts broad nullable tool parameters. (#16129)
@mastra/server@1.33.0
Minor Changes
-
Added in-memory A2A push notification support for task updates. (#16175)
Clients can now register push notification configs with
message/send,message/stream, or thetasks/pushNotificationConfig/*methods. The server advertises push notification support in the agent card and sends the current task snapshot to registered webhooks when a task reachescompleted,failed,canceled, orinput-required.Webhook delivery validates the configured URL and pins outbound delivery to the validated address to reduce DNS rebinding risk. This remains in-memory and best-effort; operators should still use normal egress controls and avoid exposing push delivery to networks with sensitive internal services unless they trust the configured webhook targets.
await a2a.setTaskPushNotificationConfig({ taskId: 'task-123', pushNotificationConfig: { url: 'https://example.com/a2a-webhook', token: 'session-token', }, }); -
Allow stored Responses API follow-up requests to use
previous_response_idwithout also passingagent_id. (#16246)When callers pass both
previous_response_idand an explicitagent_id, mismatched agents now return a clear 400 response instead of looking like a missing stored response.The create-response schema now also rejects empty
agent_idandprevious_response_idstrings. -
Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)
const { sendMessage } = useChat({ agentId, resourceId, threadId }); await sendMessage({ message: 'Follow up while streaming', threadId }); -
Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)
Call
agent.sendSignal()to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. UseifActive.behaviorandifIdle.behaviorto deliver, persist, discard, or wake from a signal.Use
agent.subscribeToThread()to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.const subscription = await agent.subscribeToThread({ resourceId, threadId }); void (async () => { for await (const part of subscription.stream) { if (part.type === 'finish') { subscription.unsubscribe(); } } })(); agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId }); -
Responses streams now emit tool call events so clients can track tool arguments and results in real time. (#16285)
Tool outputs now use consistent IDs (
<toolCallId>:output) so streamed arguments can be matched to completed results.for await (const event of stream) { if (event.type === 'response.function_call_arguments.delta') { console.log(event.delta); } if (event.type === 'response.output_item.done' && event.item.type === 'function_call') { console.log(event.item.id); } } -
Added support for signed A2A Agent Cards. (#16207)
Example
const mastra = new Mastra({ server: { a2a: { agentCardSigning: { privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!, protectedHeader: { alg: 'ES256', kid: 'agent-card-key', }, header: { issuer: 'mastra', }, }, }, }, });Mastra now conditionally signs served A2A Agent Cards via
signAgentCard(...)whenserver.a2a.agentCardSigningis configured, and the A2A Agent Card response schema now includes thesignaturesarray. -
Added
mastra api, a machine-readable runtime CLI for calling Mastra server resources with JSON input and output. (#16128)The new API CLI supports agents, workflows, tools, MCP servers, memory threads, working memory, observability traces/logs/scores, datasets, and experiments. It includes schema-aware request handling so a single JSON input is split into path, query, and body fields based on server route contracts, plus ergonomic raw-input wrapping for tool execution.
Exposed a route-derived server API schema manifest at runtime and generated CLI route metadata from it, enabling
--schemaoutput, response-shape-aware normalization, and server-aligned pagination output. -
Add
POST /api/agents/:agentId/resume-stream-until-idleSSE route, mirroringagent.resumeStreamUntilIdle(). (#16260) -
Worker review fixes: (#16309)
- Step-execution endpoint (
POST /workflows/:id/runs/:runId/steps/execute) is now gated by Mastra's standardrequiresAuth: true+authenticateTokenpipeline rather than a parallel "worker secret" body field. The previously introducedworkerSecretconfig knob andMASTRA_WORKER_SECRETenv var have been removed (they were never released). To gate the endpoint on a standalone-worker deployment, configure an auth provider on the server'sMastrainstance — without one the framework currently treatsrequiresAuth: trueas a no-op for this route. HttpRemoteStrategynow sends credentials as a normalAuthorization: Bearer <token>header. The token comes from the newMASTRA_WORKER_AUTH_TOKENenv var or an explicitauthconstructor option.- Honor the caller's
abortSignalinHttpRemoteStrategyby combining it with the per-request timeout viaAbortSignal.any(with a manual fallback for runtimes that don't expose it). - Implement comma-separated name filtering for the
MASTRA_WORKERSenv var.MASTRA_WORKERS=scheduler,backgroundTasksnow boots only those named workers;MASTRA_WORKERS=falsestill disables all workers. - Restore
Mastra.startEventEngine/stopEventEngineas@deprecatedaliases for the renamedstartWorkers/stopWorkers. BackgroundTaskWorkernow subscribes to PubSub instart()instead ofinit(), matching the lifecycle of the other workers and makingisRunningaccurately reflect subscription state.RedisStreamsPubSubadds amaxDeliveryAttemptsoption (default 5) that drops events after the configured number of failed deliveries instead of redelivering forever, and replaces emptycatch {}blocks withlogger.warn/logger.debugcalls.RedisStreamsPubSub.unsubscribe(topic, cb)now honors the topic argument so the same callback can be subscribed to multiple topics independently.PullTransportguards the async router callback against unhandled promise rejections by attaching a.catchthat nacks the message.- Drop the dead
MASTRA_WORKER_NAMEenv var injection in the CLI worker spawn (the bundle entrypoint already passes the worker name directly). - Add a real cross-process e2e auth suite
(
pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong token, missing token, anonymous direct hits, and the no-auth-provider pin-down behavior. - Step-execution route now has a response schema, satisfying
schema-consistency.test.ts. - Internal type cleanups (drop several
as anycasts in worker strategies andBackgroundTaskWorker). RedisStreamsPubSub.maxDeliveryAttemptsnow rejects negative / NaN values at construction.0still means "no cap" for back-compat but emits a one-time warning; passInfinityto disable the cap explicitly.PullTransportaccepts a logger and uses it for unhandled router-callback rejections instead ofconsole.error.BackgroundTaskWorker.start()now throws ifinit()was not called, matching the contract of the other workers.- Cross-process integration tests now spawn a single user-owned project
(
test-fixtures/cli-project/src/mastra/index.ts) through two generic entries that mirror whatBuildBundlerand `Worker
- Step-execution endpoint (
@mastra/core@1.32.0
Highlights
Fine-Grained Authorization (FGA) across Mastra (core + server adapters + MCP)
Mastra now supports relationship-based, resource-level authorization with centralized enforcement before agent runs (generate()/stream()), tool/workflow execution, and memory thread access via new IFGAProvider/IFGAManager + checkFGA()/FGADeniedError. FGA is also enforced consistently in @mastra/server handlers, all built-in server adapters (Express/Fastify/Hono/Koa), and MCP tool execution.
WorkOS-backed FGA provider (@mastra/auth-workos)
New MastraFGAWorkos implements IFGAManager on top of the WorkOS Authorization API, including check/require/filterAccessible, resource CRUD, role assignments, org scoping, JWT/bearer-token context handling, and permission/resource mappings for multi-tenant setups.
Scheduled Workflows (cron-native) + scheduler + full HTTP/Studio/client support
Workflows can now declare schedule in createWorkflow() to run on cron with automatic promotion to the evented engine, type-checked inputData/initialState/requestContext, and safe multi-instance claiming (CAS) via WorkflowScheduler. End-to-end support includes new schedules storage domains/adapters (PG/LibSQL/MongoDB + in-memory for tests), new @mastra/server routes + @mastra/client-js schedule methods, and new Studio UI for listing schedules, viewing trigger history, and durable pause/resume.
MCP Apps: interactive UI resources over MCP (SEP-1865) + resource listing/reading APIs
MCP servers can now publish ui:// HTML app resources via appResources, with new listResources()/readResource() on MCPServerBase and client/server endpoints (MastraClient.getMcpServerResources() / readMcpServerResource(), plus @mastra/server routes). @mastra/mcp adds proxy helpers (MCPClientServerProxy, toMCPServerProxies()) and stamps serverId into tool metadata to support multi-server UI resolution.
Observability upgrades: nested-run querying + bounded high-cardinality metrics + Datadog bridge
New listBranches/getBranch (plus getSpans and getStructure) let you find and fetch runs even when an entity only appears as a nested span (not root traces), with corresponding HTTP endpoints and store implementations (ClickHouse/DuckDB). Metrics now support count_distinct plus server-side TopK (limit, ordering) for fast dashboards on high-cardinality fields; ClickHouse also adds skip indexes to speed drilldowns, and a new DatadogBridge keeps auto-instrumented spans correctly nested under Mastra spans.
Breaking Changes
- Schedules storage schema changed (
mastra_schedules,mastra_schedule_triggers): addsownerType/ownerId, renamesScheduleTrigger.status→outcome, adds triggeridPK plustriggerKind/parentTriggerId/metadata;GET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus(no compat shim; scheduled workflows are alpha). @mastra/client-jsVector return types changed to match runtime:vector.getIndexes()→string[](was{ indexes: string[] })vector.upsert()→{ ids: string[] }(wasstring[])vector.query()→QueryResult[](was{ results: QueryResult[] })
Changelog
@mastra/core@1.32.0
Minor Changes
-
Added Fine-Grained Authorization (FGA) support for relationship-based, resource-level access control. FGA answers "can this user perform this action on this specific resource?" — enabling multi-tenant isolation and per-resource permissions. (#15410)
New interfaces:
IFGAProvider(read-only checks) andIFGAManager(read + write operations) with types for access checks, resources, and role assignments.Enforcement at all execution points: FGA checks are automatically enforced before agent execution (
generate(),stream()), tool execution, workflow execution, and memory thread access. When no FGA provider is configured, all checks are skipped (backward compatible).New utility:
checkFGA()provides centralized FGA enforcement withFGADeniedErrorfor denied checks.MastraMemory.checkThreadFGA()adds thread-level access control.Request-aware authorization: Resource ID resolvers receive request context so route-level FGA checks can derive tenant- or request-scoped resource IDs.
Typed permission constants: Strongly-typed permission identifiers (e.g.
'agents:execute','workflows:execute','memory:threads:read') for use in authorization config andpermissionMapping.const mastra = new Mastra({ server: { fga: new MastraFGAWorkos({ apiKey, clientId }), }, }); -
Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to
mastra_schedulesandmastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)Schedulegains optionalownerType/ownerIdso a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.ScheduleTrigger.statusis renamed tooutcomeand the type is widened toScheduleTriggerOutcomeso future outcome values can be added without another rename.ScheduleTriggergains a stableidprimary key and newtriggerKind,parentTriggerId, andmetadatafields.triggerKinddistinguishesschedule-firerows from laterqueue-drainrows (used by upcoming heartbeat work);parentTriggerIdlinks related rows;metadatacarries outcome-specific context.- The libsql, pg, and mongodb adapters all add the new columns/indexes. Their
@mastra/corepeer dependency is tightened to>=1.32.0-0 <2.0.0-0so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field. - Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The
triggersresponse onGET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus. - The bundled Studio (Mastra CLI) is updated to read
outcomeso the schedule detail page keeps polling and rendering publish-failure rows correctly.
-
Added
listResources()andreadResource()abstract methods toMCPServerBase, enabling MCP servers to expose app resources. These resources power interactive UI rendering (MCP Apps) in Studio and other consumers. (#16004) -
Added
count_distinctaggregation and server-side TopK to the metrics storage API so dashboards built on high-cardinality fields (likethreadIdorresourceId) stay fast and bounded. (#16137)New aggregation
getMetricAggregate,getMetricBreakdown, andgetMetricTimeSeriesacceptaggregation: 'count_distinct'with adistinctColumn. Backends pick the most efficient native implementation —uniqon ClickHouse,approx_count_distincton DuckDB.distinctColumnis restricted to a low/medium-cardinality categorical allowlist (entityType,entityName,parentEntityType,parentEntityName,rootEntityType,rootEntityName,name,provider,model,environment,executionSource,serviceName). ID columns are not allowed — distinct counts over near-unique values converge to the row count and are rarely useful.await store.getMetricAggregate({ name: ['mastra_llm_tokens_total'], aggregation: 'count_distinct', distinctColumn: 'model', filters: { timestamp: { start, end } }, });Server-side TopK
getMetricBreakdownacceptslimitandorderDirection, so breakdowns never return the full cardinality of a column from the database. Ordering is always by the aggregatedvalue;orderDirectionflips between top-N (DESC, default) and bottom-N (ASC).await store.getMetricBreakdown({ name: ['mastra_agent_duration_ms'], aggregation: 'sum', groupBy: ['threadId'], limit: 20, orderDirection: 'DESC', }); -
Added RegexFilterProcessor — a zero-cost regex-based content filter for blocking, redacting, or warning on pattern matches in agent messages. Includes built-in presets for PII, secrets, URLs, and prompt injection patterns. Supports input, output, and streaming phases. (#16058)
-
Added scheduled workflows. Declare a
scheduleoncreateWorkflowand Mastra fires the workflow on cron with no extra wiring. (#15830)import { createWorkflow } from '@mastra/core/workflows'; const dailyReport = createWorkflow({ id: 'daily-report', inputSchema: z.object({ date: z.string() }), outputSchema: z.object({ summary: z.string() }), schedule: { cron: '0 9 * * *', timezone: 'America/Los_Angeles', inputData: { date: 'today' }, }, }) .then(/* steps */) .commit();A workflow with a
scheduleis auto-promoted to the evented engine, so scheduled fires share the same execution path as manualstart()calls.inputData,initialState, andrequestContexton the schedule are type-checked against the workflow's schemas at definition time. Pass an array of schedules with stableids to fire one workflow on multiple crons.Mastra auto-instantiates a
WorkflowSchedulerwhen any registered workflow declares aschedule. The scheduler claims due schedules via compare-and-swap, so multiple instances polling the same storage cannot double-fire. Projects with no scheduled workflows pay zero cost. Configure withnew Mastra({ scheduler: { tickIntervalMs, batchSize, enabled, onError } }).Requires a storage adapter that implements the new
schedulesdomain (@mastra/libsqland@mastra/pgship adapters;InMemorySchedulesStorageis included for tests). Adds acronerdependency. -
Added MCP Apps extension support (SEP-1865). MCPServer now accepts an
appResourcesconfig to register interactiveui://HTML resources. MCPClient preserves full tool_meta(includingui.resourceUri) when converting MCP tools to Mastra tools. Both advertise theio.modelcontextprotocol/uiextension capability. (#16004)Example — MCPServer with app resources:
const server = new MCPServer({ name: 'my-server', tools: { myTool }, appResources: { dashboard: { name: 'Dashboard', description: 'Interactive dashboard UI', html: '<html>...</html>', }, }, }); -
Added
CostGuardProcessor, a built-in processor for enforcing monetary cost limits across agent runs. Supports run, resource, and thread scopes with configurable time windows (default 7 days), blocking or warning when limits are reached. Also addedonViolationcallback to the baseProcessorinterface for generalized violation handling across all processors. (#16057)import { Agent } from '@mastra/core/agent'; import { CostGuardProcessor } from '@mastra/core/processors'; const costGuard = new CostGuardProcessor({ maxCost: 5.0, scope: 'resource', window: '24h', strategy: 'block', }); costGuard.onViolation = ({ processorId, message, detail }) => { console.log(`[${processorId}] ${message}`, detail); }; const agent = new Agent({ name: 'my-agent', model: 'openai/gpt-5-nano', inputProcessors: [costGuard], }); -
Added
listBranchesandgetBranchfor querying named-entity invocations across traces, including nested ones.listTracesonly returns root-rooted traces, so an entity that always runs as a child (e.g., anObserveragent inside a workflow) wasn't queryable before. (#16154)// Before: nested-only entities returned nothing await store.listTraces({ filters: { entityName: 'Observer' } }); // [] // After: one row per AGENT_RUN, WORKFLOW_RUN, PROCESSOR_RUN, SCORER_RUN, // RAG_INGESTION, TOOL_CALL, or MCP_TOOL_CALL span await store.listBranches({ filters: { entityName: 'Observer' } }); // Plus: fetch the subtree at any span, with optional depth const branch = await store.getBranch({ traceId, spanId, depth: 1 });Added
getStructure({ traceId })(canonical name for the lightweight trace skeleton;getTraceLightretained as a deprecated alias) andgetSpans({ traceId, spanIds })(batch-fetch spans by id, used internally bygetBranchto avoid pulling whole traces).
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
6dcd65f) -
Fixed Harness token usage so provider-reported totals, reasoning tokens, and cache token fields are preserved. Fixes https://github.com/mastra-ai/mastra/issues/16055 (#16072)
-
Fixed supervisor output processors so they can filter streamed chunks from delegated sub-agents. (#16071)
-
Fixed Observational Memory model resolution for user-defined gateways. Models such as
cloudflare/google/gemini-2.5-flash-litenow resolve through registered gateways instead of failing with provider-config errors. Closes #13841. (#16083) -
Fixed model step traces to show the final prompt sent to the model, including memory-injected system messages. (#16029)
-
Fixed tool results dropping provider metadata from the original tool call. (#16078)
-
Fixed workflow request context serialization to skip values that cannot be safely stored as JSON. Fixes #16043. (#16061)
-
Fix buildResumedBlockResult returning suspended instead of failed when a step throws after resume in a parallel or conditional block (#14410)
-
Fixed serializeRequestContext to handle plain Map instances passed as requestContext, restoring backward compatibility broken in #16061 (#16081)
-
Added direct score lookup support to observability storage so score records can be fetched by
scoreIdwithout scanning paginated score lists, including DuckDB and ClickHouse vNext observability stores. (#16162) -
fix(harness): use type 'image' and mimeType for image parts in convertToHarnessMessage to fix Gemini image recognition (#13917)
-
Fixed TokenLimiterProcessor failing silently when no input messages fit the token budget. (#16063)
@mastra/auth-workos@1.2.0
Minor Changes
-
Added
MastraFGAWorkosprovider for Fine-Grained Authorization using the WorkOS Authorization API. ImplementsIFGAManagerinterface with support for: (#15410)- Authorization checks (
check(),require(),filterAccessible()) - Resource management (
createResource(),getResource(),listResources(),updateResource(),deleteResource()) - Role assignments (
assignRole(),removeRole(),listRoleAssignments()) resourceMappingandpermissionMappingfor translating Mastra resource types and permissions to WorkOS resource type slugs and permission slugs- Organization scoping that denies access when the user is not a member of the configured organization
- Bearer-token / verified JWT support that carries service-token FGA context such as organization membership IDs, while ignoring JWT-derived memberships unless organization claims are trusted
- Membership caching and batched accessible-resource discovery for lower per-request latency
- Tenant inference and parent-resource filtering for scoped access checks
- Paginated organization membership lookup and limited concurrent FGA checks when filtering accessible resources
- Typed permission constants accepted in
permissionMapping
import { MastraFGAWorkos } from '@mastra/auth-workos'; const fga = new MastraFGAWorkos({ organizationId: 'org_abc123', resourceMapping: { agent: { fgaResourceType: 'team', deriveId: ctx => ctx.user.teamId }, }, permissionMapping: { 'agents:execute': 'manage-workflows', }, }); // Check whether a user can execute an agent const allowed = await fga.check(user, { resource: { type: 'agent', id: 'my-agent' }, permission: 'agents:execute', }); - Authorization checks (
Patch Changes
@mastra/clickhouse@1.7.0
Minor Changes
-
Improved metric drilldown performance with skip indexes on the high-cardinality ID columns of
metric_events. Dashboard queries that filter metrics bytraceId,threadId,resourceId,userId,organizationId,experimentId,runId,sessionId, orrequestIdskip data chunks that don't contain the filtered value instead of scanning the full time range. (#16138)Equality (
=) andINfilters benefit automatically. Aggregations andGROUP BYqueries without a filter on these columns are unaffected.Migration
Existing deployments pick up the indexes on next start. The migration is metadata-only and instant — no table lock, no rewrite, no downtime. Insert overhead is negligible and index storage is well under 1% of table size. Existing data is indexed lazily as parts merge under normal retention; no operator action is required.
-
Added
count_distinctaggregation and server-side TopK to the metrics storage API so dashboards built on high-cardinality fields (likethreadIdorresourceId) stay fast and bounded. (#16137)New aggregation
getMetricAggregate,getMetricBreakdown, andgetMetricTimeSeriesacceptaggregation: 'count_distinct'with adistinctColumn. Backends pick the most efficient native implementation —uniqon ClickHouse,approx_count_distincton DuckDB.distinctColumnis restricted to a low/medium-cardinality categorical allowlist (entityType,entityName,parentEntityType,parentEntityName,rootEntityType,rootEntityName,name,provider,model,environment,executionSource,serviceName). ID columns are not allowed — distinct counts over near-unique values converge to the row count and are rarely useful.await store.getMetricAggregate({ name: ['mastra_llm_tokens_total'], aggregation: 'count_distinct', distinctColumn: 'model', filters: { timestamp: { start, end } }, });Server-side TopK
getMetricBreakdownacceptslimitandorderDirection, so breakdowns never return the full cardinality of a column from the database. Ordering is always by the aggregatedvalue;orderDirectionflips between top-N (DESC, default) and bottom-N (ASC).await store.getMetricBreakdown({ name: ['mastra_agent_duration_ms'], aggregation: 'sum', groupBy: ['threadId'], limit: 20, orderDirection: 'DESC', }); -
- Added
listBranchesandgetSpansimplementations. (#16154) - Only spans recorded after this version is deployed are queryable via
listBranches; historical traces remain accessible through the existinglistTraces/getTraceAPIs.
- Added
Patch Changes
- Added direct score lookup support to observability storage so score records can be fetched by
scoreIdwithout scanning paginated score lists, including DuckDB and ClickHouse vNext observability stores. (#16162)
@mastra/client-js@1.17.0
Minor Changes
-
Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to
mastra_schedulesandmastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)Schedulegains optionalownerType/ownerIdso a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.ScheduleTrigger.statusis renamed tooutcomeand the type is widened toScheduleTriggerOutcomeso future outcome values can be added without another rename.ScheduleTriggergains a stableidprimary key and newtriggerKind,parentTriggerId, andmetadatafields.triggerKinddistinguishesschedule-firerows from laterqueue-drainrows (used by upcoming heartbeat work);parentTriggerIdlinks related rows;metadatacarries outcome-specific context.- The libsql, pg, and mongodb adapters all add the new columns/indexes. Their
@mastra/corepeer dependency is tightened to>=1.32.0-0 <2.0.0-0so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field. - Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The
triggersresponse onGET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus. - The bundled Studio (Mastra CLI) is updated to read
outcomeso the schedule detail page keeps polling and rendering publish-failure rows correctly.
-
Added
getMcpServerResources()andreadMcpServerResource()methods toMastraClientfor listing and reading MCP server resources from the client SDK. These methods enable frontend applications to fetch app resource HTML for interactive MCP Apps rendering. (#16004)const client = new MastraClient(); // List resources on an MCP server const resources = await client.getMcpServerResources('my-server'); // Read a specific app resource const resource = await client.readMcpServerResource('my-server', 'ui://calculator/app'); -
Added schedule methods to the client for the new scheduled workflows feature. (#15830)
import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); const schedules = await client.listSchedules({ workflowId: 'daily-report' }); const schedule = await client.getSchedule('wf_daily-report'); const triggers = await client.listScheduleTriggers('wf_daily-report', { limit: 50 }); await client.pauseSchedule('wf_daily-report'); await client.resumeSchedule('wf_daily-report');Pause is durable across redeploys. Resume recomputes the next fire time from now so a long-paused schedule does not fire a backlog.
-
Added
listBranchesandgetBranchendpoints. Use these to find specific runs of an agent, workflow, tool, or processor — even when they are nested inside another trace — and to fetch the subtree of spans rooted at any single span. (#16177)GET /observability/branches?spanType=agent_run&entityName=Observer GET /observability/traces/:traceId/branches/:spanId?depth=1Each row in
listBranchesis a single anchor span (one ofAGENT_RUN,WORKFLOW_RUN,PROCESSOR_RUN,SCORER_RUN,RAG_INGESTION,TOOL_CALL,MCP_TOOL_CALL), so entities that always run as a child (e.g., anObserveragent inside a workflow) — previously not listable throughlistTraces— are now queryable via the HTTP API.getBranchaccepts an optionaldepth(0= anchor only; omitted = full subtree).Follow-up to https://github.com/mastra-ai/mastra/pull/16154 which added the underlying
@mastra/corestorage APIs. -
Fixed Vector resource return types so they match what the server actually returns. Previously the types declared shapes that did not exist at runtime, leading to runtime failures with no TypeScript errors. (#16036)
What changed
vector.getIndexes()now returnsstring[](was{ indexes: string[] })vector.upsert()now returns{ ids: string[] }(wasstring[])vector.query()now returnsQueryResult[](was{ results: QueryResult[] })
Before
const response = await client.getVector('docs').getIndexes(); console.log(response.indexes); // undefined at runtimeAfter
const indexes = await client.getVector('docs').getIndexes(); console.log(indexes[0]); // 'docs-index'Closes #15089.
Patch Changes
-
Fixed client stream handling for step completion and finish chunks that omit step result details. (#9146)
-
Added MCP Apps extension support (SEP-1865). MCPServer now accepts an
appResourcesconfig to register interactiveui://HTML resources. MCPClient preserves full tool_meta(includingui.resourceUri) when converting MCP tools to Mastra tools. Both advertise theio.modelcontextprotocol/uiextension capability. (#16004)Example — MCPServer with app resources:
const server = new MCPServer({ name: 'my-server', tools: { myTool }, appResources: { dashboard: { name: 'Dashboard', description: 'Interactive dashboard UI', html: '<html>...</html>', }, }, }); -
Added server-generated route contract types for the JavaScript client SDK and updated the SDK to use those generated request and response types. (#15519)
@mastra/convex@1.0.10
Patch Changes
-
Fixed
@mastra/convexworkflow snapshot persistence when snapshots contain$-prefixed JSON Schema keys (for example$schemaand$ref). (#16169) Snapshots are now stored safely, preventing Convex validation failures during workflow runs. Fixes#16110. -
Improved Convex bulk insert and delete throughput. (#16149)
@mastra/datadog@1.1.0
Minor Changes
-
Added a new
DatadogBridgeintegration for Mastra tracing so Datadog can keep auto-instrumented HTTP, database, and framework spans nested under the agent, workflow, model, and tool spans that triggered them. (#15716)import tracer from 'dd-trace'; tracer.init({ service: process.env.DD_SERVICE || 'my-mastra-app', env: process.env.DD_ENV || 'production', }); import { Mastra } from '@mastra/core'; import { Observability } from '@mastra/observability'; import { DatadogBridge } from '@mastra/datadog'; const mastra = new Mastra({ observability: new Observability({ configs: { default: { serviceName: 'my-mastra-app', bridge: new DatadogBridge({ mlApp: process.env.DD_LLMOBS_ML_APP!, }), }, }, }), });
Patch Changes
@mastra/duckdb@1.3.0
Minor Changes
-
- Added
listBranchesandgetSpansimplementations. (#16154) - Historical span data is queryable immediately; no migration required.
- Added
-
Added
count_distinctaggregation and server-side TopK to the metrics storage API so dashboards built on high-cardinality fields (likethreadIdorresourceId) stay fast and bounded. (#16137)New aggregation
getMetricAggregate,getMetricBreakdown, andgetMetricTimeSeriesacceptaggregation: 'count_distinct'with adistinctColumn. Backends pick the most efficient native implementation —uniqon ClickHouse,approx_count_distincton DuckDB.distinctColumnis restricted to a low/medium-cardinality categorical allowlist (entityType,entityName,parentEntityType,parentEntityName,rootEntityType,rootEntityName,name,provider,model,environment,executionSource,serviceName). ID columns are not allowed — distinct counts over near-unique values converge to the row count and are rarely useful.await store.getMetricAggregate({ name: ['mastra_llm_tokens_total'], aggregation: 'count_distinct', distinctColumn: 'model', filters: { timestamp: { start, end } }, });Server-side TopK
getMetricBreakdownacceptslimitandorderDirection, so breakdowns never return the full cardinality of a column from the database. Ordering is always by the aggregatedvalue;orderDirectionflips between top-N (DESC, default) and bottom-N (ASC).await store.getMetricBreakdown({ name: ['mastra_agent_duration_ms'], aggregation: 'sum', groupBy: ['threadId'], limit: 20, orderDirection: 'DESC', });
Patch Changes
-
Improved performance of
listTracesandlistBrancheson DuckDB. The Traces and Branches lists in the observability UI now load noticeably faster, especially on large span tables, because filtering and pagination happen up front and the store only assembles full span data for the rows on the page being viewed. (#16165)No API or behavior changes — return shapes and filter semantics are unchanged, and no migration is required.
-
Added direct score lookup support to observability storage so score records can be fetched by
scoreIdwithout scanning paginated score lists, including DuckDB and ClickHouse vNext observability stores. (#16162)
@mastra/editor@0.7.23
Patch Changes
-
Added MCP Apps extension support (SEP-1865). MCPServer now accepts an
appResourcesconfig to register interactiveui://HTML resources. MCPClient preserves full tool_meta(includingui.resourceUri) when converting MCP tools to Mastra tools. Both advertise theio.modelcontextprotocol/uiextension capability. (#16004)Example — MCPServer with app resources:
const server = new MCPServer({ name: 'my-server', tools: { myTool }, appResources: { dashboard: { name: 'Dashboard', description: 'Interactive dashboard UI', html: '<html>...</html>', }, }, });
@mastra/express@1.3.17
Patch Changes
- Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/fastify@1.3.17
Patch Changes
- Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/hono@1.4.12
Patch Changes
- Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/koa@1.5.0
Minor Changes
-
Improved the Koa adapter to make request routing more efficient as route counts grow. (#16050)
Requests now move through a leaner routing path with lower middleware overhead, which helps Koa-based Mastra servers stay faster and produce cleaner request traces without changing the public API.
Patch Changes
- Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/libsql@1.10.0
Minor Changes
-
Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to
mastra_schedulesandmastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)Schedulegains optionalownerType/ownerIdso a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.ScheduleTrigger.statusis renamed tooutcomeand the type is widened toScheduleTriggerOutcomeso future outcome values can be added without another rename.ScheduleTriggergains a stableidprimary key and newtriggerKind,parentTriggerId, andmetadatafields.triggerKinddistinguishesschedule-firerows from laterqueue-drainrows (used by upcoming heartbeat work);parentTriggerIdlinks related rows;metadatacarries outcome-specific context.- The libsql, pg, and mongodb adapters all add the new columns/indexes. Their
@mastra/corepeer dependency is tightened to>=1.32.0-0 <2.0.0-0so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field. - Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The
triggersresponse onGET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus. - The bundled Studio (Mastra CLI) is updated to read
outcomeso the schedule detail page keeps polling and rendering publish-failure rows correctly.
-
Added the
schedulesstorage domain so LibSQL-backed Mastra apps can use scheduled workflows. Createsmastra_schedulesandmastra_schedule_triggerstables on init. (#15830)
Patch Changes
@mastra/mcp@1.7.0
Minor Changes
-
Added MCP Apps support for interactive UI rendering over MCP. (#16004)
MCPClientServerProxy — a lightweight proxy that delegates resource and tool operations to remote MCP servers via
MCPClient, enabling Studio to fetch app resources from any connected server.toMCPServerProxies()— new convenience method onMCPClientthat creates proxy objects for all configured servers, ready for Mastra-level registration.Automatic
serverIdstamping — tools returned bylistTools()now carry_meta.ui.serverId, allowing consumers to resolveui://app resources from the correct MCP server in multi-server environments.const mcp = new MCPClient({ servers: { myApps: { url: new URL('https://my-mcp-server.example.com/mcp') }, }, }); const mastra = new Mastra({ agents: { myAgent }, mcpServers: { ...mcp.toMCPServerProxies() }, }); -
Added MCP Apps extension support (SEP-1865). MCPServer now accepts an
appResourcesconfig to register interactiveui://HTML resources. MCPClient preserves full tool_meta(includingui.resourceUri) when converting MCP tools to Mastra tools. Both advertise theio.modelcontextprotocol/uiextension capability. (#16004)Example — MCPServer with app resources:
const server = new MCPServer({ name: 'my-server', tools: { myTool }, appResources: { dashboard: { name: 'Dashboard', description: 'Interactive dashboard UI', html: '<html>...</html>', }, }, });
Patch Changes
-
Added Fine-Grained Authorization (FGA) enforcement to MCP tool execution. Both transport-driven calls and direct
executeTool()calls now run the same authorization checks when a request user is present, and typed FGA permission constants are accepted in MCP server authorization config. (#15410) -
Fixed trace parenting for long-lived MCP Stream connections. (#15716)
@mastra/memory@1.17.5
Patch Changes
-
Fixed Observational Memory model resolution for user-defined gateways. Models such as
cloudflare/google/gemini-2.5-flash-litenow resolve through registered gateways instead of failing with provider-config errors. Closes #13841. (#16083) -
Fixed async reflection buffering incorrectly triggering during idle timeout and provider-change activations when observation tokens are below the reflection activation threshold (#16076)
@mastra/mongodb@1.8.0
Minor Changes
-
Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to
mastra_schedulesandmastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)Schedulegains optionalownerType/ownerIdso a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.ScheduleTrigger.statusis renamed tooutcomeand the type is widened toScheduleTriggerOutcomeso future outcome values can be added without another rename.ScheduleTriggergains a stableidprimary key and newtriggerKind,parentTriggerId, andmetadatafields.triggerKinddistinguishesschedule-firerows from laterqueue-drainrows (used by upcoming heartbeat work);parentTriggerIdlinks related rows;metadatacarries outcome-specific context.- The libsql, pg, and mongodb adapters all add the new columns/indexes. Their
@mastra/corepeer dependency is tightened to>=1.32.0-0 <2.0.0-0so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field. - Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The
triggersresponse onGET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus. - The bundled Studio (Mastra CLI) is updated to read
outcomeso the schedule detail page keeps polling and rendering publish-failure rows correctly.
-
Added the
schedulesstorage domain so MongoDB-backed Mastra apps can use scheduled workflows. Createsmastra_schedulesandmastra_schedule_triggerscollections on init, with default indexes on(status, next_fire_at)for due-schedule polling and(schedule_id, actual_fire_at)for trigger-history queries. (#15830)
Patch Changes
@mastra/observability@1.11.1
Patch Changes
-
Fixed model step traces to show the final prompt sent to the model, including memory-injected system messages. (#16029)
-
Added a new
DatadogBridgeintegration for Mastra tracing so Datadog can keep auto-instrumented HTTP, database, and framework spans nested under the agent, workflow, model, and tool spans that triggered them. (#15716)import tracer from 'dd-trace'; tracer.init({ service: process.env.DD_SERVICE || 'my-mastra-app', env: process.env.DD_ENV || 'production', }); import { Mastra } from '@mastra/core'; import { Observability } from '@mastra/observability'; import { DatadogBridge } from '@mastra/datadog'; const mastra = new Mastra({ observability: new Observability({ configs: { default: { serviceName: 'my-mastra-app', bridge: new DatadogBridge({ mlApp: process.env.DD_LLMOBS_ML_APP!, }), }, }, }), }); -
Reduced startup noise: CloudExporter missing-token message is now logged at debug level instead of warn, since being disabled is the expected state for local development (#16070)
@mastra/pg@1.10.0
Minor Changes
-
Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to
mastra_schedulesandmastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)Schedulegains optionalownerType/ownerIdso a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.ScheduleTrigger.statusis renamed tooutcomeand the type is widened toScheduleTriggerOutcomeso future outcome values can be added without another rename.ScheduleTriggergains a stableidprimary key and newtriggerKind,parentTriggerId, andmetadatafields.triggerKinddistinguishesschedule-firerows from laterqueue-drainrows (used by upcoming heartbeat work);parentTriggerIdlinks related rows;metadatacarries outcome-specific context.- The libsql, pg, and mongodb adapters all add the new columns/indexes. Their
@mastra/corepeer dependency is tightened to>=1.32.0-0 <2.0.0-0so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field. - Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The
triggersresponse onGET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus. - The bundled Studio (Mastra CLI) is updated to read
outcomeso the schedule detail page keeps polling and rendering publish-failure rows correctly.
-
Added the
schedulesstorage domain so Postgres-backed Mastra apps can use scheduled workflows. Createsmastra_schedulesandmastra_schedule_triggerstables on init, with default indexes on(status, next_fire_at)for due-schedule polling and(schedule_id, actual_fire_at)for trigger-history queries. (#15830)
Patch Changes
@mastra/playground-ui@26.0.0
Minor Changes
-
Improved the
NoLogsInfoempty state. It now accepts optionaldatePreset,dateFrom, anddateToprops to show why no logs match the active range, suggests lowering the logging level, and links to the docs. Calling<NoLogsInfo />without props keeps the original copy. (#16139) -
Removed the deprecated
Notificationcomponent. UseNoticefor inline persistent context (errors, empty states) andtoast(from@mastra/playground-ui's sonner wrapper) for transient feedback (success messages, confirmations). (#16033)// Before <Notification isVisible={true} type="error">Failed to load.</Notification> // After — inline persistent context <Notice variant="destructive">Failed to load.</Notice> // Before <Notification isVisible={true}>Saved successfully!</Notification> // After — transient feedback toast.info('Saved successfully'); -
Added Studio UI for scheduled workflows. (#15830)
/workflows/scheduleslists every schedule across the project with the most recent run's status. Append?workflowId=<id>to filter to a single workflow./workflows/schedules/:scheduleIdshows the schedule's metadata, Pause/Resume controls, and paginated trigger history. Each trigger is deep-linked to its workflow run graph. The view polls every five seconds while any fired run is still active.- A workflow's detail header shows a Schedules action when it has at least one schedule.
-
Added SectionCard component to design system. Provides card primitive with tinted header strip (title, description, optional action slot), transparent body, and
default/dangervariants. ComposesCardHeadingfor typography. Suitable for settings pages, dashboard sections, and grouped form layouts. (#16168)import { SectionCard } from '@mastra/playground-ui'; <SectionCard title="Theme" description="Customize the appearance."> <ThemeSelector /> </SectionCard>; -
Redesigned the span token usage panel to show input vs output split with proportional bar and per-side detail breakdowns.
DataKeysAndValuesgained an optionaldensity='dense'prop. (#16143) -
Removed the
CombinedButtonscomponent. UseButtonsGroupwithspacing="close"for the same segmented-style cluster of toggle buttons. (#16035)// Before <CombinedButtons> <Button>Agent</Button> <Button>Model</Button> </CombinedButtons> // After <ButtonsGroup spacing="close"> <Button>Agent</Button> <Button>Model</Button> </ButtonsGroup>
Patch Changes
-
Added support for icon-and-description layout in
Noticeby makingtitleoptional. When omitted, the notice renders as a single row with icon and description, useful for inline contextual messages. (#16033)// Before — title required <Notice variant="info" title="Heads up">Some message.</Notice> // After — title optional, single-row layout <Notice variant="info">Some message.</Notice> -
Improved trace timeline span controls. Added tooltips with row counts to the expand-children, expand-all-descendants, and expand-at-this-level buttons. The expand-children button now collapses only the direct children rather than the entire subtree, and the descendants column gained a matching collapse-all-descendants action. Root spans show a single Expand all / Collapse all button using outward/inward double-chevrons. (#16173)
-
Refreshed toast styling so it aligns with the Notice component and lets sonner own the layout. (#16144)
What changed for users:
- Variant toasts (success / error / warning / info) now render with the same notice color tokens as the
<Notice>component, including bg, border and text color in both light and dark mode. - Sonner's native layout is back in charge — the loader on
toast.promise, the close button position, the icon placement and the mobile width all work as documented instead of fighting custom overrides. - The native close button has its own polished hover: it blends with the toast at rest and lifts with a tinted bg + stronger border on hover, in every variant and theme.
- Sticky toasts can be made truly non-dismissible by passing both
dismissible: falseandcloseButton: false. toast.success / error / warning / infonow return sonner's toast id (or an array of ids when called with an array of messages) so callers can keep dismissing or updating the toast they created.
- Variant toasts (success / error / warning / info) now render with the same notice color tokens as the
-
Added a Scorer span type style on the trace timeline and a colored type dot before each span name so spans are visually flagged in both the name and timing columns. (#16160)
-
Migrated Files/Skills tabs and agent page tabs (Chat/Editor/Evaluate/Review/Traces) to the design-system Tabs component for consistent styling and accessibility (Radix tablist, arrow-key navigation). Also added a
cursor-pointeron the Tab trigger and adisabledprop on the DS Tab. (#16148) -
Added MCP Apps extension support (SEP-1865). MCPServer now accepts an
appResourcesconfig to register interactiveui://HTML resources. MCPClient preserves full tool_meta(includingui.resourceUri) when converting MCP tools to Mastra tools. Both advertise theio.modelcontextprotocol/uiextension capability. (#16004)Example — MCPServer with app resources:
const server = new MCPServer({ name: 'my-server', tools: { myTool }, appResources: { dashboard: { name: 'Dashboard', description: 'Interactive dashboard UI', html: '<html>...</html>', }, }, }); -
Added an informational notice on the trace data panel pointing users to Mastra Studio (local or deployed) when "Evaluate Trace" and "Save as Dataset Item" actions are not available in the current view. (#16157)
-
Fixed pointer cursor on interactive form controls (Button, SelectTrigger, SelectItem) for better affordance. (#16140)
@mastra/react@0.2.34
Patch Changes
- Fixed custom data stream parts so stable ids are preserved in React UI messages. (#16067)
@mastra/server@1.32.0
Minor Changes
-
Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to
mastra_schedulesandmastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)Schedulegains optionalownerType/ownerIdso a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.ScheduleTrigger.statusis renamed tooutcomeand the type is widened toScheduleTriggerOutcomeso future outcome values can be added without another rename.ScheduleTriggergains a stableidprimary key and newtriggerKind,parentTriggerId, andmetadatafields.triggerKinddistinguishesschedule-firerows from laterqueue-drainrows (used by upcoming heartbeat work);parentTriggerIdlinks related rows;metadatacarries outcome-specific context.- The libsql, pg, and mongodb adapters all add the new columns/indexes. Their
@mastra/corepeer dependency is tightened to>=1.32.0-0 <2.0.0-0so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field. - Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The
triggersresponse onGET /api/schedules/:id/triggersnow returnsoutcomeinstead ofstatus. - The bundled Studio (Mastra CLI) is updated to read
outcomeso the schedule detail page keeps polling and rendering publish-failure rows correctly.
-
Added API endpoints for MCP server resources, enabling clients to list and read app resources for interactive UI rendering. (#16004)
GET /api/mcp/:serverId/resources— lists available resources on an MCP serverPOST /api/mcp/:serverId/resources/read— reads a specific resource by URI (e.g.ui://calculator/app)
-
Added HTTP routes for scheduled workflows. (#15830)
GET /api/schedules— list schedules across the project, optionally filtered byworkflowId.GET /api/schedules/:scheduleId— fetch a schedule with its most recent run summary.GET /api/schedules/:scheduleId/triggers— paginated trigger history joined to the corresponding workflow run.POST /api/schedules/:scheduleId/pauseandPOST /api/schedules/:scheduleId/resume— durable pause/resume. Both requireschedules:writeand are idempotent. Resume recomputesnextFireAtfrom now so a long-paused schedule does not fire a backlog.
-
Added
listBranchesandgetBranchendpoints. Use these to find specific runs of an agent, workflow, tool, or processor — even when they are nested inside another trace — and to fetch the subtree of spans rooted at any single span. (#16177)GET /observability/branches?spanType=agent_run&entityName=Observer GET /observability/traces/:traceId/branches/:spanId?depth=1Each row in
listBranchesis a single anchor span (one ofAGENT_RUN,WORKFLOW_RUN,PROCESSOR_RUN,SCORER_RUN,RAG_INGESTION,TOOL_CALL,MCP_TOOL_CALL), so entities that always run as a child (e.g., anObserveragent inside a workflow) — previously not listable throughlistTraces— are now queryable via the HTTP API.getBranchaccepts an optionaldepth(0= anchor only; omitted = full subtree).Follow-up to https://github.com/mastra-ai/mastra/pull/16154 which added the underlying
@mastra/corestorage APIs. -
Added Fine-Grained Authorization (FGA) enforcement across server handlers and memory APIs: (#15410)
- Route-level checks on detail endpoints, custom routes (including request-aware resource ID resolvers and path parameters), and resource-scoped search
- Thread-level checks on reads, writes, creation, cloning, message saving, and listing — with unviewable threads hidden from totals and pagination
- Message deletion now denies access when the message's thread cannot be verified
- Authenticated user context preserved through thread authorization, and the thread's owning
resourceIdforwarded into the FGA context so providers can derive composite tenant-scoped resource IDs - Route permission derivation and memory clone checks now use the correct resource context
- Typed FGA permission constants accepted in route and thread authorization config
Patch Changes
-
Fixed A2A task resubscribe to return the current task snapshot and continue streaming live artifact and status updates for in-progress tasks. (#15987)
-
Added an observability score lookup endpoint at
GET /observability/scores/:scoreIdbacked by observability storage. (#16162) -
Added MCP Apps extension support (SEP-1865). MCPServer now accepts an
appResourcesconfig to register interactiveui://HTML resources. MCPClient preserves full tool_meta(includingui.resourceUri) when converting MCP tools to Mastra tools. Both advertise theio.modelcontextprotocol/uiextension capability. (#16004)Example — MCPServer with app resources:
const server = new MCPServer({ name: 'my-server', tools: { myTool }, appResources: { dashboard: { name: 'Dashboard', description: 'Interactive dashboard UI', html: '<html>...</html>', }, }, }); -
Added server-generated route contract types for the JavaScript client SDK and updated the SDK to use those generated request and response types. (#15519)
@mastra/slack@1.1.1
Patch Changes
- Fixed Slack app creation failing when agent description exceeds 139 characters. The manifest description is now automatically truncated to prevent the Slack API from rejecting the request. (#16093)
@mastra/temporal@0.1.0
Minor Changes
-
Added the new
@mastra/temporalpackage for running Mastra workflows on Temporal. (#15789)What changed
- Added
init()to create Temporal-backed Mastra workflows and steps. - Added
MastraPluginto bundle workflow code for Temporal workers and load generated activities. - Added
debug: truesupport to write transformed workflow modules and emitted bundles to.mastra/temporal.
Example
import { init } from '@mastra/temporal'; import { MastraPlugin } from '@mastra/temporal/worker'; import { Client, Connection } from '@temporalio/client'; import { Worker } from '@temporalio/worker'; const connection = await Connection.connect(); const client = new Client({ connection }); const { createWorkflow, createStep } = init({ client, taskQueue: 'mastra' }); const step = createStep({ id: 'hello', execute: async () => 'world' }); export const helloWorkflow = createWorkflow({ id: 'hello-workflow' }).then(step); await Worker.create({ connection, taskQueue: 'mastra', plugins: [new MastraPlugin({ src: import.meta.resolve('./mastra/index.ts') })], }); - Added
Patch Changes
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.33
- @mastra/arize@1.0.23
- @mastra/arthur@0.2.9
- @mastra/braintrust@1.0.23
- @mastra/deployer@1.32.0
- @mastra/deployer-cloud@1.32.0
- @mastra/deployer-cloudflare@1.1.31
- @mastra/deployer-netlify@1.1.7
- @mastra/deployer-vercel@1.1.25
- @mastra/dynamodb@1.0.6
- @mastra/laminar@1.0.22
- @mastra/langfuse@1.2.5
- @mastra/langsmith@1.1.20
- @mastra/longmemeval@1.0.36
- @mastra/mcp-docs-server@1.1.33
- @mastra/nestjs@0.1.1
- @mastra/opencode@0.0.33
- @mastra/otel-bridge@1.0.22
- @mastra/otel-exporter@1.0.22
- @mastra/posthog@1.0.23
- @mastra/s3@0.5.1
- @mastra/s3vectors@1.0.5
- @mastra/sentry@1.0.22
- @mastra/voice-aws-nova-sonic@0.1.1
@mastra/core@1.31.0
Highlights
Platform Channels Framework (Core + Server + Client)
A new channels architecture adds ChannelProvider, a dedicated ChannelsStorage domain, and ChannelConnectResult connection flows (OAuth, deep link, immediate), with Mastra-level connect/disconnect/list APIs and matching MastraClient.channels methods.
Slack Channel Integration (@mastra/slack)
New Slack provider connects agents to Slack workspaces with OAuth-based app provisioning, manifest drift detection, encrypted credential storage, slash commands, and threaded conversation support.
NestJS Server Adapter (@mastra/nestjs)
New @mastra/nestjs adapter runs Mastra inside NestJS (Express) apps with native module registration, DI-friendly service injection, rate limiting, graceful shutdown, streaming, and MCP transport support; MastraServerBase is now exported to support adapters that manage routing.
Google Drive Workspace Filesystem (@mastra/google-drive)
New Google Drive WorkspaceFilesystem mounts a single Drive folder as an agent workspace, supporting OAuth tokens (with refresh callbacks) and service accounts, and implementing the full read/write/list/move/stat interface with optimistic concurrency via expectedMtime.
Real-time Voice Provider for AWS Nova Sonic (@mastra/voice-aws-nova-sonic)
Adds a bidirectional streaming voice provider for Bedrock Nova 2 Sonic with live mic streaming and playback, streaming transcription (speculative/final), barge-in detection, multi-voice selection, and tool calling with per-session RequestContext.
Breaking Changes
@mastra/playground-ui:IconButtonexport removed (useButtonwithsize="icon-*") and<Alert>removed in favor of<Notice>;variant="light"/"inputLike"replaced byvariant="default".
Changelog
@mastra/core@1.31.0
Minor Changes
-
Enhanced load_tool to accept an array of tool names, enabling bulk tool loading in a single call. Returns 'loaded', 'notFound', and 'alreadyLoaded' arrays for clearer response shape. (#15472)
-
Added Microsoft Entra ID authentication support for Azure OpenAI gateways, so Azure deployments can call models without API keys when using Azure SDK credentials. (#15983)
-
Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)
-
Added top-level
environmentconfig onMastrato tag observability signals with the deployment environment. (#15956)Set it once on the
Mastrainstance and it will be attached to all observability signals automatically. Falls back toprocess.env.NODE_ENVwhen unset; per-calltracingOptions.metadata.environmentstill takes precedence.Before
await agent.generate('hello', { tracingOptions: { metadata: { environment: process.env.NODE_ENV } }, });After
new Mastra({ environment: 'production', observability: new Observability({ ... }), })mastra.getEnvironment()returns the resolved value. -
Fixed trajectory scorers in dataset.startExperiment receiving raw agent messages instead of a Trajectory object, which caused a crash when accessing run.output.steps. Trajectory scorers now receive the same pre-extracted Trajectory that runEvals provides. (#15693)
The scorers option now also accepts the same categorised shape as runEvals (AgentScorerConfig / WorkflowScorerConfig), so you no longer need to rewrite your scorer config when moving from runEvals to dataset.startExperiment.
Before (trajectory scorer crashed at runtime):
await dataset.startExperiment({ scorers: [orderScorer] }) // run.output.steps was undefined
After (works correctly, both flat and categorised forms accepted):
await dataset.startExperiment({ scorers: [orderScorer] }) await dataset.startExperiment({ scorers: { agent: [accuracyScorer], trajectory: [orderScorer] } })
Per-step scorers are now also supported for workflow targets, matching
runEvals. Passscorers: { workflow: [...], steps: { stepId: [...] }, trajectory: [...] }to score individual workflow steps with their own scorers; results carry the originatingstepIdand keeptargetScope: 'span'(withtargetEntityType: WORKFLOW_STEPon the underlying scorer run), matching howrunEvalsencodes step identity. -
Workspace search now supports batch-capable embedders. Pass an embedder branded with
batch: true(and an optionalmaxBatchSize) to embed all pending chunks for a flush in a single provider call instead of one call per chunk. This dramatically reduces index-rebuild time on large workspaces when using providers that support batch embedding (e.g. OpenAI'sembedMany). Existing single-text embedders continue to work unchanged. (#14735)import { embedMany } from 'ai'; import { openai } from '@ai-sdk/openai'; const model = openai.embedding('text-embedding-3-small'); const workspace = new Workspace({ // ... embedder: Object.assign( async (texts: string[]) => { const { embeddings } = await embedMany({ model, values: texts }); return embeddings; }, { batch: true as const, maxBatchSize: 2048 }, ), });
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
1723e09) -
Fixed workflow runs not being cancellable when steps or conditions ignored the abort signal. Cancelling a run now correctly stops
dountil,dowhile, andforeachloops at every cancellation boundary — between iterations, after a step returns, after the loop condition is evaluated, and (forforeach) between concurrency chunks and after the final chunk. Previously, long-running loops (e.g. adountilwith asetTimeoutinside the step) would keep running and eventually emitsuccesseven after the run was cancelled. Closes #15990. (#15994) -
Fixed type inference on workflow loop helpers (
foreach,dowhile,dountil) so a step'srequestContextSchemacorrectly aligns with the workflow'srequestContextSchema. Previously these methods dropped the workflow'sTRequestContextfrom the step parameter, causing TypeScript to reject typed-context steps even when the workflow declared a matching schema. Steps without arequestContextSchemaare still accepted; steps whose schema does not match the workflow's now produce a type error. Fixes #15989. (#15995) -
Fixed sub-agent delegation so nested tool results stay out of the parent model context by default while remaining available to application code. Set
delegation.includeSubAgentToolResultsInModelContextto include the full subagent result in the parent model context. (#15832) -
Added a coalesced display state subscription API for Harness. (#15974)
This helps UI clients render fewer updates while still receiving the latest state. The example below renders the initial state, then subscribes to coalesced updates with the default
windowMsandmaxWaitMstiming options.render(harness.getDisplayState()); const unsubscribe = harness.subscribeDisplayState(render, { windowMs: 250, maxWaitMs: 500, }); -
Fixed BatchPartsProcessor using a hardcoded id in batched text-delta chunks. The real message id and runId are now preserved from the original chunks, preventing AI SDK UIMessage stream from dropping batched deltas. (#14974)
-
Add
filterAfterToolStepstoToolCallFilterso tool calls can be filtered during agentic loops after they are no longer recent. By default,ToolCallFilterkeeps its previous behavior and only filters the initial input. (#15795) -
Workspace search no longer throws when requesting hybrid or vector mode if the configuration does not support it. The search tool now gracefully falls back to the best available mode instead of throwing an error. (#14533)
-
Workspace file tools no longer use misleading absolute-path examples (e.g.
/data/output.txt) that caused weaker LLMs to attempt writes at the actual filesystem root. The example paths inread_fileandwrite_fileare now relative. (#14544)Additionally, when a contained workspace rejects an absolute path that escapes its boundary, the resulting
PermissionErrornow guides the agent toward a relative path so it can self-correct on the next turn. When the path's first segment names a real directory in the workspace (e.g./src/app.tswith an existingsrc/), the error suggests the exact relative form. Otherwise it falls back to a generic hint instead of inventing a misleading suggestion for genuinely out-of-workspace paths like/etc/passwd.Fixes #14542
-
Fixed
SkillSearchProcessorso agents use it as the on-demand skill discovery path without also adding eager skill context. (#15916)When
SkillSearchProcessoris configured, agents no longer auto-add the eagerSkillsProcessor, and they hide the overlappingskillandskill_searchtools while keepingskill_readavailable for supporting skill files. Workspace file tools can still readSKILL.mdfiles during explicit file inspection or editing workflows. -
Fixed tool calls to run in parallel when active tools exclude approval or suspending tools. (#15978)
-
- SearchEngine:
indexManyusesp-mapwith a default concurrency of 8 when vector embedding runs, with optionalconcurrencyandstopOnError(same semantics asp-map). Lazy vector indexing flushes pending documents at the same concurrency, drains the queue before awaiting so concurrentindexcalls are not dropped, loops until the queue is empty before search, dedupes by document id (last wins), and re-queues the batch if a flush throws. (#14735) - Workspace: Search auto-indexing reads files in parallel with a bounded concurrency, skips unreadable paths, awaits batch indexing, and falls back to per-file indexing when the batch path throws. Successful single-file indexing returns the path so callers can track what was indexed.
- SearchEngine:
-
Fix semantic recall indexing to honor read-only memory mode. (#15949)
-
Fixed Linux bubblewrap failing when Workspace mounts use symlinks under LocalSandbox by resolving mount paths to real directories for isolation allowlists. (#15498)
@mastra/arize@1.0.22
Patch Changes
-
Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)
gen_ai.usage.cached_input_tokens→gen_ai.usage.cache_read.input_tokensgen_ai.usage.cache_write_tokens→gen_ai.usage.cache_creation.input_tokens
gen_ai.usage.input_tokensis unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.
Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.
@mastra/arthur@0.2.8
Patch Changes
-
Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)
gen_ai.usage.cached_input_tokens→gen_ai.usage.cache_read.input_tokensgen_ai.usage.cache_write_tokens→gen_ai.usage.cache_creation.input_tokens
gen_ai.usage.input_tokensis unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.
Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.
@mastra/clickhouse@1.6.0
Minor Changes
-
Added ClickhouseStoreVNext, a ClickHouse storage adapter that uses the vNext observability domain by default. Equivalent to constructing a ClickhouseStore and overriding the observability domain manually, but exposed as a single class for new projects. (#15984)
import { Mastra } from '@mastra/core'; import { ClickhouseStoreVNext } from '@mastra/clickhouse'; export const mastra = new Mastra({ storage: new ClickhouseStoreVNext({ id: 'clickhouse-storage', url: process.env.CLICKHOUSE_URL!, username: process.env.CLICKHOUSE_USERNAME!, password: process.env.CLICKHOUSE_PASSWORD!, }), });ClickhouseStoreVNext accepts the same configuration as ClickhouseStore and reuses the same ClickHouse client across every domain. ClickhouseStore continues to work for projects on the legacy observability schema.
Patch Changes
@mastra/client-js@1.16.0
Minor Changes
- Added Channels resource to MastraClient with listPlatforms, listInstallations, connect, and disconnect methods for managing platform channel integrations. (#15876)
Patch Changes
@mastra/convex@1.0.9
Patch Changes
- Fixed Convex workflow snapshot loads to use the workflow/run index. (#15971)
@mastra/dynamodb@1.0.5
Patch Changes
- Fixed an issue where automatic thread title generation was skipped when using the DynamoDB storage adapter. The adapter was overwriting empty thread titles with a
Thread <id>placeholder on save, which prevented the title-generation step (gated on an empty title) from running. Empty titles now round-trip correctly so generated titles work the same as with other storage adapters. Resolves #15998. (#16003)
@mastra/fastify@1.3.16
Patch Changes
-
Fix multipart file handling in Fastify adapter by aligning return type with other adapters and preventing stream hang on file size limit. (#15796)
-
Fix multipart upload tests to register the multipart content-type parser. The tests were manually adding the preHandler hook but skipping
registerContextMiddleware(), which meant Fastify rejectedmultipart/form-datarequests with 415 Unsupported Media Type. (#16002)
@mastra/google-drive@0.1.0
Minor Changes
-
Add
@mastra/google-drive, a new Google DriveWorkspaceFilesystemprovider that mounts a single Drive folder as an agent workspace. Supports OAuth access tokens, async refresh callbacks, and service account (JWT) authentication. Implements the fullWorkspaceFilesysteminterface — read, write, list, copy, move, mkdir, rmdir, stat, exists — plusexpectedMtimeoptimistic concurrency. (#15756)import { Agent } from '@mastra/core/agent'; import { Workspace } from '@mastra/core/workspace'; import { GoogleDriveFilesystem } from '@mastra/google-drive'; const workspace = new Workspace({ filesystem: new GoogleDriveFilesystem({ folderId: process.env.GOOGLE_DRIVE_FOLDER_ID!, accessToken: process.env.GOOGLE_DRIVE_ACCESS_TOKEN!, }), }); const agent = new Agent({ id: 'drive-agent', name: 'Drive Agent', model: 'openai/gpt-4o-mini', workspace, });A matching
googleDriveFilesystemProviderdescriptor is also exported for MastraEditor.
Patch Changes
- GoogleDriveFilesystem tweaks: mkdir defaults to recursive, appendFile uses optimistic concurrency, rmdir skips redundant child listing, JSON body requests include Content-Type header, readFile uses consistent searchParams, and concurrent token refreshes are deduplicated. (#16010)
@mastra/libsql@1.9.1
Patch Changes
- Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)
@mastra/mongodb@1.7.4
Patch Changes
-
Removed unsupported
minScorequery option from MongoDB vector store docs and README. ExportedMongoDBQueryVectorParamsso callers can typedocumentFilterforMongoDBVector.query(). (#15936)Fixes #15715
@mastra/nestjs@0.1.0
Minor Changes
-
Add NestJS server adapter (
@mastra/nestjs) for running Mastra with NestJS Express applications. Provides native module registration, DI-based service injection, rate limiting, graceful shutdown, streaming, and MCP transport support. (#12751)import { Module } from '@nestjs/common'; import { MastraModule } from '@mastra/nestjs'; import { mastra } from './mastra'; @Module({ imports: [MastraModule.register({ mastra })], }) export class AppModule {}
Patch Changes
@mastra/observability@1.11.0
Minor Changes
- Auto-attach the Mastra-level
environmentto all observability signals. (#15956)
Patch Changes
@mastra/otel-exporter@1.0.21
Patch Changes
-
Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)
gen_ai.usage.cached_input_tokens→gen_ai.usage.cache_read.input_tokensgen_ai.usage.cache_write_tokens→gen_ai.usage.cache_creation.input_tokens
gen_ai.usage.input_tokensis unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.
Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.
@mastra/perplexity@0.1.0
Minor Changes
-
Added new
@mastra/perplexityintegration with the Perplexity Search tool for agents. (#15939)import { createPerplexityTools } from '@mastra/perplexity'; const { perplexitySearch } = createPerplexityTools({ apiKey: process.env.PERPLEXITY_API_KEY });
Patch Changes
@mastra/pg@1.9.4
Patch Changes
-
Fixed workflow snapshot sanitization in
@mastra/pgfor strings containing escaped surrogate patterns like[^\ud800-\udfff]. This prevents invalid JSON escape sequences that caused PostgreSQLjsonbwrites to fail with error22P02. (#15923)Fixes #15920
-
Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)
@mastra/playground-ui@25.0.0
Minor Changes
-
Refactored Button component to use a single
cva(class-variance-authority) variant config instead of nested manual maps. ConsolidatedIconButtonintoButtonviasize="icon-sm|icon-md|icon-lg"and removed theIconButtonexport. Replacedvariant="light"andvariant="inputLike"withvariant="default"(no behavior change for default styling). Addedctaandoutlinevariants and unified active/hover styles between text- and icon-mode buttons. (#15985)Why: A single source of truth for variants means consistent visuals, fewer drift bugs, simpler maintenance, and a more predictable surface for AI agents — single-variant cva is the dominant shadcn pattern across DS components in this repo (
Card,Input,Label,Textarea,StatusBadge).Migration:
// Before import { IconButton } from '@mastra/playground-ui'; <IconButton><Settings /></IconButton> <Button variant="light">…</Button> <Combobox variant="inputLike" /> // After import { Button } from '@mastra/playground-ui'; <Button size="icon-md"><Settings /></Button> <Button variant="default">…</Button> <Combobox variant="default" /> -
Removed
<Alert>in favor of<Notice>. The two components had significant visual and behavioral overlap;<Notice>is now the single banner primitive and supports every previous<Alert>use case. (#15791)<Notice>is also redesigned with a flatter API:titleandiconare now props, each variant ships a default icon, an optionalactionprop renders a button aligned to the title, and a newnotevariant has been added alongsidewarning,destructive,info, andsuccess. Theme tokens (notice-warning,notice-destructive,notice-info,notice-success,notice-note) replace the previous hardcoded colors.Migration
// Before <Alert variant="warning"> <AlertTitle>Provider not connected</AlertTitle> <AlertDescription as="p">Set the API key environment variable.</AlertDescription> </Alert> // After <Notice variant="warning" title="Provider not connected"> <Notice.Message>Set the API key environment variable.</Notice.Message> </Notice>
Patch Changes
-
Removed the "Avg Score" KPI card from the Metrics dashboard and the avg-score summary from the Scores card. (#15967)
-
Fixed row click behavior in the dataset experiments compare view. Clicking a row while selection mode is active now toggles the row's selection instead of navigating to the experiment. Clicking directly on the checkbox no longer also triggers the row click handler. (#15492)
-
Aligned AlertDialog visual styling with Dialog component for design system consistency. AlertDialog now uses the same surface tokens, border radius, shadow, animation curves, and typography scale as Dialog. The accessibility primitive remains separate (preserves
role="alertdialog"and explicit Action/Cancel semantics) — only the visual shell was synced. Also addedAlertDialog.Bodyfor parity with Dialog. (#15988)
@mastra/react@0.2.33
Patch Changes
- Fixed suspended tool run IDs not being preserved after page refresh. (#15107)
@mastra/sentry@1.0.21
Patch Changes
-
Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)
gen_ai.usage.cached_input_tokens→gen_ai.usage.cache_read.input_tokensgen_ai.usage.cache_write_tokens→gen_ai.usage.cache_creation.input_tokens
gen_ai.usage.input_tokensis unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.
Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.
@mastra/server@1.31.0
Patch Changes
-
Fix
GET /tools/:toolIdandPOST /tools/:toolId/executeto find dynamically-resolved agent tools (provided viatoolsResolver/ function-basedtools) when they are not in the static tool registry. Errors thrown by an individual agent'slistTools()during the lookup are now logged as warnings instead of being silently swallowed. (#13989) -
Fixed memory query validation when optional JSON query params are omitted with newer Zod versions. (#15969)
-
Export
MastraServerBasefrom@mastra/core/serverso framework adapters that manage routing independently can share the same server base class. (#12751) -
Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)
@mastra/slack@1.1.0
Minor Changes
-
Added @mastra/slack channel integration for connecting AI agents to Slack workspaces. Provides automatic Slack app provisioning via OAuth, manifest management with drift detection, encrypted credential storage, slash command support, and threaded conversation handling. Usage: (#15876)
import { SlackProvider } from '@mastra/slack'; const mastra = new Mastra({ channels: { slack: new SlackProvider({ refreshToken: process.env.SLACK_APP_CONFIG_REFRESH_TOKEN!, }), }, }); // Connect an agent to Slack const result = await mastra.channels.slack.connect('my-agent'); // result.type === 'oauth' → redirect user to result.authorizationUrl
Patch Changes
@mastra/voice-aws-nova-sonic@0.1.0
Minor Changes
-
Add new
@mastra/voice-aws-nova-sonicvoice provider for AWS Bedrock Nova 2 Sonic. (#13232)The provider exposes a real-time bidirectional voice interface backed by the
InvokeModelWithBidirectionalStreamCommandAPI on AWS Bedrock, including:- Live microphone streaming (
send/listen) and assistant audio playback viaspeakingevents - Live transcription via
writingevents withSPECULATIVE/FINALgeneration stages - Barge-in / interrupt detection
- Speaker selection across all 18 Nova Sonic voices and configurable endpointing sensitivity
- Tool calling with per-session
RequestContext - Configurable AWS region, model id, credentials (or default credential provider chain), and inference / turn-detection parameters
- Live microphone streaming (
Patch Changes
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/braintrust@1.0.22
- @mastra/datadog@1.0.22
- @mastra/deployer@1.31.0
- @mastra/deployer-cloud@1.31.0
- @mastra/deployer-cloudflare@1.1.30
- @mastra/deployer-netlify@1.1.6
- @mastra/deployer-vercel@1.1.24
- @mastra/express@1.3.16
- @mastra/hono@1.4.11
- @mastra/koa@1.4.16
- @mastra/laminar@1.0.21
- @mastra/langfuse@1.2.4
- @mastra/langsmith@1.1.19
- @mastra/longmemeval@1.0.35
- @mastra/mcp-docs-server@1.1.32
- @mastra/opencode@0.0.32
- @mastra/otel-bridge@1.0.21
- @mastra/posthog@1.0.22
@mastra/core@1.30.0
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
Minor Changes
-
Add durable agents with resumable streams (#12557)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- 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) -
Update provider registry and model documentation with latest models and providers (
d587199) -
Fix MCP client support in the agent editor: (#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
Patch Changes
-
Remove incorrect deprecation markers from
getTask()andcancelTask()in the Mastra A2A client. (#15941) -
Add durable agents with resumable streams (#12557)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- Inngest-powered execution
@mastra/editor@0.7.22
Patch Changes
- Fix MCP client support in the agent editor: (#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
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)
@mastra/inngest@1.3.0
Minor Changes
-
Add durable agents with resumable streams (#12557)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- Inngest-powered execution
-
Update peer dependencies to match core package version bump (1.0.5) (#12557)
Patch Changes
@mastra/memory@1.17.4
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)
@mastra/observability@1.10.3
Patch Changes
-
Add durable agents with resumable streams (#12557)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- 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)
@mastra/playground-ui@24.0.2
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.SideDialogandAlertDialogpick up the refined ambient shadow as well, since they share the same shadow style. (#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)
-
Refined the DataPanel loading state with a smaller spinner and tightened layout for a less prominent appearance. (#15965)
@mastra/redis@1.1.0
Minor Changes
- Update peer dependencies to match core package version bump (1.0.5) (#12557)
Patch Changes
-
Add durable agents with resumable streams (#12557)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- Inngest-powered execution
@mastra/server@1.30.0
Minor Changes
- Update peer dependencies to match core package version bump (1.0.5) (#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) -
Fix MCP client support in the agent editor: (#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)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- Inngest-powered execution
-
Fix A2A streaming to emit incremental artifact updates from the agent full stream while preserving final structured output artifacts. (#15941)
@mastra/upstash@1.1.0
Minor Changes
- Update peer dependencies to match core package version bump (1.0.5) (#12557)
Patch Changes
-
Add durable agents with resumable streams (#12557)
Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.
The Problem
Standard agent streaming has two fragility points:
- 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.
- 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
Agentwith durability using factory functions: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
// 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 eventsPubSub and Cache
Durable agents use two infrastructure components:
Component Purpose Default PubSub Real-time event delivery during streaming EventEmitterPubSubCache Stores events for replay on reconnection InMemoryServerCacheWhen
stream()is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. Whenobserve()is called, missed events replay from cache before continuing with live events.Configure via Mastra instance (recommended):
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):
const durableAgent = createDurableAgent({ agent, cache: new RedisServerCache({ url: 'redis://...' }), pubsub: new RedisPubSub({ url: 'redis://...' }), });Disable caching (streams won't be resumable):
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
DurableAgentextendsAgent- base class with resumable streamsEventedAgentextendsDurableAgent- fire-and-forget executionInngestAgentextendsDurableAgent- Inngest-powered execution
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.32
- @mastra/arize@1.0.21
- @mastra/arthur@0.2.7
- @mastra/braintrust@1.0.21
- @mastra/datadog@1.0.21
- @mastra/deployer@1.30.0
- @mastra/deployer-cloud@1.30.0
- @mastra/deployer-cloudflare@1.1.29
- @mastra/deployer-netlify@1.1.5
- @mastra/deployer-vercel@1.1.23
- @mastra/express@1.3.15
- @mastra/fastify@1.3.15
- @mastra/koa@1.4.15
- @mastra/laminar@1.0.20
- @mastra/langfuse@1.2.3
- @mastra/langsmith@1.1.18
- @mastra/longmemeval@1.0.34
- @mastra/mcp-docs-server@1.1.31
- @mastra/opencode@0.0.31
- @mastra/otel-bridge@1.0.20
- @mastra/otel-exporter@1.0.20
- @mastra/posthog@1.0.21
- @mastra/react@0.2.32
- @mastra/sentry@1.0.20
@mastra/core@1.27.0
Highlights
CLI-Driven Browser Automation + Screencasts
@mastra/core and the new @mastra/browser-viewer package add end-to-end browser automation for CLI-based agent workflows. BrowserViewer launches Chrome via Playwright with remote debugging, and a new BrowserCliHandler automatically detects browser CLIs (agent-browser, browser-use, browse) and injects the CDP URL into commands — no manual wiring needed. Browser sessions are thread-isolated with automatic lifecycle management, and live screencasts stream directly to Studio. External CDP endpoints (e.g. browser-use cloud) are also supported: the system detects them, skips injection, and connects for screencast.
S3 “Prefix Mounts” Across Workspace Providers
Workspace packages (@mastra/s3, @mastra/daytona, @mastra/e2b, and @mastra/blaxel) now support mounting an S3 subdirectory via a prefix option, so sandboxes can expose only a folder within a bucket instead of the entire bucket.
More Robust Workflow Resumes for Parallel foreach
Fixes a workflow snapshot/resume bug where parallel foreach iterations could lose their suspendPayload when a sibling iteration resumed—important for HITL/tool-approval flows that rely on preserved per-iteration stream state.
Observational Memory Improvements (Temporal Markers + Correctness Fixes)
Adds opt-in temporal-gap markers (observationalMemory.temporalMarkers: true) to inject persisted <system-reminder type="temporal-gap"> when users return after 10+ minutes, and fixes duplication/conflicts by force-disabling savePerStep when observational memory is enabled.
New Package: @mastra/tavily
New integration package wrapping @tavily/core as first-class Mastra tools — createTavilySearchTool, createTavilyExtractTool, createTavilyCrawlTool, and createTavilyMapTool — with full Zod input/output schemas, lazy client initialization, and a convenience createTavilyTools() that returns all four with shared config. API key resolves from config or TAVILY_API_KEY env var.
Breaking Changes
- None noted in this changelog.
Changelog
@mastra/core@1.27.0
Minor Changes
-
Added support for CLI-driven browser automation with screencast support in
@mastra/core, including automatic CDP injection for browser CLIs. (#15415)Fixed local process spawning so workspace-relative
cwdvalues no longer get duplicated.
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
f112db1) -
Fixed
foreachparallel iterations losing theirsuspendPayloadwhen a sibling iteration was resumed. Previously, every result entry written back to the workflow snapshot had itssuspendPayloadcleared, so iterations that were still suspended (e.g. parallel tool-call approvals each carrying an agent's__streamState) lost the context they needed to resume correctly. Suspended iterations now retain theirsuspendPayloadacross resume cycles; completed iterations still have it cleared to keep snapshots small. (#15551)const approvalWorkflow = createWorkflow({ id: "approve" }).foreach(approveToolStep, { concurrency: 5 }).commit(); // Before: resuming the first approval wiped streamState on the others, // so subsequent resumes lost conversation context. // After: each suspended iteration keeps its suspendPayload (including // streamState) until it is individually resumed. -
Fixed interaction between savePerStep and observational memory that caused message duplication. The saveStepMessages method redundantly re-added response messages to the message list on every step, duplicating them. Additionally, savePerStep is now force-disabled when observational memory is enabled, since OM handles its own per-step persistence and the two features conflict. (#15652)
-
Added opt-in temporal-gap markers for observational memory. When enabled via
observationalMemory.temporalMarkers: true, the agent receives a<system-reminder type="temporal-gap">before any user message that arrives more than 10 minutes after the previous one, so it can anchor responses in real elapsed time. Markers are persisted, surfaced to the observer, and rendered by the MastraCode TUI on reload. (#15605)
@mastra/agent-builder@1.0.28
Patch Changes
- Hide internal log during
mastra devstartup (that was previously already hidden but got exposed again by a recent change) (#15616)
@mastra/blaxel@0.3.0
Minor Changes
-
Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the
prefixoption on your S3 filesystem. (#15171)Example:
const fs = new S3Filesystem({ bucket: "my-bucket", region: "us-east-1", prefix: "workspace/data", accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY! });When mounted in a sandbox, only the contents under
workspace/data/in the bucket will be visible at the mount path. This uses the s3fsbucket:/pathsyntax under the hood.Closes #15147.
Patch Changes
@mastra/browser-viewer@0.1.0
Minor Changes
-
Initial release of @mastra/browser-viewer (#15415)
Playwright-based browser viewer for CLI providers that enables screencast visualization in Studio. Supports thread-isolated browser sessions and automatic CDP connection management.
import { BrowserViewer } from "@mastra/browser-viewer"; const workspace = new Workspace({ sandbox: new LocalSandbox({ cwd: "./workspace" }), browser: new BrowserViewer({ cli: "agent-browser", headless: false }) });
Patch Changes
@mastra/clickhouse@1.5.1
Patch Changes
-
Fixed
mastra devrepeatedly reportingMIGRATION REQUIREDon ClickHouse Cloud aftermastra migratehad already run successfully. The observability migration check now recognizes the engine-name variants that ClickHouse Cloud and replicated clusters use in place ofReplacingMergeTree. (#15623) -
Improved ClickHouse v-next observability initialization errors to include the underlying ClickHouse message in the standard error text. This makes init failures actionable in loggers that only print
error.message. (#15588)
@mastra/daytona@0.3.0
Minor Changes
-
Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the
prefixoption on your S3 filesystem. (#15171)Example:
const fs = new S3Filesystem({ bucket: "my-bucket", region: "us-east-1", prefix: "workspace/data", accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY! });When mounted in a sandbox, only the contents under
workspace/data/in the bucket will be visible at the mount path. This uses the s3fsbucket:/pathsyntax under the hood.Closes #15147.
Patch Changes
@mastra/e2b@0.2.0
Minor Changes
-
Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the
prefixoption on your S3 filesystem. (#15171)Example:
const fs = new S3Filesystem({ bucket: "my-bucket", region: "us-east-1", prefix: "workspace/data", accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY! });When mounted in a sandbox, only the contents under
workspace/data/in the bucket will be visible at the mount path. This uses the s3fsbucket:/pathsyntax under the hood.Closes #15147.
Patch Changes
@mastra/fastify@1.3.11
Patch Changes
- Fix custom route handlers on the Fastify adapter silently overwriting request-body fields named
tools(e.g.POST /stored/agents,POST /stored/workspaces). The adapter now exposes registered tools asregisteredToolsin handler params, matching the Express and Hono adapters and the@mastra/serverhandler contract. (#15635)
@mastra/memory@1.17.0
Minor Changes
- Added opt-in temporal-gap markers for observational memory. When enabled via
observationalMemory.temporalMarkers: true, the agent receives a<system-reminder type="temporal-gap">before any user message that arrives more than 10 minutes after the previous one, so it can anchor responses in real elapsed time. Markers are persisted, surfaced to the observer, and rendered by the MastraCode TUI on reload. (#15605)
Patch Changes
- Fixed observer agent truncation that could cut UTF-16 surrogate pairs in half when formatting messages, tool results, or observation lines with emoji or other astral-plane characters. This produced lone surrogates that strict JSON parsers (including Anthropic's) reject with errors like
no low surrogate in string, causing observer runs to fail. (#15634)
@mastra/s3@0.4.0
Minor Changes
-
Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the
prefixoption on your S3 filesystem. (#15171)Example:
const fs = new S3Filesystem({ bucket: "my-bucket", region: "us-east-1", prefix: "workspace/data", accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY! });When mounted in a sandbox, only the contents under
workspace/data/in the bucket will be visible at the mount path. This uses the s3fsbucket:/pathsyntax under the hood.Closes #15147.
Patch Changes
@mastra/server@1.27.0
Patch Changes
-
Forward
requestContextfrom the/approve-tool-call,/decline-tool-call,/approve-tool-call-generateand/decline-tool-call-generateREST handlers toagent.approveToolCall(...)/declineToolCall(...)/approveToolCallGenerate(...)/declineToolCallGenerate(...). (#15620)Previously
requestContextwas destructured from the handler arguments but never passed through. On resume,dynamicInstructionsran withrequestContext: undefined, so any value placed on the per-requestRequestContextby upstream middleware (or bybody.requestContextauto-merge) was lost for the rest of the turn. Agents whose prompt assembly depends on request-scoped data (e.g. read-only state from the frontend) produced blank or placeholder responses after the user approved a HITL tool call. Other agent entry points (stream,generate) already forwardedrequestContextcorrectly; this brings the approval routes in line. -
Fixed screencast panel staying "Live" after browser closes due to an error. The
ViewerRegistrynow broadcastsbrowser_closedstatus when a screencast stream emits an error, not just when it stops cleanly. (#15415)
@mastra/tavily@1.0.1
Patch Changes
- Fixed runtime
ERR_MODULE_NOT_FOUNDfor@tavily/coreby making it a direct dependency. Consumers no longer need to install@tavily/coremanually. (#15628)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/client-js@1.14.1
- @mastra/deployer@1.27.0
- @mastra/deployer-cloud@1.27.0
- @mastra/deployer-cloudflare@1.1.25
- @mastra/deployer-netlify@1.1.1
- @mastra/deployer-vercel@1.1.19
- @mastra/editor@0.7.18
- @mastra/express@1.3.11
- @mastra/hono@1.4.6
- @mastra/koa@1.4.11
- @mastra/longmemeval@1.0.30
- @mastra/mcp-docs-server@1.1.27
- @mastra/opencode@0.0.27
- @mastra/playground-ui@23.0.1
- @mastra/react@0.2.28
@mastra/core@1.24.0
Highlights
End-to-end RAG Tracing + New RAG/Graph Span Types
RAG ingestion and query operations are now visible in Mastra tracing with new span types (e.g., RAG_INGESTION, RAG_EMBEDDING, RAG_VECTOR_OPERATION, RAG_ACTION, GRAPH_ACTION) plus helpers like startRagIngestion() / withRagIngestion(). Instrumentation is opt-in via an observabilityContext, and @mastra/rag automatically threads context from agent TOOL_CALL spans into vector and graph tools.
CloudExporter Now Ships All Observability Signals (Not Just Traces)
@mastra/observability CloudExporter can now batch and upload logs, metrics, scores, and feedback in addition to tracing spans, enabling a single exporter path to Mastra Cloud for all signals. This also changes the endpoint configuration to use a base collector URL and derive publish paths automatically.
Span Filtering to Reduce Observability Noise and Cost
excludeSpanTypes and spanFilter were added to ObservabilityInstanceConfig in both @mastra/core and @mastra/observability, allowing you to drop entire span categories (e.g., MODEL_CHUNK) or apply predicate-based filtering before export—useful for pay-per-span backends.
AI SDK v6 Message Support + Message Interop Helpers
@mastra/core MessageList can now accept AI SDK v6 UI/model messages and project stored messages via messageList.get.all.aiV6.ui(), supporting v6 approval request/response flows. @mastra/ai-sdk adds toAISdkMessages() to load stored Mastra messages into AI SDK v5 or v6 chat UIs.
Better Reliability and Debuggability Across Logs + MCP + Memory
Observability log correlation is fixed so logs inside agent runs carry the active span correlation fields (restoring trace↔log linking), and deepClean() now applies to all signals and better preserves Map/Set/Error detail. MCP tool discovery now retries after reconnectable errors and the MCP server returns spec-correct 404s for stale sessions; memory recall gains more precise browsing (partType, toolName, threadId: "current", anchor paging), and message parts now include createdAt timestamps for accurate part-level timing.
Breaking Changes
- CloudExporter endpoint format changed: configure a base
endpointURL (publisher paths are derived automatically) when using CloudExporter for Mastra Cloud uploads.
Changelog
@mastra/core@1.24.0
Minor Changes
-
Added
excludeSpanTypesandspanFilteroptions toObservabilityInstanceConfigfor selectively filtering spans before export. UseexcludeSpanTypesto drop entire categories of spans by type (e.g.,MODEL_CHUNK,MODEL_STEP) orspanFilterfor fine-grained predicate-based filtering by attributes, metadata, entity, or any combination. Both options help reduce noise and costs in observability platforms that charge per-span. (#15131)excludeSpanTypesexample:excludeSpanTypes: [SpanType.MODEL_CHUNK, SpanType.MODEL_STEP, SpanType.WORKFLOW_SLEEP];spanFilterexample:spanFilter: span => { if (span.type === SpanType.MODEL_CHUNK) return false; if (span.type === SpanType.TOOL_CALL && span.attributes?.success) return false; return true; }; -
Add RAG observability (#10898) (#15137)
Surfaces RAG ingestion and query operations in Mastra's AI tracing.
New span types in
@mastra/core/observability:RAG_INGESTION(root) — wraps an ingestion pipeline runRAG_EMBEDDING— embedding call (used by ingestion and query)RAG_VECTOR_OPERATION— vector store I/O (query/upsert/delete/fetch)RAG_ACTION—chunk/extract_metadata/rerankGRAPH_ACTION— non-RAG graphbuild/traverse/update/prune
New helpers exported from
@mastra/core/observability:startRagIngestion(opts)— manual: returns{ span, observabilityContext }withRagIngestion(opts, fn)— scoped: runsfn(observabilityContext), attaches the return value as the span's output, routes thrown errors tospan.error(...)
Wired in
@mastra/rag:vectorQuerySearchemitsRAG_EMBEDDING(mode:query) andRAG_VECTOR_OPERATION(operation:query)rerank/rerankWithScoreremitRAG_ACTION(action:rerank)MDocument.chunkemitsRAG_ACTION(action:chunk) andRAG_ACTION(action:extract_metadata)createGraphRAGToolemitsGRAPH_ACTION(action:build/traverse)createVectorQueryToolandcreateGraphRAGToolthreadobservabilityContextfrom the agent'sTOOL_CALLspan automatically
All new instrumentation is opt-in: functions accept an optional
observabilityContextand no-op when absent, so existing callers are unaffected.
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
8db7663) -
Added AI SDK v6 UI message support to MessageList in @mastra/core. (#14592)
MessageList can now accept AI SDK v6 UI and model messages in add(...), and project stored messages with messageList.get.all.aiV6.ui(). This adds first-class handling for v6 approval request and response message flows.
-
Fix observability log correlation: logs emitted from inside an agent run were being persisted with
entityId,runId,traceId, and the other correlation fields set tonull, breaking trace ↔ log linking in Mastra Studio and downstream observability tools. Logs now correctly carry the active span's correlation context end to end. (#15148) -
Added
createdAttimestamps to message parts in message history. (#15121)Message parts now keep their own creation timestamps so downstream code can preserve part-level timing instead of relying only on the parent message timestamp.
After:
{ type: 'text', text: 'hello', createdAt: 1712534400000 }
@mastra/ai-sdk@1.3.3
Patch Changes
-
Added toAISdkMessages() for loading stored Mastra messages into AI SDK v5 or v6 chat UIs. (#14592)
Use the default v5 behavior or pass { version: 'v6' } when your app is typed against AI SDK v6 useChat() message types.
@mastra/client-js@1.13.2
Patch Changes
- Fixed the client JS Responses types to allow omitting the model override. (#15140)
@mastra/datadog@1.0.15
Patch Changes
- Fix Datadog LLM Observability span kinds for model spans so traces match Datadog's expected shape. (#15149)
- Each call to a model now shows up as an
llmspan in Datadog (previously the per-call spans were reported astask, so Datadog's "Model Calls" count was wrong and per-call inputs/outputs were not rendered as messages). - The wrapper around a generation is now reported as a
workflowspan instead ofllm, so it no longer looks like an extra LLM call. - Token usage and cost are reported only on the per-call
llmspans, so Datadog no longer double-counts tokens against the wrapper. - Per-call
llmspans inheritmodelNameandmodelProviderfrom their parent generation, so the model is still attached in the Datadog UI.
- Each call to a model now shows up as an
@mastra/loggers@1.1.1
Patch Changes
- Removed 'component' field from pino-pretty log output to reduce noise in CLI logs (#15146)
@mastra/mcp@1.4.2
Patch Changes
-
Improved MCP tool discovery to retry once after reconnectable connection errors like
Connection closedduringtools/list. (#15141)MCPClient.listToolsets(),listToolsetsWithErrors(), andlistTools()now attempt a reconnect before treating transient discovery failures as missing tools. -
Fixed MCP server to return HTTP 404 (instead of 400) when a client sends a stale or unknown session ID. Per the MCP spec, this tells clients to re-initialize with a new session, which fixes broken tool calls after server redeploys. (#15160)
@mastra/memory@1.15.0
Minor Changes
-
Updated the recall tool to support more precise message browsing for agents. (#15116)
Agents using
recallcan now passpartTypeandtoolNameto narrow message results to specific parts, such as tool calls or tool results for one tool. This change also addsthreadId: "current"support across recall modes andanchor: "start" | "end"for no-cursor message paging, making it easier to inspect recent thread activity and past tool usage.
Patch Changes
-
Fixed reflection threshold not respecting per-record overrides set via the PATCH API. Previously, lowering the reflection threshold for a specific record had no effect on the actual reflection trigger — only the default 40k threshold was used. Now per-record overrides are correctly applied in both sync and async reflection paths. (#15170)
-
Improved observational memory formatting to use part timestamps when rendering dates and times. (#15121)
Observer history now follows part-level timing more closely, so the rendered memory context is more accurate when messages contain parts created at different times.
-
Fixed message history doubling when using Observational Memory with the Mastra gateway. The local ObservationalMemoryProcessor now detects when the agent's model is routed through the Mastra gateway and skips its input/output processing, since the gateway handles OM server-side. (#15161)
@mastra/observability@1.8.0
Minor Changes
-
Added CloudExporter support for Mastra Observability logs, metrics, scores, and feedback. (#15124)
CloudExporter now batches and uploads all Mastra Observability signals to Mastra Cloud, not just tracing spans.
This includes a breaking change to the CloudExporter endpoint format. We now pass a base endpoint URL and let let the exporter derive the standard publish paths automatically.
import { CloudExporter, Observability } from '@mastra/observability'; const observability = new Observability({ configs: { default: { serviceName: 'my-app', exporters: [ new CloudExporter({ endpoint: 'https://collector.example.com', }), ], }, }, }); // Traces, logs, metrics, scores, and feedback now all publish through CloudExporter.After updating the exporter endpoint config, the exporter will continue to work for traces, and the same exporter will now also publish structured logs, auto-extracted metrics, scores, and feedback records.
-
Added
excludeSpanTypesandspanFilteroptions toObservabilityInstanceConfigfor selectively filtering spans before export. UseexcludeSpanTypesto drop entire categories of spans by type (e.g.,MODEL_CHUNK,MODEL_STEP) orspanFilterfor fine-grained predicate-based filtering by attributes, metadata, entity, or any combination. Both options help reduce noise and costs in observability platforms that charge per-span. (#15131)excludeSpanTypesexample:excludeSpanTypes: [SpanType.MODEL_CHUNK, SpanType.MODEL_STEP, SpanType.WORKFLOW_SLEEP];spanFilterexample:spanFilter: span => { if (span.type === SpanType.MODEL_CHUNK) return false; if (span.type === SpanType.TOOL_CALL && span.attributes?.success) return false; return true; };
Patch Changes
-
ObservabilityBus now honors per-instance
serializationOptions(maxStringLength, maxDepth, maxArrayLength, maxObjectKeys) when deep-cleaning log/metric/score/feedback payloads, matching the behavior of tracing spans. Previously these signals always used the built-in defaults regardless of user configuration. (#15138) -
Apply
deepClean()to all observability signals (logs, metrics, scores, feedback) before fanning out to exporters and bridges. Previously only tracing spans were deep-cleaned at construction time, leaving free-form payload fields on other signals (e.g.log.data,log.metadata,metric.metadata,metric.costContext.costMetadata,score.metadata,feedback.metadata) susceptible to circular references, oversized strings, and other non-serializable values. Sanitization now happens centrally inObservabilityBus.emit()so every signal leaving the bus is bounded and JSON-safe. (#15135) -
deepClean()now preserves data forMap,Set, and richerErrorobjects. Previously Maps and Sets were serialized as empty{}(entries silently dropped) and Errors only keptname/message. Maps are now converted to plain objects of entries, Sets to arrays (both respectingmaxObjectKeys/maxArrayLengthand cycle detection), and Errors additionally preservestackand recursively cleanedcause. (#15136)
@mastra/playground-ui@22.1.0
Minor Changes
-
Search input can now be collapsed into a compact icon button with tooltip and auto-focuses when expanded (#15130)
-
Added DataKeysAndValues component — a compound component for displaying key-value pairs in a grid layout with support for single or two-column modes and section headers (#15126)
-
Added DateTimeRangePicker component — a date range selector with preset options (last 24h, 7d, 30d, etc.) and a custom range mode with dual calendar and time pickers (#15128)
-
Added DataCodeSection component — a read-only code viewer with JSON syntax highlighting, search, multiline toggle, and an expandable fullscreen dialog (#15125)
-
Added DataPanel compound component — a container for detail panels with header, navigation, close button, loading, and empty states (#15127)
-
New inline Traces page replacing the old dialog-based Observability page. Trace, span, and score details now open in stacked side panels instead of full-screen dialogs. URL deep-linking supports traceId, spanId, tab, and scoreId params. Includes new TracesDataList, DataList.Pagination, DataList.Subheader components, and Evaluate Trace / Save as Dataset Item actions. (#15139)
Patch Changes
-
Fixed publishing older agent versions (#15154)
Fixed agent editor to allow publishing older read-only versions. Previously, the Publish button was disabled when viewing a previous version. Now a "Publish This Version" button appears, enabling users to set any older version as the published version.
Fixed Publish button being clickable without a saved draft
The Publish button is now disabled until a draft version is saved. Previously, making edits would enable the Publish button even without a saved draft, which caused an error when clicked.
Eliminated spurious 404 error logs for code-only agents
The agent versions endpoint now checks both code-registered and stored agents before returning 404, and the frontend conditionally fetches stored agent details only when versions exist. This prevents noisy error logs when navigating to the editor for agents that haven't been published yet.
Changed editor sections to be collapsed by default
The System Prompt, Tools, and Variables sections in the agent editor are now collapsed by default when navigating to the editor page.
@mastra/rag@2.2.0
Minor Changes
-
Add RAG observability (#10898) (#15137)
Surfaces RAG ingestion and query operations in Mastra's AI tracing.
New span types in
@mastra/core/observability:RAG_INGESTION(root) — wraps an ingestion pipeline runRAG_EMBEDDING— embedding call (used by ingestion and query)RAG_VECTOR_OPERATION— vector store I/O (query/upsert/delete/fetch)RAG_ACTION—chunk/extract_metadata/rerankGRAPH_ACTION— non-RAG graphbuild/traverse/update/prune
New helpers exported from
@mastra/core/observability:startRagIngestion(opts)— manual: returns{ span, observabilityContext }withRagIngestion(opts, fn)— scoped: runsfn(observabilityContext), attaches the return value as the span's output, routes thrown errors tospan.error(...)
Wired in
@mastra/rag:vectorQuerySearchemitsRAG_EMBEDDING(mode:query) andRAG_VECTOR_OPERATION(operation:query)rerank/rerankWithScoreremitRAG_ACTION(action:rerank)MDocument.chunkemitsRAG_ACTION(action:chunk) andRAG_ACTION(action:extract_metadata)createGraphRAGToolemitsGRAPH_ACTION(action:build/traverse)createVectorQueryToolandcreateGraphRAGToolthreadobservabilityContextfrom the agent'sTOOL_CALLspan automatically
All new instrumentation is opt-in: functions accept an optional
observabilityContextand no-op when absent, so existing callers are unaffected.
Patch Changes
@mastra/server@1.24.0
Patch Changes
-
Fixed publishing older agent versions (#15154)
Fixed agent editor to allow publishing older read-only versions. Previously, the Publish button was disabled when viewing a previous version. Now a "Publish This Version" button appears, enabling users to set any older version as the published version.
Fixed Publish button being clickable without a saved draft
The Publish button is now disabled until a draft version is saved. Previously, making edits would enable the Publish button even without a saved draft, which caused an error when clicked.
Eliminated spurious 404 error logs for code-only agents
The agent versions endpoint now checks both code-registered and stored agents before returning 404, and the frontend conditionally fetches stored agent details only when versions exist. This prevents noisy error logs when navigating to the editor for agents that haven't been published yet.
Changed editor sections to be collapsed by default
The System Prompt, Tools, and Variables sections in the agent editor are now collapsed by default when navigating to the editor page.
-
Fixed the Responses API to use the agent default model when create requests omit model. (#15140)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.25
- @mastra/arize@1.0.15
- @mastra/arthur@0.2.1
- @mastra/braintrust@1.0.15
- @mastra/deployer@1.24.0
- @mastra/deployer-cloud@1.24.0
- @mastra/deployer-cloudflare@1.1.21
- @mastra/deployer-netlify@1.0.25
- @mastra/deployer-vercel@1.1.15
- @mastra/express@1.3.7
- @mastra/fastify@1.3.7
- @mastra/hono@1.4.2
- @mastra/koa@1.4.7
- @mastra/laminar@1.0.14
- @mastra/langfuse@1.1.1
- @mastra/langsmith@1.1.12
- @mastra/longmemeval@1.0.26
- @mastra/mcp-docs-server@1.1.23
- @mastra/opencode@0.0.23
- @mastra/otel-bridge@1.0.14
- @mastra/otel-exporter@1.0.14
- @mastra/posthog@1.0.15
- @mastra/react@0.2.24
- @mastra/sentry@1.0.14
@mastra/core@1.16.0
Highlights
Smarter Model Selection for Observational Memory
@mastra/memory now lets you route observer and reflector calls to different models based on input size using ModelByInputTokens. Short inputs can go to a fast, cheap model while longer ones get sent to a more capable one -- all configured declaratively with token thresholds. Tracing shows which model was selected and why.
MongoDB Support for Datasets and Experiments
@mastra/mongodb now stores versioned datasets with full item history and time-travel queries, plus experiment results and CRUD. If you're already using MongoDBStore, this works automatically with no extra setup.
Okta Auth and RBAC
New @mastra/auth-okta package brings SSO authentication and role-based access control via Okta. Map Okta groups to Mastra permissions, verify JWTs against Okta's JWKS endpoint, and manage sessions -- or pair Okta RBAC with a different auth provider like Auth0 or Clerk.
Breaking Changes
- None called out in this changelog.
Changelog
@mastra/core@1.16.0
Minor Changes
-
Added dataset-agent association and experiment status tracking for the Evaluate workflow. (#14470)
- Dataset targeting: Added
targetTypeandtargetIdsfields to datasets, enabling association with agents, scorers, or workflows. Datasets can now be linked to multiple entities. - Experiment status: Added
statusfield to experiment results ('needs-review','reviewed','complete') for review queue workflow. - Dataset experiment routes: Added API endpoints for triggering experiments from a dataset with configurable target type and target ID.
- LLM data generation: Added endpoint for generating dataset items using an LLM with configurable count and prompt.
- Failure analysis: Added endpoint for clustering experiment failures and proposing tags using LLM analysis.
- Dataset targeting: Added
-
Added agent version support for experiments. When triggering an experiment, you can now pass an
agentVersionparameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)const client = new MastraClient(); await client.triggerDatasetExperiment({ datasetId: "my-dataset", targetType: "agent", targetId: "my-agent", version: 3, // pin to dataset version 3 agentVersion: "ver_abc123" // pin to a specific agent version }); -
Added tool suspension handling to the Harness. (#14611)
When a tool calls
suspend()during execution, the harness now emits atool_suspendedevent, reportsagent_endwith reason'suspended', and exposesrespondToToolSuspension()to resume execution with user-provided data.harness.subscribe((event) => { if (event.type === "tool_suspended") { // event.toolName, event.suspendPayload, event.resumeSchema } }); // Resume after collecting user input await harness.respondToToolSuspension({ resumeData: { confirmed: true } }); -
Added
agentIdto the agent tool execution context. Tools executed by an agent can now accesscontext.agent.agentIdto identify which agent is calling them. This enables tools to look up agent metadata, share workspace configuration with sub-agents, or customize behavior per agent. (#14502) -
Improved observability metrics and logs storage support. (#14607)
- Added typed observability storage fields for shared correlation context and cost data.
- Added storage-layer metric listing and richer metric aggregations that can return estimated cost alongside values.
- Improved observability filter parity across log and metric storage APIs.
-
Add optional
?path=query param to workspace skill routes for disambiguating same-named skills. (#14430)Skill routes continue to use
:skillNamein the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional?path=query parameter to select the exact skill:GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelinesSkillMetadatanow includes apathfield, and thelist()method returns all same-named skills for disambiguation. The client SDK'sgetSkill()accepts an optionalskillPathparameter for disambiguation. -
Added
ModelByInputTokensin@mastra/memoryfor token-threshold-based model selection in Observational Memory. (#14614)When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.
Example usage:
import { Memory, ModelByInputTokens } from "@mastra/memory"; const memory = new Memory({ options: { observationalMemory: { model: new ModelByInputTokens({ upTo: { 10_000: "google/gemini-2.5-flash", 40_000: "openai/gpt-4o", 1_000_000: "openai/gpt-4.5" } }) } } });The
upTokeys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
68ed4e9) -
Fixed
Harness.destroy()to properly clean up heartbeats and workspace on teardown. (#14568) -
Fixed null detection in tool input validation to check actual values at failing paths instead of relying on error message string matching. This ensures null values from LLMs are correctly handled even when validators produce error messages that don't contain the word "null" (e.g., "must be string"). Fixes #14476. (#14496)
-
Fixed missing tool lists in agent traces for streaming runs. Exporters like Datadog LLM Observability now receive the tools available to the agent. (#14550)
-
Fix consecutive tool-only loop iterations being merged into a single assistant message block. When the agentic loop runs multiple iterations that each produce only tool calls, the LLM would misinterpret them as parallel calls from a single turn. A
step-startboundary is now inserted between iterations to ensure they are treated as sequential steps. (#14652) -
Improved custom OpenAI-compatible model configuration guidance in the models docs. (#14594)
-
Added client/server body schemas for feedback and scores that omit the timestamp field, allowing it to be set server-side (#14470)
-
Workspace skills now surface all same-named skills for disambiguation. (#14430)
When multiple skills share the same name (e.g., a local
brand-guidelinesskill and one fromnode_modules),list()now returns all of them instead of only the tie-break winner. This lets agents and UIs see every available skill, along with its path and source type.Tie-breaking behavior:
get(name)still returns a single skill using source-type priority: local > managed > external- If two skills share the same name and source type,
get(name)throws an error — rename one or move it to a different source type get(path)bypasses tie-breaking entirely and returns the exact skill
Agents and UIs now receive all same-named skills with their paths, which improves disambiguation in prompts and tool calls.
const skills = await workspace.skills.list(); // Returns both local and external "brand-guidelines" skills const exact = await workspace.skills.get("node_modules/@myorg/skills/brand-guidelines"); // Fetches the external copy directly by path -
Fixed Anthropic 'tool_use ids were found without tool_result blocks immediately after' error. When client tools (e.g. execute_command) and provider tools (e.g. web_search) are called in parallel, the tool ordering in message history could cause Anthropic to reject subsequent requests, making the thread unrecoverable. Tool blocks are now correctly split to satisfy Anthropic's ordering requirements. (#14648)
-
Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)
Mastra agent and client APIs accept schemas from either
zod/v3orzod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.
@mastra/ai-sdk@1.2.1
Patch Changes
-
Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)
Mastra agent and client APIs accept schemas from either
zod/v3orzod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.
@mastra/auth-okta@0.0.2
Patch Changes
- fix(auth-okta): harden security defaults and address code review feedback (#14553)
- Fix cache poisoning: errors in
fetchGroupsFromOktanow propagate so the outer.catchevicts the entry and retries on next request - Reduce cookie size: only store user claims, id_token (for logout), and expiry — access/refresh tokens are no longer stored, keeping cookies under the 4KB browser limit
- Add
id_token_hintto logout URL (required by Okta) - Add console.warn for auto-generated cookie password and in-memory state store in production
- Document missing env vars (
OKTA_CLIENT_SECRET,OKTA_REDIRECT_URI,OKTA_COOKIE_PASSWORD) in README and examples - Expand
MastraAuthOktaOptionsdocs to include all fields (session config, scopes, etc.) - Fix test to actually exercise
getUserIdcross-provider lookup path
- Fix cache poisoning: errors in
@mastra/client-js@1.10.0
Minor Changes
- Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14470)
Patch Changes
-
Added client SDK methods for dataset experiments and item generation. (#14470)
- Added
triggerExperiment()method to dataset resources for running experiments with configurable target type and ID - Added
generateItems()method for LLM-powered test data generation - Added
clusterFailures()method for analyzing experiment failures - Added TypeScript types for new dataset and experiment API payloads
- Added
-
Added agent version support for experiments. When triggering an experiment, you can now pass an
agentVersionparameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)const client = new MastraClient(); await client.triggerDatasetExperiment({ datasetId: "my-dataset", targetType: "agent", targetId: "my-agent", version: 3, // pin to dataset version 3 agentVersion: "ver_abc123" // pin to a specific agent version }); -
Add optional
?path=query param to workspace skill routes for disambiguating same-named skills. (#14430)Skill routes continue to use
:skillNamein the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional?path=query parameter to select the exact skill:GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelinesSkillMetadatanow includes apathfield, and thelist()method returns all same-named skills for disambiguation. The client SDK'sgetSkill()accepts an optionalskillPathparameter for disambiguation. -
Updated skill search result types and query parameters to use
skillName/skillNamesinstead ofskillPath/skillPathsfor consistency with the name-based public API. (#14430) -
Added storage type detection to the Metrics Dashboard. The
/system/packagesendpoint now returnsobservabilityStorageType, identifying the observability storage backend. The dashboard shows an empty state when the storage does not support metrics (e.g. PostgreSQL, LibSQL), and displays a warning when using in-memory storage since metrics are not persisted across server restarts. Also added a docs link button to the Metrics page header. (#14620)import { MastraClient } from "@mastra/client-js"; const client = new MastraClient(); const system = await client.getSystemPackages(); // system.observabilityStorageType contains the class name of the observability store: // - 'ObservabilityInMemory' → metrics work but are not persisted across restarts // - 'ObservabilityPG', 'ObservabilityLibSQL', etc. → metrics not supported if (system.observabilityStorageType === "ObservabilityInMemory") { console.warn("Metrics are not persisted — data will be lost on server restart."); } const SUPPORTED = new Set(["ObservabilityInMemory"]); if (!SUPPORTED.has(system.observabilityStorageType ?? "")) { console.error("Metrics require in-memory observability storage."); } -
Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)
Mastra agent and client APIs accept schemas from either
zod/v3orzod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.
@mastra/datadog@1.0.9
Patch Changes
- Fixed error info tags being recorded as [object Object] in Datadog. Error details (message, id, domain, category) are now stored as separate flattened tags (error.message, error.id, error.domain, error.category) instead of a nested object, making error information properly visible in Datadog LLM Observability. (#14570)
@mastra/deployer@1.16.0
Patch Changes
- Inject MASTRA_EXPERIMENTAL_UI environment variable into the studio HTML shell during build and deploy. (#14547)
@mastra/deployer-vercel@1.1.8
Patch Changes
- Inject MASTRA_EXPERIMENTAL_UI environment variable into the studio HTML shell during build and deploy. (#14547)
@mastra/express@1.3.0
Minor Changes
-
Added adapter auth middleware helpers for raw framework routes. (#14458)
Use
createAuthMiddleware({ mastra })when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. SetrequiresAuth: falsewhen you need to reuse the same helper chain on a public route.app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/fastify@1.3.0
Minor Changes
-
Added adapter auth middleware helpers for raw framework routes. (#14458)
Use
createAuthMiddleware({ mastra })when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. SetrequiresAuth: falsewhen you need to reuse the same helper chain on a public route.app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/hono@1.3.0
Minor Changes
-
Added adapter auth middleware helpers for raw framework routes. (#14458)
Use
createAuthMiddleware({ mastra })when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. SetrequiresAuth: falsewhen you need to reuse the same helper chain on a public route.app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/koa@1.4.0
Minor Changes
-
Added adapter auth middleware helpers for raw framework routes. (#14458)
Use
createAuthMiddleware({ mastra })when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. SetrequiresAuth: falsewhen you need to reuse the same helper chain on a public route.app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/libsql@1.7.2
Patch Changes
-
Added storage support for dataset targeting and experiment status fields. (#14470)
- Added
targetType(text) andtargetIds(jsonb) columns to datasets table for entity association - Added
tags(jsonb) column to datasets table for tag vocabulary - Added
statuscolumn to experiment results for review workflow tracking - Added migration logic to add new columns to existing tables
- Added
-
Added agent version support for experiments. When triggering an experiment, you can now pass an
agentVersionparameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)const client = new MastraClient(); await client.triggerDatasetExperiment({ datasetId: "my-dataset", targetType: "agent", targetId: "my-agent", version: 3, // pin to dataset version 3 agentVersion: "ver_abc123" // pin to a specific agent version });
@mastra/memory@1.10.0
Minor Changes
-
Added
ModelByInputTokensin@mastra/memoryfor token-threshold-based model selection in Observational Memory. (#14614)When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.
Example usage:
import { Memory, ModelByInputTokens } from "@mastra/memory"; const memory = new Memory({ options: { observationalMemory: { model: new ModelByInputTokens({ upTo: { 10_000: "google/gemini-2.5-flash", 40_000: "openai/gpt-4o", 1_000_000: "openai/gpt-4.5" } }) } } });The
upTokeys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.
Patch Changes
- Fixed observational memory reflection compression for
google/gemini-2.5-flashby using stronger compression guidance and starting it at a higher compression level during reflection.google/gemini-2.5-flashis unusually good at generating long, faithful outputs. That made reflection retries more likely to preserve too much detail and miss the compression target, wasting tokens in the process. (#14612)
@mastra/mongodb@1.6.0
Minor Changes
-
Added datasets and experiments storage support to the MongoDB store. (#14556)
Datasets — Full dataset management with versioned items. Create, update, and delete datasets and their items with automatic version tracking. Supports batch insert/delete operations, time-travel queries to retrieve items at any past version, and item history tracking.
Experiments — Run and track experiments against datasets. Full CRUD for experiments and per-item experiment results, with pagination, filtering, and cascade deletion.
Both domains are automatically available when using
MongoDBStore— no additional configuration needed.const store = new MongoDBStore({ uri: "mongodb://localhost:27017", dbName: "my-app" }); // Datasets const dataset = await store.getStorage("datasets").createDataset({ name: "my-dataset" }); await store.getStorage("datasets").addItem({ datasetId: dataset.id, input: { prompt: "hello" } }); // Experiments const experiment = await store.getStorage("experiments").createExperiment({ name: "run-1", datasetId: dataset.id });
Patch Changes
@mastra/pg@1.8.3
Patch Changes
-
Added storage support for dataset targeting and experiment status fields. (#14470)
- Added
targetType(text) andtargetIds(jsonb) columns to datasets table for entity association - Added
tags(jsonb) column to datasets table for tag vocabulary - Added
statuscolumn to experiment results for review workflow tracking - Added migration logic to add new columns to existing tables
- Added
-
Added agent version support for experiments. When triggering an experiment, you can now pass an
agentVersionparameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)const client = new MastraClient(); await client.triggerDatasetExperiment({ datasetId: "my-dataset", targetType: "agent", targetId: "my-agent", version: 3, // pin to dataset version 3 agentVersion: "ver_abc123" // pin to a specific agent version });
@mastra/playground-ui@19.0.0
Minor Changes
-
Added Evaluate tab to the agent playground with full dataset management, scorer editing, experiment execution, and review workflow. (#14470)
Evaluate tab — A new sidebar-driven tab for managing datasets, scorers, and experiments within the agent playground. Key features:
- Dataset management: Create, attach/detach, and browse datasets. Inline item editing and deletion. Background LLM-powered test data generation with review-before-add flow.
- Scorer editor: Create and edit scorers with test items, linked test datasets, configurable score ranges, and model selection. Run scorer experiments directly from the editor.
- Experiment runner: Trigger experiments from dataset or scorer views with correct target routing (agent, scorer, or workflow). Past runs displayed with pass/fail counts and auto-polling for status updates.
- Collapsible sidebar sections: Datasets, Scorers, and Experiments sections are collapsible with search-enabled attach dialogs.
Review tab — A dedicated review workflow for experiment results:
- Tag-based organization with dataset-level tag vocabulary and bulk tagging
- LLM-powered analysis ("Analyze untagged/selected") that proposes tags with reasons, using existing tags when applicable
- Thumbs up/down ratings and comments persisted via feedback API
- Mark items as complete for audit trail
- Completed items section with read-only display
-
Added dataset and agent version selectors to the experiment evaluate tab. You can now choose which dataset version and agent version to use when running an experiment. Version information is displayed in the experiment sidebar, results panel header, and Past Runs list. Added a copy button next to the agent version selector to easily copy version IDs. (#14562)
Patch Changes
-
Added EntityList.NoMatch component that displays a message when search filtering returns no results. Applied to all entity list pages: Agents, Workflows, Tools, Scorers, Processors, Prompts, Datasets, and MCP Servers. (#14621)
-
Added agent version support for experiments. When triggering an experiment, you can now pass an
agentVersionparameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)const client = new MastraClient(); await client.triggerDatasetExperiment({ datasetId: "my-dataset", targetType: "agent", targetId: "my-agent", version: 3, // pin to dataset version 3 agentVersion: "ver_abc123" // pin to a specific agent version }); -
Removed 'Create an Agent' button from agent list page and table empty state. Removed 'Create Scorer' button from top-level scorers page. Removed stored/code source icons (AgentSourceIcon) from agent headers, combobox, and table. Renamed 'Versions' tab to 'Editor' in agent page tabs. Added GaugeIcon to the 'Create Scorer' button in the review tab. (#14555)
-
Added metrics dashboard with KPI cards, trace volume, latency, model usage, and scores visualizations. Includes filtering by date range, agents, models, and providers. Added HorizontalBars, MetricsCard, MetricsKpiCard, MetricsLineChart, MetricsFlexGrid, and MetricsDataTable design system components. (#14491)
-
Internal cleanup and linting fixes (#14497)
-
Add optional
?path=query param to workspace skill routes for disambiguating same-named skills. (#14430)Skill routes continue to use
:skillNamein the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional?path=query parameter to select the exact skill:GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelinesSkillMetadatanow includes apathfield, and thelist()method returns all same-named skills for disambiguation. The client SDK'sgetSkill()accepts an optionalskillPathparameter for disambiguation. -
Updated skill search result types and query parameters to use
skillName/skillNamesinstead ofskillPath/skillPathsfor consistency with the name-based public API. (#14430) -
Added experimental entity list components with skeleton loading states, error handling, and dedicated empty state components for all list pages. Gated behind MASTRA_EXPERIMENTAL_UI environment variable. (#14547)
-
Added storage type detection to the Metrics Dashboard. The
/system/packagesendpoint now returnsobservabilityStorageType, identifying the observability storage backend. The dashboard shows an empty state when the storage does not support metrics (e.g. PostgreSQL, LibSQL), and displays a warning when using in-memory storage since metrics are not persisted across server restarts. Also added a docs link button to the Metrics page header. (#14620)import { MastraClient } from "@mastra/client-js"; const client = new MastraClient(); const system = await client.getSystemPackages(); // system.observabilityStorageType contains the class name of the observability store: // - 'ObservabilityInMemory' → metrics work but are not persisted across restarts // - 'ObservabilityPG', 'ObservabilityLibSQL', etc. → metrics not supported if (system.observabilityStorageType === "ObservabilityInMemory") { console.warn("Metrics are not persisted — data will be lost on server restart."); } const SUPPORTED = new Set(["ObservabilityInMemory"]); if (!SUPPORTED.has(system.observabilityStorageType ?? "")) { console.error("Metrics require in-memory observability storage."); } -
Added
ModelByInputTokensin@mastra/memoryfor token-threshold-based model selection in Observational Memory. (#14614)When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.
Example usage:
import { Memory, ModelByInputTokens } from "@mastra/memory"; const memory = new Memory({ options: { observationalMemory: { model: new ModelByInputTokens({ upTo: { 10_000: "google/gemini-2.5-flash", 40_000: "openai/gpt-4o", 1_000_000: "openai/gpt-4.5" } }) } } });The
upTokeys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.
-
Redesigned the agent instruction blocks editor with a Notion-like document feel. Blocks no longer show line numbers or block numbers, have tighter spacing, and display a subtle hover highlight. Reference blocks now show a sync-block header with a popover for block details, "Open original", "De-reference block", and "Used by" agents. Inline blocks can be converted to saved prompt blocks via a new "Save as prompt block" action in the hover toolbar. The prompt block edit sidebar now shows a "Used by" section listing which agents reference the block. Added a
lineNumbersprop toCodeEditorto optionally hide line numbers. (#14563)<CodeEditor language="markdown" lineNumbers={false} />
@mastra/react@0.2.17
Patch Changes
-
Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)
Mastra agent and client APIs accept schemas from either
zod/v3orzod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.
@mastra/schema-compat@1.2.7
Patch Changes
-
Fixed schema-compat ESM imports for Zod JSON Schema helpers. (#14617)
@mastra/schema-compat no longer uses createRequire in its Zod v4 adapter or runtime eval tests, which avoids createRequire-related ESM issues while preserving support for zod/v3 and zod/v4.
-
Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)
Mastra agent and client APIs accept schemas from either
zod/v3orzod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.
@mastra/server@1.16.0
Minor Changes
-
Added dataset-agent association and experiment status tracking for the Evaluate workflow. (#14470)
- Dataset targeting: Added
targetTypeandtargetIdsfields to datasets, enabling association with agents, scorers, or workflows. Datasets can now be linked to multiple entities. - Experiment status: Added
statusfield to experiment results ('needs-review','reviewed','complete') for review queue workflow. - Dataset experiment routes: Added API endpoints for triggering experiments from a dataset with configurable target type and target ID.
- LLM data generation: Added endpoint for generating dataset items using an LLM with configurable count and prompt.
- Failure analysis: Added endpoint for clustering experiment failures and proposing tags using LLM analysis.
- Dataset targeting: Added
-
Added
getAuthenticatedUser()to@mastra/server/authso server middleware can resolve the configured auth user without changing route auth behavior. (#14458)Example
import { getAuthenticatedUser } from "@mastra/server/auth"; const user = await getAuthenticatedUser({ mastra, token, request: c.req.raw }); -
Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14470)
Patch Changes
-
Added agent version support for experiments. When triggering an experiment, you can now pass an
agentVersionparameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)const client = new MastraClient(); await client.triggerDatasetExperiment({ datasetId: "my-dataset", targetType: "agent", targetId: "my-agent", version: 3, // pin to dataset version 3 agentVersion: "ver_abc123" // pin to a specific agent version }); -
Add optional
?path=query param to workspace skill routes for disambiguating same-named skills. (#14430)Skill routes continue to use
:skillNamein the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional?path=query parameter to select the exact skill:GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelinesSkillMetadatanow includes apathfield, and thelist()method returns all same-named skills for disambiguation. The client SDK'sgetSkill()accepts an optionalskillPathparameter for disambiguation. -
Updated skill search result types and query parameters to use
skillName/skillNamesinstead ofskillPath/skillPathsfor consistency with the name-based public API. (#14430) -
Added storage type detection to the Metrics Dashboard. The
/system/packagesendpoint now returnsobservabilityStorageType, identifying the observability storage backend. The dashboard shows an empty state when the storage does not support metrics (e.g. PostgreSQL, LibSQL), and displays a warning when using in-memory storage since metrics are not persisted across server restarts. Also added a docs link button to the Metrics page header. (#14620)import { MastraClient } from "@mastra/client-js"; const client = new MastraClient(); const system = await client.getSystemPackages(); // system.observabilityStorageType contains the class name of the observability store: // - 'ObservabilityInMemory' → metrics work but are not persisted across restarts // - 'ObservabilityPG', 'ObservabilityLibSQL', etc. → metrics not supported if (system.observabilityStorageType === "ObservabilityInMemory") { console.warn("Metrics are not persisted — data will be lost on server restart."); } const SUPPORTED = new Set(["ObservabilityInMemory"]); if (!SUPPORTED.has(system.observabilityStorageType ?? "")) { console.error("Metrics require in-memory observability storage."); } -
Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)
Mastra agent and client APIs accept schemas from either
zod/v3orzod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions. -
Added
ModelByInputTokensin@mastra/memoryfor token-threshold-based model selection in Observational Memory. (#14614)When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.
Example usage:
import { Memory, ModelByInputTokens } from "@mastra/memory"; const memory = new Memory({ options: { observationalMemory: { model: new ModelByInputTokens({ upTo: { 10_000: "google/gemini-2.5-flash", 40_000: "openai/gpt-4o", 1_000_000: "openai/gpt-4.5" } }) } } });The
upTokeys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.
Other updated packages
The following packages were updated with dependency changes only:
@mastra/core@1.14.0
Highlights
AI Gateway Tool Support in the Agentic Loop
@mastra/core now supports AI Gateway tools (e.g. gateway.tools.perplexitySearch()) as provider-executed tools: it infers providerExecuted, merges streamed provider results back into the originating tool calls, and skips local execution when the provider already returned a result.
More Reliable Observational Memory (Cache Stability + “As Of” Retrieval)
Observational memory persistence is more stable via dated message boundary delimiters and chunking, and @mastra/memory adds getObservationsAsOf() to retrieve the exact observation set active at a given message timestamp (useful for replay/debugging and consistent prompting).
MCP Client Diagnostics & Per-Server Control
@mastra/mcp adds per-server operational tooling—reconnectServer(serverName), listToolsetsWithErrors(), and getServerStderr(serverName)—to improve reliability and debugging of MCP stdio/server integrations.
Breaking Changes
- None called out in this changelog.
Changelog
@mastra/core@1.14.0
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
51970b3) -
Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
-
Fixed provider-executed tool calls being saved out of order or without results in memory replay. (Fixes #13762) (#13860)
-
Fix
generateEmptyFromSchemato accept both string and pre-parsed object JSON schema inputs, recursively initialize nested object properties, and respect default values. UpdatedWorkingMemoryTemplatetype to a discriminated union supportingRecord<string, unknown>content for JSON format templates. Removed duplicate private schema generator in the working-memory processor in favor of the shared utility. (#14310) -
Fixed provider-executed tool calls (e.g. Anthropic
web_search) being dropped or incorrectly persisted when deferred by the provider. Tool call parts are now persisted in stream order, and deferred tool results are correctly merged back into the originating message. (#14282) -
Fixed
replaceStringutility to properly escape$characters in replacement strings. Previously, patterns like$&in the replacement text would be interpreted as regex backreferences instead of literal text. (#14434) -
Fixed tool invocation updates to preserve
providerExecutedandproviderMetadatafrom the original tool call when updating to result state. (#14431) -
@mastra/core: patch (#14327)Added
spanIdalongsidetraceIdacross user-facing execution results that return tracing identifiers (including agent stream/generate and workflow run results) so integrations can query observability vendors by run root span ID -
Add AI Gateway tool support in the agentic loop. (#14016)
Gateway tools (e.g.,
gateway.tools.perplexitySearch()) are provider-executed but, unlike native provider tools (e.g.,openai.tools.webSearch()), the LLM provider does not store their results server-side. The agentic loop now correctly infersproviderExecutedfor these tools, merges streamed provider results with their corresponding tool calls, and skips local execution when a provider result is already present.Fixes #13190
-
Fixed schema-based working memory typing so
workingMemory.schemaaccepts supported schemas such as Zod and JSON Schema. (#14363) -
Fixed workspace search being wiped when skills refresh. Previously, calling
skills.refresh()or triggering a skills re-discovery viamaybeRefresh()would clear the entire BM25 search index, including auto-indexed workspace content. Now only skill entries are removed from the index during refresh, preserving workspace search results. (#14287) -
Added client/server body schemas for feedback and scores that omit the timestamp field, allowing it to be set server-side (#14270)
-
Fixed processor state not persisting between processOutputStream and processOutputResult when processors are wrapped in workflows. State set during stream processing is now correctly accessible in processOutputResult. (#14279)
-
Fixed type inference for requestContext schemas when using Zod v3 and v4. Agent and tool configurations now correctly infer RequestContext types from Zod schemas and other StandardSchema-compatible schemas. (#14363)
@mastra/ai-sdk@1.1.4
Patch Changes
- Clarified that chatRoute() aborts generation on client disconnect and documented a custom route pattern that continues server-side generation with consumeStream(). (#14333)
@mastra/auth@1.0.2
Patch Changes
-
Fixed Studio showing unauthenticated state when using
MastraJwtAuthwith custom headers.MastraJwtAuthnow implements theIUserProviderinterface (getCurrentUser/getUser), so the Studio capabilities endpoint can resolve the authenticated user from the JWT Bearer token. (#14411)Also added an optional
mapUseroption to customize how JWT claims are mapped to user fields:new MastraJwtAuth({ secret: process.env.JWT_SECRET, mapUser: payload => ({ id: payload.userId, name: payload.displayName, email: payload.mail, }), });Closes #14350
@mastra/auth-studio@1.2.0
Minor Changes
- Add configurable cookie domain support (#14285)
- Add
cookieDomainoption toMastraAuthStudioOptionsfor explicit configuration - Support
MASTRA_COOKIE_DOMAINenvironment variable as fallback - Use hostname-based detection for auto-detecting
.mastra.aidomain (prevents false positives from malicious URLs) - Maintain backward compatibility with existing
.mastra.aiauto-detection
- Add
Patch Changes
@mastra/client-js@1.9.0
Minor Changes
- Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14270)
Patch Changes
@mastra/deployer@1.14.0
Patch Changes
-
Added
MASTRA_HOSTenvironment variable support for configuring the server bind address. Previously, the host could only be set viaserver.hostin the Mastra config. Now it follows the same pattern asPORT: config value takes precedence, then env var, then defaults tolocalhost. (#14313) -
Added a new
MASTRA_TEMPLATESStudio runtime flag to control whether the Templates section appears in the sidebar. (#14309)MASTRA_TEMPLATES=truenow enables Templates navigation in Studio.- By default (
falseor unset), Templates is hidden. - Studio HTML injection now propagates this value in both CLI-hosted and deployer-hosted Studio builds.
- Added tests covering environment variable injection for both paths.
-
Fixed tsconfig path aliases during build when imports use .js-style module specifiers. (#13998)
-
Fixed
apiPrefixserver option not being applied to the underlying Hono server instance. Routes, welcome page, Swagger UI, and studio HTML handler now all respect the configuredapiPrefixinstead of hardcoding/api. (#14325)
@mastra/deployer-cloudflare@1.1.12
Patch Changes
- Stop writing
.envvariables towrangler.jsoncto prevent secrets from leaking into source control. (#14302)- Environment variables from
.envare no longer merged into thevarsfield of the generated wrangler config. - User-provided
varsfrom theCloudflareDeployerconstructor are still written as before. - A warning is logged during build with instructions to upload secrets via
npx wrangler secret bulk .env.
- Environment variables from
@mastra/elasticsearch@1.2.0
Minor Changes
-
Added support for constructing
ElasticSearchVectorwith a pre-configured Elasticsearch client. You can now pass either aclientinstance or connection parameters (urland optionalauth), giving you full control over client configuration when needed. (#12802)Using connection parameters:
const vectorDB = new ElasticSearchVector({ id: 'my-store', url: 'http://localhost:9200', auth: { apiKey: 'my-key' }, });Using a pre-configured client:
import { Client } from '@elastic/elasticsearch'; const client = new Client({ node: 'http://localhost:9200' }); const vectorDB = new ElasticSearchVector({ id: 'my-store', client });
Patch Changes
@mastra/libsql@1.7.1
Patch Changes
- Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
@mastra/loggers@1.0.3
Patch Changes
-
Fixed: PinoLogger now supports JSON output for log aggregators (#14306)
Previously, PinoLogger always used pino-pretty which produced multiline colored output, breaking log aggregators like Datadog, Loki, and CloudWatch. A new prettyPrint option allows switching to single-line JSON output.
@mastra/mcp@1.3.0
Minor Changes
-
Added new MCP client APIs for per-server control and diagnostics. (#14377)
- Added
reconnectServer(serverName)to reconnect a single MCP server without restarting all servers. - Added
listToolsetsWithErrors()to return both toolsets and per-server errors. - Added
getServerStderr(serverName)to inspect piped stderr for stdio servers.
Example
const { toolsets, errors } = await mcpClient.listToolsetsWithErrors(); await mcpClient.reconnectServer('slack'); const stderr = mcpClient.getServerStderr('slack'); - Added
Patch Changes
-
Improved (#14260)
- Updated
@modelcontextprotocol/sdkfrom^1.17.5to^1.27.1.
Deprecated
- Deprecated prompt
versionusage in@mastra/mcp. - Prompt versions are not part of MCP protocol behavior and will be removed.
Migration
- Use unique prompt names instead of prompt versions.
- Before:
client.prompts.get({ name: 'explain-code', version: 'v1', args }) - After:
client.prompts.get({ name: 'explain-code-v1', args }) MastraPromptis available for migration and is also deprecated.
- Updated
@mastra/memory@1.8.3
Patch Changes
-
Fixed observational memory triggering observation while provider-executed tool calls are still pending, which could split messages and cause errors on follow-up turns. (#14282)
-
Fixed working memory tool description to accurately reflect merge behavior. The previous description incorrectly stated "Set a field to null to remove it" but null values are stripped by validation before reaching the merge logic. The updated description clarifies: omit fields to preserve existing data, and pass complete arrays or omit them since arrays are replaced entirely. (#14424)
-
Limit oversized observational-memory tool results before they reach the observer. (#14344)
This strips large
encryptedContentblobs and truncates remaining tool result payloads to keep observer prompts and token estimates aligned with what the model actually sees. -
Improved observational memory cache stability by splitting persisted observations into separate prompt chunks using dated message boundary delimiters. (#14367)
Added
getObservationsAsOf()utility to retrieve the observations that were active at a specific point in time. This enables filtering observation history by message creation date.import { getObservationsAsOf } from '@mastra/memory'; // Get observations that existed when a specific message was created const observations = getObservationsAsOf(record.activeObservations, message.createdAt);
@mastra/mongodb@1.5.6
Patch Changes
- Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
@mastra/pg@1.8.1
Patch Changes
- Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
@mastra/playground-ui@17.0.0
Minor Changes
-
Added dedicated session page for agents at
/agents/<agentId>/session. This minimal view shows only the chat interface without the sidebar or information pane, making it ideal for quick internal testing or sharing with non-technical team members. If request context presets are configured, a preset dropdown appears in the header. (#13754)Added
hideModelSwitcherprop toAgentChatandThreadcomponents to allow hiding the model picker in the composer.
Patch Changes
-
Fixed crash during template installation by ensuring error values from stream events are converted to strings before being passed to the UI. This prevents the 'e?.includes is not a function' TypeError when the server returns non-string error payloads. (#14267)
-
Fixed crash when template installation errors are non-string values (e.g. objects). The error is now safely converted to a string before calling .includes(), preventing the 'e?.includes is not a function' TypeError in the studio. (#14267)
@mastra/schema-compat@1.2.5
Patch Changes
- Added ZodIntersection support so that MCP tools using allOf in their JSON Schema no longer throw 'does not support zod type: ZodIntersection'. Intersection types are flattened and merged into a single object schema across all provider compatibility layers (Anthropic, Google, OpenAI, OpenAI Reasoning, DeepSeek, Meta). (#14255)
@mastra/server@1.14.0
Minor Changes
- Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14270)
Patch Changes
- Fixed server tool serialization for plain JSON Schema objects to prevent tools from disappearing in Studio. (#13920)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.0.16
- @mastra/auth-clerk@1.0.2
- @mastra/auth-cloud@1.1.2
- @mastra/auth-workos@1.1.2
- @mastra/deployer-cloud@1.14.0
- @mastra/deployer-netlify@1.0.16
- @mastra/deployer-vercel@1.1.6
- @mastra/express@1.2.5
- @mastra/fastify@1.2.5
- @mastra/hono@1.2.5
- @mastra/koa@1.3.5
- @mastra/longmemeval@1.0.17
- @mastra/mcp-docs-server@1.1.14
- @mastra/opencode@0.0.14
- @mastra/react@0.2.15