github mastra-ai/mastra @mastra/core@1.37.0
May 27, 2026

4 hours ago

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 #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:

Don't miss a new mastra release

NewReleases is sending notifications on new releases.