releases.shpreview
Mastra/GitHub Releases

GitHub Releases

Mon
Wed
Fri
JunJulAugSepOctNovDecJanFebMarAprMay
Less
More
Releases24Avg7/moVersions@mastra/core@1.6.0 to @mastra/core@1.37.0
@mastra/core@1.36.0

Highlights

Stored Entity HTTP APIs (Agents/Skills/Workspaces) + Builder/Registry Introspection

@mastra/server adds a full HTTP surface for stored entities (CRUD, versioning, activation/restore) including favorites metadata, plus builder introspection endpoints and an external skill-registry proxy that Studio and @mastra/client-js can consume.

Browser Automation & Screencast Session Probing (incl. Stored Agents)

New browser-provider/editor primitives wire browser automation into the editor, while deployers/adapters add a probeable GET /agents/:agentId/browser/session endpoint and client SDK methods (agent.browserSession() / agent.closeBrowser()) so UIs can safely open screencast WebSockets only when supported—now also working for stored agents via editor fallback lookup.

Delta Polling for Observability List APIs (Server/Client/Stores/UI)

Observability list endpoints now support mode: 'delta' with cursors across core + @mastra/server, with store support in DuckDB/ClickHouse (and in-memory alignment) and typed client support; Playground UI’s traces list updates live via delta polling instead of full-page refetches.

Harder Authorization Defaults: FGA Coverage + ANY-of Permissions + Safer Context Keys

FGA execution checks are now enforced consistently across agents/tools/memory/workflows (requiring authenticated requestContext when invoking protected APIs), and both FGA checks and route permissions accept permission arrays (ANY-of semantics); server auth middleware also writes namespaced request-context keys to avoid collisions.

Convex Production Upgrades: Native Vector Search + Durable Server Cache

@mastra/convex adds ConvexNativeVector backed by schema vector indexes/ctx.vectorSearch for production-grade retrieval, plus ConvexServerCache to persist durable stream replay and response-cache state in Convex.

Breaking Changes
  • AgentSignalContents narrowed to string | (TextPart | FilePart)[] (no longer accepts wrapped BaseMessageListInput shapes); update agent.sendSignal callers accordingly.
  • @mastra/playground-ui Button variants changed: cta/contrast/link removed in favor of default/primary/outline/ghost; ButtonWithTooltip removed (use Button with tooltip prop).
  • PopoverContent no longer forwards onOpenAutoFocus/onCloseAutoFocus (use initialFocus/finalFocus instead).

Changelog

@mastra/core@1.36.0
Minor Changes
  • Added activateAfterIdle: "auto" for Observational Memory early activation. (#16663)

    Mastra can now choose an idle activation timeout from the active model provider's prompt cache behavior. OpenAI also respects providerOptions.openai.promptCacheRetention when available.

    const memory = new Memory({
      options: {
        observationalMemory: {
          model: 'google/gemini-2.5-flash',
          activateAfterIdle: 'auto',
          activateOnProviderChange: true,
        },
      },
    });
  • Added support for permission arrays in FGA checks and route configuration. When an array is provided, the user needs any one of the listed permissions (logical OR). (#16605)

    Affected types

    • FGACheckParams.permission
    • FGARouteConfig.permission
    • FGARouteInfo.requiresPermission
    • FGADeniedError.permission
    • CheckFGAOptions.permission

    Single-permission usage continues to work unchanged.

    // Before — single permission only
    await fga.check({
      resource: { type: 'agent', id: 'abc' },
      permission: 'agents:read',
    });
    
    // After — single permission or array (ANY-of)
    await fga.check({
      resource: { type: 'agent', id: 'abc' },
      permission: ['agents:read', 'agents:execute'],
    });
  • Added consistent FGA execution checks across agents, tools, memory, and workflows to prevent unauthenticated executions when FGA is configured. Pass an authenticated user through requestContext when invoking protected APIs directly: (#16651)

    const requestContext = new RequestContext();
    requestContext.set('user', user);
    
    await agent.generate('Summarize this thread', {
      requestContext,
    });
  • Added the EditorFavorite* types and an optional favorites namespace on IMastraEditor so editor implementations can expose favoriting of stored agents and skills. (#16749)

    import type {
      IMastraEditor,
      IEditorFavoritesNamespace,
      EditorFavoriteTargetInput,
      EditorFavoriteToggleResult,
    } from '@mastra/core/editor';
    
    interface IMastraEditor {
      // ...existing members...
      favorites?: IEditorFavoritesNamespace;
    }

    The favorites field is optional — existing implementations of IMastraEditor continue to work unchanged. @mastra/editor ships a default EditorFavoritesNamespace that wires this up against the storage favorites domain.

    Also renamed AgentFeatures.stars to AgentFeatures.favorites in @mastra/core/agent-builder/ee so the feature flag aligns with the storage column (favoriteCount), HTTP routes (/favorite), and the editor favorites namespace. The field had no functional consumers, so this is a name-only change.

  • Enterprise edition now automatically captures PostHog telemetry for EE license checks and feature usage, including license validation status, RBAC access resolution, FGA authorization calls, and EE feature invocation metadata. Telemetry is enabled by default for EE customers and can be disabled with MASTRA_TELEMETRY_DISABLED=1; community users are unaffected. (#16660)

  • Added new editor configuration primitives for browser providers, agent builder integration, and stored-agent visibility. (#16778)

    New: BrowserProvider interface

    Implement a browser provider to expose browser automation tools to agents via the editor. Each provider declares an id, name, and config schema, then returns a MastraBrowser instance from createBrowser.

    import type { BrowserProvider } from '@mastra/core/editor';
    
    const myProvider: BrowserProvider = {
      id: 'my-browser',
      name: 'My Browser',
      description: 'Custom browser automation',
      configSchema: z.object({ apiKey: z.string() }),
      createBrowser: async config => {
        return createMyBrowser(config.apiKey);
      },
    };

    New: MastraEditorConfig.browsers and .builder

    Wire browser providers and agent-builder options into the editor:

    new MastraEditor({
      browsers: { 'my-browser': myProvider },
      builder: { features: { agent: { favorites: true } } },
    });

    New: visibility on updateAgentMeta

    Set an agent's visibility (private or public) through the editor namespace:

    await editor.agent.updateAgentMeta('agent-id', { visibility: 'public' });
  • publishSkill now returns the full skill file tree so consumers can persist the UI-facing tree alongside storage blobs without re-walking the source. (#16666)

    import { publishSkill } from '@mastra/core/workspace';
    
    const result = await publishSkill({ workspace, skillId, source });
    
    // New: nested tree of folders + files; binary content base64-encoded.
    for (const node of result.files) {
      console.log(node.type, node.path);
    }

    Also added two optional capability methods to IMastraEditor for server-side gating of builder-aware behavior:

    interface IMastraEditor {
      // ...existing members...
      hasEnabledBuilderConfig?(): boolean;
      resolveBuilder?(): Promise<IAgentBuilder | undefined>;
    }

    Both methods are optional — existing implementations of IMastraEditor continue to work unchanged. Servers that consume them treat undefined / missing implementation as "no builder configured."

  • Added route-specific CORS configuration so credentialed cross-origin access can be limited to selected custom routes and channel webhooks. (#16689)

    registerApiRoute('/customer-webhook', {
      method: 'POST',
      cors: {
        origin: ['https://customer-saas.example'],
        credentials: true,
      },
      handler: async c => c.json({ ok: true }),
    });
    new Agent({
      id: 'support-agent',
      name: 'Support Agent',
      instructions: '...',
      model,
      channels: {
        adapters: {
          web: {
            adapter: createWebAdapter(),
            cors: {
              origin: ['https://customer-saas.example'],
              credentials: true,
            },
          },
        },
      },
    });

    Use server.cors for one global CORS policy across the server:

    new Mastra({
      server: {
        cors: {
          origin: '*',
        },
      },
    });
  • Narrowed AgentSignalContents from BaseMessageListInput to string | (TextPart | FilePart)[]. (#16622)

    Fixed two signal-content bugs:

    • user-message signal attributes now reach the LLM
    • multimodal non-user-message signals no longer lose file parts

    Callers that previously passed wrapped message shapes to agent.sendSignal should now pass a bare string or a bare parts array.

    Before: { type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }

    After: { type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }

    Added an optional providerOptions field to agent.sendSignal that flows through to the resulting prompt turn (as providerOptions on the LLM message) and is persisted on the stored signal message (as content.providerMetadata).

  • publishSkillFromSource() (and collectSkillForPublish()) now return a files field containing the full skill source as a tree of StorageSkillFileNode entries with base64-encoded blob content — handy for storing a UI-facing copy of a skill alongside its content-addressable tree: (#16673)

    const { snapshot, tree, files } = await publishSkillFromSource({ source });
    // files: StorageSkillFileNode[] — name, mimeType, base64 content per node

    Existing callers that only destructure { snapshot, tree } are unaffected; the field is additive.

    Also adds parseSkillSnapshotFromFiles() for parsing skill snapshot frontmatter from a flat file list (used by the registry install flow):

    import { parseSkillSnapshotFromFiles, type SkillSnapshotFile } from '@mastra/core/workspace';
    
    const files: SkillSnapshotFile[] = [{ path: 'SKILL.md', content: '...' }, ...];
    const snapshot = parseSkillSnapshotFromFiles(files);
  • Added delta polling support for observability list APIs in core, DuckDB, and ClickHouse. (#16632)

Patch Changes
  • Update provider registry and model documentation with latest models and providers (452036a)

  • Fixed task_update to auto-demote previously in_progress tasks instead of returning an error when moving another task to in_progress. (#16843)

  • Fixed durable agents to honor activeTools when streaming. (#16646)

  • Fixed type error when a tool calls suspend(...) inside execute while also declaring an outputSchema. The execute return type now allows void in addition to the declared output shape, so the idiomatic return await suspend(...) pattern type-checks correctly. (#16799)

  • Fixed a startup bug in MastraCompositeStore.init() when using default or editor. (#16786)

    Before this fix, the composite initialized inner domains directly and could skip parent store initialization. That could skip adapter setup steps and cause missing-table errors during startup (most visibly with LibSQLStore on a local file).

    Now, MastraCompositeStore.init() runs parent default and editor initialization first, then initializes only domains not already covered by those parents. This preserves adapter-specific initialization behavior and prevents startup races.

    Fixes #16782.

  • Fixed CompositeAuth incorrectly advertising SSO, session, and user provider capabilities when no inner provider supports them. Studio would show an SSO login button even when no provider had SSO configured, leading to 401 errors on login attempts. The duck-typing check now verifies that interface methods are actual functions rather than just present on the prototype chain. (#16664)

  • Hide internal workflow spans from Mastra-owned plumbing in exported traces. (#16631)

  • Channels now serialize messages per thread to keep conversations in order, and the tool approval flow is fixed end-to-end: (#16517)

    • Messages arriving while the agent is busy are delivered into the running agent loop instead of starting a new, conflicting stream on the same thread. Each Mastra thread shares one subscription, and channel/author facts (platform, message id, author name) are surfaced on the stored message under providerMetadata.mastra.channels.<platform>.
    • Tool approval flow: approving now drains the resumed run so the card is updated with the tool result and any follow-up assistant text is posted. Denying now resumes the run via declineToolCall instead of leaving it suspended. agent.subscribeToThread() consumers also receive chunks from resumed runs (the subscription used to drop the second registration for a resumed run that kept its original runId).
    • The original ChannelConfig is now exposed via the new AgentChannels.channelConfig field so channel providers can merge with existing adapters instead of replacing them.
    • Bumped chat to ^4.29.0.
  • Added the internalUsage?: UsageStats field to AIBaseAttributes, so any span type can carry token usage rolled up from internal descendant spans. Populated automatically by @mastra/observability when an internal MODEL_GENERATION ends inside a non-internal ancestor. (#16434)

  • Fixed trajectory scorers so tool calls stored only in V2 content.parts are included in extracted eval steps. (#15439)

  • Fixed thread metadata updates to merge with existing fields instead of replacing them. Previously, updating a thread's metadata would silently drop any fields not included in the update. Now existing metadata fields are preserved when updating. (#16846)

    Fixed MockMemory working memory tool to support partial JSON updates when using schema-based working memory. Previously, sending a partial update would overwrite all existing data. Now for schema-based configs, unchanged fields are preserved automatically (matching @mastra/memory behavior).

    Fixed MockMemory constructor to preserve workingMemory config options (like schema) when enableWorkingMemory is true.

  • Improved MastraCode quiet mode so terminal sessions are easier to scan. (#16771)

    • Quiet mode is now the default for new installs, and existing classic users get a one-time prompt to choose whether to enable it.
    • Added compact tool previews with a configurable preview-line limit, including an option to hide previews.
    • Improved repeated tool-call rendering, path continuation handling, task wrapping, shell/error previews, and spacing between tools, messages, plans, and completed subagents.
    • Added edited line ranges to workspace edit results so tool UIs can show where replacements happened.
  • Remove hardcoded /api/ prefix check from registerApiRoute(). The check incorrectly rejected custom routes starting with /api/ even when users configured a different apiPrefix. Reserved-path validation is already handled at the server adapter level using the actual configured prefix. (#16859)

  • Fixed infinite recursion in RequestContext.toJSON() when multiple (#16686) RequestContext instances reference each other through stored values. Previously, serializing such cross-context cycles would cause a CPU hang. Cyclic references are now detected and omitted from the serialized output, consistent with how circular references within a single context are handled.

  • Fixed crash in CacheKeyGenerator.fromAIV4Part when a tool-invocation part has undefined toolInvocation. This can happen when observational memory seals a partially-streamed assistant message. Also guarded MessageMerger against the same condition. (#16773)

  • Agent signals can now coordinate active thread runs across agents that share a PubSub instance, so thread subscribers and signal senders can observe the same run instead of being limited to one runtime instance. (#16665)

    import { Agent } from '@mastra/core/agent';
    import { EventEmitterPubSub } from '@mastra/core/events';
    
    const pubsub = new EventEmitterPubSub();
    const agent = new Agent({
      id: 'agent',
      name: 'Agent',
      instructions: 'Help the user',
      model,
      pubsub,
    });
  • Exposed formatSkillActivation(skill) from @mastra/core/workspace. It returns the activation payload — instructions plus references, scripts, and assets listings — that the built-in skill tool uses, so callers (e.g. an explicit /skill/<name> slash command) can produce the same output without duplicating the formatting logic. (#16618)

    Also preserves the user-invocable skill frontmatter field in workspace skill metadata.

    import { formatSkillActivation } from '@mastra/core/workspace';
    
    const content = formatSkillActivation(skill);
  • Fixed sub-agent streams so nested tool input progress is emitted while tool arguments are still being generated. This lets UIs show delegated agents preparing tool calls before the final tool input is available. Fixes #16422. (#16553)

  • Fixed backgroundTasks: { enabled: true } silently dispatching foreground-only tools to the background. (#16792)

    Previously, enabling backgroundTasks on the Mastra instance injected a system prompt into every agent that taught the LLM to flip any tool to background by passing _background: { enabled: true } in its arguments, and the resolver honored that override unconditionally. Models would readily do this for short, deterministic tools — a plain calculator could return "Background task started…" instead of { result: 42 }, breaking agent.generate() / agent.stream() for tool-using flows.

    The LLM _background override is now treated as a modifier on tools the developer has opted in at the tool or agent layer, not a standalone opt-in. If a tool hasn't been opted in via tool-level background: { enabled: true } or agent-level backgroundTasks: { tools: { … } } (or tools: 'all'), _background.enabled: true from the model is ignored and the tool runs in the foreground. Opted-in tools continue to honor LLM overrides for enabled, timeoutMs, and maxRetries as documented.

    Fixes https://github.com/mastra-ai/mastra/issues/16783

  • Fixed scheduler performance and correctness issues that could cause excessive Postgres CPU and noisy failed runs. (#16805)

    • Added missing indexes on the schedules tables that the tick loop polls every 10 seconds: (status, next_fire_at) on mastra_schedules and (schedule_id, actual_fire_at) on mastra_schedule_triggers. Without these the scheduler performed a full sequential scan on every tick.
    • The scheduler tick loop is now only started when at least one workflow declares a schedule (or scheduler.enabled is set explicitly), so deployments without scheduled workflows no longer poll the database at all.
    • The scheduler now validates that a schedule's target workflow is still registered before firing it. Schedules whose target workflow has been removed from the Mastra config are skipped for a short grace window and then deleted, so stale rows stop producing failed workflow runs forever.
    • The scheduler is now lazily initialized inside startWorkers(). Accessing mastra.scheduler before startWorkers() runs throws a descriptive error instead of returning a half-initialized instance.
  • Fixed Mastra getting stuck after a storage startup failure. Previously, if storage couldn't start up (for example, because the database was briefly unreachable), Mastra would keep returning the same error for every operation until the process was restarted. Now the next storage operation tries to start storage again, so brief outages recover on their own. Storage startup failures are also logged, so the problem is visible even when a later retry succeeds. (#16427)

  • Restore MastraCode local command execution to inherit parent environment variables while redacting env-shaped and secret-looking workspace trace data. (#16691)

  • Added a Unix socket PubSub transport and wired the Mastra Code TUI through a per-resource socket so local sessions can coordinate thread streams across processes. Programmatic createMastraCode usage remains opt-in: (#16669)

    await createMastraCode({ unixSocketPubSub: true });
  • Fix in-memory observability storage to match the contract validated against DuckDB/ClickHouse vNext adapters. (#16808)

    Previously, when running Mastra with the default in-memory storage, several observability operations behaved differently than they would against a production database:

    • getSpans threw 'This storage provider does not support batch-fetching spans'. It now batch-fetches spans by id within a trace, enabling the optimized getBranch path on in-memory storage.
    • batchCreateLogs, batchCreateMetrics, createScore/batchCreateScores, createFeedback/batchCreateFeedback appended duplicate records on retry. They now upsert by id, preserving the cursor id so delta polling does not re-emit the record. This makes client retries safe.
    • Discovery operations (getEntityTypes, getEntityNames, getServiceNames, getEnvironments, getTags) only inspected spans. They now also scan logs and metrics, so dimensions emitted on those surfaces are surfaced in discovery results.
    • getMetricTimeSeries merged grouped series whose label values contained the | character (e.g. {segmentA: 'a', segmentB: 'b|c'} collided with {segmentA: 'a|b', segmentB: 'c'}). Series are now keyed on the original label tuple, so colliding display names remain distinct series.
@mastra/ai-sdk@1.4.3
Patch Changes
  • Fixed sub-agent streams so nested tool input progress is emitted while tool arguments are still being generated. This lets UIs show delegated agents preparing tool calls before the final tool input is available. Fixes #16422. (#16553)
@mastra/auth-okta@0.0.3
Patch Changes
  • Fix endpoint URL construction for Okta org authorization servers. (#15694)

    MastraAuthOkta concatenated /v1/authorize (and /token, /keys, /logout) directly onto OKTA_ISSUER. That yields the right endpoint for a custom authorization server (https://{domain}/oauth2/default.../oauth2/default/v1/authorize), but 404s on an Okta org authorization server (https://{domain}.../v1/authorize, whereas the real org endpoint is .../oauth2/v1/authorize).

    An internal endpointBase is now derived from the issuer — verbatim when it already contains /oauth2/, otherwise ${issuer}/oauth2 — and used for the authorize, token, keys, and logout URLs. JWT iss-claim validation still uses the raw issuer so token validation stays correct on both server types. Trailing slashes on the issuer are also normalized so OKTA_ISSUER=https://{domain}/ no longer produces .../oauth2//v1/....

@mastra/auth-workos@1.5.0
Minor Changes
  • FGA check() and require() now accept an array of permissions and short-circuit on the first one that resolves to allow (ANY-of semantics). Single-permission usage continues to work unchanged. (#16605)

    // Before — one permission per call
    await fgaProvider.check({
      user,
      resource: { type: 'agent', id: 'abc' },
      permission: 'agents:read',
    });
    
    // After — single permission or ANY-of array
    await fgaProvider.check({
      user,
      resource: { type: 'agent', id: 'abc' },
      permission: ['agents:read', 'agents:execute'],
    });

    When all permissions in the array are denied, the thrown FGADeniedError lists them as any of [a, b, c] in its message.

Patch Changes
@mastra/clickhouse@1.9.0
Minor Changes
  • Added delta polling support for observability list APIs in core, DuckDB, and ClickHouse. (#16632)
Patch Changes
  • Fixed duplicate entries in the ClickHouse v-next observability discovery endpoints — tags, services, environments, entities, metric names, and metric labels now return each value once. Existing deployments are reconciled automatically on next startup; no manual migration required. (#16798)
@mastra/client-js@1.20.0
Minor Changes
  • Added agent.browserSession(threadId?) and agent.closeBrowser(threadId?) to the Agent resource, plus a GetAgentBrowserSessionResponse type. (#16668)

    browserSession probes the server's browser session state before opening a screencast WebSocket, so the connection is only made when the server has screencast support installed and an active session exists for the thread. closeBrowser ends the agent's browser session (or a single thread's session if threadId is passed). Both methods go through the configured client baseUrl and apiPrefix, so they work with servers mounted under a non-default API prefix.

    const probe = await client.getAgent('my-agent').browserSession(threadId);
    if (probe.screencastAvailable && probe.hasSession) {
      // safe to open the screencast WebSocket
    }
    
    await client.getAgent('my-agent').closeBrowser(threadId);
  • Added typed client-side resources for the stored-entity HTTP surface so you no longer have to hand-roll fetch calls. (#16666)

    import { MastraClient } from '@mastra/client-js';
    
    const client = new MastraClient({ baseUrl: 'http://localhost:4111' });
    
    // List/get with favorite metadata
    const { items } = await client.storedAgents.list({ page: 1, perPage: 20 });
    const agent = await client.storedAgents.get(items[0].id);
    console.log(agent.favoriteCount, agent.isFavorited);
    
    // Favorite toggle
    await client.storedAgents.favorite(agent.id);
    await client.storedAgents.unfavorite(agent.id);
    
    // Versioning + publish
    const draft = await client.storedSkills.create({
      /* ... */
    });
    const published = await client.storedSkills.publish(draft.id);
    await client.storedSkills.restore(draft.id, { version: 1 });

    Also regenerated route-types.generated.ts to cover the new editor-builder introspection routes (/editor/builder/settings, /editor/builder/infrastructure) and the external skill-registry endpoints under /editor/builder/registries (list, search, popular, preview, install).

  • Added delta polling support for observability list endpoints. (#16632)

    const page = await client.observability.listTraces({
      mode: 'page',
      filters: { entityName: 'agent-1' },
    });
    
    const delta = await client.observability.listTraces({
      mode: 'delta',
      filters: { entityName: 'agent-1' },
      after: page.deltaCursor,
    });

    Use mode: 'delta' to fetch only new items after the last cursor.

    Page-mode responses include pagination and deltaCursor when delta polling is supported. Delta-mode responses include delta and do not include pagination.

    If you read these responses directly in typed code, note that pagination is only included in page mode.

  • Narrowed AgentSignalContents from BaseMessageListInput to string | (TextPart | FilePart)[]. (#16622)

    Fixed two signal-content bugs:

    • user-message signal attributes now reach the LLM
    • multimodal non-user-message signals no longer lose file parts

    Callers that previously passed wrapped message shapes to agent.sendSignal should now pass a bare string or a bare parts array.

    Before: { type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }

    After: { type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }

    Added an optional providerOptions field to agent.sendSignal that flows through to the resulting prompt turn (as providerOptions on the LLM message) and is persisted on the stored signal message (as content.providerMetadata).

Patch Changes
  • Fixed provider and dataset item history response types to include fields returned by the API. (#16213)

  • Fixed an issue where recursive client-tool continuations after resumeStream (and resumeStreamUntilIdle) incorrectly re-hit the one-shot resume endpoint instead of falling back to the regular stream endpoint. The resume routes consume server-side resumeData and cannot be replayed, so client-tool continuations now route to /stream and /stream-until-idle respectively. (#16670)

  • Fixed the playground memory configuration display for agents using observationalMemory: true. (#16213)

@mastra/convex@1.1.0
Minor Changes
  • Added native Convex vector search support for production workloads. The new ConvexNativeVector adapter uses (#16729) Convex schema-defined vector indexes and ctx.vectorSearch instead of loading vectors through ConvexVector and scoring them in JavaScript.

    Define a native vector table in convex/schema.ts:

    import { defineSchema } from 'convex/server';
    import { defineMastraNativeVectorTable } from '@mastra/convex/schema';
    
    export default defineSchema({
      docs_vectors: defineMastraNativeVectorTable({
        dimensions: 1536,
      }),
    });

    Export the native vector handlers:

    import { mastraNativeVectorAction, mastraNativeVectorMutation, mastraNativeVectorQuery } from '@mastra/convex/server';
    
    export const query = mastraNativeVectorAction;
    export const read = mastraNativeVectorQuery;
    export const write = mastraNativeVectorMutation;

    Then configure ConvexNativeVector in your Mastra app:

    import { ConvexNativeVector } from '@mastra/convex';
    
    const vectorStore = new ConvexNativeVector({
      id: 'convex-native-vectors',
      deploymentUrl: process.env.CONVEX_URL!,
      adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
      indexes: {
        docs: {
          tableName: 'docs_vectors',
          vectorIndexName: 'by_embedding',
          dimension: 1536,
        },
      },
    });
  • Added ConvexServerCache so Convex-backed Mastra apps can keep durable stream replay and response cache state in Convex. (#16736)

    import { ConvexServerCache } from '@mastra/convex';
    
    const cache = new ConvexServerCache({
      deploymentUrl: process.env.CONVEX_URL!,
      adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
    });

    The package also exports the Convex cache schema tables and server mutation for mounting the cache handler in a Convex app. Existing Convex users who adopt the cache must add mastra_cache and mastra_cache_list_items to their Convex schema, mount the mastraCache handler, and deploy the schema update.

Patch Changes
@mastra/datadog@1.2.1
Patch Changes
  • Fixed Datadog LLM output tool calls so they render as structured tool call blocks. (#16789)

  • Fixed Datadog LLM span input formatting to remove empty user messages. (#16785)

@mastra/deployer@1.36.0
Minor Changes
  • Added route-specific CORS configuration so credentialed cross-origin access can be limited to selected custom routes and channel webhooks. (#16689)

    registerApiRoute('/customer-webhook', {
      method: 'POST',
      cors: {
        origin: ['https://customer-saas.example'],
        credentials: true,
      },
      handler: async c => c.json({ ok: true }),
    });
    new Agent({
      id: 'support-agent',
      name: 'Support Agent',
      instructions: '...',
      model,
      channels: {
        adapters: {
          web: {
            adapter: createWebAdapter(),
            cors: {
              origin: ['https://customer-saas.example'],
              credentials: true,
            },
          },
        },
      },
    });

    Use server.cors for one global CORS policy across the server:

    new Mastra({
      server: {
        cors: {
          origin: '*',
        },
      },
    });
Patch Changes
  • Browser streaming now works for stored agents. The deployer's getToolset first checks the runtime agent registry, then falls back to the editor's stored-agent lookup, so agents created at runtime through the editor can stream browser sessions without being pre-registered in code. (#16778)

  • When browser streaming is unavailable (the ws and @hono/node-ws packages aren't installed, or the deployer is running in a serverless environment), the deployer now registers a fallback GET /api/agents/:agentId/browser/session route that returns { hasSession: false, screencastAvailable: false }. This lets clients detect that screencast won't work and skip the WebSocket upgrade instead of triggering a noisy reconnect loop. (#16668)

  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)

@mastra/deployer-cloud@1.36.0
Patch Changes
  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)
@mastra/deployer-cloudflare@1.1.37
Patch Changes
  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)
@mastra/deployer-netlify@1.1.13
Patch Changes
  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)
@mastra/deployer-vercel@1.1.31
Patch Changes
  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)
@mastra/duckdb@1.4.0
Minor Changes
  • Added delta polling support for observability list APIs in core, DuckDB, and ClickHouse. (#16632)
Patch Changes
  • Fixed DuckDB observability storage so dev server restarts no longer fail when replaying cursor migrations. (#16803)
@mastra/editor@0.9.0
Minor Changes
  • EditorWorkspaceNamespace can now snapshot a live Workspace for persistence — the reverse of hydrateSnapshotToWorkspace(): (#16673)

    const snapshot = await editor.workspace.snapshotFromWorkspace(runtimeWorkspace);
    await editor.workspace.create({ id: 'my-workspace', ...snapshot });

    snapshotFromWorkspace() is async and awaits sandbox.getInfo() and filesystem.getInfo() so async providers like CompositeFilesystem keep their mount metadata in the stored config.

    Also includes two smaller behavioral fixes:

    • EditorSkillNamespace.publishSkillFromSource() stores the new files field on the published skill version and strips undefined keys before calling update() (libsql/pg adapters reject undefined bind arguments).
    • CrudEditorNamespace.clearCache(id) always calls onCacheEvict(id), even when the entity wasn't cached, so subclasses can clean up runtime registries for version-specific lookups that bypass the editor cache.
  • Added an editor.favorites namespace so direct (non-HTTP) callers can favorite, unfavorite, and query favorited stored agents/skills through the editor instance. (#16749)

    import { MastraEditor } from '@mastra/editor';
    
    const editor = new MastraEditor({ mastra });
    
    // Toggle
    await editor.favorites.favorite({ userId, entityType: 'agent', entityId });
    await editor.favorites.unfavorite({ userId, entityType: 'agent', entityId });
    
    // Lookups
    const isFav = await editor.favorites.isFavorited({ userId, entityType: 'agent', entityId });
    const favSet = await editor.favorites.isFavoritedBatch({ userId, entityType: 'agent', entityIds });
    const ids = await editor.favorites.listFavoritedIds({ userId, entityType: 'agent' });

    The namespace performs the storage mutation only — visibility and ownership enforcement still belong to the caller (the HTTP route handlers in @mastra/server already do this).

Patch Changes
@mastra/express@1.3.23
Patch Changes
  • Updated the adapter permission check to read user permissions from the new namespaced request-context key mastra__userPermissions (was userPermissions). This matches the namespaced keys that @mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)

    No action needed for typical users — install the matching @mastra/server release and the adapter will continue to enforce route permissions exactly as before.

  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)

@mastra/fastembed@1.1.0
Minor Changes
  • Replace the abandoned fastembed npm dependency with a maintained, vendored implementation. The public API and all embedding models remain unchanged — no migration needed. (#16772)
@mastra/fastify@1.3.23
Patch Changes
  • Updated the adapter permission check to read user permissions from the new namespaced request-context key mastra__userPermissions (was userPermissions). This matches the namespaced keys that @mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)

    No action needed for typical users — install the matching @mastra/server release and the adapter will continue to enforce route permissions exactly as before.

  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)

@mastra/hono@1.4.18
Patch Changes
  • Updated the adapter permission check to read user permissions from the new namespaced request-context key mastra__userPermissions (was userPermissions). This matches the namespaced keys that @mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)

    No action needed for typical users — install the matching @mastra/server release and the adapter will continue to enforce route permissions exactly as before.

  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)

  • Added GET /agents/:agentId/browser/session endpoint (under the configured apiPrefix, default /api) that reports whether a screencast WebSocket should be opened for an agent and thread. Clients can probe this before upgrading to a WebSocket to avoid idle connections and reconnect storms. (#16668)

    curl "http://localhost:4111/api/agents/my-agent/browser/session?threadId=thread-1"
    # {"hasSession":true,"screencastAvailable":true}

    The response shape is { hasSession: boolean, screencastAvailable: true }. screencastAvailable is always true when this route is registered; the deployer registers a fallback that returns { hasSession: false, screencastAvailable: false } when browser streaming packages aren't installed, so clients can use the same probe in both cases.

    setupBrowserStream now accepts an optional apiPrefix so the probe and existing POST /agents/:agentId/browser/close routes are mounted under the same prefix as the rest of the server. The deployer wires this from mastra.getServer().apiPrefix automatically.

  • The Hono adapter now awaits getToolset calls in browser-stream routes, supporting deployers that resolve agents asynchronously (such as stored agents looked up via the editor). (#16778)

@mastra/koa@1.5.6
Patch Changes
  • Updated the adapter permission check to read user permissions from the new namespaced request-context key mastra__userPermissions (was userPermissions). This matches the namespaced keys that @mastra/server's core auth middleware now writes and avoids collisions with caller-supplied context entries. (#16605)

    No action needed for typical users — install the matching @mastra/server release and the adapter will continue to enforce route permissions exactly as before.

  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)

@mastra/libsql@1.11.1
Patch Changes
  • Fixed scheduler performance and correctness issues that could cause excessive Postgres CPU and noisy failed runs. (#16805)
    • Added missing indexes on the schedules tables that the tick loop polls every 10 seconds: (status, next_fire_at) on mastra_schedules and (schedule_id, actual_fire_at) on mastra_schedule_triggers. Without these the scheduler performed a full sequential scan on every tick.
    • The scheduler tick loop is now only started when at least one workflow declares a schedule (or scheduler.enabled is set explicitly), so deployments without scheduled workflows no longer poll the database at all.
    • The scheduler now validates that a schedule's target workflow is still registered before firing it. Schedules whose target workflow has been removed from the Mastra config are skipped for a short grace window and then deleted, so stale rows stop producing failed workflow runs forever.
    • The scheduler is now lazily initialized inside startWorkers(). Accessing mastra.scheduler before startWorkers() runs throws a descriptive error instead of returning a half-initialized instance.
@mastra/mcp@1.8.0
Minor Changes
  • Added MCP tool annotations to the requireToolApproval context and exposed them on tools returned from listTools() / listToolsets(). (#16784)

    The requireToolApproval callback now receives the server-advertised annotations (title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint) alongside toolName and args. This lets you write declarative approval policies instead of hardcoding tool name lists. Annotations are also propagated onto Mastra tools as tool.mcp.annotations so apps can render them in UI.

    Security caveat (per the MCP spec): annotations are hints, not guarantees. Clients MUST treat them as untrusted unless they come from a trusted server. Do not use annotations alone as a security boundary for servers you do not control — set requireToolApproval: true for those. When the server omits annotations entirely, this field is undefined, so policies can distinguish "no annotations" from "annotated as safe".

    import { MCPClient } from '@mastra/mcp';
    
    // Before — hardcoded tool name lists, server-specific
    const mcp = new MCPClient({
      servers: {
        github: {
          url: new URL('https://example.com/mcp'),
          requireToolApproval: ({ toolName }) => toolName === 'delete_repo',
        },
      },
    });
    
    // After — annotation-driven, works across any trusted MCP server
    const mcp = new MCPClient({
      servers: {
        github: {
          url: new URL('https://example.com/mcp'),
          requireToolApproval: ({ annotations }) => {
            if (!annotations) return true;
            if (annotations.readOnlyHint) return false;
            if (annotations.destructiveHint) return true;
            return false;
          },
        },
      },
    });
    
    // Annotations are also visible on tools returned by listTools()
    const tools = await mcp.listTools();
    for (const tool of Object.values(tools)) {
      console.log(tool.mcp?.annotations);
    }

    Closes #16766.

Patch Changes
  • Fixed an issue where OAuth token requests dropped client_id and client_secret for confidential clients. The provider previously shipped an empty addClientAuthentication method that satisfied the MCP SDK's existence check and short-circuited its default credential attachment, causing invalid_request errors on token exchange and refresh against confidential-client OAuth servers. The empty stub has been removed so the SDK's built-in client authentication runs again. See #16854. (#16862)

  • Close previous SSE transport before accepting a new connection in MCPServer.connectSSE(). Previously, sequential SSE connections to the same server would fail with "Already connected to a transport" because the underlying protocol was never closed when the previous client disconnected. (#16695)

@mastra/memory@1.19.0
Minor Changes
  • Added activateAfterIdle: "auto" for Observational Memory early activation. (#16663)

    Mastra can now choose an idle activation timeout from the active model provider's prompt cache behavior. OpenAI also respects providerOptions.openai.promptCacheRetention when available.

    const memory = new Memory({
      options: {
        observationalMemory: {
          model: 'google/gemini-2.5-flash',
          activateAfterIdle: 'auto',
          activateOnProviderChange: true,
        },
      },
    });
  • Add observeAttachments to ObservationConfig for Observational Memory. Use it to control whether image/file parts on observed messages are forwarded to the Observer model alongside their placeholder text lines. (#16671)

    • true (default) — forward all attachments (existing behavior).
    • false — drop all attachments; placeholders still appear in the observer transcript.
    • string[] — allowlist of mimeType patterns, e.g. ['image/*'] or ['application/pdf']. Matching is case-insensitive and supports exact, type/*, and * patterns.

    Useful when the Observer model is text-only (some DeepSeek endpoints, etc.) while the main agent uses a multimodal model. Tool-result attachments are filtered with the same rule.

    new Memory({
      options: {
        observationalMemory: {
          observation: {
            model: 'deepseek/deepseek-chat', // text-only observer
            observeAttachments: false, // or e.g. ['image/*', 'application/pdf']
          },
        },
      },
    });
Patch Changes
  • feat(memory): start background buffering of unobserved messages when agent goes idle (#16694)

    In OM buffering mode, when the agent goes idle (turn.end()), any unobserved messages are now buffered in the background via a fire-and-forget buffer() call. This ensures observations are computed proactively rather than waiting for the next turn's step.prepare().

@mastra/nestjs@0.1.7
Patch Changes
  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)
@mastra/observability@1.13.0
Minor Changes
  • Roll up token usage from internal MODEL_GENERATION spans onto the closest exported ancestor span. When tracingPolicy.internal filters a model call out of exported traces, its tokens used to disappear from both the trace UI and metrics. Now: (#16434)

    • The visible ancestor (e.g. PROCESSOR_RUN, AGENT_RUN) gets an internalUsage attribute summing the tokens consumed by its hidden descendants — so a Mastra-owned processor that runs an internal agent (moderation, PII detector, structured output, etc.) shows its aggregate cost on the visible PROCESSOR_RUN span.
    • Token / cost metrics still emit, but are attributed via labels to the visible ancestor instead of the hidden agent.

    No action required — the rollup applies automatically whenever an internal MODEL_GENERATION ends inside a non-internal ancestor.

  • MastraStorageExporter now notifies custom exporters and connected integrations when it cannot persist observability events, such as unsupported storage or retries being exceeded. This matches the behavior already available on DefaultExporter. (#16755)

    Also fixed an issue in both exporters where span updates waiting on their parent span could be silently lost if a later flush in the same cycle failed.

Patch Changes
@mastra/pg@1.11.1
Patch Changes
  • Fixed scheduler performance and correctness issues that could cause excessive Postgres CPU and noisy failed runs. (#16805)
    • Added missing indexes on the schedules tables that the tick loop polls every 10 seconds: (status, next_fire_at) on mastra_schedules and (schedule_id, actual_fire_at) on mastra_schedule_triggers. Without these the scheduler performed a full sequential scan on every tick.
    • The scheduler tick loop is now only started when at least one workflow declares a schedule (or scheduler.enabled is set explicitly), so deployments without scheduled workflows no longer poll the database at all.
    • The scheduler now validates that a schedule's target workflow is still registered before firing it. Schedules whose target workflow has been removed from the Mastra config are skipped for a short grace window and then deleted, so stale rows stop producing failed workflow runs forever.
    • The scheduler is now lazily initialized inside startWorkers(). Accessing mastra.scheduler before startWorkers() runs throws a descriptive error instead of returning a half-initialized instance.
@mastra/playground-ui@29.0.0
Minor Changes
  • Added ContextMenu for right-click interactions. Supports submenus, checkbox and radio items, keyboard shortcuts, and a destructive variant for dangerous actions like delete. (#16791)

    import { ContextMenu } from '@mastra/playground-ui';
    
    <ContextMenu>
      <ContextMenu.Trigger className="…">Right click here</ContextMenu.Trigger>
      <ContextMenu.Content>
        <ContextMenu.Item>Rename</ContextMenu.Item>
        <ContextMenu.Item variant="destructive">Delete</ContextMenu.Item>
      </ContextMenu.Content>
    </ContextMenu>;
  • Removed ButtonWithTooltip from @mastra/playground-ui. Use Button with the tooltip prop instead. (#16719)

    Migration

    // before
    import { ButtonWithTooltip } from '@mastra/playground-ui';
    
    <ButtonWithTooltip tooltipContent="Search">
      <Search />
    </ButtonWithTooltip>;
    
    // after
    import { Button } from '@mastra/playground-ui';
    
    <Button tooltip="Search">
      <Search />
    </Button>;

    tooltip supports the same values as tooltipContent. Icon-only buttons that pass a string tooltip now also get it as their aria-label automatically, matching how labelled controls have always behaved. Pass an explicit aria-label to override.

  • Added a custom date range option to the Metrics page date picker. You can now filter metrics by an arbitrary start and end date and time, matching the Traces page, alongside the existing relative presets (last 24 hours, 3, 7, 14, and 30 days). (#16832)

    The selected range is reflected in the URL so it can be bookmarked or shared:

    /metrics?period=custom&dateFrom=2026-05-01T00:00:00.000Z&dateTo=2026-05-07T23:59:59.999Z
  • Refreshed Button + Card design system tokens. (#16769)

    Button variants (breaking): consolidated to default, primary, outline, ghost. The cta, contrast, and unused link variants have been removed. primary now uses a high-contrast neutral6 fill instead of surface4, so it reads clearly as the form submit action in both themes.

    // Before
    <Button variant="cta">Save</Button>
    <Button variant="contrast">Done</Button>
    <Button variant="link">Open</Button>
    
    // After
    <Button variant="primary">Save</Button>     // cta → primary (no brand green; theme-aware high contrast)
    <Button variant="primary">Done</Button>     // contrast → primary (same recipe, renamed)
    <Button as="a" href="…" variant="ghost">Open</Button>  // link → ghost (or plain <a> for inline text links)

    New tokens: --surface-overlay-soft and --surface-overlay-strong — alpha overlays of the opposite-theme color, used by SectionCard header strip and DashboardCard fill so cards read consistently on any surface.

    Other:

    • DashboardCard radius reduced to rounded-xl and padding tightened to px-4 py-3 for better grid density.
    • SectionCard wrapper no longer fills its background — header strip + border carry definition.
    • Dark surface2 / surface3 darkened slightly (16.84% → 16%, 19.13% → 18%) so the main frame reads as a distinct surface.
    • Dark border1 / border2 alphas bumped (6% → 7%, 10% → 11%) for closer dark/light parity.
    • Removed deprecated --section-card-* tokens and their @utility blocks.
Patch Changes
  • Added a destructive variant on DropdownMenu.Item to highlight dangerous actions like delete. (#16791)

    <DropdownMenu.Item variant="destructive">Delete project</DropdownMenu.Item>
  • PopoverContent no longer forwards the underlying library's auto-focus event handlers (onOpenAutoFocus, onCloseAutoFocus). To control focus when the popover opens or closes, use initialFocus and finalFocus. (#16791)

    // Before
    <PopoverContent onOpenAutoFocus={(e) => e.preventDefault()} />
    
    // After
    <PopoverContent initialFocus={false} />
  • Fixed a crash in filter menus with nested submenus (such as the Filter on the Agent review page) that showed "MenuPortal must be used within Menu". The submenu content now uses the design system's DropdownMenu.SubContent instead of the underlying library's portal directly. (#16829)

  • Fixed type definitions and shared UI component types for @mastra/playground-ui. (#16213)

  • AlertDialog's API and behavior are unchanged — asChild on AlertDialog.Trigger, and AlertDialog.Action and AlertDialog.Cancel, all work exactly as before. (Internally it now builds on Base UI primitives.) (#16824)

  • Moved the Collapsible component to Base UI, with a smoother height-based expand and collapse animation. The public API is unchanged — asChild on CollapsibleTrigger still works. (#16825)

  • Upgraded @base-ui/react to 1.5, making popups noticeably faster — components built on Base UI such as Tooltip, Popover, DropdownMenu and ContextMenu now open and close more quickly. (#16819)

  • Moved the Level icon from its own column into the Name column, next to the trace name, on the Observability traces list. (#16712)

  • The Traces list now updates live via delta polling. Previously the list was refetched every 10 seconds, replacing the whole page with no signal about what changed; now new traces appear within a few seconds of being created, with a brief highlight to draw attention. Status changes on already-visible rows (running → success / error) also propagate without intervention, and returning to the tab after being idle re-syncs from a fresh cursor. (#16727)

    New useTraces return fields

    • isRefetching — true while any meaningful refetch is in flight. Use it to drive a heartbeat indicator.
    • autoRefetch / setAutoRefetch — pause / resume all automatic polling so the consumer can render an opt-out toggle.
    • recentlyAddedKeysSet<string> of traceId:spanId for rows that just arrived via delta polling. Drives the temporary highlight in TracesListView.

    New polling config

    Every timing in the hook is tunable per-instance via a new polling option:

    import { useTraces, type TracesPollingConfig } from '@mastra/playground-ui';
    
    useTraces({
      filters,
      listMode,
      polling: {
        deltaPollIntervalMs: 10_000,
        idleGuardThresholdMs: 5 * 60_000,
      },
    });

    Omitted fields fall through to the defaults (delta poll every 5s, idle reset after 15 min, status refresh every 60s, etc).

    TracesListView

    New optional recentlyAddedKeys?: Set<string> prop. Rows whose traceId:spanId is in the set get the animate-row-highlight class — a brief fade-out to transparent, added to index.css.

    Compatibility

    Requires @mastra/server and @mastra/client-js at the versions that ship the observability delta-polling endpoints, and a store that opts into delta polling (@mastra/clickhouse, @mastra/duckdb, and the in-memory store today). When unavailable — older server or a store without delta capability — the hook silently falls back to page-mode interval refetching. No consumer changes required.

  • Added align and stack variants to PageLayout.Row. Use stack="responsive" for top bars that should collapse to a vertical stack on narrow viewports, and align="center" to vertically center children. Applied the new variants to the Prompts and Workflows top bars so the search field and primary action share a single row on desktop and stack on mobile. (#16714)

    <PageLayout.Row align="center" stack="responsive">
      <ListSearch ... />
      <Button ...>Create</Button>
    </PageLayout.Row>
  • Fixed the playground memory configuration display for agents using observationalMemory: true. (#16213)

  • Fixed double-counted cache token costs in the Metrics dashboard. The Model Usage & Cost table and the Token Usage by Agent table were summing cache read/write costs on top of the total input cost, which already includes them. (#16737)

  • Migrated the Tooltip primitive to Base UI while preserving the existing API. The popup explicitly sets role="tooltip" so consumers can keep querying it via getByRole('tooltip') (Base UI does not add this role automatically). Existing <TooltipTrigger asChild> usage continues to work unchanged, and Base UI's native render prop is now also supported on TooltipTrigger so consumers wrapping anchors, custom router links, or icons can pass the element directly without an asChild adapter: (#16713)

    // Still supported
    <TooltipTrigger asChild>
      <Button>Save</Button>
    </TooltipTrigger>
    
    // New: pass the element via Base UI's native API
    <TooltipTrigger render={<Button>Save</Button>} />

    Also fixed the arrow rendering so the diagonal stroke meets the popup outline at the exact same pixel center on every side, removing the ~1px seam previously visible where the arrow joined the popup edge.

  • Migrated the Slider component to base-ui with a refined neutral visual design. (#16788)

    What changed

    • Replaced @radix-ui/react-slider with @base-ui/react/slider as the underlying primitive
    • Refreshed visuals: thin rounded thumb with white border and neutral inside, opacity-based track that adapts to any surface, neutral filled indicator (no green/accent color)
    • Larger click target via padded Slider.Control and an invisible hit area on the thumb so it is easier to grab
    • Added cursor-pointer on the control and cursor-not-allowed when disabled
    • Removed the now unused @radix-ui/react-slider and @radix-ui/react-tabs dependencies

    API compatibility

    The public API is preserved. onValueChange and onValueCommitted are wrapped so consumers always receive number[], even though base-ui returns number | number[] internally. Existing call sites like <Slider value={[temperature]} onValueChange={value => setTemperature(value[0])} /> continue to work without changes.

  • Moved the Dialog component to Base UI. The public API is unchanged — asChild on DialogTrigger and DialogClose still works the same way, and open/close animations behave as before. (#16821)

@mastra/react@0.4.0
Minor Changes
  • Added clientTools option to useChat's generate/stream calls for forwarding browser-side tools to the agent on each invocation. (#16778)

    import { useChat } from '@mastra/react';
    
    const { generate } = useChat({ agentId: 'my-agent' });
    
    await generate({
      messages: [{ role: 'user', content: 'Show a toast that says hi' }],
      clientTools: {
        showToast: {
          description: 'Show a toast to the user',
          inputSchema: z.object({ message: z.string() }),
          execute: ({ message }) => toast(message),
        },
      },
    });

    Client tools are forwarded as-is to the underlying agent.generate() and agent.stream() calls.

  • Narrowed AgentSignalContents from BaseMessageListInput to string | (TextPart | FilePart)[]. (#16622)

    Fixed two signal-content bugs:

    • user-message signal attributes now reach the LLM
    • multimodal non-user-message signals no longer lose file parts

    Callers that previously passed wrapped message shapes to agent.sendSignal should now pass a bare string or a bare parts array.

    Before: { type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }

    After: { type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }

    Added an optional providerOptions field to agent.sendSignal that flows through to the resulting prompt turn (as providerOptions on the LLM message) and is persisted on the stored signal message (as content.providerMetadata).

Patch Changes
@mastra/server@1.36.0
Minor Changes
  • Added delta polling support for observability list endpoints. (#16632)

    const page = await client.observability.listTraces({
      mode: 'page',
      filters: { entityName: 'agent-1' },
    });
    
    const delta = await client.observability.listTraces({
      mode: 'delta',
      filters: { entityName: 'agent-1' },
      after: page.deltaCursor,
    });

    Use mode: 'delta' to fetch only new items after the last cursor.

    Page-mode responses include pagination and deltaCursor when delta polling is supported. Delta-mode responses include delta and do not include pagination.

    If you read these responses directly in typed code, note that pagination is only included in page mode.

  • Added automatic FGA metadata for stored resource routes plus optional request scope isolation for stored resource APIs. Enable protected-route coverage with provider options: (#16651)

    const fga = new MastraFGAWorkos({
      resourceMapping,
      permissionMapping,
      requireForProtectedRoutes: true,
      auditProtectedRoutes: 'warn',
    });
  • Added an HTTP surface for stored agents/skills/workspaces, plus introspection endpoints for the agent-builder and an external skill-registry proxy. Studio and the client SDK use these endpoints to back the new "stored entity" management UI. (#16666)

    # Browse + manage stored entities (responses include favoriteCount + isFavorited)
    GET    /stored/agents?visibility=public&page=1&perPage=20
    GET    /stored/agents/:id
    POST   /stored/agents
    PATCH  /stored/agents/:id
    DELETE /stored/agents/:id
    
    # Versioning
    POST   /stored/skills/:id/publish
    POST   /stored/skills/:id/activate
    POST   /stored/skills/:id/restore
    
    # Favorites
    PUT    /stored/agents/:id/favorite
    DELETE /stored/agents/:id/favorite
    
    # Builder introspection
    GET    /editor/builder/settings
    GET    /editor/builder/infrastructure
    
    # External skill registry proxy (skills.sh)
    GET    /editor/builder/registries
    GET    /editor/builder/registries/:registryId/search
    GET    /editor/builder/registries/:registryId/popular
    GET    /editor/builder/registries/:registryId/skills/:owner/:repo/preview
    POST   /editor/builder/registries/:registryId/skills/:owner/:repo/install

    Highlights:

    • Visibility + authorship gating. Stored agents/skills now resolve a caller's author identity from the request context. Non-admin users only see their own + public entities. Admins see everything.
    • Favorites. List/get responses include favoriteCount and the caller's isFavorited flag. PUT/DELETE /stored/{agents|skills}/:id/favorite toggle the favorite for the caller.
    • Avatar validation. Stored-agent/skill metadata avatars are validated through a new validateMetadataAvatarUrl helper (rejects payloads over the size limit or with malformed base64).
    • Model-policy enforcement. Stored-agent create/update routes invoke assertModelAllowed via the new resolveBuilderModelPolicy helper. Disallowed models map to HTTP 422 with a structured body — { code, attempted, offendingLabel, allowed } — via handleError's new ModelNotAllowedError mapping.
    • Builder introspection. GET /editor/builder/settings returns feature flags, configuration, picker visibility, and model policy. GET /editor/builder/infrastructure reports browser-provider and sandbox status. Both default to { enabled: false } when no MastraEditor is configured.
    • External skill registry. /editor/builder/registries/* proxies the public skills.sh catalog so the builder UI can browse and install registered skills.

    This also bumps the @mastra/core peer dependency floor to >=1.34.0-0 (see the separate changeset) because the new handlers and error mapping import runtime values from @mastra/core/agent-builder/ee.

  • Narrowed AgentSignalContents from BaseMessageListInput to string | (TextPart | FilePart)[]. (#16622)

    Fixed two signal-content bugs:

    • user-message signal attributes now reach the LLM
    • multimodal non-user-message signals no longer lose file parts

    Callers that previously passed wrapped message shapes to agent.sendSignal should now pass a bare string or a bare parts array.

    Before: { type: 'user-message', contents: [{ role: 'user', content: [{ type: 'text', text: 'hi' }] }] }

    After: { type: 'user-message', contents: [{ type: 'text', text: 'hi' }] }

    Added an optional providerOptions field to agent.sendSignal that flows through to the resulting prompt turn (as providerOptions on the LLM message) and is persisted on the stored signal message (as content.providerMetadata).

  • Routes can now require any one of multiple permissions by passing an array to requiresPermission. When an array is provided, the request is allowed if the caller holds any of the listed permissions. Existing single-string usage continues to work. (#16605)

    // Before — single permission only
    {
      path: '/v1/things',
      method: 'GET',
      requiresPermission: 'things:read',
      handler,
    }
    
    // After — single permission or ANY-of array
    {
      path: '/v1/things/:id/stream',
      method: 'GET',
      requiresPermission: ['things:read', 'things:execute'],
      handler,
    }

    Denial messages now read Missing required permission: a or b or c when an array is used.

    New endpoint

    GET /api/auth/roles/:roleId/permissions returns the resolved permission list for a role. Useful for client-side gating and admin tooling.

    const res = await fetch('/api/auth/roles/admin/permissions', { credentials: 'include' });
    // { "roleId": "admin", "permissions": ["*"] }

    Namespaced request-context keys (non-breaking)

    coreAuthMiddleware now writes user state under namespaced keys (mastra__user, mastra__userPermissions, mastra__userRoles) in addition to the existing bare keys (user, userPermissions, userRoles). The bare keys are still written for backward compatibility, so existing middleware, integrations, and built-in handlers that read requestContext.get('user') continue to work unchanged.

    New code should prefer the namespaced constants to avoid collisions with caller-supplied request-context entries:

    import { MASTRA_USER_KEY, MASTRA_USER_PERMISSIONS_KEY, MASTRA_USER_ROLES_KEY } from '@mastra/server/auth';
    
    const user = requestContext.get(MASTRA_USER_KEY);
    const permissions = requestContext.get(MASTRA_USER_PERMISSIONS_KEY) as string[] | undefined;
    const roles = requestContext.get(MASTRA_USER_ROLES_KEY) as string[] | undefined;

    The bare keys (user, userPermissions, userRoles) remain populated and are considered the documented public surface for this release; a future major release may deprecate them.

    Route permission derivation

    getEffectivePermission() now recognizes stored resource families (stored-agents, stored-skills, stored-prompt-blocks, stored-mcp-clients, stored-scorers, stored-workspaces) and publish / activate / restore action suffixes on stored-resource routes. Return type widened to string | string[] | null to support routes that map to multiple permissions.

Patch Changes
  • Fixed stored resource updates to preserve existing metadata keys. (#16651)

  • Hardened the stored-agent and stored-skill favorite toggle endpoints (PUT/DELETE /stored/{agents,skills}/:id/favorite) so callers can no longer favorite or unfavorite entities outside their tenant scope. (#16749)

    Deployments that configure storedResources.scope now get the same 404-on-mismatch protection on favorite toggles that already applied to read/update/delete. Single-tenant deployments are unaffected.

    Also corrected JSDoc on stored-agent and stored-skill handlers to reference the canonical resource/action names (stored-agents:read, stored-skills:write).

  • Fix DELETE custom routes forwarding JSON request bodies. (#16857)

  • Fixed CompositeAuth incorrectly advertising SSO, session, and user provider capabilities when no inner provider supports them. Studio would show an SSO login button even when no provider had SSO configured, leading to 401 errors on login attempts. The duck-typing check now verifies that interface methods are actual functions rather than just present on the prototype chain. (#16664)

  • Fixed provider and dataset item history response types to include fields returned by the API. (#16213)

  • Widened BrowserStreamConfig.getToolset to support async lookup. Existing synchronous implementations continue to work — the type now accepts MastraBrowser | undefined or Promise<MastraBrowser | undefined>. (#16778)

    This unblocks server-adapter implementations that need to resolve agents asynchronously (for example, hydrating stored agents from storage on first browser-stream connection).

    import { setupBrowserStream } from '@mastra/server';
    
    await setupBrowserStream(app, {
      getToolset: async agentId => {
        const agent = await resolveAgent(agentId);
        return agent?.browser;
      },
      apiPrefix,
    });
  • Align stored-entity authorship checks with their RBAC resource names. Stored-agent and stored-skill handlers were calling hasAdminBypass / assertReadAccess / assertWriteAccess / resolveAuthorFilter with resource: 'agents' and resource: 'skills', but the routes are gated by stored-agents:* / stored-skills:* permissions. An admin granted stored-agents:* (or stored-skills:*) without the global * wildcard would pass route authorization but be treated as a non-admin inside the handler, so they could not list, read, or update private records owned by other users. Handlers now use stored-agents and stored-skills as the authorship resource string, matching the permission strings emitted by the route layer. (#16666)

  • Restore backwards compatibility for the legacy GET /api/memory/threads?orderBy=<field>&sortDirection=<dir> query shape emitted by @mastra/client-js < 1.18 (and any hand-rolled HTTP clients written against pre-1.0 docs). Server 1.31.0 turned that shape into a hard 400 (Invalid input: expected object, received undefined); the legacy bare-string + sortDirection pair is now transparently fused into the current { orderBy: { field, direction } } object shape before validation, so pinned clients keep working. (#16745)

    Current callers using bracket notation (orderBy[field]=...&orderBy[direction]=...) or a JSON-stringified orderBy are unaffected.

  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)

  • Fixed the playground memory configuration display for agents using observationalMemory: true. (#16213)

@mastra/slack@1.2.1
Patch Changes
    • SlackProvider.connect() now merges with existing channel adapters instead of replacing them, preserving adapters the agent author already configured (e.g. Discord). (#16517)
    • Slack interactive payloads (button clicks, modal submissions) no longer return 400 Malformed JSON body. The provider only JSON-parses the body for the events callback path and forwards form-urlencoded payloads to the adapter's webhook handler unchanged.
    • Bumped chat to ^4.29.0.
@mastra/stagehand@0.2.3
Patch Changes
  • Silence Stagehand console logging by default and route logs through a logger hook to avoid corrupting host TUIs. (#16677)
@mastra/temporal@0.1.6
Patch Changes
  • Bumped the @mastra/core peer dependency floor from >=1.32.0-0 to >=1.34.0-0. (#16666)
@mastra/voice-openai-realtime@0.12.4
Patch Changes
  • Fixed OpenAI Realtime voice output streaming for current audio event names. (#16763)

  • Fixed user audio transcriptions missing or duplicating segments when OpenAI Realtime sends completion events. (#16759)

Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.37.0

Highlights

Google Cloud Spanner Storage Adapter (@mastra/spanner@1.0.0)

New first-party storage backend for Google Cloud Spanner (GoogleSQL) covering most Mastra storage domains (memory, workflows, schedules, observability, etc.), including persisting tracing spans for Studio and supporting the local Spanner emulator.

Agent Channels Streaming UX + Tool Rendering Controls (Core + Slack)

Major upgrades to channel adapters: opt-in streaming text deltas, adaptive typing indicators, and a new toolDisplay system (cards/text/timeline/grouped/hidden/function) to control how tool execution renders (including plan/timeline widgets and stable toolCallId correlation).

Client-Side Tool Observability End-to-End (@mastra/client-js + @mastra/observability)

Client-executed tools now show up in server traces via CLIENT_TOOL_CALL spans, with a shared observe helper for child spans/logs inside execute; observability exporters can ingest and forward this client telemetry (including duration metrics labeled toolType: 'client').

Agent Builder Routes Now Auto-Registered in Server Adapters

/agent-builder/* endpoints are now served automatically by any @mastra/server-based adapter (Express/Fastify/Hono/Koa/etc.) without manual wiring, while preserving lazy-loading to keep worker bundles small; new EE permissions gate access.

New Workspace Filesystem Provider via FilesSDK (@mastra/files-sdk@0.2.0)

Adds a unified WorkspaceFilesystem implementation backed by FilesSDK, letting you swap storage providers (S3/R2/GCS/Azure Blob/Vercel Blob/local FS, etc.) by changing the FilesSDK adapter without changing Mastra code.

Breaking Changes
  • @mastra/slack: removed cards: boolean and formatToolCall from Slack provider/channel config; migrate to toolDisplay: 'text' (for cards: false) or toolDisplay function form (for formatToolCall).

Changelog

@mastra/core@1.37.0
Minor Changes
  • Improved agent channels UX: (#16937)

    • Streaming text — opt-in per-adapter streaming flag (boolean | { updateIntervalMs?: number }) that pushes the agent's text deltas into the platform message progressively via the Chat SDK.

    • Adaptive typing indicator — the platform's typing status now reflects what the agent is doing (is working… at run start, is thinking… during reasoning, is typing… while generating text, is calling {toolName}… while a tool runs, is saving to memory…/is recalling memory… during memory work, is requesting approval for {toolName}… while a tool is suspended), coalesced so the platform API isn't called on every delta. Skipped while a streaming session is open since the widget itself conveys progress.

    • toolDisplay modes — new ChannelAdapterConfig.toolDisplay controls how tool calls render:

      • 'cards' (default) — per-tool running/result cards in rich Block Kit form.
      • 'text' — per-tool running/result messages as plain text (replaces the old cards: false flag).
      • 'timeline' — every tool gets its own task row in a streaming widget with status icons and args.
      • 'grouped' — all tools in the run collapse into a single streaming widget; args fold inline into the title and successful results are suppressed for an at-a-glance summary (errors keep their full text).
      • 'hidden' — tools run silently; only the typing indicator shows work.
      • Function form (ToolDisplayFn) — pass a function (event, ctx) => { kind: 'post', message } | { kind: 'stream', chunk } | undefined to fully control how every tool event renders. 'post' results post a discrete message (closing/reopening the streaming session when needed); 'stream' results push a task_update/plan_update into the active streaming widget; undefined skips the event. Every ToolDisplayEvent variant (running/result/error/approval) carries a stable toolCallId so user code can correlate events for the same tool invocation (e.g. as the id on a streamed task_update so the SDK updates a row in place rather than appending a new one).

      'timeline' and 'grouped' require streaming: true and fall back to 'cards' with a one-time warn if not enabled. 'cards'/'text' work under both streaming modes — with streaming: true, the driver closes the streaming session around each card, posts it, and reopens on the next chunk. Approve/deny prompts always render as a separate Block Kit card regardless of mode, since inline task entries and plain text can't carry interactive buttons.

      Deprecation (no breaking changes): ChannelAdapterConfig.cards: boolean and ChannelAdapterConfig.formatToolCall are now @deprecated and surface in IDEs with a strikethrough. Both still work at runtime — they're mutually exclusive with toolDisplay at the type level. When toolDisplay is not set: cards: true resolves to toolDisplay: 'cards', cards: false resolves to toolDisplay: 'text', and formatToolCall is shimmed into an equivalent ToolDisplayFn that only fires on result/error events. Migrate at your leisure: cards: falsetoolDisplay: 'text', and formatToolCall: ({ toolName, result }) => msgtoolDisplay: event => event.kind === 'result' ? { kind: 'post', message: msg } : undefined.

    • typingStatus customization — new ChannelAdapterConfig.typingStatus (boolean | (chunk, ctx) => string | false, default true). Set to false to suppress all typing indicators (useful when a live streaming widget already conveys progress), or pass a function to set custom copy per chunk. Compose with the exported defaultTypingStatus helper to fall back to built-in defaults for chunks you don't handle.

    • Signal-aware message boundaries — when a data-user-message signal echoes into the stream mid-reply, any in-flight text is flushed first so the agent's response renders as a new message after the user's signal instead of streaming into the prior reply.

    • Stronger stay-silent prompt — the channel input processor's non-DM system message now explicitly calls out anti-patterns (bracketed status notes, "Got it"/"Noted" acknowledgments, apologizing for silence) and points the model at add_reaction for silent acknowledgments. Empty responses are framed as a first-class action rather than a fallback.

    • Slack DM thread routing — each Slack thread (including top-level DMs) now maps to its own Mastra thread. Previously, replies and tool-approval clicks in a top-level DM could be routed into a sub-thread keyed by the bot's last message, causing follow-ups to thread under that message and tool approvals to fail to find the pending approval.

    • Parallel same-tool approval — fixed a bug where two parallel calls to the same tool with requireApproval: true clobbered each other's pending entry, so only the most recent could be approved.

    • Tool error rendering — failing tools now emit a closing task update in 'timeline'/'grouped' modes (previously the row stayed in_progress and rendered as ⚠ at session close) and edit their card in 'cards'/'hidden' modes. The error text is inlined into the task details (with a ⚠ glyph) while the task itself stays status: 'complete' so a single tool failure doesn't flip the overall plan header to an error state.

    • Observational-memory lifecycle in streaming widgetsdata-om-buffering-* and data-om-activation chunks are routed into the active streaming session in 'timeline'/'grouped' modes as their own task rows (e.g. Saved to memory (10x) with 12.4k → 1.2k tokens), so memory work is visible alongside tool calls. Consecutive observation activations within a session coalesce into a single Recalled memory (Nx) row with running totals instead of stacking — reflection runs often fire several activations back-to-back. The plan title is set to Updating memory on the first OM event so memory-only runs don't show the chat-SDK default of Thinking completed. OM buffering runs async in the background, so any still-in_progress OM task is optimistically marked complete when the streaming session closes — without this, the chat-SDK plan widget would flip the "Saving to memory…" row to an error icon when the stream ends before the buffer flush resolves. In non-plan modes ('cards'/'text'/'hidden'/ToolDisplayFn) OM events are skipped entirely — a phantom Plan widget showing only memory rows would be inconsistent with the mode contract. Approval plan_update/task_update rows are also gated by 'timeline'/'grouped' for the same reason; non-plan modes now post the approval card directly without flashing a single-row Plan widget that closes immediately after.

    • Logger propagation — the Mastra logger is now propagated into AgentChannels on register so channel-level logs flow through the configured logger.

    • Internal refactor (no public API change)consumeAgentStream now dispatches to one of two focused drivers (streaming vs static) instead of switching on toolDisplay inside a single 700-line loop. Tool-call correlation moved into a ToolTracker helper and observational-memory rendering into a dedicated renderOmTaskUpdate helper, both shared between drivers. Invalid combinations now warn and downgrade: streaming: false + 'timeline'/'grouped' falls back to 'cards'. streaming: true + 'cards'/'text' is now valid and uses the streaming driver's close/post/reopen lifecycle.

    import { Agent } from '@mastra/core/agent';
    import { defaultTypingStatus } from '@mastra/core/channels';
    import { createDiscordAdapter } from '@mastra/discord';
    
    const agent = new Agent({
      name: 'support-bot',
      channels: {
        adapters: {
          discord: {
            adapter: createDiscordAdapter(),
            streaming: true,
            toolDisplay: 'grouped', // 'cards' | 'text' | 'timeline' | 'grouped' | 'hidden' | ToolDisplayFn
            typingStatus: false, // suppress typing indicator when the widget already shows progress
            // Custom typing status per chunk; fall back to defaults for everything else.
            // typingStatus: (chunk, ctx) => {
            //   if (chunk.type === 'tool-call' && chunk.payload.toolName === 'searchDocs') {
            //     return 'is searching docs…';
            //   }
            //   return defaultTypingStatus(chunk, ctx);
            // },
            // Custom tool rendering via the function form: skip the running message,
            // post a single line on result. `undefined` skips rendering that event.
            // toolDisplay: event => {
            //   if (event.kind !== 'result') return undefined;
            //   return { kind: 'post', message: `🛠 ${event.toolName} → ${event.resultText}` };
            // },
          },
        },
      },
    });
  • Client-side tools now appear in your traces when observability is configured. When an agent calls a tool that executes in the browser via @mastra/client-js, a CLIENT_TOOL_CALL span is recorded on the server trace so you can see which client tools were invoked, what arguments they received, and how they relate to the rest of the agent run. (#16425)

    Tools also gain an observe helper on their execution context for recording child spans and logs from inside execute:

    execute: async ({ userId }, { observe }) => {
      observe.log('info', 'fetching user', { userId });
      return observe.span('fetch user', () => fetch(`/api/users/${userId}`));
    };
  • Added signal delivery option attributes API that conditionally merges branch attributes based on whether a signal is delivered to an active agent run (ifActive.attributes) or an idle run (ifIdle.attributes). This enables contextual signal delivery — for example, tagging user messages as while-active when the agent is actively working. (#16923)

  • Add per-provider capability files and auto mode for observeAttachments (#16922)

    • Generate per-provider capability files (e.g. capabilities/openai.json) alongside the model router registry, sourced from models.dev API
    • Export modelSupportsAttachments(modelRouterId) from @mastra/core/llm to check whether a model supports image/file attachments
    • Extend observeAttachments config to accept 'auto' in addition to boolean | string[]
    • When set to 'auto', the observer resolves the model (including function-based models) and checks the capability registry before deciding to forward or drop attachment parts
Patch Changes
  • Update provider registry and model documentation with latest models and providers (cfa2e3a)

  • Added sub-agent token usage to onDelegationComplete results, so apps can track per-sub-agent token costs (#15825)

  • Fixed thread subscription streams stalling or deadlocking when multiple consumers observe the same active run. Thread streams are now multicast to subscribers so each subscriber receives the run without competing for the underlying stream, and follow-up messages can continue while a subscription is active. (#16946)

  • Connecting or disconnecting an agent through a channel (e.g. Slack) now requires the same write permission as editing the underlying stored agent. The check runs on POST /channels/:platform/connect and POST /channels/:platform/:agentId/disconnect whenever the target agent has a record in the stored-agents store. Callers without write access receive a 404 Not found, matching the behavior of the stored-agent edit routes. Agents defined in code (no stored-agents record) are unaffected and continue to honor only the route's existing auth requirement. (#16949)

    The caller must either own the stored agent, have admin bypass, or hold agents:edit (or a scoped agents:edit:<agentId>).

    POST /channels/slack/connect
    Authorization: Bearer <token-with-agents:edit>
    Content-Type: application/json
    
    { "agentId": "support-bot" }
    POST /channels/slack/support-bot/disconnect
    Authorization: Bearer <token-with-agents:edit>

    @mastra/core is bumped as a patch to ship the regenerated permission definitions that back this check.

  • Made provider capabilities loading lazy instead of bulk-syncing on every registry access. Removed subscription setup from harness switchMode() — subscriptions are now lazily ensured at send time. Added 10s TTL cache and invalidation method for listAvailableModels(). (#17008)

  • Preserve this when resolving permissions for the "View as role" picker in buildCapabilities. Class-based RBAC providers (e.g. @mastra/auth-workos) read state from this inside getPermissionsForRole, but the method was being detached to a bare variable before invocation. This caused a TypeError: Cannot read properties of undefined (reading 'options') to be logged on every authenticated request and silently emptied the available roles list. The error was swallowed by a surrounding try/catch so admins saw an empty picker instead of a crash. (#17112)

  • Fixed workflow.parallel() type-checking for steps that declare requestContextSchema. (#16989) Steps with matching request context now type-check correctly. Steps with mismatched request context still fail with a type error. Fixes #16975.

  • Fixed split-brain broker election race in UnixSocketPubSub. When a broker process dies and multiple clients recover concurrently, an exclusive lock file now serializes the election so exactly one process becomes the new broker. (#16955)

  • Fixed a crash that could occur when background execution is enabled for tools with Zod v3 input schemas. (#16915)

    Tools with Zod v3, Zod v4, and JSON Schema input definitions now work consistently with background execution.

  • Fixed processor workflow steps so sendSignal is available when processors inject Agent signals, and updated Observational Memory temporal gap markers to use Agent signals. (#16927)

  • Fixed an issue where thread subscriptions could appear idle after a run finished. Subsequent runs now stream promptly, and post-finish signals correctly start from an idle state. (#16928)

  • Fixed agent responses being ordered before the user message that triggered them in long conversations. This prevents duplicate tool calls in the next step. This regression started in 1.35.0. Fixes https://github.com/mastra-ai/mastra/issues/16893. (#16913)

  • Fixed agent crash when backgroundTasks.enabled: true is combined with a tool whose inputSchema uses Zod's .refine() or .superRefine(). On Zod v4, such agents threw Cannot overwrite keys on object schemas containing refinements on repeated invocations and became unusable. Tools with refined input schemas now work with background tasks. (#16966)

  • UnixSocketPubSub: skip serialization when broker has 0 remote clients, lazily build ServerFrame only when a subscribed client exists, and automatically elect a new broker with resubscription when the active broker disconnects. (#16939)

  • Fixed v1 agent tools to preserve legacy tool result output while still allowing usage data in delegation hooks. (#17070)

  • Fixed thread stream subscriptions so streamed agent responses are saved to memory while still supporting multiple subscribers. (#16982)

  • Gate stored-workspace handlers by author. Previously any authenticated caller within a tenant could list, read, update, or delete another user's workspace. (#16974)

    Behavior changes

    • POST /stored/workspaces — server stamps authorId from the authenticated caller; any body-provided authorId is ignored.
    • GET /stored/workspaces/:id, PATCH /stored/workspaces/:id, DELETE /stored/workspaces/:id — return 404 Not found unless the caller is the owner, an admin (*), or holds stored-workspaces:<action>[:<id>].
    • GET /stored/workspaces — filters to the caller's own rows plus legacy unowned records; admins still see every row.
    • Legacy workspaces created before this change (no authorId) remain accessible to any authenticated caller for backwards compatibility.

    Example

    // Client POST body — authorId is ignored if sent
    await fetch('/stored/workspaces', {
      method: 'POST',
      body: JSON.stringify({ name: 'My workspace', authorId: 'someone-else' }),
    });
    
    // Stored row — authorId is stamped from the authenticated caller
    // {
    //   id: 'my-workspace',
    //   name: 'My workspace',
    //   authorId: 'user_abc123', // from requestContext, NOT from body
    //   ...
    // }

    Migration

    • Existing rows with authorId === null/undefined remain readable/writable by any authenticated caller — no action required for backwards compatibility.
    • To lock down legacy rows, backfill authorId directly in the workspaces table with the original creator's id.
    • For service accounts or tooling that need cross-user access, grant stored-workspaces:* (or per-id stored-workspaces:<action>:<id>) instead of relying on the legacy unowned bypass.
    • Admins (callers with *) continue to see and mutate every row regardless of authorId.

    The @mastra/core patch regenerates permissions.generated.ts to include the auth and infrastructure resources that already had routes on main.

  • Agent Builder action routes (/agent-builder/*) are now registered automatically through the standard server route pipeline. Any adapter built on @mastra/server (Hono, Express, Fastify, Koa, etc.) serves the 15 /agent-builder/* endpoints without consumers wiring them manually. (#17085)

    Example

    import { MastraClient } from '@mastra/client-js';
    
    const client = new MastraClient({ baseUrl: 'http://localhost:4111' });
    
    // `/agent-builder/*` routes are now reachable out-of-the-box
    const actions = await client.getAgentBuilderActions();
    
    const action = client.getAgentBuilderAction('generate-agent');
    const { runId } = await action.createRun();
    const result = await action.startAsync({ inputData: { prompt: 'Build me an agent' } }, runId);

    Why

    Previously, AGENT_BUILDER_ROUTES was a type-only entry in the route registry to keep @mastra/agent-builder out of Cloudflare worker bundles. Consumers had to register the routes themselves to expose Agent Builder functionality. Lazy-loading of @mastra/agent-builder is preserved — handlers still resolve the workflow module on first request via dynamic import(), so Cloudflare bundles are unaffected.

    New EE permissions

    The following permissions are added to the EE registry. RBAC consumers with strict allowlists must grant these to retain access to builder action routes:

    • agent-builder:read
    • agent-builder:write
    • agent-builder:execute

    Two legacy stream routes (STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE, OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE) are now registered through the standard pipeline as well.

  • Fixed a race that could cause an immediate auto-resume of a suspended tool call to fail on some storage backends. Resume now succeeds reliably whether the underlying storage is fast or slow. (#17015)

    Fixes #16158.

  • Fixed a race in Harness sendMessage where a phantom agent_end: 'complete' event could fire before any chunks arrived. Subscribers, such as apps running on Cloudflare Workers or Durable Objects, will no longer miss text deltas, messages, or tool events when an agent run completes. (#17024)

    The cause was AgentThreadStreamRuntime.subscribeToThread's activeRunId() returning null during the gap between sendSignal reserving a runId and registerRun populating the stream record, which made waitForCurrentThreadStreamIdle() exit immediately and sendMessage emit a synthetic agent_end. The subscriber now treats both a reserved-but-not-yet-registered local run and an active remote run as live, matching sendSignal's own behavior.

  • Suppressed noisy gateway fetch errors when models.dev is unreachable. The registry no longer retries or logs errors on network failure since all model data is already bundled at publish time. (#16984)

@mastra/acp@0.2.0
Minor Changes
  • Added programmatic model selection for ACP agents using the model option. (#17010)

    You can now set the model directly when creating AcpAgent or createACPTool, instead of relying on environment variables.

    const codeAgent = new AcpAgent({
      id: 'code-agent',
      description: 'ACP-compatible coding agent',
      command: 'claude',
      args: ['--acp'],
      model: 'claude-sonnet-4-20250514',
    });

    Discover available models with getAvailableModels() and change the model at runtime with setModel(). Invalid model IDs throw a descriptive error listing valid options.

Patch Changes
  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/client-js@1.21.0
Minor Changes
  • Client-side tool tracing is now built in. When server-side observability is configured, the SDK automatically measures execution duration and ships it back to the server. To add child spans and structured logs from inside your tool's execute(input, context) function, use the observe helper on the execution context: (#16425)

    execute: async ({ userId }, { observe }) => {
      observe.log('info', 'fetching user', { userId });
      return observe.span('fetch user', () => fetch(`/api/users/${userId}`));
    };

    The createTool() helper now calls execute(input, context) so client tools receive the same execution context shape as core tools.

Patch Changes
  • Fixed client-side tools getting stuck in input-available state in React's useChat messages. After a client tool finished executing, the React UI never observed a terminal tool-result (or tool-error) chunk for it, so the matching dynamic-tool part stayed at state: 'input-available' indefinitely. The client now emits a synthetic Mastra-shaped terminal chunk into the streamed response right after the client tool resolves or rejects, so the React reducer correctly flips the part to output-available (or output-error) and renders the tool result. (#16916)

    Also fixed the client stream parser so final tool-call chunks are not treated as partial streaming tool calls while preparing client-tool continuation messages.

  • Port the yj/magnificent-marquess frontend stack onto rain-purpose. (#17105)

    • @mastra/client-js: new ToolProvider resource and a getModelPolicy accessor on the root client. Route types regenerated for the new endpoints.
    • @internal/playground: Agent Builder routes (agents, skills, infrastructure, favorite, library) wired into the router, RoutePermissionGuard and RoleImpersonationProvider applied to the app shell, new login layout, role-impersonation banner, useRestoreFocus hook, StudioIndexRedirect home, and supporting tweaks across agents, browser view, LLM, and CMS surfaces.

    Existing client-tools-on-signals work and the unrouted Agent Builder view/edit pages are preserved.

  • Made optional memory response fields optional in server schemas and generated client types. (#17070)

  • Add StoredSkill.favorite() and StoredSkill.unfavorite() methods, mirroring the existing StoredAgent favorite API. Both are idempotent and call PUT/DELETE /api/stored/skills/:id/favorite. (#17101)

  • Agent Builder action routes (/agent-builder/*) are now registered automatically through the standard server route pipeline. Any adapter built on @mastra/server (Hono, Express, Fastify, Koa, etc.) serves the 15 /agent-builder/* endpoints without consumers wiring them manually. (#17085)

    Example

    import { MastraClient } from '@mastra/client-js';
    
    const client = new MastraClient({ baseUrl: 'http://localhost:4111' });
    
    // `/agent-builder/*` routes are now reachable out-of-the-box
    const actions = await client.getAgentBuilderActions();
    
    const action = client.getAgentBuilderAction('generate-agent');
    const { runId } = await action.createRun();
    const result = await action.startAsync({ inputData: { prompt: 'Build me an agent' } }, runId);

    Why

    Previously, AGENT_BUILDER_ROUTES was a type-only entry in the route registry to keep @mastra/agent-builder out of Cloudflare worker bundles. Consumers had to register the routes themselves to expose Agent Builder functionality. Lazy-loading of @mastra/agent-builder is preserved — handlers still resolve the workflow module on first request via dynamic import(), so Cloudflare bundles are unaffected.

    New EE permissions

    The following permissions are added to the EE registry. RBAC consumers with strict allowlists must grant these to retain access to builder action routes:

    • agent-builder:read
    • agent-builder:write
    • agent-builder:execute

    Two legacy stream routes (STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE, OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE) are now registered through the standard pipeline as well.

  • Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)

    Enable automatic reconnection with subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).

  • Fixed clientTools being silently dropped — and never executed — on thread-backed chats. When a chat had a threadId, the React useChat hook routed messages through the new agent signals path but did not pass the clientTools map into the signal startup flow, so client-side tools were unavailable when the model requested them. (#16540)

    The signals path now carries clientTools and other per-send stream options on sendSignal. When the subscribed stream finishes with tool-calls, the client executes matching local tools with observability support, emits tool result chunks, and posts a continuation with the assistant tool-call messages plus tool-result messages so the run resumes on the same thread with the same per-send options.

@mastra/convex@1.2.0
Minor Changes
  • Convex can now persist channel installations and provider configuration. (#16718)

    import { ConvexStore } from '@mastra/convex';
    
    const storage = new ConvexStore({
      id: 'app-storage',
      deploymentUrl: process.env.CONVEX_URL!,
      adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
    });
    
    const channels = await storage.getStore('channels');
    
    await channels?.saveInstallation({
      id: 'slack-agent-1',
      platform: 'slack',
      agentId: 'agent-1',
      status: 'active',
      webhookId: 'webhook-1',
      data: { teamId: 'T123', botUserId: 'U123' },
      createdAt: new Date(),
      updatedAt: new Date(),
    });
  • Workflow schedules can now be stored in Convex. (#16710)

    import { ConvexStore } from '@mastra/convex';
    
    const storage = new ConvexStore({
      id: 'app-storage',
      deploymentUrl: process.env.CONVEX_URL!,
      adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
    });
    
    const schedules = await storage.getStore('schedules');
    
    await schedules?.createSchedule({
      id: 'daily-summary',
      target: { type: 'workflow', workflowId: 'summary-workflow' },
      cron: '0 9 * * *',
      status: 'active',
      nextFireAt: Date.now(),
      createdAt: Date.now(),
      updatedAt: Date.now(),
    });
Patch Changes
  • Improved Convex background task reliability with safer lifecycle updates, faster filtering, and smoother upgrades from older storage rows. (#16724)
@mastra/deployer@1.37.0
Patch Changes
  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/editor@0.10.0
Minor Changes
  • Ship EditorAgentBuilder and Agent Builder runtime through the @mastra/editor/ee subpath. (#16948)

    • Adds EditorAgentBuilder class and supporting types under @mastra/editor/ee (dormant unless MastraEditorConfig.builder is configured).
    • Wires builder resolution on MastraEditor: hasEnabledBuilderConfig(), resolveBuilder(), ensureBuilderWorkspaces(), and reconcileBuilderWorkspaces().
    • Adds builder defaults plumbing in the agent namespace (applyBuilderDefaults, BUILDER_BASELINE_DEFAULTS enabling observationalMemory: true by default for Builder-created agents).
    • Adds a defense-in-depth license guard inside MastraEditor.resolveBuilder() that mirrors the server-startup check in MastraServer.validateAgentBuilderLicense(). Dev environments bypass via isEEEnabled(); production without a valid MASTRA_EE_LICENSE throws [mastra/auth-ee] Agent Builder is configured but no valid EE license was found.
    • Bumps the @mastra/core peer dependency to >=1.34.0-0 <2.0.0-0 to cover the @mastra/core/agent-builder/ee and @mastra/core/auth/ee subpaths consumed by the builder runtime.

    Opt-in usage:

    import { Mastra } from '@mastra/core';
    import { MastraEditor } from '@mastra/editor';
    
    const editor = new MastraEditor({
      builder: {
        enabled: true,
        configuration: {
          agent: {
            models: { default: { provider: 'openai', modelId: 'gpt-4o-mini' } },
          },
        },
      },
    });
    
    new Mastra({ storage, editor });
    
    // Later, on demand:
    const builder = await editor.resolveBuilder();
    // `builder` is undefined when the builder is not configured/enabled.
    // In production it requires a valid MASTRA_EE_LICENSE; dev environments bypass.

    This is plumbing — no UI consumer ships in this release.

Patch Changes
  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/evals@1.2.3
Patch Changes
  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/express@1.3.24
Patch Changes
  • Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)

    registerApiRoute('/stream', {
      method: 'GET',
      handler: async c => {
        const stream = await agent.stream(prompt, {
          abortSignal: c.req.raw.signal,
        });
    
        return stream.toTextStreamResponse();
      },
    });
  • Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)

    Enable automatic reconnection with subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).

@mastra/fastembed@1.1.1
Patch Changes
  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/fastify@1.3.24
Patch Changes
  • Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)

    registerApiRoute('/stream', {
      method: 'GET',
      handler: async c => {
        const stream = await agent.stream(prompt, {
          abortSignal: c.req.raw.signal,
        });
    
        return stream.toTextStreamResponse();
      },
    });
@mastra/files-sdk@0.2.0
Minor Changes
  • Added @mastra/files-sdk workspace filesystem provider — a unified storage adapter backed by FilesSDK. Supports any FilesSDK adapter (S3, R2, GCS, Azure Blob, Vercel Blob, local filesystem, and more) through a single FilesSDKFilesystem class that implements the WorkspaceFilesystem interface. (#17027)

    Usage

    import { Files } from 'files-sdk';
    import { s3 } from 'files-sdk/s3';
    import { FilesSDKFilesystem } from '@mastra/files-sdk';
    
    const files = new Files({ adapter: s3({ bucket: 'my-bucket', region: 'us-east-1' }) });
    
    const filesystem = new FilesSDKFilesystem({ files });

    Swap adapters without changing code — just replace s3() with r2(), gcs(), azure(), fs(), etc.

Patch Changes
@mastra/hono@1.4.19
Patch Changes
  • Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)

    registerApiRoute('/stream', {
      method: 'GET',
      handler: async c => {
        const stream = await agent.stream(prompt, {
          abortSignal: c.req.raw.signal,
        });
    
        return stream.toTextStreamResponse();
      },
    });
  • Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)

    Enable automatic reconnection with subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).

  • Fixed SSE streams so browser clients can connect to long-lived subscriptions before the first stream event is available. (#16540)

@mastra/koa@1.5.7
Patch Changes
  • Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)

    registerApiRoute('/stream', {
      method: 'GET',
      handler: async c => {
        const stream = await agent.stream(prompt, {
          abortSignal: c.req.raw.signal,
        });
    
        return stream.toTextStreamResponse();
      },
    });
  • Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)

    Enable automatic reconnection with subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).

@mastra/mcp@1.8.1
Patch Changes
  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)
@mastra/memory@1.20.0
Minor Changes
  • Add per-provider capability files and auto mode for observeAttachments (#16922)
    • Generate per-provider capability files (e.g. capabilities/openai.json) alongside the model router registry, sourced from models.dev API
    • Export modelSupportsAttachments(modelRouterId) from @mastra/core/llm to check whether a model supports image/file attachments
    • Extend observeAttachments config to accept 'auto' in addition to boolean | string[]
    • When set to 'auto', the observer resolves the model (including function-based models) and checks the capability registry before deciding to forward or drop attachment parts
Patch Changes
  • Fixed observational memory so in-progress messages are not removed during observation cleanup. (#16913)

  • Removed zod as a required peer dependency. Internal schemas now use plain JSON Schema objects instead of zod runtime. (#16726)

  • Fixed processor workflow steps so sendSignal is available when processors inject Agent signals, and updated Observational Memory temporal gap markers to use Agent signals. (#16927)

@mastra/observability@1.14.0
Minor Changes
  • Support ingesting client-side tool telemetry. Spans, logs, and duration metrics captured by the client SDK during tool execution are forwarded through the observability bus to your existing exporters. Client tool durations are reported via the existing mastra_tool_duration_ms metric with a toolType: 'client' label to distinguish them from server-side tool durations. (#16425)
Patch Changes
  • Paused observability uploads after invalid credentials so exporters stop repeatedly sending unauthorized requests. (#16743)
@mastra/playground-ui@30.0.0
Minor Changes
  • Added a Drawer component — a panel that slides in from any edge of the screen with swipe-to-dismiss gestures. (#16958)

    The Drawer can be anchored to any of the four screen edges and supports snap points, nested stacking, controlled state, non-modal mode, swipe-to-open areas, and detached triggers.

    import {
      Drawer,
      DrawerTrigger,
      DrawerContent,
      DrawerHeader,
      DrawerTitle,
      DrawerDescription,
      DrawerFooter,
      DrawerClose,
      Button,
    } from '@mastra/playground-ui';
    
    <Drawer side="right">
      <DrawerTrigger asChild>
        <Button>Open</Button>
      </DrawerTrigger>
      <DrawerContent>
        <DrawerHeader>
          <DrawerTitle>Library</DrawerTitle>
          <DrawerDescription>A panel that slides in from the right edge.</DrawerDescription>
        </DrawerHeader>
        <DrawerFooter>
          <DrawerClose asChild>
            <Button variant="outline">Close</Button>
          </DrawerClose>
        </DrawerFooter>
      </DrawerContent>
    </Drawer>;
  • Added a reusable HoverCard component (HoverCard, HoverCardTrigger, HoverCardContent) built on Base UI. You can now use these exported components to add hover card interactions anywhere in your UI. (#16919)

    import { HoverCard, HoverCardTrigger, HoverCardContent } from '@mastra/playground-ui';
    
    <HoverCard>
      <HoverCardTrigger>Weather Agent</HoverCardTrigger>
      <HoverCardContent>Answers questions about current conditions and forecasts.</HoverCardContent>
    </HoverCard>;
Patch Changes
  • Migrated the Switch component to Base UI for smoother animations and consistent behavior. No API changes — checked, defaultChecked, onCheckedChange, and disabled work exactly as before. (#16891)

  • Improved the Select component by migrating it to Base UI for more reliable positioning and accessibility. The public API (Select, SelectTrigger, SelectContent, SelectItem, SelectValue, SelectGroup) is unchanged, so no consumer updates are needed. (#16918)

  • Added DataList.RowStatic, a non-interactive row primitive. It renders a row that looks identical to other list rows but does not respond to clicks and shows no hover/focus state — use it alongside DataList.RowButton / DataList.RowLink when only some rows are clickable (e.g. error or placeholder entries in an otherwise navigable list). (#16970)

    {
      rows.map(row =>
        row.href ? (
          <DataList.RowLink key={row.id} to={row.href} LinkComponent={Link}>
            {row.cells}
          </DataList.RowLink>
        ) : (
          <DataList.RowStatic key={row.id}>{row.cells}</DataList.RowStatic>
        ),
      );
    }
  • Added DataList primitives and props for building selection-aware, condensed list rows that match the Traces/Logs visual style. (#16820)

    New cells on DataList:

    • IdCell — compact mono cell that truncates long IDs to 8 chars.
    • MonoCell — compact mono + truncate text cell (for input previews, JSON summaries, etc.).
    • DateCell — compact date cell rendering "Today" or "MMM dd".
    • TimeCell — compact mono time cell rendering HH:mm:ss.SSS with the millisecond portion tinted.
    • SelectCell — labelled checkbox cell with a shift-key range-select handler.
    • TopSelectCell — header version with indeterminate support for "select all".
    • TopCells — non-interactive header sibling of RowButton, for hosting top cells beside a leading select cell.

    New props on DataList.RowButton and DataList.RowLink:

    • flushLeft — drops the default left margin when wrapped beside a leading cell.
    • colStart — places the row starting at a column line (e.g. colStart={2} to leave column 1 for a leading cell).
    • featured — applies the highlighted background to mark the active row.

    New props on existing wrappers:

    • as on DataList.Cell and DataList.TopCell — render the cell as any HTML element (e.g. <label> so the whole cell is clickable).
    • hasLeadingCell on DataList.Top — drops default gap and left padding so a leading cell sits flush, mirroring how Row + RowButton compose.

    Example — selection row with a checkbox in column 1 and an interactive button spanning the rest:

    <DataList.Row>
      <DataList.SelectCell checked={isSelected} onToggle={shiftKey => toggle(id, shiftKey)} />
      <DataList.RowButton flushLeft colStart={2} featured={isFeatured} onClick={onRowClick}>
        <DataList.IdCell id={item.id} />
        <DataList.MonoCell>{item.input}</DataList.MonoCell>
      </DataList.RowButton>
    </DataList.Row>

    Internally the Traces and Logs list views now use the shared primitives — no behavior change for those consumers.

  • Added support for trailing cells in DataList rows. DataList.RowButton and DataList.RowLink now accept colEnd and flushRight (mirrors of the existing colStart/flushLeft), so a row can sit beside a non-interactive trailing cell (e.g. an actions column) and stay aligned with the header. Rows wrapped in DataList.Row now render a full-width separator that extends through the leading and trailing cells. DataList.MonoCell also gained an optional height prop so non-compact lists can use it without forcing compact padding. (#16888)

    Usage

    <DataList.Row>
      <DataList.RowButton flushLeft flushRight colEnd={-2} onClick={onClick}>
        {/* main row content */}
      </DataList.RowButton>
      <DataList.Cell>
        {/* trailing actions, e.g. icon buttons */}
      </DataList.Cell>
    </DataList.Row>
    
    <DataList.MonoCell height="default">long mono text…</DataList.MonoCell>
  • Migrated the Label component off Radix UI. It now renders a native <label> element with the same props and styling — htmlFor, className, and children behave exactly as before. (#16892)

  • Fixed Studio Settings page (and other default-height PageLayout pages) clipping their content with no scrollbar on viewports shorter than the form. Users on short laptop screens (under ~991px tall) could not reach the Save button under the Mastra Connection headers form, making it impossible to apply changes. Default-height PageLayout pages now grow with their content and scroll through the studio chrome wrapper; height="full" pages (Logs, Traces, Metrics, etc.) are unchanged. (#16999)

  • Restyled scrollbars across the studio UI to match the design system — thin, themed thumb on a transparent track — replacing the default OS scrollbars that clashed with dark and light surfaces. (#16918)

  • Exported ContextMenu from the package entry so it can be imported alongside other Base UI primitives. (#17062)

  • Design-system additions to support theming: (#17059)

    • Avatar now accepts optional color and textColor props for per-instance tinting, and falls back to the initial when the image fails to load.
    • Searchbar accepts an optional className to let consumers tune layout without forking.
    • TabList accepts a style prop and the active-tab indicator now reads from the --tab-indicator-color CSS variable, letting parents theme the indicator (e.g. per-agent accent color).
    • stringToColor now accepts any number for the lightness argument and defaults to 90 instead of 75 for a lighter fallback chip.
    • Global body rule enables font-smoothing / -webkit-font-smoothing for crisper UI text.
  • Removed EntryList and its sub-components (EntryList.Header, EntryList.Entries, EntryList.Entry, EntryList.EntryText, EntryList.Pagination, EntryList.NoMatch, EntryListSkeleton, etc.) from the public API. All in-repo list views have migrated to DataList, which is the recommended replacement. (#16910)

    Migration:

    // Before
    import { EntryList, EntryListSkeleton } from '@mastra/playground-ui';
    
    <EntryList>
      <EntryList.Trim>
        <EntryList.Header columns={columns} />
        <EntryList.Entries>
          {items.map(item => (
            <EntryList.Entry key={item.id} columns={columns} entry={item} onClick={}>
              {columns.map(col => <EntryList.EntryText key={col.name}>{item[col.name]}</EntryList.EntryText>)}
            </EntryList.Entry>
          ))}
        </EntryList.Entries>
      </EntryList.Trim>
      <EntryList.Pagination …/>
    </EntryList>
    
    // After
    import { DataList } from '@mastra/playground-ui';
    
    <DataList columns={gridColumns}>
      <DataList.Top>
        {columns.map(col => <DataList.TopCell key={col.name}>{col.label}</DataList.TopCell>)}
      </DataList.Top>
      {items.map(item => (
        <DataList.RowButton key={item.id} onClick={}>
          {columns.map(col => <DataList.Cell key={col.name}>{item[col.name]}</DataList.Cell>)}
        </DataList.RowButton>
      ))}
      <DataList.Pagination …/>
    </DataList>
  • Improved the Checkbox component by migrating it to Base UI. The public API is unchanged — checked (including the 'indeterminate' value), defaultChecked, onCheckedChange, and disabled all behave as before. (#16905)

  • Fixed MetricsDataTable sticky header and column backgrounds to use surface3 token, matching DashboardCard surface (#16828)

  • Improved the RadioGroup component by migrating it to Base UI. The public API (RadioGroup, RadioGroupItem, value, onValueChange, disabled) is unchanged. Also fixes the radio indicator sizing/centering — the control now stays square and the inner dot is properly centered. (#16904)

@mastra/posthog@1.0.26
Patch Changes
  • Fixed negative PostHog cost calculations when cache tokens are present. (#16876)
@mastra/react@0.4.1
Patch Changes
  • Added support for reasoning-start and reasoning-end stream chunks so reasoning blocks are opened and closed with provider metadata preserved. (#17061)

  • Fixed clientTools being silently dropped — and never executed — on thread-backed chats. When a chat had a threadId, the React useChat hook routed messages through the new agent signals path but did not pass the clientTools map into the signal startup flow, so client-side tools were unavailable when the model requested them. (#16540)

    The signals path now carries clientTools and other per-send stream options on sendSignal. When the subscribed stream finishes with tool-calls, the client executes matching local tools with observability support, emits tool result chunks, and posts a continuation with the assistant tool-call messages plus tool-result messages so the run resumes on the same thread with the same per-send options.

@mastra/sentry@1.1.0
Minor Changes
  • Added the gen_ai.conversation.id span attribute to the Sentry exporter, sourced from metadata.threadId. Spans from the same chat thread now group together in Sentry's Conversations view (part of AI Agent Monitoring). (#16925)

    const agent = mastra.getAgent('chat');
    
    // Pass threadId as before — the Sentry exporter now emits it as gen_ai.conversation.id
    await agent.generate('Hello', {
      memory: { thread: 'thread-123', resource: 'user-1' },
    });
Patch Changes
@mastra/server@1.37.0
Minor Changes
  • Connecting or disconnecting an agent through a channel (e.g. Slack) now requires the same write permission as editing the underlying stored agent. The check runs on POST /channels/:platform/connect and POST /channels/:platform/:agentId/disconnect whenever the target agent has a record in the stored-agents store. Callers without write access receive a 404 Not found, matching the behavior of the stored-agent edit routes. Agents defined in code (no stored-agents record) are unaffected and continue to honor only the route's existing auth requirement. (#16949)

    The caller must either own the stored agent, have admin bypass, or hold agents:edit (or a scoped agents:edit:<agentId>).

    POST /channels/slack/connect
    Authorization: Bearer <token-with-agents:edit>
    Content-Type: application/json
    
    { "agentId": "support-bot" }
    POST /channels/slack/support-bot/disconnect
    Authorization: Bearer <token-with-agents:edit>

    @mastra/core is bumped as a patch to ship the regenerated permission definitions that back this check.

  • Gate stored-workspace handlers by author. Previously any authenticated caller within a tenant could list, read, update, or delete another user's workspace. (#16974)

    Behavior changes

    • POST /stored/workspaces — server stamps authorId from the authenticated caller; any body-provided authorId is ignored.
    • GET /stored/workspaces/:id, PATCH /stored/workspaces/:id, DELETE /stored/workspaces/:id — return 404 Not found unless the caller is the owner, an admin (*), or holds stored-workspaces:<action>[:<id>].
    • GET /stored/workspaces — filters to the caller's own rows plus legacy unowned records; admins still see every row.
    • Legacy workspaces created before this change (no authorId) remain accessible to any authenticated caller for backwards compatibility.

    Example

    // Client POST body — authorId is ignored if sent
    await fetch('/stored/workspaces', {
      method: 'POST',
      body: JSON.stringify({ name: 'My workspace', authorId: 'someone-else' }),
    });
    
    // Stored row — authorId is stamped from the authenticated caller
    // {
    //   id: 'my-workspace',
    //   name: 'My workspace',
    //   authorId: 'user_abc123', // from requestContext, NOT from body
    //   ...
    // }

    Migration

    • Existing rows with authorId === null/undefined remain readable/writable by any authenticated caller — no action required for backwards compatibility.
    • To lock down legacy rows, backfill authorId directly in the workspaces table with the original creator's id.
    • For service accounts or tooling that need cross-user access, grant stored-workspaces:* (or per-id stored-workspaces:<action>:<id>) instead of relying on the legacy unowned bypass.
    • Admins (callers with *) continue to see and mutate every row regardless of authorId.

    The @mastra/core patch regenerates permissions.generated.ts to include the auth and infrastructure resources that already had routes on main.

  • Agent Builder action routes (/agent-builder/*) are now registered automatically through the standard server route pipeline. Any adapter built on @mastra/server (Hono, Express, Fastify, Koa, etc.) serves the 15 /agent-builder/* endpoints without consumers wiring them manually. (#17085)

    Example

    import { MastraClient } from '@mastra/client-js';
    
    const client = new MastraClient({ baseUrl: 'http://localhost:4111' });
    
    // `/agent-builder/*` routes are now reachable out-of-the-box
    const actions = await client.getAgentBuilderActions();
    
    const action = client.getAgentBuilderAction('generate-agent');
    const { runId } = await action.createRun();
    const result = await action.startAsync({ inputData: { prompt: 'Build me an agent' } }, runId);

    Why

    Previously, AGENT_BUILDER_ROUTES was a type-only entry in the route registry to keep @mastra/agent-builder out of Cloudflare worker bundles. Consumers had to register the routes themselves to expose Agent Builder functionality. Lazy-loading of @mastra/agent-builder is preserved — handlers still resolve the workflow module on first request via dynamic import(), so Cloudflare bundles are unaffected.

    New EE permissions

    The following permissions are added to the EE registry. RBAC consumers with strict allowlists must grant these to retain access to builder action routes:

    • agent-builder:read
    • agent-builder:write
    • agent-builder:execute

    Two legacy stream routes (STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE, OBSERVE_STREAM_LEGACY_AGENT_BUILDER_ACTION_ROUTE) are now registered through the standard pipeline as well.

Patch Changes
  • Developers can now cancel long-running custom API route work when clients disconnect. Node-based adapters pass abort signals into custom route handlers, clean up response streams correctly, and still surface upstream response body errors. (#16335)

    registerApiRoute('/stream', {
      method: 'GET',
      handler: async c => {
        const stream = await agent.stream(prompt, {
          abortSignal: c.req.raw.signal,
        });
    
        return stream.toTextStreamResponse();
      },
    });
  • Made optional memory response fields optional in server schemas and generated client types. (#17070)

  • Improved agent thread subscription resilience by keeping server streams active during idle periods and allowing the JavaScript client to reconnect when subscription streams close or resubscribe requests fail. (#17045)

    Enable automatic reconnection with subscription.processDataStream({ onChunk: chunk => console.log(chunk), reconnect: true }).

@mastra/slack@1.3.0
Minor Changes
  • Improved Slack channel UX: (#16937)

    • Flat adapter configSlackProvider now accepts per-adapter options (formatError, streaming, typingStatus, toolDisplay) directly at the top level instead of nesting under adapterConfig. The adapterConfig field still works as a deprecated fallback.
    • Breaking change — the cards: boolean and formatToolCall fields have been removed from SlackProviderConfig/SlackAdapterChannelConfig. Migrate cards: falsetoolDisplay: 'text', and formatToolCall: (info) => msgtoolDisplay: (event) => event.kind === 'result' ? { kind: 'post', message: msg } : undefined.
    • Opinionated defaultsSlackProvider now defaults streaming: true and toolDisplay: 'grouped' since the grouped "Thinking Steps" widget renders well in Slack's AI Assistant UI. When streaming: false is explicitly set, toolDisplay falls back to 'cards' since 'grouped' requires streaming. Override either per-config to restore other modes.
    • AI Assistant manifestassistant:write is now part of DEFAULT_BOT_SCOPES and the generated manifest declares the matching assistant_view feature, so newly generated app manifests support the AI Assistant surface and thread context in DMs.
    • Slack DM tool-approval routing — clicks on tool-approval cards in Slack DMs now resume the correct Mastra thread.
    import { SlackProvider } from '@mastra/slack';
    
    const slack = new SlackProvider({
      // Top-level options (preferred):
      streaming: true,
      toolDisplay: 'grouped', // or 'cards' | 'text' | 'timeline' | 'hidden' | ToolDisplayFn
    });
Patch Changes
@mastra/spanner@1.0.0
Major Changes
  • Added a Google Cloud Spanner storage adapter (@mastra/spanner) targeting the GoogleSQL dialect. The adapter implements the memory, workflows, scores, backgroundTasks, agents, mcpClients, mcpServers, skills, blobs, promptBlocks, scorerDefinitions, schedules, and observability storage domains and works with both managed Cloud Spanner instances and the local Spanner emulator. The schedules domain plugs into Mastra's built-in WorkflowScheduler for cron-driven workflow triggers, and the observability domain persists AI tracing spans in mastra_ai_spans to power the Studio traces UI (JSON containment filters compile to per-key JSON_VALUE equality and EXISTS over JSON_QUERY_ARRAY since Spanner has no @> operator). (#15955)

    import { SpannerStore } from '@mastra/spanner';
    
    const storage = new SpannerStore({
      id: 'spanner-storage',
      projectId: process.env.SPANNER_PROJECT_ID!,
      instanceId: process.env.SPANNER_INSTANCE_ID!,
      databaseId: process.env.SPANNER_DATABASE_ID!,
    });
Patch Changes
@mastra/voice-murf@0.12.1
Patch Changes
  • Replaced ky HTTP client with native fetch and built-in retry logic, removing the external dependency (#16960)
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.35.0

Highlights

FGA Route Policy Coverage + Resolver Hooks (Core/Server/WorkOS)

New FGA route policy coverage controls add guardrails for protected routes, with built-in resource route metadata resolution and resolver hooks so you can centrally map route → {resourceType, resourceId, permission} while still overriding per-route config.

Favorites Storage Domain for Agents/Skills (Core + Storage Adapters)

A new favorites storage domain lets users favorite/unfavorite stored agents and skills, and adds visibility (private|public) and favoriteCount fields so listings can be filtered and ordered by favorite state (e.g., pinFavoritedFor, favoritedOnly).

Favorites Support Across Major Storage Backends

Favorites is implemented across multiple adapters (@mastra/pg, @mastra/libsql, @mastra/mongodb, @mastra/clickhouse, @mastra/cloudflare), enabling consistent favoriting/visibility queries regardless of your chosen persistence layer.

Observational Memory: Accurate Token Estimation for Large File Parts

@mastra/memory improves local token estimation for non-image file parts (notably large PDFs), preventing observational memory from missing thresholds and replaying excessive history when provider token-count endpoints aren’t available.

Reliability Fixes for Workspaces, Workflows, and Route Matching

Multiple fixes improve correctness: workspace PATCH no longer overwrites stored values with undefined, scheduled workflows created via @mastra/core/workflows apply schedules correctly, NestJS adapter prefix matching is stricter, and a circular ESM import crash in @mastra/core/workflows/workflow is resolved.

Breaking Changes
  • None called out in this changelog.

Changelog

@mastra/core@1.35.0
Minor Changes
  • Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. (#16485)

    For example:

    import { MastraFGAWorkos } from '@mastra/auth-workos';
    import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee';
    import { createRoute } from '@mastra/server/server-adapter';
    
    const routeFGA = {
      'GET /billing/:accountId': {
        resourceType: 'account',
        resourceIdParam: 'accountId',
        permission: 'billing:read',
      },
    } satisfies Record<string, FGARouteConfig>;
    
    const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];
    
    const fga: IFGAProvider = new MastraFGAWorkos({
      apiKey: process.env.WORKOS_API_KEY!,
      clientId: process.env.WORKOS_CLIENT_ID!,
      requireForProtectedRoutes: true,
      auditProtectedRoutes: 'warn',
      resolveRouteFGA,
      validatePermissions: async permissions => {
        /* validate mappings */
      },
    });
    
    export const getProjectRoute = createRoute({
      method: 'GET',
      path: '/projects/:projectId',
      responseType: 'json',
      requiresAuth: true,
      fga: {
        resourceType: 'project',
        resourceIdParam: 'projectId',
        permission: 'projects:read',
      },
      handler: async () => {
        return { project: null };
      },
    });
  • Added a favorites storage domain that lets users mark stored agents and skills as favorites, plus visibility ('private' | 'public') and favoriteCount fields on stored agents and skills so callers can list, filter, and order by favorite state. (#16580)

    Existing rows without visibility or favoriteCount continue to work; the new fields and APIs are opt-in.

    Example

    const favorites = await storage.getStore('favorites');
    
    await favorites?.favorite({ userId: 'u1', entityType: 'agent', entityId: 'agent-123' });
    
    const favoritedIds = await favorites?.listFavoritedIds({ userId: 'u1', entityType: 'agent' });
    
    // List agents the user has favorited, surfaced first
    const { agents } = await storage.getStore('agents').list({
      pinFavoritedFor: 'u1',
      favoritedOnly: true,
    });
Patch Changes
  • Update provider registry and model documentation with latest models and providers (b661349)

  • Fixed a workspace PATCH bug in the inmemory workspace adapter: omitted config fields in a PATCH no longer overwrite previously-persisted values with undefined. (#16580)

  • Fixed active signals so they stay in the correct order when conversations are reloaded from memory. (#16623)

  • Fixed scheduled workflows created from the public @mastra/core/workflows entry point so declared schedules are applied correctly. (#16637)

  • Fixed a crash when importing @mastra/core/workflows/workflow from tests or apps, which previously failed with TypeError: Class extends value undefined is not a constructor or null (caused by a circular ESM import through the workflows barrel). (#16661)

@mastra/auth-workos@1.4.0
Minor Changes
  • Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. (#16485)

    For example:

    import { MastraFGAWorkos } from '@mastra/auth-workos';
    import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee';
    import { createRoute } from '@mastra/server/server-adapter';
    
    const routeFGA = {
      'GET /billing/:accountId': {
        resourceType: 'account',
        resourceIdParam: 'accountId',
        permission: 'billing:read',
      },
    } satisfies Record<string, FGARouteConfig>;
    
    const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];
    
    const fga: IFGAProvider = new MastraFGAWorkos({
      apiKey: process.env.WORKOS_API_KEY!,
      clientId: process.env.WORKOS_CLIENT_ID!,
      requireForProtectedRoutes: true,
      auditProtectedRoutes: 'warn',
      resolveRouteFGA,
      validatePermissions: async permissions => {
        /* validate mappings */
      },
    });
    
    export const getProjectRoute = createRoute({
      method: 'GET',
      path: '/projects/:projectId',
      responseType: 'json',
      requiresAuth: true,
      fga: {
        resourceType: 'project',
        resourceIdParam: 'projectId',
        permission: 'projects:read',
      },
      handler: async () => {
        return { project: null };
      },
    });
Patch Changes
  • Fixed WorkOS FGA missing resources so authorization checks deny access instead of surfacing provider errors. (#16485)
@mastra/clickhouse@1.8.0
Minor Changes
  • Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)

    Example

    const storage = new LibSQLStore({
      /* config */
    });
    const favorites = await storage.getStore('favorites');
    
    await favorites?.favorite({
      userId: 'user-1',
      entityType: 'agent',
      entityId: 'agent-42',
    });
Patch Changes
  • Bumped @mastra/core peer dependency floor to >=1.34.0-0 so the new @mastra/core/storage/domains/favorites subpath is available. Older @mastra/core versions don't ship the FavoritesStorage base class these adapters now extend. (#16580)
@mastra/cloudflare@1.4.0
Minor Changes
  • Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)

    Example

    const storage = new LibSQLStore({
      /* config */
    });
    const favorites = await storage.getStore('favorites');
    
    await favorites?.favorite({
      userId: 'user-1',
      entityType: 'agent',
      entityId: 'agent-42',
    });
Patch Changes
  • Bumped @mastra/core peer dependency floor to >=1.34.0-0 so the new @mastra/core/storage/domains/favorites subpath is available. Older @mastra/core versions don't ship the FavoritesStorage base class these adapters now extend. (#16580)
@mastra/libsql@1.11.0
Minor Changes
  • Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)

    Example

    const storage = new LibSQLStore({
      /* config */
    });
    const favorites = await storage.getStore('favorites');
    
    await favorites?.favorite({
      userId: 'user-1',
      entityType: 'agent',
      entityId: 'agent-42',
    });
Patch Changes
  • Bumped @mastra/core peer dependency floor to >=1.34.0-0 so the new @mastra/core/storage/domains/favorites subpath is available. Older @mastra/core versions don't ship the FavoritesStorage base class these adapters now extend. (#16580)

  • Fixed a workspace PATCH bug: omitted config fields in a PATCH no longer overwrite previously-persisted values with undefined. (#16580)

@mastra/memory@1.18.2
Patch Changes
  • Fixed thread deletion so it also clears thread-scoped observational memory. (#16628)

  • Fixed Observational Memory missing the observation threshold for messages with large file parts. Previously TokenCounter's local/sync counting path only stringified the file descriptor (type, mimeType, filename) for non-image files, so a 100KB PDF looked like ~8 tokens to OM and the conversation kept replaying the full unobserved history past every reasonable threshold. (#16562)

    TokenCounter now estimates non-image file part tokens locally from the attachment's byte size and mime type using a per-provider heuristic, mirroring the existing local image-token estimator:

    • Anthropic PDFs ≈ bytes / 3 (floor 1500)
    • Google PDFs ≈ bytes / 20 (floor 258)
    • OpenAI / unknown PDFs ≈ bytes / 4 (floor 500)
    • Text-ish mime types (text/*, JSON, XML, YAML) ≈ bytes / 4
    • Other binary ≈ bytes / 4

    URL-only file parts (no body to size) keep the previous descriptor-only local estimate. countMessagesAsync() continues to prefer provider token-count endpoints for supported providers; this change only improves the local fallback used when no provider endpoint is available.

    // Before: this PDF counted as ~8 tokens locally regardless of size, so OM never triggered.
    const part = {
      type: 'file',
      data: largePdfBase64,
      mimeType: 'application/pdf',
      filename: 'report.pdf',
    };
    // counter.countMessage(message) ≈ 8
    
    // After: estimated locally from byte size on the active provider.
    // counter.countMessage(message) ≈ tens of thousands of tokens
    //   → OM threshold trips as expected.

    The internal token-estimate cache version was bumped, which invalidates persisted estimates from older @mastra/memory releases on the next read; entries are recomputed automatically.

    Fixes https://github.com/mastra-ai/mastra/issues/16522

@mastra/mongodb@1.9.0
Minor Changes
  • Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)

    Example

    const storage = new LibSQLStore({
      /* config */
    });
    const favorites = await storage.getStore('favorites');
    
    await favorites?.favorite({
      userId: 'user-1',
      entityType: 'agent',
      entityId: 'agent-42',
    });
Patch Changes
  • Bumped @mastra/core peer dependency floor to >=1.34.0-0 so the new @mastra/core/storage/domains/favorites subpath is available. Older @mastra/core versions don't ship the FavoritesStorage base class these adapters now extend. (#16580)
@mastra/nestjs@0.1.6
Patch Changes
  • Fixed NestJS route matching so configured prefixes are enforced and partial prefix matches are ignored. For example, a prefix of /api no longer matches /apiish/agents; only /api and /api/* are treated as Mastra routes. (#16637)
@mastra/pg@1.11.0
Minor Changes
  • Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. (#16580)

    Example

    const storage = new LibSQLStore({
      /* config */
    });
    const favorites = await storage.getStore('favorites');
    
    await favorites?.favorite({
      userId: 'user-1',
      entityType: 'agent',
      entityId: 'agent-42',
    });
Patch Changes
  • Fixed a workspace PATCH bug: omitted config fields in a PATCH no longer overwrite previously-persisted values with undefined. (#16580)

  • Bumped @mastra/core peer dependency floor to >=1.34.0-0 so the new @mastra/core/storage/domains/favorites subpath is available. Older @mastra/core versions don't ship the FavoritesStorage base class these adapters now extend. (#16580)

@mastra/playground-ui@28.0.1
Patch Changes
  • Fixed the Observability traces page on storage providers that don't support the Branches list mode. The page now falls back to Traces mode, hides the Branches option in the filter, and shows a dismissible notice instead of a full-screen error. (#16601)

  • Improved the Observability traces list to make the with-subtraces view more discoverable. (#16643)

    • Added: A Level column whose icon distinguishes top-level Trace rows from nested Subtrace rows.
    • Added: A tooltip legend on the Level header showing both icons side by side.
    • Added: A standalone Show subtraces toggle next to Add Filter — off keeps the default top-level view, on includes subtraces.
    • Removed: The List mode entry from the Add Filter menu (now driven by the toggle).

    Usage: Open Observability → Traces → switch Show subtraces on. The Level column updates: top-level rows keep the Trace icon, nested rows show the Subtrace (↳) icon. The toggle is hidden automatically when the active storage provider doesn't support subtraces.

  • Removed the "Group traces by thread" option from the Observability traces page. The list now always displays a flat view of traces, without thread-id subheaders. To narrow results to a specific thread, use the Thread ID property in the Add filter menu (open Observability → Traces → Add filter → Thread ID → paste the threadId). (#16633)

  • Polished the property filter dropdown so the chevron icon next to each option keeps the same shape when its side panel opens (#16635)

@mastra/server@1.35.0
Minor Changes
  • Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. (#16485)

    For example:

    import { MastraFGAWorkos } from '@mastra/auth-workos';
    import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee';
    import { createRoute } from '@mastra/server/server-adapter';
    
    const routeFGA = {
      'GET /billing/:accountId': {
        resourceType: 'account',
        resourceIdParam: 'accountId',
        permission: 'billing:read',
      },
    } satisfies Record<string, FGARouteConfig>;
    
    const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];
    
    const fga: IFGAProvider = new MastraFGAWorkos({
      apiKey: process.env.WORKOS_API_KEY!,
      clientId: process.env.WORKOS_CLIENT_ID!,
      requireForProtectedRoutes: true,
      auditProtectedRoutes: 'warn',
      resolveRouteFGA,
      validatePermissions: async permissions => {
        /* validate mappings */
      },
    });
    
    export const getProjectRoute = createRoute({
      method: 'GET',
      path: '/projects/:projectId',
      responseType: 'json',
      requiresAuth: true,
      fga: {
        resourceType: 'project',
        resourceIdParam: 'projectId',
        permission: 'projects:read',
      },
      handler: async () => {
        return { project: null };
      },
    });
Patch Changes
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.34.0

Highlights

ACP Coding Agents as Tools or Subagents (New @mastra/acp)

New @mastra/acp@0.1.0 lets you run ACP-compatible coding agents as Mastra tools (createACPTool) or lightweight subagents (AcpAgent) with incremental streaming, usable anywhere Mastra accepts SubAgent (supervisors, workflows, and the Inngest adapter).

Realtime xAI Voice Provider (New @mastra/voice-xai-realtime)

New @mastra/voice-xai-realtime@0.1.0 adds a realtime voice integration for the xAI Grok Voice Agent API, enabling Agents to connect, stream audio, and run voice turns via XAIRealtimeVoice.

Agent Metadata for Filtering, Cloning, and Dynamic Resolution

Agents can now include optional metadata (static or DynamicArgument) retrievable via agent.getMetadata(), surfaced in /agents responses for client-side filtering, and preserved when cloning agents via the editor (unless overridden).

Enterprise Foundations for Admin Policies + Role-Aware Capabilities

Two new entry points (@mastra/core/agent-builder/ee and @mastra/core/auth/ee) provide building blocks for model allowlists/admin model policies and RBAC enhancements (optional IRBACProvider.getAvailableRoles() / getPermissionsForRole()), with @mastra/auth-workos implementing these methods.

Better File Handling in workspace.read_file (Native Media Parts + Safer Binaries)

read_file now returns supported media (images/PDF) as native model file/image parts (with configurable mediaTypes and maxMediaBytes), keeps text files as text, and avoids dumping unsupported/oversized binaries as base64 by returning metadata unless an explicit encoding is requested.

Observability: Lightweight Trace Listing API + Improved Retry Visibility

New GET /observability/traces/light (plus ClickHouse/DuckDB/server/client support) enables paginated trace lists without span payloads, and background-task retries now appear as separate workflow steps in run history/event traces for easier debugging.

Breaking Changes
  • None noted in this changelog.

Changelog

@mastra/core@1.34.0
Minor Changes
  • You can now run ACP-compatible coding agents as Mastra tools or lightweight subagents. ACP agents support incremental response streaming and can be used anywhere Mastra accepts a SubAgent, including supervisor delegation and workflow steps. (#16423)

    import { createACPTool, AcpAgent } from '@mastra/acp';
    
    export const codingTool = createACPTool({
      id: 'coding-agent',
      command: 'my-acp-agent',
    });
    
    export const codingAgent = new AcpAgent({
      id: 'coding-agent',
      command: 'my-acp-agent',
    });

    You can also wire an AcpAgent into a supervisor or workflow as a SubAgent-compatible implementation:

    import { Agent } from '@mastra/core/agent';
    
    export const supervisor = new Agent({
      name: 'supervisor',
      instructions: 'Delegate coding tasks to the ACP agent.',
      model,
      agents: {
        codingAgent,
      },
    });

    Workflows and the Inngest workflow adapter now recognize SubAgent-compatible implementations when creating agent-backed workflow steps.

  • Added optional metadata to code-defined agents. Pass a metadata record to new Agent({...}), read it back with agent.getMetadata(), and clients can filter on it from the existing /agents and /agents/:agentId responses without encoding the data into IDs or names. (#16603)

    Metadata supports the same DynamicArgument form as other agent config fields, so it can also be resolved per request from the request context.

    Stored agents loaded via the editor also expose their metadata through agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent via editor.agent.clone() now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.

    // Static
    const supportAgent = new Agent({
      id: 'support-agent',
      name: 'Support Agent',
      instructions: 'You help customers with support requests.',
      model: 'openai/gpt-5',
      metadata: { type: 'support' },
    });
    
    supportAgent.getMetadata(); // { type: 'support' }
    
    // Dynamic
    const tenantAgent = new Agent({
      id: 'tenant-agent',
      name: 'Tenant Agent',
      instructions: 'You help customers with tenant-specific tasks.',
      model: 'openai/gpt-5',
      metadata: ({ requestContext }) => ({
        type: 'support',
        tenant: requestContext.get('tenant'),
      }),
    });
    
    await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
  • Added an opt-in foundation for building agent-builder admin policies and role-aware capabilities, available under two new entry points. (#16578)

    @mastra/core/agent-builder/ee

    Exposes types, validators, and picker utilities for working with model allowlists and admin model policies on stored agents — for example normalizing model candidates, choosing a default from a configured allowlist, and producing typed errors when a request violates policy.

    @mastra/core/auth/ee

    Adds optional methods on IRBACProvider for listing available roles and resolving the permissions for a given role:

    interface IRBACProvider {
      // existing methods...
      getAvailableRoles?(): Promise<RoleDescriptor[]>;
      getPermissionsForRole?(role: string): Promise<PermissionDescriptor[]>;
    }

    Static defaults, an expanded permissions catalog, and a capabilities helper that surfaces availableRoles to clients when the provider supports it are also included. Providers that do not implement the new methods continue to work unchanged.

    Also adds a StorageBrowserRef shape to @mastra/core/storage for referencing a configured headless browser on stored agents.

  • Improved how the workspace read_file tool returns files to the model. Reads now branch on file type: (#16570)

    1. Media files (default: image/png, image/jpeg, image/webp, application/pdf) are surfaced as native file/image parts the model can directly view, instead of being dumped as base64 text. Capped at 10 MiB by default so large media don't get base64-encoded into context and persisted in storage — configurable via maxMediaBytes.
    2. Text-readable files (anything text/*, common code/config mime types, or unknown extensions) are returned as text content as before.
    3. Unsupported binaries (e.g. image/png when mediaTypes is disabled, application/zip, oversized media, etc.) now return a short metadata description (path, size, mime type) instead of dumping useless base64 into the conversation. Pass an explicit encoding to opt back into the raw base64/hex dump.

    The set of mime types treated as native media parts and the inline size cap are configurable per workspace:

    import { Workspace, WORKSPACE_TOOLS } from '@mastra/core/workspace';
    
    const workspace = new Workspace({
      filesystem,
      tools: {
        [WORKSPACE_TOOLS.FILESYSTEM.READ_FILE]: {
          // Broaden to any image (e.g. SVG, BMP, HEIC) — may fail on some providers
          mediaTypes: ['image/*', 'application/pdf'],
    
          // Raise the inline-media cap to 25 MiB
          maxMediaBytes: 25 * 1024 * 1024,
    
          // Or a custom predicate
          // mediaTypes: (mime) => mime.startsWith('image/'),
    
          // Or disable media parts entirely
          // mediaTypes: false,
        },
      },
    });

    The default mediaTypes is intentionally the cross-provider-safe intersection — formats universally supported across Anthropic, OpenAI, and Gemini.

  • Changed background process output retention. (#16574)

    Before: Spawned process handles retained all stdout and stderr, which could grow without bound for long-running background processes.

    After: Spawned process handles now retain the latest 1 MiB of stdout and stderr per stream by default. Pass maxRetainedBytes to processes.spawn() to customize the limit, use 0 to disable retained polling output, or use Infinity to keep the previous retain-all behavior.

    const handle = await sandbox.processes.spawn('npm run dev', {
      maxRetainedBytes: 512 * 1024,
    });

    Streaming callbacks and reader streams still receive every chunk in full. Handles also expose truncation and dropped-byte counters so callers can detect when stdout, stderr, or wait() results only include retained output.

    The built-in executeCommand() implementation still retains full output by default; pass maxRetainedBytes there only when you want bounded command results.

Patch Changes
  • Update provider registry and model documentation with latest models and providers (784ad98)

  • Improved background-task observability. (#16590)

    Retry attempts now appear as separate workflow steps in run history and event traces, making retry progression easier to debug. No public API changes.

  • Fixed approval resume for tools loaded by processor workflows. (#16365)

  • Fixed durable agents that could drop object-form system instructions when provider options like cacheControl were used. These instructions are now preserved so provider-specific options are respected. (#16599)

  • Fixed approval resume for tools loaded with ToolSearchProcessor. (#16365)

  • Fixed listMessages perPage=0 behavior in the in-memory store to match other adapters. (#16602)

  • Fixed non-deterministic ordering of cross-thread semantic recall messages. (#16600)

    When messages recalled from other threads shared the same timestamp, they were rendered into the system prompt in whatever order the vector query returned them — driven by similarity scores that can vary between equivalent runs. This made any test or evaluation that snapshots prompt output (or hashes the outbound LLM request) flaky.

    Recalled cross-thread messages are now sorted by createdAt, then threadId, then role (user → assistant → tool → system), then id before formatting, so the same set of recalled messages always produces the same prompt.

  • Expose GET /observability/traces/light and storage support for fetching paginated trace-list rows without span payload data. (#16608)

  • GET /api/observability/discovery/metric-names and GET /api/observability/discovery/metric-label-values now accept limit as a URL query parameter without pre-parsing. Previously, passing ?limit=10 was rejected as a validation error; callers can now use these endpoints directly from HTTP clients, consistent with other query endpoints (e.g. pagination). (#16489)

  • Fixed agent signals so standalone agents coordinate thread streams through a shared runtime. (#16581)

@mastra/acp@0.1.0
Minor Changes
  • You can now run ACP-compatible coding agents as Mastra tools or lightweight subagents. ACP agents support incremental response streaming and can be used anywhere Mastra accepts a SubAgent, including supervisor delegation and workflow steps. (#16423)

    import { createACPTool, AcpAgent } from '@mastra/acp';
    
    export const codingTool = createACPTool({
      id: 'coding-agent',
      command: 'my-acp-agent',
    });
    
    export const codingAgent = new AcpAgent({
      id: 'coding-agent',
      command: 'my-acp-agent',
    });

    You can also wire an AcpAgent into a supervisor or workflow as a SubAgent-compatible implementation:

    import { Agent } from '@mastra/core/agent';
    
    export const supervisor = new Agent({
      name: 'supervisor',
      instructions: 'Delegate coding tasks to the ACP agent.',
      model,
      agents: {
        codingAgent,
      },
    });

    Workflows and the Inngest workflow adapter now recognize SubAgent-compatible implementations when creating agent-backed workflow steps.

Patch Changes
@mastra/auth-workos@1.3.0
Minor Changes
  • Added optional getAvailableRoles and getPermissionsForRole methods to the WorkOS RBAC provider, so consumers can list configured roles and inspect their permissions through @mastra/auth-workos. (#16578)

    import { MastraRBACWorkos } from '@mastra/auth-workos';
    
    const rbac = new MastraRBACWorkos({
      /* config */
    });
    
    // List all available roles
    const roles = await rbac.getAvailableRoles();
    // [{ id: 'admin', name: 'Admin' }, { id: 'member', name: 'Member' }]
    
    // Get permissions for a specific role
    const permissions = await rbac.getPermissionsForRole('member');
    // ['agents:read', 'workflows:read']
Patch Changes
@mastra/clickhouse@1.7.2
Patch Changes
  • Expose GET /observability/traces/light and storage support for fetching paginated trace-list rows without span payload data. (#16608)
@mastra/client-js@1.19.0
Minor Changes
  • Added optional metadata to code-defined agents. Pass a metadata record to new Agent({...}), read it back with agent.getMetadata(), and clients can filter on it from the existing /agents and /agents/:agentId responses without encoding the data into IDs or names. (#16603)

    Metadata supports the same DynamicArgument form as other agent config fields, so it can also be resolved per request from the request context.

    Stored agents loaded via the editor also expose their metadata through agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent via editor.agent.clone() now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.

    // Static
    const supportAgent = new Agent({
      id: 'support-agent',
      name: 'Support Agent',
      instructions: 'You help customers with support requests.',
      model: 'openai/gpt-5',
      metadata: { type: 'support' },
    });
    
    supportAgent.getMetadata(); // { type: 'support' }
    
    // Dynamic
    const tenantAgent = new Agent({
      id: 'tenant-agent',
      name: 'Tenant Agent',
      instructions: 'You help customers with tenant-specific tasks.',
      model: 'openai/gpt-5',
      metadata: ({ requestContext }) => ({
        type: 'support',
        tenant: requestContext.get('tenant'),
      }),
    });
    
    await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
Patch Changes
  • Expose GET /observability/traces/light and storage support for fetching paginated trace-list rows without span payload data. (#16608)
@mastra/docker@0.2.0
Minor Changes
  • Docker sandbox containers now support resource limits and security hardening through Docker HostConfig options. Configure memory, CPU quota, process IDs, capabilities, security options, read-only root filesystems, and tmpfs mounts. (#16577)

    const sandbox = new DockerSandbox({
      memory: 512 * 1024 * 1024,
      memorySwap: 512 * 1024 * 1024,
      cpuQuota: 100_000,
      pidsLimit: 256,
      readonlyRootfs: true,
      capDrop: ['ALL'],
      securityOpt: ['no-new-privileges:true'],
    });
Patch Changes
@mastra/duckdb@1.3.2
Patch Changes
  • Expose GET /observability/traces/light and storage support for fetching paginated trace-list rows without span payload data. (#16608)
@mastra/editor@0.8.0
Minor Changes
  • Added optional metadata to code-defined agents. Pass a metadata record to new Agent({...}), read it back with agent.getMetadata(), and clients can filter on it from the existing /agents and /agents/:agentId responses without encoding the data into IDs or names. (#16603)

    Metadata supports the same DynamicArgument form as other agent config fields, so it can also be resolved per request from the request context.

    Stored agents loaded via the editor also expose their metadata through agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent via editor.agent.clone() now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.

    // Static
    const supportAgent = new Agent({
      id: 'support-agent',
      name: 'Support Agent',
      instructions: 'You help customers with support requests.',
      model: 'openai/gpt-5',
      metadata: { type: 'support' },
    });
    
    supportAgent.getMetadata(); // { type: 'support' }
    
    // Dynamic
    const tenantAgent = new Agent({
      id: 'tenant-agent',
      name: 'Tenant Agent',
      instructions: 'You help customers with tenant-specific tasks.',
      model: 'openai/gpt-5',
      metadata: ({ requestContext }) => ({
        type: 'support',
        tenant: requestContext.get('tenant'),
      }),
    });
    
    await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
Patch Changes
  • Fixed stored agent tool overrides so conditional tool configuration no longer recursively calls the forked agent when merging code-defined tools. (#16544)
@mastra/inngest@1.4.1
Patch Changes
  • You can now run ACP-compatible coding agents as Mastra tools or lightweight subagents. ACP agents support incremental response streaming and can be used anywhere Mastra accepts a SubAgent, including supervisor delegation and workflow steps. (#16423)

    import { createACPTool, AcpAgent } from '@mastra/acp';
    
    export const codingTool = createACPTool({
      id: 'coding-agent',
      command: 'my-acp-agent',
    });
    
    export const codingAgent = new AcpAgent({
      id: 'coding-agent',
      command: 'my-acp-agent',
    });

    You can also wire an AcpAgent into a supervisor or workflow as a SubAgent-compatible implementation:

    import { Agent } from '@mastra/core/agent';
    
    export const supervisor = new Agent({
      name: 'supervisor',
      instructions: 'Delegate coding tasks to the ACP agent.',
      model,
      agents: {
        codingAgent,
      },
    });

    Workflows and the Inngest workflow adapter now recognize SubAgent-compatible implementations when creating agent-backed workflow steps.

@mastra/memory@1.18.1
Patch Changes
  • Added a public escape hatch so callers can supply an authoritative token estimate for file parts whose binary payload has been stripped before persistence (for example, files uploaded to cloud storage with a hidden reference token left in data and re-hydrated by LLM middleware before inference). (#16565)

    For those pipelines TokenCounter has no on-device file size to measure, so Observational Memory thresholds and context budgets undercount large attachments. Callers can now stamp an estimate directly on the part:

    part.providerMetadata = {
      mastra: {
        tokenEstimate: { v: 0, source: 'client', key: 'client', tokens: 25_000 },
      },
    };

    When present, TokenCounter returns those tokens from both the sync and async paths and skips provider fetches. Invalid entries (NaN, negative, non-numeric) fall through to the default estimator. Parts without a client estimate are unaffected.

    Related to https://github.com/mastra-ai/mastra/issues/16522

@mastra/nestjs@0.1.5
Patch Changes
  • Fixed @mastra/nestjs coercing query parameter values to booleans, null, numbers, and parsed JSON objects/arrays before route schema validation. A route declaring queryParamSchema: z.object({ filter: z.string() }) could reject a valid request like ?filter={"a":1} because the adapter had already turned the string into an object. NestJS now forwards query values as the raw strings (or string arrays) the HTTP layer delivered — matching @mastra/hono, @mastra/express, @mastra/fastify, and @mastra/koa. (#16268)

    Routes that want type coercion should opt in via the schema, e.g. z.coerce.boolean(), z.coerce.number(), or a JSON preprocessor on the field.

    Fixes #16114.

@mastra/playground-ui@28.0.0
Minor Changes
  • Added a List mode filter to the Observability traces page for switching between Traces and Branches mode, and changed the default to Traces mode. Previously, the page opened in Branches mode; now it opens in Traces mode, and users can switch modes via the new "List mode" property in the Add filter menu. For example: open Observability → Traces (now defaults to Traces) → Add filter → List mode → pick Branches or Traces. (#16587)
Patch Changes
  • Removed the inset top gloss from the shadow-dialog token. The gloss read as a faint highlight band along the top edge of dropdown menus, popovers, selects, comboboxes, dialogs, tooltips, side dialogs and the main app container in dark mode. The token now applies a drop-shadow only and is consistent across light and dark themes. (#16544)

  • Restyled MainSidebar and swapped sidebar icons to the new Figma design system set. (#16544)

    • Section titles are larger and medium-weight (text-ui-sm, font-medium), lowercase, muted — replacing the previous uppercase + wide-tracking treatment. Underline divider beneath the title removed in both expanded and collapsed states. Active indicator bar on the left edge removed.
    • Nav items render flush: icons align horizontally with the section title, hover/active state now uses theme-aware sidebar surface tokens without item borders or shadows. The legacy indent option is still accepted but no longer changes layout.
    • New sidebar icons: WorkspacesIcon, RequestContextIcon, ScorersIcon, DatasetsIcon, ExperimentsIcon, MetricsIcon. Existing icons AgentIcon, PromptIcon, WorkflowIcon, ProcessorIcon, McpServerIcon, ToolsIcon, LogsIcon, TraceIcon updated to match the Figma artwork. All icons accept React.SVGProps<SVGSVGElement> and inherit color via currentColor.
  • Improved Studio main content framing, persistent page breadcrumbs, accessible page headings, panel layering, and theme-aware navigation/card contrast. (#16544)

  • Fixed a React/React DOM version mismatch that prevented Storybook from rendering in @mastra/playground-ui. (#16529)

@mastra/server@1.34.0
Minor Changes
  • Added optional metadata to code-defined agents. Pass a metadata record to new Agent({...}), read it back with agent.getMetadata(), and clients can filter on it from the existing /agents and /agents/:agentId responses without encoding the data into IDs or names. (#16603)

    Metadata supports the same DynamicArgument form as other agent config fields, so it can also be resolved per request from the request context.

    Stored agents loaded via the editor also expose their metadata through agent.getMetadata(), so clients can filter these agents as well. Cloning a runtime agent via editor.agent.clone() now carries the source agent's metadata over to the stored clone when the caller does not provide one explicitly.

    // Static
    const supportAgent = new Agent({
      id: 'support-agent',
      name: 'Support Agent',
      instructions: 'You help customers with support requests.',
      model: 'openai/gpt-5',
      metadata: { type: 'support' },
    });
    
    supportAgent.getMetadata(); // { type: 'support' }
    
    // Dynamic
    const tenantAgent = new Agent({
      id: 'tenant-agent',
      name: 'Tenant Agent',
      instructions: 'You help customers with tenant-specific tasks.',
      model: 'openai/gpt-5',
      metadata: ({ requestContext }) => ({
        type: 'support',
        tenant: requestContext.get('tenant'),
      }),
    });
    
    await tenantAgent.getMetadata({ requestContext }); // { type: 'support', tenant: 'acme' }
Patch Changes
  • Expose GET /observability/traces/light and storage support for fetching paginated trace-list rows without span payload data. (#16608)

  • GET /api/observability/discovery/metric-names and GET /api/observability/discovery/metric-label-values now accept limit as a URL query parameter without pre-parsing. Previously, passing ?limit=10 was rejected as a validation error; callers can now use these endpoints directly from HTTP clients, consistent with other query endpoints (e.g. pagination). (#16489)

  • Fixed agent signal wakeups so idle runs receive request context values while preserving server-owned resource identifiers. (#16557)

@mastra/voice-xai-realtime@0.1.0
Minor Changes
  • Added @mastra/voice-xai-realtime, a realtime voice provider for the xAI Grok Voice Agent API. (#16507)

    Use XAIRealtimeVoice with Mastra's Agent voice primitive to connect, stream audio, and run xAI voice turns:

    import { Agent } from '@mastra/core/agent';
    import { XAIRealtimeVoice } from '@mastra/voice-xai-realtime';
    
    const voice = new XAIRealtimeVoice({
      apiKey: process.env.XAI_API_KEY,
      model: 'grok-voice-think-fast-1.0',
      speaker: 'eve',
      turnDetection: { type: 'server_vad' },
    });
    
    const agent = new Agent({
      id: 'voice-agent',
      name: 'Voice Agent',
      instructions: 'You are a helpful voice assistant.',
      model: 'xai/grok-4.3',
      voice,
    });
    
    await agent.voice.connect();
    agent.voice.on('speaker', audioStream => playAudio(audioStream));
    await agent.voice.speak('How can I help you today?');
    await agent.voice.send(microphoneStream);
Patch Changes
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.33.0

Highlights

Push-capable PubSub + HTTP workflow event ingestion

Mastra now supports push delivery for workflow events: PubSubs declare supportedModes, Mastra.handleWorkflowEvent(event) is the unified entry point, and servers expose POST /api/workflows/events so brokers like GCP Pub/Sub push/SNS/EventBridge can deliver events over HTTP (no pull worker required).

Response caching for identical LLM steps (ResponseCache)

A new opt-in ResponseCache input processor can skip model calls by replaying cached Responses per step in agent loops, with per-request overrides (key, scope, bust) via RequestContext. Adds InMemoryServerCache for local dev and per-entry TTL support in the cache interface (also implemented by @mastra/redis).

Agent Signals for “send while streaming” threaded chat

Agents can now accept contextual signals mid-run (agent.sendSignal) and clients can follow thread activity via agent.subscribeToThread, enabling reliable follow-ups while responses stream. This is supported end-to-end across @mastra/server, @mastra/client-js, and @mastra/react (stable signal IDs, dedupe, stop/abort behavior, and thread subscriptions).

Azure OpenAI Responses API (v1 routing) + WebSocket streaming transport

AzureOpenAIGateway now supports the Azure OpenAI Responses API with v1 routing controls (useResponsesAPI: true) and adds WebSocket transport for streaming agent/tool loops, improving streaming reliability and supporting real-time tool loop execution over WS.

New storage and integration options: Aurora DSQL + Bright Data tools

Introduces @mastra/dsql (Amazon Aurora DSQL storage with IAM auth) for persisting threads/workflows/observability, and @mastra/brightdata with brightdata-search/brightdata-fetch tools to bypass bot detection/CAPTCHAs for SERP + page fetching.

Observability upgrades: MODEL_INFERENCE spans, eval score unification, and OTEL logs

Adds MODEL_INFERENCE spans under MODEL_STEP to isolate pure provider latency, and routes Mastra Eval/scorer results through the unified score pipeline (and forwards them to multiple exporters). OTEL integrations now support log forwarding/export (@mastra/otel-bridge, @mastra/otel-exporter), and SensitiveDataFilter is applied by default to prevent secret leakage.

Breaking Changes
  • @mastra/inngest now requires inngest@^4 (and Inngest Dev Server v1.18.0+); remove @inngest/realtime and import realtime helpers from inngest/realtime.
  • @mastra/client-js: paginated list methods now require orderBy: { field, direction } (flat sortDirection removed from affected param types); AgentBuilder.stream(...) now requires runId; createStoredSkill(...) now requires description.
  • @mastra/playground-ui: ScrollArea now fades edges by default (opt out with mask={false}); removed exports Threads, ThreadList, ThreadItem, ThreadLink, ThreadDeleteButton (no longer available downstream).

Changelog

@mastra/core@1.33.0
Minor Changes
  • Added processLLMRequest, a processor hook that runs after messages are converted to the provider-facing prompt and before the model request is sent. The hook lets processors make temporary, model-aware prompt changes without mutating stored message history, memory, UI history, or later provider calls. (#16176)

    ProviderHistoryCompat now uses this hook to prevent reasoning-history incompatibilities when switching providers. It strips reasoning parts from Cerebras-bound prompts that would otherwise be sent as rejected reasoning_content, and strips non-Anthropic reasoning from Anthropic-bound prompts while preserving Anthropic-native thinking blocks.

  • Improved Harness support for Agent thread signals. (#16231)

    Harness thread subscriptions now own stream processing for followed runs, echo user-message signal data with stable IDs, and support idle signal starts without delaying optimistic rendering.

    const { id, accepted } = harness.sendSignal({
      type: 'user-message',
      contents: 'Follow up while the agent is still streaming',
    });
    await accepted;
  • Added target-aware tool payload transforms for display streams and transcript messages. Tool authors can transform tool input, output, errors, approval payloads, and suspension payloads without changing raw runtime behavior or toModelOutput. See https://github.com/mastra-ai/mastra/issues/16054. (#16103)

    Use transform on tools, agents, Mastra, or individual generation calls to configure these payload transforms. Runtime callers using the previous toolPayloadProjection shape continue to be normalized for compatibility.

    const lookupCustomer = createTool({
      execute: async ({ customerId, internalPath }) => lookupCustomerRecord(customerId, internalPath),
      transform: {
        display: {
          input: ({ input }) => ({ customerId: input?.customerId }),
          output: ({ output }) => ({ displayName: output?.displayName }),
        },
        transcript: {
          input: ({ input }) => ({ customerId: input?.customerId }),
          output: ({ output }) => ({ displayName: output?.displayName }),
        },
      },
    });
  • Added Azure OpenAI Responses API and v1 routing controls. (#16246)

    Use useResponsesAPI: true to resolve Azure deployments through the Responses API with the Azure v1 route by default:

    new AzureOpenAIGateway({
      resourceName: 'my-openai-resource',
      apiKey: process.env.AZURE_API_KEY!,
      useResponsesAPI: true,
      deployments: ['my-gpt-5-4-deployment'],
    });

    When useDeploymentBasedUrls: false is used directly, the gateway now defaults apiVersion to "v1" to match the AI SDK Azure provider's v1 URL route. Passing apiVersion: "v1" by itself keeps the existing deployment-based URL default for compatibility.

  • Mastra Eval results are now emitted once through the unified observability score pipeline. (#16185)

  • Added Azure OpenAI Responses WebSocket transport support for streaming agent and tool loops. (#16246)

    Configure the Azure gateway with useResponsesAPI: true, then opt into WebSocket streaming per request:

    const stream = await agent.stream('Review this task', {
      providerOptions: {
        azure: {
          transport: 'websocket',
          websocket: { closeOnFinish: false },
        },
      },
    });

    Responses WebSocket streams now preserve transport handles through agent loops, reuse explicit API-key router connections safely, clean up cancelled streams, and reject overlapping previous_response_id continuations on the same connection.

  • Added preserveModelOutput to ToolCallFilter so filtered tool history can keep compact model-facing output without raw tool args or results. (#16060)

    import { ToolCallFilter } from '@mastra/core/processors';
    
    const filter = new ToolCallFilter({
      preserveModelOutput: true,
    });
  • Added a SubAgent interface for custom supervisor subagents. (#16359)

  • Added ResponseCache input processor (#16283)

    Cache identical LLM steps to skip the model call and replay a previously cached response. Useful for prompt templates, suggested-prompt buttons, agentic search re-asks, or guardrail LLMs that classify the same input over and over.

    Caching is opt-in: register ResponseCache explicitly on inputProcessors. There is no agent-level option — this keeps the surface small while we collect feedback on the processor API. Per-call overrides flow through RequestContext.

    import { Agent } from '@mastra/core/agent';
    import { InMemoryServerCache } from '@mastra/core/cache';
    import { ResponseCache } from '@mastra/core/processors';
    
    const cache = new InMemoryServerCache();
    
    const agent = new Agent({
      name: 'Search Agent',
      instructions: 'You answer questions concisely.',
      model: 'openai/gpt-5',
      inputProcessors: [new ResponseCache({ cache, ttl: 600 })],
    });
    
    // First call: cache miss → LLM call
    await agent.generate('What is the capital of France?');
    
    // Second identical call: cache hit → no LLM call
    await agent.generate('What is the capital of France?');

    Per-call overrides via RequestContext:

    import { ResponseCache } from '@mastra/core/processors';
    import { RequestContext } from '@mastra/core/request-context';
    
    // Force a fresh call but still update the cache.
    await agent.stream(prompt, {
      requestContext: ResponseCache.context({ bust: true }),
    });
    
    // Or merge into an existing context.
    const ctx = new RequestContext();
    ResponseCache.applyContext(ctx, { key: 'custom-key' });
    await agent.stream(prompt, { requestContext: ctx });

    Three fields are overridable per call: key, scope, bust. cache, ttl, and agentId stay on the constructor.

    A key function receives { agentId, scope, model, prompt, stepNumber } and returns a string (or Promise<string>):

    await agent.stream(prompt, {
      requestContext: ResponseCache.context({
        key: ({ model, prompt }) => `qa:${model.modelId}:${JSON.stringify(prompt).slice(-200)}`,
      }),
    });

    The cache key is derived from the resolved prompt Mastra is about to send to the model — i.e. after memory loading and earlier input processors have run — so cached entries are tenant-isolated and don't leak context across users with shared prompts but different memory state. Each step in an agentic tool loop is independently cached. By default, the cache scope falls back to MASTRA_RESOURCE_ID_KEY from the request context for automatic per-user isolation. Failed runs (errors, tripwire activations) are not cached. See Response caching for details.

    Also adds:

    • InMemoryServerCache (in @mastra/core/cache) for local development. ResponseCache accepts any MastraServerCache directly — use RedisCache from @mastra/redis for production.
    • MastraServerCache.set() now accepts an optional ttlMs argument so implementations can override the configured default TTL on a per-entry basis. InMemoryServerCache and RedisCache (in @mastra/redis) both honor this.
    • New paired processor hooks processLLMRequest and processLLMResponse. ProcessLLMRequestResult may return { response } to short-circuit the LLM call with a cached payload.
  • Added workflow state reader helpers to inspect persisted workflow runs and recover suspended or long-running workflows. (#16091)

    The reader exposes suspended steps, resume labels, step payloads, and step outputs from the public WorkflowState returned by workflow.getWorkflowRunById(), and WorkflowState step results now reflect foreach array entries.

  • Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)

    Call agent.sendSignal() to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. Use ifActive.behavior and ifIdle.behavior to deliver, persist, discard, or wake from a signal.

    Use agent.subscribeToThread() to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.

    const subscription = await agent.subscribeToThread({ resourceId, threadId });
    
    void (async () => {
      for await (const part of subscription.stream) {
        if (part.type === 'finish') {
          subscription.unsubscribe();
        }
      }
    })();
    
    agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId });
  • Add metadata filtering support to semantic recall. (#9256)

  • Fixed Azure and OpenAI Responses item handling so multi-step reasoning and tool-call histories round-trip correctly without item ID collisions. (#16246)

    Added provider-neutral response item helpers to @mastra/core/agent/message-list. Existing in-memory message cache entries are regenerated after upgrade.

  • Improved foreach workflow execution to keep concurrency slots filled as iterations finish. (#12860)

  • Added processor sendSignal support and routed built-in system reminders through signal messages. (#16438)

  • Added structured drop event types and an onDroppedEvent hook so exporters and bridge integrations can observe events dropped by the observability pipeline. (#16111)

  • Added stable IDs to Harness task items plus task_update and task_complete for updating or completing one tracked task by ID. Task tools now return structured task snapshots, and task_check returns summary and incompleteTasks fields so agents and UIs can restore and verify task state without parsing text. (#16254)

    Harness also exports TaskItemSnapshot, assignTaskIds, and harness.restoreDisplayTasks() for UI history replay, serializes task reads and mutations against the latest task state snapshot, and returns task-tool errors inside forked subagents so sidecar work cannot mutate parent task state.

  • Added new MODEL_INFERENCE span type under MODEL_STEP, covering only the model provider call. Use it to measure model latency separately from input/output processors and tool executions. (#16267)

  • Added experimental support for using remote A2A agents as Mastra subagents. (#16348)

    What changed

    • Mastra agents can register remote A2A endpoints through A2AAgent and delegate to them like other subagents.
    • Remote A2A subagents support generate, resumeGenerate, stream, and resumeStream so parent agents can use them in normal subagent flows.
    • Agent Cards can be cached and verified with pluggable verification hooks before remote execution begins.
    • Browser environments can import shared A2A types and errors from @mastra/core/a2a/client.

    Example

    import { Agent } from '@mastra/core/agent';
    import { A2AAgent } from '@mastra/core/a2a';
    
    const agent = new Agent({
      name: 'Support Agent',
      instructions: 'Use the remote billing specialist for billing questions.',
      model: 'openai/gpt-4o-mini',
      agents: {
        billingSpecialist: new A2AAgent({
          url: 'https://billing.example.com/.well-known/agent-card.json',
        }),
      },
    });
    
    const result = await agent.generate('Can you check the latest invoice status?');

    Why This lets Mastra agents compose with remote A2A agents without exposing those integrations as plain tools or depending directly on the client SDK.

  • Worker review fixes: (#16309)

    • Step-execution endpoint (POST /workflows/:id/runs/:runId/steps/execute) is now gated by Mastra's standard requiresAuth: true + authenticateToken pipeline rather than a parallel "worker secret" body field. The previously introduced workerSecret config knob and MASTRA_WORKER_SECRET env var have been removed (they were never released). To gate the endpoint on a standalone-worker deployment, configure an auth provider on the server's Mastra instance — without one the framework currently treats requiresAuth: true as a no-op for this route.
    • HttpRemoteStrategy now sends credentials as a normal Authorization: Bearer <token> header. The token comes from the new MASTRA_WORKER_AUTH_TOKEN env var or an explicit auth constructor option.
    • Honor the caller's abortSignal in HttpRemoteStrategy by combining it with the per-request timeout via AbortSignal.any (with a manual fallback for runtimes that don't expose it).
    • Implement comma-separated name filtering for the MASTRA_WORKERS env var. MASTRA_WORKERS=scheduler,backgroundTasks now boots only those named workers; MASTRA_WORKERS=false still disables all workers.
    • Restore Mastra.startEventEngine / stopEventEngine as @deprecated aliases for the renamed startWorkers / stopWorkers.
    • BackgroundTaskWorker now subscribes to PubSub in start() instead of init(), matching the lifecycle of the other workers and making isRunning accurately reflect subscription state.
    • RedisStreamsPubSub adds a maxDeliveryAttempts option (default 5) that drops events after the configured number of failed deliveries instead of redelivering forever, and replaces empty catch {} blocks with logger.warn/logger.debug calls.
    • RedisStreamsPubSub.unsubscribe(topic, cb) now honors the topic argument so the same callback can be subscribed to multiple topics independently.
    • PullTransport guards the async router callback against unhandled promise rejections by attaching a .catch that nacks the message.
    • Drop the dead MASTRA_WORKER_NAME env var injection in the CLI worker spawn (the bundle entrypoint already passes the worker name directly).
    • Add a real cross-process e2e auth suite (pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong token, missing token, anonymous direct hits, and the no-auth-provider pin-down behavior.
    • Step-execution route now has a response schema, satisfying schema-consistency.test.ts.
    • Internal type cleanups (drop several as any casts in worker strategies and BackgroundTaskWorker).
    • RedisStreamsPubSub.maxDeliveryAttempts now rejects negative / NaN values at construction. 0 still means "no cap" for back-compat but emits a one-time warning; pass Infinity to disable the cap explicitly.
    • PullTransport accepts a logger and uses it for unhandled router-callback rejections instead of console.error.
    • BackgroundTaskWorker.start() now throws if init() was not called, matching the contract of the other workers.
    • Cross-process integration tests now spawn a single user-owned project (test-fixtures/cli-project/src/mastra/index.ts) through two generic entries that mirror what BuildBundler and WorkerBundler emit. The previous one-off server.entry.ts / worker.entry.ts / scheduler.entry.ts / background.entry.ts files have been deleted — they implied users hand-roll entry files, which they don't. Worker role is selected via MASTRA_WORKERS exactly as in production.

    Push-capable PubSub:

    • The PubSub abstract class now declares a supportedModes getter (defaulting to ['pull'] for backward compatibility) so consumers can tell whether a broker delivers events through a pull loop, an in-process push, or an out-of-process HTTP push. EventEmitterPubSub reports ['pull', 'push'] (EventEmitter dispatches synchronously and works for either path), @mastra/redis-streams reports ['pull'].
    • Mastra now exposes a public handleWorkflowEvent(event) method backed by a shared WorkflowEventProcessor. It is the single entry point used by the existing pull-mode OrchestrationWorker, by in-process push pubsubs (auto-wired during startWorkers()), and by the new POST /api/workflows/events route which lets push-mode brokers (GCP Pub/Sub push, SNS, EventBridge) deliver events over HTTP.
    • When the configured pubsub does not support 'pull', Mastra automatically skips creating an OrchestrationWorker and OrchestrationWorker.init() throws a clear error if it is constructed against a push-only pubsub.
    • WorkflowEventProcessor gains a handle(event) method that returns a structured { ok, retry } result. The original process(event, ack?) method is preserved as a thin wrapper for back-compat.

    Public-API example for a push-capable PubSub:

    import { Mastra } from '@mastra/core/mastra';
    import { EventEmitterPubSub } from '@mastra/core/pubsub';
    
    const mastra = new Mastra({
      // A push-capable broker (GCP Pub/Sub push, SNS, EventEmitter, …).
      // EventEmitterPubSub reports supportedModes = ['pull', 'push'].
      pubsub: new EventEmitterPubSub(),
      workflows: { myWorkflow },
    });
    
    // In-process push pubsubs are auto-wired here. For out-of-process
    // push (e.g. HTTP webhook from a cloud broker), POST the event to
    // /api/workflows/events on your Mastra server instead.
    await mastra.startWorkers();
    
    // Direct invocation (e.g. inside an HTTP handler that bridges from a
    // cloud broker's push delivery):
    await mastra.handleWorkflowEvent({
      id: 'evt-1',
      type: 'workflow.start',
      runId: 'run-1',
      createdAt: new Date(),
      data: { workflowId: 'myWorkflow', inputData: { name: 'world' } },
    });

    CI follow-ups:

    • Mastra only auto-registers SchedulerWorker when storage is configured. Without storage the worker would crash on startup (deps.storage.getStore on undefined); the scheduler now silently no-ops in that case, matching the pre-worker scheduler behavior.
    • SchedulerWorker.init defensively logs and returns when called without storage instead of throwing a TypeError.
    • RECEIVE_WORKFLOW_EVENT_ROUTE (POST /workflows/events) createdAt is now a plain z.string() on the wire and the handler converts it to a Date (validating "Invalid Date" -> 400). The previous union(...).transform().refine() schema couldn't be exercised by the shared adapter test suite because the generator didn't unwrap Zod 4's ZodPipe.
    • _test-utils/route-test-utils recognizes Zod 4's number_format check (used for int() / safeint()), and generateContextualValue now produces a valid ISO timestamp for createdAt / updatedAt fields.
  • Configured workflow step scorers now automatically emit scores through the observability score pipeline. (#16371)

Patch Changes
  • Update provider registry and model documentation with latest models and providers (ac47842)

  • Fixed two bugs that affected scheduled workflows. (#16510)

    Scheduled workflow with mismatched id could not be dispatched (#16471)

    When a workflow's id differed from the key it was registered under, the scheduler published events the event processor could not resolve, causing the run to fail with "Workflow not found." The dispatcher now looks up workflows by .id first (falling back to the registration key), so the following now works as expected:

    const workflow = createWorkflow({ id: 'daily-report', schedule: { cron: '0 9 * * *' } });
    new Mastra({ workflows: { dailyReport: workflow } });

    Deleted scheduled workflows caused infinite event redelivery

    Removing a scheduled workflow from code used to leave its schedule row in storage. The scheduler kept firing for the missing workflow and the event processor kept telling the transport to redeliver the event forever. On boot, Mastra now cleans up declarative schedule rows (those it wrote itself, prefixed with wf_) for workflows that are no longer registered. User-created schedules made via the schedules API are left untouched. The event processor also handles in-flight events for missing workflows by emitting a single terminal workflow.fail instead of looping.

  • Fixed processor-combined workflows to use the agent logger so processor step failures are logged through the configured logger instead of console output. (#16369)

  • Fixed plan approval so accepting a plan can switch modes after the waiting plan tool resolves, clears stale abort state before starting the approved goal, and injects the goal trigger directly instead of queueing a follow-up. (#16340)

  • Fixed guardrail processor schemas so structured output works with Anthropic models. (#15739)

  • Propagate cache metrics (cachedInputTokens, cacheCreationInputTokens) through harness token usage. The step-finish handler now extracts cachedInputTokens from AI SDK usage and propagates it through usage_update events, getTokenUsage(), display state, and thread metadata persistence. (#14746)

  • Fixed workflow resume to reuse suspended step input payloads when previous step output is stale. Fixes https://github.com/mastra-ai/mastra/issues/16051. (#16066)

  • A tool's toModelOutput result is now computed before the tool-result chunk is emitted, so it travels with the chunk on providerMetadata.mastra.modelOutput. Previously toModelOutput only ran later when the message list was updated, meaning live stream consumers couldn't see it without re-running the tool. (#16457)

    The harness now forwards that providerMetadata on tool_result content (both streaming and replayed history) and on tool_end events, so UIs can render rich tool output (e.g. screenshot images) inline.

    harness.on('tool_end', event => {
      const modelOutput = event.providerMetadata?.mastra?.modelOutput;
      // e.g. { type: 'content', value: [{ type: 'image-data', mediaType: 'image/png', data: '...' }] }
    });
  • Fixed ToolCallFilter so assistant messages with top-level text are preserved when tool parts are removed. (#16077)

  • Fixed AI SDK v6 dynamic tool UI messages so MessageList preserves tool state during history normalization. Fixes #16046. (#16062)

  • Fixed tool result media content not reaching the model. Tools using toModelOutput to return images or files (e.g. screenshot tools) now work correctly with all AI SDK providers (Anthropic, OpenAI, Google). (#16449)

  • Added A2A Agent Card signing config types for server configuration. (#16207)

    Example

    const mastra = new Mastra({
      server: {
        a2a: {
          agentCardSigning: {
            privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!,
            protectedHeader: {
              alg: 'ES256',
              kid: 'agent-card-key',
            },
          },
        },
      },
    });
  • Fixed a Harness issue where reopening a thread could apply the wrong model for (#16278) the saved mode. Threads now reopen with the correct model for that mode, including when no explicit per-mode model was selected.

  • Fixed Workflow runs no longer fail to persist when request context contains non-serializable values (for example functions, circular references, or platform proxy objects). This prevents errors when saving workflow snapshots and scorer results. See #12301. (#12573)

  • Added /goal to Mastra Code, a persistent autonomous task loop similar to the goal modes in Codex and Hermes Agent. (#16065)

    A user can start a goal with /goal <objective>. Mastra Code saves that objective to the current thread, runs the normal assistant turn, then asks a separate judge model whether the goal is done, should continue, or is waiting on an explicit user checkpoint. When the judge says to continue, Mastra Code feeds the judge feedback back into the conversation and keeps working until the goal is complete, paused, cleared, or reaches the configured attempt limit.

    Use /judge to configure the default judge model and max attempts used by future goals.

    Approved plans can be selected as a goal from the inline plan approval UI, slash commands can opt into /goal/<command> with top-level goal: true, and skills can opt into goal commands with metadata.goal: true. /goal objectives can also span multiple lines.

  • Fixed nested workflow runs retaining abort listeners after completion. (#16212)

    • Fixed foreach state update and foreach bail in evented workflows (#16436)
    • Fixed suspend-resume in evented-workflows legacy stream.
  • Hide internal spans from Mastra-owned processors in exported traces. The PROCESSOR_RUN span still appears, but the agent, model, and tool spans that processors create under the hood are now marked internal and filtered out by default. (#16424)

    Affects the moderation, PII detector, language detector, prompt-injection detector, system-prompt scrubber, and structured-output processors.

    To inspect the internals (e.g. for debugging a Mastra-owned processor's behavior), set includeInternalSpans: true on your Observability config and the full subtree will be exported.

  • Fixed goal reminders in MastraCode to continue through signals without duplicating prompts. Updated core signal stream completion handling so idle-started reminder runs emit the expected lifecycle events. (#16231)

  • Fixed requestContext serialization in workflow suspend/resume. The snapshot persistence used instanceof check which fails across module boundaries, causing context values to be lost on resume. (#16082)

  • Fixed timeTravel() on evented workflows so jumping to a .branch() step no longer returns empty results for branches that did not run. Branch results now include only the branch that ran, matching the default workflow engine. (#16428)

  • Fixed an issue where trajectory and step scorer results from runEvals were not saved to storage. These scores now persist correctly and appear alongside agent and workflow scores in Studio's observability section. (#16249)

  • Tool toModelOutput invocations now emit a MAPPING tracing span, showing the transformed output the model receives. (#16347)

  • Default top-level observational memory early activation settings to observations only, while allowing per-phase overrides under observation and reflection. (#16367)

  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Fixed assistant message tracking when ObservationalMemory clears step-1 output to memory and step-2 text merges into the same assistant message, so merged text is not lost on the next response clear. (#15277)

  • Added subagent display names to Harness display state so UIs can render configured subagent names without duplicating agent type lookup. (#16237)

  • Fixed title generation to send plain text instead of JSON-serialized part objects to the title model. Previously, internal metadata like providerOptions and framework details leaked into the title prompt. Now formats messages using role-prefixed plain text (similar to observational memory formatting), supporting both single-message and multi-turn conversations. (#15798)

  • Fixed a bug where orphaned provider-executed tool calls (e.g. Anthropic web_search, Gemini code_execution) could brick a thread. When a provider dropped the tool-result chunk (#15668) or a run aborted mid-stream (#14148), the unresolved call was replayed on every subsequent request — causing Gemini to return empty text and Anthropic to reject the tool-call/tool-result invariant. (#15682)

    sanitizeV5UIMessages now only keeps an input-available provider-executed tool part on the most recent assistant message, and only when no user turn has followed it. Orphans on earlier assistant turns are dropped so the outgoing history always satisfies the tool-call/tool-result pairing required by provider APIs.

  • Fixed TokenLimiterProcessor crashing when counting text that contains special token strings. (#16302)

  • Fix nested loops in evented workflow (#16312)

  • Fixed agent requests to Anthropic failing with 400 unexpected tool_use_id when recalled history contained an incomplete tool_use / tool_result pair. This most commonly hit agents using parallel tool calls when an earlier run was interrupted before every tool finished — pulling that broken exchange into a new prompt no longer crashes the next request. (#16201)

    Closes #16193

  • Stop logging auto-recoverable provider cache corruption warnings when ~/.cache/mastra/ contains stale content from another Mastra version. Corrupted cache files are still deleted on read so they cannot propagate into a project's dist/, and the next gateway sync rewrites valid files. (#16332)

  • Fix evented workflow foreach timing out when payload is an empty array (#16358)

  • Fixed FGA EE license gating so production servers require an Enterprise license when FGA is configured. (#16362)

  • Fixed a bug where message-level providerOptions could be lost or applied to the wrong turn after tool calls. Anthropic cacheControl markers now stay attached to the intended message in tool-using conversations. (#16133)

  • Fixed MODEL_INFERENCE span timing so it measures pure model latency. (#16357)

  • Added extra defensive checks to prevent edge cases where system messages may have already been stored in message history. (#15787)

  • Fixes tool call args being lost when split across messages in client tools. When a tool invocation spans multiple messages (call with args, result with empty args), the findToolCallArgs function now continues searching for non-empty args instead of returning the first match. (#12454)

  • Replace js-tiktoken with tokenx in @mastra/core to reduce bundle size by removing the bundled BPE rank tables. Token limiting and truncation now use heuristic token estimates, which is appropriate for output limiting and truncation. (#16326)

  • Fix abort() not cancelling evented workflows (#16416)

  • Fixed suspend and resume for evented workflows that use parallel steps, .branch(), dountil/dowhile loops, and nested workflows — previously these only worked reliably for simple linear flows. (#16476)

    Parallel & .branch() steps — when more than one branch suspends at the same time (e.g. each branch waits on its own approval), every suspended branch can now be resumed, the workflow stays suspended until all of them have been resumed, and the branch outputs are merged correctly. Before, only the last branch to suspend was resumable, and resuming one branch could prematurely complete the run.

    dountil / dowhile loops — a loop body that calls suspend() now suspends the workflow instead of crashing the run. And after a resume, subsequent loop iterations run fresh instead of re-receiving the resume data — which previously made loops either run forever or skip their own suspend logic.

    Nested workflows — resuming a suspended step inside a nested workflow now gives it the correct input (the output of the step right before it, not the nested workflow's own input), so it produces correct results, even when workflows are nested several levels deep. The suspended-step path returned in a workflow result is also correct now, so you can pass it straight back into resume({ step }).

  • Added support for attaching a browser instance to the harness after initialization so consumers can defer browser creation until it is needed: (#16513)

    const harness = new Harness({ agent, mastra });
    await harness.init();
    harness.setBrowser(browser);
  • Added validation to evented workflows to ensure execution prerequisites are met. EventedWorkflow.createRun() now throw clear error messages when the workflow execution flow is empty (missing .then(), .branch(), etc. calls) or when the step graph has uncommitted changes (missing .commit() call). This catches configuration errors early rather than failing during execution. (#16361)

  • Background tasks now run as evented workflows. Each task is dispatched as a workflow run that owns executor invocation, retries, and suspend/resume; pubsub topics, lifecycle event shapes, concurrency gating, and the BackgroundTaskManager.stream() contract are unchanged. (#16260)

    Add suspend/resume to background tasks. Tools can call ctx.agent.suspend(data) from execute to pause a task and release the concurrency slot; resume with mastra.backgroundTaskManager.resume(taskId, resumeData) or agent.resumeStreamUntilIdle(resumeData, { runId, toolCallId }). Surfaces background-task-suspended / background-task-resumed chunks on backgroundTaskManager.stream() and agent.streamUntilIdle().fullStream.

  • Fixed message part ordering in agent streaming responses. Message parts (text, reasoning, tool calls) now appear in the correct order they arrived in the stream, preventing incorrect step sequencing and agent loop behavior issues. (#16073)

@mastra/agent-browser@0.2.2
Patch Changes
  • Added screenshot tool to @mastra/stagehand (stagehand_screenshot) and @mastra/agent-browser (browser_screenshot). Captures a PNG screenshot and returns image content for vision-capable models. (#16074)

    Added excludeTools config option to opt out of specific tools:

    const browser = new StagehandBrowser({
      excludeTools: ['stagehand_screenshot'],
    });
@mastra/ai-sdk@1.4.2
Patch Changes
  • Fixed cache write tokens not being set on the AI SDK v6 usage object. inputTokenDetails.cacheWriteTokens now reflects the prompt cache creation tokens reported by the provider instead of always being undefined. Previously this value was only accessible via providerMetadata.anthropic.cacheCreationInputTokens. (#16354)

  • Added support for showing different tool values to users than the values used internally during execution. AI SDK streams now read Mastra display values for tool call input, streamed input deltas, tool results, tool errors, approvals, and suspensions. (#16103)

    const lookupCustomer = createTool({
      // Runtime still receives the full input and returns the full output.
      execute: async ({ customerId, internalPath }) => lookupCustomerRecord(customerId, internalPath),
      transform: {
        display: {
          input: ({ input }) => ({ customerId: input?.customerId }),
          output: ({ output }) => ({ displayName: output?.displayName }),
          error: () => ({ message: 'Customer lookup failed' }),
        },
      },
    });

    This lets chat UIs show safe display values while runtime code keeps the original payloads. See https://github.com/mastra-ai/mastra/issues/16054.

@mastra/arize@1.1.0
Minor Changes
  • Update PHOENIX_ENDPOINT to PHOENIX_COLLECTOR_ENDPOINT environment variable (#16341)
Patch Changes
@mastra/braintrust@1.1.0
Minor Changes
  • Added a Braintrust current span resolver option so eval traces can nest correctly when applications and Mastra resolve different installed Braintrust SDK copies. (#16396)

  • Mastra Eval results are now forwarded to Braintrust. (#16185)

Patch Changes
@mastra/brightdata@0.2.0
Minor Changes
  • Added @mastra/brightdata integration with brightdata-search and brightdata-fetch tools backed by Bright Data's SERP API and Web Unlocker. The tools bypass bot detection and CAPTCHAs out of the box. (#16392)

    import { Agent } from '@mastra/core/agent';
    import { createBrightDataTools } from '@mastra/brightdata';
    
    const agent = new Agent({
      id: 'research-agent',
      name: 'Research Agent',
      model: 'anthropic/claude-sonnet-4-6',
      instructions: 'Use brightdata-search to find pages and brightdata-fetch to read them.',
      tools: createBrightDataTools(),
    });

    Set BRIGHTDATA_API_TOKEN in your environment, or pass { apiKey } explicitly.

Patch Changes
@mastra/clickhouse@1.7.1
Patch Changes
  • Fixed agent streams intermittently hanging when observability storage was backed by Replicated/Shared ClickHouse. Startup no longer re-applies no-op schema updates (e.g. ADD COLUMN IF NOT EXISTS, ADD INDEX IF NOT EXISTS, MODIFY TTL), so it no longer triggers replica-lag retry errors that could leave storage in a stuck state. (#16420)

  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/client-js@1.18.0
Minor Changes
  • Added experimental A2A Agent Card signature verification to getAgentCard. (#16207)

    Example

    const card = await a2a.getAgentCard({
      verifySignature: {
        algorithms: ['ES256'],
        keyProvider: async ({ kid, jku }) => {
          return fetchTrustedPublicJwk({ kid, jku });
        },
      },
    });

    When verification is configured, client-js now verifies signed Agent Cards when the server includes signatures. Unsigned cards are still returned unchanged.

    This also adds the new exported types GetAgentCardOptions, VerifyAgentCardSignatureOptions, AgentCardVerificationKey, and AgentCardSignatureKeyProviderInput.

  • Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)

    const { sendMessage } = useChat({ agentId, resourceId, threadId });
    await sendMessage({ message: 'Follow up while streaming', threadId });
  • Added streamed function-call argument events to @mastra/client-js Responses streams. You can now read finalized tool arguments directly from the stream: (#16285)

    for await (const event of stream) {
      if (event.type === 'response.function_call_arguments.done') {
        console.log(event.arguments);
      }
    }
  • Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)

    Call agent.sendSignal() to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. Use ifActive.behavior and ifIdle.behavior to deliver, persist, discard, or wake from a signal.

    Use agent.subscribeToThread() to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.

    const subscription = await agent.subscribeToThread({ resourceId, threadId });
    
    void (async () => {
      for await (const part of subscription.stream) {
        if (part.type === 'finish') {
          subscription.unsubscribe();
        }
      }
    })();
    
    agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId });
  • Fix orderBy shape mismatch for paginated list methods. (#16323)

    The server expects orderBy as a structured object ({ field, direction }), but several SDK methods were sending orderBy and sortDirection as flat strings, which caused server-side schema validation to fail.

    Affected methods:

    • MastraClient.listMemoryThreads
    • Agent.listVersions
    • StoredAgent.listVersions
    • StoredPromptBlock.listVersions
    • StoredScorer.listVersions

    Before:

    client.listMemoryThreads({ orderBy: 'createdAt', sortDirection: 'DESC' });

    After:

    client.listMemoryThreads({ orderBy: { field: 'createdAt', direction: 'DESC' } });

    The flat sortDirection parameter has been removed from the affected param types in favor of the nested orderBy.direction field.

  • Fix client-js bugs surfaced by the SDK ↔ server contract audit. (#16439)

    • MastraClient.getAgentBuilderActions() previously requested /agent-builder/ (trailing slash) and 404'd. Now hits /agent-builder.
    • AgentBuilder.stream(params, runId) now requires runId. The server route requires it; calls without it failed with a server-side validation error. The SDK now both types runId as required and guards at runtime.
    • MastraClient.createStoredSkill(...) now requires description in its parameter type. The server schema has always required it; the SDK type used to mark it optional, so omitting it produced a runtime 400 instead of a compile error.

    Migration:

    // Before
    await agentBuilder.stream({ inputData });
    
    // After
    await agentBuilder.stream({ inputData }, runId);
    // Before
    await client.createStoredSkill({ name, instructions });
    
    // After
    await client.createStoredSkill({ name, description, instructions });
  • Add agent.resumeStreamUntilIdle() to resume a suspended agent stream and keep the SSE connection open through the follow-up turn. (#16260)

Patch Changes
  • Fixed memory thread write methods (update, delete, deleteMessages, clone) silently sending requests without the required agentId. The methods now resolve agentId from a per-call argument first, then the constructor, and throw a clear error if neither is set — before any HTTP request is issued. Reads are unchanged. (#16310)

    // Either set agentId on the thread once...
    const thread = client.getMemoryThread({ threadId: 't1', agentId: 'a1' });
    await thread.update({ title: 'Renamed' });
    await thread.delete();
    
    // ...or pass it per call.
    const thread = client.getMemoryThread({ threadId: 't1' });
    await thread.update({ agentId: 'a1', title: 'Renamed' });
    await thread.delete({ agentId: 'a1' });

    Fixed MastraClient.deleteThread() issuing DELETE /api (an empty URL) when called without agentId or networkId. The method now requires exactly one of the two, enforced both at runtime and in the type signature.

    await client.deleteThread('t1', { agentId: 'a1' });
    await client.deleteThread('t1', { networkId: 'n1' });
  • Regenerate route types to include TRAJECTORY and STEP entityType variants on the score response (follow-up to #16249). (#16288)

  • Fix MCPTool.execute sending an empty/undefined request body when called without data or requestContext. The server's tool-execute endpoint expects an object body (with optional data), so calls like client.getMcpServerTool(serverId, toolId).execute({}) would fail with Invalid request body. The SDK now always POSTs a JSON object body, defaulting to {} when no parameters are provided. (#16488)

  • Removed Agent Builder routes from the default generated API route contracts. (#16499)

@mastra/cloudflare@1.3.3
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/cloudflare-d1@1.0.6
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/convex@1.0.11
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/datadog@1.2.0
Minor Changes
  • Mastra Eval results are now forwarded to Datadog. (#16185)

  • Mapped MODEL_INFERENCE spans to Datadog's llm kind (with token usage and model/provider attached) and MODEL_STEP to workflow. Falls back to the previous mapping when paired with an older @mastra/core that does not emit MODEL_INFERENCE. (#16363)

Patch Changes
  • Fixed double-encoded JSON on span input in Datadog. (#16256)
@mastra/deployer@1.33.0
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-cloud@1.33.0
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-cloudflare@1.1.33
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-netlify@1.1.9
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/deployer-vercel@1.1.27
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/dsql@1.0.0
Minor Changes
  • Added Amazon Aurora DSQL storage provider with IAM authentication support. (#10930)

    Enables storing threads, messages, workflows, traces, and agent data in Amazon Aurora DSQL clusters.

    import { DSQLStore } from '@mastra/dsql';
    
    const storage = new DSQLStore({
      id: 'my-dsql-store',
      host: 'abc123.dsql.us-east-1.on.aws',
    });
    
    await storage.init();

    Related: #10929

Patch Changes
@mastra/duckdb@1.3.1
Patch Changes
  • Improved DuckDB observability initialization by batching schema setup statements on one connection while preserving migration order. (#16239)
@mastra/dynamodb@1.0.7
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/e2b@0.3.1
Patch Changes
  • Fixed S3 mounts in E2B sandboxes by honoring the configured region and verifying that the FUSE mount attached successfully. (#16222)

    Mount failures that previously appeared successful now surface a clear error, making region, credential, and endpoint compatibility problems easier to diagnose.

@mastra/editor@0.7.24
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

  • Fixed @mastra/editor integrations (Composio, Arcade) collapsing every tool call onto a shared 'default' user. Tools resolved during agent.generate now scope to the authenticated resource from the request context, so per-user OAuth connections route to the correct account instead of a shared one. (#16122)

@mastra/express@1.3.19
Patch Changes
  • Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)

  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

@mastra/fastify@1.3.19
Patch Changes
  • Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)

  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

  • Fixed Fastify stream cleanup and route abort signals when clients disconnect before streamed responses finish. (#16308)

@mastra/google-cloud-pubsub@1.0.4
Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)
@mastra/hono@1.4.14
Patch Changes
  • Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)

  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

@mastra/inngest@1.4.0
Minor Changes
  • Updated @mastra/inngest to use Inngest SDK v4. (#15377)

    Breaking: Requires inngest@^4 and Inngest Dev Server v1.18.0 or later. The @inngest/realtime package is no longer needed — its functionality is now included in inngest v4. Remove it from your dependencies and import realtime helpers from inngest/realtime instead.

      // package.json
      "dependencies": {
    -   "@inngest/realtime": "^0.x",
    -   "inngest": "^3.x"
    +   "inngest": "^4.0.0"
      }
    - import { realtimeMiddleware } from '@inngest/realtime/middleware';
    - import { subscribe } from '@inngest/realtime';
    + import { subscribe } from 'inngest/realtime';
    
      const inngest = new Inngest({
        id: 'mastra',
    -   middleware: [realtimeMiddleware()],
      });

    In v4, subscribe() and realtime.publish() are first-class methods on the client; the standalone middleware is no longer required. InngestPubSub publishes via inngest.realtime.publish() instead of the function-context publish argument that no longer exists in v4, restoring realtime workflow events and agent stream events.

    Improved: Workflow result polling now uses snapshot-based polling, resulting in significantly faster retrieval (~83x).

Patch Changes
  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

  • Updated the serve and createServe JSDoc adapter examples to register Inngest at /inngest/api instead of /api/inngest, matching the Inngest deployment guide and in-repo example projects. (#16186)

    Why

    Mastra reserves the /api prefix for built-in routes (agents, workflows, memory). Custom apiRoutes[].path values that start with the server's apiPrefix (default /api) are rejected at startup, so the previous JSDoc snippets threw Custom API route "/api/inngest" must not start with "/api" when copy-pasted into a current Mastra project.

    Migration

    If you registered Inngest with the previous guide or JSDoc example:

    // Before
    apiRoutes: [
      {
        path: '/api/inngest',
        method: 'ALL',
        createHandler: async ({ mastra }) => serve({ mastra, inngest }),
      },
    ];
    
    // After
    apiRoutes: [
      {
        path: '/inngest/api',
        method: 'ALL',
        createHandler: async ({ mastra }) => serve({ mastra, inngest }),
      },
    ];

    Update the dev server URL (npx inngest-cli dev -u http://localhost:4111/inngest/api) and, in production, set the URL field on your Inngest app to match.

    If you cannot change the path, set server.apiPrefix (for example /_mastra) to relocate the built-in routes and remember to update server.auth.protected and any MastraClient apiPrefix to match. See the Inngest deployment guide for the full walkthrough.

@mastra/koa@1.5.2
Patch Changes
  • Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)

  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

  • Fixed TypeError: Cannot read properties of undefined (reading 'length') thrown during MastraServer.init() when a subclass forwards a non-Koa app-like object (for example a koa-router instance, a mounted sub-app, or a custom wrapper) to super.registerRoute(app, route, opts). The dispatcher-reuse optimization introduced in 1.5.0 now requires the target to expose an app.middleware array; otherwise it falls back to registering a fresh dispatcher per route via app.use, matching the pre-1.5.0 per-route behavior. (#16484)

    Example (subclass that previously crashed):

    import { MastraServer } from '@mastra/koa';
    import Router from 'koa-router';
    
    class CustomKoaMastraServer extends MastraServer {
      private router = new Router();
    
      async registerCustomApiRoutes() {
        const routes = this.mastra.getServer()?.apiRoutes ?? [];
        for (const route of routes) {
          // The router has no `middleware` array — this used to throw at init.
          await super.registerRoute(this.router as any, route, { prefix: this.prefix });
        }
        this.app.use(this.router.routes());
      }
    }
@mastra/laminar@1.1.0
Minor Changes
  • Mastra Eval results are now forwarded to Laminar. (#16185)
Patch Changes
@mastra/lance@1.0.6
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/langfuse@1.3.0
Minor Changes
  • Mastra Eval results are now forwarded to Langfuse. (#16185)
Patch Changes
@mastra/langsmith@1.2.0
Minor Changes
  • Mastra Eval results are now forwarded to LangSmith. (#16185)
Patch Changes
@mastra/libsql@1.10.1
Patch Changes
  • Fixed Workflow run snapshots no longer lose fields when serialized for storage. The libsql safeStringify cycle-detection treated any object that appeared more than once in a snapshot as a circular reference and dropped it. Because snapshot.result and the final step's context[step].output share the same reference on success, snapshot.result was being silently stripped on every persist. This caused listWorkflowRuns to return runs with snapshot.result === undefined and broke workflow resume when suspended-state fields were shared elsewhere in the snapshot. (#16368)

  • Fixed Workflow runs no longer fail to persist when request context contains non-serializable values (for example functions, circular references, or platform proxy objects). This prevents errors when saving workflow snapshots and scorer results. See #12301. (#12573)

  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Improved local LibSQL startup performance by applying conservative local SQLite performance settings before initialization, exposing local PRAGMA overrides, and reducing schema initialization contention. (#16513)

  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

  • Added LibSQL indexes for thread message history queries to speed up recent-message and observational-memory loading. (#16513)

@mastra/memory@1.18.0
Minor Changes
  • Add metadata filtering support to semantic recall. (#9256)
Patch Changes
  • Fixed an issue where tool results containing AI SDK v5 image-data content blocks (returned via toModelOutput) were stringified into the observational memory prompt as raw base64 text. The base64 data overflowed the observer's context, causing token-limit errors and degenerate output. (#16117)

    Image and file blocks (image-data, image-url, file-data, file-url, and media) inside tool results are now hoisted into the observer's input as proper attachments, the same way image and file message parts already are. The text body shows a placeholder like [Image #1: image/png] so the observer keeps positional context without seeing the bytes.

  • Default top-level observational memory early activation settings to observations only, while allowing per-phase overrides under observation and reflection. (#16367)

  • Auto-recover from transient transport errors (e.g. undici terminated, fetch failed, UND_ERR_*, 5xx, 429) in the OM observer and reflector LLM calls. Adds an internal retry wrapper with exponential backoff (1s, 2s, 4s, 8s, 16s, 32s, 64s, 120s — per-attempt delay capped at 2 minutes, ~4 minute total budget per call) so a single network blip from any provider no longer kills the actor turn during long-running sessions. Non-transient errors (auth, validation, etc.) and user-initiated aborts still fail fast. No public API changes. (#16454)

  • Added extra defensive checks to prevent edge cases where system messages may have already been stored in message history. (#15787)

  • Fixed read-only observational memory so existing context is still loaded. (#16059)

@mastra/mongodb@1.8.1
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/mssql@1.3.0
Minor Changes
  • Add agents storage domain to MSSQL adapter — brings @mastra/mssql to parity with @mastra/mongodb and @mastra/libsql for the agents domain. The Studio "Agents" tab and mastra.getEditor() now work against MSSQL. (#16376)

    import { MSSQLStore } from '@mastra/mssql';
    
    const store = new MSSQLStore({
      id: 'mssql-storage',
      connectionString: process.env.MSSQL_URL!,
    });
    
    const agents = await store.getStore('agents');
    const agent = await agents?.getById('agent-id');
    const page = await agents?.list({ status: 'published', perPage: 20 });
Patch Changes
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/nestjs@0.1.3
Patch Changes
  • Improved Studio agent serialization by making Studio mode and auth-related request context server-controlled across adapters. Playground requests now identify Studio traffic consistently, body and query request context cannot set reserved server values, and Studio placeholder fallback is limited to instruction rendering while serialized models, workspace, skills, tools, and default options use the real request context. (#16152)

  • Fixed peer dependency ranges so packages that use the Mastra server require a compatible Mastra core version. (#16208)

@mastra/observability@1.12.0
Minor Changes
  • DefaultExporter now notifies custom exporters and connected integrations when it cannot persist observability events, such as unsupported storage or retries being exceeded. (#16111)

  • Renamed two built-in observability exporters to clearer names. The originals are still exported (now deprecated) and continue to work unchanged, including their existing exporter name strings and error IDs, so monitoring rules and dashboards keep matching until you migrate. (#16223)

    • CloudExporterMastraPlatformExporter
    • DefaultExporterMastraStorageExporter

    Before

    import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@mastra/observability';
    
    new Observability({
      configs: {
        default: {
          serviceName: 'my-app',
          exporters: [new DefaultExporter(), new CloudExporter()],
          spanOutputProcessors: [new SensitiveDataFilter()],
        },
      },
    });

    After

    import {
      Observability,
      MastraStorageExporter,
      MastraPlatformExporter,
      SensitiveDataFilter,
    } from '@mastra/observability';
    
    new Observability({
      configs: {
        default: {
          serviceName: 'my-app',
          exporters: [new MastraStorageExporter(), new MastraPlatformExporter()],
          spanOutputProcessors: [new SensitiveDataFilter()],
        },
      },
    });
  • Apply SensitiveDataFilter by default (#16234)

    The Observability registry now auto-applies a SensitiveDataFilter span output processor to every configured instance, so secrets (API keys, tokens, passwords, etc.) are redacted before they reach exporters such as the Mastra cloud exporter. This protects against accidentally exporting sensitive data when the filter was not added manually.

    A new top-level sensitiveDataFilter option on the Observability registry config controls this behavior:

    • true (default): apply SensitiveDataFilter with default options.
    • false: opt out of auto-applied filtering.
    • a SensitiveDataFilterOptions object: customize the filter (sensitive fields, redaction token, redaction style).

    If a config already includes a SensitiveDataFilter in spanOutputProcessors, the auto-applied filter is skipped to avoid double redaction. Pre-instantiated ObservabilityInstance values are not modified.

    Before:

    import { Observability, DefaultExporter, CloudExporter, SensitiveDataFilter } from '@mastra/observability';
    
    new Observability({
      configs: {
        default: {
          serviceName: 'mastra',
          exporters: [new DefaultExporter(), new CloudExporter()],
          spanOutputProcessors: [new SensitiveDataFilter()],
        },
      },
    });

    After:

    import { Observability, DefaultExporter, CloudExporter } from '@mastra/observability';
    
    new Observability({
      configs: {
        default: {
          serviceName: 'mastra',
          exporters: [new DefaultExporter(), new CloudExporter()],
        },
      },
      // Optional: customize or disable the auto-applied filter.
      // sensitiveDataFilter: false,
      // sensitiveDataFilter: { sensitiveFields: ['myCustomSecret'] },
    });
  • Added new MODEL_INFERENCE span type under MODEL_STEP, covering only the model provider call. Use it to measure model latency separately from input/output processors and tool executions. (#16267)

Patch Changes
  • Fixed cost estimation for OpenRouter models. The Model Usage & Cost panel now shows costs for OpenRouter vendor/model ids (e.g. openai/gpt-5-mini-2025-08-07, xiaomi/mimo-v2-pro-20260318) that previously rendered an empty cost column. (#16206)

  • Support MASTRA_PLATFORM_ACCESS_TOKEN as the preferred environment variable for MastraPlatformExporter, while retaining MASTRA_CLOUD_ACCESS_TOKEN as a fallback for backward compatibility. (#16500)

  • Score events now include scorer names and target entity types. (#16185)

  • Fixed MODEL_INFERENCE span timing so it measures pure model latency. (#16357)

  • Refreshed the embedded pricing data snapshot used for cost estimation in observability metrics with the latest provider rates. (#16373)

@mastra/otel-bridge@1.1.0
Minor Changes
  • Added log forwarding to @mastra/otel-bridge. The bridge now also subscribes to Mastra log events and forwards them to the globally-registered OpenTelemetry LoggerProvider, alongside the spans it already creates. (#13529)

    Logs that originate inside a Mastra span are emitted under that span's OTEL context, so backends like Datadog, Grafana, and Honeycomb correlate them with the surrounding trace automatically. Logs without trace context fall through to the currently active OTEL context.

    To wire up logs alongside traces, register a logRecordProcessor on NodeSDK:

    import { NodeSDK } from '@opentelemetry/sdk-node';
    import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
    import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
    
    const sdk = new NodeSDK({
      // ...trace config as usual
      logRecordProcessor: new BatchLogRecordProcessor(new OTLPLogExporter()),
    });

    If no LoggerProvider is registered, log emission is a silent no-op — traces continue to work as configured.

Patch Changes
@mastra/otel-exporter@1.1.0
Minor Changes
  • Added log export to @mastra/otel-exporter. Logs emitted on the Mastra observability bus are now forwarded to the configured OTLP endpoint alongside traces, using the same provider configuration. (#13529)

    import { OtelExporter } from '@mastra/otel-exporter';
    
    new OtelExporter({
      provider: {
        custom: { endpoint: 'http://localhost:4318', protocol: 'http/json' },
      },
      // signals.logs defaults to true; set to false to disable.
      signals: { traces: true, logs: true },
    });

    Requires the matching OTLP log exporter package to be installed (e.g. @opentelemetry/exporter-logs-otlp-http for HTTP/JSON, or -proto / -grpc variants).

    Trace correlation: Logs that carry traceId and spanId are attached to the OTEL log record's native trace context, so backends like Datadog, Grafana, and Honeycomb auto-correlate logs to traces.

    Other improvements:

    • Trace and log endpoints are always normalized to a single signal-path suffix, so http://host:4318/, http://host:4318, and http://host:4318/v1/traces/ all produce well-formed URLs instead of malformed variants like //v1/logs.
    • Calling flush() or shutdown() immediately after init no longer drops telemetry — teardown waits for setup to finish before draining providers.
    • Debug log output no longer exposes credential fragments. Provider header values are fully redacted instead of printing prefix/suffix slices.
    • When a dynamically-loaded OTLP exporter package is installed but does not expose the expected named export, Mastra now disables that signal with a clear error message instead of failing later with an opaque "X is not a constructor" error.
Patch Changes
@mastra/pg@1.10.1
Patch Changes
  • Fixed @mastra/pg listing endpoints (agents, MCP clients, MCP servers, prompt blocks, scorer definitions, skills, and workspaces) so a single row with a malformed value no longer returns HTTP 500 and hides every other record in the Mastra Editor. Listings now tolerate the bad row and return the rest. (#16233)

    Fixes #16224.

  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
  • Track suspendedAt and suspendPayload on background tasks. SQL adapters auto-migrate the new columns via alterTable. (#16260)

@mastra/playground-ui@27.0.0
Minor Changes
  • Updated agent traces tab to use the rich observability traces UI (#16405)

    The agent traces tab now shows the dense 7-column trace list with a side-panel detail view featuring colored timeline spans (Agent/Workflow/Model/Scorer), expandable nested spans, Evaluate Trace, and Save as Dataset Item.

    Locked scope filter pills

    When viewing agent-scoped traces, the Primitive Type and Primitive ID filter pills are now read-only — they display the agent context, show a lock icon and tooltip, and cannot be edited or removed. The Add Filter dropdown no longer lists scope-controlled fields so users cannot accidentally override the active scope.

    PropertyFilterApplied accepts a new lockedFieldIds (and optional lockedTooltipContent) prop. PropertyFilterCreator accepts a new hiddenFieldIds prop. Both are opt-in and unset by default, so existing usages are unaffected.

    // Before
    <PropertyFilterApplied fields={fields} tokens={tokens} onTokensChange={setTokens} />
    
    // After — pills for the listed fields render locked with a tooltip
    <PropertyFilterApplied
      fields={fields}
      tokens={tokens}
      onTokensChange={setTokens}
      lockedFieldIds={['rootEntityType', 'entityId']}
      lockedTooltipContent="This filter is set by the current context."
    />
  • Added CodeBlock component with select/tabs switcher, optional shiki syntax highlighting, and Notion-style hover-only copy button (always visible on touch devices via media query). (#16202)

  • Improved ScrollArea to use Base UI internally and added a richer mask API. Edges now fade by default based on orientation (top/bottom for vertical, left/right for horizontal, all four for both), so most scrollers get the polished fade-out automatically. (#16415)

    Heads up — default behavior change: ScrollArea previously rendered without any edge fade unless showMask was passed. It now fades the edges that match orientation by default. Pass mask={false} on the callsites where you want to keep the old hard edges.

    New mask prop. Accepts a boolean (false disables the fade entirely) or an object to override individual sides. The x and y keys are shorthands for the matching axis.

    // Default — fades follow `orientation`
    <ScrollArea>...</ScrollArea>
    
    // Opt out entirely
    <ScrollArea mask={false}>...</ScrollArea>
    
    // Keep only the top fade
    <ScrollArea mask={{ bottom: false }}>...</ScrollArea>
    
    // Vertical fades only on a two-axis scroller
    <ScrollArea orientation="both" mask={{ x: false }}>...</ScrollArea>

    Migrating from showMask. The showMask boolean is now deprecated but still works — mask wins when both are set.

    // Before
    <ScrollArea showMask>...</ScrollArea>
    <ScrollArea showMask={false}>...</ScrollArea>
    
    // After
    <ScrollArea>...</ScrollArea>             // default fade matches orientation
    <ScrollArea mask={false}>...</ScrollArea> // explicitly disable
  • Added an "All traces, nested too" mode to the Observability → Traces page. (#16479)

    The traces list now has a switcher in the toolbar to toggle between two views:

    • Top-level traces only (default) — one row per top-level run, the existing behavior.
    • All traces, nested too — one row per invocation, including every agent, workflow, tool, processor, scorer, and RAG ingestion that ran nested inside another run.

    This makes it possible to find every invocation of a given entity (e.g. "every run of recipe-maker workflow") regardless of how it was triggered. Selecting a row in the new mode opens a detail panel showing just that branch's subtree.

    New hooks for consumers building their own observability UIs:

    • useBranch({ traceId, spanId, depth? }) — fetches the span subtree rooted at an anchor span.
    • useTraceOrBranchSpans({ traceId, spanId, listMode }) — returns trace spans or a branch subtree depending on the active mode.
  • Added opt-in interactivity and per-page filter persistence support for observability UI components. (#15747)

    • MetricsLineChart accepts an onPointClick callback so chart points can drive drilldowns.
    • HorizontalBars accepts row-level and segment-level hrefs for linked metric bars without nested anchors.
    • MetricsDataTable accepts getRowHref(row) for linked rows with consistent hover and focus styling.
    • MetricsCard exposes an Actions slot in the top bar for contextual icon links.
    • Observability filter helpers for Metrics, Traces, and Logs each keep their own saved-filters storage key so pages remember filters independently.

    All additions are optional, so existing consumers continue to render the same way unless they pass the new props.

    <MetricsLineChart
      data={points}
      series={series}
      onPointClick={point => navigate(`/observability?dateFrom=${point.from}&dateTo=${point.to}`)}
    />
    
    <HorizontalBars data={[{ name: 'agent-a', values: [42, 3], href: '/observability?filterEntityName=agent-a' }]} />
    
    <MetricsDataTable columns={cols} data={rows} getRowHref={row => `/observability?filterThreadId=${row.threadId}`} />
    
    <MetricsCard>
      <MetricsCard.TopBar>
        <MetricsCard.TitleAndDescription title="Latency" />
        <MetricsCard.Actions>
          <IconButton href="/observability" />
        </MetricsCard.Actions>
      </MetricsCard.TopBar>
    </MetricsCard>
  • Added a new pill variant on TabList with an animated background indicator that slides behind the active trigger. The default line variant now animates its underline smoothly between tabs as well. Implemented by migrating the underlying Tabs component from Radix UI to Base UI. (#16414)

    // Before — only the line (underline) style was available
    <Tabs defaultTab="overview">
      <TabList>
        <Tab value="overview">Overview</Tab>
        <Tab value="projects">Projects</Tab>
      </TabList>
    </Tabs>
    
    // After — opt into the new pill style via the `variant` prop on TabList
    <Tabs defaultTab="overview">
      <TabList variant="pill">
        <Tab value="overview">Overview</Tab>
        <Tab value="projects">Projects</Tab>
      </TabList>
    </Tabs>

    The public API (Tabs, TabList, Tab, TabContent) is unchanged; existing call-sites keep the default line variant.

  • Added new pill-ghost variant on Tabs and sticky prop on TabList for sticky tab headers. (#16433)

    <Tabs defaultTab="overview">
      <TabList variant="pill-ghost" sticky>
        <Tab value="overview">Overview</Tab>
        <Tab value="settings">Settings</Tab>
      </TabList>
    </Tabs>

    Added variant prop on Combobox (default, ghost, link) for flexible trigger styling. Note: this prop existed previously but was a no-op; it now actually drives the trigger appearance, so callers passing variant will see updated styles.

    // Bordered form input (default)
    <Combobox options={options} value={value} onValueChange={setValue} />
    
    // Borderless, hover-only surface
    <Combobox options={options} value={value} onValueChange={setValue} variant="ghost" />
    
    // Text-only trigger
    <Combobox options={options} value={value} onValueChange={setValue} variant="link" />

    Improved EntityHeader layout — title and children now share a single row with wrapping, and padding is tighter for denser headers.

    Improved ScrollArea to handle vertical/horizontal orientation correctly, preventing forced horizontal scroll when only vertical is needed.

    Improved PanelSeparator with a pill-shaped handle that grows on hover/active for a clearer affordance.

    Removed Threads, ThreadList, ThreadItem, ThreadLink, ThreadDeleteButton exports. These had no consumers outside this repository. Downstream users relying on these exports should compose an equivalent list locally using the underlying DS primitives (Button, Icon, Txt) — the playground package now contains a reference implementation under src/components/thread-list.

    - import { Threads, ThreadList, ThreadItem, ThreadLink, ThreadDeleteButton } from '@mastra/playground-ui';
    + // Compose locally with @mastra/playground-ui primitives (Button, Icon, Txt)
    + // or copy the reference implementation from the playground package.
  • Added MainSidebar.NavLabel — collapse-aware label slot for asChild nav items. When the sidebar collapses to icon-only mode, the label hides via VisuallyHidden (still announced by screen readers) instead of leaking outside the 36px icon rail. The default link={...} path was already collapse-aware; asChild consumers now have a matching primitive. (#16167)

    // Before: text leaked visually when the sidebar collapsed
    <MainSidebar.NavLink asChild>
      <button>
        <Bot />
        Agents
      </button>
    </MainSidebar.NavLink>
    
    // After: wrap labels in MainSidebar.NavLabel
    <MainSidebar.NavLink asChild>
      <button>
        <Bot />
        <MainSidebar.NavLabel>Agents</MainSidebar.NavLabel>
      </button>
    </MainSidebar.NavLink>

    Improved truncation handling for nav items and section headers. Long labels now clip with a single-line ellipsis instead of wrapping to a second line during the collapse/expand transition, eliminating layout jumps at narrow widths.

  • Added SettingsRow primitive for label/description + control rows in settings pages. Markup mirrors the existing platform settings row pattern (flex justify-between with title + optional description on the left, control on the right) so consumers can adopt it without visual regressions. (#16150)

    Removed the redundant SelectField wrapper. Its only internal consumer (Studio settings) was migrated to SettingsRow + Select primitives. For form fields use SelectFieldBlock; for settings rows use SettingsRow.

    Before

    <SelectField name="theme" label="Theme mode" value={theme} onValueChange={setTheme} options={THEME_OPTIONS} />

    After

    <SettingsRow label="Theme mode" htmlFor="theme">
      <Select value={theme} onValueChange={setTheme}>
        <SelectTrigger id="theme" className="w-full sm:w-48">
          <SelectValue />
        </SelectTrigger>
        <SelectContent>{/* items */}</SelectContent>
      </Select>
    </SettingsRow>
  • Added InputGroup and extended ButtonsGroup in playground-ui design system. (#16417)

    New InputGroup component

    Compose inputs with leading or trailing icons, buttons, text labels, and keyboard hints. Supports inline (left/right) and block (top/bottom) addon alignment, and works with both inputs and textareas.

    import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupButton } from '@mastra/playground-ui';
    import { SearchIcon, XIcon } from 'lucide-react';
    
    <InputGroup>
      <InputGroupAddon>
        <SearchIcon />
      </InputGroupAddon>
      <InputGroupInput placeholder="Search..." />
      <InputGroupAddon align="inline-end">
        <InputGroupButton aria-label="Clear">
          <XIcon />
        </InputGroupButton>
      </InputGroupAddon>
    </InputGroup>;

    Extended ButtonsGroup

    Added orientation (horizontal | vertical), and new ButtonsGroupSeparator and ButtonsGroupText slots. Existing API unchanged.

    <ButtonsGroup spacing="close">
      <Button variant="outline">−</Button>
      <ButtonsGroupText>42</ButtonsGroupText>
      <Button variant="outline">+</Button>
    </ButtonsGroup>
    
    <ButtonsGroup orientation="vertical">
      <Button variant="ghost">Copy</Button>
      <ButtonsGroupSeparator />
      <Button variant="ghost">Cut</Button>
    </ButtonsGroup>

    Tweaked Button ghost variant

    Aligned hover/active progression with the outline variant (surface3surface4) so click feedback is perceptible on transparent backgrounds. Existing ghost buttons throughout the playground will appear one shade lighter on hover and active.

Patch Changes
  • Improved Studio's Traces page to scale smoothly to many traces. The list now renders only the visible window, so scrolling stays responsive and memory usage stays bounded regardless of how many traces are loaded. (#16262)

  • Filter pills (PropertyFilterApplied) now match the rest of the design system — single 1px border, consistent rounded segments, no custom styling. Labels stay on one line and no longer compress when long values are present. (#16426)

    ButtonsGroupText segments also no longer wrap to multiple lines or shrink under flex pressure, which makes them safer to drop into any tight layout.

  • Fixed three issues on the Logs and Traces pages: (#16306)

    • Column widths now stay stable while scrolling — they no longer jump as different rows scroll into view.
    • Scrolling far down the Logs list no longer scrambles rows (duplicates, gaps, or empty rows after additional pages load).
    • Changing a filter or the date range now scrolls the list back to the top, instead of leaving an empty band above the new data until you nudge the scroll. Logs and Traces now behave the same way on filter changes.
  • Added NoTracesInfo component that informs the user there are no traces for the active date range. (#16303)

  • Refreshed the visual style of form controls and popups for a softer, more consistent look: (#16150)

    • Button: thinner border (border instead of border-2); text-mode buttons use rounded-full; icon-mode buttons are circular.
    • Combobox / Select / DropdownMenu / Command: triggers and items aligned on the form-input look — rounded-lg border, transparent background, soft hover/open states, consistent text-ui-smd typography.
    • Popups (Popover / Tooltip / Select / Dropdown content): rounded-xl containers with shadow-dialog; inner items rounded-lg inside p-1.
    • Tokens: bumped the radius scale (sm 2→4px, md 4→6px, lg 6→10px, xl 12→14px); replaced --shadow-dialog's outer 1px ring with an inset top gloss so the dialog shadow stops doubling up with each consumer's own border.
  • Polished Combobox dropdown items (#16411)

    • Moved the selection check to the right of each item so unselected rows no longer carry an awkward left padding gap and the whole list aligns consistently.
    • Tightened popup search/empty padding and softened the trigger hover for a calmer command-palette feel.

    Added ComboboxPrimitive export for advanced compositions

    Re-exports the raw @base-ui/react/combobox parts (Root, Trigger, Input, List, Item, Chips, etc.) so callers needing virtualization, async status, chips, or creatable patterns can compose them directly with the shared comboboxStyles tokens — without growing the monolithic <Combobox> prop surface.

    import { ComboboxPrimitive, comboboxStyles } from '@mastra/playground-ui';
    
    <ComboboxPrimitive.Root items={items}>
      <ComboboxPrimitive.Input className={comboboxStyles.searchInput} />
      <ComboboxPrimitive.Portal>
        <ComboboxPrimitive.Positioner>
          <ComboboxPrimitive.Popup className={comboboxStyles.popup}>
            <ComboboxPrimitive.List className={comboboxStyles.list}>
              {item => (
                <ComboboxPrimitive.Item value={item} className={comboboxStyles.item}>
                  {item.label}
                </ComboboxPrimitive.Item>
              )}
            </ComboboxPrimitive.List>
          </ComboboxPrimitive.Popup>
        </ComboboxPrimitive.Positioner>
      </ComboboxPrimitive.Portal>
    </ComboboxPrimitive.Root>;
  • Aligned Badge variant colors with the Notice and Toast palette so the design system uses one consistent set of semantic colors. Default badges keep their neutral surface, while success, error, info and warning variants now use the same notice tokens as Notices and Toasts. Icons inside badges are sized down to match the badge height. (#16215)

  • Improved Studio's Logs page to scale smoothly to many log records. The list now renders only the visible window, so scrolling stays responsive and memory usage stays bounded regardless of how many logs are loaded. (#16263)

  • Fixed Studio streaming render behavior for interleaved reasoning and improved chat autoscroll during rapid output. (#16331)

  • Restored auto-refresh on the traces list page, polling every 10 seconds. Polling is paused while the request is forbidden (HTTP 403) to avoid flicker. (#16204)

  • Refined Combobox and Select trigger interactions with an active state and fixed value truncation when a leading badge is rendered. Refreshed PanelSeparator with a clearer hover/active affordance, an enlarged hit area, and a focus-visible accent. Removed the default bg-surface2 background from Threads so consumers can control the surface color. (#16269)

  • Changed the default Observability list mode to branches (all traces, including nested). The query logic still recognizes ?listMode=traces to opt back into the top-level-only view. (#16493)

    Before

    /observability → top-level traces only

    After

    /observability → branches (all traces, nested too) /observability?listMode=traces → top-level traces only

  • Fixed Notice component alignment with action: button now stays at default sm size, vertically aligns to first text line via negative margin compensation, and stacks below the message as a full-width button on narrow containers. (#16150)

@mastra/react@0.3.0
Minor Changes
  • Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)

    const { sendMessage } = useChat({ agentId, resourceId, threadId });
    await sendMessage({ message: 'Follow up while streaming', threadId });
Patch Changes
  • Fixed streaming chat messages so sending while an agent is running does not duplicate assistant output or leave the previous response marked as streaming. (#16338)

  • Fixed Studio streaming render behavior for interleaved reasoning and improved chat autoscroll during rapid output. (#16331)

  • Handle background-task-suspended chunks in toUIMessage so suspend metadata is restored after resume. (#16260)

@mastra/redis@1.1.1
Patch Changes
  • Per-key TTL support in RedisCache (#16283)

    RedisCache.set() now accepts an optional ttlMs argument that overrides the configured default TTL for a single entry. Sub-second values are rounded up to one second (Redis EXPIRE granularity); a non-positive value persists the entry without expiry.

    const cache = new RedisCache({ url: 'redis://...' });
    await cache.set('weather:nyc', payload, 60_000); // expires in 60s
    await cache.set('manifest', payload, 0); // persists indefinitely
  • Respect optional resourceId in getThreadById so scoped thread lookups return null when the thread belongs to a different resource. (#14237)

    Example:

    const thread = await memory.getThreadById({
      threadId: 'my-thread-id',
      resourceId: 'my-user-id',
    });
    // Returns null if the thread does not belong to 'my-user-id'.
@mastra/redis-streams@0.0.2
Patch Changes
  • Worker review fixes: (#16309)

    • Step-execution endpoint (POST /workflows/:id/runs/:runId/steps/execute) is now gated by Mastra's standard requiresAuth: true + authenticateToken pipeline rather than a parallel "worker secret" body field. The previously introduced workerSecret config knob and MASTRA_WORKER_SECRET env var have been removed (they were never released). To gate the endpoint on a standalone-worker deployment, configure an auth provider on the server's Mastra instance — without one the framework currently treats requiresAuth: true as a no-op for this route.
    • HttpRemoteStrategy now sends credentials as a normal Authorization: Bearer <token> header. The token comes from the new MASTRA_WORKER_AUTH_TOKEN env var or an explicit auth constructor option.
    • Honor the caller's abortSignal in HttpRemoteStrategy by combining it with the per-request timeout via AbortSignal.any (with a manual fallback for runtimes that don't expose it).
    • Implement comma-separated name filtering for the MASTRA_WORKERS env var. MASTRA_WORKERS=scheduler,backgroundTasks now boots only those named workers; MASTRA_WORKERS=false still disables all workers.
    • Restore Mastra.startEventEngine / stopEventEngine as @deprecated aliases for the renamed startWorkers / stopWorkers.
    • BackgroundTaskWorker now subscribes to PubSub in start() instead of init(), matching the lifecycle of the other workers and making isRunning accurately reflect subscription state.
    • RedisStreamsPubSub adds a maxDeliveryAttempts option (default 5) that drops events after the configured number of failed deliveries instead of redelivering forever, and replaces empty catch {} blocks with logger.warn/logger.debug calls.
    • RedisStreamsPubSub.unsubscribe(topic, cb) now honors the topic argument so the same callback can be subscribed to multiple topics independently.
    • PullTransport guards the async router callback against unhandled promise rejections by attaching a .catch that nacks the message.
    • Drop the dead MASTRA_WORKER_NAME env var injection in the CLI worker spawn (the bundle entrypoint already passes the worker name directly).
    • Add a real cross-process e2e auth suite (pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong token, missing token, anonymous direct hits, and the no-auth-provider pin-down behavior.
    • Step-execution route now has a response schema, satisfying schema-consistency.test.ts.
    • Internal type cleanups (drop several as any casts in worker strategies and BackgroundTaskWorker).
    • RedisStreamsPubSub.maxDeliveryAttempts now rejects negative / NaN values at construction. 0 still means "no cap" for back-compat but emits a one-time warning; pass Infinity to disable the cap explicitly.
    • PullTransport accepts a logger and uses it for unhandled router-callback rejections instead of console.error.
    • BackgroundTaskWorker.start() now throws if init() was not called, matching the contract of the other workers.
    • Cross-process integration tests now spawn a single user-owned project (test-fixtures/cli-project/src/mastra/index.ts) through two generic entries that mirror what BuildBundler and WorkerBundler emit. The previous one-off server.entry.ts / worker.entry.ts / scheduler.entry.ts / background.entry.ts files have been deleted — they implied users hand-roll entry files, which they don't. Worker role is selected via MASTRA_WORKERS exactly as in production.

    Push-capable PubSub:

    • The PubSub abstract class now declares a supportedModes getter (defaulting to ['pull'] for backward compatibility) so consumers can tell whether a broker delivers events through a pull loop, an in-process push, or an out-of-process HTTP push. EventEmitterPubSub reports ['pull', 'push'] (EventEmitter dispatches synchronously and works for either path), @mastra/redis-streams reports ['pull'].
    • Mastra now exposes a public handleWorkflowEvent(event) method backed by a shared WorkflowEventProcessor. It is the single entry point used by the existing pull-mode OrchestrationWorker, by in-process push pubsubs (auto-wired during startWorkers()), and by the new POST /api/workflows/events route which lets push-mode brokers (GCP Pub/Sub push, SNS, EventBridge) deliver events over HTTP.
    • When the configured pubsub does not support 'pull', Mastra automatically skips creating an OrchestrationWorker and OrchestrationWorker.init() throws a clear error if it is constructed against a push-only pubsub.
    • WorkflowEventProcessor gains a handle(event) method that returns a structured { ok, retry } result. The original process(event, ack?) method is preserved as a thin wrapper for back-compat.

    Public-API example for a push-capable PubSub:

    import { Mastra } from '@mastra/core/mastra';
    import { EventEmitterPubSub } from '@mastra/core/pubsub';
    
    const mastra = new Mastra({
      // A push-capable broker (GCP Pub/Sub push, SNS, EventEmitter, …).
      // EventEmitterPubSub reports supportedModes = ['pull', 'push'].
      pubsub: new EventEmitterPubSub(),
      workflows: { myWorkflow },
    });
    
    // In-process push pubsubs are auto-wired here. For out-of-process
    // push (e.g. HTTP webhook from a cloud broker), POST the event to
    // /api/workflows/events on your Mastra server instead.
    await mastra.startWorkers();
    
    // Direct invocation (e.g. inside an HTTP handler that bridges from a
    // cloud broker's push delivery):
    await mastra.handleWorkflowEvent({
      id: 'evt-1',
      type: 'workflow.start',
      runId: 'run-1',
      createdAt: new Date(),
      data: { workflowId: 'myWorkflow', inputData: { name: 'world' } },
    });

    CI follow-ups:

    • Mastra only auto-registers SchedulerWorker when storage is configured. Without storage the worker would crash on startup (deps.storage.getStore on undefined); the scheduler now silently no-ops in that case, matching the pre-worker scheduler behavior.
    • SchedulerWorker.init defensively logs and returns when called without storage instead of throwing a TypeError.
    • RECEIVE_WORKFLOW_EVENT_ROUTE (POST /workflows/events) createdAt is now a plain z.string() on the wire and the handler converts it to a Date (validating "Invalid Date" -> 400). The previous union(...).transform().refine() schema couldn't be exercised by the shared adapter test suite because the generator didn't unwrap Zod 4's ZodPipe.
    • _test-utils/route-test-utils recognizes Zod 4's number_format check (used for int() / safeint()), and generateContextualValue now produces a valid ISO timestamp for createdAt / updatedAt fields.
@mastra/schema-compat@1.2.10
Patch Changes
  • Fixed Google-compatible schema conversion so Gemini accepts broad nullable tool parameters. (#16129)
@mastra/server@1.33.0
Minor Changes
  • Added in-memory A2A push notification support for task updates. (#16175)

    Clients can now register push notification configs with message/send, message/stream, or the tasks/pushNotificationConfig/* methods. The server advertises push notification support in the agent card and sends the current task snapshot to registered webhooks when a task reaches completed, failed, canceled, or input-required.

    Webhook delivery validates the configured URL and pins outbound delivery to the validated address to reduce DNS rebinding risk. This remains in-memory and best-effort; operators should still use normal egress controls and avoid exposing push delivery to networks with sensitive internal services unless they trust the configured webhook targets.

    await a2a.setTaskPushNotificationConfig({
      taskId: 'task-123',
      pushNotificationConfig: {
        url: 'https://example.com/a2a-webhook',
        token: 'session-token',
      },
    });
  • Allow stored Responses API follow-up requests to use previous_response_id without also passing agent_id. (#16246)

    When callers pass both previous_response_id and an explicit agent_id, mismatched agents now return a clear 400 response instead of looking like a missing stored response.

    The create-response schema now also rejects empty agent_id and previous_response_id strings.

  • Added client, React, and Studio support for Agent signals in threaded chat. Threaded user messages now send through Agent signals, stream output is consumed from the thread subscription, echoed user messages are deduped by signal ID, file and image message contents are preserved, Studio can send follow-ups while a response is streaming, Studio subscribes to open threads so additional tabs can observe active streams, and the Studio stop button aborts the active thread subscription. React chat also falls back to legacy threaded streaming when it connects to a server or core version that does not support signal routes yet. (#16338)

    const { sendMessage } = useChat({ agentId, resourceId, threadId });
    await sendMessage({ message: 'Follow up while streaming', threadId });
  • Added Agent signals for sending contextual messages into agent thread loops and subscribing to thread activity. (#16229)

    Call agent.sendSignal() to inject context into a running agent loop. When the thread is idle, that same signal becomes the prompt that starts the next loop by default. Use ifActive.behavior and ifIdle.behavior to deliver, persist, discard, or wake from a signal.

    Use agent.subscribeToThread() to follow the raw stream chunks for a memory thread, observe signal echoes with stable IDs, and abort the active stream for that thread.

    const subscription = await agent.subscribeToThread({ resourceId, threadId });
    
    void (async () => {
      for await (const part of subscription.stream) {
        if (part.type === 'finish') {
          subscription.unsubscribe();
        }
      }
    })();
    
    agent.sendSignal({ type: 'user-message', contents: 'Use the latest answer' }, { resourceId, threadId });
  • Responses streams now emit tool call events so clients can track tool arguments and results in real time. (#16285)

    Tool outputs now use consistent IDs (<toolCallId>:output) so streamed arguments can be matched to completed results.

    for await (const event of stream) {
      if (event.type === 'response.function_call_arguments.delta') {
        console.log(event.delta);
      }
    
      if (event.type === 'response.output_item.done' && event.item.type === 'function_call') {
        console.log(event.item.id);
      }
    }
  • Added support for signed A2A Agent Cards. (#16207)

    Example

    const mastra = new Mastra({
      server: {
        a2a: {
          agentCardSigning: {
            privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!,
            protectedHeader: {
              alg: 'ES256',
              kid: 'agent-card-key',
            },
            header: {
              issuer: 'mastra',
            },
          },
        },
      },
    });

    Mastra now conditionally signs served A2A Agent Cards via signAgentCard(...) when server.a2a.agentCardSigning is configured, and the A2A Agent Card response schema now includes the signatures array.

  • Added mastra api, a machine-readable runtime CLI for calling Mastra server resources with JSON input and output. (#16128)

    The new API CLI supports agents, workflows, tools, MCP servers, memory threads, working memory, observability traces/logs/scores, datasets, and experiments. It includes schema-aware request handling so a single JSON input is split into path, query, and body fields based on server route contracts, plus ergonomic raw-input wrapping for tool execution.

    Exposed a route-derived server API schema manifest at runtime and generated CLI route metadata from it, enabling --schema output, response-shape-aware normalization, and server-aligned pagination output.

  • Add POST /api/agents/:agentId/resume-stream-until-idle SSE route, mirroring agent.resumeStreamUntilIdle(). (#16260)

  • Worker review fixes: (#16309)

    • Step-execution endpoint (POST /workflows/:id/runs/:runId/steps/execute) is now gated by Mastra's standard requiresAuth: true + authenticateToken pipeline rather than a parallel "worker secret" body field. The previously introduced workerSecret config knob and MASTRA_WORKER_SECRET env var have been removed (they were never released). To gate the endpoint on a standalone-worker deployment, configure an auth provider on the server's Mastra instance — without one the framework currently treats requiresAuth: true as a no-op for this route.
    • HttpRemoteStrategy now sends credentials as a normal Authorization: Bearer <token> header. The token comes from the new MASTRA_WORKER_AUTH_TOKEN env var or an explicit auth constructor option.
    • Honor the caller's abortSignal in HttpRemoteStrategy by combining it with the per-request timeout via AbortSignal.any (with a manual fallback for runtimes that don't expose it).
    • Implement comma-separated name filtering for the MASTRA_WORKERS env var. MASTRA_WORKERS=scheduler,backgroundTasks now boots only those named workers; MASTRA_WORKERS=false still disables all workers.
    • Restore Mastra.startEventEngine / stopEventEngine as @deprecated aliases for the renamed startWorkers / stopWorkers.
    • BackgroundTaskWorker now subscribes to PubSub in start() instead of init(), matching the lifecycle of the other workers and making isRunning accurately reflect subscription state.
    • RedisStreamsPubSub adds a maxDeliveryAttempts option (default 5) that drops events after the configured number of failed deliveries instead of redelivering forever, and replaces empty catch {} blocks with logger.warn/logger.debug calls.
    • RedisStreamsPubSub.unsubscribe(topic, cb) now honors the topic argument so the same callback can be subscribed to multiple topics independently.
    • PullTransport guards the async router callback against unhandled promise rejections by attaching a .catch that nacks the message.
    • Drop the dead MASTRA_WORKER_NAME env var injection in the CLI worker spawn (the bundle entrypoint already passes the worker name directly).
    • Add a real cross-process e2e auth suite (pubsub/redis-streams/src/auth-e2e.test.ts) covering happy path, wrong token, missing token, anonymous direct hits, and the no-auth-provider pin-down behavior.
    • Step-execution route now has a response schema, satisfying schema-consistency.test.ts.
    • Internal type cleanups (drop several as any casts in worker strategies and BackgroundTaskWorker).
    • RedisStreamsPubSub.maxDeliveryAttempts now rejects negative / NaN values at construction. 0 still means "no cap" for back-compat but emits a one-time warning; pass Infinity to disable the cap explicitly.
    • PullTransport accepts a logger and uses it for unhandled router-callback rejections instead of console.error.
    • BackgroundTaskWorker.start() now throws if init() was not called, matching the contract of the other workers.
    • Cross-process integration tests now spawn a single user-owned project (test-fixtures/cli-project/src/mastra/index.ts) through two generic entries that mirror what BuildBundler and `Worker
@mastra/core@1.32.0

Highlights

Fine-Grained Authorization (FGA) across Mastra (core + server adapters + MCP)

Mastra now supports relationship-based, resource-level authorization with centralized enforcement before agent runs (generate()/stream()), tool/workflow execution, and memory thread access via new IFGAProvider/IFGAManager + checkFGA()/FGADeniedError. FGA is also enforced consistently in @mastra/server handlers, all built-in server adapters (Express/Fastify/Hono/Koa), and MCP tool execution.

WorkOS-backed FGA provider (@mastra/auth-workos)

New MastraFGAWorkos implements IFGAManager on top of the WorkOS Authorization API, including check/require/filterAccessible, resource CRUD, role assignments, org scoping, JWT/bearer-token context handling, and permission/resource mappings for multi-tenant setups.

Scheduled Workflows (cron-native) + scheduler + full HTTP/Studio/client support

Workflows can now declare schedule in createWorkflow() to run on cron with automatic promotion to the evented engine, type-checked inputData/initialState/requestContext, and safe multi-instance claiming (CAS) via WorkflowScheduler. End-to-end support includes new schedules storage domains/adapters (PG/LibSQL/MongoDB + in-memory for tests), new @mastra/server routes + @mastra/client-js schedule methods, and new Studio UI for listing schedules, viewing trigger history, and durable pause/resume.

MCP Apps: interactive UI resources over MCP (SEP-1865) + resource listing/reading APIs

MCP servers can now publish ui:// HTML app resources via appResources, with new listResources()/readResource() on MCPServerBase and client/server endpoints (MastraClient.getMcpServerResources() / readMcpServerResource(), plus @mastra/server routes). @mastra/mcp adds proxy helpers (MCPClientServerProxy, toMCPServerProxies()) and stamps serverId into tool metadata to support multi-server UI resolution.

Observability upgrades: nested-run querying + bounded high-cardinality metrics + Datadog bridge

New listBranches/getBranch (plus getSpans and getStructure) let you find and fetch runs even when an entity only appears as a nested span (not root traces), with corresponding HTTP endpoints and store implementations (ClickHouse/DuckDB). Metrics now support count_distinct plus server-side TopK (limit, ordering) for fast dashboards on high-cardinality fields; ClickHouse also adds skip indexes to speed drilldowns, and a new DatadogBridge keeps auto-instrumented spans correctly nested under Mastra spans.

Breaking Changes
  • Schedules storage schema changed (mastra_schedules, mastra_schedule_triggers): adds ownerType/ownerId, renames ScheduleTrigger.statusoutcome, adds trigger id PK plus triggerKind/parentTriggerId/metadata; GET /api/schedules/:id/triggers now returns outcome instead of status (no compat shim; scheduled workflows are alpha).
  • @mastra/client-js Vector return types changed to match runtime:
    • vector.getIndexes()string[] (was { indexes: string[] })
    • vector.upsert(){ ids: string[] } (was string[])
    • vector.query()QueryResult[] (was { results: QueryResult[] })

Changelog

@mastra/core@1.32.0
Minor Changes
  • Added Fine-Grained Authorization (FGA) support for relationship-based, resource-level access control. FGA answers "can this user perform this action on this specific resource?" — enabling multi-tenant isolation and per-resource permissions. (#15410)

    New interfaces: IFGAProvider (read-only checks) and IFGAManager (read + write operations) with types for access checks, resources, and role assignments.

    Enforcement at all execution points: FGA checks are automatically enforced before agent execution (generate(), stream()), tool execution, workflow execution, and memory thread access. When no FGA provider is configured, all checks are skipped (backward compatible).

    New utility: checkFGA() provides centralized FGA enforcement with FGADeniedError for denied checks. MastraMemory.checkThreadFGA() adds thread-level access control.

    Request-aware authorization: Resource ID resolvers receive request context so route-level FGA checks can derive tenant- or request-scoped resource IDs.

    Typed permission constants: Strongly-typed permission identifiers (e.g. 'agents:execute', 'workflows:execute', 'memory:threads:read') for use in authorization config and permissionMapping.

    const mastra = new Mastra({
      server: {
        fga: new MastraFGAWorkos({ apiKey, clientId }),
      },
    });
  • Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to mastra_schedules and mastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)

    • Schedule gains optional ownerType / ownerId so a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.
    • ScheduleTrigger.status is renamed to outcome and the type is widened to ScheduleTriggerOutcome so future outcome values can be added without another rename.
    • ScheduleTrigger gains a stable id primary key and new triggerKind, parentTriggerId, and metadata fields. triggerKind distinguishes schedule-fire rows from later queue-drain rows (used by upcoming heartbeat work); parentTriggerId links related rows; metadata carries outcome-specific context.
    • The libsql, pg, and mongodb adapters all add the new columns/indexes. Their @mastra/core peer dependency is tightened to >=1.32.0-0 <2.0.0-0 so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field.
    • Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The triggers response on GET /api/schedules/:id/triggers now returns outcome instead of status.
    • The bundled Studio (Mastra CLI) is updated to read outcome so the schedule detail page keeps polling and rendering publish-failure rows correctly.
  • Added listResources() and readResource() abstract methods to MCPServerBase, enabling MCP servers to expose app resources. These resources power interactive UI rendering (MCP Apps) in Studio and other consumers. (#16004)

  • Added count_distinct aggregation and server-side TopK to the metrics storage API so dashboards built on high-cardinality fields (like threadId or resourceId) stay fast and bounded. (#16137)

    New aggregation

    getMetricAggregate, getMetricBreakdown, and getMetricTimeSeries accept aggregation: 'count_distinct' with a distinctColumn. Backends pick the most efficient native implementation — uniq on ClickHouse, approx_count_distinct on DuckDB.

    distinctColumn is restricted to a low/medium-cardinality categorical allowlist (entityType, entityName, parentEntityType, parentEntityName, rootEntityType, rootEntityName, name, provider, model, environment, executionSource, serviceName). ID columns are not allowed — distinct counts over near-unique values converge to the row count and are rarely useful.

    await store.getMetricAggregate({
      name: ['mastra_llm_tokens_total'],
      aggregation: 'count_distinct',
      distinctColumn: 'model',
      filters: { timestamp: { start, end } },
    });

    Server-side TopK

    getMetricBreakdown accepts limit and orderDirection, so breakdowns never return the full cardinality of a column from the database. Ordering is always by the aggregated value; orderDirection flips between top-N (DESC, default) and bottom-N (ASC).

    await store.getMetricBreakdown({
      name: ['mastra_agent_duration_ms'],
      aggregation: 'sum',
      groupBy: ['threadId'],
      limit: 20,
      orderDirection: 'DESC',
    });
  • Added RegexFilterProcessor — a zero-cost regex-based content filter for blocking, redacting, or warning on pattern matches in agent messages. Includes built-in presets for PII, secrets, URLs, and prompt injection patterns. Supports input, output, and streaming phases. (#16058)

  • Added scheduled workflows. Declare a schedule on createWorkflow and Mastra fires the workflow on cron with no extra wiring. (#15830)

    import { createWorkflow } from '@mastra/core/workflows';
    
    const dailyReport = createWorkflow({
      id: 'daily-report',
      inputSchema: z.object({ date: z.string() }),
      outputSchema: z.object({ summary: z.string() }),
      schedule: {
        cron: '0 9 * * *',
        timezone: 'America/Los_Angeles',
        inputData: { date: 'today' },
      },
    })
      .then(/* steps */)
      .commit();

    A workflow with a schedule is auto-promoted to the evented engine, so scheduled fires share the same execution path as manual start() calls. inputData, initialState, and requestContext on the schedule are type-checked against the workflow's schemas at definition time. Pass an array of schedules with stable ids to fire one workflow on multiple crons.

    Mastra auto-instantiates a WorkflowScheduler when any registered workflow declares a schedule. The scheduler claims due schedules via compare-and-swap, so multiple instances polling the same storage cannot double-fire. Projects with no scheduled workflows pay zero cost. Configure with new Mastra({ scheduler: { tickIntervalMs, batchSize, enabled, onError } }).

    Requires a storage adapter that implements the new schedules domain (@mastra/libsql and @mastra/pg ship adapters; InMemorySchedulesStorage is included for tests). Adds a croner dependency.

  • Added MCP Apps extension support (SEP-1865). MCPServer now accepts an appResources config to register interactive ui:// HTML resources. MCPClient preserves full tool _meta (including ui.resourceUri) when converting MCP tools to Mastra tools. Both advertise the io.modelcontextprotocol/ui extension capability. (#16004)

    Example — MCPServer with app resources:

    const server = new MCPServer({
      name: 'my-server',
      tools: { myTool },
      appResources: {
        dashboard: {
          name: 'Dashboard',
          description: 'Interactive dashboard UI',
          html: '<html>...</html>',
        },
      },
    });
  • Added CostGuardProcessor, a built-in processor for enforcing monetary cost limits across agent runs. Supports run, resource, and thread scopes with configurable time windows (default 7 days), blocking or warning when limits are reached. Also added onViolation callback to the base Processor interface for generalized violation handling across all processors. (#16057)

    import { Agent } from '@mastra/core/agent';
    import { CostGuardProcessor } from '@mastra/core/processors';
    
    const costGuard = new CostGuardProcessor({
      maxCost: 5.0,
      scope: 'resource',
      window: '24h',
      strategy: 'block',
    });
    
    costGuard.onViolation = ({ processorId, message, detail }) => {
      console.log(`[${processorId}] ${message}`, detail);
    };
    
    const agent = new Agent({
      name: 'my-agent',
      model: 'openai/gpt-5-nano',
      inputProcessors: [costGuard],
    });
  • Added listBranches and getBranch for querying named-entity invocations across traces, including nested ones. listTraces only returns root-rooted traces, so an entity that always runs as a child (e.g., an Observer agent inside a workflow) wasn't queryable before. (#16154)

    // Before: nested-only entities returned nothing
    await store.listTraces({ filters: { entityName: 'Observer' } }); // []
    
    // After: one row per AGENT_RUN, WORKFLOW_RUN, PROCESSOR_RUN, SCORER_RUN,
    // RAG_INGESTION, TOOL_CALL, or MCP_TOOL_CALL span
    await store.listBranches({ filters: { entityName: 'Observer' } });
    
    // Plus: fetch the subtree at any span, with optional depth
    const branch = await store.getBranch({ traceId, spanId, depth: 1 });

    Added getStructure({ traceId }) (canonical name for the lightweight trace skeleton; getTraceLight retained as a deprecated alias) and getSpans({ traceId, spanIds }) (batch-fetch spans by id, used internally by getBranch to avoid pulling whole traces).

Patch Changes
  • Update provider registry and model documentation with latest models and providers (6dcd65f)

  • Fixed Harness token usage so provider-reported totals, reasoning tokens, and cache token fields are preserved. Fixes https://github.com/mastra-ai/mastra/issues/16055 (#16072)

  • Fixed supervisor output processors so they can filter streamed chunks from delegated sub-agents. (#16071)

  • Fixed Observational Memory model resolution for user-defined gateways. Models such as cloudflare/google/gemini-2.5-flash-lite now resolve through registered gateways instead of failing with provider-config errors. Closes #13841. (#16083)

  • Fixed model step traces to show the final prompt sent to the model, including memory-injected system messages. (#16029)

  • Fixed tool results dropping provider metadata from the original tool call. (#16078)

  • Fixed workflow request context serialization to skip values that cannot be safely stored as JSON. Fixes #16043. (#16061)

  • Fix buildResumedBlockResult returning suspended instead of failed when a step throws after resume in a parallel or conditional block (#14410)

  • Fixed serializeRequestContext to handle plain Map instances passed as requestContext, restoring backward compatibility broken in #16061 (#16081)

  • Added direct score lookup support to observability storage so score records can be fetched by scoreId without scanning paginated score lists, including DuckDB and ClickHouse vNext observability stores. (#16162)

  • fix(harness): use type 'image' and mimeType for image parts in convertToHarnessMessage to fix Gemini image recognition (#13917)

  • Fixed TokenLimiterProcessor failing silently when no input messages fit the token budget. (#16063)

@mastra/auth-workos@1.2.0
Minor Changes
  • Added MastraFGAWorkos provider for Fine-Grained Authorization using the WorkOS Authorization API. Implements IFGAManager interface with support for: (#15410)

    • Authorization checks (check(), require(), filterAccessible())
    • Resource management (createResource(), getResource(), listResources(), updateResource(), deleteResource())
    • Role assignments (assignRole(), removeRole(), listRoleAssignments())
    • resourceMapping and permissionMapping for translating Mastra resource types and permissions to WorkOS resource type slugs and permission slugs
    • Organization scoping that denies access when the user is not a member of the configured organization
    • Bearer-token / verified JWT support that carries service-token FGA context such as organization membership IDs, while ignoring JWT-derived memberships unless organization claims are trusted
    • Membership caching and batched accessible-resource discovery for lower per-request latency
    • Tenant inference and parent-resource filtering for scoped access checks
    • Paginated organization membership lookup and limited concurrent FGA checks when filtering accessible resources
    • Typed permission constants accepted in permissionMapping
    import { MastraFGAWorkos } from '@mastra/auth-workos';
    
    const fga = new MastraFGAWorkos({
      organizationId: 'org_abc123',
      resourceMapping: {
        agent: { fgaResourceType: 'team', deriveId: ctx => ctx.user.teamId },
      },
      permissionMapping: {
        'agents:execute': 'manage-workflows',
      },
    });
    
    // Check whether a user can execute an agent
    const allowed = await fga.check(user, {
      resource: { type: 'agent', id: 'my-agent' },
      permission: 'agents:execute',
    });
Patch Changes
@mastra/clickhouse@1.7.0
Minor Changes
  • Improved metric drilldown performance with skip indexes on the high-cardinality ID columns of metric_events. Dashboard queries that filter metrics by traceId, threadId, resourceId, userId, organizationId, experimentId, runId, sessionId, or requestId skip data chunks that don't contain the filtered value instead of scanning the full time range. (#16138)

    Equality (=) and IN filters benefit automatically. Aggregations and GROUP BY queries without a filter on these columns are unaffected.

    Migration

    Existing deployments pick up the indexes on next start. The migration is metadata-only and instant — no table lock, no rewrite, no downtime. Insert overhead is negligible and index storage is well under 1% of table size. Existing data is indexed lazily as parts merge under normal retention; no operator action is required.

  • Added count_distinct aggregation and server-side TopK to the metrics storage API so dashboards built on high-cardinality fields (like threadId or resourceId) stay fast and bounded. (#16137)

    New aggregation

    getMetricAggregate, getMetricBreakdown, and getMetricTimeSeries accept aggregation: 'count_distinct' with a distinctColumn. Backends pick the most efficient native implementation — uniq on ClickHouse, approx_count_distinct on DuckDB.

    distinctColumn is restricted to a low/medium-cardinality categorical allowlist (entityType, entityName, parentEntityType, parentEntityName, rootEntityType, rootEntityName, name, provider, model, environment, executionSource, serviceName). ID columns are not allowed — distinct counts over near-unique values converge to the row count and are rarely useful.

    await store.getMetricAggregate({
      name: ['mastra_llm_tokens_total'],
      aggregation: 'count_distinct',
      distinctColumn: 'model',
      filters: { timestamp: { start, end } },
    });

    Server-side TopK

    getMetricBreakdown accepts limit and orderDirection, so breakdowns never return the full cardinality of a column from the database. Ordering is always by the aggregated value; orderDirection flips between top-N (DESC, default) and bottom-N (ASC).

    await store.getMetricBreakdown({
      name: ['mastra_agent_duration_ms'],
      aggregation: 'sum',
      groupBy: ['threadId'],
      limit: 20,
      orderDirection: 'DESC',
    });
    • Added listBranches and getSpans implementations. (#16154)
    • Only spans recorded after this version is deployed are queryable via listBranches; historical traces remain accessible through the existing listTraces / getTrace APIs.
Patch Changes
  • Added direct score lookup support to observability storage so score records can be fetched by scoreId without scanning paginated score lists, including DuckDB and ClickHouse vNext observability stores. (#16162)
@mastra/client-js@1.17.0
Minor Changes
  • Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to mastra_schedules and mastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)

    • Schedule gains optional ownerType / ownerId so a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.
    • ScheduleTrigger.status is renamed to outcome and the type is widened to ScheduleTriggerOutcome so future outcome values can be added without another rename.
    • ScheduleTrigger gains a stable id primary key and new triggerKind, parentTriggerId, and metadata fields. triggerKind distinguishes schedule-fire rows from later queue-drain rows (used by upcoming heartbeat work); parentTriggerId links related rows; metadata carries outcome-specific context.
    • The libsql, pg, and mongodb adapters all add the new columns/indexes. Their @mastra/core peer dependency is tightened to >=1.32.0-0 <2.0.0-0 so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field.
    • Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The triggers response on GET /api/schedules/:id/triggers now returns outcome instead of status.
    • The bundled Studio (Mastra CLI) is updated to read outcome so the schedule detail page keeps polling and rendering publish-failure rows correctly.
  • Added getMcpServerResources() and readMcpServerResource() methods to MastraClient for listing and reading MCP server resources from the client SDK. These methods enable frontend applications to fetch app resource HTML for interactive MCP Apps rendering. (#16004)

    const client = new MastraClient();
    
    // List resources on an MCP server
    const resources = await client.getMcpServerResources('my-server');
    
    // Read a specific app resource
    const resource = await client.readMcpServerResource('my-server', 'ui://calculator/app');
  • Added schedule methods to the client for the new scheduled workflows feature. (#15830)

    import { MastraClient } from '@mastra/client-js';
    
    const client = new MastraClient({ baseUrl: 'http://localhost:4111' });
    
    const schedules = await client.listSchedules({ workflowId: 'daily-report' });
    const schedule = await client.getSchedule('wf_daily-report');
    const triggers = await client.listScheduleTriggers('wf_daily-report', { limit: 50 });
    
    await client.pauseSchedule('wf_daily-report');
    await client.resumeSchedule('wf_daily-report');

    Pause is durable across redeploys. Resume recomputes the next fire time from now so a long-paused schedule does not fire a backlog.

  • Added listBranches and getBranch endpoints. Use these to find specific runs of an agent, workflow, tool, or processor — even when they are nested inside another trace — and to fetch the subtree of spans rooted at any single span. (#16177)

    GET /observability/branches?spanType=agent_run&entityName=Observer
    GET /observability/traces/:traceId/branches/:spanId?depth=1

    Each row in listBranches is a single anchor span (one of AGENT_RUN, WORKFLOW_RUN, PROCESSOR_RUN, SCORER_RUN, RAG_INGESTION, TOOL_CALL, MCP_TOOL_CALL), so entities that always run as a child (e.g., an Observer agent inside a workflow) — previously not listable through listTraces — are now queryable via the HTTP API. getBranch accepts an optional depth (0 = anchor only; omitted = full subtree).

    Follow-up to https://github.com/mastra-ai/mastra/pull/16154 which added the underlying @mastra/core storage APIs.

  • Fixed Vector resource return types so they match what the server actually returns. Previously the types declared shapes that did not exist at runtime, leading to runtime failures with no TypeScript errors. (#16036)

    What changed

    • vector.getIndexes() now returns string[] (was { indexes: string[] })
    • vector.upsert() now returns { ids: string[] } (was string[])
    • vector.query() now returns QueryResult[] (was { results: QueryResult[] })

    Before

    const response = await client.getVector('docs').getIndexes();
    console.log(response.indexes); // undefined at runtime

    After

    const indexes = await client.getVector('docs').getIndexes();
    console.log(indexes[0]); // 'docs-index'

    Closes #15089.

Patch Changes
  • Fixed client stream handling for step completion and finish chunks that omit step result details. (#9146)

  • Added MCP Apps extension support (SEP-1865). MCPServer now accepts an appResources config to register interactive ui:// HTML resources. MCPClient preserves full tool _meta (including ui.resourceUri) when converting MCP tools to Mastra tools. Both advertise the io.modelcontextprotocol/ui extension capability. (#16004)

    Example — MCPServer with app resources:

    const server = new MCPServer({
      name: 'my-server',
      tools: { myTool },
      appResources: {
        dashboard: {
          name: 'Dashboard',
          description: 'Interactive dashboard UI',
          html: '<html>...</html>',
        },
      },
    });
  • Added server-generated route contract types for the JavaScript client SDK and updated the SDK to use those generated request and response types. (#15519)

@mastra/convex@1.0.10
Patch Changes
  • Fixed @mastra/convex workflow snapshot persistence when snapshots contain $-prefixed JSON Schema keys (for example $schema and $ref). (#16169) Snapshots are now stored safely, preventing Convex validation failures during workflow runs. Fixes #16110.

  • Improved Convex bulk insert and delete throughput. (#16149)

@mastra/datadog@1.1.0
Minor Changes
  • Added a new DatadogBridge integration for Mastra tracing so Datadog can keep auto-instrumented HTTP, database, and framework spans nested under the agent, workflow, model, and tool spans that triggered them. (#15716)

    import tracer from 'dd-trace';
    
    tracer.init({
      service: process.env.DD_SERVICE || 'my-mastra-app',
      env: process.env.DD_ENV || 'production',
    });
    
    import { Mastra } from '@mastra/core';
    import { Observability } from '@mastra/observability';
    import { DatadogBridge } from '@mastra/datadog';
    
    const mastra = new Mastra({
      observability: new Observability({
        configs: {
          default: {
            serviceName: 'my-mastra-app',
            bridge: new DatadogBridge({
              mlApp: process.env.DD_LLMOBS_ML_APP!,
            }),
          },
        },
      }),
    });
Patch Changes
@mastra/duckdb@1.3.0
Minor Changes
    • Added listBranches and getSpans implementations. (#16154)
    • Historical span data is queryable immediately; no migration required.
  • Added count_distinct aggregation and server-side TopK to the metrics storage API so dashboards built on high-cardinality fields (like threadId or resourceId) stay fast and bounded. (#16137)

    New aggregation

    getMetricAggregate, getMetricBreakdown, and getMetricTimeSeries accept aggregation: 'count_distinct' with a distinctColumn. Backends pick the most efficient native implementation — uniq on ClickHouse, approx_count_distinct on DuckDB.

    distinctColumn is restricted to a low/medium-cardinality categorical allowlist (entityType, entityName, parentEntityType, parentEntityName, rootEntityType, rootEntityName, name, provider, model, environment, executionSource, serviceName). ID columns are not allowed — distinct counts over near-unique values converge to the row count and are rarely useful.

    await store.getMetricAggregate({
      name: ['mastra_llm_tokens_total'],
      aggregation: 'count_distinct',
      distinctColumn: 'model',
      filters: { timestamp: { start, end } },
    });

    Server-side TopK

    getMetricBreakdown accepts limit and orderDirection, so breakdowns never return the full cardinality of a column from the database. Ordering is always by the aggregated value; orderDirection flips between top-N (DESC, default) and bottom-N (ASC).

    await store.getMetricBreakdown({
      name: ['mastra_agent_duration_ms'],
      aggregation: 'sum',
      groupBy: ['threadId'],
      limit: 20,
      orderDirection: 'DESC',
    });
Patch Changes
  • Improved performance of listTraces and listBranches on DuckDB. The Traces and Branches lists in the observability UI now load noticeably faster, especially on large span tables, because filtering and pagination happen up front and the store only assembles full span data for the rows on the page being viewed. (#16165)

    No API or behavior changes — return shapes and filter semantics are unchanged, and no migration is required.

  • Added direct score lookup support to observability storage so score records can be fetched by scoreId without scanning paginated score lists, including DuckDB and ClickHouse vNext observability stores. (#16162)

@mastra/editor@0.7.23
Patch Changes
  • Added MCP Apps extension support (SEP-1865). MCPServer now accepts an appResources config to register interactive ui:// HTML resources. MCPClient preserves full tool _meta (including ui.resourceUri) when converting MCP tools to Mastra tools. Both advertise the io.modelcontextprotocol/ui extension capability. (#16004)

    Example — MCPServer with app resources:

    const server = new MCPServer({
      name: 'my-server',
      tools: { myTool },
      appResources: {
        dashboard: {
          name: 'Dashboard',
          description: 'Interactive dashboard UI',
          html: '<html>...</html>',
        },
      },
    });
@mastra/express@1.3.17
Patch Changes
  • Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/fastify@1.3.17
Patch Changes
  • Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/hono@1.4.12
Patch Changes
  • Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/koa@1.5.0
Minor Changes
  • Improved the Koa adapter to make request routing more efficient as route counts grow. (#16050)

    Requests now move through a leaner routing path with lower middleware overhead, which helps Koa-based Mastra servers stay faster and produce cleaner request traces without changing the public API.

Patch Changes
  • Added FGA enforcement to server adapter middleware, ensuring authorization checks are applied consistently across all built-in adapters. (#15410)
@mastra/libsql@1.10.0
Minor Changes
  • Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to mastra_schedules and mastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)

    • Schedule gains optional ownerType / ownerId so a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.
    • ScheduleTrigger.status is renamed to outcome and the type is widened to ScheduleTriggerOutcome so future outcome values can be added without another rename.
    • ScheduleTrigger gains a stable id primary key and new triggerKind, parentTriggerId, and metadata fields. triggerKind distinguishes schedule-fire rows from later queue-drain rows (used by upcoming heartbeat work); parentTriggerId links related rows; metadata carries outcome-specific context.
    • The libsql, pg, and mongodb adapters all add the new columns/indexes. Their @mastra/core peer dependency is tightened to >=1.32.0-0 <2.0.0-0 so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field.
    • Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The triggers response on GET /api/schedules/:id/triggers now returns outcome instead of status.
    • The bundled Studio (Mastra CLI) is updated to read outcome so the schedule detail page keeps polling and rendering publish-failure rows correctly.
  • Added the schedules storage domain so LibSQL-backed Mastra apps can use scheduled workflows. Creates mastra_schedules and mastra_schedule_triggers tables on init. (#15830)

Patch Changes
@mastra/mcp@1.7.0
Minor Changes
  • Added MCP Apps support for interactive UI rendering over MCP. (#16004)

    MCPClientServerProxy — a lightweight proxy that delegates resource and tool operations to remote MCP servers via MCPClient, enabling Studio to fetch app resources from any connected server.

    toMCPServerProxies() — new convenience method on MCPClient that creates proxy objects for all configured servers, ready for Mastra-level registration.

    Automatic serverId stamping — tools returned by listTools() now carry _meta.ui.serverId, allowing consumers to resolve ui:// app resources from the correct MCP server in multi-server environments.

    const mcp = new MCPClient({
      servers: {
        myApps: { url: new URL('https://my-mcp-server.example.com/mcp') },
      },
    });
    
    const mastra = new Mastra({
      agents: { myAgent },
      mcpServers: { ...mcp.toMCPServerProxies() },
    });
  • Added MCP Apps extension support (SEP-1865). MCPServer now accepts an appResources config to register interactive ui:// HTML resources. MCPClient preserves full tool _meta (including ui.resourceUri) when converting MCP tools to Mastra tools. Both advertise the io.modelcontextprotocol/ui extension capability. (#16004)

    Example — MCPServer with app resources:

    const server = new MCPServer({
      name: 'my-server',
      tools: { myTool },
      appResources: {
        dashboard: {
          name: 'Dashboard',
          description: 'Interactive dashboard UI',
          html: '<html>...</html>',
        },
      },
    });
Patch Changes
  • Added Fine-Grained Authorization (FGA) enforcement to MCP tool execution. Both transport-driven calls and direct executeTool() calls now run the same authorization checks when a request user is present, and typed FGA permission constants are accepted in MCP server authorization config. (#15410)

  • Fixed trace parenting for long-lived MCP Stream connections. (#15716)

@mastra/memory@1.17.5
Patch Changes
  • Fixed Observational Memory model resolution for user-defined gateways. Models such as cloudflare/google/gemini-2.5-flash-lite now resolve through registered gateways instead of failing with provider-config errors. Closes #13841. (#16083)

  • Fixed async reflection buffering incorrectly triggering during idle timeout and provider-change activations when observation tokens are below the reflection activation threshold (#16076)

@mastra/mongodb@1.8.0
Minor Changes
  • Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to mastra_schedules and mastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)

    • Schedule gains optional ownerType / ownerId so a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.
    • ScheduleTrigger.status is renamed to outcome and the type is widened to ScheduleTriggerOutcome so future outcome values can be added without another rename.
    • ScheduleTrigger gains a stable id primary key and new triggerKind, parentTriggerId, and metadata fields. triggerKind distinguishes schedule-fire rows from later queue-drain rows (used by upcoming heartbeat work); parentTriggerId links related rows; metadata carries outcome-specific context.
    • The libsql, pg, and mongodb adapters all add the new columns/indexes. Their @mastra/core peer dependency is tightened to >=1.32.0-0 <2.0.0-0 so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field.
    • Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The triggers response on GET /api/schedules/:id/triggers now returns outcome instead of status.
    • The bundled Studio (Mastra CLI) is updated to read outcome so the schedule detail page keeps polling and rendering publish-failure rows correctly.
  • Added the schedules storage domain so MongoDB-backed Mastra apps can use scheduled workflows. Creates mastra_schedules and mastra_schedule_triggers collections on init, with default indexes on (status, next_fire_at) for due-schedule polling and (schedule_id, actual_fire_at) for trigger-history queries. (#15830)

Patch Changes
@mastra/observability@1.11.1
Patch Changes
  • Fixed model step traces to show the final prompt sent to the model, including memory-injected system messages. (#16029)

  • Added a new DatadogBridge integration for Mastra tracing so Datadog can keep auto-instrumented HTTP, database, and framework spans nested under the agent, workflow, model, and tool spans that triggered them. (#15716)

    import tracer from 'dd-trace';
    
    tracer.init({
      service: process.env.DD_SERVICE || 'my-mastra-app',
      env: process.env.DD_ENV || 'production',
    });
    
    import { Mastra } from '@mastra/core';
    import { Observability } from '@mastra/observability';
    import { DatadogBridge } from '@mastra/datadog';
    
    const mastra = new Mastra({
      observability: new Observability({
        configs: {
          default: {
            serviceName: 'my-mastra-app',
            bridge: new DatadogBridge({
              mlApp: process.env.DD_LLMOBS_ML_APP!,
            }),
          },
        },
      }),
    });
  • Reduced startup noise: CloudExporter missing-token message is now logged at debug level instead of warn, since being disabled is the expected state for local development (#16070)

@mastra/pg@1.10.0
Minor Changes
  • Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to mastra_schedules and mastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)

    • Schedule gains optional ownerType / ownerId so a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.
    • ScheduleTrigger.status is renamed to outcome and the type is widened to ScheduleTriggerOutcome so future outcome values can be added without another rename.
    • ScheduleTrigger gains a stable id primary key and new triggerKind, parentTriggerId, and metadata fields. triggerKind distinguishes schedule-fire rows from later queue-drain rows (used by upcoming heartbeat work); parentTriggerId links related rows; metadata carries outcome-specific context.
    • The libsql, pg, and mongodb adapters all add the new columns/indexes. Their @mastra/core peer dependency is tightened to >=1.32.0-0 <2.0.0-0 so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field.
    • Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The triggers response on GET /api/schedules/:id/triggers now returns outcome instead of status.
    • The bundled Studio (Mastra CLI) is updated to read outcome so the schedule detail page keeps polling and rendering publish-failure rows correctly.
  • Added the schedules storage domain so Postgres-backed Mastra apps can use scheduled workflows. Creates mastra_schedules and mastra_schedule_triggers tables on init, with default indexes on (status, next_fire_at) for due-schedule polling and (schedule_id, actual_fire_at) for trigger-history queries. (#15830)

Patch Changes
@mastra/playground-ui@26.0.0
Minor Changes
  • Improved the NoLogsInfo empty state. It now accepts optional datePreset, dateFrom, and dateTo props to show why no logs match the active range, suggests lowering the logging level, and links to the docs. Calling <NoLogsInfo /> without props keeps the original copy. (#16139)

  • Removed the deprecated Notification component. Use Notice for inline persistent context (errors, empty states) and toast (from @mastra/playground-ui's sonner wrapper) for transient feedback (success messages, confirmations). (#16033)

    // Before
    <Notification isVisible={true} type="error">Failed to load.</Notification>
    
    // After — inline persistent context
    <Notice variant="destructive">Failed to load.</Notice>
    
    // Before
    <Notification isVisible={true}>Saved successfully!</Notification>
    
    // After — transient feedback
    toast.info('Saved successfully');
  • Added Studio UI for scheduled workflows. (#15830)

    • /workflows/schedules lists every schedule across the project with the most recent run's status. Append ?workflowId=<id> to filter to a single workflow.
    • /workflows/schedules/:scheduleId shows the schedule's metadata, Pause/Resume controls, and paginated trigger history. Each trigger is deep-linked to its workflow run graph. The view polls every five seconds while any fired run is still active.
    • A workflow's detail header shows a Schedules action when it has at least one schedule.
  • Added SectionCard component to design system. Provides card primitive with tinted header strip (title, description, optional action slot), transparent body, and default/danger variants. Composes CardHeading for typography. Suitable for settings pages, dashboard sections, and grouped form layouts. (#16168)

    import { SectionCard } from '@mastra/playground-ui';
    
    <SectionCard title="Theme" description="Customize the appearance.">
      <ThemeSelector />
    </SectionCard>;
  • Redesigned the span token usage panel to show input vs output split with proportional bar and per-side detail breakdowns. DataKeysAndValues gained an optional density='dense' prop. (#16143)

  • Removed the CombinedButtons component. Use ButtonsGroup with spacing="close" for the same segmented-style cluster of toggle buttons. (#16035)

    // Before
    <CombinedButtons>
      <Button>Agent</Button>
      <Button>Model</Button>
    </CombinedButtons>
    
    // After
    <ButtonsGroup spacing="close">
      <Button>Agent</Button>
      <Button>Model</Button>
    </ButtonsGroup>
Patch Changes
  • Added support for icon-and-description layout in Notice by making title optional. When omitted, the notice renders as a single row with icon and description, useful for inline contextual messages. (#16033)

    // Before — title required
    <Notice variant="info" title="Heads up">Some message.</Notice>
    
    // After — title optional, single-row layout
    <Notice variant="info">Some message.</Notice>
  • Improved trace timeline span controls. Added tooltips with row counts to the expand-children, expand-all-descendants, and expand-at-this-level buttons. The expand-children button now collapses only the direct children rather than the entire subtree, and the descendants column gained a matching collapse-all-descendants action. Root spans show a single Expand all / Collapse all button using outward/inward double-chevrons. (#16173)

  • Refreshed toast styling so it aligns with the Notice component and lets sonner own the layout. (#16144)

    What changed for users:

    • Variant toasts (success / error / warning / info) now render with the same notice color tokens as the <Notice> component, including bg, border and text color in both light and dark mode.
    • Sonner's native layout is back in charge — the loader on toast.promise, the close button position, the icon placement and the mobile width all work as documented instead of fighting custom overrides.
    • The native close button has its own polished hover: it blends with the toast at rest and lifts with a tinted bg + stronger border on hover, in every variant and theme.
    • Sticky toasts can be made truly non-dismissible by passing both dismissible: false and closeButton: false.
    • toast.success / error / warning / info now return sonner's toast id (or an array of ids when called with an array of messages) so callers can keep dismissing or updating the toast they created.
  • Added a Scorer span type style on the trace timeline and a colored type dot before each span name so spans are visually flagged in both the name and timing columns. (#16160)

  • Migrated Files/Skills tabs and agent page tabs (Chat/Editor/Evaluate/Review/Traces) to the design-system Tabs component for consistent styling and accessibility (Radix tablist, arrow-key navigation). Also added a cursor-pointer on the Tab trigger and a disabled prop on the DS Tab. (#16148)

  • Added MCP Apps extension support (SEP-1865). MCPServer now accepts an appResources config to register interactive ui:// HTML resources. MCPClient preserves full tool _meta (including ui.resourceUri) when converting MCP tools to Mastra tools. Both advertise the io.modelcontextprotocol/ui extension capability. (#16004)

    Example — MCPServer with app resources:

    const server = new MCPServer({
      name: 'my-server',
      tools: { myTool },
      appResources: {
        dashboard: {
          name: 'Dashboard',
          description: 'Interactive dashboard UI',
          html: '<html>...</html>',
        },
      },
    });
  • Added an informational notice on the trace data panel pointing users to Mastra Studio (local or deployed) when "Evaluate Trace" and "Save as Dataset Item" actions are not available in the current view. (#16157)

  • Fixed pointer cursor on interactive form controls (Button, SelectTrigger, SelectItem) for better affordance. (#16140)

@mastra/react@0.2.34
Patch Changes
  • Fixed custom data stream parts so stable ids are preserved in React UI messages. (#16067)
@mastra/server@1.32.0
Minor Changes
  • Extend the schedules storage schema to support owned schedules and richer trigger audit. This is a breaking schema change to mastra_schedules and mastra_schedule_triggers; scheduled workflows are still in alpha so no compat shim is provided. (#16166)

    • Schedule gains optional ownerType / ownerId so a schedule row can be attributed to an owning subsystem (e.g. an agent that owns a heartbeat schedule). Workflow schedules leave both fields unset.
    • ScheduleTrigger.status is renamed to outcome and the type is widened to ScheduleTriggerOutcome so future outcome values can be added without another rename.
    • ScheduleTrigger gains a stable id primary key and new triggerKind, parentTriggerId, and metadata fields. triggerKind distinguishes schedule-fire rows from later queue-drain rows (used by upcoming heartbeat work); parentTriggerId links related rows; metadata carries outcome-specific context.
    • The libsql, pg, and mongodb adapters all add the new columns/indexes. Their @mastra/core peer dependency is tightened to >=1.32.0-0 <2.0.0-0 so installing a new storage adapter against an older core (or vice-versa) surfaces a peer-dependency warning at install time instead of silently writing/reading the wrong field.
    • Scheduler producer, server schemas/handler, and client SDK types are updated to use the new fields. The triggers response on GET /api/schedules/:id/triggers now returns outcome instead of status.
    • The bundled Studio (Mastra CLI) is updated to read outcome so the schedule detail page keeps polling and rendering publish-failure rows correctly.
  • Added API endpoints for MCP server resources, enabling clients to list and read app resources for interactive UI rendering. (#16004)

    • GET /api/mcp/:serverId/resources — lists available resources on an MCP server
    • POST /api/mcp/:serverId/resources/read — reads a specific resource by URI (e.g. ui://calculator/app)
  • Added HTTP routes for scheduled workflows. (#15830)

    • GET /api/schedules — list schedules across the project, optionally filtered by workflowId.
    • GET /api/schedules/:scheduleId — fetch a schedule with its most recent run summary.
    • GET /api/schedules/:scheduleId/triggers — paginated trigger history joined to the corresponding workflow run.
    • POST /api/schedules/:scheduleId/pause and POST /api/schedules/:scheduleId/resume — durable pause/resume. Both require schedules:write and are idempotent. Resume recomputes nextFireAt from now so a long-paused schedule does not fire a backlog.
  • Added listBranches and getBranch endpoints. Use these to find specific runs of an agent, workflow, tool, or processor — even when they are nested inside another trace — and to fetch the subtree of spans rooted at any single span. (#16177)

    GET /observability/branches?spanType=agent_run&entityName=Observer
    GET /observability/traces/:traceId/branches/:spanId?depth=1

    Each row in listBranches is a single anchor span (one of AGENT_RUN, WORKFLOW_RUN, PROCESSOR_RUN, SCORER_RUN, RAG_INGESTION, TOOL_CALL, MCP_TOOL_CALL), so entities that always run as a child (e.g., an Observer agent inside a workflow) — previously not listable through listTraces — are now queryable via the HTTP API. getBranch accepts an optional depth (0 = anchor only; omitted = full subtree).

    Follow-up to https://github.com/mastra-ai/mastra/pull/16154 which added the underlying @mastra/core storage APIs.

  • Added Fine-Grained Authorization (FGA) enforcement across server handlers and memory APIs: (#15410)

    • Route-level checks on detail endpoints, custom routes (including request-aware resource ID resolvers and path parameters), and resource-scoped search
    • Thread-level checks on reads, writes, creation, cloning, message saving, and listing — with unviewable threads hidden from totals and pagination
    • Message deletion now denies access when the message's thread cannot be verified
    • Authenticated user context preserved through thread authorization, and the thread's owning resourceId forwarded into the FGA context so providers can derive composite tenant-scoped resource IDs
    • Route permission derivation and memory clone checks now use the correct resource context
    • Typed FGA permission constants accepted in route and thread authorization config
Patch Changes
  • Fixed A2A task resubscribe to return the current task snapshot and continue streaming live artifact and status updates for in-progress tasks. (#15987)

  • Added an observability score lookup endpoint at GET /observability/scores/:scoreId backed by observability storage. (#16162)

  • Added MCP Apps extension support (SEP-1865). MCPServer now accepts an appResources config to register interactive ui:// HTML resources. MCPClient preserves full tool _meta (including ui.resourceUri) when converting MCP tools to Mastra tools. Both advertise the io.modelcontextprotocol/ui extension capability. (#16004)

    Example — MCPServer with app resources:

    const server = new MCPServer({
      name: 'my-server',
      tools: { myTool },
      appResources: {
        dashboard: {
          name: 'Dashboard',
          description: 'Interactive dashboard UI',
          html: '<html>...</html>',
        },
      },
    });
  • Added server-generated route contract types for the JavaScript client SDK and updated the SDK to use those generated request and response types. (#15519)

@mastra/slack@1.1.1
Patch Changes
  • Fixed Slack app creation failing when agent description exceeds 139 characters. The manifest description is now automatically truncated to prevent the Slack API from rejecting the request. (#16093)
@mastra/temporal@0.1.0
Minor Changes
  • Added the new @mastra/temporal package for running Mastra workflows on Temporal. (#15789)

    What changed

    • Added init() to create Temporal-backed Mastra workflows and steps.
    • Added MastraPlugin to bundle workflow code for Temporal workers and load generated activities.
    • Added debug: true support to write transformed workflow modules and emitted bundles to .mastra/temporal.

    Example

    import { init } from '@mastra/temporal';
    import { MastraPlugin } from '@mastra/temporal/worker';
    import { Client, Connection } from '@temporalio/client';
    import { Worker } from '@temporalio/worker';
    
    const connection = await Connection.connect();
    const client = new Client({ connection });
    const { createWorkflow, createStep } = init({ client, taskQueue: 'mastra' });
    
    const step = createStep({ id: 'hello', execute: async () => 'world' });
    export const helloWorkflow = createWorkflow({ id: 'hello-workflow' }).then(step);
    
    await Worker.create({
      connection,
      taskQueue: 'mastra',
      plugins: [new MastraPlugin({ src: import.meta.resolve('./mastra/index.ts') })],
    });
Patch Changes
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.31.0

Highlights

Platform Channels Framework (Core + Server + Client)

A new channels architecture adds ChannelProvider, a dedicated ChannelsStorage domain, and ChannelConnectResult connection flows (OAuth, deep link, immediate), with Mastra-level connect/disconnect/list APIs and matching MastraClient.channels methods.

Slack Channel Integration (@mastra/slack)

New Slack provider connects agents to Slack workspaces with OAuth-based app provisioning, manifest drift detection, encrypted credential storage, slash commands, and threaded conversation support.

NestJS Server Adapter (@mastra/nestjs)

New @mastra/nestjs adapter runs Mastra inside NestJS (Express) apps with native module registration, DI-friendly service injection, rate limiting, graceful shutdown, streaming, and MCP transport support; MastraServerBase is now exported to support adapters that manage routing.

Google Drive Workspace Filesystem (@mastra/google-drive)

New Google Drive WorkspaceFilesystem mounts a single Drive folder as an agent workspace, supporting OAuth tokens (with refresh callbacks) and service accounts, and implementing the full read/write/list/move/stat interface with optimistic concurrency via expectedMtime.

Real-time Voice Provider for AWS Nova Sonic (@mastra/voice-aws-nova-sonic)

Adds a bidirectional streaming voice provider for Bedrock Nova 2 Sonic with live mic streaming and playback, streaming transcription (speculative/final), barge-in detection, multi-voice selection, and tool calling with per-session RequestContext.

Breaking Changes
  • @mastra/playground-ui: IconButton export removed (use Button with size="icon-*") and <Alert> removed in favor of <Notice>; variant="light"/"inputLike" replaced by variant="default".

Changelog

@mastra/core@1.31.0
Minor Changes
  • Enhanced load_tool to accept an array of tool names, enabling bulk tool loading in a single call. Returns 'loaded', 'notFound', and 'alreadyLoaded' arrays for clearer response shape. (#15472)

  • Added Microsoft Entra ID authentication support for Azure OpenAI gateways, so Azure deployments can call models without API keys when using Azure SDK credentials. (#15983)

  • Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)

  • Added top-level environment config on Mastra to tag observability signals with the deployment environment. (#15956)

    Set it once on the Mastra instance and it will be attached to all observability signals automatically. Falls back to process.env.NODE_ENV when unset; per-call tracingOptions.metadata.environment still takes precedence.

    Before

    await agent.generate('hello', {
      tracingOptions: { metadata: { environment: process.env.NODE_ENV } },
    });

    After

    new Mastra({
      environment: 'production',
      observability: new Observability({ ... }),
    })

    mastra.getEnvironment() returns the resolved value.

  • Fixed trajectory scorers in dataset.startExperiment receiving raw agent messages instead of a Trajectory object, which caused a crash when accessing run.output.steps. Trajectory scorers now receive the same pre-extracted Trajectory that runEvals provides. (#15693)

    The scorers option now also accepts the same categorised shape as runEvals (AgentScorerConfig / WorkflowScorerConfig), so you no longer need to rewrite your scorer config when moving from runEvals to dataset.startExperiment.

    Before (trajectory scorer crashed at runtime):

    await dataset.startExperiment({ scorers: [orderScorer] }) // run.output.steps was undefined

    After (works correctly, both flat and categorised forms accepted):

    await dataset.startExperiment({ scorers: [orderScorer] }) await dataset.startExperiment({ scorers: { agent: [accuracyScorer], trajectory: [orderScorer] } })

    Per-step scorers are now also supported for workflow targets, matching runEvals. Pass scorers: { workflow: [...], steps: { stepId: [...] }, trajectory: [...] } to score individual workflow steps with their own scorers; results carry the originating stepId and keep targetScope: 'span' (with targetEntityType: WORKFLOW_STEP on the underlying scorer run), matching how runEvals encodes step identity.

  • Workspace search now supports batch-capable embedders. Pass an embedder branded with batch: true (and an optional maxBatchSize) to embed all pending chunks for a flush in a single provider call instead of one call per chunk. This dramatically reduces index-rebuild time on large workspaces when using providers that support batch embedding (e.g. OpenAI's embedMany). Existing single-text embedders continue to work unchanged. (#14735)

    import { embedMany } from 'ai';
    import { openai } from '@ai-sdk/openai';
    
    const model = openai.embedding('text-embedding-3-small');
    
    const workspace = new Workspace({
      // ...
      embedder: Object.assign(
        async (texts: string[]) => {
          const { embeddings } = await embedMany({ model, values: texts });
          return embeddings;
        },
        { batch: true as const, maxBatchSize: 2048 },
      ),
    });
Patch Changes
  • Update provider registry and model documentation with latest models and providers (1723e09)

  • Fixed workflow runs not being cancellable when steps or conditions ignored the abort signal. Cancelling a run now correctly stops dountil, dowhile, and foreach loops at every cancellation boundary — between iterations, after a step returns, after the loop condition is evaluated, and (for foreach) between concurrency chunks and after the final chunk. Previously, long-running loops (e.g. a dountil with a setTimeout inside the step) would keep running and eventually emit success even after the run was cancelled. Closes #15990. (#15994)

  • Fixed type inference on workflow loop helpers (foreach, dowhile, dountil) so a step's requestContextSchema correctly aligns with the workflow's requestContextSchema. Previously these methods dropped the workflow's TRequestContext from the step parameter, causing TypeScript to reject typed-context steps even when the workflow declared a matching schema. Steps without a requestContextSchema are still accepted; steps whose schema does not match the workflow's now produce a type error. Fixes #15989. (#15995)

  • Fixed sub-agent delegation so nested tool results stay out of the parent model context by default while remaining available to application code. Set delegation.includeSubAgentToolResultsInModelContext to include the full subagent result in the parent model context. (#15832)

  • Added a coalesced display state subscription API for Harness. (#15974)

    This helps UI clients render fewer updates while still receiving the latest state. The example below renders the initial state, then subscribes to coalesced updates with the default windowMs and maxWaitMs timing options.

    render(harness.getDisplayState());
    
    const unsubscribe = harness.subscribeDisplayState(render, {
      windowMs: 250,
      maxWaitMs: 500,
    });
  • Fixed BatchPartsProcessor using a hardcoded id in batched text-delta chunks. The real message id and runId are now preserved from the original chunks, preventing AI SDK UIMessage stream from dropping batched deltas. (#14974)

  • Add filterAfterToolSteps to ToolCallFilter so tool calls can be filtered during agentic loops after they are no longer recent. By default, ToolCallFilter keeps its previous behavior and only filters the initial input. (#15795)

  • Workspace search no longer throws when requesting hybrid or vector mode if the configuration does not support it. The search tool now gracefully falls back to the best available mode instead of throwing an error. (#14533)

  • Workspace file tools no longer use misleading absolute-path examples (e.g. /data/output.txt) that caused weaker LLMs to attempt writes at the actual filesystem root. The example paths in read_file and write_file are now relative. (#14544)

    Additionally, when a contained workspace rejects an absolute path that escapes its boundary, the resulting PermissionError now guides the agent toward a relative path so it can self-correct on the next turn. When the path's first segment names a real directory in the workspace (e.g. /src/app.ts with an existing src/), the error suggests the exact relative form. Otherwise it falls back to a generic hint instead of inventing a misleading suggestion for genuinely out-of-workspace paths like /etc/passwd.

    Fixes #14542

  • Fixed SkillSearchProcessor so agents use it as the on-demand skill discovery path without also adding eager skill context. (#15916)

    When SkillSearchProcessor is configured, agents no longer auto-add the eager SkillsProcessor, and they hide the overlapping skill and skill_search tools while keeping skill_read available for supporting skill files. Workspace file tools can still read SKILL.md files during explicit file inspection or editing workflows.

  • Fixed tool calls to run in parallel when active tools exclude approval or suspending tools. (#15978)

    • SearchEngine: indexMany uses p-map with a default concurrency of 8 when vector embedding runs, with optional concurrency and stopOnError (same semantics as p-map). Lazy vector indexing flushes pending documents at the same concurrency, drains the queue before awaiting so concurrent index calls are not dropped, loops until the queue is empty before search, dedupes by document id (last wins), and re-queues the batch if a flush throws. (#14735)
    • Workspace: Search auto-indexing reads files in parallel with a bounded concurrency, skips unreadable paths, awaits batch indexing, and falls back to per-file indexing when the batch path throws. Successful single-file indexing returns the path so callers can track what was indexed.
  • Fix semantic recall indexing to honor read-only memory mode. (#15949)

  • Fixed Linux bubblewrap failing when Workspace mounts use symlinks under LocalSandbox by resolving mount paths to real directories for isolation allowlists. (#15498)

@mastra/arize@1.0.22
Patch Changes
  • Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)

    • gen_ai.usage.cached_input_tokensgen_ai.usage.cache_read.input_tokens
    • gen_ai.usage.cache_write_tokensgen_ai.usage.cache_creation.input_tokens

    gen_ai.usage.input_tokens is unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.

    Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.

    Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.

@mastra/arthur@0.2.8
Patch Changes
  • Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)

    • gen_ai.usage.cached_input_tokensgen_ai.usage.cache_read.input_tokens
    • gen_ai.usage.cache_write_tokensgen_ai.usage.cache_creation.input_tokens

    gen_ai.usage.input_tokens is unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.

    Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.

    Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.

@mastra/clickhouse@1.6.0
Minor Changes
  • Added ClickhouseStoreVNext, a ClickHouse storage adapter that uses the vNext observability domain by default. Equivalent to constructing a ClickhouseStore and overriding the observability domain manually, but exposed as a single class for new projects. (#15984)

    import { Mastra } from '@mastra/core';
    import { ClickhouseStoreVNext } from '@mastra/clickhouse';
    
    export const mastra = new Mastra({
      storage: new ClickhouseStoreVNext({
        id: 'clickhouse-storage',
        url: process.env.CLICKHOUSE_URL!,
        username: process.env.CLICKHOUSE_USERNAME!,
        password: process.env.CLICKHOUSE_PASSWORD!,
      }),
    });

    ClickhouseStoreVNext accepts the same configuration as ClickhouseStore and reuses the same ClickHouse client across every domain. ClickhouseStore continues to work for projects on the legacy observability schema.

Patch Changes
@mastra/client-js@1.16.0
Minor Changes
  • Added Channels resource to MastraClient with listPlatforms, listInstallations, connect, and disconnect methods for managing platform channel integrations. (#15876)
Patch Changes
@mastra/convex@1.0.9
Patch Changes
  • Fixed Convex workflow snapshot loads to use the workflow/run index. (#15971)
@mastra/dynamodb@1.0.5
Patch Changes
  • Fixed an issue where automatic thread title generation was skipped when using the DynamoDB storage adapter. The adapter was overwriting empty thread titles with a Thread <id> placeholder on save, which prevented the title-generation step (gated on an empty title) from running. Empty titles now round-trip correctly so generated titles work the same as with other storage adapters. Resolves #15998. (#16003)
@mastra/fastify@1.3.16
Patch Changes
  • Fix multipart file handling in Fastify adapter by aligning return type with other adapters and preventing stream hang on file size limit. (#15796)

  • Fix multipart upload tests to register the multipart content-type parser. The tests were manually adding the preHandler hook but skipping registerContextMiddleware(), which meant Fastify rejected multipart/form-data requests with 415 Unsupported Media Type. (#16002)

@mastra/google-drive@0.1.0
Minor Changes
  • Add @mastra/google-drive, a new Google Drive WorkspaceFilesystem provider that mounts a single Drive folder as an agent workspace. Supports OAuth access tokens, async refresh callbacks, and service account (JWT) authentication. Implements the full WorkspaceFilesystem interface — read, write, list, copy, move, mkdir, rmdir, stat, exists — plus expectedMtime optimistic concurrency. (#15756)

    import { Agent } from '@mastra/core/agent';
    import { Workspace } from '@mastra/core/workspace';
    import { GoogleDriveFilesystem } from '@mastra/google-drive';
    
    const workspace = new Workspace({
      filesystem: new GoogleDriveFilesystem({
        folderId: process.env.GOOGLE_DRIVE_FOLDER_ID!,
        accessToken: process.env.GOOGLE_DRIVE_ACCESS_TOKEN!,
      }),
    });
    
    const agent = new Agent({
      id: 'drive-agent',
      name: 'Drive Agent',
      model: 'openai/gpt-4o-mini',
      workspace,
    });

    A matching googleDriveFilesystemProvider descriptor is also exported for MastraEditor.

Patch Changes
  • GoogleDriveFilesystem tweaks: mkdir defaults to recursive, appendFile uses optimistic concurrency, rmdir skips redundant child listing, JSON body requests include Content-Type header, readFile uses consistent searchParams, and concurrent token refreshes are deduplicated. (#16010)
@mastra/libsql@1.9.1
Patch Changes
  • Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)
@mastra/mongodb@1.7.4
Patch Changes
  • Removed unsupported minScore query option from MongoDB vector store docs and README. Exported MongoDBQueryVectorParams so callers can type documentFilter for MongoDBVector.query(). (#15936)

    Fixes #15715

@mastra/nestjs@0.1.0
Minor Changes
  • Add NestJS server adapter (@mastra/nestjs) for running Mastra with NestJS Express applications. Provides native module registration, DI-based service injection, rate limiting, graceful shutdown, streaming, and MCP transport support. (#12751)

    import { Module } from '@nestjs/common';
    import { MastraModule } from '@mastra/nestjs';
    import { mastra } from './mastra';
    
    @Module({
      imports: [MastraModule.register({ mastra })],
    })
    export class AppModule {}
Patch Changes
@mastra/observability@1.11.0
Minor Changes
  • Auto-attach the Mastra-level environment to all observability signals. (#15956)
Patch Changes
@mastra/otel-exporter@1.0.21
Patch Changes
  • Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)

    • gen_ai.usage.cached_input_tokensgen_ai.usage.cache_read.input_tokens
    • gen_ai.usage.cache_write_tokensgen_ai.usage.cache_creation.input_tokens

    gen_ai.usage.input_tokens is unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.

    Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.

    Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.

@mastra/perplexity@0.1.0
Minor Changes
  • Added new @mastra/perplexity integration with the Perplexity Search tool for agents. (#15939)

    import { createPerplexityTools } from '@mastra/perplexity';
    
    const { perplexitySearch } = createPerplexityTools({ apiKey: process.env.PERPLEXITY_API_KEY });
Patch Changes
@mastra/pg@1.9.4
Patch Changes
  • Fixed workflow snapshot sanitization in @mastra/pg for strings containing escaped surrogate patterns like [^\ud800-\udfff]. This prevents invalid JSON escape sequences that caused PostgreSQL jsonb writes to fail with error 22P02. (#15923)

    Fixes #15920

  • Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)

@mastra/playground-ui@25.0.0
Minor Changes
  • Refactored Button component to use a single cva (class-variance-authority) variant config instead of nested manual maps. Consolidated IconButton into Button via size="icon-sm|icon-md|icon-lg" and removed the IconButton export. Replaced variant="light" and variant="inputLike" with variant="default" (no behavior change for default styling). Added cta and outline variants and unified active/hover styles between text- and icon-mode buttons. (#15985)

    Why: A single source of truth for variants means consistent visuals, fewer drift bugs, simpler maintenance, and a more predictable surface for AI agents — single-variant cva is the dominant shadcn pattern across DS components in this repo (Card, Input, Label, Textarea, StatusBadge).

    Migration:

    // Before
    import { IconButton } from '@mastra/playground-ui';
    <IconButton><Settings /></IconButton>
    <Button variant="light">…</Button>
    <Combobox variant="inputLike" />
    
    // After
    import { Button } from '@mastra/playground-ui';
    <Button size="icon-md"><Settings /></Button>
    <Button variant="default">…</Button>
    <Combobox variant="default" />
  • Removed <Alert> in favor of <Notice>. The two components had significant visual and behavioral overlap; <Notice> is now the single banner primitive and supports every previous <Alert> use case. (#15791)

    <Notice> is also redesigned with a flatter API: title and icon are now props, each variant ships a default icon, an optional action prop renders a button aligned to the title, and a new note variant has been added alongside warning, destructive, info, and success. Theme tokens (notice-warning, notice-destructive, notice-info, notice-success, notice-note) replace the previous hardcoded colors.

    Migration

    // Before
    <Alert variant="warning">
      <AlertTitle>Provider not connected</AlertTitle>
      <AlertDescription as="p">Set the API key environment variable.</AlertDescription>
    </Alert>
    
    // After
    <Notice variant="warning" title="Provider not connected">
      <Notice.Message>Set the API key environment variable.</Notice.Message>
    </Notice>
Patch Changes
  • Removed the "Avg Score" KPI card from the Metrics dashboard and the avg-score summary from the Scores card. (#15967)

  • Fixed row click behavior in the dataset experiments compare view. Clicking a row while selection mode is active now toggles the row's selection instead of navigating to the experiment. Clicking directly on the checkbox no longer also triggers the row click handler. (#15492)

  • Aligned AlertDialog visual styling with Dialog component for design system consistency. AlertDialog now uses the same surface tokens, border radius, shadow, animation curves, and typography scale as Dialog. The accessibility primitive remains separate (preserves role="alertdialog" and explicit Action/Cancel semantics) — only the visual shell was synced. Also added AlertDialog.Body for parity with Dialog. (#15988)

@mastra/react@0.2.33
Patch Changes
  • Fixed suspended tool run IDs not being preserved after page refresh. (#15107)
@mastra/sentry@1.0.21
Patch Changes
  • Renamed emitted OTel GenAI cache usage attributes to match the OpenTelemetry semantic conventions: (#15966)

    • gen_ai.usage.cached_input_tokensgen_ai.usage.cache_read.input_tokens
    • gen_ai.usage.cache_write_tokensgen_ai.usage.cache_creation.input_tokens

    gen_ai.usage.input_tokens is unchanged and remains the total prompt-token count. Cache attributes are emitted separately as subsets of that total.

    Updated Arize, Arthur, and Sentry mappings so cache values continue to flow through those exporters.

    Direct consumers should update any dashboards, alerts, or queries that reference the old attribute names.

@mastra/server@1.31.0
Patch Changes
  • Fix GET /tools/:toolId and POST /tools/:toolId/execute to find dynamically-resolved agent tools (provided via toolsResolver / function-based tools) when they are not in the static tool registry. Errors thrown by an individual agent's listTools() during the lookup are now logged as warnings instead of being silently swallowed. (#13989)

  • Fixed memory query validation when optional JSON query params are omitted with newer Zod versions. (#15969)

  • Export MastraServerBase from @mastra/core/server so framework adapters that manage routing independently can share the same server base class. (#12751)

  • Added platform channels framework with ChannelProvider interface, ChannelsStorage domain, and ChannelConnectResult discriminated union supporting OAuth, deep link, and immediate connection flows. Channels can be registered on the Mastra instance and expose connect/disconnect/list APIs for platform integrations. (#15876)

@mastra/slack@1.1.0
Minor Changes
  • Added @mastra/slack channel integration for connecting AI agents to Slack workspaces. Provides automatic Slack app provisioning via OAuth, manifest management with drift detection, encrypted credential storage, slash command support, and threaded conversation handling. Usage: (#15876)

    import { SlackProvider } from '@mastra/slack';
    
    const mastra = new Mastra({
      channels: {
        slack: new SlackProvider({
          refreshToken: process.env.SLACK_APP_CONFIG_REFRESH_TOKEN!,
        }),
      },
    });
    
    // Connect an agent to Slack
    const result = await mastra.channels.slack.connect('my-agent');
    // result.type === 'oauth' → redirect user to result.authorizationUrl
Patch Changes
@mastra/voice-aws-nova-sonic@0.1.0
Minor Changes
  • Add new @mastra/voice-aws-nova-sonic voice provider for AWS Bedrock Nova 2 Sonic. (#13232)

    The provider exposes a real-time bidirectional voice interface backed by the InvokeModelWithBidirectionalStreamCommand API on AWS Bedrock, including:

    • Live microphone streaming (send / listen) and assistant audio playback via speaking events
    • Live transcription via writing events with SPECULATIVE / FINAL generation stages
    • Barge-in / interrupt detection
    • Speaker selection across all 18 Nova Sonic voices and configurable endpointing sensitivity
    • Tool calling with per-session RequestContext
    • Configurable AWS region, model id, credentials (or default credential provider chain), and inference / turn-detection parameters
Patch Changes
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.30.0

Highlights

Durable Agents + Resumable Streams (crash/disconnect resilient execution)

New DurableAgent support lets agent streams resume after client disconnects and continue through server crashes/restarts by caching stream events and enabling reconnection via observe(runId, { offset }).

Workflow-backed “Durable Execution” (Evented + Inngest strategies)

Agents can now run outside the HTTP request using workflow execution (createEventedAgent for built-in evented engine, createInngestAgent for Inngest), enabling reliable long-running tool loops while clients subscribe to progress.

Pluggable PubSub + Cache infrastructure (Redis/Upstash-ready)

Durable streaming is backed by a PubSub + ServerCache layer (defaults: EventEmitterPubSub + InMemoryServerCache), with recommended production configs using Redis-backed implementations so any instance can serve reconnect/replay.

Improved A2A streaming artifacts in @mastra/server

A2A streaming now emits incremental artifact updates during the full agent stream while still preserving final structured output artifacts.

Observability noise/volume reduction by default

Cloud observability uploads now filter model chunk spans by default and raise the default observability log level to warn, reducing data volume and chatter.

Breaking Changes
  • None called out in this changelog.

Changelog

@mastra/core@1.30.0
Minor Changes
  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
Patch Changes
  • Fixed a regression in 1.29.0 where configuring an agent with channel adapters (e.g. channels.adapters.slack) caused server startup to crash with a "Custom API route ... must not start with /api" error. The custom-route prefix validation now skips framework-generated webhook routes. (#15952)

  • Update provider registry and model documentation with latest models and providers (d587199)

  • Fix MCP client support in the agent editor: (#15945)

    • MCP client form dirty state: Save button now enables after adding/removing MCP clients
    • MCP tool name matching: Both bare and namespaced tool names are matched correctly
    • Auth token forwarding: Token from cookie or header is forwarded to auth-protected MCP servers
    • String interpolation: Request context variables in system prompts now resolve correctly
@mastra/client-js@1.15.2
Patch Changes
  • Remove incorrect deprecation markers from getTask() and cancelTask() in the Mastra A2A client. (#15941)

  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
@mastra/editor@0.7.22
Patch Changes
  • Fix MCP client support in the agent editor: (#15945)
    • MCP client form dirty state: Save button now enables after adding/removing MCP clients
    • MCP tool name matching: Both bare and namespaced tool names are matched correctly
    • Auth token forwarding: Token from cookie or header is forwarded to auth-protected MCP servers
    • String interpolation: Request context variables in system prompts now resolve correctly
@mastra/hono@1.4.10
Patch Changes
  • Fixed a regression in 1.29.0 where configuring an agent with channel adapters (e.g. channels.adapters.slack) caused server startup to crash with a "Custom API route ... must not start with /api" error. The custom-route prefix validation now skips framework-generated webhook routes. (#15952)
@mastra/inngest@1.3.0
Minor Changes
  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
  • Update peer dependencies to match core package version bump (1.0.5) (#12557)

Patch Changes
@mastra/memory@1.17.4
Patch Changes
  • Fixed idle timeout and provider-change observation activations blocking on in-progress reflection buffering. These triggers now return immediately, letting the background reflection complete asynchronously. (#15937)
@mastra/observability@1.10.3
Patch Changes
  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
  • Reduced default cloud observability volume by filtering model chunk spans from CloudExporter uploads by default and raising the default observability log level to warn. (#15815)

@mastra/playground-ui@24.0.2
Patch Changes
  • Updated the look and motion of Dialog. The surface is now lighter and translucent with a subtle backdrop blur, the typography is tighter, and the open/close animation feels snappier. SideDialog and AlertDialog pick up the refined ambient shadow as well, since they share the same shadow style. (#15958)

  • Polished DataList visuals: removed the trailing "No more data to load" message and dropped the bottom border on the last row for a cleaner end-of-list appearance. (#15959)

  • Refined the DataPanel loading state with a smaller spinner and tightened layout for a less prominent appearance. (#15965)

@mastra/redis@1.1.0
Minor Changes
  • Update peer dependencies to match core package version bump (1.0.5) (#12557)
Patch Changes
  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
@mastra/server@1.30.0
Minor Changes
  • Update peer dependencies to match core package version bump (1.0.5) (#12557)
Patch Changes
  • Fixed a regression in 1.29.0 where configuring an agent with channel adapters (e.g. channels.adapters.slack) caused server startup to crash with a "Custom API route ... must not start with /api" error. The custom-route prefix validation now skips framework-generated webhook routes. (#15952)

  • Fix MCP client support in the agent editor: (#15945)

    • MCP client form dirty state: Save button now enables after adding/removing MCP clients
    • MCP tool name matching: Both bare and namespaced tool names are matched correctly
    • Auth token forwarding: Token from cookie or header is forwarded to auth-protected MCP servers
    • String interpolation: Request context variables in system prompts now resolve correctly
  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
  • Fix A2A streaming to emit incremental artifact updates from the agent full stream while preserving final structured output artifacts. (#15941)

@mastra/upstash@1.1.0
Minor Changes
  • Update peer dependencies to match core package version bump (1.0.5) (#12557)
Patch Changes
  • Add durable agents with resumable streams (#12557)

    Durable agents make agent execution resilient to disconnections, crashes, and long-running operations.

    The Problem

    Standard agent streaming has two fragility points:

    1. Connection drops - If a client disconnects mid-stream (network blip, browser refresh, mobile app backgrounded), all subsequent events are lost. The client has no way to "catch up" on what they missed.
    2. Long-running operations - Agent loops with tool calls can take minutes. Holding an HTTP connection open that long is unreliable. If the server restarts or the connection times out, the work is lost.
    The Solution

    Resumable streams solve connection drops. Every event is cached with a sequential index. If a client disconnects at event 5, they can reconnect and request events starting from index 6. They receive cached events immediately, then continue with live events as they arrive.

    Durable execution solves long-running operations. Instead of executing the agent loop directly in the HTTP request, execution happens in a workflow engine (built-in evented engine or Inngest). The HTTP request just subscribes to events. If the connection drops, execution continues. The client can reconnect anytime to observe progress.

    Usage

    Wrap any existing Agent with durability using factory functions:

    import { Agent } from '@mastra/core/agent';
    import { createDurableAgent } from '@mastra/core/agent/durable';
    
    const agent = new Agent({
      id: 'my-agent',
      model: openai('gpt-4'),
      instructions: 'You are helpful',
    });
    
    const durableAgent = createDurableAgent({ agent });

    Factory functions for different execution strategies:

    FactoryExecutionUse Case
    createDurableAgent({ agent })Local, synchronousDevelopment, simple deployments
    createEventedAgent({ agent })Fire-and-forget via workflow engineLong-running operations
    createInngestAgent({ agent, inngest })Inngest-poweredProduction, distributed systems
    Resumable Streams
    // Start streaming
    const { runId, output } = await durableAgent.stream('Analyze this data...');
    
    // Client disconnects at event 5...
    
    // Reconnect and resume from where we left off
    const { output: resumed } = await durableAgent.observe(runId, { offset: 6 });
    // Receives events 6, 7, 8... from cache, then continues with live events
    PubSub and Cache

    Durable agents use two infrastructure components:

    ComponentPurposeDefault
    PubSubReal-time event delivery during streamingEventEmitterPubSub
    CacheStores events for replay on reconnectionInMemoryServerCache

    When stream() is called, events flow through pubsub in real-time. The cache stores each event with a sequential index. When observe() is called, missed events replay from cache before continuing with live events.

    Configure via Mastra instance (recommended):

    const mastra = new Mastra({
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
      agents: {
        // Inherits cache and pubsub from Mastra
        myAgent: createDurableAgent({ agent }),
      },
    });

    Configure per-agent (overrides Mastra):

    const durableAgent = createDurableAgent({
      agent,
      cache: new RedisServerCache({ url: 'redis://...' }),
      pubsub: new RedisPubSub({ url: 'redis://...' }),
    });

    Disable caching (streams won't be resumable):

    const durableAgent = createDurableAgent({ agent, cache: false });

    For single-instance deployments, the defaults work fine. For multi-instance deployments (load balancer, horizontal scaling), use Redis-backed implementations so any instance can serve reconnection requests.

    Class Hierarchy
    • DurableAgent extends Agent - base class with resumable streams
    • EventedAgent extends DurableAgent - fire-and-forget execution
    • InngestAgent extends DurableAgent - Inngest-powered execution
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.25.0

Highlights

Per-user resource scoping in server auth (secure by default)

Server auth now supports mapUserToResourceId, automatically mapping an authenticated user to a resource ID so memory/threads are isolated per user without custom middleware, and client attempts to inject reserved context keys (mastra__resourceId, mastra__threadId) are stripped.

Experiments + observability are now version-aware end-to-end

Experiments correctly execute the specified agentVersion, and observability gains first-class entityVersionId/parentEntityVersionId/rootEntityVersionId across spans/metrics/scores/logs/feedback (with storage migrations in ClickHouse/DuckDB/Postgres/MongoDB/LibSQL), plus new experiment query filters and experimentId propagation into agent spans and scorer context.

Agent version targeting in AI SDK route handlers

chatRoute(), handleChatStream(), networkRoute(), and handleNetworkStream() now accept agentVersion (and runtime ?versionId= / ?status= overrides) to route traffic to draft/published or specific version IDs (requires the Editor).

Tool calling reliability upgrades: strict mode + better API error recovery

You can enable per-tool strict: true in createTool() for providers that support strict tool calling, and processors can now intercept LLM failures via processAPIError (including a built-in PrefillErrorHandler that retries Anthropic “assistant message prefill” errors automatically).

Browser automation persistence & stability improvements

Agent Browser/Stagehand add lightweight auth persistence via storageState/exportStorageState(), support persistent sessions via profile and custom Chrome via executablePath, and improve cleanup (stale lock files/orphaned Chrome processes) for more reliable runs.

Breaking Changes
  • @mastra/voice-sarvam@1.0.0 drops deprecated Sarvam models (bulbul:v1, saarika:v1/v2/flash), defaults shift to bulbul:v3 + speaker shubh, and the TTS request payload changes from inputs[] to text.

Changelog

@mastra/core@1.25.0
Minor Changes
  • feat(server): Add mapUserToResourceId callback to auth config for automatic resource ID scoping (#13954)

    Auth configs now accept a mapUserToResourceId callback that maps the authenticated user to a resource ID after successful authentication. This enables per-user memory and thread isolation without requiring custom middleware or adapter subclassing.

    const mastra = new Mastra({
      server: {
        auth: {
          authenticateToken: async token => verifyToken(token),
          mapUserToResourceId: user => user.id,
        },
      },
    });

    The callback is called in coreAuthMiddleware after the user is authenticated and set on the request context. The returned value is set as MASTRA_RESOURCE_ID_KEY, which takes precedence over client-provided values for security. Works across all server adapters (Hono, Express, Next.js, etc.).

  • Added processAPIError hook to the Processor interface for intercepting LLM API call failures before they surface as errors. New built-in PrefillErrorHandler automatically recovers from Anthropic "assistant message prefill" errors by appending a <system-reminder>continue</system-reminder> user message and retrying once. (#14435)

  • Experiments now run the correct agent version (#15317)

    When an experiment specifies agentVersion, the experiment pipeline now resolves and executes against that specific version instead of ignoring it. Previously, the version was stored as metadata but the agent always ran with its current default configuration.

    entityVersionId is now a first-class observability dimension

    New entityVersionId, parentEntityVersionId, and rootEntityVersionId fields are available on all observability records (spans, metrics, scores, feedback, logs). This enables filtering and grouping OLAP queries by version at any level of the span tree. rootEntityVersionId is particularly useful for aggregating all signals within a versioned agent's trace. This replaces the previous resolvedVersionId attribute which was buried in span attributes and unfilterable.

    experimentId propagated to agent spans

    Agent spans created during experiment execution now carry the experimentId, enabling trace-to-experiment cross-referencing.

    Scorer correlation context

    Scorers running in the experiment pipeline now receive full targetCorrelationContext (including experimentId), so scores emitted via observability carry experiment context.

    New experiment query filters

    listExperiments now supports filtering by targetType, targetId, agentVersion, and status. listExperimentResults now supports filtering by traceId and status.

  • Added profile and executablePath options to browser config for persistent sessions and custom browser support. Automatically cleans up stale Chrome lock files on browser close. (#15194)

  • Added (#15313) Added per-tool strict mode for providers that support strict tool calling. You can now set strict: true on createTool() and Mastra will forward it when preparing tool definitions.

    const weatherTool = createTool({
      id: 'weather',
      description: 'Get weather for a city',
      strict: true,
      inputSchema: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ city }),
    });
Patch Changes
  • Update provider registry and model documentation with latest models and providers (582644c)

  • Fixed mastra_workspace_list_files silently returning no files when agents passed an empty pattern (e.g. pattern: [] or pattern: ''). Empty and whitespace-only patterns are now treated as "no filter" and return the full listing instead of a dirs-only view or a picomatch error. (#15360)

    Fixed harness tool approval, decline, and resume handlers hardcoding requireToolApproval: true. They now follow the harness yolo state like sendMessage already does, so resumed tool calls in yolo mode no longer get unexpectedly re-gated on approval.

  • Update references to "Mastra Cloud" to "Mastra platform" (#15297)

  • Fixed symlinked skill paths so workspace skills resolve consistently and allowed path checks work through both symlink and real paths. (#15228)

  • AgentBrowser with default thread scope now initializes correctly. Previously, calling launch() followed by getPage() would throw "Browser not launched" when no explicit thread ID was provided. (#15285)

  • fix: ensure listVectorStores always returns a string id (#15239)

  • Improved structuredOutput.model error messages to surface upstream structuring failures, including plain-object errors, instead of a generic internal agent error. (#15226)

  • Agent instances can now create lightweight clones that preserve all configuration, so version overrides and tools are isolated without mutating the shared runtime agent. (#15314)

  • Fixed structuredOutput.model custom gateway resolution by registering the internal structuring agent with the parent Mastra instance. (#15230)

  • Fixed OpenAI reasoning summary streaming so reasoning summary text is preserved when multiple summaries overlap or finish out of order. (#15225)

  • Upgraded model router providers to AI SDK v3 spec: OpenAI, Anthropic, Google, xAI, Groq, and Mistral now use the latest v6 SDK packages. Providers built on openai-compatible (Cerebras, DeepInfra, DeepSeek, Perplexity, TogetherAI) remain on v2 spec until their base package is updated. All provider packages (both v5 and v6) bumped to their latest stable patch versions. (#15358)

    Fixed 'item missing its reasoning part' error for OpenAI reasoning models (gpt-5-mini, gpt-5.2). The v5 SDK couldn't serialize reasoning items for OpenAI's Responses API, so Mastra stripped them from prompts — but this caused errors in multi-turn conversations with memory enabled. With v3 providers, reasoning items are serialized natively and the stripping workaround has been removed.

  • Fixed gateway model detection to use duck typing instead of instanceof check, preventing potential failures from cross-package module resolution issues. Propagates gatewayId through the AISDKV5LanguageModel wrapper so duck-type detection works even when models are re-wrapped. (#15168)

  • Fixed Channels not working on Vercel serverless (and other serverless platforms). Webhook handlers now await initialization on cold starts instead of immediately returning 503, and pass the platform's waitUntil to the Chat SDK so agent processing survives after the HTTP response is sent. See #15300. (#15335)

  • fix(core): Restore AI SDK v6 provider option typings for vector embeddings (#15306)

    The vendored AI SDK v6 declaration build now re-exports ProviderOptions after type bundling renames it to ProviderOptions_2. This fixes TS2724 errors in @mastra/core when vector embeddings import AI SDK v6 provider option types.

@mastra/agent-browser@0.2.0
Minor Changes
  • Added storageState option and exportStorageState() method for lightweight auth persistence (cookies and localStorage). Also kills orphaned Chrome child processes on close to prevent zombies. (#15194)
Patch Changes
  • AgentBrowser with default thread scope now initializes correctly. Previously, calling launch() followed by getPage() would throw "Browser not launched" when no explicit thread ID was provided. (#15285)
@mastra/ai-sdk@1.4.0
Minor Changes
  • Added structured output streaming to the AI SDK UI stream. When an agent produces structured output, the final object is now emitted as a data-structured-output data part in the UI message stream, making it available to frontends via AI SDK UI's custom data handling. (#15237)

  • Added agent versioning support to chat and network route handlers. You can now pass agentVersion to chatRoute(), handleChatStream(), networkRoute(), and handleNetworkStream() to target a specific agent version by ID or status (draft/published). Route handlers also accept ?versionId=<id> or ?status=draft|published query parameters at request time, which take precedence over static configuration. Requires the Editor to be configured. (#15296)

    // Static version on route config
    chatRoute({
      path: '/chat',
      agent: 'weatherAgent',
      agentVersion: { status: 'published' },
    });
    
    // Programmatic version on handler
    const stream = await handleChatStream({
      mastra,
      agentId: 'weatherAgent',
      agentVersion: { versionId: 'ver_abc123' },
      params,
    });
Patch Changes
  • Fixed reasoning streams so reasoning UI parts are only emitted when reasoning content is included. (#15207)
@mastra/clickhouse@1.4.1
Patch Changes
  • Added entityVersionId, parentEntityVersionId, and rootEntityVersionId columns to observability storage tables (spans, metrics, scores, feedback, logs) for filtering and grouping traces by entity version. Added ALTER TABLE migrations for existing databases. Added targetType, targetId, agentVersion, and status filters to listExperiments, and traceId and status filters to listExperimentResults. (#15317)
@mastra/duckdb@1.1.2
Patch Changes
  • Added entityVersionId, parentEntityVersionId, and rootEntityVersionId columns to observability storage tables (spans, metrics, scores, feedback, logs) for filtering and grouping traces by entity version. Added ALTER TABLE migrations for existing databases. Added targetType, targetId, agentVersion, and status filters to listExperiments, and traceId and status filters to listExperimentResults. (#15317)
@mastra/editor@0.7.16
Patch Changes
  • Resolving stored agent versions no longer mutates the shared singleton agent instance. Instruction and tool overrides are now applied to an isolated clone, making concurrent version resolution safe and preventing overrides from leaking onto the global agent. (#15314)
@mastra/libsql@1.8.1
Patch Changes
  • Fixed "column does not exist" errors when using experiment review features on databases created before the review pipeline was introduced. Startup now automatically migrates older experiment tables to the latest schema. (#15304)

  • Added entityVersionId, parentEntityVersionId, and rootEntityVersionId columns to observability storage tables (spans, metrics, scores, feedback, logs) for filtering and grouping traces by entity version. Added ALTER TABLE migrations for existing databases. Added targetType, targetId, agentVersion, and status filters to listExperiments, and traceId and status filters to listExperimentResults. (#15317)

@mastra/mcp@1.5.0
Minor Changes
  • Added requireToolApproval option to MCP server configuration for requiring human approval before tool execution. Supports both boolean (all tools) and function (dynamic per-tool logic). (#15315)
Patch Changes
  • Preserve forwarded MCP client elicitation capabilities so client-supported URL and form elicitations work correctly. (#15233)
@mastra/memory@1.15.1
Patch Changes
  • Fixed gateway model detection to use duck typing instead of instanceof check, preventing potential failures from cross-package module resolution issues. Propagates gatewayId through the AISDKV5LanguageModel wrapper so duck-type detection works even when models are re-wrapped. (#15168)
@mastra/mongodb@1.7.1
Patch Changes
  • Added entityVersionId, parentEntityVersionId, and rootEntityVersionId columns to observability storage tables (spans, metrics, scores, feedback, logs) for filtering and grouping traces by entity version. Added ALTER TABLE migrations for existing databases. Added targetType, targetId, agentVersion, and status filters to listExperiments, and traceId and status filters to listExperimentResults. (#15317)
@mastra/observability@1.9.1
Patch Changes
  • Fixed double-counting of Anthropic cache tokens in usage metrics (#15316)

  • Cost estimates now use the latest model pricing rates for more accurate calculations (#15362)

  • Update references to "Mastra Cloud" to "Mastra platform" (#15297)

  • Reduced observability overhead for MODEL_STEP spans by storing a lightweight message preview of request bodies. (#15249)

    This keeps span previews readable and avoids pulling large payloads into exporter input.

  • Fixed cost lookup for models with date suffixes. Providers like OpenAI often return model names with date suffixes (e.g., gpt-5.4-mini-2026-03-17) that don't exactly match pricing data entries. The lookup now tries multiple variants including stripping date suffixes and converting dots to dashes. (#15349)

  • Added entityVersionId, parentEntityVersionId, and rootEntityVersionId to span correlation context, enabling version information to propagate to scores, metrics, logs, and feedback emitted during traced execution. (#15317)

  • Fixed stack traces for errors reported to Sentry. Exceptions now point to the code that threw the error instead of SentryExporter.handleSpanEnded inside the exporter, so issues in Sentry are actually debuggable. (#15343)

    This was caused by two issues, both fixed:

    • @mastra/sentry passed the error message as a string to Sentry.captureException, which made Sentry synthesize a stack trace from the exporter's call site. It now passes an Error instance with the captured stack attached.
    • @mastra/observability stored the wrapping MastraError's stack on the span, hiding the original error's location. When the MastraError has a cause, the cause's stack is now preserved.

    Fixes #15337.

@mastra/pg@1.9.1
Patch Changes
  • Fixed vector similarity queries to leverage HNSW and IVFFlat indexes. When querying without filters on an HNSW or IVFFlat-indexed table, ORDER BY and LIMIT are now placed inside the CTE so PostgreSQL can use the index for faster approximate nearest neighbor searches instead of scanning all rows. (#14574)

  • Fixed batchInsert and batchUpdate in @mastra/pg to run on a single Postgres transaction connection. (#15312)

    This prevents pooled BEGIN/COMMIT/ROLLBACK calls from landing on different connections and leaving idle transactions open during batch writes.

  • Fixed "column does not exist" errors when using experiment review features on databases created before the review pipeline was introduced. Startup now automatically migrates older experiment tables to the latest schema. (#15304)

  • Fixed vector operations failing when pgvector extension is installed in a custom schema. The search_path is now set before index creation and vector similarity queries, ensuring operator classes (e.g. vector_cosine_ops) and distance operators (e.g. <=>) resolve correctly regardless of where the extension is installed. Previously, only table creation set the search_path, causing CREATE INDEX and query operations to fail with unresolvable operator errors. (#14526)

  • Added entityVersionId, parentEntityVersionId, and rootEntityVersionId columns to observability storage tables (spans, metrics, scores, feedback, logs) for filtering and grouping traces by entity version. Added ALTER TABLE migrations for existing databases. Added targetType, targetId, agentVersion, and status filters to listExperiments, and traceId and status filters to listExperimentResults. (#15317)

@mastra/playground-ui@22.1.2
Patch Changes
  • Added PageLayout and PageHeader compound components for consistent page structure across Mastra Studio, plus a NoDataPageLayout helper for 401/403/empty/error states. (#15243)

    List components (AgentsList, WorkflowsList, ToolsList, ProcessorsList, McpServersList, PromptsList, LogsList, ObservabilityTracesList) no longer handle errors or empty states internally — handle those at the page level. If you consume these components directly, move error/empty-state rendering to the parent.

  • Fix dataset detail tab badges to use total item and experiment counts instead of currently loaded rows. (#14994)

  • Added EntityList.Pagination sub-component for server-side pagination of EntityList views. Mirrors the existing ItemList.Pagination API. (#15353)

  • Added ValueLink, ValueWithTooltip, and ValueWithCopyBtn variants to DataKeysAndValues component (#15208)

  • Refresh Studio Evaluation pages with an updated UI and flattened top-level URLs (/scorers, /datasets, /experiments; /evaluation remains as the overview). @mastra/playground-ui removes EvaluationDashboard and all Evaluation*-prefixed list components, constants, and hooks — use the per-domain replacements (e.g. ScorersList) instead. (#15258)

  • Fixed DataList to only take as much height as its content needs instead of always stretching to fill available space (#15291)

@mastra/posthog@1.0.17
Patch Changes
  • Fixed generation traces producing stringified JSON in messages instead of structured content. Input messages wrapped as {messages: [...]} and output objects with text are now properly extracted and formatted. (#15203)
@mastra/schema-compat@1.2.8
Patch Changes
  • --- (#14624) @mastra/schema-compat: patch


    Improved provider schema compatibility for structured outputs and tool calls. Fixed validation for optional, nullable, and defaulted fields, and for ISO date strings returned for date fields.

@mastra/sentry@1.0.16
Patch Changes
  • Fixed stack traces for errors reported to Sentry. Exceptions now point to the code that threw the error instead of SentryExporter.handleSpanEnded inside the exporter, so issues in Sentry are actually debuggable. (#15343)

    This was caused by two issues, both fixed:

    • @mastra/sentry passed the error message as a string to Sentry.captureException, which made Sentry synthesize a stack trace from the exporter's call site. It now passes an Error instance with the captured stack attached.
    • @mastra/observability stored the wrapping MastraError's stack on the span, hiding the original error's location. When the MastraError has a cause, the cause's stack is now preserved.

    Fixes #15337.

@mastra/server@1.25.0
Minor Changes
  • feat(server): Add mapUserToResourceId callback to auth config for automatic resource ID scoping (#13954)

    Auth configs now accept a mapUserToResourceId callback that maps the authenticated user to a resource ID after successful authentication. This enables per-user memory and thread isolation without requiring custom middleware or adapter subclassing.

    const mastra = new Mastra({
      server: {
        auth: {
          authenticateToken: async token => verifyToken(token),
          mapUserToResourceId: user => user.id,
        },
      },
    });

    The callback is called in coreAuthMiddleware after the user is authenticated and set on the request context. The returned value is set as MASTRA_RESOURCE_ID_KEY, which takes precedence over client-provided values for security. Works across all server adapters (Hono, Express, Next.js, etc.).

Patch Changes
  • fix(server): Strip reserved context keys from client-provided requestContext (#13954)

    Clients could inject mastra__resourceId or mastra__threadId via the request body or query params to impersonate other users' memory/thread access. Reserved keys are now filtered out during request context creation in mergeRequestContext, so only server-side code (auth callbacks, middleware) can set them.

@mastra/stagehand@0.2.0
Minor Changes
  • Added automatic cleanup on browser close: patches exit_type to prevent restore dialogs, kills orphaned Chrome child processes, and uses CDP events for reliable disconnect detection in both shared and thread scope. (#15194)
Patch Changes
@mastra/voice-sarvam@1.0.0
Major Changes
  • Added support for Sarvam's current TTS and STT models. Previously the package only supported the now-deprecated bulbul:v1 and saarika:v1/v2/flash models, which Sarvam has retired. (#15204)

    What's new:

    • TTS models: bulbul:v2, bulbul:v3 (default), and bulbul:v3-beta. bulbul:v3 and bulbul:v3-beta support 39 speakers; bulbul:v2 supports 7 speakers.
    • STT models: saarika:v2.5 (default) and saaras:v3. saaras:v3 is a multi-mode model that supports transcribe, translate, verbatim, translit, and codemix via a new mode option.
    • New bulbul:v3 parameters: temperature, dict_id, output_audio_codec.
    • Expanded speech_sample_rate options: 8000, 16000, 22050, 24000, 32000, 44100, 48000.

    Breaking changes:

    • Removed the deprecated bulbul:v1 TTS model and its speakers (meera, pavithra, maitreyi, arvind, amol, amartya, diya, neel, misha, vian, arjun, maya). Sarvam has retired the underlying API.
    • Removed the deprecated saarika:v1, saarika:v2, and saarika:flash STT models.
    • The default TTS model is now bulbul:v3 and the default speaker is shubh. Speakers are not interchangeable between bulbul versions — each has its own catalog.
    • The TTS request body now sends text (single string) instead of inputs (array), matching Sarvam's current API.

    Migration:

    Before:

    const voice = new SarvamVoice({
      speechModel: { model: 'bulbul:v1', language: 'en-IN' },
      speaker: 'meera',
      listeningModel: { model: 'saarika:v2' },
    });

    After:

    const voice = new SarvamVoice({
      speechModel: { model: 'bulbul:v3', language: 'en-IN' },
      speaker: 'shubh',
      listeningModel: { model: 'saarika:v2.5' },
    });
    
    // Or use saaras:v3 for speech translation:
    await voice.listen(audio, { model: 'saaras:v3', mode: 'translate' });

    Resolves #15188.

Patch Changes
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.29.0

Highlights

Azure Blob Storage workspace + blob store (@mastra/azure)

New @mastra/azure adds an Azure Blob Storage WorkspaceFilesystem provider (@mastra/azure/blob) and an AzureBlobStore content-addressable store for skill versioning, with support for connection string, account key, SAS token, DefaultAzureCredential, anonymous auth, prefix namespacing, and read-only mode.

Stream “until idle” to include background task results in the same turn

agent.streamUntilIdle() (plus POST /api/agents/:agentId/stream-until-idle) keeps SSE streaming open until all background tasks finish, then automatically re-invokes the agent so task results are incorporated without needing a second user message; client-js and React useChat now use this flow.

Resume suspended agent streams over HTTP (resume-stream)

Mastra Server adds POST /agents/:agentId/resume-stream and @mastra/client-js adds agent.resumeStream() so apps can resume a previously suspended run with custom data (e.g., approvals/choices) via the client SDK.

Forked subagents for isolated execution on a cloned thread

A new forked flag for HarnessSubagent (and the built-in subagent tool input) runs subagents on a cloned parent thread to preserve prompt-cache prefix while isolating writes; forks inherit parent tools/instructions, flush pending message saves before cloning, and are hidden from Harness.listThreads() by default unless explicitly included.

Cloudflare Workers / V8-isolate support for MCP JSON schema validation

@mastra/mcp adds a jsonSchemaValidator pass-through for MCPClient and MCPServer, enabling validators like CfWorkerJsonSchemaValidator so tools with outputSchema work in environments where Ajv’s new Function(...) compilation is blocked.

Breaking Changes
  • None noted in this changelog.

Changelog

@mastra/core@1.29.0
Minor Changes
  • Added prepareRun and filterRun utilities for scorer input preparation, allowing scorers to filter and transform agent messages before scoring. Scorers can now declare a prepareRun hook or use the filterRun builder to select specific message part types and tool names before scoring runs. (#15642)

    Added getCurrentTraceId() to Harness, which captures the observability trace ID from agent stream responses. This allows callers to correlate feedback and other annotations to the correct trace.

  • Added progressThrottleMs to background task configuration so high-frequency progress output can be coalesced before it reaches pubsub and stream consumers. (#15829)

    const mastra = new Mastra({
      backgroundTasks: {
        enabled: true,
        progressThrottleMs: 500,
      },
    });
  • Added forked subagents: a new forked flag on HarnessSubagent definitions and the built-in subagent tool input. When set, the subagent runs on a clone of the parent thread using the parent agent's instructions and tools, preserving prompt-cache prefix while isolating writes from the main conversation. (#15695)

Patch Changes
  • Stop logging client-disconnect aborts as Error in LLM execution at error level. The catch block in agentic-execution/llm-execution-step.ts now checks for isAbortError(error) first and exits via a debug-level log + the existing onAbort flow before the upstream-error / generic-error branches run. Closes #15844. (#15847)

  • Fixed Studio observability tabs so runtime-injected observability unlocks them. (#15821)

  • Update provider registry and model documentation with latest models and providers (b510d36)

  • Added a stream error retry processor with OpenAI Responses stream error matching. (#15760)

  • Enable ProviderHistoryCompat error processor by default in mastracode (#15730)

  • Fixed AgentChannels.consumeAgentStream silently dropping tripwire chunks, which left channel users (Slack, Discord) with no response when a strategy: "block" processor fired. The chunk is now handled: when retry is false/unset the block reason is posted to the channel (prefixed with the processorId when present); when retry is true the chunk is skipped so the agent's retried output can flow through normally. (#15692)

  • Fixed toModelOutput lookup for dynamically loaded tools via ToolSearchProcessor (#15452)

  • Replace wildcard ./* export with an explicit allowlist of 48 valid subpath exports. The wildcard combined with tsc emitting individual .d.ts files created phantom subpaths (e.g. @mastra/core/auth/ee/defaults) that compiled in TypeScript but crashed at runtime with MODULE_NOT_FOUND. The allowlist approach only exposes subpaths that have actual runtime JS, preventing phantom imports entirely. (#15794)

  • fix(tools): preserve args for programmatic tool calls when merging synthetic tool-call (#15227)

    Fixes an issue where programmatic tool calls (PTC) received empty {} arguments during streaming.

    When a synthetic tool-call was created with empty args, the real tool-call event (containing actual args) was ignored. This change ensures that args from the real tool-call are merged into the synthetic one when missing.

  • Fixed agents forcing temperature: 0 when the user did not explicitly set it. Previously, every agent.stream() / agent.generate() call silently injected temperature: 0 into model settings, which broke models that restrict acceptable temperature values (for example Moonshot Kimi K2.5, which only accepts temperature=1 and rejects any other value with 400 Bad Request). The model provider's own default is now used when the user does not configure a temperature. Users who explicitly set temperature (including temperature: 0 for deterministic output) are unaffected. Fixes #15240. (#15611)

  • Fix forked subagent fork threads starting with empty history. The parent stream's message saves are debounced through SaveQueueManager, so a forked subagent that calls memory.cloneThread mid-stream used to clone from an empty store and lose the parent's user + assistant turn. The tool now drains the parent save queue via a new flushMessages callback on AgentToolExecutionContext before cloning, so forks actually carry the prior conversation. (#15695)

  • The internal <subagent-meta /> tag is no longer appended to subagent tool result content. The tag was previously visible to the parent model in the tool result, which could cause it to be echoed back as literal markup in the parent's assistant text on subsequent turns. Live UIs continue to receive model / duration / tool-call information via the structured subagent_* events; history UIs read the persisted tool_call.args.modelId. parseSubagentMeta is retained so already-persisted threads carrying the legacy tag continue to render cleanly (and the tag is stripped before display in all cases). (#15695)

  • Forked subagents now inherit the parent agent's toolsets (so harness-injected tools like ask_user, submit_plan, and user-configured harness tools remain available inside a fork). The subagent tool entry is kept in the inherited toolset with its id, description, and schemas unchanged so the LLM request prefix stays byte-identical to the parent's and the prompt cache continues to hit; recursive forking is blocked at the runtime layer by replacing only the tool's execute with a stub that returns a "tool unavailable inside a forked subagent" message. Forked runs allow follow-up steps so the model can recover and answer directly if it accidentally calls that stub. Fork threads are tagged with metadata.forkedSubagent: true and metadata.parentThreadId, and Harness.listThreads() hides them by default so they don't surface in user-facing thread pickers; pass includeForkedSubagents: true to opt back in for admin/debug tooling. (#15695)

    Mastra Code now renders forked subagent footers as subagent fork <parent model id>, including persisted history reloaded after the live event metadata is gone.

  • Added missing A2A vNext error variants for protocol 0.3 handling. (#15720)

  • Users now get a clear error when using Observational Memory with agent network. (#15808)

  • Improved skills discovery performance by parallelizing filesystem I/O operations. Discovery of multiple skills, subdirectory scanning, reference file reads, and staleness checks now run concurrently instead of sequentially. Also fixed a bug in CompositeVersionedSkillSource where the root directory stat always returned the current time, causing unnecessary re-discovery on every refresh cycle. (#14360)

  • Fixed tool strict: true being silently dropped when routing through V2 (AI SDK v5) OpenAI providers. V2 providers use a global strictJsonSchema provider option instead of per-tool strict, so Mastra now propagates the intent automatically — when any tool on a call has strict: true, providerOptions.openai.strictJsonSchema is set to true before the request is sent. Explicit user-supplied strictJsonSchema values are respected and never overridden. (#15450)

  • Fixed execute_command timeout handling for models that send numeric values as strings. (#15765)

  • Fixed resourceId not being forwarded to createRun() in the agentic loop, which caused persistWorkflowSnapshot to receive resourceId: undefined. (#15742)

  • Fixed agent loops so truncated model responses stop instead of retrying pending tool calls until max steps. (#15788)

  • Fixed dataset.startExperiment for workflow targets to match runEvals. Previously, scorers running inside a persisted experiment could not access per-step input or output, requestContext configured on the experiment was not forwarded into the workflow run, and direct agent calls inside workflow steps could start detached traces instead of nesting under the workflow step span. Step-level data is now exposed to scorers via run.targetMetadata.stepResults and run.targetMetadata.stepExecutionPath, the workflow's root span ID is available as run.targetSpanId, requestContext propagates into every step, and ambient workflow step tracing is used when creating nested spans. Fixes #15613. (#15792)

  • Fixed requireApproval being silently ignored for tools loaded dynamically via ToolSearchProcessor. The approval gate now fires a tool-call-approval event and pauses execution before running, matching the behaviour of tools registered directly on the agent. (#15782)

  • Fixed AI SDK v5 message rehydration so suspended and approval tool state data parts are restored from persisted message metadata after reload. (#14246)

  • Fixed the TypeScript type for requireApproval on tools so it accepts a function in addition to a boolean. The runtime already supported per-call approval functions (added in #15346), but the type still required boolean, forcing an as any cast. You can now pass a sync or async predicate without a cast — the predicate receives the validated tool input and an optional { requestContext, workspace } second argument. Fixes #15647. (#15783)

  • Added ProviderHistoryCompat error processor that automatically sanitizes tool-call IDs when switching between LLM providers. When a provider rejects tool IDs from another provider's history (e.g. Anthropic enforces ^[a-zA-Z0-9_-]+$), the processor rewrites invalid characters and retries the request. (#15730)

  • Add agent.streamUntilIdle() and default sub-agents to run as background tasks. (#15686)

    streamUntilIdle

    A new agent streaming method that keeps the stream open until all background tasks dispatched during the turn complete. When a task finishes, the agent is re-invoked automatically so the result is processed in the same call — no second user turn required.

    // Before — stream closes once the LLM returns. Background task
    // results are only processed on the next user message.
    const result = await agent.stream('Research quantum computing', { memory });
    for await (const chunk of result.fullStream) {
      /* ... */
    }
    
    // After — stream stays open through the background task completion
    // and the follow-up agent turn; the final answer arrives in the same call.
    const result = await agent.streamUntilIdle('Research quantum computing', { memory });
    for await (const chunk of result.fullStream) {
      /* ... */
    }
@mastra/auth-studio@1.2.2
Patch Changes
  • Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. (#15819)

    Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.

@mastra/azure@0.2.0
Minor Changes
  • Add @mastra/azure, exporting an Azure Blob Storage WorkspaceFilesystem provider via @mastra/azure/blob with support for connection string, account key, SAS token, DefaultAzureCredential, and anonymous auth, plus prefix namespacing and read-only mode. (#15217)

  • Added AzureBlobStore, a content-addressable blob store backed by Azure Blob Storage for skill versioning. Available alongside the existing AzureBlobFilesystem from @mastra/azure/blob. (#15853)

    import { AzureBlobStore } from '@mastra/azure/blob';
    
    const blobs = new AzureBlobStore({
      container: 'my-skill-blobs',
      connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
    });

    Supports the same authentication methods as AzureBlobFilesystem: connection string, account key, SAS token, DefaultAzureCredential, and anonymous access. A matching azureBlobStoreProvider descriptor is also exported for MastraEditor.

Patch Changes
@mastra/client-js@1.15.0
Minor Changes
  • Added support for resuming suspended agent streams over HTTP with custom data. This adds the POST /agents/:agentId/resume-stream server endpoint and the client SDK agent.resumeStream() method, so apps can continue a suspended agent run through the Mastra client. (#14579)

    Usage example (client SDK):

    const agent = mastraClient.getAgent('my-agent');
    
    // Resume a suspended agent stream with custom data
    const response = await agent.resumeStream(
      { approved: true, selectedOption: 'plan-b' },
      { runId: 'previous-run-id', toolCallId: 'tool-123' },
    );
    
    await response.processDataStream({
      onChunk: chunk => console.log(chunk),
    });
  • Improved the Mastra A2A client to feel closer to the official A2A SDK without introducing a breaking change. (#15720)

    • Added official-style A2A methods such as getAgentCard(), sendMessageStream(), getExtendedAgentCard(), and getTaskPushNotificationConfig().
    • Added typed A2A stream consumption for sendMessageStream() and resubscribeTask().
    • Kept older methods available as deprecated compatibility methods, including getCard() and sendStreamingMessage().
Patch Changes
  • Fixed Studio observability tabs so runtime-injected observability unlocks them. (#15821)

  • Add streamUntilIdle to the agent client, mirroring the new server route. The client keeps the SSE connection open through background task completion and the agent's follow-up turn, and preserves the /stream-until-idle endpoint across client-tool continuations. (#15686)

    const stream = await client.getAgent('my-agent').streamUntilIdle({
      messages: 'Research quantum computing',
    });
    for await (const chunk of stream) {
      /* ... */
    }
@mastra/deployer@1.29.0
Patch Changes
  • Fixed slow or stuck mastra dev startup in large monorepos when workspace packages share internal dependencies. (#12963)

    What changed

    • Mastra now avoids repeating the same dependency analysis work during dev startup when multiple workspace packages depend on the same internal package.
    • This reduces repeated startup work in large monorepos and helps the dev server reach a ready state more reliably.

    Fixes #12843.

@mastra/express@1.3.13
Patch Changes
  • Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. (#15819)

    Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.

@mastra/fastify@1.3.13
Patch Changes
  • Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. (#15819)

    Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.

@mastra/hono@1.4.8
Patch Changes
  • Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. (#15819)

    Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.

  • Refactored Hono adapter's registerCustomApiRoutes() to use the shared buildCustomRouteHandler() from the base class instead of duplicating route/handler resolution logic. Added forwardCustomRouteRequest() to the base class for adapters that already have a raw Request object (avoiding unnecessary request reconstruction). (#15793)

@mastra/koa@1.4.13
Patch Changes
  • Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. (#15819)

    Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.

@mastra/mcp@1.6.0
Minor Changes
  • Added jsonSchemaValidator pass-through option on MCPClient server entries and MCPServer. Forward this option from @modelcontextprotocol/sdk to opt into a non-default validator. Pass CfWorkerJsonSchemaValidator from @modelcontextprotocol/sdk/validation/cfworker to make tools with outputSchema work in Cloudflare Workers / V8 isolates, where the default Ajv validator's new Function(...) compile path is blocked. (#15866)

    import { MCPClient, MCPServer } from '@mastra/mcp';
    import { CfWorkerJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/cfworker';
    
    const mcp = new MCPClient({
      servers: {
        upstream: {
          url: new URL('https://example/mcp'),
          jsonSchemaValidator: new CfWorkerJsonSchemaValidator(),
        },
      },
    });
    
    const server = new MCPServer({
      name: 'My Server',
      version: '1.0.0',
      tools: { ... },
      jsonSchemaValidator: new CfWorkerJsonSchemaValidator(),
    });

    Closes #15862.

Patch Changes
@mastra/memory@1.17.2
Patch Changes
  • Fixed the recall tool so message browsing uses the current thread by default and explains when it does. (#15807)
@mastra/observability@1.10.1
Patch Changes
  • Fixed requestContext filtering in span creation to prevent large objects from being serialized into trace data. (#15642)
@mastra/playground-ui@24.0.0
Minor Changes
  • Added shared ThemeProvider, useTheme, and ThemeToggle to unify theme management. (#15838)

    Added

    • ThemeProvider applies the resolved theme class to <html> and persists the choice under the shared mastra-theme localStorage key, with a one-time migration from previously stored preferences.
    • useTheme() works without a <ThemeProvider> ancestor: it returns a read-only fallback that tracks the OS color scheme and exposes a no-op setTheme, so theme-aware leaf components (e.g. CodeDiff, CodeEditor) keep working when embedded standalone.
    • ThemeToggle renders a system/light/dark pill and supports both controlled and uncontrolled usage.
Patch Changes
  • Migrated color tokens to oklch() for perceptually uniform, wide-gamut (P3) ready colors. Light theme neutrals and surfaces no longer have a blue tint (slate → true gray). Dark theme is visually unchanged. (#15713)

  • Added shared Logs components and hooks under @mastra/playground-ui. Consumers can now reuse the Logs page building blocks together with the data hooks and the URL-state / filter-persistence helpers instead of duplicating them per app. (#15723)

  • Added shared metrics components and hooks under @mastra/playground-ui. Consumers can now reuse the metrics dashboard building blocks (KPI, Latency, Scores, Token Usage, Trace Volume, Model Usage Cost cards), their data hooks, and the MetricsProvider / DateRangeSelector primitives instead of duplicating them per app. (#15705)

    New peer dependency: @tanstack/react-query ^5.90.21. Add it alongside your existing playground-ui install.

  • Added shared Traces components and hooks under @mastra/playground-ui. Consumers can now reuse the Traces page building blocks together with the data hooks and the URL-state / filter-persistence helpers instead of duplicating them per app. (#15714)

  • Fixed the logs date filter button height to match other filter controls. (#15801)

@mastra/posthog@1.0.19
Patch Changes
  • Updated posthog-node from v4 to v5 to pick up the latest fixes and LLM analytics improvements. Resolves #15858. (#15867)
@mastra/react@0.2.30
Patch Changes
  • The useChat hook stream now calls the new agent.streamUntilIdle method and the background-task chunks are processed in toUIMessage. (#15686)
@mastra/redis@1.0.2
Patch Changes
  • Fixed Redis package releases to include built files. (#15763)
@mastra/s3@0.5.0
Minor Changes
  • Added AWS credential provider chain support to S3Filesystem and S3BlobStore. You can now pass a credentials option with a credential provider function (e.g. fromNodeProviderChain()) for auto-refreshing credentials on ECS, Lambda, SSO, or AssumeRole deployments. When all credential options are omitted, the AWS SDK default credential provider chain is used automatically instead of falling back to anonymous access. Static accessKeyId/secretAccessKey credentials continue to work as before. (#15437)

    New credentials option

    import { S3Filesystem } from '@mastra/s3';
    import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
    
    // Auto-refreshing credentials (ECS task role, SSO, etc.)
    const fs = new S3Filesystem({
      bucket: 'my-bucket',
      region: 'us-east-1',
      credentials: fromNodeProviderChain(),
    });

    SDK default credential chain (no credentials needed)

    // Credentials discovered from environment automatically
    const fs = new S3Filesystem({
      bucket: 'my-bucket',
      region: 'us-east-1',
    });

    Fixes https://github.com/mastra-ai/mastra/issues/14289

Patch Changes
@mastra/server@1.29.0
Minor Changes
  • Added support for resuming suspended agent streams over HTTP with custom data. This adds the POST /agents/:agentId/resume-stream server endpoint and the client SDK agent.resumeStream() method, so apps can continue a suspended agent run through the Mastra client. (#14579)

    Usage example (client SDK):

    const agent = mastraClient.getAgent('my-agent');
    
    // Resume a suspended agent stream with custom data
    const response = await agent.resumeStream(
      { approved: true, selectedOption: 'plan-b' },
      { runId: 'previous-run-id', toolCallId: 'tool-123' },
    );
    
    await response.processDataStream({
      onChunk: chunk => console.log(chunk),
    });
Patch Changes
  • Fixed Studio observability tabs so runtime-injected observability unlocks them. (#15821)

  • Authentication now refreshes expired server-side sessions transparently, so recoverable token expiry no longer causes unexpected user sign-outs. Only truly expired sessions (e.g. refresh token dead) return a 401. (#15819)

    Server adapters now forward refreshed session cookies consistently, and auth-studio logs session validation and refresh failures to improve diagnostics.

  • Updated the A2A server to match the v0.3 protocol shapes and methods. (#15720)

  • Refactored Hono adapter's registerCustomApiRoutes() to use the shared buildCustomRouteHandler() from the base class instead of duplicating route/handler resolution logic. Added forwardCustomRouteRequest() to the base class for adapters that already have a raw Request object (avoiding unnecessary request reconstruction). (#15793)

  • Custom API routes now validate that their paths don't collide with the built-in route prefix. If a custom route path starts with the server's apiPrefix (default /api), a descriptive error is thrown at startup. This prevents custom routes from shadowing built-in Mastra routes (e.g. /api/agents, /api/tools). (#15743)

  • Add POST /api/agents/:agentId/stream-until-idle SSE route that mirrors agent.streamUntilIdle(). The route keeps the SSE stream open through background task completion and the agent's follow-up turn, so clients receive the final answer in a single request. (#15686)

Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.26.0

Highlights

Background Tasks (Async Tool Execution + APIs + Storage Support)

Agents can now dispatch slow tool calls as background tasks while the main conversation keeps streaming, then inject results back into the loop when they finish. This comes with new /api/background-tasks endpoints (list/get/SSE stream), client methods (listBackgroundTasks, getBackgroundTask, streamBackgroundTasks), and new BackgroundTasksStorage domain implementations across major storage adapters.

New Redis Storage Adapter (@mastra/redis)

Introduces @mastra/redis, a Redis-backed Mastra storage provider (memory/workflows/scores) using the official node-redis client, with flexible connection options including connection strings or injected preconfigured clients.

Netlify Edge Deployment Target

NetlifyDeployer adds a target: 'edge' option to deploy as Netlify Edge Functions (Deno at the edge) with CPU-time limits instead of hard wall-clock timeouts—better suited for longer-running AI workflows than 60s serverless limits.

Observability: RAG Runs in Traces + Lightweight Trace/Span Fetching

RAG ingestion runs now appear in observability traces alongside agents/workflows, and traces can be filtered by traceId. New lightweight schemas and endpoints (including GET /observability/traces/:traceId/light and storage getTraceLight) reduce timeline payloads dramatically by omitting heavy span fields until details are requested.

Security & Governance for Telemetry (Credential Leak Fix + Per-request Redaction/Tags)

Span serialization is hardened to prevent LLM/API credentials and auth headers from leaking into telemetry across routers, gateways, and model wrappers. Additionally, server calls can now set tracingOptions (tags, hideInput, hideOutput) per request to control span labeling and redaction.

Breaking Changes
  • None called out in the provided changelog (no consolidated breaking-change section for these versions).

Changelog

@mastra/core@1.26.0
Minor Changes
  • RAG ingestion runs now appear in observability traces, next to your agents, workflows, and scorers. (#15512)

    You can now filter traces by traceId when listing them.

    Added lightweight span and trace schemas (LightSpanRecord, GetTraceLightResponse) that exclude heavy fields like input, output, attributes, and metadata — reducing per-span payload by ~97% for timeline rendering.

  • Fixed potential credential leakage in observability spans. LLM API keys, authentication headers, and gateway tokens could previously appear in span input or output data sent to telemetry backends. (#15489)

    What's fixed

    The model router, AI SDK model wrappers (v4 legacy, v5, v6), built-in gateways (Mastra, Netlify, Models.dev, Azure OpenAI), and the voice provider base class now restrict what they expose to spans. Only public identity fields — model ID, provider, gateway ID, voice name — are included. Private configuration such as API keys, Authorization headers, OAuth tokens, and proxy credentials is no longer serialized into spans.

    Legacy AI SDK v4 models passed to resolveModelConfig were previously returned unwrapped. They are now wrapped in AISDKV4LegacyLanguageModel, which applies the same serializeForSpan() safety as the v5/v6 wrappers while preserving the LanguageModelV1 interface so existing consumers continue to work.

    The SensitiveDataFilter span output processor already redacted values under common field names (apiKey, token, authorization, etc.) when enabled. This fix closes the gap for users who did not have it configured, and for cases where credentials were nested under custom field names that the filter's exact-match list did not cover.

    Recommended action

    • Review existing telemetry data for leaked credentials and rotate any keys that may have been captured.
    • Custom gateways extending MastraModelGateway and custom voice providers extending MastraVoice are automatically covered — they inherit the new safe default. Override serializeForSpan() only if you want to expose additional non-sensitive fields.
    • For any other class you pass into a span (e.g. as input, output, attributes, or metadata) that holds enumerable fields with credentials or other sensitive state, add a serializeForSpan() method. TypeScript-private properties are still walked by span serialization because private is compile-time only.
    class MyServiceClient {
      constructor(private config: { apiKey: string; endpoint: string }) {}
    
      // Without this, spans carrying a MyServiceClient instance would
      // serialize `config.apiKey` through every enumerable property.
      serializeForSpan() {
        return { endpoint: this.config.endpoint };
      }
    }
  • Added support for sub-agent version overrides in core execution. Global defaults can be set on the Mastra instance and overridden per generate()/stream() call, with cascading propagation via requestContext. (#15373)

  • Added per-entry modelSettings, providerOptions, and headers to agent model fallback arrays. Each entry can now specify its own temperature, topP, provider-specific options, and HTTP headers — either statically or as a function of requestContext. Closes #15421. (#15429)

    Example

    const agent = new Agent({
      model: [
        {
          model: 'google/gemini-2.5-flash',
          maxRetries: 2,
          modelSettings: { temperature: 0.3 },
          providerOptions: { google: { thinkingConfig: { thinkingBudget: 0 } } },
        },
        {
          model: 'openai/gpt-5-mini',
          maxRetries: 2,
          modelSettings: { temperature: 0.7 },
          providerOptions: { openai: { reasoningEffort: 'low' } },
        },
      ],
    });

    Precedence:

    • modelSettings and providerOptions: per-fallback entry > call-time stream() / generate() options > agent defaultOptions. modelSettings shallow-merges by key; providerOptions deep-merges recursively, preserving sibling and nested keys.
    • headers: call-time modelSettings.headers > per-fallback headers > model-router-extracted headers. This preserves the existing Mastra contract from #11275, where runtime headers (typically tracing, auth, tenancy) intentionally override model-level headers.
  • Added activateAfterIdle setting for observational memory so buffered observations can activate after idle time before the next prompt. (#15365)

    Example

    Set activateAfterIdle: 300_000 (or "5m") on the observationalMemory config to activate buffered context after 5 minutes of inactivity.

    This helps long-running threads reuse compressed context after prompt cache TTLs expire instead of sending a larger raw message window on the next request.

  • You can now opt into parent-agent reuse for the separate structured-output pass with structuredOutput: { schema, model, useAgent: true }, which lets the structuring request reuse the parent agent config, including memory. (#15318)

  • Added unique IDs (logId, metricId, scoreId, feedbackId) to all observability signals, generated automatically at emission time for de-duplication across the framework pipeline and cross-system correlation. User-facing APIs (logger.info(), metrics.emit(), addScore(), addFeedback()) are unchanged. (#15242)

    For existing ClickHouse and DuckDB observability signal tables, run npx mastra migrate before initializing the store so the new signal-ID schema is applied.

  • Processor traces now store hook-specific inputs and only include changed outputs, reducing payload size while keeping traces more replayable. If you consume PROCESSOR_RUN payloads directly, update any dashboards or parsers that depend on the previous shape. (#15493)

Patch Changes
  • Fixed CompositeAuth types so typed auth providers, such as SimpleAuth<MyUser> or MastraAuthClerk, can be combined without casts. (#15556)

  • Update provider registry and model documentation with latest models and providers (3d83d06)

  • Fixed browser context reminders breaking prompt cache. Browser reminders are now added as new user messages instead of modifying existing message history. (#15417)

  • Fixed Harness subagent tracing so delegated runs keep the parent tracing context and show up in the same trace in observability exporters. Fixes #15461. (#15473)

  • Refactored how assistant messages are constructed during streaming. Messages are now built from the complete chunk sequence after each step instead of being assembled mid-stream. This fixes duplicate OpenAI item IDs (rs_*, msg_*), eliminates empty text parts from streaming artifacts, and ensures provider metadata is correctly attributed. (#15454)

  • Fixed nested workflows dropping resourceId when executed as a step of a parent workflow. Child workflow snapshots now preserve the parent run's resource association, so tenant-scoped persistence works end-to-end. Closes #15246. (#15447)

    const run = await parent.createRun({
      runId: 'run-1',
      resourceId: 'workspace-1',
    });
    
    await run.start({ inputData: { ok: true } });
    // Before: child snapshots persisted with resourceId: undefined
    // After:  child snapshots persisted with resourceId: 'workspace-1'
  • Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. (#15566)

  • Fixed messages not being persisted when multiple memory processors are used together. Processor state is now correctly passed between chained workflow steps, ensuring all messages are saved. (#14884)

  • Fix prototype pollution in setNestedValue (@mastra/core/utils) and generateOpenAPIDocument (@mastra/server). (#15565)

    setNestedValue now rejects dot-path segments named __proto__, constructor, or prototype, preventing attacker-controlled field paths passed to selectFields from polluting Object.prototype. generateOpenAPIDocument builds its paths map with Object.create(null) so a route path of __proto__ cannot poison the prototype chain.

  • Fixed assistant model attribution so provider and model information is preserved more reliably in stored assistant messages. (#15462)

    Loop runs now keep the resolved model on the first step-start, already-attributed step-start parts are left alone, and post-tool assistant continuations preserve their incoming metadata when they merge into an existing assistant message.

    This keeps downstream features working with the correct model identity instead of falling back to incomplete metadata or losing it during merge.

  • Fixed channel webhook handling in Node.js when no execution context is available. (#15441)

  • Recalled V4 messages now preserve data-* message parts (e.g. data-tool-call-suspended) after a page refresh, so suspended HITL workflows can resume correctly. (#14211)

  • Fixed structured output to keep persisted assistant text behavior aligned with existing memory recall paths. (#15318)

  • Fixed processOutputStep not receiving token usage data. Output processors now receive usage (inputTokens, outputTokens, totalTokens) for the current LLM step, enabling per-step cost tracking and token budget enforcement. (#15068)

  • Fixed requireApproval on tools to accept a function in addition to a boolean. Previously, passing a function for requireApproval on a tool created with createTool was silently ignored and approval was never required. (#15346)

    import { createTool } from '@mastra/core/tools';
    import { z } from 'zod';
    
    createTool({
      id: 'delete-file',
      description: 'Delete a file',
      inputSchema: z.object({ path: z.string() }),
      // Now works: only require approval for paths outside /tmp
      requireApproval: input => !input.path.startsWith('/tmp/'),
      execute: async ({ context }) => {
        // ...
      },
    });
  • Fixed resume errors for suspended agent runs: resumeStream() and resumeGenerate() now return a clear message when storage is missing or the runId is invalid. (#15514)

  • Fixed OpenAI tool strict mode when requests pass through the model router. strict: true on function tools now survives compatibility prep, so OpenAI Responses models receive strict tool definitions instead of silently downgrading them to non-strict. (#15397)

  • Added multi-select choices to the Harness ask_user tool. (#15485)

  • Fixed noisy browser reminders being added to non-browser turns. Browser reminders are now added only when browser context exists (for example, current page URL or title). (#15416)

  • Fixed dataset.startExperiment hanging forever when targetType is 'workflow'. Workflow experiments now complete normally, honour itemTimeout, and surface failures. Fixes #15453. (#15570)

  • Fixed PrefillErrorHandler to recover from Qwen/llama.cpp prefill rejections with enable_thinking, so agents retry with a continue reminder instead of failing after skill/tool turns. (#15518)

  • Add background task execution for agents. Agents can dispatch slow tool calls to run asynchronously while the conversation keeps streaming, and results are injected back into the loop when they complete. (#15307)

  • Fixed fallback model attribution in agent traces. When an agent fell back after the primary model failed, token usage and cost were reported against the primary model instead of the fallback that actually served the response (e.g. in Langfuse). Fixes #13547. (#15503)

  • Fixed agent stream errors when providers end a stream without an error payload. (#15435)

  • Fixed provider-defined tools with custom execute callbacks (e.g. openai.tools.applyPatch) being incorrectly skipped during execution. Previously, all provider-defined tools were assumed to be provider-executed, which meant user-supplied execute functions were never called. Now, provider tools with a custom execute are correctly identified as client-executed. (#14819)

  • Added model metadata to step-start parts so model changes can be detected across steps, including within a single assistant message. (#15420)

  • Fixed message serialization to preserve millisecond precision in createdAt timestamps. (#15500)

@mastra/ai-sdk@1.4.1
Patch Changes
  • Fixed workflow streaming in @mastra/ai-sdk so intermediate data-workflow parts stop repeating every completed step output. Added data-workflow-step parts with the full payload for the step that just changed, which reduces stream size for long-running workflows while preserving final workflow outputs. (#15218)

    If your UI reads live step outputs during workflow execution, it should now consume data-workflow-step parts in addition to data-workflow. Final workflow snapshots still include the full step outputs.

  • Fix AI SDK v6 approval replay so ordinary user follow-up turns do not resume stale approval responses. (#15480)

  • Fixed tool call approvals in AI SDK v6: handleChatStream now automatically routes to resumeStream when the AI SDK v6 native approval flow is used on the client (no extra server-side wiring required). The v6 stream now emits native tool-approval-request parts so useChat can surface approval UI and call addToolApprovalResponse(), while also emitting the existing data-tool-call-approval chunk for backwards compatibility. (#15345)

  • Fixed AI SDK v6 tool approval streams so requireApproval works with handleChatStream and AssistantChatTransport. (#15345)

@mastra/arthur@0.2.4
Patch Changes
  • Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. (#15566)
@mastra/clickhouse@1.5.0
Minor Changes
  • Added unique IDs (logId, metricId, scoreId, feedbackId) to all observability signals, generated automatically at emission time for de-duplication across the framework pipeline and cross-system correlation. User-facing APIs (logger.info(), metrics.emit(), addScore(), addFeedback()) are unchanged. (#15242)

    For existing ClickHouse and DuckDB observability signal tables, run npx mastra migrate before initializing the store so the new signal-ID schema is applied.

Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)

  • Added getTraceLight method to the observability storage, returning only lightweight span fields needed for timeline rendering. This avoids transferring heavy fields like input, output, attributes, and metadata when they are not needed. (#15574)

@mastra/client-js@1.14.0
Minor Changes
  • Added forEachIndex option to run.resume(), run.resumeAsync(), and run.resumeStream(). Use it to resume a single iteration of a suspended .foreach() step while leaving the other iterations suspended. (#15563)

    await client
      .getWorkflow('myWorkflow')
      .createRun(runId)
      .resume({
        step: 'approve',
        resumeData: { ok: true },
        forEachIndex: 1, // only resume the second iteration
      });
Patch Changes
  • Add /api/background-tasks routes (SSE stream, list with filters + pagination, get by ID) and matching MastraClient methods (listBackgroundTasks, getBackgroundTask, streamBackgroundTasks). (#15307)

  • Fixed @mastra/client-js to re-export RequestContext so client SDK users can import it from @mastra/client-js. (#15413)

  • Added observabilityRuntimeStrategy to GetSystemPackagesResponse so clients can read the active observability tracing strategy (realtime, batch-with-updates, insert-only, or event-sourced) reported by the server. (#15512)

@mastra/cloudflare@1.3.2
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)
@mastra/cloudflare-d1@1.0.5
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)
@mastra/convex@1.0.8
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)
@mastra/deployer-netlify@1.1.0
Minor Changes
  • Added target option to NetlifyDeployer for deploying as Netlify Edge Functions. (#13103)

    export const mastra = new Mastra({
      deployer: new NetlifyDeployer({
        target: 'edge',
      }),
    });

    Edge functions run on Deno at the network edge, closer to users, with no hard execution timeout (only a CPU time limit). This makes them a better fit for longer-running AI workflows that may exceed the 60s serverless function timeout.

    The default target remains 'serverless', so existing usage is unaffected.

Patch Changes
@mastra/docker@0.1.0
Minor Changes
  • Added @mastra/docker, a Docker container sandbox provider for Mastra workspaces. Executes commands inside local Docker containers using long-lived containers with docker exec. Supports bind mounts, environment variables, container reconnection by label, custom images, and network configuration. Targets local development, CI/CD, air-gapped deployments, and cost-sensitive scenarios where cloud sandboxes are unnecessary. (#14500)

    Usage

    import { Agent } from '@mastra/core/agent';
    import { Workspace } from '@mastra/core/workspace';
    import { DockerSandbox } from '@mastra/docker';
    
    const workspace = new Workspace({
      sandbox: new DockerSandbox({
        image: 'node:22-slim',
        timeout: 60_000,
      }),
    });
    
    const agent = new Agent({
      name: 'dev-agent',
      model: 'anthropic/claude-opus-4-6',
      workspace,
    });
Patch Changes
  • Fixed process kill to target the entire process group (negative PID) with fallback, ensuring child processes spawned inside the container are properly cleaned up. Tracked process handles are now cleared after container stop or destroy to prevent stale references. (#14500)
@mastra/duckdb@1.2.0
Minor Changes
  • Added unique IDs (logId, metricId, scoreId, feedbackId) to all observability signals, generated automatically at emission time for de-duplication across the framework pipeline and cross-system correlation. User-facing APIs (logger.info(), metrics.emit(), addScore(), addFeedback()) are unchanged. (#15242)

    For existing ClickHouse and DuckDB observability signal tables, run npx mastra migrate before initializing the store so the new signal-ID schema is applied.

Patch Changes
  • Fixed DuckDB installs by using a resolvable @duckdb/node-api version range. (#15419)

  • Added getTraceLight method to the observability storage, returning only lightweight span fields needed for timeline rendering. This avoids transferring heavy fields like input, output, attributes, and metadata when they are not needed. (#15574)

@mastra/dynamodb@1.0.4
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)
@mastra/google-cloud-pubsub@1.0.3
Patch Changes
  • Add nack support and deliveryAttempt tracking on the subscriber callback, and enable exactly-once delivery on grouped subscriptions. (#15307)
@mastra/laminar@1.0.17
Patch Changes
  • Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. (#15566)
@mastra/lance@1.0.5
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)
@mastra/langfuse@1.2.0
Minor Changes
  • Added new attribute mappings to the Langfuse exporter so more Mastra attributes are filterable in Langfuse's UI. (#15445)

    Observation-level metadatagen_ai.agent.id, gen_ai.agent.name, mastra.span.type, and gen_ai.operation.name are now mapped to langfuse.observation.metadata.*, making them top-level filterable keys on each observation. This lets you scope Langfuse evaluators to specific agents or span types.

    Trace-level attributesmastra.metadata.traceName and mastra.metadata.version are now mapped to langfuse.trace.name and langfuse.trace.version, enabling custom trace names and version-based filtering.

Patch Changes
  • Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. (#15566)

  • Improved Langfuse trace batching for streamed runs by adding flushAt and flushInterval controls. (#15460)

@mastra/libsql@1.9.0
Minor Changes
  • Use DiskANN vector_top_k() index for faster vector queries when available (#14913)

    LibSQLVector.query() now automatically uses the existing DiskANN index for approximate nearest neighbor search instead of brute-force full table scans, providing 10-25x query speedups on larger datasets. Falls back to brute-force when no index exists.

Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)

  • Added getTraceLight method to the observability storage, returning only lightweight span fields needed for timeline rendering. This avoids transferring heavy fields like input, output, attributes, and metadata when they are not needed. (#15574)

@mastra/mcp@1.5.1
Patch Changes
  • Fixed MCP tool strict mode propagation. MCP servers now expose Mastra tool strictness in MCP metadata, and the MCP client restores that flag when rebuilding tools so strict OpenAI tool calling works for MCP-backed tools too. (#15397)

  • Fixed MCP tools with recursive JSON Schema refs so they stay serializable when loaded. (#15400)

@mastra/memory@1.16.0
Minor Changes
  • Added activateAfterIdle setting for observational memory so buffered observations can activate after idle time before the next prompt. (#15365)

    Example

    Set activateAfterIdle: 300_000 (or "5m") on the observationalMemory config to activate buffered context after 5 minutes of inactivity.

    This helps long-running threads reuse compressed context after prompt cache TTLs expire instead of sending a larger raw message window on the next request.

  • Added activateOnProviderChange so observational memory can activate buffered observations and reflections before switching to a different provider or model. (#15420)

    const memory = new Memory({
      options: {
        observationalMemory: {
          model: 'google/gemini-2.5-flash',
          activateOnProviderChange: true,
        },
      },
    });

    This helps keep prompt-cache savings when the next step cannot reuse the previous provider's cache.

Patch Changes
  • Fixed early observational memory activations so buffered reflections are only activated when they still leave a healthy active observation set. (#15462)

    Before this change, idle-timeout (activateAfterIdle) and model/provider-change (activateOnProviderChange) activations could swap in a buffered reflection too early. In bad cases, that replaced a large raw observation tail with a much smaller mostly-compressed result, which hurt reflection quality.

    Early activations now stay buffered unless both of these checks pass:

    • The unreflected observation tail is at least as large as the buffered reflection, so the activated result is not dominated by compressed content.
    • The combined post-activation size is at least 75% of what a normal threshold activation would produce, so early activations do not cliff far below the regular target.

    This update also fixes false provider_change activations when older persisted messages only contain a bare model id like gpt-5.4 while newer turns use the fully qualified provider/modelId form.

  • Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. (#15566)

  • Fixed other-thread context filtering falling back to the observational memory record timestamp when thread metadata is missing. (#15269)

@mastra/mongodb@1.7.2
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)

  • Added getTraceLight method to the observability storage, returning only lightweight span fields needed for timeline rendering. This avoids transferring heavy fields like input, output, attributes, and metadata when they are not needed. (#15574)

@mastra/mssql@1.2.1
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)

  • Added getTraceLight method to the observability storage, returning only lightweight span fields needed for timeline rendering. This avoids transferring heavy fields like input, output, attributes, and metadata when they are not needed. (#15574)

@mastra/observability@1.10.0
Minor Changes
  • Changed MODEL_CHUNK tool-result span output handling. (#15495)

    What changed

    • MODEL_CHUNK spans for tool-result now omit output for locally executed tools.
    • TOOL_CALL remains the canonical span for locally executed tool result payloads.
    • MODEL_CHUNK spans for provider-executed tool-result chunks still include output.
    • MODEL_CHUNK metadata still includes toolCallId, toolName, and providerExecuted.

    Why This reduces duplicate tool result payloads in traces without dropping provider-emitted tool results that may not have a matching TOOL_CALL span.

  • Added unique IDs (logId, metricId, scoreId, feedbackId) to all observability signals, generated automatically at emission time for de-duplication across the framework pipeline and cross-system correlation. User-facing APIs (logger.info(), metrics.emit(), addScore(), addFeedback()) are unchanged. (#15242)

    For existing ClickHouse and DuckDB observability signal tables, run npx mastra migrate before initializing the store so the new signal-ID schema is applied.

Patch Changes
  • Fixed span serialization replacing tool parameter JSON schemas with lossy summaries like "unknown (required)". JSON schemas in span data are now preserved as-is, keeping full type information for debugging in observability tools like Datadog. Also fixed MODEL_STEP span input showing only a keys summary instead of actual messages for AI SDK v5 providers. (#15404)

  • Fixed CloudExporter to default to observability.mastra.ai for Mastra platform exports. (#15418)

  • Improved tracing overhead when filtering spans. Spans dropped by excludeSpanTypes or the internal-span filter (includeInternalSpans: false) now skip payload serialization and retention entirely instead of paying the cost and discarding at export time. (#15487)

@mastra/otel-bridge@1.0.17
Patch Changes
  • Return undefined from OtelBridge.createSpan when no OpenTelemetry SDK is registered, so core generates valid span/trace IDs instead of reusing the OTEL no-op all-zero IDs. This prevents downstream trace exporters from dropping spans and stops the infinite-loop CPU spike in parent-matching. Fixes #15589. (#15591)
@mastra/pg@1.9.2
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)

  • Added getTraceLight method to the observability storage, returning only lightweight span fields needed for timeline rendering. This avoids transferring heavy fields like input, output, attributes, and metadata when they are not needed. (#15574)

@mastra/playground-ui@23.0.0
Minor Changes
  • Added ErrorBoundary component to catch and display runtime errors in the studio. Wraps routes in the local playground so a crash on one page (e.g. an agent editor referencing an unresolved workspace skill) surfaces a friendly recovery UI with Try again (in-place React reset), Reload page (full browser refresh), and Report issue (opens the Mastra GitHub issues page in a new tab) actions, plus a collapsible stack trace — instead of a blank screen. (#15561)

    The fallback is spatially aware: it fills its parent and the icon, heading, and body text scale up on wider containers via Tailwind container queries. Scope the boundary to a single widget to keep the rest of the UI interactive while one panel fails.

    Usage

    import { ErrorBoundary } from '@mastra/playground-ui';
    import { useLocation } from 'react-router';
    
    // Route-level: wrap the router outlet, reset when the path changes
    function Layout({ children }) {
      const { pathname } = useLocation();
      return <ErrorBoundary resetKeys={[pathname]}>{children}</ErrorBoundary>;
    }
    
    // Scoped: contain the crash to one panel, leave the rest of the tree alone
    <ErrorBoundary variant="inline" title="The editor failed to render">
      <AgentEditor />
    </ErrorBoundary>;

    Props: fallback (node or render prop with { error, errorInfo, reset }), onError for reporting, resetKeys for automatic reset, variant ('section' — fills available space, default; 'inline' — stays compact), and title / description overrides.

  • Added BrandLoader, a branded pulse-wave loader component for brand moments like app boot or agent thinking. Complements Spinner, which remains the inline utility loader. (#15490)

  • Added new Logo component to the playground-ui design system. Supports two sizes (sm, md), uses currentColor for theming, and includes an optional outline-on-hover animation that respects prefers-reduced-motion. (#15513)

Patch Changes
  • Added a dedicated trace details page at /traces/:traceId, plus the design-system changes that support it: (#15392)

    • Button: new link variant (inline, no padding/background/border).
    • DataKeysAndValues: numOfCol now accepts 3.
    • DataPanel.Header: minimum height so heading-only headers match the height of ones with button actions.
  • Fix unhandled TypeError in getFileContentType when the URL is relative (#15433) or malformed. The catch block now falls back to inferring the MIME type from the raw string's file extension and strips query/hash fragments so inputs like /files/report.pdf, https://x.dev/a.pdf?token=1, and /files/report.pdf#page=2 all resolve to application/pdf instead of rejecting.

    Closes #15432.

  • Refactored DataKeysAndValues.ValueLink to use the standard as prop for custom link components, replacing the previous LinkComponent prop (#15391)

  • Added a Foundations/Tokens page to the @mastra/playground-ui Storybook so you can browse all typography, color, spacing, radius, shadow, and animation tokens in one place. (#15475)

  • New filter UX on the studio's Traces and Logs pages. Click + Add Filter to pick a property and narrow by value; active filters render as editable pills. Filter state lives in the URL so filtered views survive reloads and can be shared by link. Save filters for next time remembers a default; Clear and Remove all filters are one click away. (#15512)

  • Align BrandLoader geometry with the Mastra logo: match disk positions to the logo path, introduce per-size stroke widths and bubble radii (sm/md/lg), and rebalance the gooey filter for rounder ridge↔disk fillets. Shift the size scale so sm stays, md is now w-8, lg is now w-10, and the old w-16 size is removed. (#15531)

  • Added ScoresDataList for rendering lists of score evaluation results. (#15339)

  • Updated PageHeader.Description styling to use text color (neutral2) and simplified top margin (#15389)

  • Improved visual consistency across Chip, DropdownMenu, Notification, Popover, and toast components — unified radius and border scale. Deduplicated dropdown menu item classes and added max-height scroll handling for long menus. (#15440)

@mastra/rag@2.2.1
Patch Changes
  • Fixed a security issue where several parsing and tracing paths could slow down on malformed or attacker-crafted input. Normal behavior is unchanged, and these packages now handle pathological input in linear time. (#15566)
@mastra/redis@1.0.1
Patch Changes
  • Add Redis storage provider (#11795)

    Introduces @mastra/redis, a Redis-backed storage implementation for Mastra built on the official redis (node-redis) client.

    Includes support for the core storage domains (memory, workflows, scores) and multiple connection options: connectionString, host/port/db/password, or injecting a pre-configured client for advanced setups (e.g. custom socket/retry settings, Sentinel/Cluster via custom client).

@mastra/schema-compat@1.2.9
Patch Changes
  • Fixed MCP tool validation failures when tools use JSON Schema draft 2020-12. Tools from providers like Firecrawl that declare $schema: "https://json-schema.org/draft/2020-12/schema" now validate correctly instead of throwing "no schema with key or ref" errors. (#14530)

  • Fixed MCP tools with recursive JSON Schema refs so they stay serializable when loaded. (#15400)

@mastra/server@1.26.0
Minor Changes
  • You can now tag spans and redact sensitive input or output per request by passing tags, hideInput, or hideOutput in tracingOptions when calling an agent or workflow. (#15512)

    Added a lightweight trace endpoint (GET /observability/traces/:traceId/light) that returns only timeline-relevant span fields, dramatically reducing payload size when rendering trace timelines. Also added a dedicated span endpoint (GET /observability/traces/:traceId/spans/:spanId) to fetch full span details on demand.

  • Added forEachIndex to the workflow resume request body schema. The /workflows/:workflowId/resume, /resume-async, and /resume-stream endpoints (including their agent-builder equivalents) now accept an optional zero-based forEachIndex so clients can target a specific iteration of a suspended .foreach() step. (#15563)

    // POST /workflows/:workflowId/resume
    // body
    {
      step: 'approve',
      resumeData: { ok: true },
      forEachIndex: 1, // resume only the second iteration; others stay suspended
    }
Patch Changes
  • Add /api/background-tasks routes (SSE stream, list with filters + pagination, get by ID) and matching MastraClient methods (listBackgroundTasks, getBackgroundTask, streamBackgroundTasks). (#15307)

  • Added support for versions field in agent generate and stream request bodies, enabling per-request sub-agent version overrides that propagate through delegation. (#15373)

  • Fix prototype pollution in setNestedValue (@mastra/core/utils) and generateOpenAPIDocument (@mastra/server). (#15565)

    setNestedValue now rejects dot-path segments named __proto__, constructor, or prototype, preventing attacker-controlled field paths passed to selectFields from polluting Object.prototype. generateOpenAPIDocument builds its paths map with Object.create(null) so a route path of __proto__ cannot poison the prototype chain.

  • Fixed noisy 'Background task manager not available' error log in studio when background tasks are not enabled. The list endpoint now returns an empty list, the get-by-id endpoint returns 404, and the SSE stream endpoint returns an empty stream that closes on disconnect — instead of throwing an HTTP 400 that gets logged as an error. (#15600)

  • Added unique IDs (logId, metricId, scoreId, feedbackId) to all observability signals, generated automatically at emission time for de-duplication across the framework pipeline and cross-system correlation. User-facing APIs (logger.info(), metrics.emit(), addScore(), addFeedback()) are unchanged. (#15242)

    For existing ClickHouse and DuckDB observability signal tables, run npx mastra migrate before initializing the store so the new signal-ID schema is applied.

@mastra/tavily@1.0.0
Major Changes
  • Added the @mastra/tavily integration with first-class Mastra tools for Tavily web search, extract, crawl, and map APIs, and migrated mastracode's web search tools to use it. (#15448)
Patch Changes
@mastra/upstash@1.0.5
Patch Changes
  • Add BackgroundTasksStorage domain implementation so @mastra/core background task execution works with any storage adapter. (#15307)

  • Fixed slow Upstash message saves by using the message index and treating unindexed messages as new, avoiding full database scans. Also adds index-first lookups to updateMessages. Addresses #15386. (#15393)

Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.28.0

Highlights

Modal Cloud Sandbox Provider (@mastra/modal)

New @mastra/modal adds a Modal-backed ModalSandbox for running workspace commands in an isolated cloud environment with pause/resume support—expanding Mastra’s deployment/execution options beyond local sandboxes.

Per-request Workspace Filesystem Resolver (Multi-tenant Routing)

Workspace’s filesystem option now supports a resolver function, enabling per-request filesystem selection/routing from a single Workspace instance—useful for multi-tenant setups and scoped permissions without spinning up multiple Workspaces.

Custom Language Server Registration in LSPConfig

You can now register additional language servers via lsp.servers (and override built-ins by ID), unlocking LSP-based inspection for languages beyond the default set (e.g., PHP, Ruby, Java, Kotlin, Swift, Elixir).

Vector Search “Works Out of the Box” (Indexing + Large File Chunking)

Workspace file indexing now auto-creates vector indices where required (e.g., LibSQL) and splits large files into overlapping chunks instead of skipping them—preventing empty vector stores and making search reliable with autoIndexPaths.

Streaming & Observational Memory Reliability + Better Usage Introspection

Multiple fixes prevent duplicated/replayed messages and tool outputs when observational memory is enabled (including disabling savePerStep in Harness in this mode), and agent.stream() callbacks now preserve provider-specific usage.raw so you can access cache metrics without wrapping streams.

Breaking Changes
  • None noted in this changelog.

Changelog

@mastra/core@1.28.0
Minor Changes
  • The Workspace filesystem option now accepts a resolver function in addition to a static instance. (#13150)

    Before: filesystem: WorkspaceFilesystem (static, same filesystem for every request) After: filesystem: WorkspaceFilesystem | (({ requestContext }) => WorkspaceFilesystem) (static or per-request)

    This enables per-request filesystem routing from a single Workspace — useful for multi-tenant setups, role-based access (e.g. admin vs user directories), and scoped filesystem permissions without creating separate Workspace instances.

  • Added support for custom language server registration with the servers field in LSPConfig. Previously, LSP inspection only worked with built-in server definitions for TypeScript, JavaScript, Python, Go, and Rust. You can now register additional language servers, such as PHP, Ruby, Java, Kotlin, Swift, or Elixir, by providing a CustomLSPServer definition. (#14969)

    Example:

    const workspace = new Workspace({
      lsp: {
        servers: {
          phpactor: {
            id: 'phpactor',
            name: 'Phpactor Language Server',
            languageIds: ['php'],
            extensions: ['.php'],
            markers: ['composer.json'],
            command: 'phpactor language-server',
          },
        },
      },
    });

    Custom servers are merged with built-in servers and can also override them by using the same ID. Closes #14828.

Patch Changes
  • Update provider registry and model documentation with latest models and providers (733bf53)

  • Fixed output processors returning undefined from processOutputStream causing an undefined chunk to be enqueued into the consumer stream. A processor that forgets to return part (or explicitly returns undefined) now drops that chunk, matching existing null behavior, instead of emitting a bogus value to downstream readers. (#15674)

    // Before: returning undefined emitted { value: undefined, done: false } to consumers
    // After:  returning undefined drops the chunk, same as returning null
    const processor = {
      id: 'my-processor',
      processOutputStream: async ({ part }) => {
        if (shouldDrop(part)) return; // implicit undefined — now safely dropped
        return part;
      },
    };
  • Fixed streamed tool results being replayed when observational memory runs mid-stream. (#15701) Fixed observational memory markers being saved as separate empty assistant messages.

  • Fixed false positive provider change detection in observational memory. Message metadata now uses the configured model ID instead of the API response model ID, ensuring consistency with step-start parts and preventing incorrect 'Model changed' activations when the provider returns versioned model names (e.g., gpt-5.4-2026-03-05 vs gpt-5.4). (#15681)

  • Fixed interaction between savePerStep and observational memory that caused message duplication. The saveStepMessages method redundantly re-added response messages to the message list on every step, duplicating them. Additionally, savePerStep is now force-disabled when observational memory is enabled, since OM handles its own per-step persistence and the two features conflict. (#15684)

  • Fixed rotated response message ids not propagating to the active output stream after error processor retries, which could split a single response across two ids on the API-error retry path. (#15702)

    Fixed processor-supplied options to writer.custom being dropped in the agentic execution step, so future options like transient now reach the underlying output writer.

  • Fixed agent.stream() callbacks so that onStepFinish and onFinish now preserve the provider-level usage.raw object on LanguageModelUsage. This lets consumers inspect provider-specific cache metrics (e.g., Anthropic and Bedrock prompt caching) directly from the callback payload without having to wrap the stream. (#15546)

    Closes #15510.

  • Add opt-in checkSkillFileMtime option to detect in-place SKILL.md edits during hot reload. (#15676)

    Previously, only directory mtime was checked for skill staleness, so editing a skill's name (to fix a validation error) or updating its description wouldn't trigger re-discovery until server restart.

    The option is off by default since it doubles stat() calls per skill during staleness checks. Recommended for local development only, not for cloud storage backends where stat() has higher latency.

    const myAgent = new Agent({
      workspace: {
        filesystem: new LocalFilesystem({ basePath: process.cwd() }),
        skills: ['./**/skills'],
        checkSkillFileMtime: true, // Enable for local dev
      },
    });
  • Added filterIncompleteToolCalls option to memory config. When set to false, suspended tool calls remain visible in the agent's prompt context, allowing the agent to see its own pending interactions in thread history. Defaults to true (current behavior). Useful for suspend/resume patterns with providers that support incomplete tool calls (e.g. Anthropic). (#14721)

  • Fixed workspace file indexing so vector search works out of the box. (#15011)

    • Large files that exceeded the embedding model token limit were previously silently skipped, leaving the vector store empty and causing search failures. Large files are now split into overlapping line-based chunks, each indexed separately with correct line-range tracking back to the original file.
    • The vector index is now created automatically before the first upsert. Previously, backends that require an explicit createIndex call (e.g. LibSQL) would leave the table uncreated, causing no such table errors on search. Workspaces with vectorStore + embedder + autoIndexPaths configured now work without any manual setup.
  • Disable savePerStep in Harness to prevent duplicate messages when observational memory is enabled (#15684)

    The savePerStep option in Harness caused message duplication when used alongside observational memory. This change temporarily disables savePerStep in the Harness runtime while we work on a permanent fix.

@mastra/agent-browser@0.2.1
Patch Changes
  • Standardize headless default to true across all browser providers. Each provider now resolves headless once in its constructor and passes it to the thread manager via the base class getter, removing duplicate fallback logic. (#15696)

  • Fixed browser_evaluate so expression scripts now return their computed value instead of undefined (for example, document.querySelectorAll('a').length). (#15689)

@mastra/browser-viewer@0.1.1
Patch Changes
  • Remove unused userDataDir config option from BrowserViewerConfig. (#15696)

  • Standardize headless default to true across all browser providers. Each provider now resolves headless once in its constructor and passes it to the thread manager via the base class getter, removing duplicate fallback logic. (#15696)

@mastra/gcs@0.2.1
Patch Changes
  • Fix toKey() to resolve "." and "./" as the root path (#14824)

    Both GCSFilesystem and S3Filesystem produced invalid object keys when called with path: "." (e.g. prefix/. instead of prefix/). Since the built-in mastra_workspace_list_files tool and Mastra Studio both default to path: ".", workspace directory listings returned empty results when backed by GCS or S3.

    toKey() now normalises "." and "./" to empty string before prepending the prefix, matching the existing behaviour of "/". Dotfiles like .env or .gitignore are unaffected.

@mastra/mcp@1.5.2
Patch Changes
  • Replace uuid with @lukeed/uuid and node:crypto (#15691)
@mastra/mcp-registry-registry@1.0.1
Patch Changes
  • Replace uuid with @lukeed/uuid and node:crypto (#15691)
@mastra/memory@1.17.1
Patch Changes
  • Fixed streamed tool results being replayed when observational memory runs mid-stream. (#15701) Fixed observational memory markers being saved as separate empty assistant messages.
@mastra/modal@0.2.0
Minor Changes
  • Added @mastra/modal — Modal cloud sandbox provider for Mastra workspaces. (#14486)

    Use ModalSandbox to run commands in an isolated Modal environment with pause/resume support:

    import { Workspace } from '@mastra/core/workspace';
    import { ModalSandbox } from '@mastra/modal';
    
    const workspace = new Workspace({
      sandbox: new ModalSandbox({
        tokenId: process.env.MODAL_TOKEN_ID!,
        tokenSecret: process.env.MODAL_TOKEN_SECRET!,
      }),
    });
Patch Changes
@mastra/mongodb@1.7.3
Patch Changes
  • Replace uuid with @lukeed/uuid and node:crypto (#15691)
@mastra/s3@0.4.1
Patch Changes
  • Fix toKey() to resolve "." and "./" as the root path (#14824)

    Both GCSFilesystem and S3Filesystem produced invalid object keys when called with path: "." (e.g. prefix/. instead of prefix/). Since the built-in mastra_workspace_list_files tool and Mastra Studio both default to path: ".", workspace directory listings returned empty results when backed by GCS or S3.

    toKey() now normalises "." and "./" to empty string before prepending the prefix, matching the existing behaviour of "/". Dotfiles like .env or .gitignore are unaffected.

@mastra/s3vectors@1.0.4
Patch Changes
  • Replace uuid with @lukeed/uuid and node:crypto (#15691)
@mastra/server@1.28.0
Patch Changes
  • Fixed non-Zod Standard Schema types (e.g. ArkType) being incorrectly called as lazy getters in resolveLazySchema, which caused Studio UI tool forms to receive validation errors instead of the actual JSON Schema (#15670)

  • Fix: Public origin resolution for AWS ALB deployments (#15666)

    Implement cascading header resolution in getPublicOrigin() to properly handle:

    • X-Forwarded-Host (traditional reverse proxies) → always HTTPS
    • Host header (AWS ALB with Preserve Host Header) → respect X-Forwarded-Proto or default HTTPS
    • request.url (local development) → fallback

    Fixes OAuth callback URLs being resolved to http:// instead of https:// when deployed behind AWS ALB with Preserve Host Header enabled.

@mastra/stagehand@0.2.1
Patch Changes
  • Standardize headless default to true across all browser providers. Each provider now resolves headless once in its constructor and passes it to the thread manager via the base class getter, removing duplicate fallback logic. (#15696)
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.27.0

Highlights

CLI-Driven Browser Automation + Screencasts

@mastra/core and the new @mastra/browser-viewer package add end-to-end browser automation for CLI-based agent workflows. BrowserViewer launches Chrome via Playwright with remote debugging, and a new BrowserCliHandler automatically detects browser CLIs (agent-browser, browser-use, browse) and injects the CDP URL into commands — no manual wiring needed. Browser sessions are thread-isolated with automatic lifecycle management, and live screencasts stream directly to Studio. External CDP endpoints (e.g. browser-use cloud) are also supported: the system detects them, skips injection, and connects for screencast.

S3 “Prefix Mounts” Across Workspace Providers

Workspace packages (@mastra/s3, @mastra/daytona, @mastra/e2b, and @mastra/blaxel) now support mounting an S3 subdirectory via a prefix option, so sandboxes can expose only a folder within a bucket instead of the entire bucket.

More Robust Workflow Resumes for Parallel foreach

Fixes a workflow snapshot/resume bug where parallel foreach iterations could lose their suspendPayload when a sibling iteration resumed—important for HITL/tool-approval flows that rely on preserved per-iteration stream state.

Observational Memory Improvements (Temporal Markers + Correctness Fixes)

Adds opt-in temporal-gap markers (observationalMemory.temporalMarkers: true) to inject persisted <system-reminder type="temporal-gap"> when users return after 10+ minutes, and fixes duplication/conflicts by force-disabling savePerStep when observational memory is enabled.

New Package: @mastra/tavily

New integration package wrapping @tavily/core as first-class Mastra tools — createTavilySearchTool, createTavilyExtractTool, createTavilyCrawlTool, and createTavilyMapTool — with full Zod input/output schemas, lazy client initialization, and a convenience createTavilyTools() that returns all four with shared config. API key resolves from config or TAVILY_API_KEY env var.

Breaking Changes
  • None noted in this changelog.

Changelog

@mastra/core@1.27.0
Minor Changes
  • Added support for CLI-driven browser automation with screencast support in @mastra/core, including automatic CDP injection for browser CLIs. (#15415)

    Fixed local process spawning so workspace-relative cwd values no longer get duplicated.

Patch Changes
  • Update provider registry and model documentation with latest models and providers (f112db1)

  • Fixed foreach parallel iterations losing their suspendPayload when a sibling iteration was resumed. Previously, every result entry written back to the workflow snapshot had its suspendPayload cleared, so iterations that were still suspended (e.g. parallel tool-call approvals each carrying an agent's __streamState) lost the context they needed to resume correctly. Suspended iterations now retain their suspendPayload across resume cycles; completed iterations still have it cleared to keep snapshots small. (#15551)

    const approvalWorkflow = createWorkflow({ id: "approve" }).foreach(approveToolStep, { concurrency: 5 }).commit();
    
    // Before: resuming the first approval wiped streamState on the others,
    //         so subsequent resumes lost conversation context.
    // After:  each suspended iteration keeps its suspendPayload (including
    //         streamState) until it is individually resumed.
  • Fixed interaction between savePerStep and observational memory that caused message duplication. The saveStepMessages method redundantly re-added response messages to the message list on every step, duplicating them. Additionally, savePerStep is now force-disabled when observational memory is enabled, since OM handles its own per-step persistence and the two features conflict. (#15652)

  • Added opt-in temporal-gap markers for observational memory. When enabled via observationalMemory.temporalMarkers: true, the agent receives a <system-reminder type="temporal-gap"> before any user message that arrives more than 10 minutes after the previous one, so it can anchor responses in real elapsed time. Markers are persisted, surfaced to the observer, and rendered by the MastraCode TUI on reload. (#15605)

@mastra/agent-builder@1.0.28
Patch Changes
  • Hide internal log during mastra dev startup (that was previously already hidden but got exposed again by a recent change) (#15616)
@mastra/blaxel@0.3.0
Minor Changes
  • Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the prefix option on your S3 filesystem. (#15171)

    Example:

    const fs = new S3Filesystem({
      bucket: "my-bucket",
      region: "us-east-1",
      prefix: "workspace/data",
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
    });

    When mounted in a sandbox, only the contents under workspace/data/ in the bucket will be visible at the mount path. This uses the s3fs bucket:/path syntax under the hood.

    Closes #15147.

Patch Changes
@mastra/browser-viewer@0.1.0
Minor Changes
  • Initial release of @mastra/browser-viewer (#15415)

    Playwright-based browser viewer for CLI providers that enables screencast visualization in Studio. Supports thread-isolated browser sessions and automatic CDP connection management.

    import { BrowserViewer } from "@mastra/browser-viewer";
    
    const workspace = new Workspace({
      sandbox: new LocalSandbox({ cwd: "./workspace" }),
      browser: new BrowserViewer({
        cli: "agent-browser",
        headless: false
      })
    });
Patch Changes
@mastra/clickhouse@1.5.1
Patch Changes
  • Fixed mastra dev repeatedly reporting MIGRATION REQUIRED on ClickHouse Cloud after mastra migrate had already run successfully. The observability migration check now recognizes the engine-name variants that ClickHouse Cloud and replicated clusters use in place of ReplacingMergeTree. (#15623)

  • Improved ClickHouse v-next observability initialization errors to include the underlying ClickHouse message in the standard error text. This makes init failures actionable in loggers that only print error.message. (#15588)

@mastra/daytona@0.3.0
Minor Changes
  • Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the prefix option on your S3 filesystem. (#15171)

    Example:

    const fs = new S3Filesystem({
      bucket: "my-bucket",
      region: "us-east-1",
      prefix: "workspace/data",
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
    });

    When mounted in a sandbox, only the contents under workspace/data/ in the bucket will be visible at the mount path. This uses the s3fs bucket:/path syntax under the hood.

    Closes #15147.

Patch Changes
@mastra/e2b@0.2.0
Minor Changes
  • Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the prefix option on your S3 filesystem. (#15171)

    Example:

    const fs = new S3Filesystem({
      bucket: "my-bucket",
      region: "us-east-1",
      prefix: "workspace/data",
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
    });

    When mounted in a sandbox, only the contents under workspace/data/ in the bucket will be visible at the mount path. This uses the s3fs bucket:/path syntax under the hood.

    Closes #15147.

Patch Changes
@mastra/fastify@1.3.11
Patch Changes
  • Fix custom route handlers on the Fastify adapter silently overwriting request-body fields named tools (e.g. POST /stored/agents, POST /stored/workspaces). The adapter now exposes registered tools as registeredTools in handler params, matching the Express and Hono adapters and the @mastra/server handler contract. (#15635)
@mastra/memory@1.17.0
Minor Changes
  • Added opt-in temporal-gap markers for observational memory. When enabled via observationalMemory.temporalMarkers: true, the agent receives a <system-reminder type="temporal-gap"> before any user message that arrives more than 10 minutes after the previous one, so it can anchor responses in real elapsed time. Markers are persisted, surfaced to the observer, and rendered by the MastraCode TUI on reload. (#15605)
Patch Changes
  • Fixed observer agent truncation that could cut UTF-16 surrogate pairs in half when formatting messages, tool results, or observation lines with emoji or other astral-plane characters. This produced lone surrogates that strict JSON parsers (including Anthropic's) reject with errors like no low surrogate in string, causing observer runs to fail. (#15634)
@mastra/s3@0.4.0
Minor Changes
  • Added S3 prefix (subdirectory) mount support. You can now mount a specific folder within an S3 bucket instead of the entire bucket by setting the prefix option on your S3 filesystem. (#15171)

    Example:

    const fs = new S3Filesystem({
      bucket: "my-bucket",
      region: "us-east-1",
      prefix: "workspace/data",
      accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
      secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
    });

    When mounted in a sandbox, only the contents under workspace/data/ in the bucket will be visible at the mount path. This uses the s3fs bucket:/path syntax under the hood.

    Closes #15147.

Patch Changes
@mastra/server@1.27.0
Patch Changes
  • Forward requestContext from the /approve-tool-call, /decline-tool-call, /approve-tool-call-generate and /decline-tool-call-generate REST handlers to agent.approveToolCall(...) / declineToolCall(...) / approveToolCallGenerate(...) / declineToolCallGenerate(...). (#15620)

    Previously requestContext was destructured from the handler arguments but never passed through. On resume, dynamicInstructions ran with requestContext: undefined, so any value placed on the per-request RequestContext by upstream middleware (or by body.requestContext auto-merge) was lost for the rest of the turn. Agents whose prompt assembly depends on request-scoped data (e.g. read-only state from the frontend) produced blank or placeholder responses after the user approved a HITL tool call. Other agent entry points (stream, generate) already forwarded requestContext correctly; this brings the approval routes in line.

  • Fixed screencast panel staying "Live" after browser closes due to an error. The ViewerRegistry now broadcasts browser_closed status when a screencast stream emits an error, not just when it stops cleanly. (#15415)

@mastra/tavily@1.0.1
Patch Changes
  • Fixed runtime ERR_MODULE_NOT_FOUND for @tavily/core by making it a direct dependency. Consumers no longer need to install @tavily/core manually. (#15628)
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.24.0

Highlights

End-to-end RAG Tracing + New RAG/Graph Span Types

RAG ingestion and query operations are now visible in Mastra tracing with new span types (e.g., RAG_INGESTION, RAG_EMBEDDING, RAG_VECTOR_OPERATION, RAG_ACTION, GRAPH_ACTION) plus helpers like startRagIngestion() / withRagIngestion(). Instrumentation is opt-in via an observabilityContext, and @mastra/rag automatically threads context from agent TOOL_CALL spans into vector and graph tools.

CloudExporter Now Ships All Observability Signals (Not Just Traces)

@mastra/observability CloudExporter can now batch and upload logs, metrics, scores, and feedback in addition to tracing spans, enabling a single exporter path to Mastra Cloud for all signals. This also changes the endpoint configuration to use a base collector URL and derive publish paths automatically.

Span Filtering to Reduce Observability Noise and Cost

excludeSpanTypes and spanFilter were added to ObservabilityInstanceConfig in both @mastra/core and @mastra/observability, allowing you to drop entire span categories (e.g., MODEL_CHUNK) or apply predicate-based filtering before export—useful for pay-per-span backends.

AI SDK v6 Message Support + Message Interop Helpers

@mastra/core MessageList can now accept AI SDK v6 UI/model messages and project stored messages via messageList.get.all.aiV6.ui(), supporting v6 approval request/response flows. @mastra/ai-sdk adds toAISdkMessages() to load stored Mastra messages into AI SDK v5 or v6 chat UIs.

Better Reliability and Debuggability Across Logs + MCP + Memory

Observability log correlation is fixed so logs inside agent runs carry the active span correlation fields (restoring trace↔log linking), and deepClean() now applies to all signals and better preserves Map/Set/Error detail. MCP tool discovery now retries after reconnectable errors and the MCP server returns spec-correct 404s for stale sessions; memory recall gains more precise browsing (partType, toolName, threadId: "current", anchor paging), and message parts now include createdAt timestamps for accurate part-level timing.

Breaking Changes
  • CloudExporter endpoint format changed: configure a base endpoint URL (publisher paths are derived automatically) when using CloudExporter for Mastra Cloud uploads.

Changelog

@mastra/core@1.24.0
Minor Changes
  • Added excludeSpanTypes and spanFilter options to ObservabilityInstanceConfig for selectively filtering spans before export. Use excludeSpanTypes to drop entire categories of spans by type (e.g., MODEL_CHUNK, MODEL_STEP) or spanFilter for fine-grained predicate-based filtering by attributes, metadata, entity, or any combination. Both options help reduce noise and costs in observability platforms that charge per-span. (#15131)

    excludeSpanTypes example:

    excludeSpanTypes: [SpanType.MODEL_CHUNK, SpanType.MODEL_STEP, SpanType.WORKFLOW_SLEEP];

    spanFilter example:

    spanFilter: span => {
      if (span.type === SpanType.MODEL_CHUNK) return false;
      if (span.type === SpanType.TOOL_CALL && span.attributes?.success) return false;
      return true;
    };

    Resolves https://github.com/mastra-ai/mastra/issues/12710

  • Add RAG observability (#10898) (#15137)

    Surfaces RAG ingestion and query operations in Mastra's AI tracing.

    New span types in @mastra/core/observability:

    • RAG_INGESTION (root) — wraps an ingestion pipeline run
    • RAG_EMBEDDING — embedding call (used by ingestion and query)
    • RAG_VECTOR_OPERATION — vector store I/O (query/upsert/delete/fetch)
    • RAG_ACTIONchunk / extract_metadata / rerank
    • GRAPH_ACTION — non-RAG graph build / traverse / update / prune

    New helpers exported from @mastra/core/observability:

    • startRagIngestion(opts) — manual: returns { span, observabilityContext }
    • withRagIngestion(opts, fn) — scoped: runs fn(observabilityContext), attaches the return value as the span's output, routes thrown errors to span.error(...)

    Wired in @mastra/rag:

    • vectorQuerySearch emits RAG_EMBEDDING (mode: query) and RAG_VECTOR_OPERATION (operation: query)
    • rerank / rerankWithScorer emit RAG_ACTION (action: rerank)
    • MDocument.chunk emits RAG_ACTION (action: chunk) and RAG_ACTION (action: extract_metadata)
    • createGraphRAGTool emits GRAPH_ACTION (action: build / traverse)
    • createVectorQueryTool and createGraphRAGTool thread observabilityContext from the agent's TOOL_CALL span automatically

    All new instrumentation is opt-in: functions accept an optional observabilityContext and no-op when absent, so existing callers are unaffected.

Patch Changes
  • Update provider registry and model documentation with latest models and providers (8db7663)

  • Added AI SDK v6 UI message support to MessageList in @mastra/core. (#14592)

    MessageList can now accept AI SDK v6 UI and model messages in add(...), and project stored messages with messageList.get.all.aiV6.ui(). This adds first-class handling for v6 approval request and response message flows.

  • Fix observability log correlation: logs emitted from inside an agent run were being persisted with entityId, runId, traceId, and the other correlation fields set to null, breaking trace ↔ log linking in Mastra Studio and downstream observability tools. Logs now correctly carry the active span's correlation context end to end. (#15148)

  • Added createdAt timestamps to message parts in message history. (#15121)

    Message parts now keep their own creation timestamps so downstream code can preserve part-level timing instead of relying only on the parent message timestamp.

    After:

    { type: 'text', text: 'hello', createdAt: 1712534400000 }
@mastra/ai-sdk@1.3.3
Patch Changes
  • Added toAISdkMessages() for loading stored Mastra messages into AI SDK v5 or v6 chat UIs. (#14592)

    Use the default v5 behavior or pass { version: 'v6' } when your app is typed against AI SDK v6 useChat() message types.

@mastra/client-js@1.13.2
Patch Changes
  • Fixed the client JS Responses types to allow omitting the model override. (#15140)
@mastra/datadog@1.0.15
Patch Changes
  • Fix Datadog LLM Observability span kinds for model spans so traces match Datadog's expected shape. (#15149)
    • Each call to a model now shows up as an llm span in Datadog (previously the per-call spans were reported as task, so Datadog's "Model Calls" count was wrong and per-call inputs/outputs were not rendered as messages).
    • The wrapper around a generation is now reported as a workflow span instead of llm, so it no longer looks like an extra LLM call.
    • Token usage and cost are reported only on the per-call llm spans, so Datadog no longer double-counts tokens against the wrapper.
    • Per-call llm spans inherit modelName and modelProvider from their parent generation, so the model is still attached in the Datadog UI.
@mastra/loggers@1.1.1
Patch Changes
  • Removed 'component' field from pino-pretty log output to reduce noise in CLI logs (#15146)
@mastra/mcp@1.4.2
Patch Changes
  • Improved MCP tool discovery to retry once after reconnectable connection errors like Connection closed during tools/list. (#15141)

    MCPClient.listToolsets(), listToolsetsWithErrors(), and listTools() now attempt a reconnect before treating transient discovery failures as missing tools.

  • Fixed MCP server to return HTTP 404 (instead of 400) when a client sends a stale or unknown session ID. Per the MCP spec, this tells clients to re-initialize with a new session, which fixes broken tool calls after server redeploys. (#15160)

@mastra/memory@1.15.0
Minor Changes
  • Updated the recall tool to support more precise message browsing for agents. (#15116)

    Agents using recall can now pass partType and toolName to narrow message results to specific parts, such as tool calls or tool results for one tool. This change also adds threadId: "current" support across recall modes and anchor: "start" | "end" for no-cursor message paging, making it easier to inspect recent thread activity and past tool usage.

Patch Changes
  • Fixed reflection threshold not respecting per-record overrides set via the PATCH API. Previously, lowering the reflection threshold for a specific record had no effect on the actual reflection trigger — only the default 40k threshold was used. Now per-record overrides are correctly applied in both sync and async reflection paths. (#15170)

  • Improved observational memory formatting to use part timestamps when rendering dates and times. (#15121)

    Observer history now follows part-level timing more closely, so the rendered memory context is more accurate when messages contain parts created at different times.

  • Fixed message history doubling when using Observational Memory with the Mastra gateway. The local ObservationalMemoryProcessor now detects when the agent's model is routed through the Mastra gateway and skips its input/output processing, since the gateway handles OM server-side. (#15161)

@mastra/observability@1.8.0
Minor Changes
  • Added CloudExporter support for Mastra Observability logs, metrics, scores, and feedback. (#15124)

    CloudExporter now batches and uploads all Mastra Observability signals to Mastra Cloud, not just tracing spans.

    This includes a breaking change to the CloudExporter endpoint format. We now pass a base endpoint URL and let let the exporter derive the standard publish paths automatically.

    import { CloudExporter, Observability } from '@mastra/observability';
    
    const observability = new Observability({
      configs: {
        default: {
          serviceName: 'my-app',
          exporters: [
            new CloudExporter({
              endpoint: 'https://collector.example.com',
            }),
          ],
        },
      },
    });
    
    // Traces, logs, metrics, scores, and feedback now all publish through CloudExporter.

    After updating the exporter endpoint config, the exporter will continue to work for traces, and the same exporter will now also publish structured logs, auto-extracted metrics, scores, and feedback records.

  • Added excludeSpanTypes and spanFilter options to ObservabilityInstanceConfig for selectively filtering spans before export. Use excludeSpanTypes to drop entire categories of spans by type (e.g., MODEL_CHUNK, MODEL_STEP) or spanFilter for fine-grained predicate-based filtering by attributes, metadata, entity, or any combination. Both options help reduce noise and costs in observability platforms that charge per-span. (#15131)

    excludeSpanTypes example:

    excludeSpanTypes: [SpanType.MODEL_CHUNK, SpanType.MODEL_STEP, SpanType.WORKFLOW_SLEEP];

    spanFilter example:

    spanFilter: span => {
      if (span.type === SpanType.MODEL_CHUNK) return false;
      if (span.type === SpanType.TOOL_CALL && span.attributes?.success) return false;
      return true;
    };

    Resolves https://github.com/mastra-ai/mastra/issues/12710

Patch Changes
  • ObservabilityBus now honors per-instance serializationOptions (maxStringLength, maxDepth, maxArrayLength, maxObjectKeys) when deep-cleaning log/metric/score/feedback payloads, matching the behavior of tracing spans. Previously these signals always used the built-in defaults regardless of user configuration. (#15138)

  • Apply deepClean() to all observability signals (logs, metrics, scores, feedback) before fanning out to exporters and bridges. Previously only tracing spans were deep-cleaned at construction time, leaving free-form payload fields on other signals (e.g. log.data, log.metadata, metric.metadata, metric.costContext.costMetadata, score.metadata, feedback.metadata) susceptible to circular references, oversized strings, and other non-serializable values. Sanitization now happens centrally in ObservabilityBus.emit() so every signal leaving the bus is bounded and JSON-safe. (#15135)

  • deepClean() now preserves data for Map, Set, and richer Error objects. Previously Maps and Sets were serialized as empty {} (entries silently dropped) and Errors only kept name/message. Maps are now converted to plain objects of entries, Sets to arrays (both respecting maxObjectKeys/maxArrayLength and cycle detection), and Errors additionally preserve stack and recursively cleaned cause. (#15136)

@mastra/playground-ui@22.1.0
Minor Changes
  • Search input can now be collapsed into a compact icon button with tooltip and auto-focuses when expanded (#15130)

  • Added DataKeysAndValues component — a compound component for displaying key-value pairs in a grid layout with support for single or two-column modes and section headers (#15126)

  • Added DateTimeRangePicker component — a date range selector with preset options (last 24h, 7d, 30d, etc.) and a custom range mode with dual calendar and time pickers (#15128)

  • Added DataCodeSection component — a read-only code viewer with JSON syntax highlighting, search, multiline toggle, and an expandable fullscreen dialog (#15125)

  • Added DataPanel compound component — a container for detail panels with header, navigation, close button, loading, and empty states (#15127)

  • New inline Traces page replacing the old dialog-based Observability page. Trace, span, and score details now open in stacked side panels instead of full-screen dialogs. URL deep-linking supports traceId, spanId, tab, and scoreId params. Includes new TracesDataList, DataList.Pagination, DataList.Subheader components, and Evaluate Trace / Save as Dataset Item actions. (#15139)

Patch Changes
  • Fixed publishing older agent versions (#15154)

    Fixed agent editor to allow publishing older read-only versions. Previously, the Publish button was disabled when viewing a previous version. Now a "Publish This Version" button appears, enabling users to set any older version as the published version.

    Fixed Publish button being clickable without a saved draft

    The Publish button is now disabled until a draft version is saved. Previously, making edits would enable the Publish button even without a saved draft, which caused an error when clicked.

    Eliminated spurious 404 error logs for code-only agents

    The agent versions endpoint now checks both code-registered and stored agents before returning 404, and the frontend conditionally fetches stored agent details only when versions exist. This prevents noisy error logs when navigating to the editor for agents that haven't been published yet.

    Changed editor sections to be collapsed by default

    The System Prompt, Tools, and Variables sections in the agent editor are now collapsed by default when navigating to the editor page.

@mastra/rag@2.2.0
Minor Changes
  • Add RAG observability (#10898) (#15137)

    Surfaces RAG ingestion and query operations in Mastra's AI tracing.

    New span types in @mastra/core/observability:

    • RAG_INGESTION (root) — wraps an ingestion pipeline run
    • RAG_EMBEDDING — embedding call (used by ingestion and query)
    • RAG_VECTOR_OPERATION — vector store I/O (query/upsert/delete/fetch)
    • RAG_ACTIONchunk / extract_metadata / rerank
    • GRAPH_ACTION — non-RAG graph build / traverse / update / prune

    New helpers exported from @mastra/core/observability:

    • startRagIngestion(opts) — manual: returns { span, observabilityContext }
    • withRagIngestion(opts, fn) — scoped: runs fn(observabilityContext), attaches the return value as the span's output, routes thrown errors to span.error(...)

    Wired in @mastra/rag:

    • vectorQuerySearch emits RAG_EMBEDDING (mode: query) and RAG_VECTOR_OPERATION (operation: query)
    • rerank / rerankWithScorer emit RAG_ACTION (action: rerank)
    • MDocument.chunk emits RAG_ACTION (action: chunk) and RAG_ACTION (action: extract_metadata)
    • createGraphRAGTool emits GRAPH_ACTION (action: build / traverse)
    • createVectorQueryTool and createGraphRAGTool thread observabilityContext from the agent's TOOL_CALL span automatically

    All new instrumentation is opt-in: functions accept an optional observabilityContext and no-op when absent, so existing callers are unaffected.

Patch Changes
@mastra/server@1.24.0
Patch Changes
  • Fixed publishing older agent versions (#15154)

    Fixed agent editor to allow publishing older read-only versions. Previously, the Publish button was disabled when viewing a previous version. Now a "Publish This Version" button appears, enabling users to set any older version as the published version.

    Fixed Publish button being clickable without a saved draft

    The Publish button is now disabled until a draft version is saved. Previously, making edits would enable the Publish button even without a saved draft, which caused an error when clicked.

    Eliminated spurious 404 error logs for code-only agents

    The agent versions endpoint now checks both code-registered and stored agents before returning 404, and the frontend conditionally fetches stored agent details only when versions exist. This prevents noisy error logs when navigating to the editor for agents that haven't been published yet.

    Changed editor sections to be collapsed by default

    The System Prompt, Tools, and Variables sections in the agent editor are now collapsed by default when navigating to the editor page.

  • Fixed the Responses API to use the agent default model when create requests omit model. (#15140)

Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.16.0

Highlights

Smarter Model Selection for Observational Memory

@mastra/memory now lets you route observer and reflector calls to different models based on input size using ModelByInputTokens. Short inputs can go to a fast, cheap model while longer ones get sent to a more capable one -- all configured declaratively with token thresholds. Tracing shows which model was selected and why.

MongoDB Support for Datasets and Experiments

@mastra/mongodb now stores versioned datasets with full item history and time-travel queries, plus experiment results and CRUD. If you're already using MongoDBStore, this works automatically with no extra setup.

Okta Auth and RBAC

New @mastra/auth-okta package brings SSO authentication and role-based access control via Okta. Map Okta groups to Mastra permissions, verify JWTs against Okta's JWKS endpoint, and manage sessions -- or pair Okta RBAC with a different auth provider like Auth0 or Clerk.

Breaking Changes
  • None called out in this changelog.

Changelog

@mastra/core@1.16.0
Minor Changes
  • Added dataset-agent association and experiment status tracking for the Evaluate workflow. (#14470)

    • Dataset targeting: Added targetType and targetIds fields to datasets, enabling association with agents, scorers, or workflows. Datasets can now be linked to multiple entities.
    • Experiment status: Added status field to experiment results ('needs-review', 'reviewed', 'complete') for review queue workflow.
    • Dataset experiment routes: Added API endpoints for triggering experiments from a dataset with configurable target type and target ID.
    • LLM data generation: Added endpoint for generating dataset items using an LLM with configurable count and prompt.
    • Failure analysis: Added endpoint for clustering experiment failures and proposing tags using LLM analysis.
  • Added agent version support for experiments. When triggering an experiment, you can now pass an agentVersion parameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)

    const client = new MastraClient();
    
    await client.triggerDatasetExperiment({
      datasetId: "my-dataset",
      targetType: "agent",
      targetId: "my-agent",
      version: 3, // pin to dataset version 3
      agentVersion: "ver_abc123" // pin to a specific agent version
    });
  • Added tool suspension handling to the Harness. (#14611)

    When a tool calls suspend() during execution, the harness now emits a tool_suspended event, reports agent_end with reason 'suspended', and exposes respondToToolSuspension() to resume execution with user-provided data.

    harness.subscribe((event) => {
      if (event.type === "tool_suspended") {
        // event.toolName, event.suspendPayload, event.resumeSchema
      }
    });
    
    // Resume after collecting user input
    await harness.respondToToolSuspension({ resumeData: { confirmed: true } });
  • Added agentId to the agent tool execution context. Tools executed by an agent can now access context.agent.agentId to identify which agent is calling them. This enables tools to look up agent metadata, share workspace configuration with sub-agents, or customize behavior per agent. (#14502)

  • Improved observability metrics and logs storage support. (#14607)

    • Added typed observability storage fields for shared correlation context and cost data.
    • Added storage-layer metric listing and richer metric aggregations that can return estimated cost alongside values.
    • Improved observability filter parity across log and metric storage APIs.
  • Add optional ?path= query param to workspace skill routes for disambiguating same-named skills. (#14430)

    Skill routes continue to use :skillName in the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional ?path= query parameter to select the exact skill:

    GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelines

    SkillMetadata now includes a path field, and the list() method returns all same-named skills for disambiguation. The client SDK's getSkill() accepts an optional skillPath parameter for disambiguation.

  • Added ModelByInputTokens in @mastra/memory for token-threshold-based model selection in Observational Memory. (#14614)

    When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.

    Example usage:

    import { Memory, ModelByInputTokens } from "@mastra/memory";
    
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: new ModelByInputTokens({
            upTo: {
              10_000: "google/gemini-2.5-flash",
              40_000: "openai/gpt-4o",
              1_000_000: "openai/gpt-4.5"
            }
          })
        }
      }
    });

    The upTo keys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.

    Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.

Patch Changes
  • Update provider registry and model documentation with latest models and providers (68ed4e9)

  • Fixed Harness.destroy() to properly clean up heartbeats and workspace on teardown. (#14568)

  • Fixed null detection in tool input validation to check actual values at failing paths instead of relying on error message string matching. This ensures null values from LLMs are correctly handled even when validators produce error messages that don't contain the word "null" (e.g., "must be string"). Fixes #14476. (#14496)

  • Fixed missing tool lists in agent traces for streaming runs. Exporters like Datadog LLM Observability now receive the tools available to the agent. (#14550)

  • Fix consecutive tool-only loop iterations being merged into a single assistant message block. When the agentic loop runs multiple iterations that each produce only tool calls, the LLM would misinterpret them as parallel calls from a single turn. A step-start boundary is now inserted between iterations to ensure they are treated as sequential steps. (#14652)

  • Improved custom OpenAI-compatible model configuration guidance in the models docs. (#14594)

  • Added client/server body schemas for feedback and scores that omit the timestamp field, allowing it to be set server-side (#14470)

  • Workspace skills now surface all same-named skills for disambiguation. (#14430)

    When multiple skills share the same name (e.g., a local brand-guidelines skill and one from node_modules), list() now returns all of them instead of only the tie-break winner. This lets agents and UIs see every available skill, along with its path and source type.

    Tie-breaking behavior:

    • get(name) still returns a single skill using source-type priority: local > managed > external
    • If two skills share the same name and source type, get(name) throws an error — rename one or move it to a different source type
    • get(path) bypasses tie-breaking entirely and returns the exact skill

    Agents and UIs now receive all same-named skills with their paths, which improves disambiguation in prompts and tool calls.

    const skills = await workspace.skills.list();
    // Returns both local and external "brand-guidelines" skills
    
    const exact = await workspace.skills.get("node_modules/@myorg/skills/brand-guidelines");
    // Fetches the external copy directly by path
  • Fixed Anthropic 'tool_use ids were found without tool_result blocks immediately after' error. When client tools (e.g. execute_command) and provider tools (e.g. web_search) are called in parallel, the tool ordering in message history could cause Anthropic to reject subsequent requests, making the thread unrecoverable. Tool blocks are now correctly split to satisfy Anthropic's ordering requirements. (#14648)

  • Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)

    Mastra agent and client APIs accept schemas from either zod/v3 or zod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.

@mastra/ai-sdk@1.2.1
Patch Changes
  • Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)

    Mastra agent and client APIs accept schemas from either zod/v3 or zod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.

@mastra/auth-okta@0.0.2
Patch Changes
  • fix(auth-okta): harden security defaults and address code review feedback (#14553)
    • Fix cache poisoning: errors in fetchGroupsFromOkta now propagate so the outer .catch evicts the entry and retries on next request
    • Reduce cookie size: only store user claims, id_token (for logout), and expiry — access/refresh tokens are no longer stored, keeping cookies under the 4KB browser limit
    • Add id_token_hint to logout URL (required by Okta)
    • Add console.warn for auto-generated cookie password and in-memory state store in production
    • Document missing env vars (OKTA_CLIENT_SECRET, OKTA_REDIRECT_URI, OKTA_COOKIE_PASSWORD) in README and examples
    • Expand MastraAuthOktaOptions docs to include all fields (session config, scopes, etc.)
    • Fix test to actually exercise getUserId cross-provider lookup path
@mastra/client-js@1.10.0
Minor Changes
  • Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14470)
Patch Changes
  • Added client SDK methods for dataset experiments and item generation. (#14470)

    • Added triggerExperiment() method to dataset resources for running experiments with configurable target type and ID
    • Added generateItems() method for LLM-powered test data generation
    • Added clusterFailures() method for analyzing experiment failures
    • Added TypeScript types for new dataset and experiment API payloads
  • Added agent version support for experiments. When triggering an experiment, you can now pass an agentVersion parameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)

    const client = new MastraClient();
    
    await client.triggerDatasetExperiment({
      datasetId: "my-dataset",
      targetType: "agent",
      targetId: "my-agent",
      version: 3, // pin to dataset version 3
      agentVersion: "ver_abc123" // pin to a specific agent version
    });
  • Add optional ?path= query param to workspace skill routes for disambiguating same-named skills. (#14430)

    Skill routes continue to use :skillName in the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional ?path= query parameter to select the exact skill:

    GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelines

    SkillMetadata now includes a path field, and the list() method returns all same-named skills for disambiguation. The client SDK's getSkill() accepts an optional skillPath parameter for disambiguation.

  • Updated skill search result types and query parameters to use skillName/skillNames instead of skillPath/skillPaths for consistency with the name-based public API. (#14430)

  • Added storage type detection to the Metrics Dashboard. The /system/packages endpoint now returns observabilityStorageType, identifying the observability storage backend. The dashboard shows an empty state when the storage does not support metrics (e.g. PostgreSQL, LibSQL), and displays a warning when using in-memory storage since metrics are not persisted across server restarts. Also added a docs link button to the Metrics page header. (#14620)

    import { MastraClient } from "@mastra/client-js";
    
    const client = new MastraClient();
    const system = await client.getSystemPackages();
    
    // system.observabilityStorageType contains the class name of the observability store:
    // - 'ObservabilityInMemory' → metrics work but are not persisted across restarts
    // - 'ObservabilityPG', 'ObservabilityLibSQL', etc. → metrics not supported
    
    if (system.observabilityStorageType === "ObservabilityInMemory") {
      console.warn("Metrics are not persisted — data will be lost on server restart.");
    }
    
    const SUPPORTED = new Set(["ObservabilityInMemory"]);
    if (!SUPPORTED.has(system.observabilityStorageType ?? "")) {
      console.error("Metrics require in-memory observability storage.");
    }
  • Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)

    Mastra agent and client APIs accept schemas from either zod/v3 or zod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.

@mastra/datadog@1.0.9
Patch Changes
  • Fixed error info tags being recorded as [object Object] in Datadog. Error details (message, id, domain, category) are now stored as separate flattened tags (error.message, error.id, error.domain, error.category) instead of a nested object, making error information properly visible in Datadog LLM Observability. (#14570)
@mastra/deployer@1.16.0
Patch Changes
  • Inject MASTRA_EXPERIMENTAL_UI environment variable into the studio HTML shell during build and deploy. (#14547)
@mastra/deployer-vercel@1.1.8
Patch Changes
  • Inject MASTRA_EXPERIMENTAL_UI environment variable into the studio HTML shell during build and deploy. (#14547)
@mastra/express@1.3.0
Minor Changes
  • Added adapter auth middleware helpers for raw framework routes. (#14458)

    Use createAuthMiddleware({ mastra }) when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. Set requiresAuth: false when you need to reuse the same helper chain on a public route.

    app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/fastify@1.3.0
Minor Changes
  • Added adapter auth middleware helpers for raw framework routes. (#14458)

    Use createAuthMiddleware({ mastra }) when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. Set requiresAuth: false when you need to reuse the same helper chain on a public route.

    app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/hono@1.3.0
Minor Changes
  • Added adapter auth middleware helpers for raw framework routes. (#14458)

    Use createAuthMiddleware({ mastra }) when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. Set requiresAuth: false when you need to reuse the same helper chain on a public route.

    app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/koa@1.4.0
Minor Changes
  • Added adapter auth middleware helpers for raw framework routes. (#14458)

    Use createAuthMiddleware({ mastra }) when you mount routes directly on a Hono, Express, Fastify, or Koa app and still want Mastra auth to run. Set requiresAuth: false when you need to reuse the same helper chain on a public route.

    app.get("/custom/protected", createAuthMiddleware({ mastra }), handler);
Patch Changes
@mastra/libsql@1.7.2
Patch Changes
  • Added storage support for dataset targeting and experiment status fields. (#14470)

    • Added targetType (text) and targetIds (jsonb) columns to datasets table for entity association
    • Added tags (jsonb) column to datasets table for tag vocabulary
    • Added status column to experiment results for review workflow tracking
    • Added migration logic to add new columns to existing tables
  • Added agent version support for experiments. When triggering an experiment, you can now pass an agentVersion parameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)

    const client = new MastraClient();
    
    await client.triggerDatasetExperiment({
      datasetId: "my-dataset",
      targetType: "agent",
      targetId: "my-agent",
      version: 3, // pin to dataset version 3
      agentVersion: "ver_abc123" // pin to a specific agent version
    });
@mastra/memory@1.10.0
Minor Changes
  • Added ModelByInputTokens in @mastra/memory for token-threshold-based model selection in Observational Memory. (#14614)

    When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.

    Example usage:

    import { Memory, ModelByInputTokens } from "@mastra/memory";
    
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: new ModelByInputTokens({
            upTo: {
              10_000: "google/gemini-2.5-flash",
              40_000: "openai/gpt-4o",
              1_000_000: "openai/gpt-4.5"
            }
          })
        }
      }
    });

    The upTo keys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.

    Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.

Patch Changes
  • Fixed observational memory reflection compression for google/gemini-2.5-flash by using stronger compression guidance and starting it at a higher compression level during reflection. google/gemini-2.5-flash is unusually good at generating long, faithful outputs. That made reflection retries more likely to preserve too much detail and miss the compression target, wasting tokens in the process. (#14612)
@mastra/mongodb@1.6.0
Minor Changes
  • Added datasets and experiments storage support to the MongoDB store. (#14556)

    Datasets — Full dataset management with versioned items. Create, update, and delete datasets and their items with automatic version tracking. Supports batch insert/delete operations, time-travel queries to retrieve items at any past version, and item history tracking.

    Experiments — Run and track experiments against datasets. Full CRUD for experiments and per-item experiment results, with pagination, filtering, and cascade deletion.

    Both domains are automatically available when using MongoDBStore — no additional configuration needed.

    const store = new MongoDBStore({ uri: "mongodb://localhost:27017", dbName: "my-app" });
    
    // Datasets
    const dataset = await store.getStorage("datasets").createDataset({ name: "my-dataset" });
    await store.getStorage("datasets").addItem({ datasetId: dataset.id, input: { prompt: "hello" } });
    
    // Experiments
    const experiment = await store.getStorage("experiments").createExperiment({ name: "run-1", datasetId: dataset.id });
Patch Changes
@mastra/pg@1.8.3
Patch Changes
  • Added storage support for dataset targeting and experiment status fields. (#14470)

    • Added targetType (text) and targetIds (jsonb) columns to datasets table for entity association
    • Added tags (jsonb) column to datasets table for tag vocabulary
    • Added status column to experiment results for review workflow tracking
    • Added migration logic to add new columns to existing tables
  • Added agent version support for experiments. When triggering an experiment, you can now pass an agentVersion parameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)

    const client = new MastraClient();
    
    await client.triggerDatasetExperiment({
      datasetId: "my-dataset",
      targetType: "agent",
      targetId: "my-agent",
      version: 3, // pin to dataset version 3
      agentVersion: "ver_abc123" // pin to a specific agent version
    });
@mastra/playground-ui@19.0.0
Minor Changes
  • Added Evaluate tab to the agent playground with full dataset management, scorer editing, experiment execution, and review workflow. (#14470)

    Evaluate tab — A new sidebar-driven tab for managing datasets, scorers, and experiments within the agent playground. Key features:

    • Dataset management: Create, attach/detach, and browse datasets. Inline item editing and deletion. Background LLM-powered test data generation with review-before-add flow.
    • Scorer editor: Create and edit scorers with test items, linked test datasets, configurable score ranges, and model selection. Run scorer experiments directly from the editor.
    • Experiment runner: Trigger experiments from dataset or scorer views with correct target routing (agent, scorer, or workflow). Past runs displayed with pass/fail counts and auto-polling for status updates.
    • Collapsible sidebar sections: Datasets, Scorers, and Experiments sections are collapsible with search-enabled attach dialogs.

    Review tab — A dedicated review workflow for experiment results:

    • Tag-based organization with dataset-level tag vocabulary and bulk tagging
    • LLM-powered analysis ("Analyze untagged/selected") that proposes tags with reasons, using existing tags when applicable
    • Thumbs up/down ratings and comments persisted via feedback API
    • Mark items as complete for audit trail
    • Completed items section with read-only display
  • Added dataset and agent version selectors to the experiment evaluate tab. You can now choose which dataset version and agent version to use when running an experiment. Version information is displayed in the experiment sidebar, results panel header, and Past Runs list. Added a copy button next to the agent version selector to easily copy version IDs. (#14562)

Patch Changes
  • Added EntityList.NoMatch component that displays a message when search filtering returns no results. Applied to all entity list pages: Agents, Workflows, Tools, Scorers, Processors, Prompts, Datasets, and MCP Servers. (#14621)

  • Added agent version support for experiments. When triggering an experiment, you can now pass an agentVersion parameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)

    const client = new MastraClient();
    
    await client.triggerDatasetExperiment({
      datasetId: "my-dataset",
      targetType: "agent",
      targetId: "my-agent",
      version: 3, // pin to dataset version 3
      agentVersion: "ver_abc123" // pin to a specific agent version
    });
  • Removed 'Create an Agent' button from agent list page and table empty state. Removed 'Create Scorer' button from top-level scorers page. Removed stored/code source icons (AgentSourceIcon) from agent headers, combobox, and table. Renamed 'Versions' tab to 'Editor' in agent page tabs. Added GaugeIcon to the 'Create Scorer' button in the review tab. (#14555)

  • Added metrics dashboard with KPI cards, trace volume, latency, model usage, and scores visualizations. Includes filtering by date range, agents, models, and providers. Added HorizontalBars, MetricsCard, MetricsKpiCard, MetricsLineChart, MetricsFlexGrid, and MetricsDataTable design system components. (#14491)

  • Internal cleanup and linting fixes (#14497)

  • Add optional ?path= query param to workspace skill routes for disambiguating same-named skills. (#14430)

    Skill routes continue to use :skillName in the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional ?path= query parameter to select the exact skill:

    GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelines

    SkillMetadata now includes a path field, and the list() method returns all same-named skills for disambiguation. The client SDK's getSkill() accepts an optional skillPath parameter for disambiguation.

  • Updated skill search result types and query parameters to use skillName/skillNames instead of skillPath/skillPaths for consistency with the name-based public API. (#14430)

  • Added experimental entity list components with skeleton loading states, error handling, and dedicated empty state components for all list pages. Gated behind MASTRA_EXPERIMENTAL_UI environment variable. (#14547)

  • Added storage type detection to the Metrics Dashboard. The /system/packages endpoint now returns observabilityStorageType, identifying the observability storage backend. The dashboard shows an empty state when the storage does not support metrics (e.g. PostgreSQL, LibSQL), and displays a warning when using in-memory storage since metrics are not persisted across server restarts. Also added a docs link button to the Metrics page header. (#14620)

    import { MastraClient } from "@mastra/client-js";
    
    const client = new MastraClient();
    const system = await client.getSystemPackages();
    
    // system.observabilityStorageType contains the class name of the observability store:
    // - 'ObservabilityInMemory' → metrics work but are not persisted across restarts
    // - 'ObservabilityPG', 'ObservabilityLibSQL', etc. → metrics not supported
    
    if (system.observabilityStorageType === "ObservabilityInMemory") {
      console.warn("Metrics are not persisted — data will be lost on server restart.");
    }
    
    const SUPPORTED = new Set(["ObservabilityInMemory"]);
    if (!SUPPORTED.has(system.observabilityStorageType ?? "")) {
      console.error("Metrics require in-memory observability storage.");
    }
  • Added ModelByInputTokens in @mastra/memory for token-threshold-based model selection in Observational Memory. (#14614)

    When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.

    Example usage:

    import { Memory, ModelByInputTokens } from "@mastra/memory";
    
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: new ModelByInputTokens({
            upTo: {
              10_000: "google/gemini-2.5-flash",
              40_000: "openai/gpt-4o",
              1_000_000: "openai/gpt-4.5"
            }
          })
        }
      }
    });

    The upTo keys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.

    Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.

  • Redesigned the agent instruction blocks editor with a Notion-like document feel. Blocks no longer show line numbers or block numbers, have tighter spacing, and display a subtle hover highlight. Reference blocks now show a sync-block header with a popover for block details, "Open original", "De-reference block", and "Used by" agents. Inline blocks can be converted to saved prompt blocks via a new "Save as prompt block" action in the hover toolbar. The prompt block edit sidebar now shows a "Used by" section listing which agents reference the block. Added a lineNumbers prop to CodeEditor to optionally hide line numbers. (#14563)

    <CodeEditor language="markdown" lineNumbers={false} />
@mastra/react@0.2.17
Patch Changes
  • Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)

    Mastra agent and client APIs accept schemas from either zod/v3 or zod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.

@mastra/schema-compat@1.2.7
Patch Changes
  • Fixed schema-compat ESM imports for Zod JSON Schema helpers. (#14617)

    @mastra/schema-compat no longer uses createRequire in its Zod v4 adapter or runtime eval tests, which avoids createRequire-related ESM issues while preserving support for zod/v3 and zod/v4.

  • Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)

    Mastra agent and client APIs accept schemas from either zod/v3 or zod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.

@mastra/server@1.16.0
Minor Changes
  • Added dataset-agent association and experiment status tracking for the Evaluate workflow. (#14470)

    • Dataset targeting: Added targetType and targetIds fields to datasets, enabling association with agents, scorers, or workflows. Datasets can now be linked to multiple entities.
    • Experiment status: Added status field to experiment results ('needs-review', 'reviewed', 'complete') for review queue workflow.
    • Dataset experiment routes: Added API endpoints for triggering experiments from a dataset with configurable target type and target ID.
    • LLM data generation: Added endpoint for generating dataset items using an LLM with configurable count and prompt.
    • Failure analysis: Added endpoint for clustering experiment failures and proposing tags using LLM analysis.
  • Added getAuthenticatedUser() to @mastra/server/auth so server middleware can resolve the configured auth user without changing route auth behavior. (#14458)

    Example

    import { getAuthenticatedUser } from "@mastra/server/auth";
    
    const user = await getAuthenticatedUser({
      mastra,
      token,
      request: c.req.raw
    });
  • Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14470)

Patch Changes
  • Added agent version support for experiments. When triggering an experiment, you can now pass an agentVersion parameter to pin which agent version to use. The agent version is stored with the experiment and returned in experiment responses. (#14562)

    const client = new MastraClient();
    
    await client.triggerDatasetExperiment({
      datasetId: "my-dataset",
      targetType: "agent",
      targetId: "my-agent",
      version: 3, // pin to dataset version 3
      agentVersion: "ver_abc123" // pin to a specific agent version
    });
  • Add optional ?path= query param to workspace skill routes for disambiguating same-named skills. (#14430)

    Skill routes continue to use :skillName in the URL path (no breaking change). When two skills share the same name (e.g. from different directories), pass the optional ?path= query parameter to select the exact skill:

    GET /workspaces/:workspaceId/skills/:skillName?path=skills/brand-guidelines

    SkillMetadata now includes a path field, and the list() method returns all same-named skills for disambiguation. The client SDK's getSkill() accepts an optional skillPath parameter for disambiguation.

  • Updated skill search result types and query parameters to use skillName/skillNames instead of skillPath/skillPaths for consistency with the name-based public API. (#14430)

  • Added storage type detection to the Metrics Dashboard. The /system/packages endpoint now returns observabilityStorageType, identifying the observability storage backend. The dashboard shows an empty state when the storage does not support metrics (e.g. PostgreSQL, LibSQL), and displays a warning when using in-memory storage since metrics are not persisted across server restarts. Also added a docs link button to the Metrics page header. (#14620)

    import { MastraClient } from "@mastra/client-js";
    
    const client = new MastraClient();
    const system = await client.getSystemPackages();
    
    // system.observabilityStorageType contains the class name of the observability store:
    // - 'ObservabilityInMemory' → metrics work but are not persisted across restarts
    // - 'ObservabilityPG', 'ObservabilityLibSQL', etc. → metrics not supported
    
    if (system.observabilityStorageType === "ObservabilityInMemory") {
      console.warn("Metrics are not persisted — data will be lost on server restart.");
    }
    
    const SUPPORTED = new Set(["ObservabilityInMemory"]);
    if (!SUPPORTED.has(system.observabilityStorageType ?? "")) {
      console.error("Metrics require in-memory observability storage.");
    }
  • Fix Zod v3 and Zod v4 compatibility across public structured-output APIs. (#14464)

    Mastra agent and client APIs accept schemas from either zod/v3 or zod/v4, matching the documented peer dependency range and preserving TypeScript compatibility for both Zod versions.

  • Added ModelByInputTokens in @mastra/memory for token-threshold-based model selection in Observational Memory. (#14614)

    When configured, OM automatically selects different observer or reflector models based on the actual input token count at the time the OM call runs.

    Example usage:

    import { Memory, ModelByInputTokens } from "@mastra/memory";
    
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: new ModelByInputTokens({
            upTo: {
              10_000: "google/gemini-2.5-flash",
              40_000: "openai/gpt-4o",
              1_000_000: "openai/gpt-4.5"
            }
          })
        }
      }
    });

    The upTo keys are inclusive upper bounds. OM resolves the matching tier directly at the observer or reflector call site. If the input exceeds the largest configured threshold, OM throws an error.

    Improved Observational Memory tracing so traces show the observer and reflector spans and make it easier to see which resolved model was used at runtime.

Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.14.0

Highlights

AI Gateway Tool Support in the Agentic Loop

@mastra/core now supports AI Gateway tools (e.g. gateway.tools.perplexitySearch()) as provider-executed tools: it infers providerExecuted, merges streamed provider results back into the originating tool calls, and skips local execution when the provider already returned a result.

More Reliable Observational Memory (Cache Stability + “As Of” Retrieval)

Observational memory persistence is more stable via dated message boundary delimiters and chunking, and @mastra/memory adds getObservationsAsOf() to retrieve the exact observation set active at a given message timestamp (useful for replay/debugging and consistent prompting).

MCP Client Diagnostics & Per-Server Control

@mastra/mcp adds per-server operational tooling—reconnectServer(serverName), listToolsetsWithErrors(), and getServerStderr(serverName)—to improve reliability and debugging of MCP stdio/server integrations.

Breaking Changes
  • None called out in this changelog.

Changelog

@mastra/core@1.14.0
Patch Changes
  • Update provider registry and model documentation with latest models and providers (51970b3)

  • Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)

  • Fixed provider-executed tool calls being saved out of order or without results in memory replay. (Fixes #13762) (#13860)

  • Fix generateEmptyFromSchema to accept both string and pre-parsed object JSON schema inputs, recursively initialize nested object properties, and respect default values. Updated WorkingMemoryTemplate type to a discriminated union supporting Record<string, unknown> content for JSON format templates. Removed duplicate private schema generator in the working-memory processor in favor of the shared utility. (#14310)

  • Fixed provider-executed tool calls (e.g. Anthropic web_search) being dropped or incorrectly persisted when deferred by the provider. Tool call parts are now persisted in stream order, and deferred tool results are correctly merged back into the originating message. (#14282)

  • Fixed replaceString utility to properly escape $ characters in replacement strings. Previously, patterns like $& in the replacement text would be interpreted as regex backreferences instead of literal text. (#14434)

  • Fixed tool invocation updates to preserve providerExecuted and providerMetadata from the original tool call when updating to result state. (#14431)

  • @mastra/core: patch (#14327)

    Added spanId alongside traceId across user-facing execution results that return tracing identifiers (including agent stream/generate and workflow run results) so integrations can query observability vendors by run root span ID

  • Add AI Gateway tool support in the agentic loop. (#14016)

    Gateway tools (e.g., gateway.tools.perplexitySearch()) are provider-executed but, unlike native provider tools (e.g., openai.tools.webSearch()), the LLM provider does not store their results server-side. The agentic loop now correctly infers providerExecuted for these tools, merges streamed provider results with their corresponding tool calls, and skips local execution when a provider result is already present.

    Fixes #13190

  • Fixed schema-based working memory typing so workingMemory.schema accepts supported schemas such as Zod and JSON Schema. (#14363)

  • Fixed workspace search being wiped when skills refresh. Previously, calling skills.refresh() or triggering a skills re-discovery via maybeRefresh() would clear the entire BM25 search index, including auto-indexed workspace content. Now only skill entries are removed from the index during refresh, preserving workspace search results. (#14287)

  • Added client/server body schemas for feedback and scores that omit the timestamp field, allowing it to be set server-side (#14270)

  • Fixed processor state not persisting between processOutputStream and processOutputResult when processors are wrapped in workflows. State set during stream processing is now correctly accessible in processOutputResult. (#14279)

  • Fixed type inference for requestContext schemas when using Zod v3 and v4. Agent and tool configurations now correctly infer RequestContext types from Zod schemas and other StandardSchema-compatible schemas. (#14363)

@mastra/ai-sdk@1.1.4
Patch Changes
  • Clarified that chatRoute() aborts generation on client disconnect and documented a custom route pattern that continues server-side generation with consumeStream(). (#14333)
@mastra/auth@1.0.2
Patch Changes
  • Fixed Studio showing unauthenticated state when using MastraJwtAuth with custom headers. MastraJwtAuth now implements the IUserProvider interface (getCurrentUser/getUser), so the Studio capabilities endpoint can resolve the authenticated user from the JWT Bearer token. (#14411)

    Also added an optional mapUser option to customize how JWT claims are mapped to user fields:

    new MastraJwtAuth({
      secret: process.env.JWT_SECRET,
      mapUser: payload => ({
        id: payload.userId,
        name: payload.displayName,
        email: payload.mail,
      }),
    });

    Closes #14350

@mastra/auth-studio@1.2.0
Minor Changes
  • Add configurable cookie domain support (#14285)
    • Add cookieDomain option to MastraAuthStudioOptions for explicit configuration
    • Support MASTRA_COOKIE_DOMAIN environment variable as fallback
    • Use hostname-based detection for auto-detecting .mastra.ai domain (prevents false positives from malicious URLs)
    • Maintain backward compatibility with existing .mastra.ai auto-detection
Patch Changes
@mastra/client-js@1.9.0
Minor Changes
  • Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14270)
Patch Changes
@mastra/deployer@1.14.0
Patch Changes
  • Added MASTRA_HOST environment variable support for configuring the server bind address. Previously, the host could only be set via server.host in the Mastra config. Now it follows the same pattern as PORT: config value takes precedence, then env var, then defaults to localhost. (#14313)

  • Added a new MASTRA_TEMPLATES Studio runtime flag to control whether the Templates section appears in the sidebar. (#14309)

    • MASTRA_TEMPLATES=true now enables Templates navigation in Studio.
    • By default (false or unset), Templates is hidden.
    • Studio HTML injection now propagates this value in both CLI-hosted and deployer-hosted Studio builds.
    • Added tests covering environment variable injection for both paths.
  • Fixed tsconfig path aliases during build when imports use .js-style module specifiers. (#13998)

  • Fixed apiPrefix server option not being applied to the underlying Hono server instance. Routes, welcome page, Swagger UI, and studio HTML handler now all respect the configured apiPrefix instead of hardcoding /api. (#14325)

@mastra/deployer-cloudflare@1.1.12
Patch Changes
  • Stop writing .env variables to wrangler.jsonc to prevent secrets from leaking into source control. (#14302)
    • Environment variables from .env are no longer merged into the vars field of the generated wrangler config.
    • User-provided vars from the CloudflareDeployer constructor are still written as before.
    • A warning is logged during build with instructions to upload secrets via npx wrangler secret bulk .env.
@mastra/elasticsearch@1.2.0
Minor Changes
  • Added support for constructing ElasticSearchVector with a pre-configured Elasticsearch client. You can now pass either a client instance or connection parameters (url and optional auth), giving you full control over client configuration when needed. (#12802)

    Using connection parameters:

    const vectorDB = new ElasticSearchVector({
      id: 'my-store',
      url: 'http://localhost:9200',
      auth: { apiKey: 'my-key' },
    });

    Using a pre-configured client:

    import { Client } from '@elastic/elasticsearch';
    
    const client = new Client({ node: 'http://localhost:9200' });
    const vectorDB = new ElasticSearchVector({ id: 'my-store', client });
Patch Changes
@mastra/libsql@1.7.1
Patch Changes
  • Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
@mastra/loggers@1.0.3
Patch Changes
  • Fixed: PinoLogger now supports JSON output for log aggregators (#14306)

    Previously, PinoLogger always used pino-pretty which produced multiline colored output, breaking log aggregators like Datadog, Loki, and CloudWatch. A new prettyPrint option allows switching to single-line JSON output.

@mastra/mcp@1.3.0
Minor Changes
  • Added new MCP client APIs for per-server control and diagnostics. (#14377)

    • Added reconnectServer(serverName) to reconnect a single MCP server without restarting all servers.
    • Added listToolsetsWithErrors() to return both toolsets and per-server errors.
    • Added getServerStderr(serverName) to inspect piped stderr for stdio servers.

    Example

    const { toolsets, errors } = await mcpClient.listToolsetsWithErrors();
    await mcpClient.reconnectServer('slack');
    const stderr = mcpClient.getServerStderr('slack');
Patch Changes
  • Improved (#14260)

    • Updated @modelcontextprotocol/sdk from ^1.17.5 to ^1.27.1.

    Deprecated

    • Deprecated prompt version usage in @mastra/mcp.
    • Prompt versions are not part of MCP protocol behavior and will be removed.

    Migration

    • Use unique prompt names instead of prompt versions.
    • Before: client.prompts.get({ name: 'explain-code', version: 'v1', args })
    • After: client.prompts.get({ name: 'explain-code-v1', args })
    • MastraPrompt is available for migration and is also deprecated.
@mastra/memory@1.8.3
Patch Changes
  • Fixed observational memory triggering observation while provider-executed tool calls are still pending, which could split messages and cause errors on follow-up turns. (#14282)

  • Fixed working memory tool description to accurately reflect merge behavior. The previous description incorrectly stated "Set a field to null to remove it" but null values are stripped by validation before reaching the merge logic. The updated description clarifies: omit fields to preserve existing data, and pass complete arrays or omit them since arrays are replaced entirely. (#14424)

  • Limit oversized observational-memory tool results before they reach the observer. (#14344)

    This strips large encryptedContent blobs and truncates remaining tool result payloads to keep observer prompts and token estimates aligned with what the model actually sees.

  • Improved observational memory cache stability by splitting persisted observations into separate prompt chunks using dated message boundary delimiters. (#14367)

    Added getObservationsAsOf() utility to retrieve the observations that were active at a specific point in time. This enables filtering observation history by message creation date.

    import { getObservationsAsOf } from '@mastra/memory';
    
    // Get observations that existed when a specific message was created
    const observations = getObservationsAsOf(record.activeObservations, message.createdAt);
@mastra/mongodb@1.5.6
Patch Changes
  • Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
@mastra/pg@1.8.1
Patch Changes
  • Added dated message boundary delimiters when activating buffered observations for improved cache stability. (#14367)
@mastra/playground-ui@17.0.0
Minor Changes
  • Added dedicated session page for agents at /agents/<agentId>/session. This minimal view shows only the chat interface without the sidebar or information pane, making it ideal for quick internal testing or sharing with non-technical team members. If request context presets are configured, a preset dropdown appears in the header. (#13754)

    Added hideModelSwitcher prop to AgentChat and Thread components to allow hiding the model picker in the composer.

Patch Changes
  • Fixed crash during template installation by ensuring error values from stream events are converted to strings before being passed to the UI. This prevents the 'e?.includes is not a function' TypeError when the server returns non-string error payloads. (#14267)

  • Fixed crash when template installation errors are non-string values (e.g. objects). The error is now safely converted to a string before calling .includes(), preventing the 'e?.includes is not a function' TypeError in the studio. (#14267)

@mastra/schema-compat@1.2.5
Patch Changes
  • Added ZodIntersection support so that MCP tools using allOf in their JSON Schema no longer throw 'does not support zod type: ZodIntersection'. Intersection types are flattened and merged into a single object schema across all provider compatibility layers (Anthropic, Google, OpenAI, OpenAI Reasoning, DeepSeek, Meta). (#14255)
@mastra/server@1.14.0
Minor Changes
  • Added new observability API endpoints and client methods for logs, scores, feedback, metrics (aggregate, breakdown, time series, percentiles), and discovery (metric names, label keys/values, entity types/names, service names, environments, tags) (#14270)
Patch Changes
  • Fixed server tool serialization for plain JSON Schema objects to prevent tools from disappearing in Studio. (#13920)
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.13.0

Highlights

Observability Storage Domain (schemas + in-memory implementations)

Mastra now ships zod-based storage schemas and in-memory implementations for all observability signals (scores, logs, feedback, metrics, discovery), with full type inference and a base ObservabilityStorage that includes default method implementations.

New persistent Workspace filesystem: @mastra/agentfs

The new AgentFSFilesystem workspace provider adds Turso/SQLite-backed, database-persistent file storage for agents across sessions via agentfs-sdk.

Observability pipeline upgrades: renamed types + EventBuffer batching

@mastra/observability exporters/event bus were updated to align with renamed core observability types, and an EventBuffer was added to batch non-tracing signals with configurable flush intervals.

Type-safe server route inference via @mastra/server/schemas

A new @mastra/server/schemas export provides utility types (RouteMap, InferPathParams, InferBody, InferResponse, etc.) that automatically infer request/response types from SERVER_ROUTES, including routes added via createRoute().

Lower token costs for long-running Observational Memory

Observational Memory adds observation.previousObserverTokens to truncate the “Previous Observations” context to a token budget (or omit/disable truncation), reducing observer prompt size in long conversations.

Breaking Changes
  • MetricType (counter/gauge/histogram) is deprecated — metrics are now raw events with aggregation at query time
  • Score schemas now use scorerId instead of scorerName
  • ObservabilityBus constructor now takes a config object (cardinalityFilter, autoExtractMetrics); setCardinalityFilter() and enableAutoExtractedMetrics() were removed

Changelog

@mastra/core@1.13.0
Minor Changes
  • Added observability storage domain schemas and implementations (#14214)

    Introduced comprehensive storage schemas and in-memory implementations for all observability signals (scores, logs, feedback, metrics, discovery). All schemas are zod-based with full type inference. The ObservabilityStorage base class includes default implementations for all new methods.

    Breaking changes:

    • MetricType (counter/gauge/histogram) is deprecated — metrics are now raw events with aggregation at query time
    • Score schemas use scorerId instead of scorerName for scorer identification
Patch Changes
  • Update provider registry and model documentation with latest models and providers (ea86967)

  • Fixed provider tools (e.g. openai.tools.webSearch()) being silently dropped when using a custom gateway that returns AI SDK v6 (V3) models. The router now remaps tool types from provider-defined to provider when delegating to V3 models, so provider tools work correctly through gateways. Fixes #13667. (#13895)

  • Fixed TypeScript type errors in onStepFinish and onFinish callbacks, and resolved compatibility issues with createOpenRouter() across different AI SDK versions. (#14229)

  • Fixed a bug where thread metadata (e.g. title, custom properties) passed via options.memory.thread was discarded when MASTRA_THREAD_ID_KEY was set in the request context. The thread ID from context still takes precedence, but all other user-provided thread properties are now preserved. (#13146)

  • Fixed workspace tools such as mastra_workspace_list_files and mastra_workspace_read_file failing with WorkspaceNotAvailableError in some execution paths. (#14228)

    Workspace tools now work consistently across execution paths.

  • Added observer context optimization for Observational Memory. The observation.previousObserverTokens field reduces Observer input token costs for long-running conversations: (#13568)

    • previousObserverTokens (default: 2000): Truncates the 'Previous Observations' section to a token budget, keeping the most recent observations and automatically replacing already-reflected lines with the buffered reflection summary. Set to 0 to omit previous observations entirely, or false to disable truncation and keep the full observation history.
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: 'google/gemini-2.5-flash',
          observation: {
            previousObserverTokens: 10_000,
          },
        },
      },
    });
@mastra/agentfs@0.1.0
Minor Changes
  • Added AgentFSFilesystem workspace provider — a Turso/SQLite-backed filesystem via the agentfs-sdk that gives agents persistent, database-backed file storage across sessions. (#13450)

    Basic usage

    import { Workspace } from '@mastra/core/workspace';
    import { AgentFSFilesystem } from '@mastra/agentfs';
    
    const workspace = new Workspace({
      filesystem: new AgentFSFilesystem({
        agentId: 'my-agent',
      }),
    });
Patch Changes
@mastra/deployer@1.13.0
Patch Changes
  • Bump esbuild from ^0.25.10 to ^0.27.3 to resolve Go stdlib CVEs (CVE-2025-22871, CVE-2025-61729) flagged by npm audit in consumer projects. (#13124)

  • Fixed Agent-to-Agent requests to return a clear error message when the agent ID parameter is missing. (#14229)

  • Add dynamicPackages bundler config for runtime-loaded packages and auto-detect pino (#11779)

    Adds a new dynamicPackages bundler config option for packages that are loaded dynamically at runtime and cannot be detected by static analysis (e.g., pino.transport({ target: "pino-opentelemetry-transport" })).

    Usage:

    import { Mastra } from '@mastra/core';
    
    export const mastra = new Mastra({
      bundler: {
        dynamicPackages: ['my-custom-transport', 'some-plugin'],
      },
    });

    Additionally, pino transport targets are now automatically detected from the bundled code, so most pino users won't need any configuration.

    This keeps externals for its intended purpose (packages to not bundle) and provides a clear mechanism for dynamic packages that need to be in the output package.json.

    Fixes #10893

@mastra/mcp@1.2.1
Patch Changes
  • Fixed TypeScript compilation errors when using MCP resource methods in projects with pnpm workspaces. (#14229)
@mastra/memory@1.8.0
Minor Changes
  • Added observer context optimization for Observational Memory. The observation.previousObserverTokens field reduces Observer input token costs for long-running conversations: (#13568)

    • previousObserverTokens (default: 2000): Truncates the 'Previous Observations' section to a token budget, keeping the most recent observations and automatically replacing already-reflected lines with the buffered reflection summary. Set to 0 to omit previous observations entirely, or false to disable truncation and keep the full observation history.
    const memory = new Memory({
      options: {
        observationalMemory: {
          model: 'google/gemini-2.5-flash',
          observation: {
            previousObserverTokens: 10_000,
          },
        },
      },
    });
Patch Changes
@mastra/observability@1.5.0
Minor Changes
  • Updated exporters and event bus to use renamed observability types from @mastra/core. Added EventBuffer for batching non-tracing signals with configurable flush intervals. (#14214)

    Breaking changes:

    • ObservabilityBus now takes a config object in its constructor (cardinalityFilter, autoExtractMetrics); setCardinalityFilter() and enableAutoExtractedMetrics() removed
Patch Changes
@mastra/playground-ui@16.1.0
Minor Changes
  • Added group-by-thread utility for the Observability tab. Traces can now be grouped by their conversation thread ID, making it easier to follow multi-turn agent conversations in Studio. See #14004. (#14112)
Patch Changes
  • Fixed agent playground panels growing together when content overflows. Left and right columns now scroll independently. (#14244)

  • Fixed an agent chat editor crash in Playground UI caused by duplicate CodeMirror state instances. (#14241)

  • Improved studio loading performance by lazy-loading the Prettier code formatter. Prettier and its plugins are now loaded on-demand when formatting is triggered, rather than being bundled in the initial page load. (#13934)

  • Improved list-style pages across the Playground UI (agents, datasets, MCPs, processors, prompt blocks, scorers, tools, workflows) with a new list layout and updated empty states. (#14173)

@mastra/schema-compat@1.2.2
Patch Changes
  • @mastra/schema-compat: patch (#14195)

    Fixed published @mastra/schema-compat types so AI SDK v5 schemas resolve correctly for consumers

  • Fixed false z.toJSONSchema is not available errors for compatible Zod versions. (#14264)

    What changed

    • Improved Zod schema conversion detection so JSON Schema generation works more reliably across different runtime setups.
@mastra/server@1.13.0
Minor Changes
  • Added @mastra/server/schemas export with utility types that infer path params, query params, request body, and response types from any route in SERVER_ROUTES. When you add a new route via createRoute(), it automatically appears in the RouteMap type — no manual contract needed. (#14008)

    import type { RouteMap, RouteContract, InferPathParams, InferBody, InferResponse } from '@mastra/server/schemas';
    
    type GetAgentParams = InferPathParams<RouteMap['GET /agents/:agentId']>;
    // => { agentId: string }
    
    type GenerateBody = InferBody<RouteMap['POST /agents/:agentId/generate']>;
    // => { messages: CoreMessage[], ... }
    
    type AgentResponse = InferResponse<RouteMap['GET /agents/:agentId']>;
    // => { name: string, tools: ..., ... }
Patch Changes
  • Fixed OpenAPI spec for custom route paths. Custom routes registered via registerApiRoute are served at the root path (e.g. /health), not under /api. The OpenAPI spec now correctly represents this so that API tools and clients using the spec will resolve them to the correct URL. (#13930)

  • Fixed an unnecessary runtime dependency in @mastra/server, reducing install size for consumers. Moved @mastra/schema-compat from dependencies to devDependencies since it is only needed at build time. (#14223)

Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.12.0

Highlights

Cloudflare Durable Objects Storage Adapter

@mastra/cloudflare adds a new Durable Objects–backed storage implementation (in addition to KV), with SQLite persistence, batch operations, and table/column validation—enabling more robust stateful storage on Cloudflare.

Workspace Filesystem Path Resolution Now Matches Real Filesystem Semantics

LocalFilesystem no longer treats absolute paths like /file.txt as workspace-relative; absolute paths now resolve to real filesystem locations (with containment checks), relative paths resolve against basePath, and ~/ expands to the home directory.

Improved Observability for MCP Tooling and Agent/Workflow Execution

MCP tool calls are now traced with a dedicated MCP_TOOL_CALL span type (with server name/version metadata), Studio adds MCP-specific timeline styling, processor-triggered aborts are fully visible in traces, and workflow suspend/resume now preserves trace continuity under the original span.

More Reliable Agent Loops and Token Budgeting in Multi-step Runs

Fixes include the agent loop continuing correctly when onIterationComplete returns continue: true, and preventing exponential token growth by running token-based message pruning at every step (including tool call continuations).

Sandbox & Workspace Extensibility via Provider-specific Getters and String PIDs

Sandbox process IDs are now string-based (ProcessHandle.pid: string) to support session IDs across providers, and sandboxes/filesystems expose underlying SDK instances via new provider-specific getters (e.g., sandbox.daytona, sandbox.blaxel, filesystem.client for S3, filesystem.storage/bucket for GCS).

Breaking Changes
  • LocalFilesystem no longer treats absolute paths (e.g. /src/index.ts) as basePath-relative; update callers to pass relative paths when targeting the workspace.
  • ProcessHandle.pid changed from number to string; update any code that assumes numeric PIDs (including processes.get(...)).

Changelog

@mastra/core@1.12.0
Minor Changes
  • MCP tool calls now use MCP_TOOL_CALL span type instead of TOOL_CALL in traces. CoreToolBuilder detects mcpMetadata on tools and creates spans with MCP server name, version, and tool description attributes. (#13274)

  • Absolute paths now resolve to real filesystem locations instead of being treated as workspace-relative. (#13804)

    Previously, LocalFilesystem in contained mode treated absolute paths like /file.txt as shorthand for basePath/file.txt (a "virtual-root" convention). This could silently resolve paths to unexpected locations — for example, /home/user/.config/file.txt would resolve to basePath/home/user/.config/file.txt instead of the real path.

    Now:

    • Absolute paths (starting with /) are real filesystem paths, subject to containment checks
    • Relative paths (e.g., file.txt, src/index.ts) resolve against basePath
    • Tilde paths (e.g., ~/Documents) expand to the home directory
    Migration

    If your code passes paths like /file.txt to workspace filesystem methods expecting them to resolve relative to basePath, change them to relative paths:

    // Before
    await filesystem.readFile('/src/index.ts');
    
    // After
    await filesystem.readFile('src/index.ts');

    Also fixed:

    • allowedPaths resolving against the working directory instead of basePath, causing unexpected permission errors when basePath differed from cwd
    • Permission errors when accessing paths under allowedPaths directories that don't exist yet (e.g., during skills discovery)
  • Changed ProcessHandle.pid type from number to string to support sandbox providers that use non-numeric process identifiers (e.g., session IDs). (#13591)

    Before:

    const handle = await sandbox.processes.spawn('node server.js');
    handle.pid; // number
    await sandbox.processes.get(42);

    After:

    const handle = await sandbox.processes.spawn('node server.js');
    handle.pid; // string (e.g., '1234' for local, 'session-abc' for Daytona)
    await sandbox.processes.get('1234');
Patch Changes
  • Added a mastra/<version> User-Agent header to all provider API requests (OpenAI, Anthropic, Google, Mistral, Groq, xAI, DeepSeek, and others) across models.dev, Netlify, and Azure gateways for better traffic attribution. (#13087)

  • Update provider registry and model documentation with latest models and providers (9cede11)

  • Fixed processor-triggered aborts not appearing in traces. Processor spans now include abort details (reason, retry flag, metadata) and agent-level spans capture the same information when an abort short-circuits the agent run. This makes guardrail and processor aborts fully visible in tracing dashboards. (#14038)

  • Fix agent loop not continuing when onIterationComplete returns continue: true (#14170)

  • Fixed exponential token growth during multi-step agent workflows by implementing processInputStep on TokenLimiterProcessor and removing the redundant processInput method. Token-based message pruning now runs at every step of the agentic loop (including tool call continuations), keeping the in-memory message list within budget before each LLM call. Also refactored Tiktoken encoder to use the shared global singleton from getTiktoken() instead of creating a new instance per processor. (#13929)

  • Sub-agents with defaultOptions.memory configurations were having their memory settings overridden when called as tools from a parent agent. The parent unconditionally passed its own memory option (with newly generated thread/resource IDs), which replaced the sub-agent's intended memory configuration due to shallow object merging. (#11561)

    This fix checks if the sub-agent has its own defaultOptions.memory before applying parent-derived memory settings. Sub-agents without their own memory config continue to receive parent-derived IDs as a fallback.

    No code changes required for consumers - sub-agents with explicit defaultOptions.memory will now work correctly when used via the agents: {} option.

  • Fixed listConfiguredInputProcessors() and listConfiguredOutputProcessors() returning a combined workflow instead of individual processors. Previously, these methods wrapped all configured processors into a single committed workflow, making it impossible to inspect or look up processors by ID. Now they return the raw flat array of configured processors as intended. (#14158)

  • fetchWithRetry now backs off in sequence 2s → 4s → 8s and then caps at 10s. (#14159)

  • Preserve trace continuity across workflow suspend/resume for workflows run by the default engine, so resumed workflows appear as children of the original span in tracing tools. (#12276)

@mastra/blaxel@0.2.0
Minor Changes
  • Added provider-specific blaxel getter to access the underlying Blaxel SandboxInstance directly. Deprecated the generic instance getter in favor of the new blaxel getter for better IDE discoverability and consistency with other sandbox providers. (#14166)

    // Before
    const blaxelSandbox = sandbox.instance;
    
    // After
    const blaxelSandbox = sandbox.blaxel;
Patch Changes
  • Use provider-native string process IDs directly as ProcessHandle.pid, removing the previous parseInt() workaround. (#13591)

    const handle = await sandbox.processes.spawn('node server.js');
    handle.pid; // string — the Blaxel SDK's native process ID
@mastra/cloudflare@1.3.0
Minor Changes
  • feat: add Cloudflare Durable Objects storage adapter (#12366)

    Adds a new Durable Objects-based storage implementation alongside the existing KV store. Includes SQL-backed persistence via DO's SQLite storage, batch operations, and proper table/column validation.

Patch Changes
@mastra/cloudflare-d1@1.0.3
Patch Changes
  • Fixed D1 listMessages returning empty results when using semantic recall with perPage=0 and many include targets, by batching UNION ALL queries to avoid SQLite's compound SELECT limit (#14117)
@mastra/daytona@0.2.0
Minor Changes
  • Added provider-specific daytona getter to access the underlying Daytona Sandbox instance directly. Deprecated the generic instance getter in favor of the new daytona getter for better IDE discoverability and consistency with other sandbox providers. (#14166)

    // Before
    const daytonaSandbox = sandbox.instance;
    
    // After
    const daytonaSandbox = sandbox.daytona;
Patch Changes
  • Improved Daytona process handling to use provider session IDs directly as ProcessHandle.pid. (#13591)

    const handle = await sandbox.processes.spawn('node server.js');
    await sandbox.processes.get(handle.pid);
  • Fixed sandbox reconnection when Daytona sandbox is externally stopped or times out due to inactivity. Previously, the error thrown by the Daytona SDK (e.g. "failed to resolve container IP") did not match the known dead-sandbox patterns, so the automatic retry logic would not trigger and the error would propagate to the user. Added two new error patterns to correctly detect stopped sandboxes and trigger automatic recovery. (#14175)

@mastra/deployer@1.12.0
Patch Changes
  • Improved Studio load times by serving compressed static assets in both deploy and dev. Large bundles now download much faster and use significantly less bandwidth. (#13945)

  • --- (#14162) @mastra/deployer: patch


    Fixed deployment dependency resolution so required schema compatibility packages are resolved automatically.

  • Fixed gzip compression being applied globally to all API routes, causing JSON responses to be unreadable by clients that don't auto-decompress. Compression is now scoped to studio static assets only. (#14190)

@mastra/e2b@0.1.2
Patch Changes
  • ProcessHandle.pid is now a string. Numeric PIDs from the E2B SDK are stringified automatically. (#13591)

    const handle = await sandbox.processes.spawn('node server.js');
    handle.pid; // string (e.g., '1234')
@mastra/gcs@0.2.0
Minor Changes
  • Added public storage and bucket getters to access the underlying Google Cloud Storage instances directly. Use these when you need GCS features not exposed through the WorkspaceFilesystem interface. (#14166)

    const gcsStorage = filesystem.storage;
    const gcsBucket = filesystem.bucket;
Patch Changes
@mastra/mcp@1.2.0
Minor Changes
  • MCP client now attaches mcpMetadata (server name and version) to every tool it creates, enabling automatic MCP_TOOL_CALL span tracing without user code changes. (#13274)
Patch Changes
  • Added stderr and cwd options to stdio server configuration so you can control child process error output and set the server working directory. (#13959)

    import { MCPClient } from '@mastra/mcp';
    
    const mcp = new MCPClient({
      servers: {
        myServer: {
          command: 'node',
          args: ['server.js'],
          stderr: 'pipe',
          cwd: '/path/to/server',
        },
      },
    });
@mastra/memory@1.7.0
Minor Changes
  • Observational Memory now performs local threshold checks with lower CPU and memory overhead. (#14178)

    This update keeps the same multimodal thresholding behavior for image-aware inputs, so existing Observational Memory configurations continue to work without changes.

Patch Changes
@mastra/playground-ui@16.0.1
Patch Changes
  • Updated form field components in Studio to use the new FieldBlock design system pattern. Replaced legacy SelectField, InputField, and SearchField with SelectFieldBlock, TextFieldBlock, and SearchFieldBlock across all domain components (observability, scores, datasets, templates). Refined button styles and layout improvements. (#14138)

  • Fixed Studio form crash when workflow input schemas contain z.array() fields with Zod v4. Array, union, and intersection fields now render and accept input correctly in the workflow run form. (#14131)

  • Added MCP-specific icon and color in trace timeline for mcp_tool_call spans. (#13274)

  • Change file browser root path from / to . so workspace navigation starts from the workspace directory instead of the host filesystem root. (#13804)

@mastra/s3@0.3.0
Minor Changes
  • Added public client getter to access the underlying S3Client instance directly. Use this when you need S3 features not exposed through the WorkspaceFilesystem interface (e.g., presigned URLs, multipart uploads). (#14166)

    const s3Client = filesystem.client;
Patch Changes
@mastra/schema-compat@1.2.1
Patch Changes
  • Fixed Zod v4 schema conversion when zod/v4 compat layer from Zod 3.25.x is used. Schemas like ask_user and other harness tools were not being properly converted to JSON Schema when ~standard.jsonSchema was absent, causing type: "None" errors from the Anthropic API. (#14157)
@mastra/server@1.12.0
Patch Changes
  • Fixed workspace filesystem permission errors returning HTTP 500 instead of 403. Permission denied errors now return the correct status code, preventing unnecessary client-side retries. (#13804)
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.11.0

Highlights

Dynamic Model Fallback Arrays (Runtime Routing)

Agents can now use model functions that return full fallback arrays (ModelWithRetries[]), enabling context-driven model routing (tier/region/etc.) with nested/async selection and proper maxRetries inheritance.

Standard Schema + Zod v4 Compatibility Layer

Mastra adds Standard Schema normalization (toStandardSchema, standardSchemaToJSONSchema) across Zod v3/v4, AI SDK Schema, and JSON Schema via @mastra/schema-compat, unifying schema handling and improving strict-mode provider compatibility.

Customizable Request Validation Errors Across All Server Adapters

New onValidationError hook on ServerConfig and createRoute() lets you control status codes and response bodies for Zod validation failures, supported consistently in Hono/Express/Fastify/Koa adapters.

RequestContext End-to-End (Tracing + Datasets/Experiments + Storage)

requestContext is now captured on tracing spans (and persisted in ClickHouse/PG/LibSQL/MSSQL span tables) and is supported on dataset items and experiments, allowing request-scoped metadata (tenant/user/flags) to flow through evaluation and observability.

Faster & More Flexible Storage: Recall Performance + PgVector Indexing + New Vector Types

Semantic recall is significantly faster across multiple adapters (notably Postgres for very large threads), PgVector adds metadataIndexes for btree indexing filtered metadata fields, and @mastra/pg now supports pgvector bit and sparsevec vector types.

Breaking Changes
  • Minimum Zod version is now ^3.25.0 (for v3) or ^4.0.0 (for v4 support).

Changelog

@mastra/core@1.11.0
Minor Changes
  • feat: support dynamic functions returning model fallback arrays (#11975)

    Agents can now use dynamic functions that return entire fallback arrays based on runtime context. This enables:

    • Dynamic selection of complete fallback configurations
    • Context-based model selection with automatic fallback
    • Flexible model routing based on user tier, region, or other factors
    • Nested dynamic functions within returned arrays (each model in array can also be dynamic)

    Examples

    Basic dynamic fallback array
    const agent = new Agent({
      model: ({ requestContext }) => {
        const tier = requestContext.get('tier');
        if (tier === 'premium') {
          return [
            { model: 'openai/gpt-4', maxRetries: 2 },
            { model: 'anthropic/claude-3-opus', maxRetries: 1 },
          ];
        }
        return [{ model: 'openai/gpt-3.5-turbo', maxRetries: 1 }];
      },
    });
    Region-based routing with nested dynamics
    const agent = new Agent({
      model: ({ requestContext }) => {
        const region = requestContext.get('region');
        return [
          {
            model: ({ requestContext }) => {
              // Select model variant based on region
              return region === 'eu' ? 'openai/gpt-4-eu' : 'openai/gpt-4';
            },
            maxRetries: 2,
          },
          { model: 'anthropic/claude-3-opus', maxRetries: 1 },
        ];
      },
      maxRetries: 1, // Agent-level default for models without explicit maxRetries
    });
    Async dynamic selection
    const agent = new Agent({
      model: async ({ requestContext }) => {
        // Fetch user's tier from database
        const userId = requestContext.get('userId');
        const user = await db.users.findById(userId);
    
        if (user.tier === 'enterprise') {
          return [
            { model: 'openai/gpt-4', maxRetries: 3 },
            { model: 'anthropic/claude-3-opus', maxRetries: 2 },
          ];
        }
        return [{ model: 'openai/gpt-3.5-turbo', maxRetries: 1 }];
      },
    });

    Technical Details

    • Functions can return MastraModelConfig (single model) or ModelWithRetries[] (array)
    • Models without explicit maxRetries inherit agent-level maxRetries default
    • Each model in returned array can also be a dynamic function for nested selection
    • Empty arrays are validated and throw errors early
    • Arrays are normalized to ModelFallbacks with all required fields filled in
    • Performance optimization: Already-normalized arrays skip re-normalization

    Fixes and Improvements

    • Dynamic model fallbacks now properly inherit agent-level maxRetries when not explicitly specified
    • getModelList() now correctly handles dynamic functions that return arrays
    • Added validation for empty arrays returned from dynamic functions
    • Added type guard optimization to prevent double normalization of static arrays
    • Preserved backward-compatible getLLM() and getModel() return behavior while adding dynamic fallback array support
    • Comprehensive test coverage for edge cases (async functions, nested dynamics, error handling)

    Documentation

    • Added dynamic fallback array example in Models docs under Model fallbacks

    Migration Guide

    No breaking changes. All existing model configurations continue to work:

    • Static single models: model: 'openai/gpt-4'
    • Static arrays: model: [{ model: 'openai/gpt-4', maxRetries: 2 }]
    • Dynamic single: model: ({ requestContext }) => 'openai/gpt-4'
    • Dynamic arrays (NEW): model: ({ requestContext }) => [{ model: 'openai/gpt-4', maxRetries: 2 }]

    Closes #11951

  • Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)

    const mastra = new Mastra({
      server: {
        onValidationError: (error, context) => ({
          status: 422,
          body: {
            ok: false,
            errors: error.issues.map(i => ({
              path: i.path.join('.'),
              message: i.message,
            })),
            source: context,
          },
        }),
      },
    });
  • Added requestContext field to tracing spans. Each span now automatically captures a snapshot of the active RequestContext, making request-scoped values like user IDs, tenant IDs, and feature flags available when viewing traces. (#14020)

  • Added allowedWorkspaceTools to HarnessSubagent. Subagents now automatically inherit the parent agent's workspace. Use allowedWorkspaceTools to restrict which workspace tools a subagent can see: (#13940)

    const subagent: HarnessSubagent = {
      id: 'explore',
      name: 'Explore',
      allowedWorkspaceTools: ['view', 'search_content', 'find_files'],
    };
  • Enabled tracing for tool executions through mcp server (#12804)

    Traces now appear in the Observability UI for MCP server tool calls

  • Added result to processOutputResult args, providing resolved generation data (usage, text, steps, finishReason) directly. This replaces raw stream chunks with an easy-to-use OutputResult object containing the same data available in the onFinish callback. (#13810)

    const usageProcessor: Processor = {
      id: 'usage-processor',
      processOutputResult({ result, messages }) {
        console.log(`Text: ${result.text}`);
        console.log(`Tokens: ${result.usage.inputTokens} in, ${result.usage.outputTokens} out`);
        console.log(`Finish reason: ${result.finishReason}`);
        console.log(`Steps: ${result.steps.length}`);
        return messages;
      },
    };
  • Added requestContext support for dataset items and experiments. (#13938)

    Dataset items now accept an optional requestContext field when adding or updating items. This lets you store per-item request context alongside inputs and ground truths.

    Datasets now support a requestContextSchema field to describe the expected shape of request context on items.

    Experiments now accept a requestContext option that gets passed through to agent.generate() during execution. Per-item request context merges with (and takes precedence over) the experiment-level context.

    // Add item with request context
    await dataset.addItem({
      input: messages,
      groundTruth: expectedOutput,
      requestContext: { userId: '123', locale: 'en' },
    });
    
    // Run experiment with global request context
    await runExperiment(mastra, {
      datasetId: 'my-dataset',
      targetType: 'agent',
      targetId: 'my-agent',
      requestContext: { environment: 'staging' },
    });
  • Add Zod v4 and Standard Schema support (#12238)

    Zod v4 Breaking Changes

    • Fix all z.record() calls to use 2-argument form (key + value schema) as required by Zod v4
    • Update ZodError.errors to ZodError.issues (Zod v4 API change)
    • Update @ai-sdk/provider versions for Zod v4 compatibility

    Standard Schema Integration

    • Add packages/core/src/schema/ module that re-exports from @mastra/schema-compat
    • Migrate codebase to use PublicSchema type for schema parameters
    • Use toStandardSchema() for normalizing schemas across Zod v3, Zod v4, AI SDK Schema, and JSON Schema
    • Use standardSchemaToJSONSchema() for JSON Schema conversion

    Schema Compatibility (@mastra/schema-compat)

    • Add new adapter exports: @mastra/schema-compat/adapters/ai-sdk, @mastra/schema-compat/adapters/zod-v3, @mastra/schema-compat/adapters/json-schema
    • Enhance test coverage with separate v3 and v4 test suites
    • Improve zod-to-json conversion with unrepresentable: 'any' support

    TypeScript Fixes

    • Resolve deep instantiation errors in client-js and model.ts
    • Add proper type assertions where Zod v4 inference differs

    BREAKING CHANGE: Minimum Zod version is now ^3.25.0 for v3 compatibility or ^4.0.0 for v4

Patch Changes
  • Update provider registry and model documentation with latest models and providers (332c014)

  • fix(workflows): add generic bail signature with overloads. The bail() function now uses method overloads - bail(result: TStepOutput) for backward compatibility and bail<T>(result: ...) for workflow type inference. This allows flexible early exits while maintaining type safety for workflow chaining. Runtime validation will be added in a follow-up. (#12211)

  • Fixed structured output parsing when JSON string fields include fenced JSON examples. (#13948)

  • Fixed writer being undefined in processOutputStream for all output processors. The root cause was that processPart in ProcessorRunner did not pass the writer to executeWorkflowAsProcessor in the outputStream phase. Since all user processors are wrapped into workflows via combineProcessorsIntoWorkflow, this meant no processor ever received a writer. Custom output processors (like guardrail processors) can now reliably use writer.custom() to emit stream events. (#14111)

  • Added JSON repair for malformed tool call arguments from LLM providers. When an LLM (e.g., Kimi/K2) generates broken JSON for tool call arguments, Mastra now attempts to fix common errors (missing quotes on property names, single quotes, trailing commas) before falling back to undefined. This reduces silent tool execution failures caused by minor JSON formatting issues. See https://github.com/mastra-ai/mastra/issues/11078 (#14033)

  • Fixed Windows shell command execution to avoid visible cmd.exe popups and broken output piping. (#13886)

  • Fixed OpenAI reasoning models (e.g. gpt-5-mini) failing with "function*call was provided without its required reasoning item" when the agent loops back after a tool call. The issue was that callProviderMetadata.openai carrying fc*\*item IDs was not being stripped alongside reasoning parts, causing the AI SDK to senditem_referenceinstead of inlinefunction_call content. (#14144)

  • Output processors can now inspect, modify, or block custom data-* chunks emitted by tools via writer.custom() during streaming. Processors must opt in by setting processDataParts = true to receive these chunks in processOutputStream. (#13823)

    class MyDataProcessor extends Processor {
      processDataParts = true;
    
      processOutputStream(part, { abort }) {
        if (part.type === 'data-sensitive') {
          abort('Blocked sensitive data');
        }
        return part;
      }
    }
  • Fixed agent tool calls not being surfaced in evented workflow streams. Added StreamChunkWriter abstraction and stream format configuration ('legacy' | 'vnext') to forward agent stream chunks through the workflow output stream. (#12692)

  • Fixed OpenAI strict mode schema rejection when using agent networks with structured output. The compat layer was skipped when modelId was undefined, causing optional fields to be missing from the required array. (Fixes #12284) (#13695)

  • Fixed activeTools to also enforce at execution time, not just at the model prompt. Tool calls to tools not in the active set are now rejected with a ToolNotFoundError. (#13949)

  • Fix build failures on Windows when running build:patch-commonjs during pnpm run setup (#14029)

  • Experiments now fail immediately with a clear error when triggered on a dataset with zero items, instead of getting stuck in "pending" status forever. The experiment trigger API returns HTTP 400 for empty datasets. Unexpected errors during async experiment setup are now logged and mark the experiment as failed. (#14031)

  • fix: respect lastMessages: false in recall() to disable conversation history (#12951)

    Setting lastMessages: false in Memory options now correctly prevents recall() from returning previous messages. Previously, the agent would retain the full conversation history despite this setting being disabled.

    Callers can still pass perPage: false explicitly to recall() to retrieve all messages (e.g., for displaying thread history in a UI).

  • @mastra/core: patch (#14103)

    Fixed reasoning content being lost in multi-turn conversations with thinking models (kimi-k2.5, DeepSeek-R1) when using OpenAI-compatible providers (e.g., OpenRouter).

    Previously, reasoning content could be discarded during streaming, causing 400 errors when the model tried to continue the conversation. Multi-turn conversations now preserve reasoning content correctly across all turns.

  • fix(workflows): propagate logger to executionEngine (#12517)

    When a custom logger is set on a Workflow via __registerPrimitives or __setLogger, it is now correctly propagated to the internal executionEngine. This ensures workflow step execution errors are logged through the custom logger instead of the default ConsoleLogger, enabling proper observability integration.

  • Added permission denied handling for dataset pages. Datasets now show a "Permission Denied" screen when the user lacks access, matching the behavior of agents, workflows, and other resources. (#13876)

  • Fixed stream freezing when using Anthropic's Programmatic Tool Calling (PTC). Streams that contain only tool-input streaming chunks (without explicit tool-call chunks) now correctly synthesize tool-call events and complete without hanging. See #12390. (#12400)

  • Fixed subagents receiving parent's tool call/result parts in their context messages. On subsequent queries in a conversation, these references to tools the subagent doesn't have caused models (especially via custom gateways) to return blank or incorrect results. Parent delegation tool artifacts are now stripped from context before forwarding to subagents. (#13927)

  • Memory now automatically creates btree indexes on thread_id and resource_id metadata fields when using PgVector. This prevents sequential scans on the memory_messages vector table, resolving performance issues under high load. (#14034)

    Fixes #12109

  • Clarified the idGenerator documentation to reflect the current context-aware function signature and documented the available IdGeneratorContext fields used for type-specific ID generation. (#14081)

  • Reasoning content from OpenAI models is now stripped from conversation history before replaying it to the LLM, preventing API errors on follow-up messages while preserving reasoning data in the database. Fixes #12980. (#13418)

  • Added transient option for data chunks to skip database persistence. Chunks marked as transient are streamed to the client for live display but not saved to storage, reducing bloat from large streaming outputs. (#13869)

    await context.writer?.custom({
      type: 'data-my-stream',
      data: { output: line },
      transient: true,
    });

    Workspace tools now use this to mark stdout/stderr streaming chunks as transient.

  • Fixed message ID mismatch between generate/stream response and memory-stored messages. When an agent used memory, the message IDs returned in the response (e.g. response.uiMessages[].id) could differ from the IDs persisted to the database. This was caused by a format conversion that stripped message IDs during internal re-processing. Messages now retain their original IDs throughout the entire save pipeline. (#13796)

  • Fixed assistant messages to persist content.metadata.modelId during streaming. (#12969) This ensures stored and processed assistant messages keep the model identifier. Developers can now reliably read content.metadata.modelId from downstream storage adapters and processors.

  • Fixed savePerStep: true not actually persisting messages to storage during step execution. Previously, onStepFinish only accumulated messages in the in-memory MessageList but never flushed them to the storage backend. The only persistence path was executeOnFinish, which is skipped when the stream is aborted. Now messages are flushed to storage after each completed step, so they survive page refreshes and stream aborts. Fixes https://github.com/mastra-ai/mastra/issues/13984 (#14030)

  • Fixed agentic loop continuing indefinitely when model hits max output tokens (finishReason: 'length'). Previously, only 'stop' and 'error' were treated as termination conditions, causing runaway token generation up to maxSteps when using structuredOutput with generate(). The loop now correctly stops on 'length' finish reason. Fixes #13012. (#13861)

  • Fixed tool-call arguments being silently lost when LLMs append internal tokens to JSON (#13400)

    LLMs (particularly via OpenRouter and OpenAI) sometimes append internal tokens like <|call|>, <|endoftext|>, or <|end|> to otherwise valid JSON in streamed tool-call arguments. Previously, these inputs would fail JSON.parse and the tool call would silently lose its arguments (set to undefined).

    Now, sanitizeToolCallInput strips these token patterns before parsing, recovering valid data that was previously discarded. Valid JSON containing <|...|> inside string values is left untouched. Truly malformed JSON still gracefully returns undefined.

    Fixes https://github.com/mastra-ai/mastra/issues/13185 and https://github.com/mastra-ai/mastra/issues/13261.

  • Fixed agent loop stopping prematurely when LLM returns tool calls with finishReason 'stop'. Some models (e.g., OpenAI gpt-5.3-codex) return 'stop' even when tool calls are present, causing the agent to halt instead of processing tool results and continuing. The agent now correctly continues the loop whenever tool calls exist, regardless of finishReason. (#14043)

  • Fixed (#14133)

    • Prevented provider-executed tools from triggering extra loop iterations and duplicate requests.
    • Preserved tool-call metadata during streaming so multi-turn conversations continue to work correctly with provider-executed tools.
  • Fixed observational memory activation using outdated buffered observations in some long-running threads. Activation now uses the latest thread state so the correct observations are promoted. (#13955)

  • Fixed model fallback retry behavior. Non-retryable errors (401, 403) are no longer retried on the same model before falling back. Retryable errors (429, 500) are now only retried by a single layer (p-retry) instead of being duplicated across two layers, preventing (maxRetries + 1)² total calls. The per-model maxRetries setting now correctly controls how many times p-retry retries on that specific model before the fallback loop moves to the next model. (#14039)

  • Added processor-driven response message ID rotation so streamed assistant IDs use the rotated ID. (#13887)

    Processors that run outside the agent loop no longer need synthetic response message IDs.

  • Fixed a regression where dynamic model functions returning a single v1 model were treated as model arrays. (#14018)

  • Fixed requestContext not being forwarded to tools dynamically added by input processors. (#13827)

  • Added 'sandbox_access_request' to the HarnessEvent union type, enabling type-safe handling of sandbox access request events without requiring type casts. (#13648)

  • Fix wrong threadId and resourceId being sent to subagent (#13868)

@mastra/ai-sdk@1.1.3
Patch Changes
  • Fixed handleChatStream not merging providerOptions from params and defaultOptions. Previously, params.providerOptions would completely replace defaultOptions.providerOptions instead of merging them. Now provider-specific keys from both sources are merged, with params.providerOptions taking precedence for the same provider. (#13820)
@mastra/clickhouse@1.3.0
Minor Changes
  • Added requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)
Patch Changes
  • Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)

    For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.

  • Fixed slow semantic recall in the libsql, Cloudflare D1, and ClickHouse storage adapters. Recall performance no longer degrades as threads grow larger. (Fixes #11702) (#14022)

@mastra/client-js@1.8.0
Minor Changes
  • Added requestContext field to dataset item API endpoints and requestContextSchema to dataset CRUD endpoints. Added requestContext option to the experiment trigger endpoint, which gets forwarded to agent execution during experiments. (#13938)

    Usage with @mastra/client-js:

    // Create a dataset with a request context schema
    await client.createDataset({
      name: 'my-dataset',
      requestContextSchema: {
        type: 'object',
        properties: { region: { type: 'string' } },
      },
    });
    
    // Add an item with request context
    await client.addDatasetItem({
      datasetId: 'my-dataset',
      input: { prompt: 'Hello' },
      requestContext: { region: 'us-east-1' },
    });
    
    // Trigger an experiment with request context forwarded to agent
    await client.triggerDatasetExperiment({
      datasetId: 'my-dataset',
      agentId: 'my-agent',
      requestContext: { region: 'eu-west-1' },
    });
Patch Changes
  • Fixed @mastra/client-js streaming APIs to expose standard Web Streams types in public declarations for browser consumers. (#14079)
@mastra/cloudflare@1.2.3
Patch Changes
  • Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)
@mastra/cloudflare-d1@1.0.2
Patch Changes
  • Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)

    For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.

  • Fixed slow semantic recall in the libsql, Cloudflare D1, and ClickHouse storage adapters. Recall performance no longer degrades as threads grow larger. (Fixes #11702) (#14022)

@mastra/convex@1.0.6
Patch Changes
  • Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)
@mastra/datadog@1.0.6
Patch Changes
  • Fixed Datadog trace tags being formatted incorrectly. Tags using the standard key:value format (e.g. instance_name:career-scout-api) were having :true appended to the value in the Datadog UI, resulting in career-scout-api:true instead of career-scout-api. Tags are now correctly split into proper key-value pairs before being sent to Datadog's LLM Observability API. (#13900)
@mastra/deployer@1.11.0
Patch Changes
  • Fixed CORS preflight blocking dev playground requests by adding the x-mastra-dev-playground header to the allowed CORS headers list. This resolves the browser error when the playground UI (running on a different port) makes requests to the Mastra dev server. (#14097)
@mastra/dynamodb@1.0.3
Patch Changes
  • Fixed slow semantic recall on large threads in the MongoDB and DynamoDB memory stores. Included message lookups now avoid unnecessary work when semantic recall only needs specific messages and nearby context. (Fixes #11702) (#14022)
@mastra/express@1.2.0
Minor Changes
  • Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)

    const mastra = new Mastra({
      server: {
        onValidationError: (error, context) => ({
          status: 422,
          body: {
            ok: false,
            errors: error.issues.map(i => ({
              path: i.path.join('.'),
              message: i.message,
            })),
            source: context,
          },
        }),
      },
    });
Patch Changes
@mastra/fastify@1.2.0
Minor Changes
  • Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)

    const mastra = new Mastra({
      server: {
        onValidationError: (error, context) => ({
          status: 422,
          body: {
            ok: false,
            errors: error.issues.map(i => ({
              path: i.path.join('.'),
              message: i.message,
            })),
            source: context,
          },
        }),
      },
    });
Patch Changes
@mastra/hono@1.2.0
Minor Changes
  • Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)

    const mastra = new Mastra({
      server: {
        onValidationError: (error, context) => ({
          status: 422,
          body: {
            ok: false,
            errors: error.issues.map(i => ({
              path: i.path.join('.'),
              message: i.message,
            })),
            source: context,
          },
        }),
      },
    });
Patch Changes
@mastra/koa@1.3.0
Minor Changes
  • Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)

    const mastra = new Mastra({
      server: {
        onValidationError: (error, context) => ({
          status: 422,
          body: {
            ok: false,
            errors: error.issues.map(i => ({
              path: i.path.join('.'),
              message: i.message,
            })),
            source: context,
          },
        }),
      },
    });
Patch Changes
@mastra/lance@1.0.4
Patch Changes
  • Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)

    For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.

  • Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)

@mastra/libsql@1.7.0
Minor Changes
  • Added requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)

  • Added requestContext and requestContextSchema column support to dataset storage. Dataset items now persist request context alongside input and ground truth data. (#13938)

Patch Changes
  • Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)

    For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.

  • Fixed slow semantic recall in the libsql, Cloudflare D1, and ClickHouse storage adapters. Recall performance no longer degrades as threads grow larger. (Fixes #11702) (#14022)

@mastra/memory@1.6.2
Patch Changes
  • Add image and file attachment support to Observational Memory. The observer can now see and reason about images and files in conversation history, and attachment token counts are included in observation thresholds. Provider-backed token counting is used when available, with results cached for faster subsequent runs. (#13953)

  • Added observational memory repro tooling for recording, analyzing, and sanitizing captures before sharing them. (#13877)

  • fix: respect lastMessages: false in recall() to disable conversation history (#12951)

    Setting lastMessages: false in Memory options now correctly prevents recall() from returning previous messages. Previously, the agent would retain the full conversation history despite this setting being disabled.

    Callers can still pass perPage: false explicitly to recall() to retrieve all messages (e.g., for displaying thread history in a UI).

  • fix(memory): handle dynamic functions returning ModelWithRetries[] in observational memory model resolution (#13902)

  • Fixed observational memory activation using outdated buffered observations in some long-running threads. Activation now uses the latest thread state so the correct observations are promoted. (#13955)

  • Fixed message loss when saving certain messages so text content is preserved. (#13918)

  • Added a compatibility guard so observational memory now fails fast when @mastra/core does not support request-response-id-rotation. (#13887)

@mastra/mongodb@1.5.5
Patch Changes
  • Fixed slow semantic recall on large threads in the MongoDB and DynamoDB memory stores. Included message lookups now avoid unnecessary work when semantic recall only needs specific messages and nearby context. (Fixes #11702) (#14022)
@mastra/mssql@1.2.0
Minor Changes
  • Added requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)
Patch Changes
  • Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)

    For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.

  • Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)

@mastra/observability@1.4.0
Minor Changes
  • Added requestContext field to tracing spans. Each span now automatically captures a snapshot of the active RequestContext, making request-scoped values like user IDs, tenant IDs, and feature flags available when viewing traces. (#14020)
Patch Changes
  • feat(tracing): implement metadata inheritance for child spans (#11959)
    • Updated the BaseSpan constructor to inherit metadata from parent spans when not explicitly provided, merging values if both exist.
    • Added tests to verify that child spans correctly inherit and can override metadata from their parent spans.
    • Enhanced existing tests to ensure proper metadata propagation in tracing scenarios.
@mastra/pg@1.8.0
Minor Changes
  • Added metadataIndexes option to createIndex() for PgVector. This allows creating btree indexes on specific metadata fields in vector tables, significantly improving query performance when filtering by those fields. This is especially impactful for Memory's memory_messages table, which filters by thread_id and resource_id — previously causing sequential scans under load. (#14034)

    Usage example:

    await pgVector.createIndex({
      indexName: 'my_vectors',
      dimension: 1536,
      metadataIndexes: ['thread_id', 'resource_id'],
    });

    Fixes #12109

  • Add support for pgvector's bit and sparsevec vector storage types (#12815)

    You can now store binary and sparse vectors in @mastra/pg:

    // Binary vectors for fast similarity search
    await db.createIndex({
      indexName: 'my_binary_index',
      dimension: 128,
      metric: 'hamming', // or 'jaccard'
      vectorType: 'bit',
    });
    
    // Sparse vectors for BM25/TF-IDF representations
    await db.createIndex({
      indexName: 'my_sparse_index',
      dimension: 500,
      metric: 'cosine',
      vectorType: 'sparsevec',
    });

    What's new:

    • vectorType: 'bit' for binary vectors with 'hamming' and 'jaccard' distance metrics
    • vectorType: 'sparsevec' for sparse vectors (cosine, euclidean, dotproduct)
    • Automatic metric normalization: bit defaults to 'hamming' when no metric is specified
    • includeVector round-trips work correctly for all vector types
    • Requires pgvector >= 0.7.0
  • Added requestContext column to the spans table. Request context data from tracing is now persisted alongside other span data. (#14020)

  • Added requestContext and requestContextSchema column support to dataset storage. Dataset items now persist request context alongside input and ground truth data. (#13938)

Patch Changes
  • Added resilient column handling to insert and update operations. Unknown columns in records are now silently dropped instead of causing SQL errors, ensuring forward compatibility when newer domain packages add fields that haven't been migrated yet. (#14021)

    For example, calling db.insert({ tableName, record: { id: '1', title: 'Hello', futureField: 'value' } }) will silently ignore futureField if it doesn't exist in the database table, rather than throwing. The same applies to update — unknown fields in the data payload are dropped before building the SQL statement.

  • Fixed slow semantic recall in the Postgres storage adapter for large threads. Recall now completes in under 500ms even for threads with 7,000+ messages, down from ~30 seconds. (Fixes #11702) (#14022)

@mastra/playground-ui@16.0.0
Minor Changes
  • Added Playground and Traces tabs to the agent detail page. (#13938)

    Agent Playground tab provides a side-by-side environment for iterating on agent configuration (instructions, tools, model settings) and testing changes in a live chat — without modifying the deployed agent. Includes version comparison, request context configuration, and the ability to trigger dataset experiments directly from the playground.

    Agent Traces tab shows a compact table of all agent traces with columns for status, timestamp, input preview, output preview, and duration. Supports date range filtering, infinite scroll pagination, and clicking rows to inspect full trace details. Includes checkbox selection and bulk "Add to dataset" for quickly building evaluation datasets from production traces.

    Tools edit page now shows configured (enabled) tools in a dedicated section at the top, making it easier to find and edit tools that are already in use.

    Dataset save actions on the test chat: per-message save button on user messages and a "Save full conversation" action at the bottom of the thread.

  • Added input message preview column to the observability trace list. You can now see a truncated preview of user input messages directly in the trace table, making it easier to find specific traces without clicking into each one. (#14025)

Patch Changes
  • The "Run Experiment" button is now disabled when a dataset has no items, with a tooltip explaining that items must be added first. (#14031)

  • fix: maxTokens from Studio Advanced Settings now correctly limits model output (#13912)

    The modelSettingsArgs object was prematurely renaming maxTokens to maxOutputTokens. The React hook (useChat) destructures maxTokens from this object, so the rename caused it to receive undefined, and the value was silently dropped from the API request body.

  • Added permission denied handling for dataset pages. Datasets now show a "Permission Denied" screen when the user lacks access, matching the behavior of agents, workflows, and other resources. (#13876)

  • Fixed sandbox execution badge to show full streaming output during live sessions instead of snapping to the truncated tool result. The truncated view now only appears after page refresh when streaming data is no longer available. (#13869)

  • Fix the observational memory sidebar so the observation token label uses the live observation window token count shown by the progress UI. (#13953)

@mastra/react@0.2.10
Patch Changes
  • Fixed @mastra/react root imports so Vite and other browser builds no longer pull in Node-only @mastra/core code. (#14079)
@mastra/schema-compat@1.2.0
Minor Changes
  • Add Zod v4 and Standard Schema support (#12238)

    Zod v4 Breaking Changes

    • Fix all z.record() calls to use 2-argument form (key + value schema) as required by Zod v4
    • Update ZodError.errors to ZodError.issues (Zod v4 API change)
    • Update @ai-sdk/provider versions for Zod v4 compatibility

    Standard Schema Integration

    • Add packages/core/src/schema/ module that re-exports from @mastra/schema-compat
    • Migrate codebase to use PublicSchema type for schema parameters
    • Use toStandardSchema() for normalizing schemas across Zod v3, Zod v4, AI SDK Schema, and JSON Schema
    • Use standardSchemaToJSONSchema() for JSON Schema conversion

    Schema Compatibility (@mastra/schema-compat)

    • Add new adapter exports: @mastra/schema-compat/adapters/ai-sdk, @mastra/schema-compat/adapters/zod-v3, @mastra/schema-compat/adapters/json-schema
    • Enhance test coverage with separate v3 and v4 test suites
    • Improve zod-to-json conversion with unrepresentable: 'any' support

    TypeScript Fixes

    • Resolve deep instantiation errors in client-js and model.ts
    • Add proper type assertions where Zod v4 inference differs

    BREAKING CHANGE: Minimum Zod version is now ^3.25.0 for v3 compatibility or ^4.0.0 for v4

Patch Changes
  • Fixed Gemini supervisor agent tool calls failing with INVALID_ARGUMENT when delegated tool schemas include nullable fields. Fixes #13988. (#14012)

  • Fixed OpenAI and OpenAI Reasoning compat layers to ensure all properties appear in the JSON Schema required array when using processToJSONSchema. This prevents OpenAI strict mode rejections for schemas with optional, default, or nullish fields. (Fixes #12284) (#13695)

@mastra/server@1.11.0
Minor Changes
  • Added onValidationError hook to ServerConfig and createRoute(). When a request fails Zod schema validation (query parameters, request body, or path parameters), this hook lets you customize the error response — including the HTTP status code and response body — instead of the default 400 response. Set it on the server config to apply globally, or on individual routes to override per-route. All server adapters (Hono, Express, Fastify, Koa) support this hook. (#13477)

    const mastra = new Mastra({
      server: {
        onValidationError: (error, context) => ({
          status: 422,
          body: {
            ok: false,
            errors: error.issues.map(i => ({
              path: i.path.join('.'),
              message: i.message,
            })),
            source: context,
          },
        }),
      },
    });
  • Added requestContext field to dataset item API endpoints and requestContextSchema to dataset CRUD endpoints. Added requestContext option to the experiment trigger endpoint, which gets forwarded to agent execution during experiments. (#13938)

    Usage with @mastra/client-js:

    // Create a dataset with a request context schema
    await client.createDataset({
      name: 'my-dataset',
      requestContextSchema: {
        type: 'object',
        properties: { region: { type: 'string' } },
      },
    });
    
    // Add an item with request context
    await client.addDatasetItem({
      datasetId: 'my-dataset',
      input: { prompt: 'Hello' },
      requestContext: { region: 'us-east-1' },
    });
    
    // Trigger an experiment with request context forwarded to agent
    await client.triggerDatasetExperiment({
      datasetId: 'my-dataset',
      agentId: 'my-agent',
      requestContext: { region: 'eu-west-1' },
    });
Patch Changes
  • Experiments now fail immediately with a clear error when triggered on a dataset with zero items, instead of getting stuck in "pending" status forever. The experiment trigger API returns HTTP 400 for empty datasets. Unexpected errors during async experiment setup are now logged and mark the experiment as failed. (#14031)

  • Fixed getPublicOrigin to parse only the first value from the X-Forwarded-Host header. When requests pass through multiple proxies, each proxy appends its host to the header, creating a comma-separated list. The previous code used the raw value, producing a malformed URL that broke OAuth redirect URIs. Now only the first (client-facing) host is used, per RFC 7239. (#13935)

@mastra/upstash@1.0.4
Patch Changes
  • Improved semantic recall performance for large message histories. Semantic recall no longer loads entire threads when only the recalled messages are needed, eliminating delays that previously scaled with total message count. (Fixes #11702) (#14022)
@mastra/voice-google-gemini-live@0.11.3
Patch Changes
  • Add new models to GeminiVoiceModel type and mark deprecated models with @deprecated JSDoc. (#12625)

    Added:

    • gemini-live-2.5-flash-native-audio (GA)
    • gemini-live-2.5-flash-preview-native-audio-09-2025
    • gemini-2.5-flash-native-audio-preview-12-2025
    • gemini-2.5-flash-native-audio-preview-09-2025

    Deprecated:

    • gemini-2.0-flash-exp (shut down 2025-12-09)
    • gemini-2.0-flash-exp-image-generation (shut down 2025-11-14)
    • gemini-2.0-flash-live-001 (shut down 2025-12-09)
    • gemini-live-2.5-flash-preview-native-audio (use gemini-live-2.5-flash-preview-native-audio-09-2025)
    • gemini-2.5-flash-exp-native-audio-thinking-dialog (shut down 2025-10-20)
    • gemini-live-2.5-flash-preview (shut down 2025-12-09)
@mastra/voice-modelslab@0.1.0
Minor Changes
  • Initial release: ModelsLab TTS voice provider for Mastra
Other updated packages

The following packages were updated with dependency changes only:

@mastra/core@1.10.0

Highlights

Tool inputExamples to improve model tool-call accuracy

Tool definitions can now include inputExamples, which are passed through to models that support them (e.g., Anthropic’s input_examples) to demonstrate valid inputs and reduce malformed tool calls.

MCP client fetch hooks now receive RequestContext (auth/cookie forwarding)

@mastra/mcp adds requestContext support to custom fetch functions for MCP HTTP server definitions, enabling request-scoped forwarding of cookies/bearer tokens during tool execution while remaining backward compatible with (url, init) fetch signatures.

Reliability + DX fixes for agents, streaming, and memory cleanup

Provider stream errors are now consistently surfaced from generate()/resumeGenerate(), AI SDK errors are routed through the Mastra logger with structured context, client-side tools no longer lose history in stateless deployments, and memory.deleteThread()/deleteMessages() now automatically cleans up orphaned vector embeddings across supported vector stores.

Changelog

@mastra/core@1.10.0
Minor Changes
  • Add inputExamples support on tool definitions to show AI models what valid tool inputs look like. Models that support this (e.g., Anthropic's input_examples) will receive the examples alongside the tool schema, improving tool call accuracy. (#12932)

    • Added optional inputExamples field to ToolAction, CoreTool, and Tool class
    const weatherTool = createTool({
      id: "get-weather",
      description: "Get weather for a location",
      inputSchema: z.object({
        city: z.string(),
        units: z.enum(["celsius", "fahrenheit"])
      }),
      inputExamples: [{ input: { city: "New York", units: "fahrenheit" } }, { input: { city: "Tokyo", units: "celsius" } }],
      execute: async ({ city, units }) => {
        return await fetchWeather(city, units);
      }
    });
Patch Changes
  • dependencies updates: (#13209)

  • dependencies updates: (#13210)

  • Update provider registry and model documentation with latest models and providers (33e2fd5)

  • Fixed execute_command tool timeout parameter to accept seconds instead of milliseconds, preventing agents from accidentally setting extremely short timeouts (#13799)

  • Skill tools are now stable across conversation turns and prompt-cache friendly. (#13744)

    • Renamed skill-activateskill — returns full skill instructions directly in the tool result
    • Consolidated skill-read-reference, skill-read-script, skill-read-assetskill_read
    • Renamed skill-searchskill_search
    • <available_skills> in the system message is now sorted deterministically
  • Fixed Cloudflare Workers build failures when using @mastra/core. Local process execution now loads its runtime dependency lazily, preventing incompatible Node-only modules from being bundled during worker builds. (#13813)

  • Fix mimeTypemediaType typo in sendMessage file part construction. This caused file attachments to be routed through the V4 adapter instead of V5, preventing them from being correctly processed by AI SDK v5 providers. (#13833)

  • Fixed onIterationComplete feedback being discarded when it returns { continue: false } — feedback is now added to the conversation and the model gets one final turn to produce a text response before the loop stops. (#13759)

  • Fixed generate() and resumeGenerate() to always throw provider stream errors. Previously, certain provider errors were silently swallowed, returning false "successful" empty responses. Now errors are always surfaced to the caller, making retry logic reliable when providers fail transiently. (#13802)

  • Remove the default maxSteps limit so stopWhen can control sub-agent execution (#13764)

  • Fix suspendedToolRunId required error when it shouldn't be required (#13722)

  • Fixed subagent tool defaulting maxSteps to 50 when no stop condition is configured, preventing unbounded execution loops. When stopWhen is set, maxSteps is left to the caller. (#13777)

  • Fixed prompt failures by removing assistant messages that only contain sources before model calls. (#13790)

    • Fixed experiment pending count showing negative values when experiments are triggered from the Studio (#13831)
    • Fixed scorer prompt metadata (analysis context, generated prompts) being lost when saving experiment scores
  • Fixed RequestContext constructor crashing when constructed from a deserialized plain object. (#13856)

  • Fixed LLM errors (generateText, generateObject, streamText, streamObject) being swallowed by the AI SDK's default handler instead of being routed through the Mastra logger. Errors now appear with structured context (runId, modelId, provider, etc.) in your logger, and streaming errors are captured via onError callbacks. (#13857)

  • Fixed workspace tool output truncation so it no longer gets prematurely cut off when short lines precede a very long line (e.g. minified JSON). Output now uses the full token budget instead of stopping at line boundaries, resulting in more complete tool results. (#13828)

  • Fixed subagent tool to default maxSteps to 50 when no stopWhen condition is configured, preventing unbounded agent loops. When stopWhen is set, maxSteps remains unset so the stop condition controls termination. (#13777)

@mastra/agent-builder@1.0.10
Patch Changes
@mastra/ai-sdk@1.1.2
Patch Changes
@mastra/arize@1.0.6
Patch Changes
@mastra/auth@1.0.1
Patch Changes
@mastra/auth-clerk@1.0.1
Patch Changes
@mastra/auth-cloud@1.1.1
Patch Changes
@mastra/auth-workos@1.1.1
Patch Changes
@mastra/braintrust@1.0.6
Patch Changes
@mastra/client-js@1.7.3
Patch Changes
@mastra/couchbase@1.0.2
Patch Changes
@mastra/datadog@1.0.5
Patch Changes
@mastra/deployer@1.10.0
Patch Changes
@mastra/deployer-cloud@1.10.0
Patch Changes
@mastra/deployer-cloudflare@1.1.7
Patch Changes
@mastra/deployer-netlify@1.0.11
Patch Changes
@mastra/deployer-vercel@1.1.1
Patch Changes
@mastra/elasticsearch@1.1.2
Patch Changes
@mastra/express@1.1.9
Patch Changes
@mastra/fastembed@1.0.1
Patch Changes
  • dependencies updates: (#10195)

  • Add warmup() export to pre-download fastembed models without creating ONNX sessions. This prevents concurrent download race conditions when multiple consumers call FlagEmbedding.init() in parallel, which could corrupt the model archive and cause Z_BUF_ERROR. (#13752)

@mastra/fastify@1.1.9
Patch Changes
@mastra/hono@1.1.9
Patch Changes
@mastra/koa@1.2.5
Patch Changes
@mastra/laminar@1.0.5
Patch Changes
@mastra/langfuse@1.0.5
Patch Changes
@mastra/langsmith@1.1.3
Patch Changes
@mastra/libsql@1.6.4
Patch Changes
@mastra/longmemeval@1.0.11
Patch Changes
@mastra/mcp@1.1.0
Minor Changes
  • Added requestContext support to the custom fetch option in MCP client HTTP server definitions. The fetch function now receives the current request context as an optional third argument, enabling users to forward authentication cookies, bearer tokens, and other request-scoped data from the incoming request to remote MCP servers during tool execution. (#13773)

    Example usage:

    const mcp = new MCPClient({
      servers: {
        myServer: {
          url: new URL("https://api.example.com/mcp"),
          fetch: async (url, init, requestContext) => {
            const headers = new Headers(init?.headers);
            const cookie = requestContext?.get("cookie");
            if (cookie) {
              headers.set("cookie", cookie);
            }
            return fetch(url, { ...init, headers });
          }
        }
      }
    });

    This change is fully backward-compatible — existing fetch functions with (url, init) signatures continue to work unchanged. Closes #13769.

Patch Changes
@mastra/mcp-docs-server@1.1.8
Patch Changes
@mastra/memory@1.6.1
Patch Changes
  • Improved confidence in observational memory threshold behavior through expanded automated test coverage. No public API changes. (#13785)

  • Improved Observational Memory reliability: fixed future-date annotations producing invalid strings, fixed a duplicate inline-date matching bug, and hardened the process-level operation registry against concurrent operation tracking errors. (#13774)

  • Fixed buffered activation cleanup to respect the configured retention floor so message history does not collapse unexpectedly after activation. (#13745)

  • Fixed orphaned vector embeddings accumulating when memory threads or messages are deleted. Calling memory.deleteThread() or memory.deleteMessages() now automatically cleans up associated vector embeddings across all supported vector store backends. Cleanup is non-blocking and does not slow down the delete call. Also fixed updateMessages not cleaning up old vectors correctly when using a non-default index separator (e.g. Pinecone). (#12227)

  • Repeated token counts in OM are faster and more reliable, estimates are now persisted on metadata, and totals remain consistent after saving and loading conversations. (#13745)

  • Improved observational memory marker creation consistency for more reliable debugging and UI status behavior. No public API changes. (#13779)

  • Fixed observational memory token counting to use stored model output for tool results transformed with toModelOutput. (#13862)

  • Fix working memory data corruption when using resource scope across threads (#12415)

    • Add mutex protection to updateWorkingMemory() to prevent race conditions during concurrent updates
    • Add normalized whitespace comparison to __experimental_updateWorkingMemoryVNext() to detect template duplicates with whitespace variations
    • Add validation to updateWorkingMemoryTool to prevent LLM from accidentally wiping existing data by sending empty template
    • Improve template removal logic to handle line ending variations
  • Updated dependencies [41e48c1, 82469d3, 33e2fd5, 7ef6e2c, b12d2a5, fa37d39, b12d2a5, 71c38bf, f993c38, f51849a, 9bf3a0d, cafa045, 1fd9ddb, 6135ef4, d9d228c, 5576507, 79d69c9, 94f44b8, 13187db, 2ae5311, 6135ef4]:

    • @mastra/core@1.10.0
@mastra/mongodb@1.5.4
Patch Changes
@mastra/observability@1.3.1
Patch Changes
@mastra/opencode@0.0.8
Patch Changes
@mastra/otel-bridge@1.0.5
Patch Changes
@mastra/otel-exporter@1.0.5
Patch Changes
@mastra/pg@1.7.2
Patch Changes
@mastra/playground-ui@15.2.0
Patch Changes
  • dependencies updates: (#13236)

  • dependencies updates: (#13271)

  • dependencies updates: (#13283)

  • dependencies updates: (#13771)

  • dependencies updates: (#13847)

  • Fixed experiment results page showing only 10 items, empty summary tab with no scorers, and scores not updating during experiment runs. (#13831)

  • Updated skill activation indicators to match new skill tool names. (#13744)

  • Fix saving traces and scores as dataset items in the Studio. (#13800)

    • Traces with structured message content (e.g. multi-part content arrays) can now be saved as dataset items without validation errors
    • Score dialog now has a "Save as Dataset Item" button for scorer calibration workflows, pre-filling scorer input/output and expected score
    • Dataset output schema updated to match the full experiment output shape (text, object, toolCalls, files, usage, etc.)
  • Fixed Playground UI agent settings so temperature is no longer reset to 1 on refresh. Temperature now stays unset unless saved settings or code defaults provide a value. (#13778)

  • Fixed documentation link in empty datasets page pointing to the correct datasets docs instead of evals (#13872)

  • Fix wrong threads showing for agents on studio (#13789)

  • Fixed dev playground auth bypass not working in capabilities endpoint. The client now passes MastraClient headers (including x-mastra-dev-playground) to the auth capabilities endpoint, and the server returns disabled state when this header is present. This prevents the login gate from appearing in dev playground mode. (#13801)

  • Updated dependencies [41e48c1, 82469d3, 33e2fd5, 7ef6e2c, 88061a8, b12d2a5, fa37d39, b12d2a5, 71c38bf, f993c38, f51849a, 9bf3a0d, cafa045, 1fd9ddb, d1e26f0, 6135ef4, d9d228c, 5576507, 79d69c9, 94f44b8, 13187db, 2ae5311, 6135ef4]:

    • @mastra/core@1.10.0
    • @mastra/ai-sdk@1.1.2
    • @mastra/client-js@1.7.3
    • @mastra/react@0.2.9
@mastra/posthog@1.0.6
Patch Changes
@mastra/react@0.2.9
Patch Changes
@mastra/sentry@1.0.5
Patch Changes
@mastra/server@1.10.0
Patch Changes
Last Checked
4h ago
Latest
@mastra/core@1.36.0
Tracking since Apr 8, 2025