Highlights
Agent Scopes (multi-tenant identity + safer memory/thread routing)
Agents now support scopes (<type>:<value>) passed per call, configured on the agent, or injected by trusted middleware via MASTRA_SCOPES_KEY; Mastra merges all three. Memory can derive resource and thread from resource:/thread: scopes (with conflicts now returning HTTP 400), nested calls can choose their own thread, and resumed runs preserve their original scopes (no adding new ones).
Trace Aggregation API (server + client + React)
Mastra adds trace aggregation end-to-end: POST /observability/traces/aggregate is discoverable via GET /observability/capabilities (traceAggregate), clients get mastraClient.aggregateTraces(), and React adds useTraceAggregateAvailable(). Field discovery now returns an aggregate schema describing valid group-by dimensions and measures.
Typed Signal Queueing for Agents (queueSignal)
You can now queue a typed signal behind the active run—without degrading it into a plain user message—via Agent.queueSignal() / session support and the new server route POST /agents/:agentId/queue-signal. Runs triggered by a signal also record signal metadata on the AGENT_RUN span for better traceability.
Faster, more usable trace lists with stored input/output/error previews
Trace and span list APIs now return outputPreview and errorPreview (and spans add errorTruncated), backed by a shared preview format in @mastra/core/storage (buildPreview, PREVIEW_MAX_LENGTH). Stores (ClickHouse/Postgres/DuckDB) persist previews at write time so queryTraces()/querySpans() no longer need to read full payloads—improving latency, memory use, and enabling UIs like Playground’s new Traces “Output” column.
Better reliability for long-running & durable workflows
Workflow execution is more robust: .foreach() failures now correctly fail the run (instead of hanging or corrupting output), durable agents are correctly honored inside workflow steps across engines, resume behavior preserves suspendData after crashes, and invalid resume data now throws AGENT_RESUME_INVALID_DATA without consuming the suspended run.
Breaking Changes
buildInputPreview()andINPUT_PREVIEW_MAX_LENGTHare deprecated in favor ofbuildPreview()andPREVIEW_MAX_LENGTH.- Factory sessions no longer have modes (Plan/Explore removed); thinking defaults and related config endpoints now use a single global default, and
PUT /web/config/thinkingrejectsmodeDefaults. - Factory credential behavior changed: cross-scope credential fallback is removed; custom credential providers must move from
orgFirstto explicitcredentialScope/ policy resolution.
Changelog
@mastra/core@1.78.0
Minor Changes
-
Added
scopesto agents: the addresses a run acts as, written as<type>:<value>(for exampleorg:acme,resource:user-123,thread:conversation-123). (#26593)Setting scopes. Pass them per call on
stream,generate,network,sendMessage,queueMessageandsendSignal, set them on the agent, or set them from trusted middleware with the newMASTRA_SCOPES_KEYrequest context key. Mastra combines all three. Read an agent's configured scopes withagent.getScopes().Memory. Memory reads its resource and thread from
resource:andthread:scopes, in addition tomemory.resource,memory.threadand the existing request context keys. If they name different values, the call throws a 400 error instead of silently picking one. Runs with no scopes from any source behave as before.const agent = new Agent({ id: 'support', model: 'openai/gpt-5-mini', memory: new Memory(), scopes: ({ requestContext }) => { // Set by your auth middleware. Request bodies can fill unreserved keys, so only read trusted ones. const orgId = requestContext.get('org-id'); return orgId ? [`org:${orgId}`] : []; }, }); // Before await agent.generate('Hi', { memory: { resource: 'user-123', thread: 'conversation-123' } }); // After await agent.generate('Hi', { scopes: ['resource:user-123', 'thread:conversation-123'] });
Nested calls. Tools, processors and subagents see the run's other scopes (for example
org:acme) underMASTRA_SCOPES_KEY, but not itsresource:andthread:scopes, so nested agent calls can choose their own thread.Suspended runs. Suspended runs keep their scopes when resumed: a resume may repeat some or all of them but cannot add new ones.
Upgrading. Upgrade
@mastra/servertogether with this release. While an older server is registered, agents refuse scopes from the call and the request context with anAGENT_SCOPES_SERVER_OUTDATEDerror (HTTP 400); the agent's ownscopesoption still applies. This detection relies on the server registering itself with Mastra, which@mastra/serverhas done since1.0.0-beta.5. Servers older than that — and custom server adapters that never callmastra.setMastraServer()— are indistinguishable from no server at all, so they are not detected: upgrade them rather than relying on the guard.Runs suspended before this release have no recorded scope set; resuming one adopts the scopes supplied at resume time (the same trust checks as a fresh run apply) instead of rejecting them. Runs suspended after the upgrade record their set — including the empty set — and a resume may not add to it.
-
Added
traceAggregateDiscoverySchemaand an optionalaggregatefield on the trace-query fields response, describing the dimensions and measuresaggregateTraces()accepts. (#27185) -
Added
queueSignal()toAgentand AgentControllerSession. It queues a typed signal behind the active run likequeueMessage(), but keeps the signal type, tag, and attributes instead of turning it into a plain user message. (#27081)agent.queueSignal( { type: 'reactive', tagName: 'skill', attributes: { name: 'triage' }, contents: 'Triage the new issue.' }, { resourceId: 'user-123', threadId: 'thread-abc' }, );
Runs started by a signal now record it on the
AGENT_RUNspan asattributes.signal(id,type,tagName,attributes), so traces show which signal started a run. -
Memory can now contribute error processors to an agent.
MastraMemory.getErrorProcessors()works likegetInputProcessors()andgetOutputProcessors(): the agent appends what it returns to its resolvederrorProcessors, on both regular and durable agents. These processors run after configured and default error processors, and stay in place when a call passes its ownerrorProcessors. The base implementation returns none, so existing memory classes are unaffected. (#26029)class MyMemory extends MastraMemory { async getErrorProcessors() { return [new MyRecoveryProcessor()]; } }
-
Added one preview format for span input, output and error. Trace lists (
queryTraces(),listTracesLight()) now returnoutputPreviewanderrorPreviewnext toinputPreview, andquerySpans()rows adderrorPreviewanderrorTruncated. (#26567)New exports from
@mastra/core/storagebuildPreview(value)builds a preview from a decoded payload, andbuildPreviewFromJson(json)from stored JSON text. Both cut toPREVIEW_MAX_LENGTH(100) characters and end with…when cut.- Exported spans and span records accept optional
inputPreview,outputPreviewanderrorPreview, preserved through create/update record builders. Stores trim nonblank supplied previews and cap them like generated ones (100 characters plus…, never splitting a surrogate pair); missing or blank previews are generated from the payload.
import { buildPreview } from '@mastra/core/storage'; buildPreview({ messages: [{ role: 'user', content: 'Where is my order?' }] }); // 'Where is my order?' buildPreview({ message: 'Tool failed' }); // 'Tool failed'
Behavior changes
- For a message list, previews now show the last user message instead of every user message joined with
|. querySpans()previews are now at most 100 characters of readable text plus an ellipsis instead of the first 256 characters of raw JSON.
buildInputPreview()andINPUT_PREVIEW_MAX_LENGTHare deprecated. UsebuildPreview()andPREVIEW_MAX_LENGTH.buildInputPreview(input, maxLength)still caps its preview atmaxLength. -
Added a
beforeApprovalhook to tools that require approval. It runs before the agent pauses for approval and can reject the call by returning{ proceed: false, output }. The agent then skips the approval pause and does not execute the tool, andoutputgoes back to the model as the tool result. Invalid calls are returned to the model to fix, and people are only asked to approve valid ones. (#26927)const issueRefund = createTool({ id: 'issue-refund', inputSchema: z.object({ orderId: z.string(), amount: z.number() }), requireApproval: true, beforeApproval: async ({ orderId, amount }) => { const order = await orders.find(orderId); if (!order) return { proceed: false, output: { error: `Order ${orderId} not found` } }; if (amount > order.paid) return { proceed: false, output: { error: `Amount exceeds ${order.paid}` } }; }, execute: async ({ orderId, amount }) => refunds.issue(orderId, amount), });
The hook does not run again when the call resumes after an approval decision. If the hook throws, the error is logged and the call proceeds to the approval gate. Keep validating inside
executetoo, because state can change while the call is waiting for approval. -
Removed the 31-day limit on trace search time ranges. You can now search all of your retained trace data, as long as the range starts before it ends. (#27042)
// Before: rejected because the range is longer than 31 days // After: returns traces from the whole six-month range await mastraClient.queryTraces({ timeRange: { from: '2026-04-01T00:00:00Z', to: '2026-10-01T00:00:00Z' }, });
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
bed1ccc) -
Fixed
resumeGenerate()andresumeStream()using up a suspended run when the resume data does not match the tool'sresumeSchema. They now throwAGENT_RESUME_INVALID_DATAand the run stays suspended, so you can resume again with valid data. (#27130) -
Fixed trace trees for agents that use output stream processors. In multi-step runs, each step's output stream processor span now sits inside that step and also covers the step's tool results, instead of every span showing up as a flat list under the agent run. Structured output processor spans stay under the agent run, because structured output runs once on the final result. (#27172)
-
Fixed live assistant messages missing the model that wrote them. Messages streamed by an
AgentControllersession now carrymodelIdandproviderin their metadata, so a client can show the model without reloading the thread. After a fallback, they name the fallback model. (#27030) -
Fixed model inference spans so they record the messages sent to the model. Observability backends that read the inference span, such as Langfuse through the OpenTelemetry exporter or Datadog, now show the input for every model call instead of an empty value. (#26995)
-
Durable agent turns no longer create an unused tool stream writer on every LLM step. A writer is now created only when a step actually uses it. (#26987)
-
Fixed
.foreach()on the evented workflow engine when an iteration fails. The run now ends withstatus: 'failed'and the iteration's error. Before, it hung forever (run.start()never resolved) or, withconcurrency: 1, finished assuccesswith a corrupted output array. Iterations that finish after the failure no longer mark the foreach step successful. (#26971) -
Fixed workflow agent steps to support durable agents on both execution engines. (#26919)
-
Fixed full agent output repeating earlier text after a feedback continuation. (#26543)
-
Fixed a resumed workflow step losing its
suspendDatawhen the run restarts after a crash. The step now gets the payload it passed tosuspend()again, alongside itsresumeData. (#27127) -
Disabled workspace tools no longer add setup work on every agent turn. Agents with workspace tools turned off now start each turn faster and allocate less memory. Fixes #26401. (#26657)
-
Fixed Amazon Bedrock India inference profiles (
in.prefix, e.g.in.anthropic.claude-sonnet-5) sendingtemperatureto models that reject it, which returned a 400. They now resolve model capabilities like the other geography prefixes. Fixes #26387. (#26982) -
Fixed step scorers in nested workflows so their scores are saved. Before, scores from steps inside a child workflow were silently dropped. (#26984)
-
Fixed agent runs crashing with
RangeError: Invalid string lengthwhen a command printed a very large amount of output.sandbox.executeCommand()now keeps the newest 1 MiB of stdout and stderr by default, the same limit spawned processes already use. Streaming callbacks still receive every chunk, and the result reportsstdoutTruncatedandstdoutDroppedBytes(and the stderr equivalents) when output was dropped. (#27106)If you need the complete output, pass
maxRetainedBytes: Infinity:// Before: all output was kept by default const result = await sandbox.executeCommand('cat', ['data.json']); // After: pass Infinity to keep all output const result = await sandbox.executeCommand('cat', ['data.json'], { maxRetainedBytes: Infinity });
Complete output is limited to 256 MiB per stream. A command that prints more is killed, and
executeCommand()(orhandle.wait()) rejects withProcessOutputLimitErrorinstead of crashing the process.The
execute_commandandget_process_outputtools now tell the model when the start of the output was dropped, so it doesn't mistake the newest output for all of it. -
Fixed document citations disappearing after a reload. When a model streams a document source (for example an Anthropic PDF citation), memory now stores it as a
source-documentpart with its media type, title and filename. Previously it was saved as a URL source with an emptyurl, so reloaded conversations showed an empty link instead of the cited document. (#26805)const { messages } = await memory.recall({ threadId, resourceId }); // assistant parts now include: // { type: 'source-document', sourceId: 'doc-1', mediaType: 'application/pdf', title: 'Q3 report', filename: 'q3.pdf' }
AI SDK v5 UI messages now also keep
source-documentparts and thesourceIdandtitleof URL sources when converting to and from stored messages. -
Fixed durable recovery observers hanging when recovery fails before the stream starts. (#26918)
-
Fixed
resumeGenerate()andresumeStream()saving messages to the thread whenmemory.options.readOnlyistrue. Resuming a suspended run now leaves read-only threads untouched, matchinggenerate()andstream(). (#27056) -
Fixed background task events leaking across resources. Previously, calling
BackgroundTaskManager.stream()without aresourceIddelivered every resource's task events, including tool arguments and results. Without aresourceId,stream()now only delivers tasks that have no resource. To subscribe to every resource, passallResources: true: (#26895)const events = mastra.backgroundTaskManager.stream({ agentId: 'my-agent', allResources: true });
The
/background-tasks,/background-tasks/streamand/background-tasks/:backgroundTaskIdroutes now use the resource from the request context. A resource-scoped caller only sees its own tasks: requesting a differentresourceIdreturns 403, and fetching another resource's task returns 404. An empty resource id in the request context is rejected with 403. Requests without a resource in the request context keep access to all resources.Also in: @mastra/server@1.78.0
-
Fixed agent controller channel sessions losing the sender's identity on the first message in a new chat thread. A channel session now opens directly on its chat thread, keeping the inbound request context, instead of creating an extra empty thread and switching over without it. Hosts that resolve memory per user from the request context's
userno longer fail on a new thread. (#27113) -
Fixed prompt caching after a suspended or tool-approval run resumes from Postgres storage. (#26943)
Tool-call arguments and JSON tool results in model prompts now use sorted object keys, so the same content produces the same request after a run resumes. Stored messages are unchanged.
-
Fixed new conversation failures when ToolSearchProcessor uses context storage with memory. (#26902)
-
Fixed agents sending the same request until
maxStepswhen a text-only response ended with a non-standard finish reason, such asother. The run now ends after that response, so the text and token usage are no longer repeated. (#26905) -
Fixed two OpenAI errors with tool inputs and structured output: (#26979)
- Optional tool inputs that accept more than one type can now be left empty. Before, OpenAI models filled them with made-up values.
- Structured output with a field that has free-form keys, such as
z.record(z.string(), z.string()), now works on OpenAI instead of failing with a 400 error. These schemas are sent without strict mode. To keep strict mode anyway, setproviderOptions.openai.strictJsonSchema: true.
Also in: @mastra/schema-compat@1.3.15
-
Fixed
createStep(agent)running adurable: trueagent as a plain agent. When the agent is registered with Mastra, the workflow step now runs through the registered durable wrapper on both the default and evented engines, so durable streaming and cleanup apply as expected. (#27078)A step now logs a warning when a
durable: trueagent still has to run non-durably: when no durable wrapper for it is registered on the workflow's Mastra instance, or when it uses a legacy v1 model. -
Fixed token estimates for long numbers. A long number used to count as one token, so output full of numbers could grow far past memory and token limits without triggering them. Long numbers are now counted accurately, and limits trigger at the right time. (#27208)
Estimates saved on existing messages are recalculated, so existing conversations also get the corrected counts.
Also in: @mastra/memory@1.38.0
-
Fixed tool
transformreplacing stored tool-call arguments with aTool input-available payload unavailableplaceholder when only another phase (such astranscript.output) was configured. A tool-level transform now only changes the phases it defines, so storedargsround-trip unchanged and replaying the thread with Anthropic preserved thinking no longer fails. Transformers that throw or returnundefinedstill fall back to the placeholder, and raw input deltas stay suppressed. Fixes #26597. (#26965)
@mastra/clickhouse@1.25.1
Patch Changes
-
Trace aggregates now read each trace's root span once from the requested time window, instead of re-reading every stored root row of the matching traces. This lowers
aggregateTraces()latency, bytes read, and memory, most on large projects. (#26912) -
Fixed ClickHouse deleteMessages bumping thread updatedAt with an ALTER TABLE UPDATE mutation, which rewrote whole data parts (often forbidden on managed deployments) and stamped the same updatedAt on every stored version of the thread row — making thread reads ambiguous on the ReplacingMergeTree until parts merged, so a just-renamed or archived thread could read back its previous state. The timestamp bump now inserts a strictly newer thread version, like every other thread write. Fixes #25580 (#26838)
-
Trace and span lists use less memory. Every span now stores short previews of its input, output and error when it is written, so
queryTraces(),listTracesLight()andquerySpans()read those previews instead of each span's full payloads. Trace and span lists now also returnoutputPreviewanderrorPreview. Spans written before this change still show previews, built from their own payloads, even when another tenant has a root span with the same trace and span IDs. (#26567)storage.init()addsinputPreview,outputPreviewanderrorPreviewcolumns tomastra_span_events,mastra_trace_rootsandmastra_trace_branches. This runs automatically on first use. If you setdisableInit: true, runstorage.init()from your migration step before deploying, or apply the columns yourself. Add them tomastra_trace_rootsandmastra_trace_branchesbeforemastra_span_events, because the views that copy span events into those tables select every column:ALTER TABLE mastra_trace_roots ADD COLUMN IF NOT EXISTS inputPreview Nullable(String); ALTER TABLE mastra_trace_roots ADD COLUMN IF NOT EXISTS outputPreview Nullable(String); ALTER TABLE mastra_trace_roots ADD COLUMN IF NOT EXISTS errorPreview Nullable(String); ALTER TABLE mastra_trace_branches ADD COLUMN IF NOT EXISTS inputPreview Nullable(String); ALTER TABLE mastra_trace_branches ADD COLUMN IF NOT EXISTS outputPreview Nullable(String); ALTER TABLE mastra_trace_branches ADD COLUMN IF NOT EXISTS errorPreview Nullable(String); ALTER TABLE mastra_span_events ADD COLUMN IF NOT EXISTS inputPreview Nullable(String); ALTER TABLE mastra_span_events ADD COLUMN IF NOT EXISTS outputPreview Nullable(String); ALTER TABLE mastra_span_events ADD COLUMN IF NOT EXISTS errorPreview Nullable(String);
On a replicated cluster, add
ON CLUSTER <cluster>after each table name.Nonblank supplied previews are trimmed and capped like generated ones (100 characters plus
…). Missing or blank write-time previews are generated through the optional core helper. Stored empty strings do not trigger read fallback. With older core versions that lack the helper, preview fields pass through unchanged without generation. -
Reduced the memory used by
queryTraces()on large projects. Trace lists, numbered pages, delta polling and thread groups no longer sort every root span payload in the time range, so busy projects that previously hit ClickHouse memory limits now return results. Trace inputs and metadata are now read only for the traces on the returned page. (#26567)queryTraces()now treats each trace as having one root span. A trace that was written with more than one root span is listed once, under one of its roots, and matches root filters through any of them. Delta polling lists such a trace under the same root on every poll, and the next-page cursor still advances when a page comes back short.
@mastra/client-js@1.55.0
Minor Changes
-
Added
aggregateTraces()to group and measure traces from the client. Checkcapabilities.traceAggregatefromgetObservabilityCapabilities()before calling it. (#27185)const { rows } = await mastraClient.aggregateTraces({ timeRange: { from: '2026-08-01T00:00:00.000Z', to: '2026-08-08T00:00:00.000Z' }, groupBy: ['entityName'], measures: ['count', 'duration.p95'], });
-
Added
agent.queueSignal()for queuing a typed signal behind the active thread run. (#27081)await agent.queueSignal({ signal: { type: 'reactive', tagName: 'skill', attributes: { name: 'triage' }, contents: 'Triage the new issue.' }, resourceId: 'user-123', threadId: 'thread-abc', });
Patch Changes
-
Added an optional
traceRetentionDaysto the observability capabilities type and response schema, for hosts that serveGET /observability/capabilitiesthemselves and know how long traces are kept. Clients can use it to stop users picking dates whose traces are already gone. Mastra's own server and built-in stores don't report a retention, so the field is omitted there. (#27166)const { capabilities } = await client.getObservabilityCapabilities(); capabilities.traceRetentionDays; // e.g. 180, or undefined
Also in: @mastra/server@1.78.0
-
Added
outputPreviewanderrorPreviewto trace list rows, anderrorPreviewanderrorTruncatedtoquerySpans()rows. (#26567)Also in: @mastra/react@1.11.0
@mastra/cloudflare@1.7.4
Patch Changes
-
storage.prune()now deletes durable agent run fencing records. Configureretention.workflows.runOwnership(by inactivity) andretention.memory.runFences(by time since retirement) to stop keeping one claim and one fence record per run forever. Fences of running executions are never pruned. (#26966)Also in: @mastra/cloudflare-d1@1.4.3, @mastra/dsql@1.5.3, @mastra/dynamodb@1.4.3, @mastra/mssql@1.10.4, @mastra/mysql@0.13.2, @mastra/oracledb@0.5.3, @mastra/spanner@1.9.5
@mastra/code-sdk@1.14.0
Minor Changes
-
createMastraCodeAgentControllernow acceptsmodes: falsefor hosts that don't offer modes. Every session runs as the single Build agent: a thread last used in another mode reopens in Build, and per-mode thinking defaults from settings never apply. Runs without a session override use the global thinking level. (#26854)const controller = await createMastraCodeAgentController({ modes: false });
-
You can now remove agent controller built-in tools such as
submit_planwithout reaching into the controller's private config. SetdisableBuiltinToolsoncreateMastraCode()/prepareAgentControllerMount(), or onMastraFactoryto apply it to every Factory session. Whensubmit_planis unavailable, plan mode tells the model to write the plan file instead of calling it. (#26967)new MastraFactory({ storage, disableBuiltinTools: ['submit_plan'] });
Also in: @mastra/factory@0.23.0
-
Removed cross-scope credential fallback in deployed sessions so each session uses only Factory or user credentials. (#26934)
Custom credential store providers must replace
orgFirstwith an explicitcredentialScope:// Before const tenant = { orgId, userId, orgFirst: true }; // After const tenant = { orgId, userId, credentialScope: 'factory' as const };
Use
"user"to resolve only personal credentials. Hosts that select credentials for a session owner rather than the signed-in caller can register an asynchronous policy resolver as the second argument tosetCredentialStoreProvider. Resolve that policy from authenticated identity and durable session records, not client-writable session state. CallprepareCredentialTenant(requestContext)before resolving a model; the SDK’s built-in model, memory, and goal-judge resolvers do this automatically and refresh the policy again before provider calls.
Patch Changes
-
Fixed revoked or invalid OAuth sign-ins showing a raw API error. When Anthropic, OpenAI Codex, GitHub Copilot, xAI, or Kimi For Coding rejects a stored sign-in with a 401, the error is now a
ProviderAuthRequiredErrorthat says the sign-in was rejected, the same as a missing sign-in. Hosts can match on the error name to prompt the user to sign in again. (#27187)if (error.name === 'ProviderAuthRequiredError') { // e.g. "Anthropic rejected the stored sign-in: OAuth access token is invalid." promptSignIn(); }
-
Improved the "Tenant model credential policy could not be resolved" error. It now states why resolution failed: (#27138)
- The request has no authenticated user.
- No credential policy applies to the user.
Added a warning with non-secret diagnostics and a stack trace. Use it to locate where the request context was lost.
-
Fixed model selection for Kimi For Coding, GitHub Copilot and custom providers in
/models,/setup,/modeland/om. Picking a Kimi or Copilot model no longer saves an id that fails with "Could not find config for provider mastracode". (#26692)These models are now listed under ids that resolve:
- Kimi:
kimi-code-plan-cn/<model>(your Kimi login orKIMI_API_KEYauthenticates it) - GitHub Copilot:
github-copilot/<model> - Custom providers:
<custom-provider>/<model>
Ids you already saved keep working:
kimi-for-coding/<model>,mastracode/kimi-for-coding/<model>andmastracode/github-copilot/<model>.A Kimi API key saved in
/api-keysor Factory's provider settings is now used for Kimi models, and a Kimi login shows as connected in/api-keys.Also in: @mastra/factory@0.23.0
- Kimi:
-
Kept sandbox-backed file reads, listings and searches complete now that sandbox commands keep only the newest 1 MiB of output by default. (#27106)
-
Fixed the thinking level being ignored by more providers. It now reaches Groq, Mistral, xAI, OpenRouter, Together AI, DeepInfra, Cerebras, Perplexity and Alibaba models. (#26411)
The thinking picker lists only the levels a model's request can carry. For example, GLM-5.2 through Mistral tops out at High instead of failing the request on Max. Models whose requests cannot carry a level now offer only Off instead of levels that did nothing. That includes Z.ai, Kimi, GitHub Copilot and Amazon Bedrock models. Off still leaves each model on its own default.
-
Fixed the missing provider credential error to say which credentials the Factory uses. It now names the Factory's model scope (organization or personal credentials only), notes that the other set doesn't apply, and says what to add. (#27077)
@mastra/convex@1.7.4
Patch Changes
-
storage.prune()now deletes durable agent run fencing records. Configureretention.workflows.runOwnership(by inactivity) andretention.memory.runFences(by time since retirement) to stop keeping one claim and one fence record per run forever. Fences of running executions are never pruned. (#26966)Redeploy your Convex functions after upgrading so the new prune operation is available.
@mastra/deployer@1.78.0
Patch Changes
- Fixed
mastra devfailing with "Missing subpath specifier" when the app depends on a private workspace package that has noversionin itspackage.json. Fixes #26321. (#26991)
@mastra/deployer-cloudflare@1.2.37
Patch Changes
- Fixed Cloudflare builds failing with
Could not resolve "@opentelemetry/api"afterposthog-node5.56.1 started importing that optional package. When your project doesn't install@opentelemetry/api, the generated Wrangler config now points it at a stub, sowrangler devandwrangler deploywork again. Fixes #27037. (#27079)
@mastra/deployer-sandbox@0.3.22
Patch Changes
- Kept sandbox deploy commands and worker output reads complete now that sandbox commands keep only the newest 1 MiB of output by default. (#27106)
@mastra/duckdb@1.14.1
Patch Changes
-
Trace and span lists now read short stored previews instead of each span's full input. Every span event stores previews of its input, output and error when it is written, and
queryTraces(),listTracesLight()andquerySpans()read them. Lists also returnoutputPreviewanderrorPreview, andquerySpans()addserrorPreviewanderrorTruncated. (#26567)Generated previews use the shared format from
@mastra/core: at most 100 characters plus…when cut. For message lists they show the last user message.querySpans()previews used to be the first 256 characters of raw JSON. Nonblank supplied previews are trimmed and capped the same way; missing or blank write-time previews are generated from their payloads.Reconstructed previews stay aligned with the event carrying their payload, including newer empty or missing previews. Each event also records whether it carries an input, output or error payload, so
listTracesLight()picks previews without reading theinputandoutputpayloads. NULL previews trigger batched payload reads and application-side generation; stored empty strings do not. With older core versions that lack the optional preview helper, preview fields pass through unchanged without generation.The store adds
inputPreview,outputPreview,errorPreview,hasInputPayload,hasOutputPayloadandhasErrorPayloadcolumns tospan_eventswhen it starts. No manual migration is needed.
@mastra/e2b@0.14.1
Patch Changes
-
Changed the Factory sandbox memory setting from a free-form MB field to a dropdown of sizes: 512 MB, 1 GB, 2 GB, 4 GB or 8 GB, matching E2B's 8 GB limit. Memory set through the
defaultsoption in code is unaffected. (#27098)Also in: @mastra/platform-workspace@1.8.1
@mastra/factory@0.23.0
Minor Changes
-
Added a Factory model scope setting so sessions can use organization models and credentials or their owner’s personal models and credentials. (#26934)
Organization admins choose the scope through the project API:
PATCH /web/factory/projects/:id Content-Type: application/json {"modelScope":"user"}
Use
"factory"for organization models and credentials. Credential scope changes apply on the next model invocation without replacing a thread’s selected model. Session ownership and project policy are resolved from durable records, and unavailable or ambiguous policy stops execution instead of falling back to another account. -
Added a hybrid model scope: sessions run on the owner's credentials and default model, work items run on the Factory's. New Factories start in hybrid; existing Factories keep their scope. Because new Factories now bill members' interactive sessions to their own provider accounts, admins who want everything on organization credentials should switch the Factory to the factory scope. (#27095)
Previously, setting
modelScopetofactoryonPATCH /web/factory/projects/:idput both sessions and work items on organization credentials:{ "modelScope": "factory" }Admins can now split sessions and work items by setting it to
hybrid:{ "modelScope": "hybrid" } -
Factory sessions no longer have modes: Plan and Explore are gone, and every session runs as Build. Mastra Code in the terminal keeps its modes. (#26854)
-
The composer no longer shows a mode picker, and Shift+Tab no longer switches modes.
/settingsno longer prints a mode line. -
A thread last used in Plan or Explore mode reopens in Build mode, so it can still write files.
-
Thinking defaults have a single global level. Per-mode thinking defaults no longer apply to Factory runs. Per-mode defaults saved by the terminal app stay in the shared settings file.
-
GET /web/config/thinkingno longer returnsmodesormodeDefaults, andPUT /web/config/thinkingonly acceptsglobalDefault. A body withmodeDefaultsnow gets a 400.- PUT /web/config/thinking { "globalDefault": "medium", "modeDefaults": { "plan": "high" } } + PUT /web/config/thinking { "globalDefault": "medium" }
-
Explore subagents in sessions without a default model now use Build mode's default model instead of Explore mode's, like sessions that have a default model already do for every subagent.
-
Audit events for runs no longer record an agent name.
-
-
Slack threads no longer create Work board cards. A Slack DM or @-mention still starts its session and posts the "New session started" card, whatever the factory's old setting was. (#27063)
The
slackWorkItemsEnabledproject setting is removed from the project update API.Before:
PATCH /web/factory/projects/:id { "slackWorkItemsEnabled": true }
After: sent alone, the field is rejected as an empty update (400, "At least one project field is required"). Sent with other fields, it is ignored:
PATCH /web/factory/projects/:id { "name": "Platform" }
Slack-thread cards that already exist keep receiving
asidecomments and keep posting feed comments back to Slack.
Patch Changes
-
Added
scope=usertoGET /web/config/modelsto list models supported by the caller's personal credentials. Organization-only models are excluded from this personal catalog. (#27080) -
Factory sessions and work-item runs can now start on a model you have credentials for. This applies when no default model was chosen and the built-in default model has no credential. (#27177)
The session uses the default model of a provider you have connected, for example
anthropic/claude-fable-5for Claude oropenai/gpt-5.6-solfor ChatGPT. A default you or your Factory chose always wins. The fallback is not saved as a default preference. -
New Factories now default to the hybrid model scope everywhere. Onboarding preselects "Company work · personal sessions", and Factories with no saved scope, or one that isn't recognized, are treated as hybrid instead of company account. (#27202)
-
Improved the Slack "Could not authorize the owner of Slack Factory session" error. It now states which check failed: (#27183)
- The sender's account link has no organization.
- No source-control session row exists for the session, or its project repository row is missing.
- The source-control session row has no organization.
-
The session detail route
GET /web/user-sessions/:sessionIdnow returns the owner profile (owner: { id, name, avatarUrl? }), the same field the session list routes already return.owneris missing when the owner has no profile in the user directory. (#27031)const { session } = await fetch(`${baseUrl}/web/user-sessions/${sessionId}`).then(response => response.json()); const isTeammateSession = session.userId !== viewerUserId; const ownerName = session.owner?.name ?? 'a teammate';
-
Fixed Factory pages outside a session failing with "Factory session ownership is unavailable." Overview, Rules, Settings and other Factory pages now use the Factory's model and credential policy for the signed-in member of that organization. (#27164)
-
Fixed new Factory sessions starting on the built-in mode model instead of your default model. When a session opened before its chat thread was attached, your default went to a thread the chat never used, so the first message could run on a model you have no credentials for and fail with a provider authentication error. (#27080)
-
Added
scope=orgtoGET /web/config/models. It lists only the models your organization credentials can run, next to the existingscope=userfor personal credentials. (#27163)GET /web/config/models?scope=orgAn unsupported
scopevalue now returns400withinvalid_model_scope. Before, it was ignored and the unfiltered list came back. -
Fixed runs failing with "Factory source-control repository not found." after a repository's source-control connection was reinstalled. When a card's repository is linked but turned off in the Factory's environment, the error now names the repository and says to turn it on under Settings → Environment. (#26970)
-
Fixed model pickers offering models that an organization-scoped Factory cannot run.
GET /web/config/modelsaccepts an optionalfactoryIdand then lists only models served by that Factory's model scope (organization credentials for organization scope, personal credentials for personal scope). Personal model settings now note when the current Factory uses organization credentials only. (#27077)GET /web/config/models?factoryId=<factory-id> -
Fixed new chats so they run on the default model you saved in Settings. Before, the draft showed your default model but the chat ran on the built-in one. Chats where you already sent a message keep their model. (#26996)
-
Fixed sandbox settings becoming impossible to save when a previously stored value is no longer allowed (for example, a memory size above the new 8 GB limit). The outdated value is dropped and the provider default applies instead. (#27098)
-
Fixed the GitHub repository list failing entirely when an organization still had a personal-account GitHub installation it is no longer allowed to use; those installations are now skipped. Added a refresh option that bypasses the repository cache so repositories newly granted on GitHub appear without waiting for a webhook. (#27047)
-
Fixed Slack mentions failing with "Tenant model credential policy could not be resolved". New Slack sessions now open directly on their Slack thread and no longer leave an extra empty thread behind. (#27113)
-
Kept workspace file listings, change lists and untracked-file lookups complete now that sandbox commands keep only the newest 1 MiB of output by default. (#27106)
@mastra/inngest@1.10.6
Patch Changes
- Inngest agents honor agent
scopeswhen used with a@mastra/corerelease that supports them. Memory identity comes fromresource:andthread:scopes, tools and processors see the other scopes, and a resumed run keeps the scopes it started with. Resuming with a scope the run did not hold throws an error and leaves the suspended run untouched. Scopes in the agent'sdefaultOptionscount as the agent's own scopes, not the caller's. (#26593)
@mastra/libsql@1.26.2
Patch Changes
-
Fixed
mastra_notificationsbeing created without its("threadId", "id")primary key. Without it, duplicate notification ids could be stored, and on PostgreSQL the table had no replica identity, so updates and deletes failed when the table was part of a logical replication publication. (#26664)New tables now get the key, and
exportSchemas()includes it. Existing tables are upgraded the next time storage initializes: PostgreSQL adds the primary key, and LibSQL adds a unique index on the same columns. If the key cannot be added because an existing table already contains duplicate rows, or because the database role does not own the table, initialization still succeeds and logs a warning with the SQL needed to add the key.Also in: @mastra/pg@1.31.2
@mastra/loggers@1.3.8
Patch Changes
-
Fixed
HttpTransportretrying JSON serialization failures. Log batches are now serialized once before sending: values that cannot be serialized (circular references, BigInts, throwingtoJSON) fail immediately instead of waiting through retry delays, and network retries resend the exact same payload. (#27174) -
Fixed
FileTransportandUpstashTransportlog queries accepting invalidpage/perPagevalues.listLogsandlistLogsByRunIdnow throw aRangeErrorfor negative, fractional, non-finite, or unsafe pagination values instead of silently returning misleading results or sending malformed Redis ranges.page: 0andperPage: 0still default to page 1 and 100 per page. (#27175)
@mastra/mcp@2.4.1
Patch Changes
-
Fixed
MCPClientsending malformed tool input schemas to model providers. One MCP tool with a broken schema (for example arequiredlist placed insideproperties) used to make strict providers such as OpenAI reject every request, even ones that didn't use that tool. (#25995)- Tools with a malformed input schema, or one over the nesting/size limit, are now skipped with a warning naming the server, tool, and problem. The server's other tools keep working.
toolsFromDefinitionsskips such cached definitions;toolFromDefinitionthrowsMCP_CLIENT_INVALID_TOOL_INPUT_SCHEMAfor them.
Fixes #23731.
-
MCP tools that declare an
outputSchemanow pass the text of embeddedresourceblocks to the model, together with their text blocks. Before, only the text blocks reached the model, so tools such as GitHub'sget_file_contentsshowed the model a status line but not the file.structuredContentis still kept out of the model input. (#27131)
@mastra/memory@1.38.0
Minor Changes
-
Observational Memory now recovers automatically when a model provider rejects a request for exceeding its context window (#21657). (#25055)
A provider can count more tokens than Observational Memory's own estimate (attachments, formatting, or switching to a model with a smaller window), so a request can overflow before the observation threshold is reached. Previously the agent's request just failed. Now Observational Memory observes everything still pending and retries once with the smaller context. No configuration is needed. Automatic recovery needs a
@mastra/coreversion that includesMastraMemory.getErrorProcessors(); with an older core, the error reaches the caller as before. To turn it off:const memory = new Memory({ options: { observationalMemory: { observation: { observeOnContextOverflow: false }, }, }, });
observe()also acceptsforce: trueto observe pending messages regardless of the threshold, for that call only.
@mastra/nestjs@0.2.37
Patch Changes
- The NestJS server marks itself as reserving agent scopes, like the other server adapters. Its routes already ignore
scopesin request bodies and treatmastra__scopesas reserved, so agents accept scopes from your code and middleware when served through NestJS. (#26593)
@mastra/observability@1.20.1
Patch Changes
-
Update bundled model pricing with the latest prices from models.dev (
bed1ccc) -
Fixed cost estimates for Amazon Bedrock India inference profiles (
in.prefix), which previously reportedno_matching_model. Fixes #26387. (#26982) -
Tracking exporters now wait for in-progress trace-cap evictions in
flush()andshutdown(), so the aborted spans are sent instead of being queued after the flush or aborted twice. (#26916)
@mastra/otel-exporter@1.4.8
Patch Changes
- Fixed tool calls going missing from
gen_ai.output.messages. A model step that only calls tools is now exported as an assistant message withtool_callparts instead of an empty text message. Inference span inputs are also converted to the GenAI message format. (#26995)
@mastra/pg@1.31.2
Patch Changes
-
Trace and span lists now read short stored previews instead of each span's full input. Every span stores previews of its input, output and error when it is written, and
queryTraces()andquerySpans()read them. Lists also returnoutputPreviewanderrorPreview, andquerySpans()addserrorPreviewanderrorTruncated. (#26567)Generated previews use the shared format from
@mastra/core: at most 100 characters plus…when cut. For message lists they show the last user message.querySpans()previews used to be the first 256 characters of raw JSON. Nonblank supplied previews are trimmed and capped the same way; missing or blank write-time previews are generated from their payloads.listTracesLight()also reads stored previews. NULL previews trigger batched payload reads and application-side generation; stored empty strings do not. With older core versions that lack the optional preview helper, preview fields pass through unchanged without generation.storage.init()addsinputPreview,outputPreviewanderrorPreviewcolumns tomastra_span_events. This runs automatically on first use. If you setdisableInit: true, runstorage.init()from your migration step before deploying, or apply the columns yourself (use your schema name if it isn'tpublic):ALTER TABLE "public"."mastra_span_events" ADD COLUMN IF NOT EXISTS "inputPreview" text; ALTER TABLE "public"."mastra_span_events" ADD COLUMN IF NOT EXISTS "outputPreview" text; ALTER TABLE "public"."mastra_span_events" ADD COLUMN IF NOT EXISTS "errorPreview" text;
@mastra/playground-ui@64.0.0
Minor Changes
-
Added a
minDateprop toDateTimeRangePickerandTraceTimeRangeChip, so you can stop users picking dates with no data, such as traces past retention. Days beforeminDateare disabled in both custom range calendars, and a range that already starts earlier can't be applied. (#27166)<TraceTimeRangeChip {...rangeProps} minDate={subDays(new Date(), retentionDays)} />
-
Comboboxes in forms now search right in the field. Inside a
Field, aComboboxis a text field you type into to filter, and amultipleone shows its picks as removable chips (× or Backspace removes one) instead of "N selected". Comboboxes outside aField(toolbars, breadcrumbs, inline pickers),ghostand icon-size triggers keep the trigger with the search in the popup. TheComboboxprops are unchanged. (#27112)Comboboxis now built on new composable parts, also exported, for pickers itsoptionsprop can't describe: groups with labels, items with a title, description and end detail, or a custom field. They follow shadcn's Combobox structure; the root isComboboxRootbecauseComboboxis the options-based component.import { ComboboxRoot, ComboboxInput, ComboboxContent, ComboboxEmpty, ComboboxList, ComboboxItem, } from '@mastra/playground-ui/components/Combobox'; <ComboboxRoot items={agents} value={agent} onValueChange={setAgent} autoHighlight> <ComboboxInput placeholder="Pick an agent" /> <ComboboxContent> <ComboboxEmpty>No agents found.</ComboboxEmpty> <ComboboxList> {item => ( <ComboboxItem key={item.id} value={item}> {item.name} </ComboboxItem> )} </ComboboxList> </ComboboxContent> </ComboboxRoot>;
For small triggers (an icon, a ghost button), use
ComboboxTriggerwithComboboxSearchat the top ofComboboxContentto keep the search inside the popup. -
DropdownMenugains two typed helpers for one-of-many choices.DropdownMenu.RadioOptionsrenders a list of options, andDropdownMenu.RadioSubputs them in a submenu whose row shows the selected label, so a menu of several settings reads its current state without opening each submenu.onValueChangereceives your option type, notany.DropdownMenu.SubTriggeralso takes ahintto show a current value next to its chevron. (#27003)import { DropdownMenu } from '@mastra/playground-ui/components/DropdownMenu'; import type { DropdownMenuRadioOption } from '@mastra/playground-ui/components/DropdownMenu'; const ownerOptions: DropdownMenuRadioOption<'mine' | 'all'>[] = [ { value: 'mine', label: 'Mine' }, { value: 'all', label: 'Everyone' }, ]; <DropdownMenu> <DropdownMenu.Trigger>Properties</DropdownMenu.Trigger> <DropdownMenu.Content> <DropdownMenu.RadioSub label="Owner" options={ownerOptions} value={owner} onValueChange={setOwner} /> </DropdownMenu.Content> </DropdownMenu>;
-
Added helpers to filter the metrics dashboard with
FilterBar, the filter bar Traces uses. (#27034)MetricsTimeRangeChipshows the dashboard's date range as the filter bar's Time chip.createMetricsFilterBarFieldsandMETRICS_FILTER_BAR_OPERATORSdescribe the dimensions you can filter on.metricsTokensToFilterBarItemsandfilterBarItemsToMetricsTokensconvert betweenFilterBaritems and the dashboard's URL-backed filter tokens.
DateRangeSelectorandcreateMetricsPropertyFilterFieldsare deprecated in favor of these.import { FilterBar } from '@mastra/playground-ui/components/FilterBar'; import type { FilterBarItem } from '@mastra/playground-ui/components/FilterBar'; import { MetricsTimeRangeChip } from '@mastra/playground-ui/domains/metrics/components/metrics-time-range-chip'; import { createMetricsFilterBarFields, filterBarItemsToMetricsTokens, METRICS_FILTER_BAR_OPERATORS, metricsTokensToFilterBarItems, } from '@mastra/playground-ui/domains/metrics/metrics-filters'; import { TRACE_TIME_RANGE_FIELD, TRACE_TIME_RANGE_FIELD_ID, TRACE_TIME_RANGE_ITEM, } from '@mastra/playground-ui/domains/traces/components/trace-time-range-chip'; const isTimeRange = (item: FilterBarItem) => item.fieldId === TRACE_TIME_RANGE_FIELD_ID; <FilterBar fields={[TRACE_TIME_RANGE_FIELD, ...createMetricsFilterBarFields(discovered)]} operators={METRICS_FILTER_BAR_OPERATORS} value={[TRACE_TIME_RANGE_ITEM, ...metricsTokensToFilterBarItems(filterTokens)]} onValueChange={items => setFilterTokens(filterBarItemsToMetricsTokens(items.filter(item => !isTimeRange(item))))} > <FilterBar.Chips renderChip={item => (isTimeRange(item) ? <MetricsTimeRangeChip /> : <FilterBar.Chip item={item} />)} /> <FilterBar.Input /> </FilterBar>;
-
Added
createLocalStorageStoreanduseLocalStorageStorefor a browser-stored value that every component reading it shares, along with other tabs. UnlikeuseLocalStorageState, a write updates every reader at once. A write that storage refuses still applies for the rest of the session. (#27025)import { createLocalStorageStore, useLocalStorageStore } from '@mastra/playground-ui/hooks/use-local-storage-store'; const pinnedStore = createLocalStorageStore<ReadonlySet<string>>({ key: 'app.pinned', defaultValue: new Set(), parse: stored => { if (!Array.isArray(stored)) throw new Error('not a list of ids'); return new Set(stored.filter(id => typeof id === 'string')); }, toJSON: pinned => [...pinned], }); function PinButton({ threadId }: { threadId: string }) { const pinned = useLocalStorageStore(pinnedStore); return <button onClick={() => pinnedStore.write(new Set([...pinned, threadId]))}>Pin</button>; }
Added
Sidebar.NavShowMore, a "Show N more" / "Show less" toggle for a sidebar list cut to its first rows. It hides itself when nothing is cut off or when the sidebar is collapsed to its icon rail.<Sidebar.NavList>{(expanded ? threads : threads.slice(0, 5)).map(renderThread)}</Sidebar.NavList> <Sidebar.NavShowMore foldedCount={threads.length - 5} expanded={expanded} onExpandedChange={setExpanded} />
-
Added an
insetvariant toSettingsContainerfor a short stack of rows that is the main content of a dialog. The default variant is unchanged and stays the one to use for settings pages and forms. (#27171) -
Added an Output column to the Traces table, so you can see what a run returned without opening it. (#27195)
- Shows the trace's output preview, or its error in red when the trace failed.
- On by default, next to Input. Hide it from the Columns menu or through
columnPreferences. - Saved column choices gain Output next to Input when Input is shown.
- Servers that don't send previews show
-.
import { TracesListView } from '@mastra/playground-ui/domains/traces/components/traces-list-view'; import { DEFAULT_TRACE_COLUMN_PREFERENCES } from '@mastra/playground-ui/domains/traces/trace-list-columns'; <TracesListView {...props} columnPreferences={{ ...DEFAULT_TRACE_COLUMN_PREFERENCES, visibleColumns: DEFAULT_TRACE_COLUMN_PREFERENCES.visibleColumns.filter(column => column !== 'output'), }} />;
-
Added
THEME_OPTIONS, the System, Light, and Dark choices thatThemeToggleshows. Use it to offer the same theme choices inside a menu, where a segmented control does not fit. (#27097)import { DropdownMenu } from '@mastra/playground-ui/components/DropdownMenu'; import { useTheme } from '@mastra/playground-ui/components/ThemeProvider'; import { THEME_OPTIONS } from '@mastra/playground-ui/components/ThemeToggle'; function ThemeMenuItem() { const { theme, setTheme } = useTheme(); return <DropdownMenu.RadioSub label="Theme" options={THEME_OPTIONS} value={theme} onValueChange={setTheme} />; }
-
The trace thread view now shows the whole thread instead of only the last 31 days. Field suggestions on the traces page now use the full selected date range. (#27042)
Removed
clampTraceDiscoveryTimeRangeandTRACE_QUERY_DISCOVERY_MAX_RANGE_MSfrom@mastra/playground-ui/domains/traces/trace-query-filters. Pass the selected time range directly:// Before const timeRange = clampTraceDiscoveryTimeRange(buildTraceQueryRequest(options).timeRange); // After const timeRange = buildTraceQueryRequest(options).timeRange;
-
Added
Meter, a thin bar that shows usage against an allowance. Usage past the allowance turns red, sized to how far over it is. (#27194)import { Meter } from '@mastra/playground-ui/components/Meter'; <Meter value={112} max={100} aria-label="vCPU" aria-valuetext="112 of 100 vCPU-hours" />;
Pass
Meter.Sourcechildren to show where the usage came from. Hovering or focusing the bar splits it into one color per source, with a key of each source's amount:<Meter value={148} max={360} aria-label="Memory"> <Meter.Source label="research-workflows" value={112} /> <Meter.Source label="Sandboxes" value={36} /> </Meter>
MetricsStackedBarChartcan now print a label above a column. SetcolumnLabelKeyto a row field, and rows that have it show the text above their column:<MetricsStackedBarChart data={rows} series={series} columnLabelKey="overage" /> // rows: [..., { time: 'Oct 13', included: 100, over: 12, overage: '$1.20 over' }]
An inset
Tablecan now draw its header inside the inset with the rows, for a table nested in a card or an expanded row:<Table variant="inset"> <Table.Header placement="inside">…</Table.Header> <Table.Body>…</Table.Body> </Table>
-
Added an
infoprop toFieldLabel. It shows a small info icon after the label with your text in a tooltip on hover or keyboard focus. Screen readers announce the text as the icon's description, and clicking the icon never focuses the field. (#27114)<FieldLabel info="Shown to people who pick this agent.">Display name</FieldLabel>
Added
InfoTooltip, the same info icon for use outside form labels, such as next to a heading or a toolbar control:import { InfoTooltip } from '@mastra/playground-ui/components/Tooltip'; <InfoTooltip aria-label="Version information" side="bottom" align="start"> Each save creates a version you can switch back to. </InfoTooltip>;
TooltipContenttakeskeepMountedto keep a closed tooltip in the page (hidden), for example so its trigger can pointaria-describedbyat it.Removed
RequestContextLabel(@mastra/playground-ui/domains/request-context/components/request-context-label). UseFieldLabelwithinfoinstead:<FieldLabel info="Values passed to the agent at runtime.">Request context</FieldLabel>
Patch Changes
-
Fixed avatars that stayed on the fallback initial after an image failed. They now show the new image when
srcchanges. (#27097) -
Fixed
formatModelNamefor Bedrock model ids. Ids such asus.anthropic.claude-sonnet-4-5-20250929-v1:0now read as "Claude Sonnet 4.5" instead of "Us.anthropic.claude Sonnet 4.5 20250929 V1:0". (#27001) -
Fixed scrolling in long dropdown menus, context menus and selects. Their scrollbars now match the rest of the app and stay clear of the popup's rounded corners. (#27124)
-
Added an optional
keywordsfield toComboboxoptions. Search matches these terms as well as the label, so an option can show a friendly label and still be found by an id or alias. (#27000)<Combobox options={[ { label: 'Claude Haiku 4.5', value: 'anthropic/claude-haiku-4-5', keywords: ['anthropic/claude-haiku-4-5'] }, ]} />
-
Added an optional
renderDragPreviewprop toContentBlocks. When set, the dragged item is rendered ondocument.body, so a list inside a menu or popover no longer shows the dragged item far from the pointer. (#27028)<ContentBlocks items={groups} onChange={setGroups} renderDragPreview={group => <GroupRow group={group} />}> {groups.map((group, index) => ( <ContentBlock key={group.id} draggableId={group.id} index={index}> {dragHandleProps => <GroupRow group={group} dragHandleProps={dragHandleProps} />} </ContentBlock> ))} </ContentBlocks>
-
Added an
infotone toDataListrows. It adds a faint blue tint to set a row apart from the others, for example to mark the rows that match the current selection. Hover and selection still show through the tint. (#27055)<DataList.RowLink to={href} variant={isComparable ? 'info' : 'default'}> {cells} </DataList.RowLink>
-
Fixed the hover highlight in lists with collapsed sections. Rows in a collapsed section are now skipped, so the highlight keeps following the visible rows instead of staying at their old positions. (#27068)
-
Added
describeSuggestionsErrortoFilterBarso apps can explain why value suggestions failed to load, for example when a server limit was hit. Returnundefinedto keep the default "Couldn't load values." text. (#27083)<FilterBar fields={fields} operators={operators} value={value} onValueChange={setValue} describeSuggestionsError={error => (isTooExpensive(error) ? 'Too many values. Narrow the time range.' : undefined)} > <FilterBar.Chips /> <FilterBar.Input /> </FilterBar>
-
Fixed the Expand button on clipped collapsible boxes looking see-through: it now uses the default solid button style, so it stays readable over the faded content. (#27084)
-
Fixed scrollbars in rounded popups, popovers, command lists and the composer so their ends no longer get cut off by the rounded corners. (#27124)
-
Changed
PageLayoutto scroll its body in aScrollAreainstead of reserving a stable scrollbar gutter. The body no longer shifts sideways when a scrollbar appears, and pages no longer show an empty strip beside their content. AddedcontentClassNametoScrollAreaViewportto style the wrapper around its children. (#27018)Added a
maskprop toPageLayoutto fade the body's scrolled edges, using the same values asScrollArea. It is off by default. A fade covers whatever sits at that edge, so leave it off for bodies with sticky table headers.<PageLayout mask={{ y: true }}>{/* ... */}</PageLayout>
-
Fixed visual glitches when switching tabs in
TabbedContainer. The search and filter controls now switch together with the table, so two search fields no longer overlap mid-switch and the panel corner no longer shows a gap or a stray control. (#26810) -
Fixed code blocks updating state after they are removed from the page, which could throw
window is not definedin tests. (#27104) -
Fixed section labels in dropdown and context menus so they line up with the text of the items under them, including inset labels. (#27120)
-
Fixed Studio showing a resumed workflow run as Suspended. This happened when the run was resumed from code or the API, such as
run.resume(). Studio asked for approval again. Studio now shows the run as running, with the approved step marked complete. (#26939) -
Added a
flushvariant toInputGroupAddon. Use it to place adjacent number steppers at the field edge without a gap or edge inset. (#27017)<InputGroupAddon align="inline-end" variant="flush"> <InputNumberDecrement /> <InputNumberIncrement /> </InputGroupAddon>
-
Fixed field labels catching clicks across the whole row. Clicking the empty space beside a text label no longer focuses its field; only the label text does. Labels that wrap a checkbox, switch or radio still toggle it from anywhere in the row. (#27110)
-
Studio traces: the error screen shows the server's reason instead of a generic message, and auto refresh stops retrying a request the server rejected so the page no longer flickers between the list and the error.
DateTimeRangePickeralso accepts an optionalmaxRangeMsprop that blocks custom ranges longer than the given duration. (#27076)
@mastra/react@1.11.0
Minor Changes
- Added
useTraceAggregateAvailable(), which reports whether the server supports trace aggregation. A server that does not report the capability is treated as not supporting it. (#27185)
Patch Changes
-
Review items returned by
useReviewItemsanduseCompletedItemsnow includetargetType,startedAtandcompletedAtfrom the experiment result, so a review item can be rendered exactly like the same result on its experiment page (agent answer as text, run duration). (#27084) -
Fixed
useChatleaving a pending user message in the chat after a send fails with thread signals enabled. The optimistic message is now removed when the server never accepted it, so retrying shows the message once instead of a stuck pending copy plus the retried one. (#26975)
@mastra/schema-compat@1.3.15
Patch Changes
- Fixed Azure OpenAI GPT models filling optional tool inputs with made-up values. They now leave optional inputs empty, as they already do through
@ai-sdk/openai. This applies when the Azure deployment name contains an OpenAI model name, such asgpt-4o,my-gpt5-deploymentoro3-mini. Deployments with other names, such asproduction, and non-OpenAI models served through Azure behave as before. (#26680)
@mastra/server@1.78.0
Minor Changes
-
Added
traceAggregatetoGET /observability/capabilities, so clients can tell whetherPOST /observability/traces/aggregateis available. Trace-scope field discovery (POST /observability/traces/query/fields) now also returns anaggregateblock listing the groupable dimensions and available measures when the store supports aggregation. (#27185) -
Added
POST /agents/:agentId/queue-signal, which queues a typed signal to run after the active thread run completes, or starts a run right away when the thread is idle. The body takes the samesignalshape asPOST /agents/:agentId/signals. (#27081)curl -X POST http://localhost:4111/api/agents/support-agent/queue-signal \ -H 'Content-Type: application/json' \ -d '{ "resourceId": "user-123", "threadId": "thread-123", "signal": { "type": "reactive", "tagName": "skill", "attributes": { "name": "triage" }, "contents": "Triage the new issue." } }'
Patch Changes
-
Agent scopes support: (#26593)
-
Agent routes ignore
scopesin request bodies (includingifIdle.streamOptions.scopes), andmastra__scopesis a reserved request context key. -
Set scopes from trusted server middleware instead:
import { MASTRA_SCOPES_KEY } from '@mastra/core/request-context'; server: { middleware: [async (c, next) => { c.get('requestContext').set(MASTRA_SCOPES_KEY, [`org:${orgIdFromAuth(c)}`]); await next(); }], }
-
A conflicting
resource:orthread:scope returns a 400 error. -
With an older
@mastra/serverregistered, agent calls that pass scopes — from the call or the request context — fail withAGENT_SCOPES_SERVER_OUTDATED(HTTP 400). Scopes configured on the agent itself still work. Upgrade the server to lift the restriction.
-
-
Tightened ownership checks on workflow run routes when
server.auth.mapUserToResourceIdis configured. A caller mapped to a resource can only act on its own runs, including when it passes an existing run id to/stream,/create-run,/start-asyncor/stream-legacy, and when it resumes, recovers or approves a durable agent run. The worker endpointssteps/executeand/workflows/eventsapply the same check to the run they address. Restarting all active runs of a workflow now requires a privileged caller. A caller without a resource id is treated as privileged only when a fine-grained authorization (FGA) provider is configured to authorize the route. (#26958)Before: with
mapUserToResourceIdconfigured, a caller with no mapped resource id could read and act on any run, and a mapped caller could act on runs saved without a resource id.After: a mapped caller acts only on its own runs. Admin, service, worker and push-broker callers that are not mapped to a resource need an FGA provider for run access, and restarting all active runs requires such a privileged caller. Runs saved without a resource id are no longer reachable by mapped callers.
To keep privileged run access, configure an FGA provider next to the mapper:
export const mastra = new Mastra({ server: { auth: { authenticateToken: async token => verifyAccessToken(token), mapUserToResourceId: user => `${user.workspaceId}:${user.id}`, }, fga: fgaProvider, }, });
Servers without
mapUserToResourceIdkeep the previous rule: deny only when the caller's resource id and the run's resource id are both present and differ. That rule now also runs on routes that had no ownership check:/stream,/create-run,/start-async,/stream-legacy,steps/execute,/workflows/events, and the durable agent resume and recover routes. Reusing another resource's run id on those routes used to succeed and now returns 403.
@mastra/voice-sarvam@1.1.2
Patch Changes
-
Fixed
SarvamVoice.listen()sending the speech model's API key.listen()now usesoptions.apiKey, then the listening model's key, andspeak()uses the speech model's key, so projects with separate Sarvam keys for speech-to-text and text-to-speech bill each to the right key. (#27206)const voice = new SarvamVoice({ speechModel: { apiKey: 'tts-key' }, listeningModel: { apiKey: 'stt-key' }, }); await voice.listen(audio); // uses 'stt-key' await voice.listen(audio, { apiKey: 'per-call-key' }); // uses 'per-call-key'
-
SarvamVoice.listen()now throws on Sarvam API errors (e.g. invalid API key, rate limits) instead of silently returningundefined. Error messages for bothlisten()andspeak()now surface Sarvam'serror.message. (#27173)Before, a failed
listen()call returnedundefined. Now it throws, so handle it withtry/catch:try { const text = await voice.listen(audio); } catch (error) { // error.message: "Sarvam AI API Error: <message>" }
-
Fixed
SarvamVoice.listen()ignoring thelisteningModelpassed to the constructor. A voice created withlisteningModel: { model: 'saaras:v3', languageCode: 'hi-IN', mode: 'translit' }now transcribes with that model, language and mode; before, it always sentsaarika:v2.5with automatic language detection. Options passed tolisten()still take precedence. (#27102)
@mastra/weaviate@0.1.2
Patch Changes
- Fixed
$infilters with an empty array, which made Weaviate queries fail on text-array metadata. An empty$innow matches nothing and an empty$ninmatches everything, like the other vector stores. (#26917)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agent-builder@1.1.29
- @mastra/arize@1.3.25
- @mastra/arthur@0.4.25
- @mastra/braintrust@1.3.22
- @mastra/connect@1.0.3
- @mastra/datadog@1.4.17
- @mastra/deepeval@0.1.19
- @mastra/deployer-cloud@1.78.0
- @mastra/deployer-netlify@1.2.37
- @mastra/deployer-vercel@1.2.37
- @mastra/e2b-desktop@0.1.8
- @mastra/editor@0.16.3
- @mastra/elysia@0.1.18
- @mastra/express@1.5.22
- @mastra/fastify@1.5.22
- @mastra/hono@1.7.20
- @mastra/koa@1.7.22
- @mastra/laminar@1.3.27
- @mastra/langfuse@1.6.1
- @mastra/langsmith@1.3.27
- @mastra/longmemeval@1.1.37
- @mastra/mcp-docs-server@1.3.8
- @mastra/next@0.2.36
- @mastra/opencode@0.1.37
- @mastra/otel-bridge@1.5.17
- @mastra/posthog@1.4.2
- @mastra/sentry@1.2.27
- @mastra/tanstack-start@0.2.36
- @mastra/temporal@0.4.16
- @mastra/turso@0.1.16
- @mastra/voice-google-gemini-live@0.14.16
- @mastra/voice-openai-realtime@0.14.6
- @mastra/voice-xai-realtime@0.2.15