Highlights
Push-capable PubSub + HTTP workflow event ingestion
Mastra now supports push delivery for workflow events: PubSubs declare supportedModes, Mastra.handleWorkflowEvent(event) is the unified entry point, and servers expose POST /api/workflows/events so brokers like GCP Pub/Sub push/SNS/EventBridge can deliver events over HTTP (no pull worker required).
Response caching for identical LLM steps (ResponseCache)
A new opt-in ResponseCache input processor can skip model calls by replaying cached Responses per step in agent loops, with per-request overrides (key, scope, bust) via RequestContext. Adds InMemoryServerCache for local dev and per-entry TTL support in the cache interface (also implemented by @mastra/redis).
Agent Signals for “send while streaming” threaded chat
Agents can now accept contextual signals mid-run (agent.sendSignal) and clients can follow thread activity via agent.subscribeToThread, enabling reliable follow-ups while responses stream. This is supported end-to-end across @mastra/server, @mastra/client-js, and @mastra/react (stable signal IDs, dedupe, stop/abort behavior, and thread subscriptions).
Azure OpenAI Responses API (v1 routing) + WebSocket streaming transport
AzureOpenAIGateway now supports the Azure OpenAI Responses API with v1 routing controls (useResponsesAPI: true) and adds WebSocket transport for streaming agent/tool loops, improving streaming reliability and supporting real-time tool loop execution over WS.
New storage and integration options: Aurora DSQL + Bright Data tools
Introduces @mastra/dsql (Amazon Aurora DSQL storage with IAM auth) for persisting threads/workflows/observability, and @mastra/brightdata with brightdata-search/brightdata-fetch tools to bypass bot detection/CAPTCHAs for SERP + page fetching.
Observability upgrades: MODEL_INFERENCE spans, eval score unification, and OTEL logs
Adds MODEL_INFERENCE spans under MODEL_STEP to isolate pure provider latency, and routes Mastra Eval/scorer results through the unified score pipeline (and forwards them to multiple exporters). OTEL integrations now support log forwarding/export (@mastra/otel-bridge, @mastra/otel-exporter), and SensitiveDataFilter is applied by default to prevent secret leakage.
Breaking Changes
@mastra/inngestnow requiresinngest@^4(and Inngest Dev Serverv1.18.0+); remove@inngest/realtimeand import realtime helpers frominngest/realtime.@mastra/client-js: paginated list methods now requireorderBy: { field, direction }(flatsortDirectionremoved from affected param types);AgentBuilder.stream(...)now requiresrunId;createStoredSkill(...)now requiresdescription.@mastra/playground-ui:ScrollAreanow fades edges by default (opt out withmask={false}); removed exportsThreads,ThreadList,ThreadItem,ThreadLink,ThreadDeleteButton(no longer available downstream).
Changelog
@mastra/core@1.33.0
Minor Changes
-
Added
processLLMRequest, a processor hook that runs after messages are converted to the provider-facing prompt and before the model request is sent. The hook lets processors make temporary, model-aware prompt changes without mutating stored message history, memory, UI history, or later provider calls. (#16176)ProviderHistoryCompatnow uses this hook to prevent reasoning-history incompatibilities when switching providers. It strips reasoning parts from Cerebras-bound prompts that would otherwise be sent as rejectedreasoning_content, and strips non-Anthropic reasoning from Anthropic-bound prompts while preserving Anthropic-native thinking blocks. -
Improved Harness support for Agent thread signals. (#16231)
Harness thread subscriptions now own stream processing for followed runs, echo user-message signal data with stable IDs, and support idle signal starts without delaying optimistic rendering.
const { id, accepted } = harness.sendSignal({ type: 'user-message', contents: 'Follow up while the agent is still streaming', }); await accepted;
-
Added target-aware tool payload transforms for display streams and transcript messages. Tool authors can transform tool input, output, errors, approval payloads, and suspension payloads without changing raw runtime behavior or toModelOutput. See #16054. (#16103)
Use
transformon tools, agents, Mastra, or individual generation calls to configure these payload transforms. Runtime callers using the previoustoolPayloadProjectionshape continue to be normalized for compatibility.const lookupCustomer = createTool({ execute: async ({ customerId, internalPath }) => lookupCustomerRecord(customerId, internalPath), transform: { display: { input: ({ input }) => ({ customerId: input?.customerId }), output: ({ output }) => ({ displayName: output?.displayName }), }, transcript: { input: ({ input }) => ({ customerId: input?.customerId }), output: ({ output }) => ({ displayName: output?.displayName }), }, }, });
-
Added Azure OpenAI Responses API and v1 routing controls. (#16246)
Use
useResponsesAPI: trueto resolve Azure deployments through the Responses API with the Azure v1 route by default:new AzureOpenAIGateway({ resourceName: 'my-openai-resource', apiKey: process.env.AZURE_API_KEY!, useResponsesAPI: true, deployments: ['my-gpt-5-4-deployment'], });
When
useDeploymentBasedUrls: falseis used directly, the gateway now defaultsapiVersionto"v1"to match the AI SDK Azure provider's v1 URL route. PassingapiVersion: "v1"by itself keeps the existing deployment-based URL default for compatibility. -
Mastra Eval results are now emitted once through the unified observability score pipeline. (#16185)
-
Added Azure OpenAI Responses WebSocket transport support for streaming agent and tool loops. (#16246)
Configure the Azure gateway with
useResponsesAPI: true, then opt into WebSocket streaming per request:const stream = await agent.stream('Review this task', { providerOptions: { azure: { transport: 'websocket', websocket: { closeOnFinish: false }, }, }, });
Responses WebSocket streams now preserve transport handles through agent loops, reuse explicit API-key router connections safely, clean up cancelled streams, and reject overlapping
previous_response_idcontinuations on the same connection. -
Added
preserveModelOutputtoToolCallFilterso filtered tool history can keep compact model-facing output without raw tool args or results. (#16060)import { ToolCallFilter } from '@mastra/core/processors'; const filter = new ToolCallFilter({ preserveModelOutput: true, });
-
Added a SubAgent interface for custom supervisor subagents. (#16359)
-
Added
ResponseCacheinput processor (#16283)Cache identical LLM steps to skip the model call and replay a previously cached response. Useful for prompt templates, suggested-prompt buttons, agentic search re-asks, or guardrail LLMs that classify the same input over and over.
Caching is opt-in: register
ResponseCacheexplicitly oninputProcessors. There is no agent-level option — this keeps the surface small while we collect feedback on the processor API. Per-call overrides flow throughRequestContext.import { Agent } from '@mastra/core/agent'; import { InMemoryServerCache } from '@mastra/core/cache'; import { ResponseCache } from '@mastra/core/processors'; const cache = new InMemoryServerCache(); const agent = new Agent({ name: 'Search Agent', instructions: 'You answer questions concisely.', model: 'openai/gpt-5', inputProcessors: [new ResponseCache({ cache, ttl: 600 })], }); // First call: cache miss → LLM call await agent.generate('What is the capital of France?'); // Second identical call: cache hit → no LLM call await agent.generate('What is the capital of France?');
Per-call overrides via
RequestContext:import { ResponseCache } from '@mastra/core/processors'; import { RequestContext } from '@mastra/core/request-context'; // Force a fresh call but still update the cache. await agent.stream(prompt, { requestContext: ResponseCache.context({ bust: true }), }); // Or merge into an existing context. const ctx = new RequestContext(); ResponseCache.applyContext(ctx, { key: 'custom-key' }); await agent.stream(prompt, { requestContext: ctx });
Three fields are overridable per call:
key,scope,bust.cache,ttl, andagentIdstay on the constructor.A
keyfunction receives{ agentId, scope, model, prompt, stepNumber }and returns a string (orPromise<string>):await agent.stream(prompt, { requestContext: ResponseCache.context({ key: ({ model, prompt }) => `qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`, }), });
The cache key is derived from the resolved prompt Mastra is about to send to the model — i.e. after memory loading and earlier input processors have run — so cached entries are tenant-isolated and don't leak context across users with shared prompts but different memory state. Each step in an agentic tool loop is independently cached. By default, the cache scope falls back to
MASTRA_RESOURCE_ID_KEYfrom the request context for automatic per-user isolation. Failed runs (errors, tripwire activations) are not cached. See Response caching for details.Also adds:
InMemoryServerCache(in@mastra/core/cache) for local development.ResponseCacheaccepts anyMastraServerCachedirectly — useRedisCachefrom@mastra/redisfor production.MastraServerCache.set()now accepts an optionalttlMsargument so implementations can override the configured default TTL on a per-entry basis.InMemoryServerCacheandRedisCache(in@mastra/redis) both honor this.- New paired processor hooks
processLLMRequestandprocessLLMResponse.ProcessLLMRequestResultmay return{ response }to short-circuit the LLM call with a cached payload.
-
Added workflow state reader helpers to inspect persisted workflow runs and recover suspended or long-running workflows. (#16091)
The reader exposes suspended steps, resume labels, step payloads, and step outputs from the public WorkflowState returned by workflow.getWorkflowRunById(), and WorkflowState step results now reflect foreach array entries.
-
Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)
Call
agent.sendSignal()to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. UseifActive.behaviorandifIdle.behaviorto deliver, persist, discard, or wake from a signal.Use
agent.subscribeToThread()to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.const subscription = await agent.subscribeToThread({ resourceId, threadId }); void (async () => { for await (const part of subscription.stream) { if (part.type === 'finish') { subscription.unsubscribe(); } } })(); agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId });
-
Add metadata filtering support to semantic recall. (#9256)
-
Fixed Azure and OpenAI Responses item handling so multi-step reasoning and tool-call histories round-trip correctly without item ID collisions. (#16246)
Added provider-neutral response item helpers to
@mastra/core/agent/message-list. Existing in-memory message cache entries are regenerated after upgrade. -
Improved foreach workflow execution to keep concurrency slots filled as iterations finish. (#12860)
-
Added processor
sendSignalsupport and routed built-in system reminders through signal messages. (#16438) -
Added structured drop event types and an
onDroppedEventhook so exporters and bridge integrations can observe events dropped by the observability pipeline. (#16111) -
Added stable IDs to Harness task items plus
task_updateandtask_completefor updating or completing one tracked task by ID. Task tools now return structured task snapshots, andtask_checkreturnssummaryandincompleteTasksfields so agents and UIs can restore and verify task state without parsing text. (#16254)Harness also exports
TaskItemSnapshot,assignTaskIds, andharness.restoreDisplayTasks()for UI history replay, serializes task reads and mutations against the latest task state snapshot, and returns task-tool errors inside forked subagents so sidecar work cannot mutate parent task state. -
Added new
MODEL_INFERENCEspan type underMODEL_STEP, covering only the model provider call. Use it to measure model latency separately from input/output processors and tool executions. (#16267) -
Added experimental support for using remote A2A agents as Mastra subagents. (#16348)
What changed
- Mastra agents can register remote A2A endpoints through
A2AAgentand delegate to them like other subagents. - Remote A2A subagents support
generate,resumeGenerate,stream, andresumeStreamso parent agents can use them in normal subagent flows. - Agent Cards can be cached and verified with pluggable verification hooks before remote execution begins.
- Browser environments can import shared A2A types and errors from
@mastra/core/a2a/client.
Example
import { Agent } from '@mastra/core/agent'; import { A2AAgent } from '@mastra/core/a2a'; const agent = new Agent({ name: 'Support Agent', instructions: 'Use the remote billing specialist for billing questions.', model: 'openai/gpt-4o-mini', agents: { billingSpecialist: new A2AAgent({ url: 'https://billing.example.com/.well-known/agent-card.json', }), }, }); const result = await agent.generate('Can you check the latest invoice status?');
Why
This lets Mastra agents compose with remote A2A agents without exposing those integrations as plain tools or depending directly on the client SDK. - Mastra agents can register remote A2A endpoints through
-
Worker review fixes: (#16309)
- Step-execution endpoint (
POST /workflows/:id/runs/:runId/steps/execute) is
now gated by Mastra's standardrequiresAuth: true+authenticateToken
pipeline rather than a parallel "worker secret" body field. The previously
introducedworkerSecretconfig knob andMASTRA_WORKER_SECRETenv var
have been removed (they were never released). To gate the endpoint on a
standalone-worker deployment, configure an auth provider on the server's
Mastrainstance — without one the framework currently treats
requiresAuth: trueas a no-op for this route. HttpRemoteStrategynow sends credentials as a normalAuthorization: Bearer <token>header. The token comes from the new
MASTRA_WORKER_AUTH_TOKENenv var or an explicitauthconstructor option.- Honor the caller's
abortSignalinHttpRemoteStrategyby combining it
with the per-request timeout viaAbortSignal.any(with a manual fallback
for runtimes that don't expose it). - Implement comma-separated name filtering for the
MASTRA_WORKERSenv var.
MASTRA_WORKERS=scheduler,backgroundTasksnow boots only those named
workers;MASTRA_WORKERS=falsestill disables all workers. - Restore
Mastra.startEventEngine/stopEventEngineas@deprecated
aliases for the renamedstartWorkers/stopWorkers. BackgroundTaskWorkernow subscribes to PubSub instart()instead of
init(), matching the lifecycle of the other workers and making
isRunningaccurately reflect subscription state.RedisStreamsPubSubadds amaxDeliveryAttemptsoption (default 5) that
drops events after the configured number of failed deliveries instead of
redelivering forever, and replaces emptycatch {}blocks with
logger.warn/logger.debugcalls.RedisStreamsPubSub.unsubscribe(topic, cb)now honors the topic argument
so the same callback can be subscribed to multiple topics independently.PullTransportguards the async router callback against unhandled promise
rejections by attaching a.catchthat nacks the message.- Drop the dead
MASTRA_WORKER_NAMEenv var injection in the CLI worker
spawn (the bundle entrypoint already passes the worker name directly). - Add a real cross-process e2e auth suite
(pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong
token, missing token, anonymous direct hits, and the no-auth-provider
pin-down behavior. - Step-execution route now has a response schema, satisfying
schema-consistency.test.ts. - Internal type cleanups (drop several
as anycasts in worker strategies
andBackgroundTaskWorker). RedisStreamsPubSub.maxDeliveryAttemptsnow rejects negative / NaN values
at construction.0still means "no cap" for back-compat but emits a
one-time warning; passInfinityto disable the cap explicitly.PullTransportaccepts a logger and uses it for unhandled router-callback
rejections instead ofconsole.error.BackgroundTaskWorker.start()now throws ifinit()was not called,
matching the contract of the other workers.- Cross-process integration tests now spawn a single user-owned project
(test-fixtures/cli-project/src/mastra/index.ts) through two generic
entries that mirror whatBuildBundlerandWorkerBundleremit. The
previous one-offserver.entry.ts/worker.entry.ts/
scheduler.entry.ts/background.entry.tsfiles have been deleted —
they implied users hand-roll entry files, which they don't. Worker role
is selected viaMASTRA_WORKERSexactly as in production.
Push-capable PubSub:
- The
PubSubabstract class now declares asupportedModesgetter
(defaulting to['pull']for backward compatibility) so consumers can
tell whether a broker delivers events through a pull loop, an in-process
push, or an out-of-process HTTP push.EventEmitterPubSubreports
['pull', 'push'](EventEmitter dispatches synchronously and works for
either path),@mastra/redis-streamsreports['pull']. Mastranow exposes a publichandleWorkflowEvent(event)method backed
by a sharedWorkflowEventProcessor. It is the single entry point used
by the existing pull-modeOrchestrationWorker, by in-process push
pubsubs (auto-wired duringstartWorkers()), and by the new
POST /api/workflows/eventsroute which lets push-mode brokers (GCP
Pub/Sub push, SNS, EventBridge) deliver events over HTTP.- When the configured pubsub does not support
'pull', Mastra
automatically skips creating anOrchestrationWorkerand
OrchestrationWorker.init()throws a clear error if it is constructed
against a push-only pubsub. WorkflowEventProcessorgains ahandle(event)method that returns a
structured{ ok, retry }result. The originalprocess(event, ack?)
method is preserved as a thin wrapper for back-compat.
Public-API example for a push-capable PubSub:
import { Mastra } from '@mastra/core/mastra'; import { EventEmitterPubSub } from '@mastra/core/pubsub'; const mastra = new Mastra({ // A push-capable broker (GCP Pub/Sub push, SNS, EventEmitter, …). // EventEmitterPubSub reports supportedModes = ['pull', 'push']. pubsub: new EventEmitterPubSub(), workflows: { myWorkflow }, }); // In-process push pubsubs are auto-wired here. For out-of-process // push (e.g. HTTP webhook from a cloud broker), POST the event to // /api/workflows/events on your Mastra server instead. await mastra.startWorkers(); // Direct invocation (e.g. inside an HTTP handler that bridges from a // cloud broker's push delivery): await mastra.handleWorkflowEvent({ id: 'evt-1', type: 'workflow.start', runId: 'run-1', createdAt: new Date(), data: { workflowId: 'myWorkflow', inputData: { name: 'world' } }, });
CI follow-ups:
Mastraonly auto-registersSchedulerWorkerwhen storage is configured.
Without storage the worker would crash on startup (deps.storage.getStore
on undefined); the scheduler now silently no-ops in that case, matching the
pre-worker scheduler behavior.SchedulerWorker.initdefensively logs and returns when called without
storage instead of throwing a TypeError.RECEIVE_WORKFLOW_EVENT_ROUTE(POST /workflows/events)createdAtis
now a plainz.string()on the wire and the handler converts it to a
Date(validating "Invalid Date" -> 400). The previous
union(...).transform().refine()schema couldn't be exercised by the
shared adapter test suite because the generator didn't unwrap Zod 4's
ZodPipe._test-utils/route-test-utilsrecognizes Zod 4'snumber_formatcheck
(used forint()/safeint()), andgenerateContextualValuenow
produces a valid ISO timestamp forcreatedAt/updatedAtfields.
- Step-execution endpoint (
-
Configured workflow step scorers now automatically emit scores through the observability score pipeline. (#16371)
Patch Changes
-
Update provider registry and model documentation with latest models and providers (
ac47842) -
Fixed two bugs that affected scheduled workflows. (#16510)
Scheduled workflow with mismatched
idcould not be dispatched (#16471)When a workflow's
iddiffered from the key it was registered under, the scheduler published events the event processor could not resolve, causing the run to fail with "Workflow not found." The dispatcher now looks up workflows by.idfirst (falling back to the registration key), so the following now works as expected:const workflow = createWorkflow({ id: 'daily-report', schedule: { cron: '0 9 * * *' } }); new Mastra({ workflows: { dailyReport: workflow } });
Deleted scheduled workflows caused infinite event redelivery
Removing a scheduled workflow from code used to leave its schedule row in storage. The scheduler kept firing for the missing workflow and the event processor kept telling the transport to redeliver the event forever. On boot, Mastra now cleans up declarative schedule rows (those it wrote itself, prefixed with
wf_) for workflows that are no longer registered. User-created schedules made via the schedules API are left untouched. The event processor also handles in-flight events for missing workflows by emitting a single terminalworkflow.failinstead of looping. -
Fixed processor-combined workflows to use the agent logger so processor step failures are logged through the configured logger instead of console output. (#16369)
-
Fixed plan approval so accepting a plan can switch modes after the waiting plan tool resolves, clears stale abort state before starting the approved goal, and injects the goal trigger directly instead of queueing a follow-up. (#16340)
-
Fixed guardrail processor schemas so structured output works with Anthropic models. (#15739)
-
Propagate cache metrics (
cachedInputTokens,cacheCreationInputTokens) through harness token usage. The step-finish handler now extractscachedInputTokensfrom AI SDK usage and propagates it throughusage_updateevents,getTokenUsage(), display state, and thread metadata persistence. (#14746) -
Fixed workflow resume to reuse suspended step input payloads when previous step output is stale. Fixes #16051. (#16066)
-
A tool's
toModelOutputresult is now computed before thetool-resultchunk is emitted, so it travels with the chunk onproviderMetadata.mastra.modelOutput. PreviouslytoModelOutputonly ran later when the message list was updated, meaning live stream consumers couldn't see it without re-running the tool. (#16457)The harness now forwards that
providerMetadataontool_resultcontent (both streaming and replayed history) and ontool_endevents, so UIs can render rich tool output (e.g. screenshot images) inline.harness.on('tool_end', event => { const modelOutput = event.providerMetadata?.mastra?.modelOutput; // e.g. { type: 'content', value: [{ type: 'image-data', mediaType: 'image/png', data: '...' }] } });
-
Fixed ToolCallFilter so assistant messages with top-level text are preserved when tool parts are removed. (#16077)
-
Fixed AI SDK v6 dynamic tool UI messages so MessageList preserves tool state during history normalization. Fixes #16046. (#16062)
-
Fixed tool result media content not reaching the model. Tools using
toModelOutputto return images or files (e.g. screenshot tools) now work correctly with all AI SDK providers (Anthropic, OpenAI, Google). (#16449) -
Added A2A Agent Card signing config types for server configuration. (#16207)
Example
const mastra = new Mastra({ server: { a2a: { agentCardSigning: { privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!, protectedHeader: { alg: 'ES256', kid: 'agent-card-key', }, }, }, }, });
-
Fixed a Harness issue where reopening a thread could apply the wrong model for (#16278)
the saved mode. Threads now reopen with the correct model for that mode,
including when no explicit per-mode model was selected. -
Fixed Workflow runs no longer fail to persist when request context contains non-serializable values (for example functions, circular references, or platform proxy objects). This prevents errors when saving workflow snapshots and scorer results. See #12301. (#12573)
-
Added
/goalto Mastra Code, a persistent autonomous task loop similar to the goal modes in Codex and Hermes Agent. (#16065)A user can start a goal with
/goal <objective>. Mastra Code saves that objective to the current thread, runs the normal assistant turn, then asks a separate judge model whether the goal isdone, shouldcontinue, or iswaitingon an explicit user checkpoint. When the judge says to continue, Mastra Code feeds the judge feedback back into the conversation and keeps working until the goal is complete, paused, cleared, or reaches the configured attempt limit.Use
/judgeto configure the default judge model and max attempts used by future goals.Approved plans can be selected as a goal from the inline plan approval UI, slash commands can opt into
/goal/<command>with top-levelgoal: true, and skills can opt into goal commands withmetadata.goal: true./goalobjectives can also span multiple lines. -
Fixed nested workflow runs retaining abort listeners after completion. (#16212)
-
- Fixed
foreachstate update andforeachbail in evented workflows (#16436) - Fixed suspend-resume in evented-workflows legacy stream.
- Fixed
-
Hide internal spans from Mastra-owned processors in exported traces. The
PROCESSOR_RUNspan still appears, but the agent, model, and tool spans that processors create under the hood are now marked internal and filtered out by default. (#16424)Affects the moderation, PII detector, language detector, prompt-injection detector, system-prompt scrubber, and structured-output processors.
To inspect the internals (e.g. for debugging a Mastra-owned processor's behavior), set
includeInternalSpans: trueon your Observability config and the full subtree will be exported. -
Fixed goal reminders in MastraCode to continue through signals without duplicating prompts. Updated core signal stream completion handling so idle-started reminder runs emit the expected lifecycle events. (#16231)
-
Fixed requestContext serialization in workflow suspend/resume. The snapshot persistence used instanceof check which fails across module boundaries, causing context values to be lost on resume. (#16082)
-
Fixed
timeTravel()on evented workflows so jumping to a.branch()step no longer returns empty results for branches that did not run. Branch results now include only the branch that ran, matching the default workflow engine. (#16428) -
Fixed an issue where trajectory and step scorer results from
runEvalswere not saved to storage. These scores now persist correctly and appear alongside agent and workflow scores in Studio's observability section. (#16249) -
Tool
toModelOutputinvocations now emit aMAPPINGtracing span, showing the transformed output the model receives. (#16347) -
Default top-level observational memory early activation settings to observations only, while allowing per-phase overrides under
observationandreflection. (#16367) -
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Fixed assistant message tracking when ObservationalMemory clears step-1 output to memory and step-2 text merges into the same assistant message, so merged text is not lost on the next response clear. (#15277)
-
Added subagent display names to Harness display state so UIs can render configured subagent names without duplicating agent type lookup. (#16237)
-
Fixed title generation to send plain text instead of JSON-serialized part objects to the title model. Previously, internal metadata like providerOptions and framework details leaked into the title prompt. Now formats messages using role-prefixed plain text (similar to observational memory formatting), supporting both single-message and multi-turn conversations. (#15798)
-
Fixed a bug where orphaned provider-executed tool calls (e.g. Anthropic
web_search, Geminicode_execution) could brick a thread. When a provider dropped the tool-result chunk (#15668) or a run aborted mid-stream (#14148), the unresolved call was replayed on every subsequent request — causing Gemini to return empty text and Anthropic to reject the tool-call/tool-result invariant. (#15682)sanitizeV5UIMessagesnow only keeps aninput-availableprovider-executed tool part on the most recent assistant message, and only when no user turn has followed it. Orphans on earlier assistant turns are dropped so the outgoing history always satisfies the tool-call/tool-result pairing required by provider APIs. -
Fixed TokenLimiterProcessor crashing when counting text that contains special token strings. (#16302)
-
Fix nested loops in evented workflow (#16312)
-
Fixed agent requests to Anthropic failing with
400 unexpected tool_use_idwhen recalled history contained an incompletetool_use/tool_resultpair. This most commonly hit agents using parallel tool calls when an earlier run was interrupted before every tool finished — pulling that broken exchange into a new prompt no longer crashes the next request. (#16201)Closes #16193
-
Stop logging auto-recoverable provider cache corruption warnings when
~/.cache/mastra/contains stale content from another Mastra version. Corrupted cache files are still deleted on read so they cannot propagate into a project'sdist/, and the next gateway sync rewrites valid files. (#16332) -
Fix evented workflow foreach timing out when payload is an empty array (#16358)
-
Fixed FGA EE license gating so production servers require an Enterprise license when FGA is configured. (#16362)
-
Fixed a bug where message-level
providerOptionscould be lost or applied to the wrong turn after tool calls. AnthropiccacheControlmarkers now stay attached to the intended message in tool-using conversations. (#16133) -
Fixed
MODEL_INFERENCEspan timing so it measures pure model latency. (#16357) -
Added extra defensive checks to prevent edge cases where system messages may have already been stored in message history. (#15787)
-
Fixes tool call args being lost when split across messages in client tools. When a tool invocation spans multiple messages (call with args, result with empty args), the
findToolCallArgsfunction now continues searching for non-empty args instead of returning the first match. (#12454) -
Replace
js-tiktokenwithtokenxin@mastra/coreto reduce bundle size by removing the bundled BPE rank tables. Token limiting and truncation now use heuristic token estimates, which is appropriate for output limiting and truncation. (#16326) -
Fix
abort()not cancelling evented workflows (#16416) -
Fixed suspend and resume for evented workflows that use parallel steps,
.branch(),dountil/dowhileloops, and nested workflows — previously these only worked reliably for simple linear flows. (#16476)Parallel &
.branch()steps — when more than one branch suspends at the same time (e.g. each branch waits on its own approval), every suspended branch can now be resumed, the workflow stays suspended until all of them have been resumed, and the branch outputs are merged correctly. Before, only the last branch to suspend was resumable, and resuming one branch could prematurely complete the run.dountil/dowhileloops — a loop body that callssuspend()now suspends the workflow instead of crashing the run. And after a resume, subsequent loop iterations run fresh instead of re-receiving the resume data — which previously made loops either run forever or skip their own suspend logic.Nested workflows — resuming a suspended step inside a nested workflow now gives it the correct input (the output of the step right before it, not the nested workflow's own input), so it produces correct results, even when workflows are nested several levels deep. The suspended-step path returned in a workflow result is also correct now, so you can pass it straight back into
resume({ step }). -
Added support for attaching a browser instance to the harness after initialization so consumers can defer browser creation until it is needed: (#16513)
const harness = new Harness({ agent, mastra }); await harness.init(); harness.setBrowser(browser);
-
Added validation to evented workflows to ensure execution prerequisites are met.
EventedWorkflow.createRun()now throw clear error messages when the workflow execution flow is empty (missing.then(),.branch(), etc. calls) or when the step graph has uncommitted changes (missing.commit()call). This catches configuration errors early rather than failing during execution. (#16361) -
Background tasks now run as evented workflows. Each task is dispatched as a workflow run that owns executor invocation, retries, and suspend/resume; pubsub topics, lifecycle event shapes, concurrency gating, and the
BackgroundTaskManager.stream()contract are unchanged. (#16260)Add suspend/resume to background tasks. Tools can call
ctx.agent.suspend(data)fromexecuteto pause a task and release the concurrency slot; resume withmastra.backgroundTaskManager.resume(taskId, resumeData)oragent.resumeStreamUntilIdle(resumeData, { runId, toolCallId }). Surfacesbackground-task-suspended/background-task-resumedchunks onbackgroundTaskManager.stream()andagent.streamUntilIdle().fullStream. -
Fixed message part ordering in agent streaming responses. Message parts (text, reasoning, tool calls) now appear in the correct order they arrived in the stream, preventing incorrect step sequencing and agent loop behavior issues. (#16073)
@mastra/agent-browser@0.2.2
Patch Changes
-
Added
screenshottool to@mastra/stagehand(stagehand_screenshot) and@mastra/agent-browser(browser_screenshot). Captures a PNG screenshot and returns image content for vision-capable models. (#16074)Added
excludeToolsconfig option to opt out of specific tools:const browser = new StagehandBrowser({ excludeTools: ['stagehand_screenshot'], });
@mastra/ai-sdk@1.4.2
Patch Changes
-
Fixed cache write tokens not being set on the AI SDK v6 usage object.
inputTokenDetails.cacheWriteTokensnow reflects the prompt cache creation tokens reported by the provider instead of always beingundefined. Previously this value was only accessible viaproviderMetadata.anthropic.cacheCreationInputTokens. (#16354) -
Added support for showing different tool values to users than the values used internally during execution. AI SDK streams now read Mastra display values for tool call input, streamed input deltas, tool results, tool errors, approvals, and suspensions. (#16103)
const lookupCustomer = createTool({ // Runtime still receives the full input and returns the full output. execute: async ({ customerId, internalPath }) => lookupCustomerRecord(customerId, internalPath), transform: { display: { input: ({ input }) => ({ customerId: input?.customerId }), output: ({ output }) => ({ displayName: output?.displayName }), error: () => ({ message: 'Customer lookup failed' }), }, }, });
This lets chat UIs show safe display values while runtime code keeps the original payloads. See #16054.
@mastra/arize@1.1.0
Minor Changes
- Update PHOENIX_ENDPOINT to PHOENIX_COLLECTOR_ENDPOINT environment variable (#16341)
Patch Changes
@mastra/braintrust@1.1.0
Minor Changes
-
Added a Braintrust current span resolver option so eval traces can nest correctly when applications and Mastra resolve different installed Braintrust SDK copies. (#16396)
-
Mastra Eval results are now forwarded to Braintrust. (#16185)
Patch Changes
@mastra/brightdata@0.2.0
Minor Changes
-
Added
@mastra/brightdataintegration withbrightdata-searchandbrightdata-fetchtools backed by Bright Data's SERP API and Web Unlocker. The tools bypass bot detection and CAPTCHAs out of the box. (#16392)import { Agent } from '@mastra/core/agent'; import { createBrightDataTools } from '@mastra/brightdata'; const agent = new Agent({ id: 'research-agent', name: 'Research Agent', model: 'anthropic/claude-sonnet-4-6', instructions: 'Use brightdata-search to find pages and brightdata-fetch to read them.', tools: createBrightDataTools(), });
Set
BRIGHTDATA_API_TOKENin your environment, or pass{ apiKey }explicitly.
Patch Changes
@mastra/clickhouse@1.7.1
Patch Changes
-
Fixed agent streams intermittently hanging when observability storage was backed by Replicated/Shared ClickHouse. Startup no longer re-applies no-op schema updates (e.g.
ADD COLUMN IF NOT EXISTS,ADD INDEX IF NOT EXISTS,MODIFY TTL), so it no longer triggers replica-lag retry errors that could leave storage in a stuck state. (#16420) -
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/client-js@1.18.0
Minor Changes
-
Added experimental A2A Agent Card signature verification to
getAgentCard. (#16207)Example
const card = await a2a.getAgentCard({ verifySignature: { algorithms: ['ES256'], keyProvider: async ({ kid, jku }) => { return fetchTrustedPublicJwk({ kid, jku }); }, }, });
When verification is configured,
client-jsnow verifies signed Agent Cards when the server includessignatures. Unsigned cards are still returned unchanged.This also adds the new exported types
GetAgentCardOptions,VerifyAgentCardSignatureOptions,AgentCardVerificationKey, andAgentCardSignatureKeyProviderInput. -
Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)
const { sendMessage } = useChat({ agentId, resourceId, threadId }); await sendMessage({ message: 'Follow up while streaming', threadId });
-
Added streamed function-call argument events to
@mastra/client-jsResponses streams. You can now read finalized tool arguments directly from the stream: (#16285)for await (const event of stream) { if (event.type === 'response.function_call_arguments.done') { console.log(event.arguments); } }
-
Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)
Call
agent.sendSignal()to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. UseifActive.behaviorandifIdle.behaviorto deliver, persist, discard, or wake from a signal.Use
agent.subscribeToThread()to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.const subscription = await agent.subscribeToThread({ resourceId, threadId }); void (async () => { for await (const part of subscription.stream) { if (part.type === 'finish') { subscription.unsubscribe(); } } })(); agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId });
-
Fix
orderByshape mismatch for paginated list methods. (#16323)The server expects
orderByas a structured object ({ field, direction }),
but several SDK methods were sendingorderByandsortDirectionas flat
strings, which caused server-side schema validation to fail.Affected methods:
MastraClient.listMemoryThreadsAgent.listVersionsStoredAgent.listVersionsStoredPromptBlock.listVersionsStoredScorer.listVersions
Before:
client.listMemoryThreads({ orderBy: 'createdAt', sortDirection: 'DESC' });
After:
client.listMemoryThreads({ orderBy: { field: 'createdAt', direction: 'DESC' } });
The flat
sortDirectionparameter has been removed from the affected param
types in favor of the nestedorderBy.directionfield. -
Fix client-js bugs surfaced by the SDK ↔ server contract audit. (#16439)
MastraClient.getAgentBuilderActions()previously requested/agent-builder/(trailing slash) and 404'd. Now hits/agent-builder.AgentBuilder.stream(params, runId)now requiresrunId. The server route requires it; calls without it failed with a server-side validation error. The SDK now both typesrunIdas required and guards at runtime.MastraClient.createStoredSkill(...)now requiresdescriptionin its parameter type. The server schema has always required it; the SDK type used to mark it optional, so omitting it produced a runtime 400 instead of a compile error.
Migration:
// Before await agentBuilder.stream({ inputData }); // After await agentBuilder.stream({ inputData }, runId);
// Before await client.createStoredSkill({ name, instructions }); // After await client.createStoredSkill({ name, description, instructions });
-
Add
agent.resumeStreamUntilIdle()to resume a suspended agent stream and keep the SSE connection open through the follow-up turn. (#16260)
Patch Changes
-
Fixed memory thread write methods (
update,delete,deleteMessages,clone) silently sending requests without the requiredagentId. The methods now resolveagentIdfrom a per-call argument first, then the constructor, and throw a clear error if neither is set — before any HTTP request is issued. Reads are unchanged. (#16310)// Either set agentId on the thread once... const thread = client.getMemoryThread({ threadId: 't1', agentId: 'a1' }); await thread.update({ title: 'Renamed' }); await thread.delete(); // ...or pass it per call. const thread = client.getMemoryThread({ threadId: 't1' }); await thread.update({ agentId: 'a1', title: 'Renamed' }); await thread.delete({ agentId: 'a1' });
Fixed
MastraClient.deleteThread()issuingDELETE /api(an empty URL) when called withoutagentIdornetworkId. The method now requires exactly one of the two, enforced both at runtime and in the type signature.await client.deleteThread('t1', { agentId: 'a1' }); await client.deleteThread('t1', { networkId: 'n1' });
-
Regenerate route types to include
TRAJECTORYandSTEPentityType variants on the score response (follow-up to #16249). (#16288) -
Fix
MCPTool.executesending an empty/undefined request body when called withoutdataorrequestContext. The server's tool-execute endpoint expects an object body (with optionaldata), so calls likeclient.getMcpServerTool(serverId, toolId).execute({})would fail withInvalid request body. The SDK now always POSTs a JSON object body, defaulting to{}when no parameters are provided. (#16488) -
Removed Agent Builder routes from the default generated API route contracts. (#16499)
@mastra/cloudflare@1.3.3
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/cloudflare-d1@1.0.6
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/convex@1.0.11
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/datadog@1.2.0
Minor Changes
-
Mastra Eval results are now forwarded to Datadog. (#16185)
-
Mapped
MODEL_INFERENCEspans to Datadog'sllmkind (with token usage and model/provider attached) andMODEL_STEPtoworkflow. Falls back to the previous mapping when paired with an older@mastra/corethat does not emitMODEL_INFERENCE. (#16363)
Patch Changes
- Fixed double-encoded JSON on span input in Datadog. (#16256)
@mastra/deployer@1.33.0
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-cloud@1.33.0
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-cloudflare@1.1.33
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-netlify@1.1.9
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-vercel@1.1.27
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/dsql@1.0.0
Minor Changes
-
Added Amazon Aurora DSQL storage provider with IAM authentication support. (#10930)
Enables storing threads, messages, workflows, traces, and agent data in Amazon Aurora DSQL clusters.
import { DSQLStore } from '@mastra/dsql'; const storage = new DSQLStore({ id: 'my-dsql-store', host: 'abc123.dsql.us-east-1.on.aws', }); await storage.init();
Related: #10929
Patch Changes
@mastra/duckdb@1.3.1
Patch Changes
- Improved DuckDB observability initialization by batching schema setup statements on one connection while preserving migration order. (#16239)
@mastra/dynamodb@1.0.7
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/e2b@0.3.1
Patch Changes
-
Fixed S3 mounts in E2B sandboxes by honoring the configured region and verifying that the FUSE mount attached successfully. (#16222)
Mount failures that previously appeared successful now surface a clear error, making region, credential, and endpoint compatibility problems easier to diagnose.
@mastra/editor@0.7.24
Patch Changes
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Fixed
@mastra/editorintegrations (Composio, Arcade) collapsing every tool call onto a shared'default'user. Tools resolved duringagent.generatenow scope to the authenticated resource from the request context, so per-user OAuth connections route to the correct account instead of a shared one. (#16122)
@mastra/express@1.3.19
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/fastify@1.3.19
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Fixed Fastify stream cleanup and route abort signals when clients disconnect before streamed responses finish. (#16308)
@mastra/google-cloud-pubsub@1.0.4
Patch Changes
- Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/hono@1.4.14
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/inngest@1.4.0
Minor Changes
-
Updated
@mastra/inngestto use Inngest SDK v4. (#15377)Breaking: Requires
inngest@^4and Inngest Dev Serverv1.18.0or later. The@inngest/realtimepackage is no longer needed — its functionality is now included ininngestv4. Remove it from your dependencies and import realtime helpers frominngest/realtimeinstead.// package.json "dependencies": { - "@inngest/realtime": "^0.x", - "inngest": "^3.x" + "inngest": "^4.0.0" }- import { realtimeMiddleware } from '@inngest/realtime/middleware'; - import { subscribe } from '@inngest/realtime'; + import { subscribe } from 'inngest/realtime'; const inngest = new Inngest({ id: 'mastra', - middleware: [realtimeMiddleware()], });
In v4,
subscribe()andrealtime.publish()are first-class methods on the client; the standalone middleware is no longer required.InngestPubSubpublishes viainngest.realtime.publish()instead of the function-contextpublishargument that no longer exists in v4, restoring realtime workflow events and agent stream events.Improved: Workflow result polling now uses snapshot-based polling, resulting in significantly faster retrieval (~83x).
Patch Changes
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Updated the
serveandcreateServeJSDoc adapter examples to register Inngest at/inngest/apiinstead of/api/inngest, matching the Inngest deployment guide and in-repo example projects. (#16186)Why
Mastra reserves the
/apiprefix for built-in routes (agents, workflows, memory). CustomapiRoutes[].pathvalues that start with the server'sapiPrefix(default/api) are rejected at startup, so the previous JSDoc snippets threwCustom API route "/api/inngest" must not start with "/api"when copy-pasted into a current Mastra project.Migration
If you registered Inngest with the previous guide or JSDoc example:
// Before apiRoutes: [ { path: '/api/inngest', method: 'ALL', createHandler: async ({ mastra }) => serve({ mastra, inngest }), }, ]; // After apiRoutes: [ { path: '/inngest/api', method: 'ALL', createHandler: async ({ mastra }) => serve({ mastra, inngest }), }, ];
Update the dev server URL (
npx inngest-cli dev -u http://localhost:4111/inngest/api) and, in production, set the URL field on your Inngest app to match.If you cannot change the path, set
server.apiPrefix(for example/_mastra) to relocate the built-in routes and remember to updateserver.auth.protectedand anyMastraClientapiPrefixto match. See the Inngest deployment guide for the full walkthrough.
@mastra/koa@1.5.2
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
-
Fixed
TypeError: Cannot read properties of undefined (reading 'length')thrown duringMastraServer.init()when a subclass forwards a non-Koa app-like object (for example akoa-routerinstance, a mounted sub-app, or a custom wrapper) tosuper.registerRoute(app, route, opts). The dispatcher-reuse optimization introduced in 1.5.0 now requires the target to expose anapp.middlewarearray; otherwise it falls back to registering a fresh dispatcher per route viaapp.use, matching the pre-1.5.0 per-route behavior. (#16484)Example (subclass that previously crashed):
import { MastraServer } from '@mastra/koa'; import Router from 'koa-router'; class CustomKoaMastraServer extends MastraServer { private router = new Router(); async registerCustomApiRoutes() { const routes = this.mastra.getServer()?.apiRoutes ?? []; for (const route of routes) { // The router has no `middleware` array — this used to throw at init. await super.registerRoute(this.router as any, route, { prefix: this.prefix }); } this.app.use(this.router.routes()); } }
@mastra/laminar@1.1.0
Minor Changes
- Mastra Eval results are now forwarded to Laminar. (#16185)
Patch Changes
@mastra/lance@1.0.6
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/langfuse@1.3.0
Minor Changes
- Mastra Eval results are now forwarded to Langfuse. (#16185)
Patch Changes
- Scope Langfuse traces to the agent or workflow that started them. See Scoping evaluators per agent for more info. (#16393)
@mastra/langsmith@1.2.0
Minor Changes
- Mastra Eval results are now forwarded to LangSmith. (#16185)
Patch Changes
@mastra/libsql@1.10.1
Patch Changes
-
Fixed Workflow run snapshots no longer lose fields when serialized for storage. The libsql
safeStringifycycle-detection treated any object that appeared more than once in a snapshot as a circular reference and dropped it. Becausesnapshot.resultand the final step'scontext[step].outputshare the same reference on success,snapshot.resultwas being silently stripped on every persist. This causedlistWorkflowRunsto return runs withsnapshot.result === undefinedand broke workflow resume when suspended-state fields were shared elsewhere in the snapshot. (#16368) -
Fixed Workflow runs no longer fail to persist when request context contains non-serializable values (for example functions, circular references, or platform proxy objects). This prevents errors when saving workflow snapshots and scorer results. See #12301. (#12573)
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Improved local LibSQL startup performance by applying conservative local SQLite performance settings before initialization, exposing local PRAGMA overrides, and reducing schema initialization contention. (#16513)
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260) -
Added LibSQL indexes for thread message history queries to speed up recent-message and observational-memory loading. (#16513)
@mastra/memory@1.18.0
Minor Changes
- Add metadata filtering support to semantic recall. (#9256)
Patch Changes
-
Fixed an issue where tool results containing AI SDK v5
image-datacontent blocks (returned viatoModelOutput) were stringified into the observational memory prompt as raw base64 text. The base64 data overflowed the observer's context, causing token-limit errors and degenerate output. (#16117)Image and file blocks (
image-data,image-url,file-data,file-url, andmedia) inside tool results are now hoisted into the observer's input as proper attachments, the same way image and file message parts already are. The text body shows a placeholder like[Image #1: image/png]so the observer keeps positional context without seeing the bytes. -
Default top-level observational memory early activation settings to observations only, while allowing per-phase overrides under
observationandreflection. (#16367) -
Auto-recover from transient transport errors (e.g. undici
terminated,fetch failed,UND_ERR_*, 5xx, 429) in the OM observer and reflector LLM calls. Adds an internal retry wrapper with exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s — per-attempt delay capped at 2 minutes, ~4 minute total budget per call) so a single network blip from any provider no longer kills the actor turn during long-running sessions. Non-transient errors (auth, validation, etc.) and user-initiated aborts still fail fast. No public API changes. (#16454) -
Added extra defensive checks to prevent edge cases where system messages may have already been stored in message history. (#15787)
-
Fixed read-only observational memory so existing context is still loaded. (#16059)
@mastra/mongodb@1.8.1
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/mssql@1.3.0
Minor Changes
-
Add agents storage domain to MSSQL adapter — brings @mastra/mssql to parity with @mastra/mongodb and @mastra/libsql for the agents domain. The Studio "Agents" tab and
mastra.getEditor()now work against MSSQL. (#16376)import { MSSQLStore } from '@mastra/mssql'; const store = new MSSQLStore({ id: 'mssql-storage', connectionString: process.env.MSSQL_URL!, }); const agents = await store.getStore('agents'); const agent = await agents?.getById('agent-id'); const page = await agents?.list({ status: 'published', perPage: 20 });
Patch Changes
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/nestjs@0.1.3
Patch Changes
-
Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)
-
Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/observability@1.12.0
Minor Changes
-
DefaultExporternow notifies custom exporters and connected integrations when it cannot persist observability events, such as unsupported storage or retries being exceeded. (#16111) -
Renamed two built-in observability exporters to clearer names. The originals are still exported (now deprecated) and continue to work unchanged, including their existing exporter
namestrings and error IDs, so monitoring rules and dashboards keep matching until you migrate. (#16223)CloudExporter→MastraPlatformExporterDefaultExporter→MastraStorageExporter
Before
import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'my-app', exporters: [new DefaultExporter(), new CloudExporter()], spanOutputProcessors: [new SensitiveDataFilter()], }, }, });
After
import { Observability, MastraStorageExporter, MastraPlatformExporter, SensitiveDataFilter, } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'my-app', exporters: [new MastraStorageExporter(), new MastraPlatformExporter()], spanOutputProcessors: [new SensitiveDataFilter()], }, }, });
-
Apply
SensitiveDataFilterby default (#16234)The
Observabilityregistry now auto-applies aSensitiveDataFilterspan output processor to every configured instance, so secrets (API keys, tokens, passwords, etc.) are redacted before they reach exporters such as the Mastra cloud exporter. This protects against accidentally exporting sensitive data when the filter was not added manually.A new top-level
sensitiveDataFilteroption on theObservabilityregistry config controls this behavior:true(default): applySensitiveDataFilterwith default options.false: opt out of auto-applied filtering.- a
SensitiveDataFilterOptionsobject: customize the filter (sensitive fields, redaction token, redaction style).
If a config already includes a
SensitiveDataFilterinspanOutputProcessors, the auto-applied filter is skipped to avoid double redaction. Pre-instantiatedObservabilityInstancevalues are not modified.Before:
import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'mastra', exporters: [new DefaultExporter(), new CloudExporter()], spanOutputProcessors: [new SensitiveDataFilter()], }, }, });
After:
import { Observability, DefaultExporter, CloudExporter } from '@mastra/observability'; new Observability({ configs: { default: { serviceName: 'mastra', exporters: [new DefaultExporter(), new CloudExporter()], }, }, // Optional: customize or disable the auto-applied filter. // sensitiveDataFilter: false, // sensitiveDataFilter: { sensitiveFields: ['myCustomSecret'] }, });
-
Added new
MODEL_INFERENCEspan type underMODEL_STEP, covering only the model provider call. Use it to measure model latency separately from input/output processors and tool executions. (#16267)
Patch Changes
-
Fixed cost estimation for OpenRouter models. The Model Usage & Cost panel now shows costs for OpenRouter
vendor/modelids (e.g.openai/gpt-5-mini-2025-08-07,xiaomi/mimo-v2-pro-20260318) that previously rendered an empty cost column. (#16206) -
Support
MASTRA_PLATFORM_ACCESS_TOKENas the preferred environment variable forMastraPlatformExporter, while retainingMASTRA_CLOUD_ACCESS_TOKENas a fallback for backward compatibility. (#16500) -
Score events now include scorer names and target entity types. (#16185)
-
Fixed
MODEL_INFERENCEspan timing so it measures pure model latency. (#16357) -
Refreshed the embedded pricing data snapshot used for cost estimation in observability metrics with the latest provider rates. (#16373)
@mastra/otel-bridge@1.1.0
Minor Changes
-
Added log forwarding to
@mastra/otel-bridge. The bridge now also subscribes to Mastra log events and forwards them to the globally-registered OpenTelemetryLoggerProvider, alongside the spans it already creates. (#13529)Logs that originate inside a Mastra span are emitted under that span's OTEL context, so backends like Datadog, Grafana, and Honeycomb correlate them with the surrounding trace automatically. Logs without trace context fall through to the currently active OTEL context.
To wire up logs alongside traces, register a
logRecordProcessoronNodeSDK:import { NodeSDK } from '@opentelemetry/sdk-node'; import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http'; import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs'; const sdk = new NodeSDK({ // ...trace config as usual logRecordProcessor: new BatchLogRecordProcessor(new OTLPLogExporter()), });
If no
LoggerProvideris registered, log emission is a silent no-op — traces continue to work as configured.
Patch Changes
@mastra/otel-exporter@1.1.0
Minor Changes
-
Added log export to
@mastra/otel-exporter. Logs emitted on the Mastra observability bus are now forwarded to the configured OTLP endpoint alongside traces, using the same provider configuration. (#13529)import { OtelExporter } from '@mastra/otel-exporter'; new OtelExporter({ provider: { custom: { endpoint: 'http://localhost:4318', protocol: 'http/json' }, }, // signals.logs defaults to true; set to false to disable. signals: { traces: true, logs: true }, });
Requires the matching OTLP log exporter package to be installed (e.g.
@opentelemetry/exporter-logs-otlp-httpfor HTTP/JSON, or-proto/-grpcvariants).Trace correlation: Logs that carry
traceIdandspanIdare attached to the OTEL log record's native trace context, so backends like Datadog, Grafana, and Honeycomb auto-correlate logs to traces.Other improvements:
- Trace and log endpoints are always normalized to a single signal-path suffix, so
http://host:4318/,http://host:4318, andhttp://host:4318/v1/traces/all produce well-formed URLs instead of malformed variants like//v1/logs. - Calling
flush()orshutdown()immediately after init no longer drops telemetry — teardown waits for setup to finish before draining providers. - Debug log output no longer exposes credential fragments. Provider header values are fully redacted instead of printing prefix/suffix slices.
- When a dynamically-loaded OTLP exporter package is installed but does not expose the expected named export, Mastra now disables that signal with a clear error message instead of failing later with an opaque "X is not a constructor" error.
- Trace and log endpoints are always normalized to a single signal-path suffix, so
Patch Changes
@mastra/pg@1.10.1
Patch Changes
-
Fixed
@mastra/pglisting endpoints (agents, MCP clients, MCP servers, prompt blocks, scorer definitions, skills, and workspaces) so a single row with a malformed value no longer returns HTTP 500 and hides every other record in the Mastra Editor. Listings now tolerate the bad row and return the rest. (#16233)Fixes #16224.
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
-
Track
suspendedAtandsuspendPayloadon background tasks. SQL adapters auto-migrate the new columns viaalterTable. (#16260)
@mastra/playground-ui@27.0.0
Minor Changes
-
Updated agent traces tab to use the rich observability traces UI (#16405)
The agent traces tab now shows the dense 7-column trace list with a side-panel detail view featuring colored timeline spans (Agent/Workflow/Model/Scorer), expandable nested spans, Evaluate Trace, and Save as Dataset Item.
Locked scope filter pills
When viewing agent-scoped traces, the Primitive Type and Primitive ID filter pills are now read-only — they display the agent context, show a lock icon and tooltip, and cannot be edited or removed. The Add Filter dropdown no longer lists scope-controlled fields so users cannot accidentally override the active scope.
PropertyFilterAppliedaccepts a newlockedFieldIds(and optionallockedTooltipContent) prop.PropertyFilterCreatoraccepts a newhiddenFieldIdsprop. Both are opt-in and unset by default, so existing usages are unaffected.// Before <PropertyFilterApplied fields={fields} tokens={tokens} onTokensChange={setTokens} /> // After — pills for the listed fields render locked with a tooltip <PropertyFilterApplied fields={fields} tokens={tokens} onTokensChange={setTokens} lockedFieldIds={['rootEntityType', 'entityId']} lockedTooltipContent="This filter is set by the current context." />
-
Added CodeBlock component with select/tabs switcher, optional shiki syntax highlighting, and Notion-style hover-only copy button (always visible on touch devices via media query). (#16202)
-
Improved
ScrollAreato use Base UI internally and added a richer mask API. Edges now fade by default based onorientation(top/bottom for vertical, left/right for horizontal, all four for both), so most scrollers get the polished fade-out automatically. (#16415)Heads up — default behavior change:
ScrollAreapreviously rendered without any edge fade unlessshowMaskwas passed. It now fades the edges that matchorientationby default. Passmask={false}on the callsites where you want to keep the old hard edges.New
maskprop. Accepts a boolean (falsedisables the fade entirely) or an object to override individual sides. Thexandykeys are shorthands for the matching axis.// Default — fades follow `orientation` <ScrollArea>...</ScrollArea> // Opt out entirely <ScrollArea mask={false}>...</ScrollArea> // Keep only the top fade <ScrollArea mask={{ bottom: false }}>...</ScrollArea> // Vertical fades only on a two-axis scroller <ScrollArea orientation="both" mask={{ x: false }}>...</ScrollArea>
Migrating from
showMask. TheshowMaskboolean is now deprecated but still works —maskwins when both are set.// Before <ScrollArea showMask>...</ScrollArea> <ScrollArea showMask={false}>...</ScrollArea> // After <ScrollArea>...</ScrollArea> // default fade matches orientation <ScrollArea mask={false}>...</ScrollArea> // explicitly disable
-
Added an "All traces, nested too" mode to the Observability → Traces page. (#16479)
The traces list now has a switcher in the toolbar to toggle between two views:
- Top-level traces only (default) — one row per top-level run, the existing behavior.
- All traces, nested too — one row per invocation, including every agent, workflow, tool, processor, scorer, and RAG ingestion that ran nested inside another run.
This makes it possible to find every invocation of a given entity (e.g. "every run of
recipe-makerworkflow") regardless of how it was triggered. Selecting a row in the new mode opens a detail panel showing just that branch's subtree.New hooks for consumers building their own observability UIs:
useBranch({ traceId, spanId, depth? })— fetches the span subtree rooted at an anchor span.useTraceOrBranchSpans({ traceId, spanId, listMode })— returns trace spans or a branch subtree depending on the active mode.
-
Added opt-in interactivity and per-page filter persistence support for observability UI components. (#15747)
MetricsLineChartaccepts anonPointClickcallback so chart points can drive drilldowns.HorizontalBarsaccepts row-level and segment-level hrefs for linked metric bars without nested anchors.MetricsDataTableacceptsgetRowHref(row)for linked rows with consistent hover and focus styling.MetricsCardexposes anActionsslot in the top bar for contextual icon links.- Observability filter helpers for Metrics, Traces, and Logs each keep their own saved-filters storage key so pages remember filters independently.
All additions are optional, so existing consumers continue to render the same way unless they pass the new props.
<MetricsLineChart data={points} series={series} onPointClick={point => navigate(`/observability?dateFrom=${point.from}&dateTo=${point.to}`)} /> <HorizontalBars data={[{ name: 'agent-a', values: [42, 3], href: '/observability?filterEntityName=agent-a' }]} /> <MetricsDataTable columns={cols} data={rows} getRowHref={row => `/observability?filterThreadId=${row.threadId}`} /> <MetricsCard> <MetricsCard.TopBar> <MetricsCard.TitleAndDescription title="Latency" /> <MetricsCard.Actions> <IconButton href="/observability" /> </MetricsCard.Actions> </MetricsCard.TopBar> </MetricsCard>
-
Added a new
pillvariant onTabListwith an animated background indicator that slides behind the active trigger. The defaultlinevariant now animates its underline smoothly between tabs as well. Implemented by migrating the underlying Tabs component from Radix UI to Base UI. (#16414)// Before — only the line (underline) style was available <Tabs defaultTab="overview"> <TabList> <Tab value="overview">Overview</Tab> <Tab value="projects">Projects</Tab> </TabList> </Tabs> // After — opt into the new pill style via the `variant` prop on TabList <Tabs defaultTab="overview"> <TabList variant="pill"> <Tab value="overview">Overview</Tab> <Tab value="projects">Projects</Tab> </TabList> </Tabs>
The public API (
Tabs,TabList,Tab,TabContent) is unchanged; existing call-sites keep the defaultlinevariant. -
Added new
pill-ghostvariant onTabsandstickyprop onTabListfor sticky tab headers. (#16433)<Tabs defaultTab="overview"> <TabList variant="pill-ghost" sticky> <Tab value="overview">Overview</Tab> <Tab value="settings">Settings</Tab> </TabList> </Tabs>
Added
variantprop onCombobox(default,ghost,link) for flexible trigger styling. Note: this prop existed previously but was a no-op; it now actually drives the trigger appearance, so callers passingvariantwill see updated styles.// Bordered form input (default) <Combobox options={options} value={value} onValueChange={setValue} /> // Borderless, hover-only surface <Combobox options={options} value={value} onValueChange={setValue} variant="ghost" /> // Text-only trigger <Combobox options={options} value={value} onValueChange={setValue} variant="link" />
Improved
EntityHeaderlayout — title and children now share a single row with wrapping, and padding is tighter for denser headers.Improved
ScrollAreato handle vertical/horizontal orientation correctly, preventing forced horizontal scroll when only vertical is needed.Improved
PanelSeparatorwith a pill-shaped handle that grows on hover/active for a clearer affordance.Removed
Threads,ThreadList,ThreadItem,ThreadLink,ThreadDeleteButtonexports. These had no consumers outside this repository. Downstream users relying on these exports should compose an equivalent list locally using the underlying DS primitives (Button,Icon,Txt) — theplaygroundpackage now contains a reference implementation undersrc/components/thread-list.- import { Threads, ThreadList, ThreadItem, ThreadLink, ThreadDeleteButton } from '@mastra/playground-ui'; + // Compose locally with @mastra/playground-ui primitives (Button, Icon, Txt) + // or copy the reference implementation from the playground package.
-
Added
MainSidebar.NavLabel— collapse-aware label slot forasChildnav items. When the sidebar collapses to icon-only mode, the label hides viaVisuallyHidden(still announced by screen readers) instead of leaking outside the 36px icon rail. The defaultlink={...}path was already collapse-aware;asChildconsumers now have a matching primitive. (#16167)// Before: text leaked visually when the sidebar collapsed <MainSidebar.NavLink asChild> <button> <Bot /> Agents </button> </MainSidebar.NavLink> // After: wrap labels in MainSidebar.NavLabel <MainSidebar.NavLink asChild> <button> <Bot /> <MainSidebar.NavLabel>Agents</MainSidebar.NavLabel> </button> </MainSidebar.NavLink>
Improved truncation handling for nav items and section headers. Long labels now clip with a single-line ellipsis instead of wrapping to a second line during the collapse/expand transition, eliminating layout jumps at narrow widths.
-
Added SettingsRow primitive for label/description + control rows in settings pages. Markup mirrors the existing platform settings row pattern (flex justify-between with title + optional description on the left, control on the right) so consumers can adopt it without visual regressions. (#16150)
Removed the redundant SelectField wrapper. Its only internal consumer (Studio settings) was migrated to SettingsRow + Select primitives. For form fields use SelectFieldBlock; for settings rows use SettingsRow.
Before
<SelectField name="theme" label="Theme mode" value={theme} onValueChange={setTheme} options={THEME_OPTIONS} />
After
<SettingsRow label="Theme mode" htmlFor="theme"> <Select value={theme} onValueChange={setTheme}> <SelectTrigger id="theme" className="w-full sm:w-48"> <SelectValue /> </SelectTrigger> <SelectContent>{/* items */}</SelectContent> </Select> </SettingsRow>
-
Added
InputGroupand extendedButtonsGroupin playground-ui design system. (#16417)New
InputGroupcomponentCompose inputs with leading or trailing icons, buttons, text labels, and keyboard hints. Supports inline (left/right) and block (top/bottom) addon alignment, and works with both inputs and textareas.
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupButton } from '@mastra/playground-ui'; import { SearchIcon, XIcon } from 'lucide-react'; <InputGroup> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> <InputGroupInput placeholder="Search..." /> <InputGroupAddon align="inline-end"> <InputGroupButton aria-label="Clear"> <XIcon /> </InputGroupButton> </InputGroupAddon> </InputGroup>;
Extended
ButtonsGroupAdded
orientation(horizontal|vertical), and newButtonsGroupSeparatorandButtonsGroupTextslots. Existing API unchanged.<ButtonsGroup spacing="close"> <Button variant="outline">−</Button> <ButtonsGroupText>42</ButtonsGroupText> <Button variant="outline">+</Button> </ButtonsGroup> <ButtonsGroup orientation="vertical"> <Button variant="ghost">Copy</Button> <ButtonsGroupSeparator /> <Button variant="ghost">Cut</Button> </ButtonsGroup>
Tweaked
Buttonghost variantAligned hover/active progression with the outline variant (
surface3→surface4) so click feedback is perceptible on transparent backgrounds. Existing ghost buttons throughout the playground will appear one shade lighter on hover and active.
Patch Changes
-
Improved Studio's Traces page to scale smoothly to many traces. The list now renders only the visible window, so scrolling stays responsive and memory usage stays bounded regardless of how many traces are loaded. (#16262)
-
Filter pills (
PropertyFilterApplied) now match the rest of the design system — single 1px border, consistent rounded segments, no custom styling. Labels stay on one line and no longer compress when long values are present. (#16426)ButtonsGroupTextsegments also no longer wrap to multiple lines or shrink under flex pressure, which makes them safer to drop into any tight layout. -
Fixed three issues on the Logs and Traces pages: (#16306)
- Column widths now stay stable while scrolling — they no longer jump as different rows scroll into view.
- Scrolling far down the Logs list no longer scrambles rows (duplicates, gaps, or empty rows after additional pages load).
- Changing a filter or the date range now scrolls the list back to the top, instead of leaving an empty band above the new data until you nudge the scroll. Logs and Traces now behave the same way on filter changes.
-
Added
NoTracesInfocomponent that informs the user there are no traces for the active date range. (#16303) -
Refreshed the visual style of form controls and popups for a softer, more consistent look: (#16150)
- Button: thinner border (
borderinstead ofborder-2); text-mode buttons userounded-full; icon-mode buttons are circular. - Combobox / Select / DropdownMenu / Command: triggers and items aligned on the form-input look —
rounded-lgborder, transparent background, soft hover/open states, consistenttext-ui-smdtypography. - Popups (Popover / Tooltip / Select / Dropdown content):
rounded-xlcontainers withshadow-dialog; inner itemsrounded-lginsidep-1. - Tokens: bumped the radius scale (
sm2→4px,md4→6px,lg6→10px,xl12→14px); replaced--shadow-dialog's outer 1px ring with an inset top gloss so the dialog shadow stops doubling up with each consumer's own border.
- Button: thinner border (
-
Polished Combobox dropdown items (#16411)
- Moved the selection check to the right of each item so unselected rows no longer carry an awkward left padding gap and the whole list aligns consistently.
- Tightened popup search/empty padding and softened the trigger hover for a calmer command-palette feel.
Added
ComboboxPrimitiveexport for advanced compositionsRe-exports the raw
@base-ui/react/comboboxparts (Root, Trigger, Input, List, Item, Chips, etc.) so callers needing virtualization, async status, chips, or creatable patterns can compose them directly with the sharedcomboboxStylestokens — without growing the monolithic<Combobox>prop surface.import { ComboboxPrimitive, comboboxStyles } from '@mastra/playground-ui'; <ComboboxPrimitive.Root items={items}> <ComboboxPrimitive.Input className={comboboxStyles.searchInput} /> <ComboboxPrimitive.Portal> <ComboboxPrimitive.Positioner> <ComboboxPrimitive.Popup className={comboboxStyles.popup}> <ComboboxPrimitive.List className={comboboxStyles.list}> {item => ( <ComboboxPrimitive.Item value={item} className={comboboxStyles.item}> {item.label} </ComboboxPrimitive.Item> )} </ComboboxPrimitive.List> </ComboboxPrimitive.Popup> </ComboboxPrimitive.Positioner> </ComboboxPrimitive.Portal> </ComboboxPrimitive.Root>;
-
Aligned Badge variant colors with the Notice and Toast palette so the design system uses one consistent set of semantic colors. Default badges keep their neutral surface, while success, error, info and warning variants now use the same notice tokens as Notices and Toasts. Icons inside badges are sized down to match the badge height. (#16215)
-
Improved Studio's Logs page to scale smoothly to many log records. The list now renders only the visible window, so scrolling stays responsive and memory usage stays bounded regardless of how many logs are loaded. (#16263)
-
Fixed Studio streaming render behavior for interleaved reasoning and improved chat autoscroll during rapid output. (#16331)
-
Restored auto-refresh on the traces list page, polling every 10 seconds. Polling is paused while the request is forbidden (HTTP 403) to avoid flicker. (#16204)
-
Refined Combobox and Select trigger interactions with an active state and fixed value truncation when a leading badge is rendered. Refreshed PanelSeparator with a clearer hover/active affordance, an enlarged hit area, and a focus-visible accent. Removed the default
bg-surface2background fromThreadsso consumers can control the surface color. (#16269) -
Changed the default Observability list mode to branches (all traces, including nested). The query logic still recognizes
?listMode=tracesto opt back into the top-level-only view. (#16493)Before
/observability→ top-level traces onlyAfter
/observability→ branches (all traces, nested too)
/observability?listMode=traces→ top-level traces only -
Fixed Notice component alignment with action: button now stays at default sm size, vertically aligns to first text line via negative margin compensation, and stacks below the message as a full-width button on narrow containers. (#16150)
@mastra/react@0.3.0
Minor Changes
-
Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)
const { sendMessage } = useChat({ agentId, resourceId, threadId }); await sendMessage({ message: 'Follow up while streaming', threadId });
Patch Changes
-
Fixed streaming chat messages so sending while an agent is running does not duplicate assistant output or leave the previous response marked as streaming. (#16338)
-
Fixed Studio streaming render behavior for interleaved reasoning and improved chat autoscroll during rapid output. (#16331)
-
Handle
background-task-suspendedchunks intoUIMessageso suspend metadata is restored after resume. (#16260)
@mastra/redis@1.1.1
Patch Changes
-
Per-key TTL support in
RedisCache(#16283)RedisCache.set()now accepts an optionalttlMsargument that overrides the configured default TTL for a single entry. Sub-second values are rounded up to one second (RedisEXPIREgranularity); a non-positive value persists the entry without expiry.const cache = new RedisCache({ url: 'redis://...' }); await cache.set('weather:nyc', payload, 60_000); // expires in 60s await cache.set('manifest', payload, 0); // persists indefinitely
-
Respect optional
resourceIdingetThreadByIdso scoped thread lookups returnnullwhen the thread belongs to a different resource. (#14237)Example:
const thread = await memory.getThreadById({ threadId: 'my-thread-id', resourceId: 'my-user-id', }); // Returns null if the thread does not belong to 'my-user-id'.
@mastra/redis-streams@0.0.2
Patch Changes
-
Worker review fixes: (#16309)
- Step-execution endpoint (
POST /workflows/:id/runs/:runId/steps/execute) is
now gated by Mastra's standardrequiresAuth: true+authenticateToken
pipeline rather than a parallel "worker secret" body field. The previously
introducedworkerSecretconfig knob andMASTRA_WORKER_SECRETenv var
have been removed (they were never released). To gate the endpoint on a
standalone-worker deployment, configure an auth provider on the server's
Mastrainstance — without one the framework currently treats
requiresAuth: trueas a no-op for this route. HttpRemoteStrategynow sends credentials as a normalAuthorization: Bearer <token>header. The token comes from the new
MASTRA_WORKER_AUTH_TOKENenv var or an explicitauthconstructor option.- Honor the caller's
abortSignalinHttpRemoteStrategyby combining it
with the per-request timeout viaAbortSignal.any(with a manual fallback
for runtimes that don't expose it). - Implement comma-separated name filtering for the
MASTRA_WORKERSenv var.
MASTRA_WORKERS=scheduler,backgroundTasksnow boots only those named
workers;MASTRA_WORKERS=falsestill disables all workers. - Restore
Mastra.startEventEngine/stopEventEngineas@deprecated
aliases for the renamedstartWorkers/stopWorkers. BackgroundTaskWorkernow subscribes to PubSub instart()instead of
init(), matching the lifecycle of the other workers and making
isRunningaccurately reflect subscription state.RedisStreamsPubSubadds amaxDeliveryAttemptsoption (default 5) that
drops events after the configured number of failed deliveries instead of
redelivering forever, and replaces emptycatch {}blocks with
logger.warn/logger.debugcalls.RedisStreamsPubSub.unsubscribe(topic, cb)now honors the topic argument
so the same callback can be subscribed to multiple topics independently.PullTransportguards the async router callback against unhandled promise
rejections by attaching a.catchthat nacks the message.- Drop the dead
MASTRA_WORKER_NAMEenv var injection in the CLI worker
spawn (the bundle entrypoint already passes the worker name directly). - Add a real cross-process e2e auth suite
(pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong
token, missing token, anonymous direct hits, and the no-auth-provider
pin-down behavior. - Step-execution route now has a response schema, satisfying
schema-consistency.test.ts. - Internal type cleanups (drop several
as anycasts in worker strategies
andBackgroundTaskWorker). RedisStreamsPubSub.maxDeliveryAttemptsnow rejects negative / NaN values
at construction.0still means "no cap" for back-compat but emits a
one-time warning; passInfinityto disable the cap explicitly.PullTransportaccepts a logger and uses it for unhandled router-callback
rejections instead ofconsole.error.BackgroundTaskWorker.start()now throws ifinit()was not called,
matching the contract of the other workers.- Cross-process integration tests now spawn a single user-owned project
(test-fixtures/cli-project/src/mastra/index.ts) through two generic
entries that mirror whatBuildBundlerandWorkerBundleremit. The
previous one-offserver.entry.ts/worker.entry.ts/
scheduler.entry.ts/background.entry.tsfiles have been deleted —
they implied users hand-roll entry files, which they don't. Worker role
is selected viaMASTRA_WORKERSexactly as in production.
Push-capable PubSub:
- The
PubSubabstract class now declares asupportedModesgetter
(defaulting to['pull']for backward compatibility) so consumers can
tell whether a broker delivers events through a pull loop, an in-process
push, or an out-of-process HTTP push.EventEmitterPubSubreports
['pull', 'push'](EventEmitter dispatches synchronously and works for
either path),@mastra/redis-streamsreports['pull']. Mastranow exposes a publichandleWorkflowEvent(event)method backed
by a sharedWorkflowEventProcessor. It is the single entry point used
by the existing pull-modeOrchestrationWorker, by in-process push
pubsubs (auto-wired duringstartWorkers()), and by the new
POST /api/workflows/eventsroute which lets push-mode brokers (GCP
Pub/Sub push, SNS, EventBridge) deliver events over HTTP.- When the configured pubsub does not support
'pull', Mastra
automatically skips creating anOrchestrationWorkerand
OrchestrationWorker.init()throws a clear error if it is constructed
against a push-only pubsub. WorkflowEventProcessorgains ahandle(event)method that returns a
structured{ ok, retry }result. The originalprocess(event, ack?)
method is preserved as a thin wrapper for back-compat.
Public-API example for a push-capable PubSub:
import { Mastra } from '@mastra/core/mastra'; import { EventEmitterPubSub } from '@mastra/core/pubsub'; const mastra = new Mastra({ // A push-capable broker (GCP Pub/Sub push, SNS, EventEmitter, …). // EventEmitterPubSub reports supportedModes = ['pull', 'push']. pubsub: new EventEmitterPubSub(), workflows: { myWorkflow }, }); // In-process push pubsubs are auto-wired here. For out-of-process // push (e.g. HTTP webhook from a cloud broker), POST the event to // /api/workflows/events on your Mastra server instead. await mastra.startWorkers(); // Direct invocation (e.g. inside an HTTP handler that bridges from a // cloud broker's push delivery): await mastra.handleWorkflowEvent({ id: 'evt-1', type: 'workflow.start', runId: 'run-1', createdAt: new Date(), data: { workflowId: 'myWorkflow', inputData: { name: 'world' } }, });
CI follow-ups:
Mastraonly auto-registersSchedulerWorkerwhen storage is configured.
Without storage the worker would crash on startup (deps.storage.getStore
on undefined); the scheduler now silently no-ops in that case, matching the
pre-worker scheduler behavior.SchedulerWorker.initdefensively logs and returns when called without
storage instead of throwing a TypeError.RECEIVE_WORKFLOW_EVENT_ROUTE(POST /workflows/events)createdAtis
now a plainz.string()on the wire and the handler converts it to a
Date(validating "Invalid Date" -> 400). The previous
union(...).transform().refine()schema couldn't be exercised by the
shared adapter test suite because the generator didn't unwrap Zod 4's
ZodPipe._test-utils/route-test-utilsrecognizes Zod 4'snumber_formatcheck
(used forint()/safeint()), andgenerateContextualValuenow
produces a valid ISO timestamp forcreatedAt/updatedAtfields.
- Step-execution endpoint (
@mastra/schema-compat@1.2.10
Patch Changes
- Fixed Google-compatible schema conversion so Gemini accepts broad nullable tool parameters. (#16129)
@mastra/server@1.33.0
Minor Changes
-
Added in-memory A2A push notification support for task updates. (#16175)
Clients can now register push notification configs with
message/send,message/stream, or thetasks/pushNotificationConfig/*methods. The server advertises push notification support in the agent card and sends the current task snapshot to registered webhooks when a task reachescompleted,failed,canceled, orinput-required.Webhook delivery validates the configured URL and pins outbound delivery to the validated address to reduce DNS rebinding risk. This remains in-memory and best-effort; operators should still use normal egress controls and avoid exposing push delivery to networks with sensitive internal services unless they trust the configured webhook targets.
await a2a.setTaskPushNotificationConfig({ taskId: 'task-123', pushNotificationConfig: { url: 'https://example.com/a2a-webhook', token: 'session-token', }, });
-
Allow stored Responses API follow-up requests to use
previous_response_idwithout also passingagent_id. (#16246)When callers pass both
previous_response_idand an explicitagent_id, mismatched agents now return a clear 400 response instead of looking like a missing stored response.The create-response schema now also rejects empty
agent_idandprevious_response_idstrings. -
Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)
const { sendMessage } = useChat({ agentId, resourceId, threadId }); await sendMessage({ message: 'Follow up while streaming', threadId });
-
Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)
Call
agent.sendSignal()to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. UseifActive.behaviorandifIdle.behaviorto deliver, persist, discard, or wake from a signal.Use
agent.subscribeToThread()to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.const subscription = await agent.subscribeToThread({ resourceId, threadId }); void (async () => { for await (const part of subscription.stream) { if (part.type === 'finish') { subscription.unsubscribe(); } } })(); agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId });
-
Responses streams now emit tool call events so clients can track tool arguments and results in real time. (#16285)
Tool outputs now use consistent IDs (
<toolCallId>:output) so streamed arguments can be matched to completed results.for await (const event of stream) { if (event.type === 'response.function_call_arguments.delta') { console.log(event.delta); } if (event.type === 'response.output_item.done' && event.item.type === 'function_call') { console.log(event.item.id); } }
-
Added support for signed A2A Agent Cards. (#16207)
Example
const mastra = new Mastra({ server: { a2a: { agentCardSigning: { privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!, protectedHeader: { alg: 'ES256', kid: 'agent-card-key', }, header: { issuer: 'mastra', }, }, }, }, });
Mastra now conditionally signs served A2A Agent Cards via
signAgentCard(...)whenserver.a2a.agentCardSigningis configured, and the A2A Agent Card response schema now includes thesignaturesarray. -
Added
mastra api, a machine-readable runtime CLI for calling Mastra server resources with JSON input and output. (#16128)The new API CLI supports agents, workflows, tools, MCP servers, memory threads, working memory, observability traces/logs/scores, datasets, and experiments. It includes schema-aware request handling so a single JSON input is split into path, query, and body fields based on server route contracts, plus ergonomic raw-input wrapping for tool execution.
Exposed a route-derived server API schema manifest at runtime and generated CLI route metadata from it, enabling
--schemaoutput, response-shape-aware normalization, and server-aligned pagination output. -
Add
POST /api/agents/:agentId/resume-stream-until-idleSSE route, mirroringagent.resumeStreamUntilIdle(). (#16260) -
Worker review fixes: (#16309)
- Step-execution endpoint (
POST /workflows/:id/runs/:runId/steps/execute) is
now gated by Mastra's standardrequiresAuth: true+authenticateToken
pipeline rather than a parallel "worker secret" body field. The previously
introducedworkerSecretconfig knob andMASTRA_WORKER_SECRETenv var
have been removed (they were never released). To gate the endpoint on a
standalone-worker deployment, configure an auth provider on the server's
Mastrainstance — without one the framework currently treats
requiresAuth: trueas a no-op for this route. HttpRemoteStrategynow sends credentials as a normalAuthorization: Bearer <token>header. The token comes from the new
MASTRA_WORKER_AUTH_TOKENenv var or an explicitauthconstructor option.- Honor the caller's
abortSignalinHttpRemoteStrategyby combining it
with the per-request timeout viaAbortSignal.any(with a manual fallback
for runtimes that don't expose it). - Implement comma-separated name filtering for the
MASTRA_WORKERSenv var.
MASTRA_WORKERS=scheduler,backgroundTasksnow boots only those named
workers;MASTRA_WORKERS=falsestill disables all workers. - Restore
Mastra.startEventEngine/stopEventEngineas@deprecated
aliases for the renamedstartWorkers/stopWorkers. BackgroundTaskWorkernow subscribes to PubSub instart()instead of
init(), matching the lifecycle of the other workers and making
isRunningaccurately reflect subscription state.RedisStreamsPubSubadds amaxDeliveryAttemptsoption (default 5) that
drops events after the configured number of failed deliveries instead of
redelivering forever, and replaces emptycatch {}blocks with
logger.warn/logger.debugcalls.RedisStreamsPubSub.unsubscribe(topic, cb)now honors the topic argument
so the same callback can be subscribed to multiple topics independently.PullTransportguards the async router callback against unhandled promise
rejections by attaching a.catchthat nacks the message.- Drop the dead
MASTRA_WORKER_NAMEenv var injection in the CLI worker
spawn (the bundle entrypoint already passes the worker name directly). - Add a real cross-process e2e auth suite
(pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong
token, missing token, anonymous direct hits, and the no-auth-provider
pin-down behavior. - Step-execution route now has a response schema, satisfying
schema-consistency.test.ts. - Internal type cleanups (drop several
as anycasts in worker strategies
andBackgroundTaskWorker). RedisStreamsPubSub.maxDeliveryAttemptsnow rejects negative / NaN values
at construction.0still means "no cap" for back-compat but emits a
one-time warning; passInfinityto disable the cap explicitly.PullTransportaccepts a logger and uses it for unhandled router-callback
rejections instead ofconsole.error.BackgroundTaskWorker.start()now throws ifinit()was not called,
matching the contract of the other workers.- Cross-process integration tests now spawn a single user-owned project
(test-fixtures/cli-project/src/mastra/index.ts) through two generic
entries that mirror whatBuildBundlerand `Worker
- Step-execution endpoint (