Releases: mastra-ai/mastra
Release list
July 15, 2026
Highlights
Durable Agent Crash Recovery (server + client + boot-time)
Durable agents can now discover and recover orphaned RUNNING runs after a restart via DurableAgent.listActiveRuns(), recover(), and recoverActiveRuns(), plus Mastra.recoverAllDurableAgents() and an opt-in MastraConfig.recovery: { durableAgents: 'auto' } for boot-time recovery. There’s also a standard HTTP/client surface (POST /agents/:agentId/recover + client-js agent.recover({ runId })) to reattach and stream the remaining output from dashboards or admin tools.
Scoped AgentController Sessions (parallel sessions per resource)
AgentController sessions now support a scope/sessionScope, allowing multiple independent sessions to run in parallel over the same resourceId (e.g., one per git worktree) without sharing run loops, threads, or state. The scope is also exposed in request context for integrations, and controller APIs now report run activity (running + per-thread state) for better UIs.
New Workspace + Sandbox Providers for Mastra Platform
A new package, @mastra/platform-workspace@0.1.0, adds PlatformFilesystem and PlatformSandbox providers that connect agents to Mastra Platform sandboxes and bucket-backed filesystems via the workspace-proxy API. This enables running workspace-dependent tooling against Platform-managed infrastructure with env-var or config-based setup.
Mastra Code SDK Now Public + More Extensible Integrations
@mastra/code-sdk@0.1.0 is now published as a first-class package (previously internal), enabling third parties to build custom UIs/surfaces on top of the Mastra Code coding agent. It also adds async per-request extraTools resolution and a postToolObserver hook for reacting to tool calls without replacing built-ins.
MCP Server: Better Serverless Streaming + Dynamic Tooling + Notifications
@mastra/mcp adds serverlessStreaming to MCPServer.startHTTP() to deliver request-scoped SSE progress notifications even in serverless mode, and expands notification support (tools list changed, server logs, per-session resource subscriptions, and reliable broadcast across streamable HTTP). MCP tool execution context also gains optional log/progress functions so tools can report status back to clients.
Breaking Changes
- None noted in this changelog.
Changelog
@mastra/core@1.51.0
Minor Changes
-
Added session scoping to
AgentControllerso independent sessions can run in parallel over the same resource (for example one session per git worktree). (#19357)Previously
createSession()was get-or-create byresourceIdalone, so two callers sharing a resource always resolved to the same session — with one run loop, one thread binding, and shared mode/model/state. Passing the newscopeoption creates an independent session per scope:// Two independent sessions over the same resource: const a = await controller.createSession({ resourceId: 'repo-123', scope: '/worktrees/feature-a', tags: { projectPath: '/worktrees/feature-a' }, }); const b = await controller.createSession({ resourceId: 'repo-123', scope: '/worktrees/feature-b', tags: { projectPath: '/worktrees/feature-b' }, }); // Look up a scoped session later: const session = await controller.getSessionByResource('repo-123', '/worktrees/feature-a');
Calls with the same
resourceIdandscopestill resume the same session (get-or-create), and unscoped sessions behave exactly as before. -
Added support for custom processors on
createScorerjudges. You can now passinputProcessors,outputProcessors,errorProcessors, andmaxProcessorRetriesin a scorer'sjudgeconfig (or per-step judge config) to apply processors to the internal judge agent — for example wiring upStreamErrorRetryProcessorto retry transient LLM errors while scoring. (#19195)import { createScorer } from '@mastra/core/evals'; import { StreamErrorRetryProcessor } from '@mastra/core/processors'; const scorer = createScorer({ id: 'my-scorer', description: 'Scores responses', judge: { model: myModel, instructions: 'You are an expert evaluator...', errorProcessors: [new StreamErrorRetryProcessor()], maxProcessorRetries: 3, }, });
-
Added support for resolving
foreachconcurrency at execution time.concurrencycan now be a function that receives the foreach input and the workflow's init data and returns a number, in addition to a static number: (#19329)workflow.foreach(step, { concurrency: ({ inputData, getInitData }) => (getInitData().fast ? 10 : 1), });
Durable agents use this to honor
toolCallConcurrency: parallel tool calls now run concurrently (default 10) instead of always sequentially, while runs that require tool approval or use tools that can suspend still execute tool calls one at a time. -
Added an option to retry unknown stream errors while allowing known authorization failures to surface immediately, and enabled resilient retries for coding agents. (#19290)
import { StreamErrorRetryProcessor } from '@mastra/core/processors'; const processor = new StreamErrorRetryProcessor({ retryUnknownErrors: true, maxRetries: 2, delayMs: 3000, });
-
Added:
runEvals()now supports gate-only runs.scorersis optional when at least one gate is provided. (#19348) -
Added
maxRetryAfterMstoStreamErrorRetryProcessor, with a default of 30 seconds, so providerRetry-Afterwaits can't exceed a configured limit. Aborting during the wait now stops the processor retry before another model request. (#19383)Improved structured-output recovery so transport and provider failures don't trigger JSON prompt injection. Scorer judges that use Mastra's current generation API can use existing error processors for a coordinated retry budget. Legacy model adapters keep their separate
generateLegacy()retry settings.Before
import { StreamErrorRetryProcessor } from '@mastra/core/processors'; new StreamErrorRetryProcessor({ maxRetries: 2, });
After
import { StreamErrorRetryProcessor } from '@mastra/core/processors'; new StreamErrorRetryProcessor({ maxRetries: 2, maxRetryAfterMs: 30_000, });
-
Workflow run event topics are now cleaned up automatically. The evented workflow engine deletes each run's
workflow.events.v2.<runId>pub/sub topic shortly after the run reaches a terminal state (success, failure, or cancellation), so persistent transports like Redis Streams no longer accumulate streams from finished workflow runs (#19123). (#19418)clearTopicis now part of thePubSubbase class with a default no-op implementation. Custom transports that retain messages per topic should override it to delete that state:import { PubSub } from '@mastra/core/events'; class CustomPubSub extends PubSub { async clearTopic(topic: string): Promise<void> { // delete retained state for the topic } }
Callers no longer need to probe for the method before calling it —
CachingPubSuband the durable-agent runtime now forwardclearTopicunconditionally. -
Added anonymous feature usage telemetry for server startup surface counts and a
trackFeatureUsage()API. (#19159) -
Added atomic caller-defined dataset IDs for idempotent dataset creation across built-in storage adapters. Supplying
idtomastra.datasets.create()now creates the dataset once and resolves compatible retries to the persisted record; incompatible immutable identity fields throwDATASET_ID_CONFLICT. (#19370) -
Added PROVIDER_TOOL_CALL observability spans for provider-executed tools (e.g. Anthropic code execution, server-side web search). Provider tool input and output are now visible in traces and Studio, with spans anchored to the AGENT_RUN parent. (#19261)
-
Added the ability to explicitly disable a storage domain on
MastraCompositeStoreby setting it tofalsein thedomainsconfig. A disabled domain no longer falls back to theeditorordefaultstore, so writes for that domain are dropped instead of silently landing in the fallback database. (#19059)const storage = new MastraCompositeStore({ id: 'my-storage', default: libsqlStore, domains: { // don't persist traces/metrics when observability is turned off observability: false, }, });
prune()also accepts a per-callretentionoption that replaces the configured retention policies for that call only — for example to skip a domain (keep chat history) or prune more aggressively without reconstructing the store:// prune everything except the memory domain, one time await storage.prune({ retention: { observability: { spans: { maxAge: '14d' } }, }, });
-
Added the authoritative session scope to agent controller request context for scoped session integrations. (#19446)
const controllerContext = requestContext.get('controller'); console.log...
July 6, 2026
Highlights
LiveKit Realtime Voice Agents (@mastra/livekit)
New @mastra/livekit package turns Mastra agents (or per-turn workflows) into realtime voice agents with LiveKit handling the full audio loop (WebRTC, VAD, STT/TTS, turn detection, barge-in) while Mastra drives replies, tools, and memory—plus built-in tracing and a Studio “voice mode”.
File-System Routed Mastra Primitives + Zero-Boilerplate Startup
Mastra can now auto-discover and register storage.ts, observability.ts, server.ts, studio.ts, workflows/*.ts, and agent processors from the src/mastra directory during mastra dev/mastra build; @mastra/deployer can also auto-construct a Mastra instance when src/mastra/index.ts is missing, enabling fully file-based projects with no new Mastra(...) entry file.
Unified Schedules API (Heartbeats → Schedules)
Agent “heartbeats” and workflow schedules are unified under mastra.schedules and /api/schedules (client + server), with deprecated/removed heartbeat routes and methods; persisted legacy rows are normalized (target.type: 'heartbeat' → 'agent') so existing schedules keep firing across supported stores.
Workspace Provider Registry in MastraEditor
MastraEditor now supports a workspace-level provider registry: register a single createWorkspace factory that builds complete Workspace instances (filesystem + sandbox), and let stored agents reference them via { type: 'provider', provider, config } for cleaner hydration and multi-environment workspace setups.
Observability Improvements for Serverless + File-Based Config
Observability can now be file-routed via src/mastra/observability.ts, and ObservabilityEntrypoint adds flush() so mastra.observability.flush() works directly in serverless environments (delegating to all instances, similar to shutdown()).
Breaking Changes
- Heartbeats were renamed to schedules:
mastra.heartbeats→mastra.schedules, configheartbeat→schedules, type renames (e.g.,Heartbeat→AgentSchedule), new agent schedule IDs useagent_, and the default fire signal tag is now<schedule>. - Server removed
/api/heartbeats/*routes in favor of the unified/api/schedules/*API. - Client deprecated
*Heartbeat()methods in favor of unified schedule methods (to be removed in a future release).
Changelog
@mastra/core@1.50.0
Minor Changes
-
Added file-system-routed observability singleton. Place an
observability.tsfile in your mastra directory that default-exports anObservabilityEntrypoint, and it will be auto-discovered and registered when runningmastra devormastra build. Code-registered observability takes precedence if both are present. (#18887)// src/mastra/observability.ts import { Observability, MastraStorageExporter } from '@mastra/observability'; export default new Observability({ configs: { default: { serviceName: 'mastra', exporters: [new MastraStorageExporter()] } }, });
-
Added file-system routed storage support. A
storage.tsfile under the mastra directory is now auto-discovered and registered duringmastra dev/mastra build. The default export replaces the InMemoryStore fallback. Code-registered storage (passed tonew Mastra({storage})) wins on collision. (#18885)// src/mastra/storage.ts import { LibSQLStore } from '@mastra/libsql'; export default new LibSQLStore({ url: 'file:local.db' });
-
Added file-system-routed workflows support. Workflows placed in
workflows/*.tsunder the mastra directory are now auto-discovered and registered duringmastra dev/mastra build, matching the existing file-based agents convention. Code-registered workflows win on name collisions. (#18883)// src/mastra/workflows/onboarding.ts import { createWorkflow } from '@mastra/core/workflows'; export default createWorkflow({ id: 'onboarding' /* ...steps */ });
-
Added workspace-level provider registry to MastraEditor. You can now register WorkspaceProvider factories that build complete Workspace instances as a single unit, instead of composing from separate filesystem and sandbox providers. Stored agents can reference a workspace provider via
{ type: 'provider', provider: 'my-cloud', config: { ... } }and the editor will call the registered factory during agent hydration. (#18781)import { MastraEditor } from '@mastra/editor'; import { Workspace } from '@mastra/core/workspace'; const editor = new MastraEditor({ workspaces: { 'my-cloud': { id: 'my-cloud', name: 'My Cloud Workspace', createWorkspace: config => new Workspace({ id: 'cloud-ws', name: 'Cloud WS', filesystem: new MyCloudFilesystem(config), sandbox: new MyCloudSandbox(config), }), }, }, }); // Stored agent workspace reference using the provider: // { type: 'provider', provider: 'my-cloud', config: { region: 'us-east-1' } }
-
models.dev gateway: honor per-model
provideroverrides (endpoint, request shape, SDK). (10959d5)A provider can now serve individual models over a different base URL / request shape than the provider default — e.g. a model served over the OpenAI Responses API while the provider default is chat-completions. The models.dev gateway now reads each model's
providerblock (api,shape,npm), soresolveLanguageModelroutesshape: "responses"models via the OpenAI Responses API andbuildUrlprefers a per-modelapiwhen present. Providers without per-model overrides are unaffected. -
Added file-system routed server config singleton. Place a server.ts file in your mastra directory that default-exports a ServerConfig object, and it will be auto-discovered and registered when running mastra dev or mastra build. Code-registered server config takes precedence if both are present. (#18888)
-
Added file-system-routed agent processors. Place input and output processor files under
agents/<name>/processors/input/andagents/<name>/processors/output/. Each file default-exports a processor, and they are auto-discovered and merged with config-defined processors when runningmastra devormastra build. Config-defined processors run first, and a dynamic (function)inputProcessors/outputProcessorsinconfig.tstakes precedence over discovered files. (#18890)src/mastra/agents/support/ ├── config.ts ├── instructions.md └── processors/ ├── input/ │ └── moderation.ts └── output/ └── redact-pii.ts// src/mastra/agents/support/processors/input/moderation.ts import { ModerationProcessor } from '@mastra/core/processors'; export default new ModerationProcessor({ model: 'openai/gpt-5-nano' });
-
Renamed heartbeats to schedules. Agent heartbeats and workflow schedules are now one unified Schedules API:
mastra.schedulesmanages both. The name "heartbeat" implied a liveness check; these are cron-based agent schedules, so they are now simply called schedules. (#18874)Before
const hb = await mastra.heartbeats.create({ agentId: 'chef', cron: '0 9 * * *', prompt: 'Suggest a dish of the day', }); await mastra.heartbeats.pause(hb.id);
After
// Schedule an agent (was a heartbeat) const schedule = await mastra.schedules.create({ agentId: 'chef', cron: '0 9 * * *', prompt: 'Suggest a dish of the day', }); // Schedule a workflow with the same API await mastra.schedules.create({ workflowId: 'daily-report', cron: '0 6 * * *', inputData: { region: 'us' }, }); await mastra.schedules.pause(schedule.id);
What changed:
mastra.heartbeatsis nowmastra.schedulesand also creates, lists, updates, pauses, resumes, runs, and deletes workflow schedules. Results are discriminated byagentIdvsworkflowId.- The Mastra config option
heartbeat: { ... }(lifecycle hooks) is nowschedules: { ... }, and hook types were renamed (HeartbeatHooks→ScheduleHooks,HeartbeatPrepareContext→SchedulePrepareContext, and so on). - New agent schedule ids use the
agent_prefix instead ofhb_. Existinghb_ids keep working. - The default signal tag an agent receives on a fire is now
<schedule>instead of<heartbeat>. - Types renamed:
Heartbeat→AgentSchedule,CreateHeartbeatInput→CreateAgentScheduleInput,HeartbeatScheduleTarget→AgentScheduleTarget(persistedtarget.typeis now'agent'instead of'heartbeat').
Existing schedules stored in your database keep working: rows persisted with the old
target.type: 'heartbeat'are read as'agent'automatically and keep firing. -
Added file-system routed studio config singleton. Place a studio.ts file in your mastra directory that default-exports a StudioConfig object, and it will be auto-discovered and registered when running mastra dev or mastra build. Code-registered studio config takes precedence if both are present. (#18889)
Patch Changes
- Fix workflow snapshot bloat on agent HITL tool-approval suspensions (#18647). Agent-run snapshots previously grew with thread length × number of ...
July 3, 2026
Highlights
Opt-in Storage Retention + storage.prune() (Core + Postgres/libSQL/MongoDB)
Mastra now supports per-domain/per-table retention policies (retention: { ...maxAge }) and a safe, batched, resumable storage.prune() to delete aged data across major “growth” tables (memory, observability spans, workflow snapshots, background tasks, experiments, schedules, etc.). Postgres/libSQL/MongoDB adapters add first-class pruning implementations (including partition/chunk drops for v-next observability in Postgres).
Multi-tenant Isolation for Datasets, Experiments, Scores, and Scorers
End-to-end optional organizationId/projectId scoping was added across dataset and experiment reads/deletes (including storage-layer predicates to prevent cross-tenant access), plus tenancy metadata for persisted scores and stored scorer definitions with scoped list APIs. Server routes and client-js methods also accept tenancy parameters so HTTP and SDK usage can enforce the same isolation.
Persisted Trace Scoring APIs (scoreTrace, scoreTraceBatch) + Score Provenance
New scoreTrace() and scoreTraceBatch() let you score already-stored traces without re-running agents, using an existing scorer instance and bounded concurrency for batches. Persisted scores now optionally include batchId, datasetId, and datasetItemId so baseline scoring passes can be grouped and joined back to dataset items across supported stores.
Agent Authoring & Runtime Improvements (Nested Subagents, Durable Parity, Model Router Safety)
File-based agents can now nest subagents/ up to three levels deep, enabling deeper delegation hierarchies without custom wiring. Durable agents gained significant behavior/streaming parity fixes (signals draining/echo, stop semantics, processor/tool lifecycle hooks, header sanitization, replay ordering), and the model router now strips unsupported sampling params (e.g., temperature) to prevent 400s on models that don’t accept them.
New Workspace/Sandbox Options and Providers (Apple Container, Mesa, Railway, E2B)
New @mastra/apple-container adds an Apple container CLI workspace sandbox provider, and @mastra/mesa adds a Mesa filesystem provider for workspaces. RailwaySandbox gains checkpoint-backed restart/reconnect, and E2BSandbox adds a forwarded network option for finer-grained network controls and per-host request transforms.
Breaking Changes
@mastra/playground-uiremoved root named exports; import from explicit subpaths (e.g.@mastra/playground-ui/components/Button).@mastra/playground-uiremovedSearchbar; useInputGroupcomposition instead.
Changelog
@mastra/core@1.49.0
Minor Changes
-
Added opt-in storage retention. Declare per-table
maxAgepolicies in theretentionconfig, then callstorage.prune()to delete rows older than their age. Anything you don't configure is kept forever, so there is no change until you opt in. (#18733)Retention covers growth tables across ten domains —
memory(threads, messages, resources),threadState,observability(spans),scores,workflows(run snapshots),backgroundTasks,experiments,notifications,harness(sessions), andschedules(fire history). Anchors are chosen somaxAgeis honest: creation time for append-only logs, last activity for workflow snapshots and thread state, and completion time for background tasks and experiments (in-flight work is never pruned). User-authored artifacts and config (agents, skills, workspaces, datasets, schedule definitions, and so on) are not prunable.prune()is safe on large tables: it deletes in bounded, batched, resumable, cancellable chunks and never locks the database for long. Call it from your own scheduler; when a result reportsdone: false, eligible rows remain and the next run continues.prune()only deletes rows — reclaiming disk to the OS is left to the underlying database and the operator.const storage = new MastraCompositeStore({ id: 'composite', retention: { memory: { messages: { maxAge: '30d' }, threads: { maxAge: '90d' } }, observability: { spans: { maxAge: '7d' } }, }, domains: { /* ... */ }, }); // Wire this to your own cron — Mastra never runs it for you. const results = await storage.prune();
-
Added maxDurationMs, maxWidth, and maxHeight options to BrowserRecordingOptions. These can now be set on the recording config object to provide defaults for every recording, instead of relying on agent instructions to pass them to the tool at start time. (#18814)
const browser = new AgentBrowser({ recording: { outputDir: './recordings', maxDurationMs: 60_000, maxWidth: 1280, maxHeight: 720, }, });
Per-recording overrides via the browser_record tool still take precedence.
-
File-based agents can now nest subagents up to three levels deep. A subagent directory can declare its own
subagents/, and each level is assembled and wired into its parent as a delegation tool. Levels deeper than the cap are ignored with a warning. (#18780)src/mastra/agents/ supervisor/ # depth 0 subagents/ researcher/ # depth 1 subagents/ summarizer/ # depth 2 -
Fixed a cross-tenant data-access issue on datasets by scoping
DatasetsManager.getandDatasetsManager.deleteto tenancy filters. (#18750)Previously
get({ id })anddelete({ id })looked up a dataset by its primary key alone. Any caller who knew a dataset id could read or delete it regardless of whichorganizationId/projectIdit belonged to. This is now closed at the storage layer via a scoped SQL predicate (option (a) — no fetch-then-assert).What changed
DatasetsManager.getandDatasetsManager.deleteaccept optionalorganizationIdandprojectId.- The tenancy is stashed on the returned
Datasethandle and forwarded to every downstream storage call (getDetails,update,addItem, item batch ops,startExperimentAsync). - The abstract storage contract (
getDatasetById,deleteDataset) gained an optionalfilters?: DatasetTenancyFiltersarg. - Item-mutation inputs (
AddDatasetItemInput,UpdateDatasetItemInput,BatchInsertItemsInput,BatchDeleteItemsInput) andUpdateDatasetInputaccept optionalfiltersfor the internal existence check.
Behavior
- Omitting tenancy preserves the existing behavior (no predicate added) — fully backwards compatible.
- On tenancy mismatch,
getthrows NOT_FOUND (returns null at the storage layer) anddeleteis a silent no-op — matching how a missing id already behaves, so existence does not leak through error timing or messages.
Example
// Before const ds = await mastra.datasets.get({ id }); await mastra.datasets.delete({ id }); // After — scope to a tenant const ds = await mastra.datasets.get({ id, organizationId, projectId }); await mastra.datasets.delete({ id, organizationId, projectId });
-
Added
scoreTrace()andscoreTraceBatch()to@mastra/core/evals/scoreTracesfor scoring stored traces without re-running the agent. (#18331)scoreTrace()can score either a stored trace reference or a preloadedTraceRecord, and it returns the persistedScoreRowDataafter the write.scoreTraceBatch()runs one scorer instance across multiple stored traces with bounded concurrency and returns per-target success and failure results.
Why
This gives baseline-style callers a small public API for persisted trace scoring when they already have a scorer instance, without widening the existing workflow-based
scoreTraces()API.Before
await scoreTraces({ mastra, scorerId: 'helpfulness', targets: [{ traceId, spanId }], });
After
import { scoreTrace, scoreTraceBatch } from '@mastra/core/evals/scoreTraces'; const savedScore = await scoreTrace({ storage, scorer, target: { trace: preloadedTrace, spanId }, batchId, datasetId, datasetItemId, }); const result = await scoreTraceBatch({ storage, scorer, targets, batchId, datasetId, });
-
Add optional
batchId,datasetId, anddatasetItemIdfields to persisted scores so saved baseline scores can be grouped as one scoring pass and joined back to the dataset items they came from. (#18331)scoreTrace()accepts top-levelbatchId,datasetId, anddatasetItemIdwhen persisting a score for a stored trace.ScoreRowDataand score save payloads now include nullablebatchId,datasetId, anddatasetItemId.- Built-in stores with explicit score schema or attribute mappings now persist these provenance fields on saved scores.
- D1, DSQL, MSSQL, and Upstash score stores now apply additive provenance migrations or deterministic score ordering for persisted score reads.
await scoreTrace({ storage, scorer, target: { traceId }, batchId: 'baseline-batch-1', datasetId, datasetItemId, });
-
Added multi-tenant scoping to stored scorer definitions. Stored scorers now persist optional
organizationIdandprojectIdon the definition record, andlist/listResolvedaccept matching filters to scope results by tenant. The Postgres adapter backfills the new columns and applies the scoped f...
July 1, 2026
Highlights
Heartbeats (scheduled agents on cron)
New mastra.heartbeats APIs let you schedule agents to run on a recurring cron, either delivering into an existing thread as a normal signal or running threadless; schedules persist across restarts and are available in @mastra/core, @mastra/server (/api/heartbeats), and @mastra/client-js.
File-based agents & subagents (FS convention + bundler auto-registration)
You can now define agents by dropping directories under src/mastra/agents/<name>/ (with config.ts/instructions.md, tools/, skills/, optional memory.ts and workspace/ seeding), and the deployer/build pipeline will bundle + register them automatically; supports one-level subagents/<childId>/ that become delegatable tools.
Human-in-the-loop reliability: storage-backed suspended run discovery
New agent.listSuspendedRuns() reads pending approvals/suspends from storage (not memory), enabling approval UIs to recover after refresh/restart and across multiple server instances; sendToolApproval() now falls back to this discovery when no in-memory active run is found.
Observational Memory upgrades: Extractor API + OM-managed working memory
@mastra/memory adds a public Observational Memory Extractor API (inline XML extraction + structured follow-ups with built-in extractors like task/suggested response/thread title) and an option for the Observer to automatically manage working memory via observationalMemory.observation.manageWorkingMemory.
Durable/Inngest execution parity & stronger auth context propagation
InngestAgent is brought to parity with DurableAgent (execution options, abort handling, idle-aware resume, generate()/resumeGenerate()), Inngest observe() now replays buffered events for late subscribers, and Inngest workflows now support the FGA actor signal end-to-end across durable/nested boundaries.
Breaking Changes
@mastra/vercel: sandbox exports renamed—VercelSandboxnow means MicroVM-backed Vercel Sandbox; the serverless implementation is nowVercelServerlessSandbox(andVercelMicroVMSandboxis replaced byVercelSandbox).
Changelog
@mastra/core@1.48.0
Minor Changes
-
Renamed the AgentController interval API.
heartbeatHandlersis nowintervalHandlers, theHeartbeatHandlertype is nowIntervalHandler, and theremoveHeartbeat()/stopHeartbeats()methods are nowremoveInterval()/stopIntervals(). This better reflects that these are fixed-interval background tasks, not liveness pings, and is distinct from the unrelatedmastra.heartbeatsscheduled-agent feature. (#18665)Before
const { controller } = await createMastraCode({ heartbeatHandlers: [{ id: 'sync', intervalMs: 60_000, handler: async () => {} }], }); await controller.removeHeartbeat({ id: 'sync' }); await controller.stopHeartbeats();
After
const { controller } = await createMastraCode({ intervalHandlers: [{ id: 'sync', intervalMs: 60_000, handler: async () => {} }], }); await controller.removeInterval({ id: 'sync' }); await controller.stopIntervals();
-
Added optional
maxStepstoScorerJudgeConfigandGoalConfig, letting callers control the internal judge agent's agentic-loop iteration limit instead of relying on the implicit default of 5. Also fixed judge agent not receiving Mastra registration, which caused the observer's API key resolution to fail silently. (#18544) -
Added optional
maxStepstoScorerJudgeConfig, letting callers control the internal judge agent's agentic-loop iteration limit instead of relying on the implicit default of 5. (#18544) -
Added
createCodingAgentfactory and a reusablebuildBasePromptso other projects can build a coding agent on top of the same defaults MastraCode uses. (#18695)The factory wires sensible, portable defaults that you can override per field:
- Workspace — a local filesystem + sandbox rooted at
process.cwd()(setbasePath, pass your ownworkspace, or passworkspace: undefinedto opt out entirely). - Task signals —
TaskSignalProviderso a task list persists across turns. - Error handling — retries on
ECONNRESETand bad-request errors, plus prefill and provider-history compatibility processors. - Goal judging — the default goal judge prompt.
buildBasePromptis parameterized withproductName,coAuthorName(both default to "Mastra Code"), andcoAuthorEmail(defaults to "noreply@mastra.ai"), so you can brand the system prompt and commit trailer without forking it.import { createCodingAgent } from '@mastra/core/coding-agent'; const agent = createCodingAgent({ id: 'my-coding-agent', name: 'My Coding Agent', model: 'openai/gpt-5', instructions: 'You help with my project.', tools: {}, basePath: '/path/to/repo', });
- Workspace — a local filesystem + sandbox rooted at
-
Added heartbeats: schedule an agent to run on a recurring cron, either inside an existing conversation thread or on its own. (#18184)
A heartbeat fires a prompt to an agent on a schedule. When it has a thread, the run is delivered into that thread as a normal agent signal, so anything watching the thread sees it like any other message; without a thread, the agent just runs in isolation. Each heartbeat has its own id and an optional
name, so one agent or thread can have several heartbeats with different schedules and prompts. The id is generated for you, or you can pass your ownidtocreatefor a stable handle (it's normalized tohb_<slug>). Heartbeats are persisted, so they keep firing across process restarts with no extra setup.const hb = await mastra.heartbeats.create({ agentId: 'chef', name: 'morning-checkin', threadId, resourceId, cron: '*/5 * * * *', prompt: 'Check in on the user', ifActive: { behavior: 'discard' }, // skip if the user is mid-conversation ifIdle: { behavior: 'wake' }, // wake the agent if the thread is idle }); // Threadless: run the agent on a cron with no conversation. await mastra.heartbeats.create({ agentId: 'chef', cron: '0 * * * *', prompt: 'Run the hourly summary', }); await mastra.heartbeats.list({ agentId: 'chef' }); await mastra.heartbeats.get(hb.id); await mastra.heartbeats.update(hb.id, { prompt: 'check in gently' }); await mastra.heartbeats.pause(hb.id); await mastra.heartbeats.resume(hb.id); await mastra.heartbeats.run(hb.id); // fire once now await mastra.heartbeats.delete(hb.id);
The same CRUD is available over HTTP through
@mastra/server(under/api/heartbeats) and as top-level methods on the@mastra/client-jsclient (client.createHeartbeat,client.getHeartbeat,client.listHeartbeats, etc.).Lifecycle hooks
React to heartbeat runs via
heartbeaton theMastraconstructor. It's a single hook bundle that runs for every agent's heartbeats; each hook receives the firingagentIdso you can branch on it.prepareresolves fire-time parameters (for example, creating a fresh thread per fire), andonFinish/onError/onAbortmirroragent.stream.new Mastra({ // ... heartbeat: { // Return overrides, `null` to skip this fire, or `undefined` to use defaults. prepare: async ({ agentId, heartbeat }) => { if (agentId === 'chef' && heartbeat.name === 'daily-digest') { return { threadId: await createDailyThread(), resourceId: 'slack:U095PUH0FKL' }; } }, onFinish: ({ agentId, outcome, result, heartbeat }) => { metrics.record({ agentId, heartbeat: heartbeat.name, outcome }); }, onError: ({ agentId, error, phase, heartbeat }) => { alerts.send(`heartbeat ${agentId}/${heartbeat.name} failed in ${phase}: ${error.message}`); }, }, });
Signal shaping
A heartbeat fire surfaces to the agent as a signal. By default it uses the
notificationtype and renders as<heartbeat>…</heartbeat>; overridesignalTypeandtagNameto change either.ifActiveandifIdlemirror theagent.sendSignaloptions shape ({ behavior, attributes }, plusstreamOptionsonifIdle) and stay JSON-serializable so they persist with the schedule.ifIdle.streamOptionscurrently acceptsrequestContext, which is rehydrated onto the woken run. Top-levelattributesare rendered on the signal tag, and top-levelproviderOptionsare merged into the signal payload on every fire.await mastra.heartbeats.create({ agentId: 'chef', threadId, resourceId, cron: '*/5 * * * *', prompt: 'Check in on the user', tagName: 'check-in', // renders as <check-in>…</check-in> attributes: { source: 'cron' }, providerOptions: { openai: { store: false } }, ifIdle: { behavior: 'wake', streamOptions: { requestContext: { locale: 'en-US' } }, }, });
-
add OM-managed working memory (#18654)
Adds
observationalMemory.observation.manageWorkingMemoryso the Observer can update working memory automatically instead of requiring the main agent to call the working memory tool.new Memory({ options: { workingMemory: { enabled: true }, observationalMemory: { enabled: true, observation: { manageWorkingMemory: true }, }, }, });
This option adds
WorkingMemoryExtractor, defaults `workingMemory.ag...
June 26, 2026
Highlights
AI SDK v7 (LanguageModelV4) support + upgraded model router
Agents can now accept AI SDK v7 provider models directly (LanguageModelV4), while still supporting v4/v5/v6; Mastra auto-detects model spec versions so you can mix providers across agents. The model router was upgraded to resolve V4 models natively, and modelSettings adds a new reasoning effort control for V4-capable providers.
DurableAgent API parity (stream/resume/generate) + better cancellation & tracing
DurableAgent now mirrors Agent across stream/resume/generate, including full AgentExecutionOptions support (model settings, stop conditions, transforms, per-call instructions/system, tracing options, actor, etc.). New abort surfaces (abortSignal, result.abort(), onAbort) and forwarded abort to delegated subagents improve control, and durable runs now emit richer AGENT_RUN span metadata for observability.
Gateway-first model discovery & auth via new GatewayManager (Harness hooks removed)
Harness now sources model catalog, auth status, mode agents, observational-memory models, and subagents entirely from registered gateways (no fallback to resolveModel, customModelCatalogProvider, or modelAuthChecker, which were removed). Shared gateway operations are centralized in a new GatewayManager exported from @mastra/core, and gateways can implement handlesModel to claim bare provider model IDs for auth resolution.
AgentController becomes the canonical “Harness” surface across core/server/client
Harness is renamed to AgentController with a new canonical import path @mastra/core/agent-controller, and server routes are now exclusively under /agent-controller/.... Client usage follows suit (MastraClient.getAgentController, listAgentControllers, AgentControllerSession), with enriched session state (OM progress, token usage, behavior settings) and thread scoping via session tags (e.g. per git worktree).
Evals quality gates + micro-scorer checks namespace
runEvals now supports pass/fail gates, per-scorer { scorer, threshold } (including { min, max }), and returns verdict, gateResults, and thresholdResults. @mastra/evals adds checks micro-scorers (text assertions and tool-use assertions) to make common eval criteria easy to compose.
Breaking Changes
- Harness→AgentController migration:
@mastra/serverremoved legacy/harness/...routes andharness:*permissions; clients must use/agent-controller/...andagent-controller:*permissions. @mastra/client-jsremoved deprecatedgetHarness/listHarnessesandHarness/HarnessSessionclasses (breaking for recent client users).- Harness config removed
resolveModel,customModelCatalogProvider, andmodelAuthChecker; register model gateways viagatewaysinstead. - Session identity is now required:
idandownerIdare mandatory onharness.createSession()andSession/SessionIdentityconstructors (no defaults). - Workspace/browser ownership moved to per-session:
Sessionconstructor now requiresworkspace: Workspace;HarnessConfig.workspaceno longer acceptsWorkspaceConfigshorthand (must provideWorkspaceinstance or factory).
Changelog
@mastra/core@1.47.0
Minor Changes
-
Added support for AI SDK v7 models (
LanguageModelV4). You can now pass any AI SDK v7 provider model directly to an agent, alongside the existing AI SDK v4, v5, and v6 support. (#18477)import { Agent } from '@mastra/core/agent'; import { openai } from '@ai-sdk/openai'; // AI SDK v7 const agent = new Agent({ name: 'my-agent', instructions: 'You are a helpful assistant.', model: openai('gpt-5'), });
Mastra detects the model's specification version automatically, so mixing models from different AI SDK versions across your agents continues to work without any extra configuration.
-
Finished closing the gap between
DurableAgentandAgent. After this change the durable agent surface mirrors the in-process agent's stream, resume, and generate APIs. (#18508)What's new
abortSignalonstream()andresume(), plusresult.abort()onstream(),resume(), andobserve().result.abort()onstreamUntilIdle()fans out to every inner run.untilIdleonresume()(it already existed onstream()), using the sharedrunWithIdleWrapperso both paths drive the same idle loop.DurableAgent.generate()andDurableAgent.resumeGenerate()wrapstream()/resume()and resolve aFullOutputeven when the run suspends mid-flight.delegationcallbacks (onDelegationStart,onDelegationComplete,messageFilter) are forwarded toconvertToolsat prepare time and baked into sub-agent tool wrappers.- Per-call
clientToolsandtoolsetssurvive in-process resume via the run registry (cross-process resume still falls back to the agent's static tools). - Scorers configured on the wrapped agent or passed per call now actually execute under durable runs and emit
ON_SCORER_RUNpayloads matching the non-durable shape. AGENT_RUNspans now carryconversationId,instructions,resolvedVersionId,entityVersionId, and the agent'stracingPolicy. Resume spans use'agent run: <id> (resumed)'and includeresumedFromSpanId.
Example
import { createDurableAgent } from '@mastra/core/agent/durable'; const durable = createDurableAgent({ agent: myAgent }); // 1. generate() — drains a durable run to a single FullOutput const out = await durable.generate('Plan a week in Lisbon', { abortSignal: controller.signal, modelSettings: { temperature: 0.2 }, }); // 2. stream() with result.abort() — cancel mid-run const result = await durable.stream('Long research task'); setTimeout(() => result.abort(), 5_000); for await (const chunk of result.output.fullStream) { process.stdout.write(chunk.payload?.text ?? ''); } // 3. Suspend → resumeGenerate() round-trip (e.g. tool approval) const first = await durable.generate('Run the dangerous tool', { requireToolApproval: true, }); if (first.finishReason === 'suspended') { const final = await durable.resumeGenerate(first.runId!, { approved: true }); console.log(final.text); } // 4. resume({ untilIdle }) — drive a resume through to a quiescent state await durable.resume(runId, resumeData, { untilIdle: true });
-
Brought
DurableAgent.stream()to parity withAgent.stream()for the fullAgentExecutionOptionssurface, so the same call works on both. (#18461)What's new
DurableAgent.stream()now honors these options that were previously dropped or silently coerced:- Full
modelSettings(was: onlytemperature) —maxOutputTokens,topP,topK,presencePenalty,frequencyPenalty,stopSequences,seed,headers stopWhen,prepareStep,isTaskComplete,transform- Per-call
instructionsandsystem disableBackgroundTasks,tracingOptions,actor- Function-form
requireToolApproval(toolName, args, …)(was: coerced to "approve all" — now evaluated per tool call) - New callbacks:
onAbortandonIterationComplete(joining the existingonChunk/onStepFinish/onFinish/onError/onSuspendedbridge)
Example
The same options that work on
Agent.stream()now work onDurableAgent.stream():const durable = createDurableAgent({ agent, pubsub }); await durable.stream('Plan the trip', { modelSettings: { temperature: 0.2, maxOutputTokens: 500, topP: 0.9 }, stopWhen: ({ steps }) => steps.length >= 3, prepareStep: ({ stepNumber }) => ({ activeTools: stepNumber === 0 ? ['search'] : ['book'], }), requireToolApproval: ({ toolName }) => toolName === 'book', onIterationComplete: ({ iteration }) => console.log('iter', iteration), });
- Full
-
Resolve all Harness models through gateways. The Harness now builds its available-models catalog, model auth status, mode agents, Observational Memory models, and subagents from the gateways you register, instead of the separate
resolveModel,customModelCatalogProvider, andmodelAuthCheckerconfig hooks. Removed those three options fromHarnessConfig(and theModelAuthCheckertype) — register a gateway viagatewaysinstead. (#18382)listAvailableModels()andgetCurrentModelAuthStatus()are now sourced entirely from gateways: model discovery comes from each gateway'sfetchProviders()(a network call for gateways like models.dev and Netlify), and auth status is resolved through the same gateway chain the model router uses at run time (resolveAuth(), falling back togetApiKey()). There is no static provider-registry fallback — everything the model picker shows comes from a gateway.The gateway-chain operations shared by
ModelRouterLanguageModeland the Harness (gateway merging, model→gateway selection, auth resolution, provider/model listing) are now centralized in a newGatewayManagerclass exported from@mastra/core. Both consumers delegate to it, eliminating duplicated logic.defaultGatewaysis still re-exported from the same paths for backward compatibility. -
Added
reasoningoption tomodelSettingsfor controlling model reasoning effort level. This option accepts standardized levels ('provider-default' | 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh') and is effective with LanguageModelV4 (AI SDK v7) providers that support reasoning. When used with older model providers (V2/V3), the option is a no-op....
June 24, 2026
Highlights
Multi-session Harness architecture (Session-first APIs)
Harness is now a pure factory/shared-resource owner: create isolated sessions via harness.createSession() (get-or-create by resourceId), with run control, state, event bus, model/mode switching, permissions, OM, subagent settings, and thread lifecycle all moved onto Session domains (e.g. session.sendMessage(), session.thread.*, session.model.switch(), session.subscribe()), enabling safe concurrent multi-user/multi-thread hosting.
Drive Harness sessions over HTTP (and from the JS client)
Registered harnesses on Mastra can now be operated via new harness-scoped server routes (send/steer/abort/approve tool calls, manage threads, read state, and subscribe via SSE), and @mastra/client-js adds a first-class harness resource (client.getHarness(id).session(resourceId)) to control sessions remotely.
Cross-process signal delivery with distributed leasing + new accepted contract
Signal/message APIs now return an accepted promise resolving to a discriminated routing decision (wake/deliver/persist/discard) with authoritative runId when applicable, and introduce a LeaseProvider abstraction (in-memory and Redis Streams implementations) to ensure only one process owns/wakes a thread run in multi-instance/serverless deployments.
Deterministic agent experiments with item-level tool mocks + multi-tenant datasets/experiments
Dataset items can now declare ordered toolMocks (with strict/ignore arg matching) and experiments return a toolMockReport, enabling deterministic replay without running real tools; datasets and experiments also gain optional (organizationId, projectId) tenancy scoping plus dataset-level candidate identity (candidateKey, candidateId) and a new dataset item source type 'candidate-screener'.
New integrations & streaming ergonomics
New packages add major deployment/integration options: @mastra/next (Next.js App Router adapter), @mastra/tanstack-start (TanStack Start adapter), and @mastra/archil (Archil filesystem provider for workspaces); streaming gains a unified untilIdle option on DurableAgent.stream() and InngestAgent.stream() to keep streams open through background task continuations.
Breaking Changes
- Harness no longer exposes a singleton
harness.session; callers must useawait harness.createSession()and operate on the returnedSession. - Run control moved from
Harness.*toSession(e.g.sendMessage,sendSignal,abort,respondToToolSuspension, etc. removed from Harness). - Event subscription moved from
Harness.subscribe()tosession.subscribe()(session-scoped event isolation). - Thread lifecycle APIs moved to
session.thread.*; Harness no longer exposescreateThread/switchThread/cloneThread/renameThread/detachFromCurrentThreadorharness.memory. - Model/mode switching moved to
session.model.switch()andsession.mode.switch(); OM accessors moved tosession.om.*; permissions tosession.permissions.*; subagent model accessors tosession.subagents.model.*. - Deprecated
Harness.getState()/Harness.setState()compatibility wrappers removed (usesession.state.get()/session.state.set()).
Changelog
@mastra/core@1.46.0
Minor Changes
-
Removed the deprecated
Harness.getState()andHarness.setState()compatibility wrappers, along with the unused privateupdateState. Harness state has lived on the session for a while; these were thin proxies marked@deprecated. (#18200)Before
const state = harness.getState(); await harness.setState({ count: 1 });
After
const state = harness.session.state.get(); await harness.session.state.set({ count: 1 });
This does not affect the tool-facing harness context, which continues to expose
state/getState/setState/updateStatealongsidesession.state.mastracodeis updated to set browser settings viasession.state.set(). -
Moved the observational-memory model accessors off the Harness onto
session.om. Reading and switching the observer/reflector models and reading observation/reflection thresholds now live on the session, next to the state they read and write. (#18200)Before
const observer = harness.getObserverModelId(); await harness.switchObserverModel({ modelId: 'openai/gpt-4o' });
After
const observer = harness.session.om.observer.modelId(); await harness.session.om.observer.switchModel({ modelId: 'openai/gpt-4o' });
The accessors are grouped by role under
session.om.observerandsession.om.reflector, each exposingmodelId(),threshold(),resolvedModel(), andswitchModel({ modelId }).Removed
Harness.getObserverModelId,getReflectorModelId,getObservationThreshold,getReflectionThreshold,getResolvedObserverModel,getResolvedReflectorModel,switchObserverModel, andswitchReflectorModel.mastracodeis updated to consume the new API: the/omcommand and status line now read and switch observer/reflector models viasession.om. -
Moved the run-control surface off the Harness onto the
Session. Sending messages and signals, steering, following up, aborting, responding to tool suspensions, and saving system reminders now live on the session that owns the run state they drive, instead of being delegated through the Harness. This is the final step of the single-session extraction series and a prerequisite for the upcoming multi-session (createSession) work: every per-session operation now lives onSession, while the Harness retains only genuinely shared machinery (agent, config builders, storage/lock gateway), which it injects into each session via theSessionMachineryprovider. (#18213)Before
await harness.sendMessage({ content: 'hello' }); harness.sendSignal({ content: 'steer the run' }); harness.abort(); await harness.respondToToolSuspension({ toolCallId, approved: true });
After
const session = await harness.createSession(); await session.sendMessage({ content: 'hello' }); session.sendSignal({ content: 'steer the run' }); session.abort(); await session.respondToToolSuspension({ toolCallId, approved: true });
Removed
Harness.sendMessage,Harness.sendSignal,Harness.sendNotificationSignal,Harness.steer,Harness.followUp,Harness.abort,Harness.respondToToolSuspension,Harness.saveSystemReminderMessage, andHarness.waitForCurrentThreadStreamIdle. TheSessionreaches Harness-owned machinery through the injectedSessionMachineryprovider, so the heavy run loop is still constructed and owned by the Harness while being parameterized by the session it runs on.mastracodeis updated to consume the new API: the TUI run loop, slash-command dispatch, goal lifecycle, prompt handlers, and headless entry points all drive run-control through the session returned byharness.createSession(). -
The Harness event bus now lives on the Session. Each
Sessionowns its own listeners and emit pipeline (session.subscribe()/ internalsession.emit()), so events emitted on one session are delivered only to that session's subscribers — never to another session's. This is the isolation foundation for serving a single Harness to multiple concurrent sessions (e.g. one Harness backing many channel threads). (#18213)Breaking (Harness is under active development):
Harness.subscribe()is removed. Subscribe on the session instead:- harness.subscribe(listener) + harness.session.subscribe(listener)
Session subsystems (mode/model/om/permissions/subagents/state) no longer receive an injected
emitcallback — they emit directly to their session's bus.mastracodeis updated to subscribe viaharness.session.subscribe(). -
Add multi-tenant filtering and candidate identity to the datasets domain. (#18314)
DatasetRecord,DatasetItem,DatasetItemRow,CreateDatasetInput, and thefiltersonListDatasetsInput/ListDatasetItemsInputnow expose optionalorganizationIdandprojectId, matching the per-row tenancy contract already used by the observability domain. Dataset items inherit tenancy from their parent dataset automatically — they cannot be set per-call.DatasetRecordandCreateDatasetInputalso gain two new optional identity fields,candidateKeyandcandidateId, for use cases that need a stable per-incident identity at the dataset level (such as auto-materialized candidate datasets).The
DatasetItemSource['type']union now includes'candidate-screener'so externally-materialized items can be distinguished from user-uploaded ones.DATASETS_SCHEMAandDATASET_ITEMS_SCHEMAgain matching nullable columns, andDatasetsInMemorypersists and filters on them.DatasetsManager.create()accepts the new optional fields, andDatasetsManager.list()accepts an optionalfiltersarg that forwards to the storage layer.Before
const dataset = await storage.createDataset({ name: 'goldens/checkout' }); const items = await storage.listDatasets({ pagination: { page: 0, perPage: 20 } });
After
const dataset = await storage.createDataset({ name: 'candidates/missing-tool-call/incident-123', organizationId: 'org_abc', projectId: 'project_xyz', candidateKey: 'missing-tool-call',
...
June 19, 2026
Highlights
Faster long-thread resume via improved state signal restoration
@mastra/core now restores state signals without scanning every message, making it significantly cheaper and faster to resume very long threads.
More reliable signal turn handling in agents
Fixes to agent signal drains ensure pending signals are recorded through the canonical signal transcript path and response message IDs rotate consistently—preventing follow-up signal turns from attaching to the wrong assistant response.
Clearer system guidance around automatic state signals
New system guidance clarifies that browser and task-list state signals are automatic context updates rather than user instructions, reducing prompt confusion and accidental instruction-following.
Redesigned Studio panel & code-surface UI primitives in @mastra/playground-ui
Adds reusable UI support for revamped Studio panels, including true resizable panels with smooth open/close, mobile drawer behavior via PanelDrawer, a new useIsMobile hook, and CodeBlock.actions for header controls; plus new popover collision-avoidance alignment controls.
WorkOS OAuth login fix (PKCE verifier cookie missing)
@mastra/auth-workos fixes an OAuth/SSO login failure affecting both single-auth and dual-auth setups, restoring reliable WorkOS sign-in flows.
Breaking Changes
- None called out in this changelog.
Changelog
@mastra/core@1.45.0
Minor Changes
- Random bump (#18178)
Patch Changes
-
Improved state signal restoration so long threads can resume without scanning every message. (#18182)
-
Fix agent signal drains so pending signals are recorded through the canonical signal transcript path and consistently rotate the response message id. This prevents follow-up signal turns from being attached to the previous assistant response and helps the agent see the latest completed step before continuing. (#18105)
-
Add system guidance explaining that browser and task-list state signals are automatic context updates, not user instructions. (#18163)
@mastra/acp@0.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/agent-browser@0.4.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/agent-builder@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/agentcore@0.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/agentfs@0.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/ai-sdk@1.5.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/arize@1.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/arthur@0.4.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/astra@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-auth0@1.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-better-auth@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-clerk@1.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-cloud@1.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-firebase@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-neon@0.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-okta@0.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-studio@1.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-supabase@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/auth-workos@1.6.0
Minor Changes
- Random bump (#18178)
Patch Changes
- Fix WorkOS OAuth login failing with "PKCE verifier cookie missing" error. SSO login now works correctly for both single auth and dual auth configurations. (#18035)
@mastra/azure@0.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/blaxel@0.5.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/braintrust@1.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/brightdata@0.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/browser-firecrawl@0.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/browser-viewer@0.2.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/chroma@1.1.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/claude@0.3.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/clickhouse@1.11.0
Minor Changes
- Random bump (#18178)
Patch Changes
@mastra/client-js@1.26.0
Minor Changes
- Random bump ([#18178](https://github.com/mastra-ai/mastra/...
June 12, 2026
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.re...
- Removed
June 4, 2026
Highlights
Per-request Workspace sandboxes (multi-tenant + isolation)
@mastra/core Workspace sandbox can now be a resolver function, enabling per-request sandbox routing for multi-tenant deployments with isolated working directories/permissions; includes prompt-safe “stable placeholder” instructions by default with an opt-in instructions.dynamicSandbox mode.
Sandbox continuity for background processes via sandboxCacheKey
When using dynamic sandboxes, sandboxCacheKey lets background process tools (execute_command with background: true, get_process_output, kill_process) reliably attach to the same sandbox across follow-up requests, with cache management via workspace.clearSandboxCache().
Unified “stream until idle” behavior via untilIdle option (core + server + client SDKs)
stream()/resumeStream() now support untilIdle (boolean or { maxIdleMs }) to keep streams open across background-task continuations; the same untilIdle request field is supported on server endpoints and client SDKs.
Deprecation: dedicated *UntilIdle methods/endpoints
streamUntilIdle() and resumeStreamUntilIdle() (and the dedicated /stream-until-idle and /resume-stream-until-idle endpoints) are deprecated in favor of stream(..., { untilIdle: true }) / resumeStream(..., { untilIdle: true }).
Breaking Changes
- None noted in this changelog.
Changelog
@mastra/core@1.41.0
Minor Changes
-
Workspace
sandboxnow accepts a resolver function for per-request sandboxes. (#16048)Before:
sandbox: WorkspaceSandbox(static, same sandbox for every request)
After:sandbox: WorkspaceSandbox | (({ requestContext }) => WorkspaceSandbox)(static or per-request)This enables per-request sandbox routing from a single Workspace — useful for multi-tenant deployments where each user/role needs an isolated working directory or different execution permissions.
const workspace = new Workspace({ sandbox: ({ requestContext }) => { const userId = requestContext.get('user-id') as string; return new LocalSandbox({ workingDirectory: `/workspaces/${userId}` }); }, });
When using a resolver, the caller owns the returned sandbox's lifecycle — the Workspace will not call
start()ordestroy()on it.mountsthrows anINVALID_CONFIGerror with a resolver, andlsp: trueis disabled with a warning because both require a concrete sandbox instance up front.Stable prompts by default
Building workspace instructions no longer calls a sandbox resolver. Resolver-backed sandboxes contribute stable placeholder text to the agent's system message, so constructing the prompt never provisions a caller-owned sandbox and the prompt stays cache-friendly. Opt into concrete per-request instructions with
instructions.dynamicSandbox:const workspace = new Workspace({ sandbox: ({ requestContext }) => resolveSandbox(requestContext), instructions: { dynamicSandbox: 'resolve' }, // or a ({ requestContext }) => string function });
Background process continuity
Set
sandboxCacheKeyto keepexecute_command({ background: true }),get_process_output, andkill_processon the same sandbox across follow-up requests — continuity is keyed by a stable id rather than theRequestContextinstance:const workspace = new Workspace({ sandbox: ({ requestContext }) => resolveSandbox(requestContext), sandboxCacheKey: ({ requestContext }) => requestContext.get('thread-id') as string, });
Failed sandbox resolver calls are removed from the cache so later calls can retry. Use
workspace.clearSandboxCache(cacheKey)to drop a keyed sandbox reference when your own lifecycle code has destroyed or replaced that sandbox.When background process tools cannot find a PID on a dynamic sandbox without
sandboxCacheKey, the tool output now points tosandboxCacheKeyso callers can fix continuity across follow-up requests.
Patch Changes
-
Added
untilIdleoption tostream()andresumeStream()methods. PassuntilIdle: true(oruntilIdle: { maxIdleMs: 60_000 }) to keep the stream open across background-task continuations — same behavior as the now-deprecatedstreamUntilIdle()method. (#17536)Example:
const result = await agent.stream('Research solana for me', { untilIdle: true, memory: { thread: 't1', resource: 'u1' }, });
Deprecated
streamUntilIdle()andresumeStreamUntilIdle()— they still work but now delegate internally tostream({ untilIdle: true }).
@mastra/acp@0.2.1
Patch Changes
- Fixed ACP tools to keep their default session alive across executions. (#17516)
@mastra/client-js@1.23.2
Patch Changes
-
The
/agents/:agentId/streamand/agents/:agentId/resume-streamendpoints now accept anuntilIdlefield in the request body. When set, the stream stays open across background-task continuations (same behavior as the/stream-until-idleendpoint). The dedicated/stream-until-idleand/resume-stream-until-idleendpoints remain available but are deprecated. (#17536)Server example:
// POST /api/agents/:agentId/stream fetch(`/api/agents/${agentId}/stream`, { method: 'POST', body: JSON.stringify({ messages: [{ role: 'user', content: 'Research solana for me' }], untilIdle: true, // or { maxIdleMs: 60000 } }), });
Client SDK:
streamUntilIdle()andresumeStreamUntilIdle()are deprecated — usestream(messages, { untilIdle: true })instead.
@mastra/playground-ui@32.0.2
Patch Changes
-
Fixed dropdown, combobox, select, and popover popups silently failing to render when opened outside a side panel (most visibly the model and provider pickers in the chat composer). The shared portal-container resolver now always falls back to the document body instead of leaking an unrenderable value. (#17560)
-
Fixed combobox popups so model picker dropdowns open correctly outside side dialogs. (#17556)
@mastra/react@0.5.2
Patch Changes
-
The
/agents/:agentId/streamand/agents/:agentId/resume-streamendpoints now accept anuntilIdlefield in the request body. When set, the stream stays open across background-task continuations (same behavior as the/stream-until-idleendpoint). The dedicated/stream-until-idleand/resume-stream-until-idleendpoints remain available but are deprecated. (#17536)Server example:
// POST /api/agents/:agentId/stream fetch(`/api/agents/${agentId}/stream`, { method: 'POST', body: JSON.stringify({ messages: [{ role: 'user', content: 'Research solana for me' }], untilIdle: true, // or { maxIdleMs: 60000 } }), });
Client SDK:
streamUntilIdle()andresumeStreamUntilIdle()are deprecated — usestream(messages, { untilIdle: true })instead.
@mastra/server@1.41.0
Minor Changes
-
The
/agents/:agentId/streamand/agents/:agentId/resume-streamendpoints now accept anuntilIdlefield in the request body. When set, the stream stays open across background-task continuations (same behavior as the/stream-until-idleendpoint). The dedicated/stream-until-idleand/resume-stream-until-idleendpoints remain available but are deprecated. (#17536)Server example:
// POST /api/agents/:agentId/stream fetch(`/api/agents/${agentId}/stream`, { method: 'POST', body: JSON.stringify({ messages: [{ role: 'user', content: 'Research solana for me' }], untilIdle: true, // or { maxIdleMs: 60000 } }), });
Client SDK:
streamUntilIdle()andresumeStreamUntilIdle()are deprecated — usestream(messages, { untilIdle: true })instead.
Patch Changes
Other updated packages
The following packages were updated with dependency changes only:
June 3, 2026
Highlights
Gateway Embedding Routing via mastra/provider/model IDs
@mastra/core adds support for routing embeddings through the Gateway using mastra/provider/model identifiers (e.g., new ModelRouterEmbeddingModel('mastra/__GATEWAY_OPENAI_EMBEDDING_MODEL__')).
Breaking Changes
- None noted in this changelog (other packages were dependency-only updates).
Changelog
@mastra/core@1.40.0
Minor Changes
-
Added support for Gateway embedding routing with
mastra/provider/modelIDs: (#17469)const embedder = new ModelRouterEmbeddingModel('mastra/__GATEWAY_OPENAI_EMBEDDING_MODEL__');
Other updated packages
The following packages were updated with dependency changes only:
- @mastra/client-js@1.23.1
- @mastra/deployer@1.40.0
- @mastra/deployer-cloud@1.40.0
- @mastra/deployer-cloudflare@1.1.41
- @mastra/deployer-netlify@1.1.17
- @mastra/deployer-vercel@1.1.35
- @mastra/express@1.3.28
- @mastra/fastify@1.3.28
- @mastra/hono@1.4.23
- @mastra/koa@1.5.11
- @mastra/longmemeval@1.0.47
- @mastra/mcp-docs-server@1.1.44
- @mastra/nestjs@0.1.12
- @mastra/opencode@0.0.44
- @mastra/playground-ui@32.0.1
- @mastra/react@0.5.1
- @mastra/server@1.40.0
- @mastra/temporal@0.1.11