Highlights
Trusted “system actor” execution for workflows, tools, memory, and agents
You can now run server-side/background work (cron jobs, schedulers, queues) as a trusted actor across workflow.execute(), tool.execute(), agent.generate()/stream(), and memory thread FGA checks—preserving tenant-scoped authorization while avoiding JWT/human membership requirements (requires organizationId in request context).
SignalProvider framework + declarative agent signal wiring (webhooks/polling/subscriptions)
A new SignalProvider abstraction enables building notification signal providers with built-in subscription tracking, polling lifecycle management, and webhook support; includes WebhookSignalProvider and also powers higher-level bundled providers like TaskSignalProvider.
Agent-agnostic interactive tools via native tool suspension (ask_user, submit_plan) + new Harness suspension API
ask_user and submit_plan now suspend using the generic tool-suspension primitive, so they work on any agent (not just Harness). Harness now surfaces tool_suspended events and resumes via respondToToolSuspension, with support for multiple concurrent suspensions.
ThreadState storage domain unlocks durable, agent-agnostic task tools
Task tools (task_write/update/complete/check) are now agent-agnostic by storing task state in a new per-thread threadState storage domain (keyed by (threadId, type)), projected to the state-signal lane via TaskStateProcessor for delta-first updates and better prompt-cache behavior; LibSQL adds ThreadStateLibSQL for durability across restarts.
New infrastructure/storage options: Postgres vNext observability + ClickHouse replication + durable harness sessions
Postgres gains PostgresStoreVNext with a dedicated, partitioned, insert-only observability domain (separate observability connection) for scalable spans/logs/metrics OLAP; ClickHouse adds opt-in replicated tables for multi-replica clusters; Harness v1 sessions become durable across backends (including new LibSQL support).
New packages: OpenAI Agents SDK + Railway Sandboxes + VoyageAI embeddings/reranking
Added @mastra/openai to run OpenAI Agents SDK agents through Mastra’s generate()/stream() with tracing/usage continuity; added @mastra/railway as a WorkspaceSandbox provider for Railway Sandboxes; added @mastra/voyageai for VoyageAI embeddings (incl. multimodal) and rerankers.
Developer experience: batching PubSub delivery + tool hooks + cheaper tool discovery
PubSub now supports per-subscriber batched/coalesced delivery (SubscribeOptions.batch) and flush(); agents/workspaces gain beforeToolCall/afterToolCall hooks; ToolSearchProcessor adds autoLoad (skip load round-trip) and restart-safe storage: 'context' mode.
Breaking Changes
- Removed
harness.respondToQuestion(...)→ useharness.respondToToolSuspension(...). - Removed
ask_questionevent → listen fortool_suspendedand readevent.suspendPayload. - Removed Harness question-related APIs/types (
registerQuestion,pendingQuestion,HarnessQuestion*types);pendingSuspensionbecomespendingSuspensions: Map<toolCallId, ...>. - Removed
harness.respondToPlanApproval(...)andplan_approval_required/plan_approvedevents → resumesubmit_planviarespondToToolSuspension({ toolCallId, resumeData: { action, feedback } }).
Changelog
@mastra/core@1.42.0
Minor Changes
-
You can now execute workflows, tools, and memory thread checks as a trusted actor, such as a background job or scheduled task. Pass an
actorobject to identify the system process making the call while keeping fine-grained authorization checks tenant-scoped. (#17484)const actor = { actorKind: 'system', sourceWorkflow: 'nightly-sync' } as const; await workflow.execute({ ...executeOptions, actor }); await tool.execute(input, { ...toolOptions, actor }); await MastraMemory.checkThreadFGA({ ...threadFGAOptions, actor });
-
Added an actor signal to core FGA checks for trusted server-side membership bypasses. (#17483)
const actor = { actorKind: 'system', sourceWorkflow: 'nightly-workflow' } as const; await checkFGA({ ...fgaOptions, requestContext, actor }); await requireFGA({ ...fgaOptions, requestContext, actor });
-
Enabled persistent storage for durable harness sessions across storage backends. Harness v1 can now resolve its session store from a configured storage adapter. (#17712)
-
Added the
actoroption to agentgenerate()andstream()invocations so trusted background work can run without a JWT or human membership. (#17487)const requestContext = new RequestContext(); requestContext.set('organizationId', 'org_123'); await agent.generate('Process daily report', { requestContext, actor: { actorKind: 'system', sourceWorkflow: 'daily-report-cron' }, });
Mastra denies trusted actor FGA checks when the request context does not include an
organizationId. -
Added SignalProvider abstraction for building notification signal providers. Enables declarative signal wiring in Agent config with built-in subscription tracking, polling lifecycle, and webhook support. Includes WebhookSignalProvider as a proof-of-concept. (#17577)
Writing a signal provider:
import { SignalProvider } from '@mastra/core/signals'; import type { SignalSubscription } from '@mastra/core/signals'; class SlackSignalProvider extends SignalProvider<'slack-signals'> { readonly id = 'slack-signals' as const; readonly pollInterval = 30_000; // poll every 30s async poll(subscriptions: SignalSubscription[]) { for (const sub of subscriptions) { const messages = await fetchNewMessages(sub.externalResourceId); if (messages.length > 0) { await this.notify( { source: 'slack', kind: 'new-message', summary: `${messages.length} new messages` }, { threadId: sub.threadId, resourceId: sub.resourceId }, ); } } } }
Declarative wiring:
import { Agent } from '@mastra/core/agent'; const agent = new Agent({ signals: [new SlackSignalProvider()], // ... other config });
-
Added optional
getUsers(userIds)batch lookup method toIUserProvider. Auth providers can implement it to resolve multiple users in a single call; providers that don't implement it continue to work via per-idgetUserfallback. (#17205)// optional batch lookup, returns results positionally aligned to userIds const users = await provider.getUsers?.(['u_1', 'u_2', 'u_3']);
-
Added MCP server Fine-Grained Authorization mapping overrides for tool authorization. (#17529)
Use the new
fgaoption onMCPServerto customize the resource and permission mappings used fortools/listandtools/callchecks without changing the Mastra instance-leveltoolmapping used by internal agent and workflow tool execution.const server = new MCPServer({ name: 'My Server', version: '1.0.0', tools: { getData }, fga: { resourceMapping: { tool: { fgaResourceType: 'user', deriveId: ({ user }) => user.id, }, }, permissionMapping: { 'tools:execute': 'read', }, }, });
-
Made the
ask_userbuilt-in tool agent-agnostic and removed the Harness question channel in favor of native tool suspension. (#17806)ask_usernow pauses through the same tool-suspension primitive used by every other interactive tool, so it works on any agent — not just inside a Harness. The Harness no longer has a separate question channel; instead it surfaces these pauses through the generictool_suspendedevent and resumes them withrespondToToolSuspension.Breaking changes
- Removed
harness.respondToQuestion(...). Useharness.respondToToolSuspension(...)instead. - Removed the
ask_questionevent. Listen fortool_suspendedand read the question fromevent.suspendPayload. - Removed
registerQuestionfromHarnessRequestContext, theHarnessDisplayState.pendingQuestionfield, and theHarnessQuestionAnswer,HarnessQuestionOption, andHarnessQuestionSelectionModetypes. HarnessDisplayState.pendingSuspension(a single object ornull) is nowHarnessDisplayState.pendingSuspensions, aMapkeyed bytoolCallId. This lets the display state hold several parked prompts at once, so resuming one parallelask_userno longer hides the others. Read a specific prompt withdisplayState.pendingSuspensions.get(toolCallId).
respondToToolSuspensionaccepts an optionaltoolCallIdso concurrently suspended tools (for example, parallelask_usercalls) can each be answered independently.harness.abort()now clears any pending tool suspensions, so a run parked in asuspend()(e.g. an unansweredask_user) can be aborted instead of staying parked forever. A newharness.hasPendingSuspensions()method reports whether the harness is awaiting a resume — useful because a suspended run nulls its internal abort controller, soisRunning()returnsfalsewhile the run is still pending.Before
harness.subscribe(event => { if (event.type === 'ask_question') { harness.respondToQuestion({ questionId: event.questionId, answer: 'Yes' }); } });
After
harness.subscribe(event => { if (event.type === 'tool_suspended' && event.toolName === 'ask_user') { const { question } = event.suspendPayload as { question: string }; harness.respondToToolSuspension({ toolCallId: event.toolCallId, resumeData: 'Yes' }); } });
- Removed
-
Made the
submit_planbuilt-in tool agent-agnostic and removed the Harness plan-approval channel in favor of native tool suspension. (#17817)submit_plannow pauses through the same tool-suspension primitive used by every other interactive tool, so it works on any agent — not just inside a Harness. A plain Agent (e.g. in Studio or a customer app) can render the plan and resume the tool withagent.resumeStream({ action, feedback }). The Harness surfaces the pause through the generictool_suspendedevent and resumes it withrespondToToolSuspension; on approval it additionally switches to its default (execution) mode.Breaking changes
- Removed
harness.respondToPlanApproval(...). Resume a submitted plan withharness.respondToToolSuspension({ toolCallId, resumeData: { action, feedback } }). - Removed the
plan_approval_requiredandplan_approvedevents. Listen fortool_suspendedwithevent.toolName === 'submit_plan'and read{ title, plan }fromevent.suspendPayload. - Removed
registerPlanApprovalfromHarnessRequestContextand theHarnessDisplayState.pendingPlanApprovalfield. A suspendedsubmit_plannow appears asHarnessDisplayState.pendingSuspensionlike any other suspended tool.
Before
harness.subscribe(event => { if (event.type === 'plan_approval_required') { harness.respondToPlanApproval({ planId: event.planId, response: { action: 'approved' } }); } });
After
harness.subscribe(event => { if (event.type === 'tool_suspended' && event.toolName === 'submit_plan') { const { title, plan } = event.suspendPayload as { title: string; plan: string }; harness.respondToToolSuspension({ toolCallId: event.toolCallId, resumeData: { action: 'approved' } }); } });
- Removed
-
Add batching primitives to the PubSub abstraction. (#16482)
New options
SubscribeOptions.batch(SubscribeBatchOptions): opt in to coalesced delivery on a per-subscriber basis. Fields:maxSize,maxWaitMs,minIntervalMs,isImmediate,coalesce,maxBufferSize,overflow.EventEmitterPubSubconstructor accepts optionalEventEmitterPubSubOptionswith aloggerfor batched-delivery error diagnostics.
Example
import { EventEmitterPubSub } from '@mastra/core/events'; const pubsub = new EventEmitterPubSub(); await pubsub.subscribe( 'agent-events', event => { // delivered in coalesced batches; the cb is still invoked once per event }, { batch: { maxSize: 10, // flush once 10 events have queued maxWaitMs: 500, // ...or after 500ms, whichever comes first }, }, );
New exports
SubscribeBatchOptionstype.PubSub.supportsNativeBatching— advertises whether an adapter honorsoptions.batchinternally.
Behavior
EventEmitterPubSub.supportsNativeBatching === true. Batched subscribers receive coalesced delivery driven by an in-memory buffer governed bymaxSize/maxWaitMs/minIntervalMs/isImmediate/coalesce/overflow/maxBufferSize.EventEmitterPubSub.flush()drains every batched subscriber buffer and waits for any pending nack redeliveries before resolving. Non-callback rejections surface through the configuredloggerinstead of being swallowed.CachingPubSubis transparent to batching: it forwardsoptions(includingbatch) to its inner PubSub and forwardssupportsNativeBatchingfrom the inner. Whether batching is honored depends entirely on the wrapped transport.
Contract notes
SubscribeBatchOptions.coalescemust return a subset of its input array by reference identity. Returning freshly-constructedEventobjects (even with matchingid) is treated as a contract violation: the batching layer can't routeack/nackto original transport handles for manufactured events, so the entire batch is discarded and every original event is acked as dropped. If you need merged payloads, build them in the subscriber callback after delivery.flush()is best-effort: a successful resolution does not guarantee every subscriber callback succeeded. Per-event errors surface via the configured logger.
-
Added agent and workspace tool hooks for applications that need to run logic before and after tool calls execute. Mastra Code now uses agent hooks so hook handlers run for built-in workspace tools as well as dynamic tools. (#17637)
Example
const agent = new Agent({ name: 'Support Agent', instructions: 'Help users.', model, hooks: { beforeToolCall: ({ toolName, input }) => { console.log(`Running ${toolName}`, input); }, afterToolCall: ({ toolName, output, error }) => { console.log(`Finished ${toolName}`, { output, error }); }, }, }); const workspace = new Workspace({ tools: { hooks: { beforeToolCall: ({ toolName, workspaceToolName, input }) => { console.log(`Running ${toolName} from ${workspaceToolName}`, input); }, }, }, });
-
Added interface-first model gateways while keeping the existing
MastraModelGatewaybase class backwards compatible. (#17608)Added
MastraModelGatewayInterfacefor plain object/custom gateway implementations and optional gatewayresolveAuthhooks.Moved MastraCode gateway-routed OAuth model construction into a custom Mastra gateway so
ModelRouterLanguageModelcan route through gatewayresolveAuthand provider-specificresolveLanguageModelbehavior.Usage:
import { MastraModelGatewayInterface, ModelRouterLanguageModel } from '@mastra/core/llm'; const myGateway: MastraModelGatewayInterface = { id: 'my-gateway', name: 'My Gateway', async fetchProviders() { return {}; }, buildUrl() { return 'https://api.example.com'; }, async getApiKey() { return process.env.API_KEY ?? ''; }, // Optional: own authentication lookup async resolveAuth(request) { return { apiKey: process.env.API_KEY, source: 'gateway' }; }, async resolveLanguageModel({ modelId, providerId, apiKey }) { // Return an AI SDK language model instance }, }; // Register and route through the gateway const router = new ModelRouterLanguageModel({ modelId: 'my-gateway/provider/model' }, [myGateway]);
Additional changes in this release:
- Inline three-tier auth resolution (explicit → gateway.resolveAuth → legacy getApiKey) into
ModelRouterLanguageModel.resolveAuthand deprecate the standaloneresolveModelAuthhelper. - Fix
defaultGatewaysdeduplication in theMastraclass to usegetGatewayId(gateway)instead of registry keys. - Remove no-op
resolveModelIdidentity function in mastracode in favor of direct usage. - Fix
defaultNameGeneratorregex in_llm-recorderto anchor directory matches to path boundaries (prevents false matches like-authsuffixes).
- Inline three-tier auth resolution (explicit → gateway.resolveAuth → legacy getApiKey) into
-
Make the task tools (
task_write,task_update,task_complete,task_check) agent-agnostic. (#17820)The task tools no longer depend on the Harness request context. The task list is now held in a generic, thread-scoped
threadStatestorage domain (ThreadStateStorage) — the source of truth — and projected onto the agent state-signal lane (stateId: "tasks") by the newTaskStateProcessor, so they work on anyAgentand not only inside the Harness.A new storage domain
threadStateis registered onMastraStorage(accessible viastorage.getStore("threadState")). It is keyed by(threadId, type)so it can hold any per-thread state; the task list lives undertype: "task"and the domain is intentionally generic so other agent-scoped state (e.g."goal") can reuse it. The composite store always wires anInMemoryThreadStateStorageby default, so task tracking works out of the box without configuring a backend, and a durable backend (@mastra/libsql'sThreadStateLibSQL) persists the list across process restarts. The tools read/write it within a run viacontext.mastra.getStorage().getStore("threadState"), scoped by the run'sthreadId.The state-signal projection is delta-first (modelled on working memory), cache-aware, and observational-memory-aware:
- The first projection of a populated list (and every compaction) is a full
<current-task-list>snapshot; each later mutation emits a small<task-list-update>delta carrying only that turn's add/remove/update ops, so a large task list is not re-sent in full on every change. - The task list is carried on the state-signal lane instead of being injected into the cached system prompt, so task updates no longer invalidate the prompt-cache prefix.
- The base snapshot and its deltas accumulate in the window; the processor re-snapshots once enough deltas accumulate (compaction) to bound window growth, and whenever observational-memory truncation drops the base snapshot from the window (deltas are meaningless without their base), reading the
threadStatestore so the agent never loses track of its tasks.
The task tools and the processor require a memory-backed thread. On a run that is not memory backed (no
threadId/resourceId), the task tools no-op and return a result explaining that task tracking requires agent memory.For the simplest setup, register
TaskSignalProvidervia the agent'ssignalsarray — it bundles the task tools and theTaskStateProcessorso they cannot get out of sync:import { Agent } from '@mastra/core/agent'; import { TaskSignalProvider } from '@mastra/core/signals'; const agent = new Agent({ name, instructions, model, memory, signals: [new TaskSignalProvider()] });
The Agent merges the tools into its toolset and registers the processor automatically.
New exports from
@mastra/core/storage:ThreadStateStorage,InMemoryThreadStateStorage, and theTaskRecordtype. New exports from@mastra/core/tools:taskWriteTool,taskUpdateTool,taskCompleteTool,taskCheckTool,TaskStateProcessor, and the task helpers/types (assignTaskIds,summarizeTaskCheck,TaskItem,TaskItemSnapshot,TaskCheckSummary,TaskCheckResult, etc.). New export from@mastra/core/signals:TaskSignalProvider, alongside the other signal providers. The Harness continues to re-export the task tools, so existing imports and toolset identity are unchanged.Internal behavior change: the Harness no longer stores the task list in session state. Task mutations still emit the
task_updateddisplay event, so the Harness display snapshot and any pinned task UI are unaffected. To adopt the new behavior on a plain agent (withMemoryand a Mastrastorage), registernew TaskSignalProvider()insignals— or, for manual control, addnew TaskStateProcessor()toinputProcessorsalongside the task tools. - The first projection of a populated list (and every compaction) is a full
-
Add workspace registry cleanup and shutdown destruction. (#17661)
Mastra now exposes
removeWorkspace(id, { destroy })for removing runtime workspaces from the registry. Whendestroyis true, the workspace is destroyed before removal and remains registered if destruction fails.mastra.shutdown()now destroys registered workspaces before closing storage and unregisters only the workspaces that clean up successfully. Workspace tool execution also updateslastAccessedAt, so runtime activity is tracked beyond search operations.await mastra.removeWorkspace('workspace-123', { destroy: true }); // destroys, then unregisters await mastra.removeWorkspace('workspace-456'); // unregisters only; caller owns destroy await mastra.shutdown(); // destroys registered workspaces, then closes storage
-
Added two options to
ToolSearchProcessorfor cheaper, more cache-friendly tool discovery. (#17691)autoLoad: one-turn discoverySet
search.autoLoadso thatsearch_toolsactivates the matching tools immediately, removing the separateload_toolround-trip. The model searches once and can call a discovered tool on its next turn instead of searching, loading, then calling. This cuts one model turn (and a full prompt replay) per discovery.import { ToolSearchProcessor } from '@mastra/core/processors'; const toolSearch = new ToolSearchProcessor({ tools: allTools, search: { topK: 3, autoLoad: true }, });
storage: opt-in'context'modeChoose where loaded-tool state lives. The default
'in-memory'keeps the original behavior. The new opt-in'context'mode derives loaded tools from the conversation messages, so it is restart-safe, needs no memory configuration, and de-loads a tool once its discovery result is no longer in the messages.const toolSearch = new ToolSearchProcessor({ tools: allTools, storage: 'context', });
Both modes are cache-friendly when loading tools, since loads are append-only and keep the cached prompt prefix stable. The default
'in-memory'store still shares a single'default'entry across anonymous (no thread ID) requests; usestorage: 'context'to keep anonymous requests isolated and derive loaded tools from the conversation messages.
Patch Changes
-
Fixed ConsoleLogger.warn() to correctly call console.warn() instead of console.info(), ensuring warn-level logs are routed to stderr and properly classified by log backends (e.g. Datadog) (#17623)
-
Fixed silent error swallowing when agent.stream() fails during idle-start, continuation, or pending-idle signal processing. Errors now propagate to the subscription stream via a new run-failed event, so harness consumers (like MastraCode) surface proper error events instead of silently completing with no response. (#17727)
-
Added author enrichment to the stored-agents list and get handlers. When an auth provider is configured, each agent record now includes a resolved
authorobject alongside the existingauthorId: (#17205){ id: 'agent_…', authorId: 'user_…', id, name?, email?, avatarUrl? } // new, optional // … }
Lookups are deduplicated per request and use the provider's
getUsersbatch method when available, falling back to per-idgetUsercalls otherwise. The field is omitted when no auth provider is configured or the ID can't be resolved, so existing clients keep working unchanged. -
Fixed ingestion of client-side tool tracing from AI SDK v6
toolMetadatawhen messages return through chatRoute. (#17202) -
Fixed durable and evented agent streams so tool-call steps continue to final text when maxSteps allows it. (#16679)
-
Clarified Studio config JSDoc to explain dual auth opt-in behavior. (#17722)
-
Fixed a multi-instance hang in evented execution mode. When two Mastra processes shared a UnixSocketPubSub broker, the agent's evented run could fail with
AGENT_GENERATE_MALFORMED_RESULT,condition is not a function, or hang silently with ECANCELED errors during shutdown. (#17788)What changed
- Internal workflow events (execution-workflow, agentic-loop, nested children walked via
parentWorkflow), per-run watch streams (workflow.events.v2.*), and scheduler-spawned background runs (sched_wf_*) are now delivered only inside the publishing process and no longer cross the unix socket. - Live class instances on event payloads (e.g. the
MastraModelOutputreturned by an agent run) are preserved instead of being stripped by JSON serialization on the broker round-trip.
Why
Follow-up to the broker-side filter shipped in #17727. That filter ran after the publish frame had already been serialized, which stripped functions/streams from payloads and still fanned out cumulative
stepResultsblobs (often 9 MB+) to every connected client. Short-circuiting these publishes in-process removes the serialization round-trip entirely and lets the agent's run result survive intact. - Internal workflow events (execution-workflow, agentic-loop, nested children walked via
-
Added anonymous, aggregated model token usage telemetry. When a Mastra server starts and observability metrics are enabled, the input and output token totals per provider and model are sent to Mastra's telemetry. Only aggregate token counts are collected — never prompts, responses, or message content. Opt out by setting MASTRA_TELEMETRY_DISABLED=1. (#17750)
-
Plan due notification dispatches per thread from a single due snapshot so high-priority notifications that already emitted summaries are delivered in full before lower-priority notifications or summaries can wake the same thread.
Agent.sendNotificationSignalalso accepts an array of notification inputs so related notifications can be evaluated against the same initial thread state before any delivery wakes the thread. (#17590) -
- Use evented-workflow engine in the internal agentic loop. (#16796)
- Fix nested workflow resume with resumeLabel in evented-workflow engine.
- Default to an in-memory store when no
storageis configured onMastra, and warn that it is not durable and a persistent storage adapter should be used in production.
-
Prevented user-created filesystem stored agents from being treated as code agents when they are not declared in the code Mastra instance. (#17622)
-
Fixed per-item request context being dropped for inline experiment data. When running an experiment with inline
data, each item'srequestContextis now passed to the agent or workflow and merged over the global request context (per-item values win on key collisions), matching the behavior of storage-backed datasets. (#17597)await dataset.startExperiment({ data: [{ input: { prompt: 'Hello' }, requestContext: { clinicId: 'clinic-1' } }], targetType: 'agent', targetId: 'support-agent', }); // Tools can now read clinicId from requestContext during inline experiments
-
Fixed thread subscribers so they can join an already-running remote stream and receive new output without waiting for the next run. (#17635)
-
Improved BM25 tokenization to support CJK (Japanese, Chinese, Korean) and other non-Latin languages. Added
TokenizeOptions.tokenizerfor plugging in custom tokenizers (e.g. n-gram, kuromoji). Workspacebm25config now acceptstokenizeoptions for full control over how text is split into search tokens. (#17640)Before: BM25 search returned no results for non-English content — CJK characters were silently stripped during tokenization.
After: Non-Latin characters are preserved by default. Users can also plug in a custom tokenizer:
new Workspace({ bm25: { tokenize: { tokenizer: myCustomCjkTokenizer, }, }, });
Fixes #17636
-
Added OTel span instrumentation for the hardcoded 10-second rate-limit backpressure sleep, making it visible in traces as a 'rate-limit-sleep' span with remainingTokens and delayMs metadata (#17623)
-
Fixed medium-priority notification summaries so they wake idle agent threads when they are ready to deliver. (#17590)
-
Fixed message part timestamps affecting transcript ordering by ensuring only message-level timestamps advance the ordering watermark. (#17598)
-
Added a local development warning when Enterprise features are used without a valid license. (#17694)
-
Fix
response.modelIdbeing overwritten withundefinedinstep-finishchunks when the upstream model stream omitsmodelIdfromresponse-metadata. The explicit empty-string fallback was placed before a...otherMetadataspread that could overwrite it withundefined. Moved themodelIdassignment after the spread so the fallback always wins. (#17795)This restores consistent
response.modelId === ''behavior across both the direct and evented agentic-loop workflow paths. -
Fix
Cannot get workflow run. Mastra storage is not initializeddebug log spam on agents that use memory or any input/output processors. (#17571)#17344 fixed this for the internal
execution-workflow, but agents with memory/processors also build an internal processor workflow (Agent.combineProcessorsIntoWorkflow, run byProcessorRunner.executeWorkflowAsProcessor) that never received the parentMastrainstance — so itscreateRun()→getWorkflowRunById()saw no storage and logged the noise on every run. The processor workflow now receives the parentMastrainstance and opts out of snapshot persistence (shouldPersistSnapshot: () => false), mirroring the execution-workflow fix. Follow-up to #17137 / #17344. -
Fix
request_accessgranting a path but the file staying unreadable withEACCES. After approving access, the very next tool (e.g.view) could still be rejected for the path that was just granted. The grant now reliably applies to the filesystem the agent's tools actually read from, and persists before the run resumes, so reads of an approved path succeed. (#17806) -
Update ai-sdk deps (#17144)
-
Fixed browser state signals so they no longer show 'Browser is closed' before the browser is used. Added attribution for browser closes and active URL changes when the agent or user caused them. (#17631)
-
Added optional
authorfield onStoredAgentResponseso consumers can render the resolved author (name, email, avatar) returned by stored-agent list and detail endpoints. (#17205) -
Fixed Windows absolute paths breaking workspace skill discovery.
WorkspaceSkillssplit paths on/only, so a skill referenced by an absolute Windows path loaded with the wrong name (the full path string orunknown) or failed to load, and skills undernode_moduleswere misclassified as local instead of external. (#17671)Skills passed by absolute path now resolve the same way on Windows and POSIX:
new Workspace({ skills: ['C:\\Users\\me\\skills\\my-skill'], });
@mastra/agent-browser@0.3.1
Patch Changes
- Fixed browser state reporting to identify when the agent or user closed the browser or changed the active URL. (#17631)
@mastra/agent-builder@1.0.41
Patch Changes
- Bumped @ai-sdk/anthropic to pick up refusal stop condition (vercel/ai#15928). When Anthropic refuses a request due to usage policy, the SDK now surfaces a proper stop reason instead of silently halting the agent loop. (#17799)
@mastra/ai-sdk@1.4.5
Patch Changes
- Fixed client-side tool tracing for useChat by carrying observability context through AI SDK v6
toolMetadataontool-input-availablestream chunks. (#17202)
@mastra/auth-workos@1.5.2
Patch Changes
- Added guidance for using
actorwith WorkOS integrations when trusted background jobs, such as cron jobs and scheduled workflows, have no JWT or human membership. (#17487)
@mastra/browser-viewer@0.1.2
Patch Changes
- Added support for the current Browserbase browse CLI config while keeping browse-cli configs working. (#17780)
@mastra/claude@0.2.0
Minor Changes
-
Added structured output support for Claude and OpenAI SDK agents using their provider-native structured output APIs. Cursor SDK agent calls now fail clearly when structuredOutput is requested because the Cursor TypeScript SDK does not expose a schema-constrained output API. SDK agents now implement provider-native resume through Mastra's existing resumeGenerate/resumeStream methods by accepting provider-specific resumeData with a message payload. Cursor SDK agent options now use the same clear source split as OpenAI: pass either a pre-created agent or SDK options for wrapper-created agents. (#17580)
Example:
await claudeAgent.resumeGenerate({ message: 'Continue the task.', sessionId: 'claude-session-id', }); await openAIAgent.resumeStream({ message: 'Continue the task.', previousResponseId: 'resp_123', }); const result = await openAIAgent.generate('Return the answer as JSON.', { structuredOutput: { schema: z.object({ answer: z.string() }), }, }); // result.object has shape { answer: string }
Claude and OpenAI SDK agents support
structuredOutputthrough their native SDK APIs.CursorSDKAgentthrows a clear error whenstructuredOutputis requested because the Cursor TypeScript SDK does not expose schema-constrained output.
Patch Changes
- Fixed Claude SDK agent generate responses to include renderable message text. (#17581)
@mastra/clickhouse@1.10.0
Minor Changes
-
Added opt-in replicated ClickHouse table support for multi-replica clusters. (#17684)
Configure
replicationto have Mastra create replicated MergeTree tables and addON CLUSTERto Mastra-owned DDL when a cluster name is provided:new ClickhouseStoreVNext({ id: 'clickhouse-storage', url: process.env.CLICKHOUSE_URL!, username: process.env.CLICKHOUSE_USERNAME!, password: process.env.CLICKHOUSE_PASSWORD!, replication: { cluster: 'company_cluster', }, });
Notes:
- If existing Mastra tables use local
MergeTreeorReplacingMergeTreeengines, initialization fails. Migrate existing local tables toReplicated*engines before enablingreplication. - vNext observability signal-table migrations (
migrateSpans()) are blocked whilereplicationis configured. Migrate legacy signal tables before turning on replication. - The default
zookeeperPathis/clickhouse/tables/{shard}/{database}/{table}. Override it if your cluster uses a different convention.
- If existing Mastra tables use local
Patch Changes
- Fixed DateTime64 comparison error with ClickHouse 26.5 by removing invalid empty string comparison on nullable DateTime64 columns in span deduplication query (#17149)
@mastra/client-js@1.24.0
Minor Changes
-
Add server endpoints so Studio can resolve agent-builder model availability and auth permission patterns without importing server-only EE code in the browser: (#17489)
GET /editor/builder/models/availablereturns the provider/model list already filtered by the active builder model policy (requiresAuth: true,stored-agents:read).GET /auth/permission-patternsreturns the valid permission-pattern strings. It is gated byrequiresAuth: truewith no finer-grained permission: the response is the non-sensitive route-permission vocabulary that every authenticated user needs to gate their own sidebar/redirects, and there is no narrower permission that fits.
@mastra/client-jsgainsgetBuilderAvailableModels()andgetPermissionPatterns()to consume these endpoints.import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); const { providers } = await client.getBuilderAvailableModels(); const { patterns } = await client.getPermissionPatterns();
-
Added optional
authorfield onStoredAgentResponseso consumers can render the resolved author (name, email, avatar) returned by stored-agent list and detail endpoints. (#17205)
Patch Changes
-
Added author enrichment to the stored-agents list and get handlers. When an auth provider is configured, each agent record now includes a resolved
authorobject alongside the existingauthorId: (#17205){ id: 'agent_…', authorId: 'user_…', id, name?, email?, avatarUrl? } // new, optional // … }
Lookups are deduplicated per request and use the provider's
getUsersbatch method when available, falling back to per-idgetUsercalls otherwise. The field is omitted when no auth provider is configured or the ID can't be resolved, so existing clients keep working unchanged. -
Fixed subscribed client tool continuations so browser-provided tool results keep their original inputs and client tools remain available across continuation runs. (#17603)
-
Added optional
getUsers(userIds)batch lookup method toIUserProvider. Auth providers can implement it to resolve multiple users in a single call; providers that don't implement it continue to work via per-idgetUserfallback. (#17205)// optional batch lookup, returns results positionally aligned to userIds const users = await provider.getUsers?.(['u_1', 'u_2', 'u_3']);
-
Added agent memory-support metadata to the agents API and client types. (#17581)
-
Added metric time series response type exports for client SDK consumers. (#17686)
-
Fixed subscribed client-tool continuations so client tools remain available across multiple continuation runs. (#17593)
@mastra/cursor@0.2.0
Minor Changes
-
Added structured output support for Claude and OpenAI SDK agents using their provider-native structured output APIs. Cursor SDK agent calls now fail clearly when structuredOutput is requested because the Cursor TypeScript SDK does not expose a schema-constrained output API. SDK agents now implement provider-native resume through Mastra's existing resumeGenerate/resumeStream methods by accepting provider-specific resumeData with a message payload. Cursor SDK agent options now use the same clear source split as OpenAI: pass either a pre-created agent or SDK options for wrapper-created agents. (#17580)
Example:
await claudeAgent.resumeGenerate({ message: 'Continue the task.', sessionId: 'claude-session-id', }); await openAIAgent.resumeStream({ message: 'Continue the task.', previousResponseId: 'resp_123', }); const result = await openAIAgent.generate('Return the answer as JSON.', { structuredOutput: { schema: z.object({ answer: z.string() }), }, }); // result.object has shape { answer: string }
Claude and OpenAI SDK agents support
structuredOutputthrough their native SDK APIs.CursorSDKAgentthrows a clear error whenstructuredOutputis requested because the Cursor TypeScript SDK does not expose schema-constrained output.
Patch Changes
@mastra/datadog@1.2.4
Patch Changes
-
Fixed DatadogBridge score forwarding to Datadog LLM Observability. (#17540)
-
Fixed missing model options and tool calls in Datadog LLM Observability spans. (#17693)
Forward model request options — Model call settings (temperature, maxOutputTokens) and provider-specific options (like OpenAI's reasoningEffort) now reach Datadog. Previously they were stripped before export, so they never appeared on the LLM span. They now show up under the span's metadata, alongside the available tools and tool choice.
Render tool calls in conversation history — Tool calls in an LLM's input messages now render correctly in Datadog instead of appearing as empty objects. Previously the raw tool-call shape was passed through unchanged and dropped by Datadog's tracer, losing tool calls from the conversation history. Output tool calls were already handled; input now matches.
@mastra/deployer@1.42.0
Patch Changes
-
Fixed deploy preflight checks so bundled Mastra dependency shims do not report example local storage URLs as real project storage paths. (#17674)
-
Mastra servers now report anonymous, aggregated model token usage at startup when observability metrics are enabled. Opt out by setting MASTRA_TELEMETRY_DISABLED=1. (#17750)
@mastra/e2b@0.3.3
Patch Changes
-
Add prefix-scoped GCS sandbox mounts (parity with S3 and Azure) (#17613)
GCSFilesystem.getMountConfig()now includes itsprefix(trailing slash
stripped), and the E2BmountGCSadds the correspondinggcsfuse --only-dir
flag when a prefix is set. This scopes the FUSE mount to the prefixed
subdirectory so sandbox paths map directly to prefixed GCS keys — matching the
existing S3 (bucket:/prefix) and Azure (--subdirectory) mounts. Previously a
prefixedGCSFilesystemdropped the prefix at both layers and mounted the entire
bucket inside the sandbox.The prefix is validated with the existing
validatePrefix(path-traversal guard)
and shell-escaped withshellQuote.
@mastra/editor@0.11.2
Patch Changes
-
Improved Agent Builder defaults so generated agent instructions stay concise while still covering the required operating checklist. Agent Builder chat requests now also use lower OpenAI reasoning effort by default. (#17604)
-
Update ai-sdk deps (#17144)
@mastra/evals@1.3.0
Minor Changes
-
Added
createRubricScorer, an LLM-as-judge scorer that grades agent output against a rubric of criteria and returns a binary verdict (1 only when every required criterion is satisfied) with per-criterion feedback. Drop it intoisTaskCompleteto make an agent self-correct until the rubric is met. (#17724)import { createRubricScorer } from '@mastra/evals/scorers/prebuilt'; const rubricScorer = createRubricScorer({ model: '__GATEWAY_OPENAI_MODEL_MINI__', criteria: [ { description: 'The response includes an analysis section' }, { description: 'The response includes concrete recommendations' }, ], }); await supervisor.stream('Research AI in education', { maxSteps: 10, isTaskComplete: { scorers: [rubricScorer], strategy: 'all' }, });
The rubric accepts a criteria array or a newline-delimited string, supports optional (non-gating) criteria, and can be supplied per run via request context under a
rubrickey.
Patch Changes
- Fixed AnswerRelevancyScorer (and other scorers) scoring live-agent turns 0. The scorer's user-input extractor ignored the
partsarray of format-2 messages, so when an agent persisted a message without the optionalcontentstring the input came through empty and every statement was judged irrelevant. The extractor now reads text fromparts(last text part wins), matching the assistant-output extractor. (#17769)
@mastra/express@1.3.30
Patch Changes
- Updated to support dual auth system. Adapters now check for both
studio.authandserver.authwhen gating RBAC, and route requests to the correct auth provider based on thex-mastra-client-typeheader. (#17722)
@mastra/fastify@1.3.30
Patch Changes
- Updated to support dual auth system. Adapters now check for both
studio.authandserver.authwhen gating RBAC, and route requests to the correct auth provider based on thex-mastra-client-typeheader. (#17722)
@mastra/gcs@0.2.2
Patch Changes
-
Add prefix-scoped GCS sandbox mounts (parity with S3 and Azure) (#17613)
GCSFilesystem.getMountConfig()now includes itsprefix(trailing slash
stripped), and the E2BmountGCSadds the correspondinggcsfuse --only-dir
flag when a prefix is set. This scopes the FUSE mount to the prefixed
subdirectory so sandbox paths map directly to prefixed GCS keys — matching the
existing S3 (bucket:/prefix) and Azure (--subdirectory) mounts. Previously a
prefixedGCSFilesystemdropped the prefix at both layers and mounted the entire
bucket inside the sandbox.The prefix is validated with the existing
validatePrefix(path-traversal guard)
and shell-escaped withshellQuote. -
Fixed an issue where images and PDFs stored in Google Cloud Storage were not surfaced to the agent as viewable media parts. The GCS workspace filesystem now populates
mimeTypeonstat()from the object's storedContent-Type(with extension-based fallback), so the workspaceread_filetool can route them through its native media-part path the same way the local filesystem does. (#16911)
@mastra/github-signals@0.1.1
Patch Changes
-
Gate GitHub signal notifications behind author permission checks to guard against prompt injection from random commenters. Only comments from users with write access (admin, maintain, write) trigger notifications. Bot comments are opt-in via an allowlist that defaults to CodeRabbit and Devin, with
ignoredBotsstill available as an explicit blocklist. Unauthorized latest comments are excluded before notification classification so noisy bot edits do not render in notification metadata or mask the latest authorized comment. Scheduled polls now include comments and detect latest-comment timestamp changes so comment notifications are not lost behind stale or unchanged thread hashes. Comment activity notifications render the latest authorized comment author and excerpt as high-priority GitHub signal updates. (#17590)New options on
GithubSignalsOptions:authorizedPermissions— permission levels that authorize human commenters (default:['admin', 'maintain', 'write'])authorizedBots— bot logins authorized to trigger notifications (default:['coderabbitai[bot]', 'devin-ai-integration[bot]'])ignoredBots— bot logins whose comments should NOT trigger notifications, even if authorizedpermissionResolver— injectable resolver for looking up collaborator permissions (default:gh api)
@mastra/hono@1.4.25
Patch Changes
- Updated to support dual auth system. Adapters now check for both
studio.authandserver.authwhen gating RBAC, and route requests to the correct auth provider based on thex-mastra-client-typeheader. (#17722)
@mastra/koa@1.5.13
Patch Changes
- Updated to support dual auth system. Adapters now check for both
studio.authandserver.authwhen gating RBAC, and route requests to the correct auth provider based on thex-mastra-client-typeheader. (#17722)
@mastra/langfuse@1.3.5
Patch Changes
-
Added support for custom top-level trace metadata in the Langfuse exporter. Any keys you set under
metadata.langfuse(other than the reservedpromptkey) are now forwarded as top-level Langfuse trace metadata, so you can filter and group traces by them. Nested values are serialized with JSON. (#17689)const tracingOptions = { metadata: { langfuse: { customerId: 'cust_123', tier: 'enterprise', }, }, }; // produces langfuse.trace.metadata.customerId and langfuse.trace.metadata.tier
@mastra/libsql@1.13.0
Minor Changes
-
Added LibSQL storage support for durable harness sessions. (#17712)
const storage = new LibSQLStore({ id: 'mastra-storage', url: 'file:./mastra.db' }); const harness = new Harness({ ownerId: 'my-app', agent, memory, storage, modes: [{ id: 'default', defaultModelId: '__GATEWAY_OPENAI_MODEL__' }], defaultModeId: 'default', });
-
Add
ThreadStateLibSQL, the LibSQL implementation of the newThreadStateStoragedomain. It persists per-thread, per-type state (e.g. the agent task list undertype: "task") in themastra_thread_statetable, keyed by(threadId, type). Composing a LibSQL store wires this domain automatically, so an agent's task list now survives a process restart. (#17820)
Patch Changes
-
Fixed concurrent writes silently disappearing when using LibSQL with a local (
file:) database. (#16796)LibSQL backs a local database with a single connection. When one operation held an interactive write transaction (for example, persisting workflow snapshots) and another operation wrote at the same time (for example, creating a dataset experiment), the second write could be swept into the open transaction and rolled back — so it appeared to succeed but never persisted. This surfaced as concurrent agent/workflow runs losing unrelated records.
Writes on a LibSQL client are now serialized, so a write issued during an in-flight transaction no longer interleaves with it.
@mastra/mcp@1.10.0
Minor Changes
-
Added MCP server Fine-Grained Authorization mapping overrides for tool authorization. (#17529)
Use the new
fgaoption onMCPServerto customize the resource and permission mappings used fortools/listandtools/callchecks without changing the Mastra instance-leveltoolmapping used by internal agent and workflow tool execution.const server = new MCPServer({ name: 'My Server', version: '1.0.0', tools: { getData }, fga: { resourceMapping: { tool: { fgaResourceType: 'user', deriveId: ({ user }) => user.id, }, }, permissionMapping: { 'tools:execute': 'read', }, }, });
Patch Changes
-
Fixed
MCPServerleaking one caller's resources to other callers. The result of the firstresources/listrequest was cached on the shared, long-lived server instance and replayed to everyone, so a dynamic resource provider that scopes resources per user or tenant (resolved fromextra.authInfo) served the first caller's resource index — names and URIs — to subsequent callers. The same stale cache also backedresources/readURI resolution and the publiclistResources()method. Theresources/templates/listhandler had the same defect for dynamic resource template providers. (#17610)Resource and resource template providers are now invoked on every request with the current caller's context, so each caller only sees their own resources. See #17609
-
Fixed flaky MCP server tests by replacing real weather API calls with deterministic mock tool (#17572)
@mastra/mcp-docs-server@1.1.46
Patch Changes
- Fixed the MCP docs server package root export so importing @mastra/mcp-docs-server loads a published runtime file. Fixes #17700. (#17709)
@mastra/memory@1.20.3
Patch Changes
- Fix final assistant text being lost after tool-approval resume when Observational Memory is enabled. During resume, input processors are skipped so no OM turn is created. The OM processor now directly persists new response messages in
processOutputResultwhen no active turn exists. (#17757)
@mastra/openai@0.1.0
Minor Changes
-
Added structured output support for Claude and OpenAI SDK agents using their provider-native structured output APIs. Cursor SDK agent calls now fail clearly when structuredOutput is requested because the Cursor TypeScript SDK does not expose a schema-constrained output API. SDK agents now implement provider-native resume through Mastra's existing resumeGenerate/resumeStream methods by accepting provider-specific resumeData with a message payload. Cursor SDK agent options now use the same clear source split as OpenAI: pass either a pre-created agent or SDK options for wrapper-created agents. (#17580)
Example:
await claudeAgent.resumeGenerate({ message: 'Continue the task.', sessionId: 'claude-session-id', }); await openAIAgent.resumeStream({ message: 'Continue the task.', previousResponseId: 'resp_123', }); const result = await openAIAgent.generate('Return the answer as JSON.', { structuredOutput: { schema: z.object({ answer: z.string() }), }, }); // result.object has shape { answer: string }
Claude and OpenAI SDK agents support
structuredOutputthrough their native SDK APIs.CursorSDKAgentthrows a clear error whenstructuredOutputis requested because the Cursor TypeScript SDK does not expose schema-constrained output. -
Added
@mastra/openai, a new package for using OpenAI Agents SDK agents in Mastra. (#17525)OpenAISDKAgentlets you register an OpenAI Agents SDK agent with Mastra, call it with Mastra-compatiblegenerate()andstream()methods, and keep usage and tracing data connected to the Mastra run.import { OpenAISDKAgent } from '@mastra/openai'; export const openaiAgent = new OpenAISDKAgent({ id: 'openai-sdk-agent', name: 'OpenAI SDK Agent', description: 'Use OpenAI Agents SDK through Mastra.', sdkOptions: { name: 'Repository assistant', instructions: 'Answer clearly and cite the relevant files.', model: '__GATEWAY_OPENAI_MODEL_BASE__', }, });
Use
sdkOptionswhen you want Mastra to create the OpenAI SDK agent. Passagentwhen your app already creates and owns the SDK agent.
Patch Changes
@mastra/pg@1.13.0
Minor Changes
-
Add the v-next observability storage domain for
@mastra/pg, an insert-only, (#16760)
partitioned Postgres adapter for low-volume observability (~100 calls/sec,
up to roughly 1,500 calls/sec sustained on a single primary).The new
PostgresStoreVNextcomposes a primaryPostgresStore(memory,
workflows, scores, agents, etc.) with anObservabilityStoragePostgresVNext
for spans, logs, metrics, scores, and feedback. All observability writes go
through a single multi-rowINSERT ... ON CONFLICT DO NOTHINGpath. Storage
is partitioned per day with three modes auto-detected atinit()time:
TimescaleDB hypertables, pg_partman (4.x or 5.x), or native Postgres range
partitions. Root-span lookups are served by partial indexes, and OLAP queries
(aggregates, breakdowns, time-series, percentiles) prune partitions by
timestamp. A small discovery cache table powers stale-while-revalidate
lookups for entity names/types/labels.The
observabilityconnection is required — callers always make an
explicit decision about where observability data lives. For production,
point it at a dedicated Postgres instance to keep OLAP scans from
contending with your primary OLTP workload. Reusing the primary
connection works for local development and logs a runtime warning on every
construction.import { Mastra } from '@mastra/core'; import { PostgresStoreVNext } from '@mastra/pg'; export const mastra = new Mastra({ storage: new PostgresStoreVNext({ id: 'app', connectionString: process.env.DATABASE_URL!, observability: { connectionString: process.env.OBSERVABILITY_DATABASE_URL!, }, }), });
Delta polling uses Postgres transaction IDs and a safe transaction horizon so
concurrent writers cannot cause late-committing rows to be skipped. The
observability-delta-pollingfeature flag is opt-in.ensureNativePartitions()swallows the42P07 relation already exists
error aroundCREATE TABLE IF NOT EXISTS … PARTITION OF, matching the
existing guard used for base-table and index DDL. This makes concurrent
init()from two processes (serverless cold-start, blue/green overlap, two
stores sharing a schema) idempotent instead of letting the loser surface an
unhandled duplicate-relation error.
Patch Changes
-
Added full Agent Builder storage support to the PostgreSQL adapter, bringing it to parity with libSQL. (#17596)
Previously, projects using PostgreSQL could not store tool provider connections or agent tool providers, and several Agent Builder tables were missing from the exported schema.
- Added storage for tool provider connections, so connections can be created, read, listed by author, and deleted on PostgreSQL.
- Agent versions now persist their tool providers on PostgreSQL across save and load.
- Fixed schema export so all Agent Builder tables are included.
-
Fixed
PgVectorignoring an explicitssloption when the connection string also contained ansslmode=(orssl=) query parameter.node-postgresre-parses the connection string and overwrote the explicitsslobject, causingUNABLE_TO_GET_ISSUER_CERT_LOCALLY/ "self-signed certificate" errors against self-signed or internal CAs even when verification was meant to be skipped. (#17650)PgVectornow honors an explicitssloption over the connection string, matching the existingPostgresStorebehavior. Connection-string-only SSL (?sslmode=requirewith no explicitssl) keeps working as before.import { PgVector } from '@mastra/pg'; // This now connects instead of throwing UNABLE_TO_GET_ISSUER_CERT_LOCALLY const vector = new PgVector({ id: 'my-vector', connectionString: 'postgresql://user:pass@host:5432/db?sslmode=require', ssl: { rejectUnauthorized: false }, });
-
Make atomic db updates better (#16796)
@mastra/playground-ui@33.0.0
Minor Changes
-
Added shared chat loading and tool card components to the playground UI package. (#17774)
-
Added per-component entrypoints under
@mastra/playground-ui/components/*and enabled tree-shaking via thesideEffectsfield. (#17740)New per-component entrypoints
Every design-system component can now be imported directly, without going through the root barrel:
import { Button } from '@mastra/playground-ui/components/Button';
The root
@mastra/playground-uiimport keeps working unchanged — this change is purely additive. Deep imports let bundlers pull in only the components you use instead of the whole library.Better tree-shaking
The package now declares
"sideEffects": ["**/*.css"], so bundlers can drop unused re-exports even for apps that keep importing from the root barrel. The CSS contract is unchanged: import@mastra/playground-ui/style.cssonce, then import components from any subpath. -
Removed legacy raw font-name aliases from playground UI theme tokens. Consumers should use
--font-display,--font-body, and--font-monodirectly. (#17707) -
Redesigned resize handles across the studio. The sidebar and panel separators now show a subtle gradient line that fades in on hover and stays visible for the whole drag — including when the sidebar is collapsed to icons and the cursor moves away from the handle (the handle now captures the pointer during the gesture).
PanelSeparatoraccepts a newvariantprop:line(default) fits panels with a visible container edge,pillshows a floating pill for panels without one. (#17823)<PanelSeparator /> // gradient line (default) <PanelSeparator variant="pill" /> // floating pill
-
Added native switch thumb icons so checked and unchecked states can show their own icon without custom overlay code. (#17569)
-
Added a Download trace JSON button to the trace detail panel in Studio. It saves the entire trace — every span with its full input, output, metadata, and attributes — to a
trace-<id>.jsonfile, so you can share a trace, attach it to a bug report, or build an eval dataset offline. Previously only individual IDs and per-section values could be copied. (#17752) -
Improved code rendering in the design system so
CodeBlockis the canonical surface for static code. (#17430)Fixed syntax-highlighted code to follow light and dark mode by default. Token colors are now resolved in CSS from the active theme class instead of JavaScript, so highlighted code works without a
ThemeProvider.Added a low-level
Codecomponent, now shared byCodeBlockandMarkdownRendererand exported for custom code surfaces:import { Code } from '@mastra/playground-ui'; <Code code="pnpm dlx mastra init" lang="bash" />;
Added an
overflowprop toCodeBlock. Use the defaultwrapfor commands and snippets, andscrollfor source code where preserving columns matters:<CodeBlock code={source} lang="typescript" overflow="scroll" />
Added Python syntax highlighting support.
-
Added a new
@mastra/playground-ui/theme.cssexport and made the Mastra brand green a built-in color. (#17668)New theme.css export
You can now import the design tokens as raw CSS:
@import 'tailwindcss'; @import '@mastra/playground-ui/theme.css'; /* design tokens */ @import '@mastra/playground-ui/style.css'; /* component styles */
Before, apps had to copy every token into their own
@themeblock so Tailwind would generate the matching utility classes (likebg-surface1ortext-neutral4). Now your app's Tailwind reads the tokens from this file directly. This is the same pattern astailwindcss/theme.css, and it keeps the tokens defined in one place.Brand green and chart colors
The
green-*classes now use the Mastra brand green (in both light and dark mode) instead of Tailwind's default green. New--chart-1through--chart-5colors are also available for charts. If you used the stock Tailwind green before, the new green looks a bit more vivid. -
Added role-based semantic font tokens so consumers can swap fonts in one declaration. Components now reference
--font-display(headlines, brand) and--font-body(UI, paragraphs) instead of type-based--font-sans/--font-serif. Existing utilities keep working through backward-compat aliases (--font-sans→--font-body,--font-serif→--font-display). (#16265)Override fonts in your app
:root { --font-display: 'Inter', system-ui, sans-serif; --font-body: 'Inter', system-ui, sans-serif; --font-mono: 'Commit Mono', ui-monospace, monospace; }
Backward-compat for existing consumers — the legacy raw font-name vars
--geist-mono,--font-inter,--tasa-explorercontinue to resolve via aliases to the semantic tokens, so anyfont-family: var(--geist-mono)keeps working without code changes. New code should referencevar(--font-mono)directly.The package no longer ships font files — defaults are system fonts. Bring your own fonts via
@font-facein your app and override the tokens above. -
Added an
xssize to Button, Input, Select, and InputGroup for compact, dense layouts. (#17675)<Button size="xs">Compact</Button> <Input size="xs" />
Unified the keyboard-focus look across controls. Buttons now show the same subtle border highlight on focus as inputs and selects, instead of a green ring, so a row of buttons, inputs, and selects feels consistent.
Made Select and Combobox triggers match buttons. They are now pill-shaped and reuse the Button styling, so a select reads like a button with a dropdown arrow. Their field-safe visual variants are
default(filled, used by default),outline, andghost— the same looks as buttons, minus the high-emphasisprimary. Sincedefaultis the default, you only pass avariantto switch tooutlineorghost. LegacySelectTrigger variant="primary"andCombobox variant="link"are still accepted for source compatibility, but render as the closest field-safe look. MultiCombobox'svariantnow works (it previously had no effect).Deprecated
asChildon the DropdownMenu, Dialog, AlertDialog, and Popover triggers (and DialogClose). Pass your element to therenderprop instead.asChildstill works for now.// Before <DropdownMenu.Trigger asChild> <Button>Open</Button> </DropdownMenu.Trigger> // After <DropdownMenu.Trigger render={<Button>Open</Button>} />
-
Added a token-usage-over-time timeline to the Studio metrics dashboard, showing input/output tokens per day and cost, filterable by agent and workflow. (#17686)
Patch Changes
-
Removed the generic tool card from the shared playground UI package. (#17774)
-
Removed an unused assistant UI dependency from the playground UI package. (#17774)
-
Fixed combobox keyboard selection after filtering so pressing Enter chooses the first matching option. (#17772)
-
Removed the
defaultButton size. Buttons now usemdwhen no size is provided, and the previousdefaultstyling is available aslg. (#17778)Migration
Before:
<Button size="default">Save</Button>
After:
<Button size="lg">Save</Button>
Use
size="md"or omitsizefor the new default Button size. -
Fixed the Studio Metrics Latency card drilldown, which was a silent no-op on all three tabs (Agents, Workflows, Tools). The view-level click guard and the container-level navigation handler both read a
rawTimestampfield that the hook never produces; the only timestamp on aLatencyPointistsMs. Clicking a chart point now correctly navigates to the Traces page filtered to the 1-hour bucket and the entity type of the active tab. (#17704) -
Added an optional
data-testidprop to the BreadcrumbCrumbso individual crumbs can be targeted in tests and automation. (#17687) -
Improved DataList pagination controls, row and header separators, wrapped-row spacing, text truncation, horizontal overflow handling, and empty-cell rendering. (#17718)
-
Removed five rarely-used components from the root barrel export.
SettingsRow,PrevNextNav,MetricsKpiCard,SideDialog, andContextMenumust now be imported from their per-component entrypoints (added in v33.1): (#17753)// before import { SettingsRow, SideDialog, type SideDialogRootProps } from '@mastra/playground-ui'; // after import { SettingsRow } from '@mastra/playground-ui/components/SettingsRow'; import { SideDialog, type SideDialogRootProps } from '@mastra/playground-ui/components/SideDialog';
This is the first step of gradually slimming down the root barrel so apps only load the components they use. All other root exports are unchanged.
-
Removed an unnecessary overflow clip on the Switch track so thumb motion effects are no longer cut off at the edges (#17822)
-
Removed the right padding on the agent memory sidebar so it sits flush against the panel resize handle. (#17823)
-
Fixed the Evaluate Trace button in Studio (Agents → Traces) doing nothing on the first click. Selecting the anchor span and opening the scoring tab now happen in a single URL update instead of two racing ones, so the scoring panel opens immediately. (#17730)
-
Fixed the Agent and Workflow icon not appearing in the Traces and Logs entity column. The icon now matches the stored lowercase entity types (
agent,workflow_run) instead of only uppercase values, so you can tell agent runs from workflow runs at a glance. (#17730) -
Fixed input focus borders so hovering no longer overrides the focused state. (#17570)
@mastra/posthog@1.0.28
Patch Changes
- Fixed PostHog group analytics not being populated for AI events. Events that include
metadata.$groupsare now correctly attached to their PostHog groups, so you can slice LLM analytics such as cost by group. (#17624)
@mastra/railway@0.1.0
Minor Changes
-
Add
@mastra/railway, a sandbox provider for Railway Sandboxes. (#17784)Provisions ephemeral, isolated Linux VMs on Railway through the Railway
TypeScript SDK and exposes them as a MastraWorkspaceSandbox. Supports command
execution with streaming output and timeouts, background process management,
configurable idle timeout,ISOLATED/PRIVATEnetwork isolation, custom base
images via the Railway template builder, forking a running sandbox into a new
one, and reattaching to an existing sandbox by ID. Also exports
railwaySandboxProviderfor registration withMastraEditor.import { Workspace } from '@mastra/core/workspace'; import { RailwaySandbox } from '@mastra/railway'; const workspace = new Workspace({ sandbox: new RailwaySandbox({ idleTimeoutMinutes: 30, networkIsolation: 'PRIVATE', }), });
Patch Changes
@mastra/react@0.6.0
Minor Changes
-
Added voice helpers to the React SDK and made agent text-to-speech audible. (#17663)
The React SDK now exposes voice helpers:
useSpeechRecognitionfor speech-to-text,playStreamWithWebAudiofor buffered Web Audio playback from aReadableStream, andrecordMicrophoneToFilefor capturing microphone audio. TheuseSpeechRecognitionhook automatically uses an agent's voice provider when one is configured, and falls back to the browser's built-in speech recognition otherwise.import { useSpeechRecognition } from '@mastra/react'; function VoiceInput({ agentId }: { agentId?: string }) { const { start, stop, isListening, transcript } = useSpeechRecognition({ agentId }); return <button onClick={isListening ? stop : start}>{transcript}</button>; }
Also fixed agent text-to-speech being inaudible. The
voice/speakroute now returns a web-standard audio response (Content-Type: audio/mpeg) so server-side adapters pass raw audio bytes through to the client instead of JSON-encoding them. This also resolvesgetReadercrashes when an agent speaks using providers like OpenAI voice. -
Show a sent user message instantly as a pending bubble in the chat thread, then settle it in place when the server confirms it — instead of staging it near the composer. The optimistic bubble and the outgoing message share a client-generated correlation id, so the server echo reconciles the exact bubble with no duplicate message. (#17688)
Patch Changes
-
Fixed MessageFactory Reasoning renderer types to expose streaming state metadata. (#17774)
-
Fix streamed user messages with attachments in React chat state (#17774)
@mastra/server@1.42.0
Minor Changes
-
Added author enrichment to the stored-agents list and get handlers. When an auth provider is configured, each agent record now includes a resolved
authorobject alongside the existingauthorId: (#17205){ id: 'agent_…', authorId: 'user_…', id, name?, email?, avatarUrl? } // new, optional // … }
Lookups are deduplicated per request and use the provider's
getUsersbatch method when available, falling back to per-idgetUsercalls otherwise. The field is omitted when no auth provider is configured or the ID can't be resolved, so existing clients keep working unchanged. -
Added dual auth system for separate Studio and API authentication. (#17722)
When configured, Studio and server (API) can use different auth providers:
server.authhandles API authentication (external customers)studio.authhandles Studio authentication (internal team)
Dual auth is opt-in: If
studio.authis not configured, Studio requests fall back toserver.authfor backward compatibility. To enable strict separation, configure both.Example
const mastra = new Mastra({ server: { auth: new MastraAuthWorkos({ ... }), // External customers }, studio: { auth: new MastraAuthOkta({ ... }), // Internal team rbac: new StaticRBACProvider({ roles: DEFAULT_ROLES, getUserRoles: (user) => [user.role], }), }, });
This pattern matches real-world SaaS architecture (e.g., Stripe, Supabase) with separate dashboard and API authentication.
-
Add server endpoints so Studio can resolve agent-builder model availability and auth permission patterns without importing server-only EE code in the browser: (#17489)
GET /editor/builder/models/availablereturns the provider/model list already filtered by the active builder model policy (requiresAuth: true,stored-agents:read).GET /auth/permission-patternsreturns the valid permission-pattern strings. It is gated byrequiresAuth: truewith no finer-grained permission: the response is the non-sensitive route-permission vocabulary that every authenticated user needs to gate their own sidebar/redirects, and there is no narrower permission that fits.
@mastra/client-jsgainsgetBuilderAvailableModels()andgetPermissionPatterns()to consume these endpoints.import { MastraClient } from '@mastra/client-js'; const client = new MastraClient({ baseUrl: 'http://localhost:4111' }); const { providers } = await client.getBuilderAvailableModels(); const { patterns } = await client.getPermissionPatterns();
Patch Changes
-
Fixed the tools API endpoint to include dynamically created tools (e.g., MCP tools) alongside bundler-discovered tools. Previously, when the CLI bundler discovered tools, dynamically created tools registered via
new Mastra({ tools })were silently dropped from the/api/toolsresponse. Now both sources are merged, with bundler-discovered tools taking precedence on conflicts. (#17535) -
Added voice helpers to the React SDK and made agent text-to-speech audible. (#17663)
The React SDK now exposes voice helpers:
useSpeechRecognitionfor speech-to-text,playStreamWithWebAudiofor buffered Web Audio playback from aReadableStream, andrecordMicrophoneToFilefor capturing microphone audio. TheuseSpeechRecognitionhook automatically uses an agent's voice provider when one is configured, and falls back to the browser's built-in speech recognition otherwise.import { useSpeechRecognition } from '@mastra/react'; function VoiceInput({ agentId }: { agentId?: string }) { const { start, stop, isListening, transcript } = useSpeechRecognition({ agentId }); return <button onClick={isListening ? stop : start}>{transcript}</button>; }
Also fixed agent text-to-speech being inaudible. The
voice/speakroute now returns a web-standard audio response (Content-Type: audio/mpeg) so server-side adapters pass raw audio bytes through to the client instead of JSON-encoding them. This also resolvesgetReadercrashes when an agent speaks using providers like OpenAI voice. -
Added optional
getUsers(userIds)batch lookup method toIUserProvider. Auth providers can implement it to resolve multiple users in a single call; providers that don't implement it continue to work via per-idgetUserfallback. (#17205)// optional batch lookup, returns results positionally aligned to userIds const users = await provider.getUsers?.(['u_1', 'u_2', 'u_3']);
-
Fixed the Agent Builder selecting models from providers with no API key configured. The builder available-models list now only includes providers whose API key is set, so an agent created in the Agent Builder can no longer be assigned a model that can never run. (#17725)
-
Added agent memory-support metadata to the agents API and client types. (#17581)
-
Added optional
authorfield onStoredAgentResponseso consumers can render the resolved author (name, email, avatar) returned by stored-agent list and detail endpoints. (#17205) -
Allow
workflows:executeto authorizePOST /workflows/:workflowId/create-runandPOST /workflows/events. Both routes previously requiredworkflows:write(which is intended for editing workflow definitions); they now accept eitherworkflows:writeorworkflows:execute. This unblocks Studio's "Run workflow" flow for roles that only have*:execute(e.g. WorkOSmember) and aligns the broker push endpoint's permission with what it actually does (advance runtime state). (#17855)
@mastra/stagehand@0.2.4
Patch Changes
- Fixed Stagehand browser support for OpenAI-compatible model configuration. (#17219)
@mastra/tavily@1.0.2
Patch Changes
- Update ai-sdk deps (#17144)
@mastra/turbopuffer@1.0.2
Patch Changes
- Fixed Turbopuffer vector store compatibility with the latest Turbopuffer client. (#17148)
@mastra/voice-google-gemini-live@0.12.1
Patch Changes
- Update ai-sdk deps (#17144)
@mastra/voyageai@0.1.0
Minor Changes
-
feat(voyageai): add VoyageAI embeddings and reranker integration (#14296)
Adds the
@mastra/voyageaipackage underembedders/with:- Text embeddings (voyage-4 and voyage-3 series, plus code/finance/law models)
with token-aware batching via the SDKtokenize()method - Multimodal embeddings (text + images + video) via voyage-multimodal-3/3.5
- Contextualized chunk embeddings via voyage-context-3
- Rerankers (rerank-2.5 and rerank-2 families) implementing
RelevanceScoreProvider
- Text embeddings (voyage-4 and voyage-3 series, plus code/finance/law models)
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/agentcore@0.2.1
- @mastra/arize@1.2.2
- @mastra/arthur@0.3.2
- @mastra/auth-supabase@1.0.1
- @mastra/blaxel@0.4.1
- @mastra/couchbase@1.0.3
- @mastra/deployer-cloud@1.42.0
- @mastra/deployer-cloudflare@1.1.43
- @mastra/deployer-netlify@1.1.19
- @mastra/deployer-vercel@1.1.37
- @mastra/dsql@1.0.2
- @mastra/duckdb@1.4.2
- @mastra/dynamodb@1.0.8
- @mastra/google-cloud-pubsub@1.0.5
- @mastra/inngest@1.5.1
- @mastra/laminar@1.2.2
- @mastra/longmemeval@1.0.49
- @mastra/mongodb@1.9.2
- @mastra/mssql@1.3.1
- @mastra/nestjs@0.1.14
- @mastra/opencode@0.0.46
- @mastra/opensearch@1.0.2
- @mastra/otel-bridge@1.2.2
- @mastra/otel-exporter@1.2.2
- @mastra/redis@1.1.2
- @mastra/redis-streams@0.0.3
- @mastra/s3@0.5.2
- @mastra/s3vectors@1.0.6
- @mastra/sentry@1.1.3
- @mastra/spanner@1.1.1
- @mastra/temporal@0.1.13
- @mastra/upstash@1.1.2
- @mastra/voice-aws-nova-sonic@0.1.3
- @mastra/voice-google@0.12.2