@mastra/core@1.78.0

mastra-ai/mastra@mastra/core@1.78.0Oct 16, 2026by PaulieScanlon

Upgrading?

AI Summary

A major release introducing Agent Scopes for multi-tenant identity, a Trace Aggregation API, and typed signal queueing. Performance is improved with stored previews for traces and spans, and workflow reliability is enhanced for long-running executions.

Key Highlights

  • Agent Scopes support for multi-tenant identity and safer memory/thread routing via <type>:<value> formats.
  • Trace Aggregation API (server, client, and React) for end-to-end trace grouping and measurement.
  • Typed Signal Queueing allows agents to queue signals without degrading them into plain user messages.
  • Faster trace lists due to stored input/output/error previews reducing payload reads.
  • Better reliability for long-running workflows with improved `.foreach()` and durable agent support.

Breaking Changes

  • Deprecated `buildInputPreview()` and `INPUT_PREVIEW_MAX_LENGTH` in favor of `buildPreview()` and `PREVIEW_MAX_LENGTH`.
  • Removed Plan/Explore modes from Factory sessions; thinking defaults now use a single global setting.
  • Removed cross-scope credential fallback; custom credential providers must use explicit `credentialScope` or policy resolution.

New Features

  • Agent Scopes configuration per call, agent, or middleware (MASTRA_SCOPES_KEY).
  • Trace aggregation capabilities and schema discovery via API endpoints.
  • Server route `POST /agents/:agentId/queue-signal` for typed signaling.
  • Preview storage in ClickHouse, Postgres, and DuckDB stores.
  • Tool approval hook (`beforeApproval`) to reject calls before pausing.
  • Removed 31-day limit on trace search time ranges.

Full Release Notes

## Highlights

### Agent Scopes (multi-tenant identity + safer memory/thread routing)
Agents now support `scopes` (`<type>:<value>`) passed per call, configured on the agent, or injected by trusted middleware via `MASTRA_SCOPES_KEY`; Mastra merges all three. Memory can derive `resource` and `thread` from `resource:`/`thread:` scopes (with conflicts now returning HTTP 400), nested calls can choose their own thread, and resumed runs preserve their original scopes (no adding new ones).

### Trace Aggregation API (server + client + React)
Mastra adds trace aggregation end-to-end: `POST /observability/traces/aggregate` is discoverable via `GET /observability/capabilities` (`traceAggregate`), clients get `mastraClient.aggregateTraces()`, and React adds `useTraceAggregateAvailable()`. Field discovery now returns an `aggregate` schema describing valid group-by dimensions and measures.

### Typed Signal Queueing for Agents (`queueSignal`)
You can now queue a typed signal behind the active run—without degrading it into a plain user message—via `Agent.queueSignal()` / session support and the new server route `POST /agents/:agentId/queue-signal`. Runs triggered by a signal also record signal metadata on the `AGENT_RUN` span for better traceability.

### Faster, more usable trace lists with stored input/output/error previews
Trace and span list APIs now return `outputPreview` and `errorPreview` (and spans add `errorTruncated`), backed by a shared preview format in `@mastra/core/storage` (`buildPreview`, `PREVIEW_MAX_LENGTH`). Stores (ClickHouse/Postgres/DuckDB) persist previews at write time so `queryTraces()`/`querySpans()` no longer need to read full payloads—improving latency, memory use, and enabling UIs like Playground’s new Traces “Output” column.

### Better reliability for long-running & durable workflows
Workflow execution is more robust: `.foreach()` failures now correctly fail the run (instead of hanging or corrupting output), durable agents are correctly honored inside workflow steps across engines, resume behavior preserves `suspendData` after crashes, and invalid resume data now throws `AGENT_RESUME_INVALID_DATA` without consuming the suspended run.

### Breaking Changes
- `buildInputPreview()` and `INPUT_PREVIEW_MAX_LENGTH` are deprecated in favor of `buildPreview()` and `PREVIEW_MAX_LENGTH`.
- Factory sessions no longer have modes (Plan/Explore removed); thinking defaults and related config endpoints now use a single global default, and `PUT /web/config/thinking` rejects `modeDefaults`.
- Factory credential behavior changed: cross-scope credential fallback is removed; custom credential providers must move from `orgFirst` to explicit `credentialScope` / policy resolution.

## Changelog

### [@mastra/core@1.78.0](https://github.com/mastra-ai/mastra/blob/@mastra/core@1.78.0/packages/core/CHANGELOG.md)
#### Minor Changes

