March 11, 2026
Agents can now use model functions that return full fallback arrays (ModelWithRetries[]), enabling context-driven model routing (tier/region/etc.) with nested/async selection and proper maxRetries inheritance.
Mastra adds Standard Schema normalization (toStandardSchema, standardSchemaToJSONSchema) across Zod v3/v4, AI SDK Schema, and JSON Schema via @mastra/schema-compat, unifying schema handling and improving strict-mode provider compatibility.
New onValidationError hook on ServerConfig and createRoute() lets you control status codes and response bodies for Zod validation failures, supported consistently in Hono/Express/Fastify/Koa adapters.
requestContext is now captured on tracing spans (and persisted in ClickHouse/PG/LibSQL/MSSQL span tables) and is supported on dataset items and experiments, allowing request-scoped metadata (tenant/user/flags) to flow through evaluation and observability.
Semantic recall is significantly faster across multiple adapters (notably Postgres for very large threads), PgVector adds metadataIndexes for btree indexing filtered metadata fields, and @mastra/pg now supports pgvector bit and sparsevec vector types.
^3.25.0 (for v3) or ^4.0.0 (for v4 support).feat: support dynamic functions returning model fallback arrays (#11975)
Agents can now use dynamic functions that return entire fallback arrays based on runtime context. This enables:
const agent = new Agent({
model: ({ requestContext }) => {
const tier = requestContext.get('tier');
if (tier === 'premium') {
return [
{ model: 'openai/gpt-4', maxRetries: 2 },
{ model: 'anthropic/claude-3-opus', maxRetries: 1 },
];
}
return [{ model: 'openai/gpt-3.5-turbo', maxRetries: 1 }];
},
});
const agent = new Agent({
model: ({ requestContext }) => {
const region = requestContext.get('region');
return [
{
model: ({ requestContext }) => {
// Select model variant based on region
return region === 'eu' ? 'openai/gpt-4-eu' : 'openai/gpt-4';
},
maxRetries: 2,
},
{ model: 'anthropic/claude-3-opus', maxRetries: 1 },
];
},
maxRetries: 1, // Agent-level default for models without explicit maxRetries
});
const agent = new Agent({
model: async ({ requestContext }) => {
// Fetch user's tier from database
const userId = requestContext.get('userId');
const user = await db.users.findById(userId);
if (user.tier === 'enterprise') {
return [
{ model: 'openai/gpt-4', maxRetries: 3 },
{ model: 'anthropic/claude-3-opus', maxRetries: 2 },
];
}
return [{ model: 'openai/gpt-3.5-turbo', maxRetries: 1 }];
},
});
MastraModelConfig (single model) or ModelWithRetries[] (array)maxRetries inherit agent-level maxRetries defaultModelFallbacks with all required fields filled inmaxRetries when not explicitly specifiedgetModelList() now correctly handles dynamic functions that return arraysgetLLM() and getModel() return behavior while adding dynamic fallback array supportNo breaking changes. All existing model configurations continue to work:
model: 'openai/gpt-4'model: [{ model: 'openai/gpt-4', maxRetries: 2 }]model: ({ requestContext }) => 'openai/gpt-4'model: ({ requestContext }) => [{ model: 'openai/gpt-4', maxRetries: 2 }]Closes #11951
Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)
const mastra = new Mastra({
server: {
onValidationError: (error, context) => ({
status: 422,
body: {
ok: false,
errors: error.issues.map(i => ({
path: i.path.join('.'),
message: i.message,
})),
source: context,
},
}),
},
});
Added requestContext field to tracing spans. Each span now automatically captures a snapshot of the active RequestContext, making request-scoped values like user IDs, tenant IDs, and feature flags available when viewing traces. (#14020)
Added allowedWorkspaceTools to HarnessSubagent. Subagents now automatically inherit the parent agent's workspace. Use allowedWorkspaceTools to restrict which workspace tools a subagent can see: (#13940)
const subagent: HarnessSubagent = {
id: 'explore',
name: 'Explore',
allowedWorkspaceTools: ['view', 'search_content', 'find_files'],
};
Enabled tracing for tool executions through mcp server (#12804)
Traces now appear in the Observability UI for MCP server tool calls
Added result to processOutputResult args, providing resolved generation data (usage, text, steps, finishReason) directly. This replaces raw stream chunks with an easy-to-use OutputResult object containing the same data available in the onFinish callback. (#13810)
const usageProcessor: Processor = {
id: 'usage-processor',
processOutputResult({ result, messages }) {
console.log(`Text: ${result.text}`);
console.log(`Tokens: ${result.usage.inputTokens} in, ${result.usage.outputTokens} out`);
console.log(`Finish reason: ${result.finishReason}`);
console.log(`Steps: ${result.steps.length}`);
return messages;
},
};
Added requestContext support for dataset items and experiments. (#13938)
Dataset items now accept an optional requestContext field when adding or updating items. This lets you store per-item request context alongside inputs and ground truths.
Datasets now support a requestContextSchema field to describe the expected shape of request context on items.
Experiments now accept a requestContext option that gets passed through to agent.generate() during execution. Per-item request context merges with (and takes precedence over) the experiment-level context.
// Add item with request context
await dataset.addItem({
input: messages,
groundTruth: expectedOutput,
requestContext: { userId: '123', locale: 'en' },
});
// Run experiment with global request context
await runExperiment(mastra, {
datasetId: 'my-dataset',
targetType: 'agent',
targetId: 'my-agent',
requestContext: { environment: 'staging' },
});
Add Zod v4 and Standard Schema support (#12238)
z.record() calls to use 2-argument form (key + value schema) as required by Zod v4ZodError.errors to ZodError.issues (Zod v4 API change)@ai-sdk/provider versions for Zod v4 compatibilitypackages/core/src/schema/ module that re-exports from @mastra/schema-compatPublicSchema type for schema parameterstoStandardSchema() for normalizing schemas across Zod v3, Zod v4, AI SDK Schema, and JSON SchemastandardSchemaToJSONSchema() for JSON Schema conversion@mastra/schema-compat/adapters/ai-sdk, @mastra/schema-compat/adapters/zod-v3, @mastra/schema-compat/adapters/json-schemaunrepresentable: 'any' supportBREAKING CHANGE: Minimum Zod version is now ^3.25.0 for v3 compatibility or ^4.0.0 for v4
Update provider registry and model documentation with latest models and providers (332c014)
fix(workflows): add generic bail signature with overloads. The bail() function now uses method overloads - bail(result: TStepOutput) for backward compatibility and bail<T>(result: ...) for workflow type inference. This allows flexible early exits while maintaining type safety for workflow chaining. Runtime validation will be added in a follow-up. (#12211)
Fixed structured output parsing when JSON string fields include fenced JSON examples. (#13948)
Fixed writer being undefined in processOutputStream for all output processors. The root cause was that processPart in ProcessorRunner did not pass the writer to executeWorkflowAsProcessor in the outputStream phase. Since all user processors are wrapped into workflows via combineProcessorsIntoWorkflow, this meant no processor ever received a writer. Custom output processors (like guardrail processors) can now reliably use writer.custom() to emit stream events. (#14111)
Added JSON repair for malformed tool call arguments from LLM providers. When an LLM (e.g., Kimi/K2) generates broken JSON for tool call arguments, Mastra now attempts to fix common errors (missing quotes on property names, single quotes, trailing commas) before falling back to undefined. This reduces silent tool execution failures caused by minor JSON formatting issues. See https://github.com/mastra-ai/mastra/issues/11078 (#14033)
Fixed Windows shell command execution to avoid visible cmd.exe popups and broken output piping. (#13886)
Fixed OpenAI reasoning models (e.g. gpt-5-mini) failing with "function*call was provided without its required reasoning item" when the agent loops back after a tool call. The issue was that callProviderMetadata.openai carrying fc*\*item IDs was not being stripped alongside reasoning parts, causing the AI SDK to senditem_referenceinstead of inlinefunction_call content. (#14144)
Output processors can now inspect, modify, or block custom data-* chunks emitted by tools via writer.custom() during streaming. Processors must opt in by setting processDataParts = true to receive these chunks in processOutputStream. (#13823)
class MyDataProcessor extends Processor {
processDataParts = true;
processOutputStream(part, { abort }) {
if (part.type === 'data-sensitive') {
abort('Blocked sensitive data');
}
return part;
}
}
Fixed agent tool calls not being surfaced in evented workflow streams. Added StreamChunkWriter abstraction and stream format configuration ('legacy' | 'vnext') to forward agent stream chunks through the workflow output stream. (#12692)
Fixed OpenAI strict mode schema rejection when using agent networks with structured output. The compat layer was skipped when modelId was undefined, causing optional fields to be missing from the required array. (Fixes #12284) (#13695)
Fixed activeTools to also enforce at execution time, not just at the model prompt. Tool calls to tools not in the active set are now rejected with a ToolNotFoundError. (#13949)
Fix build failures on Windows when running build:patch-commonjs during pnpm run setup (#14029)
Experiments now fail immediately with a clear error when triggered on a dataset with zero items, instead of getting stuck in "pending" status forever. The experiment trigger API returns HTTP 400 for empty datasets. Unexpected errors during async experiment setup are now logged and mark the experiment as failed. (#14031)
fix: respect lastMessages: false in recall() to disable conversation history (#12951)
Setting lastMessages: false in Memory options now correctly prevents recall() from returning previous messages. Previously, the agent would retain the full conversation history despite this setting being disabled.
Callers can still pass perPage: false explicitly to recall() to retrieve all messages (e.g., for displaying thread history in a UI).
@mastra/core: patch (#14103)
Fixed reasoning content being lost in multi-turn conversations with thinking models (kimi-k2.5, DeepSeek-R1) when using OpenAI-compatible providers (e.g., OpenRouter).
Previously, reasoning content could be discarded during streaming, causing 400 errors when the model tried to continue the conversation. Multi-turn conversations now preserve reasoning content correctly across all turns.
fix(workflows): propagate logger to executionEngine (#12517)
When a custom logger is set on a Workflow via __registerPrimitives or __setLogger, it is now correctly propagated to the internal executionEngine. This ensures workflow step execution errors are logged through the custom logger instead of the default ConsoleLogger, enabling proper observability integration.
Added permission denied handling for dataset pages. Datasets now show a "Permission Denied" screen when the user lacks access, matching the behavior of agents, workflows, and other resources. (#13876)
Fixed stream freezing when using Anthropic's Programmatic Tool Calling (PTC). Streams that contain only tool-input streaming chunks (without explicit tool-call chunks) now correctly synthesize tool-call events and complete without hanging. See #12390. (#12400)
Fixed subagents receiving parent's tool call/result parts in their context messages. On subsequent queries in a conversation, these references to tools the subagent doesn't have caused models (especially via custom gateways) to return blank or incorrect results. Parent delegation tool artifacts are now stripped from context before forwarding to subagents. (#13927)
Memory now automatically creates btree indexes on thread_id and resource_id metadata fields when using PgVector. This prevents sequential scans on the memory_messages vector table, resolving performance issues under high load. (#14034)
Fixes #12109
Clarified the idGenerator documentation to reflect the current context-aware function signature and documented the available IdGeneratorContext fields used for type-specific ID generation. (#14081)
Reasoning content from OpenAI models is now stripped from conversation history before replaying it to the LLM, preventing API errors on follow-up messages while preserving reasoning data in the database. Fixes #12980. (#13418)
Added transient option for data chunks to skip database persistence. Chunks marked as transient are streamed to the client for live display but not saved to storage, reducing bloat from large streaming outputs. (#13869)
await context.writer?.custom({
type: 'data-my-stream',
data: { output: line },
transient: true,
});
Workspace tools now use this to mark stdout/stderr streaming chunks as transient.
Fixed message ID mismatch between generate/stream response and memory-stored messages. When an agent used memory, the message IDs returned in the response (e.g. response.uiMessages[].id) could differ from the IDs persisted to the database. This was caused by a format conversion that stripped message IDs during internal re-processing. Messages now retain their original IDs throughout the entire save pipeline. (#13796)
Fixed assistant messages to persist content.metadata.modelId during streaming. (#12969)
This ensures stored and processed assistant messages keep the model identifier.
Developers can now reliably read content.metadata.modelId from downstream storage adapters and processors.
Fixed savePerStep: true not actually persisting messages to storage during step execution. Previously, onStepFinish only accumulated messages in the in-memory MessageList but never flushed them to the storage backend. The only persistence path was executeOnFinish, which is skipped when the stream is aborted. Now messages are flushed to storage after each completed step, so they survive page refreshes and stream aborts. Fixes https://github.com/mastra-ai/mastra/issues/13984 (#14030)
Fixed agentic loop continuing indefinitely when model hits max output tokens (finishReason: 'length'). Previously, only 'stop' and 'error' were treated as termination conditions, causing runaway token generation up to maxSteps when using structuredOutput with generate(). The loop now correctly stops on 'length' finish reason. Fixes #13012. (#13861)
Fixed tool-call arguments being silently lost when LLMs append internal tokens to JSON (#13400)
LLMs (particularly via OpenRouter and OpenAI) sometimes append internal tokens like <|call|>, <|endoftext|>, or <|end|> to otherwise valid JSON in streamed tool-call arguments. Previously, these inputs would fail JSON.parse and the tool call would silently lose its arguments (set to undefined).
Now, sanitizeToolCallInput strips these token patterns before parsing, recovering valid data that was previously discarded. Valid JSON containing <|...|> inside string values is left untouched. Truly malformed JSON still gracefully returns undefined.
Fixes https://github.com/mastra-ai/mastra/issues/13185 and https://github.com/mastra-ai/mastra/issues/13261.
Fixed agent loop stopping prematurely when LLM returns tool calls with finishReason 'stop'. Some models (e.g., OpenAI gpt-5.3-codex) return 'stop' even when tool calls are present, causing the agent to halt instead of processing tool results and continuing. The agent now correctly continues the loop whenever tool calls exist, regardless of finishReason. (#14043)
Fixed (#14133)
Fixed observational memory activation using outdated buffered observations in some long-running threads. Activation now uses the latest thread state so the correct observations are promoted. (#13955)
Fixed model fallback retry behavior. Non-retryable errors (401, 403) are no longer retried on the same model before falling back. Retryable errors (429, 500) are now only retried by a single layer (p-retry) instead of being duplicated across two layers, preventing (maxRetries + 1)² total calls. The per-model maxRetries setting now correctly controls how many times p-retry retries on that specific model before the fallback loop moves to the next model. (#14039)
Added processor-driven response message ID rotation so streamed assistant IDs use the rotated ID. (#13887)
Processors that run outside the agent loop no longer need synthetic response message IDs.
Fixed a regression where dynamic model functions returning a single v1 model were treated as model arrays. (#14018)
Fixed requestContext not being forwarded to tools dynamically added by input processors. (#13827)
Added 'sandbox_access_request' to the HarnessEvent union type, enabling type-safe handling of sandbox access request events without requiring type casts. (#13648)
Fix wrong threadId and resourceId being sent to subagent (#13868)
handleChatStream not merging providerOptions from params and defaultOptions. Previously, params.providerOptions would completely replace defaultOptions.providerOptions instead of merging them. Now provider-specific keys from both sources are merged, with params.providerOptions taking precedence for the same provider. (#13820)requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)
For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.
Fixed slow semantic recall in the libsql, Cloudflare D1, and ClickHouse storage adapters. Recall performance no longer degrades as threads grow larger. (Fixes #11702) (#14022)
Added requestContext field to dataset item API endpoints and requestContextSchema to dataset CRUD endpoints. Added requestContext option to the experiment trigger endpoint, which gets forwarded to agent execution during experiments. (#13938)
Usage with @mastra/client-js:
// Create a dataset with a request context schema
await client.createDataset({
name: 'my-dataset',
requestContextSchema: {
type: 'object',
properties: { region: { type: 'string' } },
},
});
// Add an item with request context
await client.addDatasetItem({
datasetId: 'my-dataset',
input: { prompt: 'Hello' },
requestContext: { region: 'us-east-1' },
});
// Trigger an experiment with request context forwarded to agent
await client.triggerDatasetExperiment({
datasetId: 'my-dataset',
agentId: 'my-agent',
requestContext: { region: 'eu-west-1' },
});
Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)
For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.
Fixed slow semantic recall in the libsql, Cloudflare D1, and ClickHouse storage adapters. Recall performance no longer degrades as threads grow larger. (Fixes #11702) (#14022)
key:value format (e.g. instance_name:career-scout-api) were having :true appended to the value in the Datadog UI, resulting in career-scout-api:true instead of career-scout-api. Tags are now correctly split into proper key-value pairs before being sent to Datadog's LLM Observability API. (#13900)x-mastra-dev-playground header to the allowed CORS headers list. This resolves the browser error when the playground UI (running on a different port) makes requests to the Mastra dev server. (#14097)Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)
const mastra = new Mastra({
server: {
onValidationError: (error, context) => ({
status: 422,
body: {
ok: false,
errors: error.issues.map(i => ({
path: i.path.join('.'),
message: i.message,
})),
source: context,
},
}),
},
});
Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)
const mastra = new Mastra({
server: {
onValidationError: (error, context) => ({
status: 422,
body: {
ok: false,
errors: error.issues.map(i => ({
path: i.path.join('.'),
message: i.message,
})),
source: context,
},
}),
},
});
Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)
const mastra = new Mastra({
server: {
onValidationError: (error, context) => ({
status: 422,
body: {
ok: false,
errors: error.issues.map(i => ({
path: i.path.join('.'),
message: i.message,
})),
source: context,
},
}),
},
});
Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)
const mastra = new Mastra({
server: {
onValidationError: (error, context) => ({
status: 422,
body: {
ok: false,
errors: error.issues.map(i => ({
path: i.path.join('.'),
message: i.message,
})),
source: context,
},
}),
},
});
Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)
For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.
Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)
Added requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)
Added requestContext and requestContextSchema column support to dataset storage. Dataset items now persist request context alongside input and ground truth data. (#13938)
Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)
For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.
Fixed slow semantic recall in the libsql, Cloudflare D1, and ClickHouse storage adapters. Recall performance no longer degrades as threads grow larger. (Fixes #11702) (#14022)
Add image and file attachment support to Observational Memory. The observer can now see and reason about images and files in conversation history, and attachment token counts are included in observation thresholds. Provider-backed token counting is used when available, with results cached for faster subsequent runs. (#13953)
Added observational memory repro tooling for recording, analyzing, and sanitizing captures before sharing them. (#13877)
fix: respect lastMessages: false in recall() to disable conversation history (#12951)
Setting lastMessages: false in Memory options now correctly prevents recall() from returning previous messages. Previously, the agent would retain the full conversation history despite this setting being disabled.
Callers can still pass perPage: false explicitly to recall() to retrieve all messages (e.g., for displaying thread history in a UI).
fix(memory): handle dynamic functions returning ModelWithRetries[] in observational memory model resolution (#13902)
Fixed observational memory activation using outdated buffered observations in some long-running threads. Activation now uses the latest thread state so the correct observations are promoted. (#13955)
Fixed message loss when saving certain messages so text content is preserved. (#13918)
Added a compatibility guard so observational memory now fails fast when @mastra/core does not support request-response-id-rotation. (#13887)
requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)
For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.
Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)
requestContext field to tracing spans. Each span now automatically captures a snapshot of the active RequestContext, making request-scoped values like user IDs, tenant IDs, and feature flags available when viewing traces. (#14020)Added metadataIndexes option to createIndex() for PgVector. This allows creating btree indexes on specific metadata fields in vector tables, significantly improving query performance when filtering by those fields. This is especially impactful for Memory's memory_messages table, which filters by thread_id and resource_id — previously causing sequential scans under load. (#14034)
Usage example:
await pgVector.createIndex({
indexName: 'my_vectors',
dimension: 1536,
metadataIndexes: ['thread_id', 'resource_id'],
});
Fixes #12109
Add support for pgvector's bit and sparsevec vector storage types (#12815)
You can now store binary and sparse vectors in @mastra/pg:
// Binary vectors for fast similarity search
await db.createIndex({
indexName: 'my_binary_index',
dimension: 128,
metric: 'hamming', // or 'jaccard'
vectorType: 'bit',
});
// Sparse vectors for BM25/TF-IDF representations
await db.createIndex({
indexName: 'my_sparse_index',
dimension: 500,
metric: 'cosine',
vectorType: 'sparsevec',
});
What's new:
vectorType: 'bit' for binary vectors with 'hamming' and 'jaccard' distance metricsvectorType: 'sparsevec' for sparse vectors (cosine, euclidean, dotproduct)bit defaults to 'hamming' when no metric is specifiedincludeVector round-trips work correctly for all vector typesAdded requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)
Added requestContext and requestContextSchema column support to dataset storage. Dataset items now persist request context alongside input and ground truth data. (#13938)
Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)
For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.
Fixed slow semantic recall in the Postgres storage adapter for large threads. Recall now completes in under 500ms even for threads with 7,000+ messages, down from ~30 seconds. (Fixes #11702) (#14022)
Added Playground and Traces tabs to the agent detail page. (#13938)
Agent Playground tab provides a side-by-side environment for iterating on agent configuration (instructions, tools, model settings) and testing changes in a live chat — without modifying the deployed agent. Includes version comparison, request context configuration, and the ability to trigger dataset experiments directly from the playground.
Agent Traces tab shows a compact table of all agent traces with columns for status, timestamp, input preview, output preview, and duration. Supports date range filtering, infinite scroll pagination, and clicking rows to inspect full trace details. Includes checkbox selection and bulk "Add to dataset" for quickly building evaluation datasets from production traces.
Tools edit page now shows configured (enabled) tools in a dedicated section at the top, making it easier to find and edit tools that are already in use.
Dataset save actions on the test chat: per-message save button on user messages and a "Save full conversation" action at the bottom of the thread.
Added input message preview column to the observability trace list. You can now see a truncated preview of user input messages directly in the trace table, making it easier to find specific traces without clicking into each one. (#14025)
The "Run Experiment" button is now disabled when a dataset has no items, with a tooltip explaining that items must be added first. (#14031)
fix: maxTokens from Studio Advanced Settings now correctly limits model output (#13912)
The modelSettingsArgs object was prematurely renaming maxTokens to maxOutputTokens. The React hook (useChat) destructures maxTokens from this object, so the rename caused it to receive undefined, and the value was silently dropped from the API request body.
Added permission denied handling for dataset pages. Datasets now show a "Permission Denied" screen when the user lacks access, matching the behavior of agents, workflows, and other resources. (#13876)
Fixed sandbox execution badge to show full streaming output during live sessions instead of snapping to the truncated tool result. The truncated view now only appears after page refresh when streaming data is no longer available. (#13869)
Fix the observational memory sidebar so the observation token label uses the live observation window token count shown by the progress UI. (#13953)
Add Zod v4 and Standard Schema support (#12238)
z.record() calls to use 2-argument form (key + value schema) as required by Zod v4ZodError.errors to ZodError.issues (Zod v4 API change)@ai-sdk/provider versions for Zod v4 compatibilitypackages/core/src/schema/ module that re-exports from @mastra/schema-compatPublicSchema type for schema parameterstoStandardSchema() for normalizing schemas across Zod v3, Zod v4, AI SDK Schema, and JSON SchemastandardSchemaToJSONSchema() for JSON Schema conversion@mastra/schema-compat/adapters/ai-sdk, @mastra/schema-compat/adapters/zod-v3, @mastra/schema-compat/adapters/json-schemaunrepresentable: 'any' supportBREAKING CHANGE: Minimum Zod version is now ^3.25.0 for v3 compatibility or ^4.0.0 for v4
Fixed Gemini supervisor agent tool calls failing with INVALID_ARGUMENT when delegated tool schemas include nullable fields. Fixes #13988. (#14012)
Fixed OpenAI and OpenAI Reasoning compat layers to ensure all properties appear in the JSON Schema required array when using processToJSONSchema. This prevents OpenAI strict mode rejections for schemas with optional, default, or nullish fields. (Fixes #12284) (#13695)
Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)
const mastra = new Mastra({
server: {
onValidationError: (error, context) => ({
status: 422,
body: {
ok: false,
errors: error.issues.map(i => ({
path: i.path.join('.'),
message: i.message,
})),
source: context,
},
}),
},
});
Added requestContext field to dataset item API endpoints and requestContextSchema to dataset CRUD endpoints. Added requestContext option to the experiment trigger endpoint, which gets forwarded to agent execution during experiments. (#13938)
Usage with @mastra/client-js:
// Create a dataset with a request context schema
await client.createDataset({
name: 'my-dataset',
requestContextSchema: {
type: 'object',
properties: { region: { type: 'string' } },
},
});
// Add an item with request context
await client.addDatasetItem({
datasetId: 'my-dataset',
input: { prompt: 'Hello' },
requestContext: { region: 'us-east-1' },
});
// Trigger an experiment with request context forwarded to agent
await client.triggerDatasetExperiment({
datasetId: 'my-dataset',
agentId: 'my-agent',
requestContext: { region: 'eu-west-1' },
});
Experiments now fail immediately with a clear error when triggered on a dataset with zero items, instead of getting stuck in "pending" status forever. The experiment trigger API returns HTTP 400 for empty datasets. Unexpected errors during async experiment setup are now logged and mark the experiment as failed. (#14031)
Fixed getPublicOrigin to parse only the first value from the X-Forwarded-Host header. When requests pass through multiple proxies, each proxy appends its host to the header, creating a comma-separated list. The previous code used the raw value, producing a malformed URL that broke OAuth redirect URIs. Now only the first (client-facing) host is used, per RFC 7239. (#13935)
Add new models to GeminiVoiceModel type and mark deprecated models with @deprecated JSDoc. (#12625)
Added:
gemini-live-2.5-flash-native-audio (GA)gemini-live-2.5-flash-preview-native-audio-09-2025gemini-2.5-flash-native-audio-preview-12-2025gemini-2.5-flash-native-audio-preview-09-2025Deprecated:
gemini-2.0-flash-exp (shut down 2025-12-09)gemini-2.0-flash-exp-image-generation (shut down 2025-11-14)gemini-2.0-flash-live-001 (shut down 2025-12-09)gemini-live-2.5-flash-preview-native-audio (use gemini-live-2.5-flash-preview-native-audio-09-2025)gemini-2.5-flash-exp-native-audio-thinking-dialog (shut down 2025-10-20)gemini-live-2.5-flash-preview (shut down 2025-12-09)The following packages were updated with dependency changes only:
Fetched April 7, 2026