- Added `scopes` to agents: the addresses a run acts as, written as `<type>:<value>` (for example `org:acme`, `resource:user-123`, `thread:conversation-123`). (#26593)

  **Setting scopes.** Pass them per call on `stream`, `generate`, `network`, `sendMessage`, `queueMessage` and `sendSignal`, set them on the agent, or set them from trusted middleware with the new `MASTRA_SCOPES_KEY` request context key. Mastra combines all three. Read an agent's configured scopes with `agent.getScopes()`.

  **Memory.** Memory reads its resource and thread from `resource:` and `thread:` scopes, in addition to `memory.resource`, `memory.thread` and the existing request context keys. If they name different values, the call throws a 400 error instead of silently picking one. Runs with no scopes from any source behave as before.

  ```ts
  const agent = new Agent({
    id: 'support',
    model: 'openai/gpt-5-mini',
    memory: new Memory(),
    scopes: ({ requestContext }) => {
      // Set by your auth middleware. Request bodies can fill unreserved keys, so only read trusted ones.
      const orgId = requestContext.get('org-id');
      return orgId ? [`org:${orgId}`] : [];
    },
  });

  // Before
  await agent.generate('Hi', { memory: { resource: 'user-123', thread: 'conversation-123' } });

  // After
  await agent.generate('Hi', { scopes: ['resource:user-123', 'thread:conversation-123'] });
  ```

  **Nested calls.** Tools, processors and subagents see the run's other scopes (for example `org:acme`) under `MASTRA_SCOPES_KEY`, but not its `resource:` and `thread:` scopes, so nested agent calls can choose their own thread.

  **Suspended runs.** Suspended runs keep their scopes when resumed: a resume may repeat some or all of them but cannot add new ones.

  **Upgrading.** Upgrade `@mastra/server` together with this release. While an older server is registered, agents refuse scopes from the call and the request context with an `AGENT_SCOPES_SERVER_OUTDATED` error (HTTP 400); the agent's own `scopes` option still applies. This detection relies on the server registering itself with Mastra, which `@mastra/server` has done since `1.0.0-beta.5`. Servers older than that — and custom server adapters that never call `mastra.setMastraServer()` — are indistinguishable from no server at all, so they are not detected: upgrade them rather than relying on the guard.

  Runs suspended before this release have no recorded scope set; resuming one adopts the scopes supplied at resume time (the same trust checks as a fresh run apply) instead of rejecting them. Runs suspended after the upgrade record their set — including the empty set — and a resume may not add to it.

- Added `traceAggregateDiscoverySchema` and an optional `aggregate` field on the trace-query fields response, describing the dimensions and measures `aggregateTraces()` accepts. (#27185)

- Added `queueSignal()` to `Agent` and AgentController `Session`. It queues a typed signal behind the active run like `queueMessage()`, but keeps the signal type, tag, and attributes instead of turning it into a plain user message. (#27081)

  ```ts
  agent.queueSignal(
    { type: 'reactive', tagName: 'skill', attributes: { name: 'triage' }, contents: 'Triage the new issue.' },
    { resourceId: 'user-123', threadId: 'thread-abc' },
  );
  ```

  Runs started by a signal now record it on the `AGENT_RUN` span as `attributes.signal` (`id`, `type`, `tagName`, `attributes`), so traces show which signal started a run.

- Memory can now contribute error processors to an agent. `MastraMemory.getErrorProcessors()` works like `getInputProcessors()` and `getOutputProcessors()`: the agent appends what it returns to its resolved `errorProcessors`, on both regular and durable agents. These processors run after configured and default error processors, and stay in place when a call passes its own `errorProcessors`. The base implementation returns none, so existing memory classes are unaffected. (#26029)

  ```ts
  class MyMemory extends MastraMemory {
    async getErrorProcessors() {
      return [new MyRecoveryProcessor()];
    }
  }
  ```

- Added one preview format for span input, output and error. Trace lists (`queryTraces()`, `listTracesLight()`) now return `outputPreview` and `errorPreview` next to `inputPreview`, and `querySpans()` rows add `errorPreview` and `errorTruncated`. (#26567)

  **New exports from `@mastra/core/storage`**

  - `buildPreview(value)` builds a preview from a decoded payload, and `buildPreviewFromJson(json)` from stored JSON text. Both cut to `PREVIEW_MAX_LENGTH` (100) characters and end with `…` when cut.
  - Exported spans and span records accept optional `inputPreview`, `outputPreview` and `errorPreview`, preserved through create/update record builders. Stores trim nonblank supplied previews and cap them like generated ones (100 characters plus `…`, never splitting a surrogate pair); missing or blank previews are generated from the payload.

  ```ts
  import { buildPreview } from '@mastra/core/storage';

  buildPreview({ messages: [{ role: 'user', content: 'Where is my order?' }] }); // 'Where is my order?'
  buildPreview({ message: 'Tool failed' }); // 'Tool failed'
  ```

  **Behavior changes**

  - For a message list, previews now show the last user message instead of every user message joined with `|`.
  - `querySpans()` previews are now at most 100 characters of readable text plus an ellipsis instead of the first 256 characters of raw JSON.

  `buildInputPreview()` and `INPUT_PREVIEW_MAX_LENGTH` are deprecated. Use `buildPreview()` and `PREVIEW_MAX_LENGTH`. `buildInputPreview(input, maxLength)` still caps its preview at `maxLength`.

- Added a `beforeApproval` hook to tools that require approval. It runs before the agent pauses for approval and can reject the call by returning `{ proceed: false, output }`. The agent then skips the approval pause and does not execute the tool, and `output` goes back to the model as the tool result. Invalid calls are returned to the model to fix, and people are only asked to approve valid ones. (#26927)

  ```ts
  const issueRefund = createTool({
    id: 'issue-refund',
    inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
    requireApproval: true,
    beforeApproval: async ({ orderId, amount }) => {
      const order = await orders.find(orderId);
      if (!order) return { proceed: false, output: { error: `Order ${orderId} not found` } };
      if (amount > order.paid) return { proceed: false, output: { error: `Amount exceeds ${order.paid}` } };
    },
    execute: async ({ orderId, amount }) => refunds.issue(orderId, amount),
  });
  ```

  The hook does not run again when the call resumes after an approval decision. If the hook throws, the error is logged and the call proceeds to the approval gate. Keep validating inside `execute` too, because state can change while the call is waiting for approval.

- Removed the 31-day limit on trace search time ranges. You can now search all of your retained trace data, as long as the range starts before it ends. (#27042)

  ```ts
  // Before: rejected because the range is longer than 31 days
  // After: returns traces from the whole six-month range
  await mastraClient.queryTraces({
    timeRange: { from: '2026-04-01T00:00:00Z', to: '2026-10-01T00:00:00Z' },
  });
  ```

#### Patch Changes

- Update provider registry and model documentation with latest models and providers (`bed1ccc`)

- Fixed `resumeGenerate()` and `resumeStream()` using up a suspended run when the resume data does not match the tool's `resumeSchema`. They now throw `AGENT_RESUME_INVALID_DATA` and the run stays suspended, so you can resume again with valid data. (#27130)

- Fixed trace trees for agents that use output stream processors. In multi-step runs, each step's output stream processor span now sits inside that step and also covers the step's tool results, instead of every span showing up as a flat list under the agent run. Structured output processor spans stay under the agent run, because structured output runs once on the final result. (#27172)

- Fixed live assistant messages missing the model that wrote them. Messages streamed by an `AgentController` session now carry `modelId` and `provider` in their metadata, so a client can show the model without reloading the thread. After a fallback, they name the fallback model. (#27030)

- Fixed model inference spans so they record the messages sent to the model. Observability backends that read the inference span, such as Langfuse through the OpenTelemetry exporter or Datadog, now show the input for every model call instead of an empty value. (#26995)

- Durable agent turns no longer create an unused tool stream writer on every LLM step. A writer is now created only when a step actually uses it. (#26987)

- Fixed `.foreach()` on the evented workflow engine when an iteration fails. The run now ends with `status: 'failed'` and the iteration's error. Before, it hung forever (`run.start()` never resolved) or, with `concurrency: 1`, finished as `success` with a corrupted output array. Iterations that finish after the failure no longer mark the foreach step successful. (#26971)

- Fixed workflow agent steps to support durable agents on both execution engines. (#26919)

- Fixed full agent output repeating earlier text after a feedback continuation. (#26543)

- Fixed a resumed workflow step losing its `suspendData` when the run restarts after a crash. The step now gets the payload it passed to `suspend()` again, alongside its `resumeData`. (#27127)

- Disabled workspace tools no longer add setup work on every agent turn. Agents with workspace tools turned off now start each turn faster and allocate less memory. Fixes #26401. (#26657)

- Fixed Amazon Bedrock India inference profiles (`in.` prefix, e.g. `in.anthropic.claude-sonnet-5`) sending `temperature` to models that reject it, which returned a 400. They now resolve model capabilities like the other geography prefixes. Fixes #26387. (#26982)

- Fixed step scorers in nested workflows so their scores are saved. Before, scores from steps inside a child workflow were silently dropped. (#26984)

- Fixed agent runs crashing with `RangeError: Invalid string length` when a command printed a very large amount of output. `sandbox.executeCommand()` now keeps the newest 1 MiB of stdout and stderr by default, the same limit spawned processes already use. Streaming callbacks still receive every chunk, and the result reports `stdoutTruncated` and `stdoutDroppedBytes` (and the stderr equivalents) when output was dropped. (#27106)

  If you need the complete output, pass `maxRetainedBytes: Infinity`:

  ```ts
  // Before: all output was kept by default
  const result = await sandbox.executeCommand('cat', ['data.json']);

  // After: pass Infinity to keep all output
  const result = await sandbox.executeCommand('cat', ['data.json'], { maxRetainedBytes: Infinity });
  ```

  Complete output is limited to 256 MiB per stream. A command that prints more is killed, and `executeCommand()` (or `handle.wait()`) rejects with `ProcessOutputLimitError` instead of crashing the process.

  The `execute_command` and `get_process_output` tools now tell the model when the start of the output was dropped, so it doesn't mistake the newest output for all of it.

- Fixed document citations disappearing after a reload. When a model streams a document source (for example an Anthropic PDF citation), memory now stores it as a `source-document` part with its media type, title and filename. Previously it was saved as a URL source with an empty `url`, so reloaded conversations showed an empty link instead of the cited document. (#26805)

  ```ts
  const { messages } = await memory.recall({ threadId, resourceId });
  // assistant parts now include:
  // { type: 'source-document', sourceId: 'doc-1', mediaType: 'application/pdf', title: 'Q3 report', filename: 'q3.pdf' }
  ```

  AI SDK v5 UI messages now also keep `source-document` parts and the `sourceId` and `title` of URL sources when converting to and from stored messages.

- Fixed durable recovery observers hanging when recovery fails before the stream starts. (#26918)

- Fixed `resumeGenerate()` and `resumeStream()` saving messages to the thread when `memory.options.readOnly` is `true`. Resuming a suspended run now leaves read-only threads untouched, matching `generate()` and `stream()`. (#27056)

- Fixed background task events leaking across resources. Previously, calling `BackgroundTaskManager.stream()` without a `resourceId` delivered every resource's task events, including tool arguments and results. Without a `resourceId`, `stream()` now only delivers tasks that have no resource. To subscribe to every resource, pass `allResources: true`: (#26895)

  ```ts
  const events = mastra.backgroundTaskManager.stream({ agentId: 'my-agent', allResources: true });
  ```

  The `/background-tasks`, `/background-tasks/stream` and `/background-tasks/:backgroundTaskId` routes now use the resource from the request context. A resource-scoped caller only sees its own tasks: requesting a different `resourceId` returns 403, and fetching another resource's task returns 404. An empty resource id in the request context is rejected with 403. Requests without a resource in the request context keep access to all resources.

  _Also in: @mastra/server@1.78.0_

- Fixed agent controller channel sessions losing the sender's identity on the first message in a new chat thread. A channel session now opens directly on its chat thread, keeping the inbound request context, instead of creating an extra empty thread and switching over without it. Hosts that resolve memory per user from the request context's `user` no longer fail on a new thread. (#27113)

- Fixed prompt caching after a suspended or tool-approval run resumes from Postgres storage. (#26943)

  Tool-call arguments and JSON tool results in model prompts now use sorted object keys, so the same content produces the same request after a run resumes. Stored messages are unchanged.

- Fixed new conversation failures when ToolSearchProcessor uses context storage with memory. (#26902)

- Fixed agents sending the same request until `maxSteps` when a text-only response ended with a non-standard finish reason, such as `other`. The run now ends after that response, so the text and token usage are no longer repeated. (#26905)

- Fixed two OpenAI errors with tool inputs and structured output: (#26979)

  - Optional tool inputs that accept more than one type can now be left empty. Before, OpenAI models filled them with made-up values.
  - Structured output with a field that has free-form keys, such as `z.record(z.string(), z.string())`, now works on OpenAI instead of failing with a 400 error. These schemas are sent without strict mode. To keep strict mode anyway, set `providerOptions.openai.strictJsonSchema: true`.

  _Also in: @mastra/schema-compat@1.3.15_

- Fixed `createStep(agent)` running a `durable: true` agent as a plain agent. When the agent is registered with Mastra, the workflow step now runs through the registered durable wrapper on both the default and evented engines, so durable streaming and cleanup apply as expected. (#27078)

  A step now logs a warning when a `durable: true` agent still has to run non-durably: when no durable wrapper for it is registered on the workflow's Mastra instance, or when it uses a legacy v1 model.

- Fixed token estimates for long numbers. A long number used to count as one token, so output full of numbers could grow far past memory and token limits without triggering them. Long numbers are now counted accurately, and limits trigger at the right time. (#27208)

  Estimates saved on existing messages are recalculated, so existing conversations also get the corrected counts.

  _Also in: @mastra/memory@1.38.0_

- Fixed tool `transform` replacing stored tool-call arguments with a `Tool input-available payload unavailable` placeholder when only another phase (such as `transcript.output`) was configured. A tool-level transform now only changes the phases it defines, so stored `args` round-trip unchanged and replaying the thread with Anthropic preserved thinking no longer fails. Transformers that throw or return `undefined` still fall back to the placeholder, and raw input deltas stay suppressed. Fixes #26597. (#26965)

### [@mastra/clickhouse@1.25.1](https://github.com/mastra-ai/mastra/blob/@mastra/clickhouse@1.25.1/stores/clickhouse/CHANGELOG.md)
#### Patch Changes

- Trace aggregates now read each trace's root span once from the requested time window, instead of re-reading every stored root row of the matching traces. This lowers `aggregateTraces()` latency, bytes read, and memory, most on large projects. (#26912)

- Fixed ClickHouse deleteMessages bumping thread updatedAt with an ALTER TABLE UPDATE mutation, which rewrote whole data parts (often forbidden on managed deployments) and stamped the same updatedAt on every stored version of the thread row — making thread reads ambiguous on the ReplacingMergeTree until parts merged, so a just-renamed or archived thread could read back its previous state. The timestamp bump now inserts a strictly newer thread version, like every other thread write. Fixes #25580 (#26838)

- Trace and span lists use less memory. Every span now stores short previews of its input, output and error when it is written, so `queryTraces()`, `listTracesLight()` and `querySpans()` read those previews instead of each span's full payloads. Trace and span lists now also return `outputPreview` and `errorPreview`. Spans written before this change still show previews, built from their own payloads, even when another tenant has a root span with the same trace and span IDs. (#26567)

  `storage.init()` adds `inputPreview`, `outputPreview` and `errorPreview` columns to `mastra_span_events`, `mastra_trace_roots` and `mastra_trace_branches`. This runs automatically on first use. If you set `disableInit: true`, run `storage.init()` from your migration step before deploying, or apply the columns yourself. Add them to `mastra_trace_roots` and `mastra_trace_branches` before `mastra_span_events`, because the views that copy span events into those tables select every column:

  ```sql
  ALTER TABLE mastra_trace_roots ADD COLUMN IF NOT EXISTS inputPreview Nullable(String);
  ALTER TABLE mastra_trace_roots ADD COLUMN IF NOT EXISTS outputPreview Nullable(String);
  ALTER TABLE mastra_trace_roots ADD COLUMN IF NOT EXISTS errorPreview Nullable(String);
  ALTER TABLE mastra_trace_branches ADD COLUMN IF NOT EXISTS inputPreview Nullable(String);
  ALTER TABLE mastra_trace_branches ADD COLUMN IF NOT EXISTS outputPreview Nullable(String);
  ALTER TABLE mastra_trace_branches ADD COLUMN IF NOT EXISTS errorPreview Nullable(String);
  ALTER TABLE mastra_span_events ADD COLUMN IF NOT EXISTS inputPreview Nullable(String);
  ALTER TABLE mastra_span_events ADD COLUMN IF NOT EXISTS outputPreview Nullable(String);
  ALTER TABLE mastra_span_events ADD COLUMN IF NOT EXISTS errorPreview Nullable(String);
  ```

  On a replicated cluster, add `ON CLUSTER <cluster>` after each table name.

  Nonblank supplied previews are trimmed and capped like generated ones (100 characters plus `…`). Missing or blank write-time previews are generated through the optional core helper. Stored empty strings do not trigger read fallback. With older core versions that lack the helper, preview fields pass through unchanged without generation.

- Reduced the memory used by `queryTraces()` on large projects. Trace lists, numbered pages, delta polling and thread groups no longer sort every root span payload in the time range, so busy projects that previously hit ClickHouse memory limits now return results. Trace inputs and metadata are now read only for the traces on the returned page. (#26567)

  `queryTraces()` now treats each trace as having one root span. A trace that was written with more than one root span is listed once, under one of its roots, and matches root filters through any of them. Delta polling lists such a trace under the same root on every poll, and the next-page cursor still advances when a page comes back short.

### [@mastra/client-js@1.55.0](https://github.com/mastra-ai/mastra/blob/@mastra/client-js@1.55.0/client-sdks/client-js/CHANGELOG.md)
#### Minor Changes

- Added `aggregateTraces()` to group and measure traces from the client. Check `capabilities.traceAggregate` from `getObservabilityCapabilities()` before calling it. (#27185)

  ```typescript
  const { rows } = await mastraClient.aggregateTraces({
    timeRange: { from: '2026-08-01T00:00:00.000Z', to: '2026-08-08T00:00:00.000Z' },
    groupBy: ['entityName'],
    measures: ['count', 'duration.p95'],
  });
  ```

- Added `agent.queueSignal()` for queuing a typed signal behind the active thread run. (#27081)

  ```ts
  await agent.queueSignal({
    signal: { type: 'reactive', tagName: 'skill', attributes: { name: 'triage' }, contents: 'Triage the new issue.' },
    resourceId: 'user-123',
    threadId: 'thread-abc',
  });
  ```

#### Patch Changes

- Added an optional `traceRetentionDays` to the observability capabilities type and response schema, for hosts that serve `GET /observability/capabilities` themselves and know how long traces are kept. Clients can use it to stop users picking dates whose traces are already gone. Mastra's own server and built-in stores don't report a retention, so the field is omitted there. (#27166)

  ```ts
  const { capabilities } = await client.getObservabilityCapabilities();
  capabilities.traceRetentionDays; // e.g. 180, or undefined
  ```

  _Also in: @mastra/server@1.78.0_

- Added `outputPreview` and `errorPreview` to trace list rows, and `errorPreview` and `errorTruncated` to `querySpans()` rows. (#26567)

  _Also in: @mastra/react@1.11.0_

### [@mastra/cloudflare@1.7.4](https://github.com/mastra-ai/mastra/blob/@mastra/cloudflare@1.7.4/stores/cloudflare/CHANGELOG.md)
#### Patch Changes

- `storage.prune()` now deletes durable agent run fencing records. Configure `retention.workflows.runOwnership` (by inactivity) and `retention.memory.runFences` (by time since retirement) to stop keeping one claim and one fence record per run forever. Fences of running executions are never pruned. (#26966)

  _Also in: @mastra/cloudflare-d1@1.4.3, @mastra/dsql@1.5.3, @mastra/dynamodb@1.4.3, @mastra/mssql@1.10.4, @mastra/mysql@0.13.2, @mastra/oracledb@0.5.3, @mastra/spanner@1.9.5_

### [@mastra/code-sdk@1.14.0](https://github.com/mastra-ai/mastra/blob/@mastra/code-sdk@1.14.0/mastracode/sdk/CHANGELOG.md)
#### Minor Changes

- `createMastraCodeAgentController` now accepts `modes: false` for hosts that don't offer modes. Every session runs as the single Build agent: a thread last used in another mode reopens in Build, and per-mode thinking defaults from settings never apply. Runs without a session override use the global thinking level. (#26854)

  ```typescript
  const controller = await createMastraCodeAgentController({ modes: false });
  ```

- You can now remove agent controller built-in tools such as `submit_plan` without reaching into the controller's private config. Set `disableBuiltinTools` on `createMastraCode()` / `prepareAgentControllerMount()`, or on `MastraFactory` to apply it to every Factory session. When `submit_plan` is unavailable, plan mode tells the model to write the plan file instead of calling it. (#26967)

  ```ts
  new MastraFactory({ storage, disableBuiltinTools: ['submit_plan'] });
  ```

  _Also in: @mastra/factory@0.23.0_

- Removed cross-scope credential fallback in deployed sessions so each session uses only Factory or user credentials. (#26934)

  Custom credential store providers must replace `orgFirst` with an explicit `credentialScope`:

  ```ts
  // Before
  const tenant = { orgId, userId, orgFirst: true };

  // After
  const tenant = { orgId, userId, credentialScope: 'factory' as const };
  ```

  Use `"user"` to resolve only personal credentials. Hosts that select credentials for a session owner rather than the signed-in caller can register an asynchronous policy resolver as the second argument to `setCredentialStoreProvider`. Resolve that policy from authenticated identity and durable session records, not client-writable session state. Call `prepareCredentialTenant(requestContext)` before resolving a model; the SDK’s built-in model, memory, and goal-judge resolvers do this automatically and refresh the policy again before provider calls.

#### Patch Changes

- Fixed revoked or invalid OAuth sign-ins showing a raw API error. When Anthropic, OpenAI Codex, GitHub Copilot, xAI, or Kimi For Coding rejects a stored sign-in with a 401, the error is now a `ProviderAuthRequiredError` that says the sign-in was rejected, the same as a missing sign-in. Hosts can match on the error name to prompt the user to sign in again. (#27187)

  ```ts
  if (error.name === 'ProviderAuthRequiredError') {
    // e.g. "Anthropic rejected the stored sign-in: OAuth access token is invalid."
    promptSignIn();
  }
  ```

- Improved the "Tenant model credential policy could not be resolved" error. It now states why resolution failed: (#27138)

  - The request has no authenticated user.
  - No credential policy applies to the user.

  Added a warning with non-secret diagnostics and a stack trace. Use it to locate where the request context was lost.

- Fixed model selection for Kimi For Coding, GitHub Copilot and custom providers in `/models`, `/setup`, `/model` and `/om`. Picking a Kimi or Copilot model no longer saves an id that fails with "Could not find config for provider mastracode". (#26692)

  These models are now listed under ids that resolve:

  - Kimi: `kimi-code-plan-cn/<model>` (your Kimi login or `KIMI_API_KEY` authenticates it)
  - GitHub Copilot: `github-copilot/<model>`
  - Custom providers: `<custom-provider>/<model>`

  Ids you already saved keep working: `kimi-for-coding/<model>`, `mastracode/kimi-for-coding/<model>` and `mastracode/github-copilot/<model>`.

  A Kimi API key saved in `/api-keys` or Factory's provider settings is now used for Kimi models, and a Kimi login shows as connected in `/api-keys`.

  _Also in: @mastra/factory@0.23.0_

- Kept sandbox-backed file reads, listings and searches complete now that sandbox commands keep only the newest 1 MiB of output by default. (#27106)

- Fixed the thinking level being ignored by more providers. It now reaches Groq, Mistral, xAI, OpenRouter, Together AI, DeepInfra, Cerebras, Perplexity and Alibaba models. (#26411)

  The thinking picker lists only the levels a model's request can carry. For example, GLM-5.2 through Mistral tops out at High instead of failing the request on Max. Models whose requests cannot carry a level now offer only Off instead of levels that did nothing. That includes Z.ai, Kimi, GitHub Copilot and Amazon Bedrock models. Off still leaves each model on its own default.

- Fixed the missing provider credential error to say which credentials the Factory uses. It now names the Factory's model scope (organization or personal credentials only), notes that the other set doesn't apply, and says what to add. (#27077)

### [@mastra/convex@1.7.4](https://github.com/mastra-ai/mastra/blob/@mastra/convex@1.7.4/stores/convex/CHANGELOG.md)
#### Patch Changes

- `storage.prune()` now deletes durable agent run fencing records. Configure `retention.workflows.runOwnership` (by inactivity) and `retention.memory.runFences` (by time since retirement) to stop keeping one claim and one fence record per run forever. Fences of running executions are never pruned. (#26966)

  Redeploy your Convex functions after upgrading so the new prune operation is available.

### [@mastra/deployer@1.78.0](https://github.com/mastra-ai/mastra/blob/@mastra/deployer@1.78.0/packages/deployer/CHANGELOG.md)
#### Patch Changes

- Fixed `mastra dev` failing with "Missing subpath specifier" when the app depends on a private workspace package that has no `version` in its `package.json`. Fixes #26321. (#26991)

### [@mastra/deployer-cloudflare@1.2.37](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-cloudflare@1.2.37/deployers/cloudflare/CHANGELOG.md)
#### Patch Changes

- Fixed Cloudflare builds failing with `Could not resolve "@opentelemetry/api"` after `posthog-node` 5.56.1 started importing that optional package. When your project doesn't install `@opentelemetry/api`, the generated Wrangler config now points it at a stub, so `wrangler dev` and `wrangler deploy` work again. Fixes #27037. (#27079)

### [@mastra/deployer-sandbox@0.3.22](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-sandbox@0.3.22/deployers/sandbox/CHANGELOG.md)
#### Patch Changes

- Kept sandbox deploy commands and worker output reads complete now that sandbox commands keep only the newest 1 MiB of output by default. (#27106)

### [@mastra/duckdb@1.14.1](https://github.com/mastra-ai/mastra/blob/@mastra/duckdb@1.14.1/stores/duckdb/CHANGELOG.md)
#### Patch Changes

- Trace and span lists now read short stored previews instead of each span's full input. Every span event stores previews of its input, output and error when it is written, and `queryTraces()`, `listTracesLight()` and `querySpans()` read them. Lists also return `outputPreview` and `errorPreview`, and `querySpans()` adds `errorPreview` and `errorTruncated`. (#26567)

  Generated previews use the shared format from `@mastra/core`: at most 100 characters plus `…` when cut. For message lists they show the last user message. `querySpans()` previews used to be the first 256 characters of raw JSON. Nonblank supplied previews are trimmed and capped the same way; missing or blank write-time previews are generated from their payloads.

  Reconstructed previews stay aligned with the event carrying their payload, including newer empty or missing previews. Each event also records whether it carries an input, output or error payload, so `listTracesLight()` picks previews without reading the `input` and `output` payloads. NULL previews trigger batched payload reads and application-side generation; stored empty strings do not. With older core versions that lack the optional preview helper, preview fields pass through unchanged without generation.

  The store adds `inputPreview`, `outputPreview`, `errorPreview`, `hasInputPayload`, `hasOutputPayload` and `hasErrorPayload` columns to `span_events` when it starts. No manual migration is needed.

### [@mastra/e2b@0.14.1](https://github.com/mastra-ai/mastra/blob/@mastra/e2b@0.14.1/workspaces/e2b/CHANGELOG.md)
#### Patch Changes

- Changed the Factory sandbox memory setting from a free-form MB field to a dropdown of sizes: 512 MB, 1 GB, 2 GB, 4 GB or 8 GB, matching E2B's 8 GB limit. Memory set through the `defaults` option in code is unaffected. (#27098)

  _Also in: @mastra/platform-workspace@1.8.1_

### [@mastra/factory@0.23.0](https://github.com/mastra-ai/mastra/blob/@mastra/factory@0.23.0/mastracode/factory/CHANGELOG.md)
#### Minor Changes

- Added a Factory model scope setting so sessions can use organization models and credentials or their owner’s personal models and credentials. (#26934)

  Organization admins choose the scope through the project API:

  ```http
  PATCH /web/factory/projects/:id
  Content-Type: application/json

  {"modelScope":"user"}
  ```

  Use `"factory"` for organization models and credentials. Credential scope changes apply on the next model invocation without replacing a thread’s selected model. Session ownership and project policy are resolved from durable records, and unavailable or ambiguous policy stops execution instead of falling back to another account.

- Added a hybrid model scope: sessions run on the owner's credentials and default model, work items run on the Factory's. New Factories start in hybrid; existing Factories keep their scope. Because new Factories now bill members' interactive sessions to their own provider accounts, admins who want everything on organization credentials should switch the Factory to the factory scope. (#27095)

  Previously, setting `modelScope` to `factory` on `PATCH /web/factory/projects/:id` put both sessions and work items on organization credentials:

  ```json
  { "modelScope": "factory" }
  ```

  Admins can now split sessions and work items by setting it to `hybrid`:

  ```json
  { "modelScope": "hybrid" }
  ```

- Factory sessions no longer have modes: Plan and Explore are gone, and every session runs as Build. Mastra Code in the terminal keeps its modes. (#26854)

  - The composer no longer shows a mode picker, and Shift+Tab no longer switches modes. `/settings` no longer prints a mode line.
  - A thread last used in Plan or Explore mode reopens in Build mode, so it can still write files.
  - Thinking defaults have a single global level. Per-mode thinking defaults no longer apply to Factory runs. Per-mode defaults saved by the terminal app stay in the shared settings file.
  - `GET /web/config/thinking` no longer returns `modes` or `modeDefaults`, and `PUT /web/config/thinking` only accepts `globalDefault`. A body with `modeDefaults` now gets a 400.

    ```diff
    - PUT /web/config/thinking  { "globalDefault": "medium", "modeDefaults": { "plan": "high" } }
    + PUT /web/config/thinking  { "globalDefault": "medium" }
    ```

  - Explore subagents in sessions without a default model now use Build mode's default model instead of Explore mode's, like sessions that have a default model already do for every subagent.
  - Audit events for runs no longer record an agent name.

- Slack threads no longer create Work board cards. A Slack DM or @-mention still starts its session and posts the "New session started" card, whatever the factory's old setting was. (#27063)

  The `slackWorkItemsEnabled` project setting is removed from the project update API.

  Before:

  ```http
  PATCH /web/factory/projects/:id
  { "slackWorkItemsEnabled": true }
  ```

  After: sent alone, the field is rejected as an empty update (400, "At least one project field is required"). Sent with other fields, it is ignored:

  ```http
  PATCH /web/factory/projects/:id
  { "name": "Platform" }
  ```

  Slack-thread cards that already exist keep receiving `aside` comments and keep posting feed comments back to Slack.

#### Patch Changes

- Added `scope=user` to `GET /web/config/models` to list models supported by the caller's personal credentials. Organization-only models are excluded from this personal catalog. (#27080)

- Factory sessions and work-item runs can now start on a model you have credentials for. This applies when no default model was chosen and the built-in default model has no credential. (#27177)

  The session uses the default model of a provider you have connected, for example `anthropic/claude-fable-5` for Claude or `openai/gpt-5.6-sol` for ChatGPT. A default you or your Factory chose always wins. The fallback is not saved as a default preference.

- New Factories now default to the hybrid model scope everywhere. Onboarding preselects "Company work · personal sessions", and Factories with no saved scope, or one that isn't recognized, are treated as hybrid instead of company account. (#27202)

- Improved the Slack "Could not authorize the owner of Slack Factory session" error. It now states which check failed: (#27183)

  - The sender's account link has no organization.
  - No source-control session row exists for the session, or its project repository row is missing.
  - The source-control session row has no organization.

- The session detail route `GET /web/user-sessions/:sessionId` now returns the owner profile (`owner: { id, name, avatarUrl? }`), the same field the session list routes already return. `owner` is missing when the owner has no profile in the user directory. (#27031)

  ```ts
  const { session } = await fetch(`${baseUrl}/web/user-sessions/${sessionId}`).then(response => response.json());
  const isTeammateSession = session.userId !== viewerUserId;
  const ownerName = session.owner?.name ?? 'a teammate';
  ```

- Fixed Factory pages outside a session failing with "Factory session ownership is unavailable." Overview, Rules, Settings and other Factory pages now use the Factory's model and credential policy for the signed-in member of that organization. (#27164)

- Fixed new Factory sessions starting on the built-in mode model instead of your default model. When a session opened before its chat thread was attached, your default went to a thread the chat never used, so the first message could run on a model you have no credentials for and fail with a provider authentication error. (#27080)

- Added `scope=org` to `GET /web/config/models`. It lists only the models your organization credentials can run, next to the existing `scope=user` for personal credentials. (#27163)

  ```text
  GET /web/config/models?scope=org
  ```

  An unsupported `scope` value now returns `400` with `invalid_model_scope`. Before, it was ignored and the unfiltered list came back.

- Fixed runs failing with "Factory source-control repository not found." after a repository's source-control connection was reinstalled. When a card's repository is linked but turned off in the Factory's environment, the error now names the repository and says to turn it on under Settings → Environment. (#26970)

- Fixed model pickers offering models that an organization-scoped Factory cannot run. `GET /web/config/models` accepts an optional `factoryId` and then lists only models served by that Factory's model scope (organization credentials for organization scope, personal credentials for personal scope). Personal model settings now note when the current Factory uses organization credentials only. (#27077)

  ```text
  GET /web/config/models?factoryId=<factory-id>
  ```

- Fixed new chats so they run on the default model you saved in Settings. Before, the draft showed your default model but the chat ran on the built-in one. Chats where you already sent a message keep their model. (#26996)

- Fixed sandbox settings becoming impossible to save when a previously stored value is no longer allowed (for example, a memory size above the new 8 GB limit). The outdated value is dropped and the provider default applies instead. (#27098)

- Fixed the GitHub repository list failing entirely when an organization still had a personal-account GitHub installation it is no longer allowed to use; those installations are now skipped. Added a refresh option that bypasses the repository cache so repositories newly granted on GitHub appear without waiting for a webhook. (#27047)

- Fixed Slack mentions failing with "Tenant model credential policy could not be resolved". New Slack sessions now open directly on their Slack thread and no longer leave an extra empty thread behind. (#27113)

- Kept workspace file listings, change lists and untracked-file lookups complete now that sandbox commands keep only the newest 1 MiB of output by default. (#27106)

### [@mastra/inngest@1.10.6](https://github.com/mastra-ai/mastra/blob/@mastra/inngest@1.10.6/workflows/inngest/CHANGELOG.md)
#### Patch Changes

- Inngest agents honor agent `scopes` when used with a `@mastra/core` release that supports them. Memory identity comes from `resource:` and `thread:` scopes, tools and processors see the other scopes, and a resumed run keeps the scopes it started with. Resuming with a scope the run did not hold throws an error and leaves the suspended run untouched. Scopes in the agent's `defaultOptions` count as the agent's own scopes, not the caller's. (#26593)

### [@mastra/libsql@1.26.2](https://github.com/mastra-ai/mastra/blob/@mastra/libsql@1.26.2/stores/libsql/CHANGELOG.md)
#### Patch Changes

- Fixed `mastra_notifications` being created without its `("threadId", "id")` primary key. Without it, duplicate notification ids could be stored, and on PostgreSQL the table had no replica identity, so updates and deletes failed when the table was part of a logical replication publication. (#26664)

  New tables now get the key, and `exportSchemas()` includes it. Existing tables are upgraded the next time storage initializes: PostgreSQL adds the primary key, and LibSQL adds a unique index on the same columns. If the key cannot be added because an existing table already contains duplicate rows, or because the database role does not own the table, initialization still succeeds and logs a warning with the SQL needed to add the key.

  _Also in: @mastra/pg@1.31.2_

### [@mastra/loggers@1.3.8](https://github.com/mastra-ai/mastra/blob/@mastra/loggers@1.3.8/packages/loggers/CHANGELOG.md)
#### Patch Changes

- Fixed `HttpTransport` retrying JSON serialization failures. Log batches are now serialized once before sending: values that cannot be serialized (circular references, BigInts, throwing `toJSON`) fail immediately instead of waiting through retry delays, and network retries resend the exact same payload. (#27174)

- Fixed `FileTransport` and `UpstashTransport` log queries accepting invalid `page`/`perPage` values. `listLogs` and `listLogsByRunId` now throw a `RangeError` for negative, fractional, non-finite, or unsafe pagination values instead of silently returning misleading results or sending malformed Redis ranges. `page: 0` and `perPage: 0` still default to page 1 and 100 per page. (#27175)

### [@mastra/mcp@2.4.1](https://github.com/mastra-ai/mastra/blob/@mastra/mcp@2.4.1/packages/mcp/CHANGELOG.md)
#### Patch Changes

- Fixed `MCPClient` sending malformed tool input schemas to model providers. One MCP tool with a broken schema (for example a `required` list placed inside `properties`) used to make strict providers such as OpenAI reject every request, even ones that didn't use that tool. (#25995)

  - Tools with a malformed input schema, or one over the nesting/size limit, are now skipped with a warning naming the server, tool, and problem. The server's other tools keep working.
  - `toolsFromDefinitions` skips such cached definitions; `toolFromDefinition` throws `MCP_CLIENT_INVALID_TOOL_INPUT_SCHEMA` for them.

  Fixes #23731.

- MCP tools that declare an `outputSchema` now pass the text of embedded `resource` blocks to the model, together with their text blocks. Before, only the text blocks reached the model, so tools such as GitHub's `get_file_contents` showed the model a status line but not the file. `structuredContent` is still kept out of the model input. (#27131)

### [@mastra/memory@1.38.0](https://github.com/mastra-ai/mastra/blob/@mastra/memory@1.38.0/packages/memory/CHANGELOG.md)
#### Minor Changes

- Observational Memory now recovers automatically when a model provider rejects a request for exceeding its context window (#21657). (#25055)

  A provider can count more tokens than Observational Memory's own estimate (attachments, formatting, or switching to a model with a smaller window), so a request can overflow before the observation threshold is reached. Previously the agent's request just failed. Now Observational Memory observes everything still pending and retries once with the smaller context. No configuration is needed. Automatic recovery needs a `@mastra/core` version that includes `MastraMemory.getErrorProcessors()`; with an older core, the error reaches the caller as before. To turn it off:

  ```typescript
  const memory = new Memory({
    options: {
      observationalMemory: {
        observation: { observeOnContextOverflow: false },
      },
    },
  });
  ```

  `observe()` also accepts `force: true` to observe pending messages regardless of the threshold, for that call only.

### [@mastra/nestjs@0.2.37](https://github.com/mastra-ai/mastra/blob/@mastra/nestjs@0.2.37/server-adapters/nestjs/CHANGELOG.md)
#### Patch Changes

- The NestJS server marks itself as reserving agent scopes, like the other server adapters. Its routes already ignore `scopes` in request bodies and treat `mastra__scopes` as reserved, so agents accept scopes from your code and middleware when served through NestJS. (#26593)

### [@mastra/observability@1.20.1](https://github.com/mastra-ai/mastra/blob/@mastra/observability@1.20.1/observability/mastra/CHANGELOG.md)
#### Patch Changes

- Update bundled model pricing with the latest prices from models.dev (`bed1ccc`)

- Fixed cost estimates for Amazon Bedrock India inference profiles (`in.` prefix), which previously reported `no_matching_model`. Fixes #26387. (#26982)

- Tracking exporters now wait for in-progress trace-cap evictions in `flush()` and `shutdown()`, so the aborted spans are sent instead of being queued after the flush or aborted twice. (#26916)

### [@mastra/otel-exporter@1.4.8](https://github.com/mastra-ai/mastra/blob/@mastra/otel-exporter@1.4.8/observability/otel-exporter/CHANGELOG.md)
#### Patch Changes

- Fixed tool calls going missing from `gen_ai.output.messages`. A model step that only calls tools is now exported as an assistant message with `tool_call` parts instead of an empty text message. Inference span inputs are also converted to the GenAI message format. (#26995)

### [@mastra/pg@1.31.2](https://github.com/mastra-ai/mastra/blob/@mastra/pg@1.31.2/stores/pg/CHANGELOG.md)
#### Patch Changes

- Trace and span lists now read short stored previews instead of each span's full input. Every span stores previews of its input, output and error when it is written, and `queryTraces()` and `querySpans()` read them. Lists also return `outputPreview` and `errorPreview`, and `querySpans()` adds `errorPreview` and `errorTruncated`. (#26567)

  Generated previews use the shared format from `@mastra/core`: at most 100 characters plus `…` when cut. For message lists they show the last user message. `querySpans()` previews used to be the first 256 characters of raw JSON. Nonblank supplied previews are trimmed and capped the same way; missing or blank write-time previews are generated from their payloads.

  `listTracesLight()` also reads stored previews. NULL previews trigger batched payload reads and application-side generation; stored empty strings do not. With older core versions that lack the optional preview helper, preview fields pass through unchanged without generation.

  `storage.init()` adds `inputPreview`, `outputPreview` and `errorPreview` columns to `mastra_span_events`. This runs automatically on first use. If you set `disableInit: true`, run `storage.init()` from your migration step before deploying, or apply the columns yourself (use your schema name if it isn't `public`):

  ```sql
  ALTER TABLE "public"."mastra_span_events" ADD COLUMN IF NOT EXISTS "inputPreview" text;
  ALTER TABLE "public"."mastra_span_events" ADD COLUMN IF NOT EXISTS "outputPreview" text;
  ALTER TABLE "public"."mastra_span_events" ADD COLUMN IF NOT EXISTS "errorPreview" text;
  ```

### [@mastra/playground-ui@64.0.0](https://github.com/mastra-ai/mastra/blob/@mastra/playground-ui@64.0.0/packages/playground-ui/CHANGELOG.md)
#### Minor Changes

- Added a `minDate` prop to `DateTimeRangePicker` and `TraceTimeRangeChip`, so you can stop users picking dates with no data, such as traces past retention. Days before `minDate` are disabled in both custom range calendars, and a range that already starts earlier can't be applied. (#27166)

  ```tsx
  <TraceTimeRangeChip {...rangeProps} minDate={subDays(new Date(), retentionDays)} />
  ```

- Comboboxes in forms now search right in the field. Inside a `Field`, a `Combobox` is a text field you type into to filter, and a `multiple` one shows its picks as removable chips (× or Backspace removes one) instead of "N selected". Comboboxes outside a `Field` (toolbars, breadcrumbs, inline pickers), `ghost` and icon-size triggers keep the trigger with the search in the popup. The `Combobox` props are unchanged. (#27112)

  `Combobox` is now built on new composable parts, also exported, for pickers its `options` prop can't describe: groups with labels, items with a title, description and end detail, or a custom field. They follow shadcn's Combobox structure; the root is `ComboboxRoot` because `Combobox` is the options-based component.

  ```tsx
  import {
    ComboboxRoot,
    ComboboxInput,
    ComboboxContent,
    ComboboxEmpty,
    ComboboxList,
    ComboboxItem,
  } from '@mastra/playground-ui/components/Combobox';

  <ComboboxRoot items={agents} value={agent} onValueChange={setAgent} autoHighlight>
    <ComboboxInput placeholder="Pick an agent" />
    <ComboboxContent>
      <ComboboxEmpty>No agents found.</ComboboxEmpty>
      <ComboboxList>
        {item => (
          <ComboboxItem key={item.id} value={item}>
            {item.name}
          </ComboboxItem>
        )}
      </ComboboxList>
    </ComboboxContent>
  </ComboboxRoot>;
  ```

  For small triggers (an icon, a ghost button), use `ComboboxTrigger` with `ComboboxSearch` at the top of `ComboboxContent` to keep the search inside the popup.

- `DropdownMenu` gains two typed helpers for one-of-many choices. `DropdownMenu.RadioOptions` renders a list of options, and `DropdownMenu.RadioSub` puts them in a submenu whose row shows the selected label, so a menu of several settings reads its current state without opening each submenu. `onValueChange` receives your option type, not `any`. `DropdownMenu.SubTrigger` also takes a `hint` to show a current value next to its chevron. (#27003)

  ```tsx
  import { DropdownMenu } from '@mastra/playground-ui/components/DropdownMenu';
  import type { DropdownMenuRadioOption } from '@mastra/playground-ui/components/DropdownMenu';

  const ownerOptions: DropdownMenuRadioOption<'mine' | 'all'>[] = [
    { value: 'mine', label: 'Mine' },
    { value: 'all', label: 'Everyone' },
  ];

  <DropdownMenu>
    <DropdownMenu.Trigger>Properties</DropdownMenu.Trigger>
    <DropdownMenu.Content>
      <DropdownMenu.RadioSub label="Owner" options={ownerOptions} value={owner} onValueChange={setOwner} />
    </DropdownMenu.Content>
  </DropdownMenu>;
  ```

- Added helpers to filter the metrics dashboard with `FilterBar`, the filter bar Traces uses. (#27034)

  - `MetricsTimeRangeChip` shows the dashboard's date range as the filter bar's Time chip.
  - `createMetricsFilterBarFields` and `METRICS_FILTER_BAR_OPERATORS` describe the dimensions you can filter on.
  - `metricsTokensToFilterBarItems` and `filterBarItemsToMetricsTokens` convert between `FilterBar` items and the dashboard's URL-backed filter tokens.

  `DateRangeSelector` and `createMetricsPropertyFilterFields` are deprecated in favor of these.

  ```tsx
  import { FilterBar } from '@mastra/playground-ui/components/FilterBar';
  import type { FilterBarItem } from '@mastra/playground-ui/components/FilterBar';
  import { MetricsTimeRangeChip } from '@mastra/playground-ui/domains/metrics/components/metrics-time-range-chip';
  import {
    createMetricsFilterBarFields,
    filterBarItemsToMetricsTokens,
    METRICS_FILTER_BAR_OPERATORS,
    metricsTokensToFilterBarItems,
  } from '@mastra/playground-ui/domains/metrics/metrics-filters';
  import {
    TRACE_TIME_RANGE_FIELD,
    TRACE_TIME_RANGE_FIELD_ID,
    TRACE_TIME_RANGE_ITEM,
  } from '@mastra/playground-ui/domains/traces/components/trace-time-range-chip';

  const isTimeRange = (item: FilterBarItem) => item.fieldId === TRACE_TIME_RANGE_FIELD_ID;

  <FilterBar
    fields={[TRACE_TIME_RANGE_FIELD, ...createMetricsFilterBarFields(discovered)]}
    operators={METRICS_FILTER_BAR_OPERATORS}
    value={[TRACE_TIME_RANGE_ITEM, ...metricsTokensToFilterBarItems(filterTokens)]}
    onValueChange={items => setFilterTokens(filterBarItemsToMetricsTokens(items.filter(item => !isTimeRange(item))))}
  >
    <FilterBar.Chips
      renderChip={item => (isTimeRange(item) ? <MetricsTimeRangeChip /> : <FilterBar.Chip item={item} />)}
    />
    <FilterBar.Input />
  </FilterBar>;
  ```

- Added `createLocalStorageStore` and `useLocalStorageStore` for a browser-stored value that every component reading it shares, along with other tabs. Unlike `useLocalStorageState`, a write updates every reader at once. A write that storage refuses still applies for the rest of the session. (#27025)

  ```tsx
  import { createLocalStorageStore, useLocalStorageStore } from '@mastra/playground-ui/hooks/use-local-storage-store';

  const pinnedStore = createLocalStorageStore<ReadonlySet<string>>({
    key: 'app.pinned',
    defaultValue: new Set(),
    parse: stored => {
      if (!Array.isArray(stored)) throw new Error('not a list of ids');
      return new Set(stored.filter(id => typeof id === 'string'));
    },
    toJSON: pinned => [...pinned],
  });

  function PinButton({ threadId }: { threadId: string }) {
    const pinned = useLocalStorageStore(pinnedStore);
    return <button onClick={() => pinnedStore.write(new Set([...pinned, threadId]))}>Pin</button>;
  }
  ```

  Added `Sidebar.NavShowMore`, a "Show N more" / "Show less" toggle for a sidebar list cut to its first rows. It hides itself when nothing is cut off or when the sidebar is collapsed to its icon rail.

  ```tsx
  <Sidebar.NavList>{(expanded ? threads : threads.slice(0, 5)).map(renderThread)}</Sidebar.NavList>
  <Sidebar.NavShowMore foldedCount={threads.length - 5} expanded={expanded} onExpandedChange={setExpanded} />
  ```

- Added an `inset` variant to `SettingsContainer` for a short stack of rows that is the main content of a dialog. The default variant is unchanged and stays the one to use for settings pages and forms. (#27171)

- Added an Output column to the Traces table, so you can see what a run returned without opening it. (#27195)

  - Shows the trace's output preview, or its error in red when the trace failed.
  - On by default, next to Input. Hide it from the Columns menu or through `columnPreferences`.
  - Saved column choices gain Output next to Input when Input is shown.
  - Servers that don't send previews show `-`.

  ```tsx
  import { TracesListView } from '@mastra/playground-ui/domains/traces/components/traces-list-view';
  import { DEFAULT_TRACE_COLUMN_PREFERENCES } from '@mastra/playground-ui/domains/traces/trace-list-columns';

  <TracesListView
    {...props}
    columnPreferences={{
      ...DEFAULT_TRACE_COLUMN_PREFERENCES,
      visibleColumns: DEFAULT_TRACE_COLUMN_PREFERENCES.visibleColumns.filter(column => column !== 'output'),
    }}
  />;
  ```

- Added `THEME_OPTIONS`, the System, Light, and Dark choices that `ThemeToggle` shows. Use it to offer the same theme choices inside a menu, where a segmented control does not fit. (#27097)

  ```tsx
  import { DropdownMenu } from '@mastra/playground-ui/components/DropdownMenu';
  import { useTheme } from '@mastra/playground-ui/components/ThemeProvider';
  import { THEME_OPTIONS } from '@mastra/playground-ui/components/ThemeToggle';

  function ThemeMenuItem() {
    const { theme, setTheme } = useTheme();
    return <DropdownMenu.RadioSub label="Theme" options={THEME_OPTIONS} value={theme} onValueChange={setTheme} />;
  }
  ```

- The trace thread view now shows the whole thread instead of only the last 31 days. Field suggestions on the traces page now use the full selected date range. (#27042)

  Removed `clampTraceDiscoveryTimeRange` and `TRACE_QUERY_DISCOVERY_MAX_RANGE_MS` from `@mastra/playground-ui/domains/traces/trace-query-filters`. Pass the selected time range directly:

  ```ts
  // Before
  const timeRange = clampTraceDiscoveryTimeRange(buildTraceQueryRequest(options).timeRange);

  // After
  const timeRange = buildTraceQueryRequest(options).timeRange;
  ```

- Added `Meter`, a thin bar that shows usage against an allowance. Usage past the allowance turns red, sized to how far over it is. (#27194)

  ```tsx
  import { Meter } from '@mastra/playground-ui/components/Meter';

  <Meter value={112} max={100} aria-label="vCPU" aria-valuetext="112 of 100 vCPU-hours" />;
  ```

  Pass `Meter.Source` children to show where the usage came from. Hovering or focusing the bar splits it into one color per source, with a key of each source's amount:

  ```tsx
  <Meter value={148} max={360} aria-label="Memory">
    <Meter.Source label="research-workflows" value={112} />
    <Meter.Source label="Sandboxes" value={36} />
  </Meter>
  ```

  `MetricsStackedBarChart` can now print a label above a column. Set `columnLabelKey` to a row field, and rows that have it show the text above their column:

  ```tsx
  <MetricsStackedBarChart data={rows} series={series} columnLabelKey="overage" />
  // rows: [..., { time: 'Oct 13', included: 100, over: 12, overage: '$1.20 over' }]
  ```

  An inset `Table` can now draw its header inside the inset with the rows, for a table nested in a card or an expanded row:

  ```tsx
  <Table variant="inset">
    <Table.Header placement="inside">…</Table.Header>
    <Table.Body>…</Table.Body>
  </Table>
  ```

- Added an `info` prop to `FieldLabel`. It shows a small info icon after the label with your text in a tooltip on hover or keyboard focus. Screen readers announce the text as the icon's description, and clicking the icon never focuses the field. (#27114)

  ```tsx
  <FieldLabel info="Shown to people who pick this agent.">Display name</FieldLabel>
  ```

  Added `InfoTooltip`, the same info icon for use outside form labels, such as next to a heading or a toolbar control:

  ```tsx
  import { InfoTooltip } from '@mastra/playground-ui/components/Tooltip';

  <InfoTooltip aria-label="Version information" side="bottom" align="start">
    Each save creates a version you can switch back to.
  </InfoTooltip>;
  ```

  `TooltipContent` takes `keepMounted` to keep a closed tooltip in the page (hidden), for example so its trigger can point `aria-describedby` at it.

  Removed `RequestContextLabel` (`@mastra/playground-ui/domains/request-context/components/request-context-label`). Use `FieldLabel` with `info` instead:

  ```tsx
  <FieldLabel info="Values passed to the agent at runtime.">Request context</FieldLabel>
  ```

#### Patch Changes

- Fixed avatars that stayed on the fallback initial after an image failed. They now show the new image when `src` changes. (#27097)

- Fixed `formatModelName` for Bedrock model ids. Ids such as `us.anthropic.claude-sonnet-4-5-20250929-v1:0` now read as "Claude Sonnet 4.5" instead of "Us.anthropic.claude Sonnet 4.5 20250929 V1:0". (#27001)

- Fixed scrolling in long dropdown menus, context menus and selects. Their scrollbars now match the rest of the app and stay clear of the popup's rounded corners. (#27124)

- Added an optional `keywords` field to `Combobox` options. Search matches these terms as well as the label, so an option can show a friendly label and still be found by an id or alias. (#27000)

  ```tsx
  <Combobox
    options={[
      { label: 'Claude Haiku 4.5', value: 'anthropic/claude-haiku-4-5', keywords: ['anthropic/claude-haiku-4-5'] },
    ]}
  />
  ```

- Added an optional `renderDragPreview` prop to `ContentBlocks`. When set, the dragged item is rendered on `document.body`, so a list inside a menu or popover no longer shows the dragged item far from the pointer. (#27028)

  ```tsx
  <ContentBlocks items={groups} onChange={setGroups} renderDragPreview={group => <GroupRow group={group} />}>
    {groups.map((group, index) => (
      <ContentBlock key={group.id} draggableId={group.id} index={index}>
        {dragHandleProps => <GroupRow group={group} dragHandleProps={dragHandleProps} />}
      </ContentBlock>
    ))}
  </ContentBlocks>
  ```

- Added an `info` tone to `DataList` rows. It adds a faint blue tint to set a row apart from the others, for example to mark the rows that match the current selection. Hover and selection still show through the tint. (#27055)

  ```tsx
  <DataList.RowLink to={href} variant={isComparable ? 'info' : 'default'}>
    {cells}
  </DataList.RowLink>
  ```

- Fixed the hover highlight in lists with collapsed sections. Rows in a collapsed section are now skipped, so the highlight keeps following the visible rows instead of staying at their old positions. (#27068)

- Added `describeSuggestionsError` to `FilterBar` so apps can explain why value suggestions failed to load, for example when a server limit was hit. Return `undefined` to keep the default "Couldn't load values." text. (#27083)

  ```tsx
  <FilterBar
    fields={fields}
    operators={operators}
    value={value}
    onValueChange={setValue}
    describeSuggestionsError={error => (isTooExpensive(error) ? 'Too many values. Narrow the time range.' : undefined)}
  >
    <FilterBar.Chips />
    <FilterBar.Input />
  </FilterBar>
  ```

- Fixed the Expand button on clipped collapsible boxes looking see-through: it now uses the default solid button style, so it stays readable over the faded content. (#27084)

- Fixed scrollbars in rounded popups, popovers, command lists and the composer so their ends no longer get cut off by the rounded corners. (#27124)

- Changed `PageLayout` to scroll its body in a `ScrollArea` instead of reserving a stable scrollbar gutter. The body no longer shifts sideways when a scrollbar appears, and pages no longer show an empty strip beside their content. Added `contentClassName` to `ScrollAreaViewport` to style the wrapper around its children. (#27018)

  Added a `mask` prop to `PageLayout` to fade the body's scrolled edges, using the same values as `ScrollArea`. It is off by default. A fade covers whatever sits at that edge, so leave it off for bodies with sticky table headers.

  ```tsx
  <PageLayout mask={{ y: true }}>{/* ... */}</PageLayout>
  ```

- Fixed visual glitches when switching tabs in `TabbedContainer`. The search and filter controls now switch together with the table, so two search fields no longer overlap mid-switch and the panel corner no longer shows a gap or a stray control. (#26810)

- Fixed code blocks updating state after they are removed from the page, which could throw `window is not defined` in tests. (#27104)

- Fixed section labels in dropdown and context menus so they line up with the text of the items under them, including inset labels. (#27120)

- Fixed Studio showing a resumed workflow run as Suspended. This happened when the run was resumed from code or the API, such as `run.resume()`. Studio asked for approval again. Studio now shows the run as running, with the approved step marked complete. (#26939)

- Added a `flush` variant to `InputGroupAddon`. Use it to place adjacent number steppers at the field edge without a gap or edge inset. (#27017)

  ```tsx
  <InputGroupAddon align="inline-end" variant="flush">
    <InputNumberDecrement />
    <InputNumberIncrement />
  </InputGroupAddon>
  ```

- Fixed field labels catching clicks across the whole row. Clicking the empty space beside a text label no longer focuses its field; only the label text does. Labels that wrap a checkbox, switch or radio still toggle it from anywhere in the row. (#27110)

- Studio traces: the error screen shows the server's reason instead of a generic message, and auto refresh stops retrying a request the server rejected so the page no longer flickers between the list and the error. `DateTimeRangePicker` also accepts an optional `maxRangeMs` prop that blocks custom ranges longer than the given duration. (#27076)

### [@mastra/react@1.11.0](https://github.com/mastra-ai/mastra/blob/@mastra/react@1.11.0/client-sdks/react/CHANGELOG.md)
#### Minor Changes

- Added `useTraceAggregateAvailable()`, which reports whether the server supports trace aggregation. A server that does not report the capability is treated as not supporting it. (#27185)

#### Patch Changes

- Review items returned by `useReviewItems` and `useCompletedItems` now include `targetType`, `startedAt` and `completedAt` from the experiment result, so a review item can be rendered exactly like the same result on its experiment page (agent answer as text, run duration). (#27084)

- Fixed `useChat` leaving a pending user message in the chat after a send fails with thread signals enabled. The optimistic message is now removed when the server never accepted it, so retrying shows the message once instead of a stuck pending copy plus the retried one. (#26975)

### [@mastra/schema-compat@1.3.15](https://github.com/mastra-ai/mastra/blob/@mastra/schema-compat@1.3.15/packages/schema-compat/CHANGELOG.md)
#### Patch Changes

- Fixed Azure OpenAI GPT models filling optional tool inputs with made-up values. They now leave optional inputs empty, as they already do through `@ai-sdk/openai`. This applies when the Azure deployment name contains an OpenAI model name, such as `gpt-4o`, `my-gpt5-deployment` or `o3-mini`. Deployments with other names, such as `production`, and non-OpenAI models served through Azure behave as before. (#26680)

### [@mastra/server@1.78.0](https://github.com/mastra-ai/mastra/blob/@mastra/server@1.78.0/packages/server/CHANGELOG.md)
#### Minor Changes

- Added `traceAggregate` to `GET /observability/capabilities`, so clients can tell whether `POST /observability/traces/aggregate` is available. Trace-scope field discovery (`POST /observability/traces/query/fields`) now also returns an `aggregate` block listing the groupable dimensions and available measures when the store supports aggregation. (#27185)

- Added `POST /agents/:agentId/queue-signal`, which queues a typed signal to run after the active thread run completes, or starts a run right away when the thread is idle. The body takes the same `signal` shape as `POST /agents/:agentId/signals`. (#27081)

  ```bash
  curl -X POST http://localhost:4111/api/agents/support-agent/queue-signal \
    -H 'Content-Type: application/json' \
    -d '{
      "resourceId": "user-123",
      "threadId": "thread-123",
      "signal": {
        "type": "reactive",
        "tagName": "skill",
        "attributes": { "name": "triage" },
        "contents": "Triage the new issue."
      }
    }'
  ```

#### Patch Changes

- Agent scopes support: (#26593)

  - Agent routes ignore `scopes` in request bodies (including `ifIdle.streamOptions.scopes`), and `mastra__scopes` is a reserved request context key.
  - Set scopes from trusted server middleware instead:

    ```ts
    import { MASTRA_SCOPES_KEY } from '@mastra/core/request-context';

    server: {
      middleware: [async (c, next) => {
        c.get('requestContext').set(MASTRA_SCOPES_KEY, [`org:${orgIdFromAuth(c)}`]);
        await next();
      }],
    }
    ```

  - A conflicting `resource:` or `thread:` scope returns a 400 error.
  - With an older `@mastra/server` registered, agent calls that pass scopes — from the call or the request context — fail with `AGENT_SCOPES_SERVER_OUTDATED` (HTTP 400). Scopes configured on the agent itself still work. Upgrade the server to lift the restriction.

- Tightened ownership checks on workflow run routes when `server.auth.mapUserToResourceId` is configured. A caller mapped to a resource can only act on its own runs, including when it passes an existing run id to `/stream`, `/create-run`, `/start-async` or `/stream-legacy`, and when it resumes, recovers or approves a durable agent run. The worker endpoints `steps/execute` and `/workflows/events` apply the same check to the run they address. Restarting all active runs of a workflow now requires a privileged caller. A caller without a resource id is treated as privileged only when a fine-grained authorization (FGA) provider is configured to authorize the route. (#26958)

  **Before:** with `mapUserToResourceId` configured, a caller with no mapped resource id could read and act on any run, and a mapped caller could act on runs saved without a resource id.

  **After:** a mapped caller acts only on its own runs. Admin, service, worker and push-broker callers that are not mapped to a resource need an FGA provider for run access, and restarting all active runs requires such a privileged caller. Runs saved without a resource id are no longer reachable by mapped callers.

  To keep privileged run access, configure an FGA provider next to the mapper:

  ```ts
  export const mastra = new Mastra({
    server: {
      auth: {
        authenticateToken: async token => verifyAccessToken(token),
        mapUserToResourceId: user => `${user.workspaceId}:${user.id}`,
      },
      fga: fgaProvider,
    },
  });
  ```

  **Servers without `mapUserToResourceId`** keep the previous rule: deny only when the caller's resource id and the run's resource id are both present and differ. That rule now also runs on routes that had no ownership check: `/stream`, `/create-run`, `/start-async`, `/stream-legacy`, `steps/execute`, `/workflows/events`, and the durable agent resume and recover routes. Reusing another resource's run id on those routes used to succeed and now returns 403.

### [@mastra/voice-sarvam@1.1.2](https://github.com/mastra-ai/mastra/blob/@mastra/voice-sarvam@1.1.2/voice/sarvam/CHANGELOG.md)
#### Patch Changes

- Fixed `SarvamVoice.listen()` sending the speech model's API key. `listen()` now uses `options.apiKey`, then the listening model's key, and `speak()` uses the speech model's key, so projects with separate Sarvam keys for speech-to-text and text-to-speech bill each to the right key. (#27206)

  ```ts
  const voice = new SarvamVoice({
    speechModel: { apiKey: 'tts-key' },
    listeningModel: { apiKey: 'stt-key' },
  });
  await voice.listen(audio); // uses 'stt-key'
  await voice.listen(audio, { apiKey: 'per-call-key' }); // uses 'per-call-key'
  ```

- `SarvamVoice.listen()` now throws on Sarvam API errors (e.g. invalid API key, rate limits) instead of silently returning `undefined`. Error messages for both `listen()` and `speak()` now surface Sarvam's `error.message`. (#27173)

  Before, a failed `listen()` call returned `undefined`. Now it throws, so handle it with `try`/`catch`:

  ```ts
  try {
    const text = await voice.listen(audio);
  } catch (error) {
    // error.message: "Sarvam AI API Error: <message>"
  }
  ```

- Fixed `SarvamVoice.listen()` ignoring the `listeningModel` passed to the constructor. A voice created with `listeningModel: { model: 'saaras:v3', languageCode: 'hi-IN', mode: 'translit' }` now transcribes with that model, language and mode; before, it always sent `saarika:v2.5` with automatic language detection. Options passed to `listen()` still take precedence. (#27102)

### [@mastra/weaviate@0.1.2](https://github.com/mastra-ai/mastra/blob/@mastra/weaviate@0.1.2/stores/weaviate/CHANGELOG.md)
#### Patch Changes

- Fixed `$in` filters with an empty array, which made Weaviate queries fail on text-array metadata. An empty `$in` now matches nothing and an empty `$nin` matches everything, like the other vector stores. (#26917)

### Other updated packages

The following packages were updated with dependency changes only:

- [@mastra/agent-builder@1.1.29](https://github.com/mastra-ai/mastra/blob/@mastra/agent-builder@1.1.29/packages/agent-builder/CHANGELOG.md)
- [@mastra/arize@1.3.25](https://github.com/mastra-ai/mastra/blob/@mastra/arize@1.3.25/observability/arize/CHANGELOG.md)
- [@mastra/arthur@0.4.25](https://github.com/mastra-ai/mastra/blob/@mastra/arthur@0.4.25/observability/arthur/CHANGELOG.md)
- [@mastra/braintrust@1.3.22](https://github.com/mastra-ai/mastra/blob/@mastra/braintrust@1.3.22/observability/braintrust/CHANGELOG.md)
- [@mastra/connect@1.0.3](https://github.com/mastra-ai/mastra/blob/@mastra/connect@1.0.3/packages/connect/CHANGELOG.md)
- [@mastra/datadog@1.4.17](https://github.com/mastra-ai/mastra/blob/@mastra/datadog@1.4.17/observability/datadog/CHANGELOG.md)
- [@mastra/deepeval@0.1.19](https://github.com/mastra-ai/mastra/blob/@mastra/deepeval@0.1.19/observability/deepeval/CHANGELOG.md)
- [@mastra/deployer-cloud@1.78.0](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-cloud@1.78.0/deployers/cloud/CHANGELOG.md)
- [@mastra/deployer-netlify@1.2.37](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-netlify@1.2.37/deployers/netlify/CHANGELOG.md)
- [@mastra/deployer-vercel@1.2.37](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-vercel@1.2.37/deployers/vercel/CHANGELOG.md)
- [@mastra/e2b-desktop@0.1.8](https://github.com/mastra-ai/mastra/blob/@mastra/e2b-desktop@0.1.8/workspaces/e2b-desktop/CHANGELOG.md)
- [@mastra/editor@0.16.3](https://github.com/mastra-ai/mastra/blob/@mastra/editor@0.16.3/packages/editor/CHANGELOG.md)
- [@mastra/elysia@0.1.18](https://github.com/mastra-ai/mastra/blob/@mastra/elysia@0.1.18/server-adapters/elysia/CHANGELOG.md)
- [@mastra/express@1.5.22](https://github.com/mastra-ai/mastra/blob/@mastra/express@1.5.22/server-adapters/express/CHANGELOG.md)
- [@mastra/fastify@1.5.22](https://github.com/mastra-ai/mastra/blob/@mastra/fastify@1.5.22/server-adapters/fastify/CHANGELOG.md)
- [@mastra/hono@1.7.20](https://github.com/mastra-ai/mastra/blob/@mastra/hono@1.7.20/server-adapters/hono/CHANGELOG.md)
- [@mastra/koa@1.7.22](https://github.com/mastra-ai/mastra/blob/@mastra/koa@1.7.22/server-adapters/koa/CHANGELOG.md)
- [@mastra/laminar@1.3.27](https://github.com/mastra-ai/mastra/blob/@mastra/laminar@1.3.27/observability/laminar/CHANGELOG.md)
- [@mastra/langfuse@1.6.1](https://github.com/mastra-ai/mastra/blob/@mastra/langfuse@1.6.1/observability/langfuse/CHANGELOG.md)
- [@mastra/langsmith@1.3.27](https://github.com/mastra-ai/mastra/blob/@mastra/langsmith@1.3.27/observability/langsmith/CHANGELOG.md)
- [@mastra/longmemeval@1.1.37](https://github.com/mastra-ai/mastra/blob/@mastra/longmemeval@1.1.37/explorations/longmemeval/CHANGELOG.md)
- [@mastra/mcp-docs-server@1.3.8](https://github.com/mastra-ai/mastra/blob/@mastra/mcp-docs-server@1.3.8/packages/mcp-docs-server/CHANGELOG.md)
- [@mastra/next@0.2.36](https://github.com/mastra-ai/mastra/blob/@mastra/next@0.2.36/server-adapters/next/CHANGELOG.md)
- [@mastra/opencode@0.1.37](https://github.com/mastra-ai/mastra/blob/@mastra/opencode@0.1.37/integrations/opencode/CHANGELOG.md)
- [@mastra/otel-bridge@1.5.17](https://github.com/mastra-ai/mastra/blob/@mastra/otel-bridge@1.5.17/observability/otel-bridge/CHANGELOG.md)
- [@mastra/posthog@1.4.2](https://github.com/mastra-ai/mastra/blob/@mastra/posthog@1.4.2/observability/posthog/CHANGELOG.md)
- [@mastra/sentry@1.2.27](https://github.com/mastra-ai/mastra/blob/@mastra/sentry@1.2.27/observability/sentry/CHANGELOG.md)
- [@mastra/tanstack-start@0.2.36](https://github.com/mastra-ai/mastra/blob/@mastra/tanstack-start@0.2.36/server-adapters/tanstack-start/CHANGELOG.md)
- [@mastra/temporal@0.4.16](https://github.com/mastra-ai/mastra/blob/@mastra/temporal@0.4.16/workflows/temporal/CHANGELOG.md)
- [@mastra/turso@0.1.16](https://github.com/mastra-ai/mastra/blob/@mastra/turso@0.1.16/stores/turso/CHANGELOG.md)
- [@mastra/voice-google-gemini-live@0.14.16](https://github.com/mastra-ai/mastra/blob/@mastra/voice-google-gemini-live@0.14.16/voice/google-gemini-live-api/CHANGELOG.md)
- [@mastra/voice-openai-realtime@0.14.6](https://github.com/mastra-ai/mastra/blob/@mastra/voice-openai-realtime@0.14.6/voice/openai-realtime-api/CHANGELOG.md)
- [@mastra/voice-xai-realtime@0.2.15](https://github.com/mastra-ai/mastra/blob/@mastra/voice-xai-realtime@0.2.15/voice/xai-realtime-api/CHANGELOG.md)

Related Repositories