@mastra/core@1.47.0

longbridge/gpui-kit@mastra/core@1.47.0Jul 1, 2026by PaulieScanlon

AI Summary

Adds support for AI SDK v7 models and performs a major architectural refactoring by renaming 'Harness' to 'AgentController' and centralizing model discovery.

Key Highlights

  • AI SDK v7 (LanguageModelV4) support with auto-detection of model specs
  • DurableAgent API parity with full stream/resume/generate support
  • New GatewayManager for centralized model discovery and auth
  • Harness renamed to AgentController with new canonical import path
  • Gateway-first model discovery replacing legacy fallback methods

Breaking Changes

  • Harness routes removed from `/harness/...`, replaced with `/agent-controller/...`
  • Removed deprecated `getHarness`, `Harness`, and `HarnessSession` classes from client-js
  • Removed `resolveModel`, `customModelCatalogProvider`, and `modelAuthChecker` from Harness config
  • Session identity (`id`, `ownerId`) is now mandatory

New Features

  • runEvals quality gates with pass/fail logic
  • Micro-scorers (`checks` namespace) for text and tool-use assertions
  • Enhanced cancellation and tracing capabilities for durable runs

Full Release Notes

## Highlights

### AI SDK v7 (LanguageModelV4) support + upgraded model router
Agents can now accept AI SDK v7 provider models directly (`LanguageModelV4`), while still supporting v4/v5/v6; Mastra auto-detects model spec versions so you can mix providers across agents. The model router was upgraded to resolve V4 models natively, and `modelSettings` adds a new `reasoning` effort control for V4-capable providers.

### DurableAgent API parity (stream/resume/generate) + better cancellation & tracing
`DurableAgent` now mirrors `Agent` across stream/resume/generate, including full `AgentExecutionOptions` support (model settings, stop conditions, transforms, per-call instructions/system, tracing options, actor, etc.). New abort surfaces (`abortSignal`, `result.abort()`, `onAbort`) and forwarded abort to delegated subagents improve control, and durable runs now emit richer `AGENT_RUN` span metadata for observability.

### Gateway-first model discovery & auth via new `GatewayManager` (Harness hooks removed)
Harness now sources model catalog, auth status, mode agents, observational-memory models, and subagents entirely from registered `gateways` (no fallback to `resolveModel`, `customModelCatalogProvider`, or `modelAuthChecker`, which were removed). Shared gateway operations are centralized in a new `GatewayManager` exported from `@mastra/core`, and gateways can implement `handlesModel` to claim bare provider model IDs for auth resolution.

### AgentController becomes the canonical “Harness” surface across core/server/client
`Harness` is renamed to `AgentController` with a new canonical import path `@mastra/core/agent-controller`, and server routes are now exclusively under `/agent-controller/...`. Client usage follows suit (`MastraClient.getAgentController`, `listAgentControllers`, `AgentControllerSession`), with enriched session state (OM progress, token usage, behavior settings) and thread scoping via session `tags` (e.g. per git worktree).

### Evals quality gates + micro-scorer `checks` namespace
`runEvals` now supports pass/fail `gates`, per-scorer `{ scorer, threshold }` (including `{ min, max }`), and returns `verdict`, `gateResults`, and `thresholdResults`. `@mastra/evals` adds `checks` micro-scorers (text assertions and tool-use assertions) to make common eval criteria easy to compose.

### Breaking Changes
- Harness→AgentController migration: `@mastra/server` removed legacy `/harness/...` routes and `harness:*` permissions; clients must use `/agent-controller/...` and `agent-controller:*` permissions.
- `@mastra/client-js` removed deprecated `getHarness`/`listHarnesses` and `Harness`/`HarnessSession` classes (breaking for recent client users).
- Harness config removed `resolveModel`, `customModelCatalogProvider`, and `modelAuthChecker`; register model gateways via `gateways` instead.
- Session identity is now required: `id` and `ownerId` are mandatory on `harness.createSession()` and `Session`/`SessionIdentity` constructors (no defaults).
- Workspace/browser ownership moved to per-session: `Session` constructor now requires `workspace: Workspace`; `HarnessConfig.workspace` no longer accepts `WorkspaceConfig` shorthand (must provide `Workspace` instance or factory).

## Changelog

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

- Added support for AI SDK v7 models (`LanguageModelV4`). You can now pass any AI SDK v7 provider model directly to an agent, alongside the existing AI SDK v4, v5, and v6 support. ([#18477](https://github.com/mastra-ai/mastra/pull/18477))

  ```ts
  import { Agent } from '@mastra/core/agent';
  import { openai } from '@ai-sdk/openai'; // AI SDK v7

  const agent = new Agent({
    name: 'my-agent',
    instructions: 'You are a helpful assistant.',
    model: openai('gpt-5'),
  });
  ```

  Mastra detects the model's specification version automatically, so mixing models from different AI SDK versions across your agents continues to work without any extra configuration.

- Finished closing the gap between `DurableAgent` and `Agent`. After this change the durable agent surface mirrors the in-process agent's stream, resume, and generate APIs. ([#18508](https://github.com/mastra-ai/mastra/pull/18508))

  **What's new**
  - `abortSignal` on `stream()` and `resume()`, plus `result.abort()` on `stream()`, `resume()`, and `observe()`. `result.abort()` on `streamUntilIdle()` fans out to every inner run.
  - `untilIdle` on `resume()` (it already existed on `stream()`), using the shared `runWithIdleWrapper` so both paths drive the same idle loop.
  - `DurableAgent.generate()` and `DurableAgent.resumeGenerate()` wrap `stream()` / `resume()` and resolve a `FullOutput` even when the run suspends mid-flight.
  - `delegation` callbacks (`onDelegationStart`, `onDelegationComplete`, `messageFilter`) are forwarded to `convertTools` at prepare time and baked into sub-agent tool wrappers.
  - Per-call `clientTools` and `toolsets` survive in-process resume via the run registry (cross-process resume still falls back to the agent's static tools).
  - Scorers configured on the wrapped agent or passed per call now actually execute under durable runs and emit `ON_SCORER_RUN` payloads matching the non-durable shape.
  - `AGENT_RUN` spans now carry `conversationId`, `instructions`, `resolvedVersionId`, `entityVersionId`, and the agent's `tracingPolicy`. Resume spans use `'agent run: <id> (resumed)'` and include `resumedFromSpanId`.

  **Example**

  ```ts
  import { createDurableAgent } from '@mastra/core/agent/durable';

  const durable = createDurableAgent({ agent: myAgent });

  // 1. generate() — drains a durable run to a single FullOutput
  const out = await durable.generate('Plan a week in Lisbon', {
    abortSignal: controller.signal,
    modelSettings: { temperature: 0.2 },
  });

  // 2. stream() with result.abort() — cancel mid-run
  const result = await durable.stream('Long research task');
  setTimeout(() => result.abort(), 5_000);
  for await (const chunk of result.output.fullStream) {
    process.stdout.write(chunk.payload?.text ?? '');
  }

  // 3. Suspend → resumeGenerate() round-trip (e.g. tool approval)
  const first = await durable.generate('Run the dangerous tool', {
    requireToolApproval: true,
  });
  if (first.finishReason === 'suspended') {
    const final = await durable.resumeGenerate(first.runId!, { approved: true });
    console.log(final.text);
  }

  // 4. resume({ untilIdle }) — drive a resume through to a quiescent state
  await durable.resume(runId, resumeData, { untilIdle: true });
  ```

- Brought `DurableAgent.stream()` to parity with `Agent.stream()` for the full `AgentExecutionOptions` surface, so the same call works on both. ([#18461](https://github.com/mastra-ai/mastra/pull/18461))

  **What's new**

  `DurableAgent.stream()` now honors these options that were previously dropped or silently coerced:
  - Full `modelSettings` (was: only `temperature`) — `maxOutputTokens`, `topP`, `topK`, `presencePenalty`, `frequencyPenalty`, `stopSequences`, `seed`, `headers`
  - `stopWhen`, `prepareStep`, `isTaskComplete`, `transform`
  - Per-call `instructions` and `system`
  - `disableBackgroundTasks`, `tracingOptions`, `actor`
  - Function-form `requireToolApproval(toolName, args, …)` (was: coerced to "approve all" — now evaluated per tool call)
  - New callbacks: `onAbort` and `onIterationComplete` (joining the existing `onChunk` / `onStepFinish` / `onFinish` / `onError` / `onSuspended` bridge)

  **Example**

  The same options that work on `Agent.stream()` now work on `DurableAgent.stream()`:

  ```ts
  const durable = createDurableAgent({ agent, pubsub });

  await durable.stream('Plan the trip', {
    modelSettings: { temperature: 0.2, maxOutputTokens: 500, topP: 0.9 },
    stopWhen: ({ steps }) => steps.length >= 3,
    prepareStep: ({ stepNumber }) => ({
      activeTools: stepNumber === 0 ? ['search'] : ['book'],
    }),
    requireToolApproval: ({ toolName }) => toolName === 'book',
    onIterationComplete: ({ iteration }) => console.log('iter', iteration),
  });
  ```

- Resolve all Harness models through gateways. The Harness now builds its available-models catalog, model auth status, mode agents, Observational Memory models, and subagents from the gateways you register, instead of the separate `resolveModel`, `customModelCatalogProvider`, and `modelAuthChecker` config hooks. Removed those three options from `HarnessConfig` (and the `ModelAuthChecker` type) — register a gateway via `gateways` instead. ([#18382](https://github.com/mastra-ai/mastra/pull/18382))

  `listAvailableModels()` and `getCurrentModelAuthStatus()` are now sourced entirely from gateways: model discovery comes from each gateway's `fetchProviders()` (a network call for gateways like models.dev and Netlify), and auth status is resolved through the same gateway chain the model router uses at run time (`resolveAuth()`, falling back to `getApiKey()`). There is no static provider-registry fallback — everything the model picker shows comes from a gateway.

  The gateway-chain operations shared by `ModelRouterLanguageModel` and the Harness (gateway merging, model→gateway selection, auth resolution, provider/model listing) are now centralized in a new `GatewayManager` class exported from `@mastra/core`. Both consumers delegate to it, eliminating duplicated logic. `defaultGateways` is still re-exported from the same paths for backward compatibility.

- Added `reasoning` option to `modelSettings` for controlling model reasoning effort level. This option accepts standardized levels ('provider-default' | 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh') and is effective with LanguageModelV4 (AI SDK v7) providers that support reasoning. When used with older model providers (V2/V3), the option is a no-op. ([#18500](https://github.com/mastra-ai/mastra/pull/18500))

  **Usage:**

  ```typescript
  const result = await agent.stream('Solve this problem', {
    modelSettings: { reasoning: 'high' },
  });
  ```

  Also upgraded the model router to support LanguageModelV4, enabling native V4 model resolution alongside existing V2 and V3 models without regressions.

- Added an optional `availableTools` allowlist to `HarnessModeBase` so each mode can declare one unified list of visible tool names. When set, the harness resolves `activeTools` at LLM-call time and hides any tool not in the list — including workspace tools, which are matched by their exposed names. Per-tool and category `deny` permission rules still take precedence over the allowlist. `undefined` means no mode-level restriction (existing behavior). This moves mode-based tool visibility out of workspace construction and into a single, serializable contract. ([#18463](https://github.com/mastra-ai/mastra/pull/18463))

  **Example**

  Declare a mode that only exposes read and plan-writing tools:

  ```ts
  import type { HarnessMode } from '@mastra/core/harness';

  const planMode: HarnessMode = {
    id: 'plan',
    // Only these tools are visible to the model in plan mode.
    // Workspace tools are matched by their exposed (renamed) names.
    availableTools: ['view', 'find_files', 'search_content', 'write_file', 'submit_plan'],
    // ...other mode config
  };

  // Omitting availableTools (or setting undefined) leaves all tools visible.
  // Set to [] to hide every tool for this mode.
  ```

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

- **Gates and verdict for `runEvals`** ([#18394](https://github.com/mastra-ai/mastra/pull/18394))

  New optional `gates` field accepts scorers that must score 1.0 for the run to pass. Scorers can now use a `{ scorer, threshold }` form to set pass/fail thresholds. `threshold` accepts a number (minimum) or `{ min, max }` for range-based checks (e.g. hallucination where high = bad). The result includes `verdict`, `gateResults`, and `thresholdResults`. Fully backward compatible.

  ```ts
  import { runEvals } from '@mastra/core/evals';
  import { checks } from '@mastra/evals/checks';

  const result = await runEvals({
    data: [{ input: 'What is the weather?' }],
    target: weatherAgent,
    gates: [checks.calledTool('get_weather')],
    scorers: [
      { scorer: faithfulnessScorer, threshold: 0.7 },
      { scorer: hallucinationScorer, threshold: { max: 0.3 } },
    ],
  });

  result.verdict; // 'passed' | 'scored' | 'failed'
  ```

- Added `waitUntil` support for channels so background agent runs survive serverless responses. ([#17832](https://github.com/mastra-ai/mastra/pull/17832))

  Without `waitUntil`, the runtime freezes the invocation as soon as the webhook response returns, killing the agent run mid-flight and leaving the user with no reply.

  **Auto-detected (no config needed):**
  - **Cloudflare Workers** — reads `c.executionCtx.waitUntil`
  - **Netlify Functions** — reads `c.env.context.waitUntil`

  **Requires explicit config:**

  Vercel needs a `waitUntil` function passed directly because it exposes `waitUntil` via AsyncLocalStorage, not the request context. AWS Lambda doesn't need `waitUntil` — it waits for the event loop to drain naturally.

  ```ts
  import { waitUntil } from '@vercel/functions';
  import { SlackProvider } from '@mastra/slack';

  new SlackProvider({ waitUntil });
  ```

  ```ts
  new Agent({
    channels: {
      adapters: { slack: createSlackAdapter({ ... }) },
      waitUntil,
    },
  });
  ```

  For runtimes where `waitUntil` lives on the request context but isn't covered by the default, pass `resolveWaitUntil: (c) => fn | undefined` instead.

  ```ts
  new Agent({
    channels: {
      adapters: { slack: createSlackAdapter({ ... }) },
      resolveWaitUntil: c => c.var.runtime?.waitUntil,
    },
  });
  ```

  Resolution order: `waitUntil` → `resolveWaitUntil(c)` → core default.

- Moved workspace and browser ownership from the Harness to individual sessions. `createSession` now accepts optional `workspace` and `browser` overrides that are passed directly to the `Session` constructor. When no override is provided, the Harness-level config is used as a fallback. ([#18467](https://github.com/mastra-ai/mastra/pull/18467))

  **Breaking changes:**
  - `Session` constructor now requires `workspace: Workspace` and accepts optional `browser?: MastraBrowser`
  - `HarnessConfig.workspace` no longer accepts a `WorkspaceConfig` object — pass a `Workspace` instance or a dynamic factory function
  - `createSession` overrides accept static `Workspace` / `MastraBrowser` instances only (not `DynamicArgument`)
  - Removed `Harness.destroyWorkspace()` — workspace lifecycle is now driven by per-session `workspace_status_changed`, `workspace_ready`, and `workspace_error` events emitted after `workspace.init()` completes
  - `Harness.getWorkspace()` returns the static workspace instance only (returns `undefined` for dynamic factory configs); use `resolveWorkspace({ session })` to resolve and cache a factory outside the request flow
  - `Harness.resolveWorkspace()` now requires a `session` parameter to resolve dynamic factories against the session's request context
  - The `SessionBus` replays the last workspace lifecycle event group to subscribers that attach after session creation, so late listeners always learn the current workspace status

  **Before:**

  ```ts
  const harness = new Harness({
    name: 'my-harness',
    workspace: { name: 'my-workspace' }, // WorkspaceConfig shorthand
  });

  const session = await harness.createSession({
    resourceId: 'user-1',
    ownerId: 'owner-1',
    id: 'session-1',
  });

  // workspace resolved lazily by the harness
  const ws = harness.getWorkspace();
  ```

  **After:**

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

  const harness = new Harness({
    name: 'my-harness',
    workspace: new Workspace({ name: 'my-workspace' }),
  });

  // workspace can be overridden per session with a static instance
  const session = await harness.createSession({
    resourceId: 'user-1',
    ownerId: 'owner-1',
    id: 'session-1',
    workspace: new Workspace({ name: 'ws-1' }),
  });

  // for dynamic per-request workspaces, use the harness-level config
  const harness2 = new Harness({
    name: 'my-harness',
    workspace: ({ requestContext, mastra }) => new Workspace({ name: `ws-${requestContext.session.id}` }),
  });
  ```

- Surface step `providerMetadata` to output-step processors ([#18432](https://github.com/mastra-ai/mastra/pull/18432))

  Output-step processors now receive the finishing step's provider-specific metadata through the new optional `providerMetadata` field on `ProcessOutputStepArgs`. This lets observability and guardrail processors attribute a model response to the provider data behind it — most notably an AWS Bedrock guardrail trace on a `content-filter` block, where the completed-steps array is empty and the assessment was previously unreachable.

  The field is present whenever the underlying model step produced provider metadata, in both streaming and non-streaming generation. Behaviour is unchanged for steps without provider metadata.

  ```ts
  class MyProcessor implements Processor {
    async processOutputStep({ finishReason, providerMetadata }: ProcessOutputStepArgs) {
      if (finishReason === 'content-filter') {
        const guardrail = providerMetadata?.bedrock?.trace?.guardrail;
        // attribute the block to the responsible policy/topic/filter
      }
    }
  }
  ```

- Scope harness session creation with tags so sessions sharing a resourceId can ([#18446](https://github.com/mastra-ai/mastra/pull/18446))
  each resume their own thread.

  `harness.createSession()` now accepts an optional `tags` record. The tags are
  (a) seeded into the new session's state, (b) stamped onto every thread the
  session creates (so thread listings can be filtered back to the session's
  scope), and (c) used to filter initial thread selection: a thread is a resume
  candidate only when its metadata matches every provided tag. Previously, initial
  thread selection only consulted the
  harness-global `initialState.projectPath`; on a multi-session server (where one
  Harness serves many scopes) a session could resume the most recently updated
  thread from a _different_ scope that shared the resourceId. Using a generic tag
  record (e.g. `{ projectPath }`) keeps room for future scoping dimensions without
  further API changes.

  The `@mastra/client-js` `HarnessSession.create()` method accepts `{ tags }`, and
  the `POST /harness/:id/sessions` route accepts a `tags` body field.

  ```ts
  // before: initial thread chosen by resourceId only
  const session = await harness.createSession({ resourceId, id, ownerId });

  // after: initial thread scoped to this worktree via a tag
  const session = await harness.createSession({
    resourceId,
    id,
    ownerId,
    tags: { projectPath: '/repo/worktree-a' },
  });
  ```

#### Patch Changes

- Fixed channel rendering on serverless runtimes (Vercel, AWS Lambda, Cloudflare Workers, Netlify). ([#17832](https://github.com/mastra-ai/mastra/pull/17832))

  **No more duplicate replies.** On serverless, the same webhook event can be processed by multiple concurrent invocations. Previously each one rendered the agent reply independently, producing duplicate messages. Now only the invocation that wins the run lease renders — losers resolve cleanly without posting.

  **Runs no longer get killed mid-flight.** The channel handler now keeps the serverless invocation alive until the agent run finishes, instead of returning immediately and relying on a background subscription that gets frozen.

  **Multi-step replies fully render.** Agents that make multiple LLM calls per run (e.g. tool use → follow-up) now render every step's text instead of only the first.

  **Tool approval buttons now appear on all rendering paths.** Previously, tool approval prompts (approve/deny buttons) could fail to render when using output processors. Fixed by routing `tool-call-approval` chunks through the output processor pipeline.

  **Tool approval and decline use the same rendering path as regular messages.** Approval and decline actions now flow through the per-run output processor instead of a separate subscription-based path, making rendering consistent across all message types.

  **User output processors now run before channel rendering.** User-configured output processors (e.g. PII redaction, translation) now transform chunks before the channel renders them to the platform, instead of after.

  **Concurrency strategy switched to `concurrent`.** The chat-sdk queue lock could get stuck in frozen serverless invocations. Ordering is now handled by the signals/lease layer, removing the stale-lock failure mode.

- feat(playground): render ask_user tool as interactive question UI in Studio ([#18374](https://github.com/mastra-ai/mastra/pull/18374))

  Adds an `AskUserBadge` component that renders suspended `ask_user` tool calls
  as interactive prompts with clickable option buttons (single/multi-select) or
  free-text input. The user's answer is sent as `resumeData` through the existing
  `sendToolApproval` flow to properly resume the tool.

  Plumbing changes:
  - `sendToolApprovalBodySchema` now accepts optional `resumeData`
  - `agent.sendToolApproval()` passes custom `resumeData` when provided
  - `@mastra/client-js` and `@mastra/react` expose the new parameter

- Update provider registry and model documentation with latest models and providers ([`7c9dd77`](https://github.com/mastra-ai/mastra/commit/7c9dd77bd18cb8dc72797e25f1a0fbdc71a11347))

- Forward the parent `abortSignal` to delegated subagents so that aborting a supervisor's `stream()` or `generate()` call cancels in-flight subagents instead of letting them run to completion. ([#17561](https://github.com/mastra-ai/mastra/pull/17561))

  Previously, calling `AbortController.abort()` on a supervisor only stopped the supervisor itself: each delegated subagent kept looping and calling its own tools and the LLM for several seconds after the abort, because the delegation tool dropped the parent's `abortSignal`. The signal is now propagated to every delegation path (`stream`, `generate`, `resumeStream`, `resumeGenerate`, and the legacy variants).

  ```typescript
  const controller = new AbortController();

  const stream = await supervisor.stream('Research AI trends', {
    abortSignal: controller.signal,
  });

  // Now also cancels any in-flight subagents, not just the supervisor
  controller.abort();
  ```

  Fixes #14820.

- Added `DatasetItemPayload`, a new exported type describing the user-supplied fields of a dataset item. ([#18489](https://github.com/mastra-ai/mastra/pull/18489))

  Dataset item inputs (`AddDatasetItemInput`, `UpdateDatasetItemInput`, and batch insert items) now share this type, so they stay consistent automatically. This is a type-only change with no runtime behavior change.

- Added `collectToolMocks` export to `@mastra/core/evals`. The helper walks a `TrajectoryStep[]` and returns `DatasetItemToolMock[]`, collecting top-level `tool_call` and `mcp_tool_call` steps in recorded order (sub-agent delegations are emitted as `matchArgs: 'ignore'`). Consumers can now derive item-level tool mocks from a hydrated trajectory directly from `@mastra/core`. ([#18488](https://github.com/mastra-ai/mastra/pull/18488))

  ```ts
  import { collectToolMocks } from '@mastra/core/evals';
  import type { Trajectory } from '@mastra/core/evals';

  const mocks = collectToolMocks(trajectory.steps);
  ```

- Fixed Studio failing to boot in the browser with `TypeError: os.tmpdir is not a function`. The local sandbox resolved its mount marker directory at module-load time via `os.tmpdir()`, which crashed the client bundle where `node:os` has no `tmpdir`. The directory is now resolved lazily, so importing the Agent runtime in the browser no longer throws. ([#18524](https://github.com/mastra-ai/mastra/pull/18524))

- Plan rejection now returns distinct tool results for inline-feedback vs. chat-based revision paths, enabling the TUI to stop the agent and wait for user chat messages. ([#18323](https://github.com/mastra-ai/mastra/pull/18323))

- Added an optional `handlesModel` hook to the model gateway interface so a gateway can claim bare provider model IDs (like `__GATEWAY_ANTHROPIC_MODEL_OPUS__`) it is able to authenticate, before routing falls back to the default models.dev catalog. This lets gateways that hold credentials (for example OAuth-backed logins) be selected for auth resolution and availability checks instead of being bypassed. ([#18503](https://github.com/mastra-ai/mastra/pull/18503))

- Preserved response message boundaries when processor message arrays are reapplied after output step processing. ([#18337](https://github.com/mastra-ai/mastra/pull/18337))

- Reworked plan mode to use named plan files. The agent writes its plan to a markdown file under `.mastracode/plans/` and calls `submit_plan` with the `path` to that file, so multiple plans stay on disk for review. The host validates the submitted path is a `.md` file inside `.mastracode/plans/` before reading it, so the tool can't be pointed at arbitrary files. Plan mode can write any `.md` file inside `.mastracode/plans/` (enforced by a tool guard) but nothing else, submitted plan snapshots are persisted for history replay, revision diffs are computed from a real line-ending-normalized LCS diff and only shown when the change is small relative to the plan, "Request Changes" stops the run immediately with no trailing model output, and diffs only compare revisions of the same plan file. ([#18490](https://github.com/mastra-ai/mastra/pull/18490))

- Fixed `InferToolInput`, `InferToolOutput`, `InferUITool`, and `InferUITools` so tools created with `createTool({ outputSchema: z.object(...) })` now produce a typed `{ input, output }` instead of falling through to `never`. UIMessage parts (`tool-<name>`) and other consumers can now discriminate on the inferred input/output types without manual workaround shims. ([#17039](https://github.com/mastra-ai/mastra/pull/17039))

  ```ts
  const echo = createTool({
    id: 'echo',
    inputSchema: z.object({ x: z.string() }),
    outputSchema: z.object({ y: z.number() }),
    execute: async ({ x }) => ({ y: x.length }),
  });

  // before: { input: never; output: never }
  // after:  { input: { x: string }; output: { y: number } }
  type UI = InferUITool<typeof echo>;
  ```

  Closes the gap left by #7184.

- Fixed the in-memory workflow store resetting a run's `createdAt` on every re-persist, so it now matches the persistent stores. Re-persisting an existing run preserves the original `createdAt` and only advances `updatedAt`. ([#18004](https://github.com/mastra-ai/mastra/pull/18004))

- Fixed duplicate step ID warnings in Inngest workflows. When a workflow step ran, two persistence calls with the same ID caused AUTOMATIC_PARALLEL_INDEXING warnings in Inngest logs. Steps now use unique IDs for each persistence call, eliminating the warnings. ([#17320](https://github.com/mastra-ai/mastra/pull/17320))

- Fixed thread auto-resume selecting the wrong thread in git worktrees by scoping startup selection to threads tagged with the current project path. When Mastra Code detects a matching project thread on a different resource after resourceId drift, it now prompts before cloning and resuming that thread under the current resource; accepting the prompt leaves the old thread untouched and loads the clone, while declining starts fresh and leaves the old resource untouched. ([#18333](https://github.com/mastra-ai/mastra/pull/18333))

- Fixed duplicate stream events when attaching to an in-progress durable or evented agent run. ([#18191](https://github.com/mastra-ai/mastra/pull/18191))

  When you call `agent.observe(runId, { offset })` (or reconnect to a stream with replay) while `agent.stream()` is still running, the buffered portion of `output.textStream` could be delivered twice before settling into normal single delivery. Late observers now receive each text-delta exactly once.

  **Why:** Two issues in the resumable-stream path could double events.
  - If you passed a caching pubsub to `new Mastra({ pubsub })` (e.g. `withCaching(...)`), the agent adopted it as its inner transport and then wrapped it again in a second caching layer sharing the same cache. Every event was stored twice, so replay delivered the buffered prefix doubled. The agent now reuses the existing caching pubsub instead of double-wrapping it.

  ```ts
  // This setup no longer double-caches:
  const cache = new InMemoryServerCache();
  const mastra = new Mastra({ pubsub: withCaching(new EventEmitterPubSub(), cache), cache });
  ```

  - Replay/live deduplication keyed on `event.id`, but the underlying pubsub regenerates `id` on publish, so the cached copy and the live copy of the same event carried different ids and dedup never matched. Deduplication now keys on the event's stable sequential index, which is preserved across both paths.

- Workspace skills now refresh on each skill-tool invocation, so edits to SKILL.md files (or new skills added to the workspace) are picked up between tool calls without restarting the server. Set `checkSkillFileMtime: true` on the workspace config to also detect content changes to existing SKILL.md files. Fixes [#16640](https://github.com/mastra-ai/mastra/issues/16640). ([#16678](https://github.com/mastra-ai/mastra/pull/16678))

- Fixed createTool type error when passing jsonSchema() or a Schema object from @ai-sdk/provider-utils as inputSchema — no more cast needed ([#17435](https://github.com/mastra-ai/mastra/pull/17435))

- Fixed reasoning text being lost when agent messages are persisted to memory and recalled on the next turn ([#17803](https://github.com/mastra-ai/mastra/pull/17803))

- Preserve original tool-call arguments when a tool result is persisted without its matching tool-call in the same message. Previously the arguments were saved as empty `{}`, which poisoned the model's context and caused it to emit invalid empty-argument tool calls after a few cycles. The adapter now recovers the original arguments from prior persisted messages, covering the server resume path, AG-UI hosts replaying client-tool results, and `@mastra/client-js` streaming recursion. ([#16981](https://github.com/mastra-ai/mastra/pull/16981))

  Also execute every streamed client tool call emitted in the same tool-calls step before continuing, instead of only continuing with one tool result. Fixes #16017 and #15576.

- Fixed images from tool results being dropped when using AI SDK v6 (`@ai-sdk/*` v3) providers. Tools that return image data via `toModelOutput` (for example screenshots) now reach the model correctly instead of arriving empty. ([#18396](https://github.com/mastra-ai/mastra/pull/18396))

- Address post-merge review follow-ups from the evented-workflow serialization work: ([#18385](https://github.com/mastra-ai/mastra/pull/18385))
  - `Mastra` now logs a warning when the TTL sweep evicts a run-scoped workflow that was still registered, surfacing abandoned suspended runs instead of dropping them silently.
  - The `RegExp` codec's `// lgtm[js/regex-injection]` suppression now spells out all three structural gates (`isEnvelope` length cap + flag whitelist, local re-narrowing in `decodeRegExpEnvelope`, `try/catch` fallback) at the suppression site so future readers don't reintroduce escaping.
  - The `evented-unix-pubsub` scenario test now allocates its Unix socket under a short `/tmp/aim-*` directory instead of `os.tmpdir()` so it stays under the macOS 104-byte `sun_path` limit.

- Added a browser-safe `@mastra/core/utils/collect-tool-mocks` export for `collectToolMocks`. The previous `@mastra/core/evals` barrel pulls in Node-only modules (`node:crypto`), which broke bundling in browser apps such as the Studio playground. Import the helper from the new subpath in browser code: ([#18504](https://github.com/mastra-ai/mastra/pull/18504))

  ```ts
  import { collectToolMocks } from '@mastra/core/utils/collect-tool-mocks';
  ```

  The `@mastra/core/evals` export still works for Node-side callers.

- Fixed dynamic workspace resolution to pass the full request context to workspace factories, allowing them to access session state through getState(), and handled async stream aborts correctly so initial streamed output is preserved. ([#18497](https://github.com/mastra-ai/mastra/pull/18497))

- Added stable session `id` and `ownerId` to Harness sessions. `SessionIdentity` now exposes `getId()` and `getOwnerId()`, and `harness.createSession()` accepts `id` and `ownerId` to set them. These identifiers are stable for the life of the session — they do not change when the resource ID is switched — and are surfaced in the harness request context session snapshot. ([#18372](https://github.com/mastra-ai/mastra/pull/18372))

  **Breaking change:** `id` and `ownerId` are now **required** on both `harness.createSession()` and the `Session`/`SessionIdentity` constructors. There are no defaults.

  ```typescript
  // Before
  const session = new Session({ resourceId: 'my-resource' });
  const session = await harness.createSession();

  // After
  const session = new Session({
    resourceId: 'my-resource',
    id: 'my-session-id',
    ownerId: 'my-owner-id',
  });
  const session = await harness.createSession({
    id: 'my-session-id',
    ownerId: 'my-owner-id',
  });

  session.identity.getId(); // 'my-session-id'
  session.identity.getOwnerId(); // 'my-owner-id'
  ```

- Fixed a thread becoming permanently unresponsive after a signal woke it without anyone reading the agent's response. ([#18493](https://github.com/mastra-ai/mastra/pull/18493))

  When a signal woke a thread in the background and nothing consumed the resulting run, the run never finished and the thread stayed busy forever. Any further signals sent to that thread were then absorbed by the stuck run instead of starting a new one, so the agent stopped responding on that thread. Threads where you read the agent's response normally were never affected. Background-woken threads now finish on their own and keep accepting new signals.

  Also fixed `consumeStream()` so that `await consumeStream()` always waits for the run to actually finish, even when consumption was already started elsewhere, and so that every caller's `onError` runs if the stream fails.

- Fixed tool-level `background` config being silently ignored. The `background` option passed to `createTool` was accepted by the type system but never stored on the tool instance, so tools opted into background execution at the tool level always ran in the foreground. The option is now stored on the tool and dispatched as a background task. ([#18454](https://github.com/mastra-ai/mastra/pull/18454))

  ```typescript
  const researchTool = createTool({
    id: 'research',
    description: 'Run a long research job',
    inputSchema: z.object({ topic: z.string() }),
    background: { enabled: true, timeoutMs: 600_000 },
    execute: async ({ topic }, context) => {
      // ...
    },
  });
  ```

- Fixed semantic recall not injecting recalled messages into the prompt when chatting through Studio. User messages sent through the agent signal pipeline (used by the Studio playground) were stored as signal rows and skipped when extracting the recall query, so recalled context never reached the model even though retrieval worked and the same request succeeded via the HTTP API. Fixes [#17797](https://github.com/mastra-ai/mastra/issues/17797) ([#17818](https://github.com/mastra-ai/mastra/pull/17818))

- Fixed durable agents (`createDurableAgent`) silently dropping stored working memory from the prompt. Working memory was saved correctly but never injected back, so per-request preferences had no effect on output. Durable preparation now establishes the memory context before resolving input processors so the working-memory injector is included. ([#18411](https://github.com/mastra-ai/mastra/pull/18411))

- Fixed conditional workflows so that re-running or rehydrating a run (time travel) no longer leaves the wrong branch marked as active. When a paused or replayed run lands on a conditional, arms whose condition does not evaluate truthy are now correctly recorded as skipped instead of staying stuck in a running state. ([#18228](https://github.com/mastra-ai/mastra/pull/18228))

  The server workflow and schedule run-status response schemas now include the `'skipped'` status so they stay in sync with core's `WorkflowRunStatus`.

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

- Removed experimental flag from retrieval-mode observational memory. The retrieval API is now stable. ([#18324](https://github.com/mastra-ai/mastra/pull/18324))

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

- Added `workflowSnapshotToStream` utility to convert a `WorkflowState` (as returned by `getWorkflowRunById`) into an AI SDK-compatible stream. This lets you display historical workflow runs using the same `useChat`-powered UI components used for live workflow streams. ([#18453](https://github.com/mastra-ai/mastra/pull/18453))

  **Example usage:**

  ```ts
  import { workflowSnapshotToStream } from '@mastra/ai-sdk';
  import { createUIMessageStreamResponse } from 'ai';

  const workflowRun = await mastra.getWorkflow('myWorkflow').getWorkflowRunById(runId);
  const stream = workflowSnapshotToStream(workflowRun);
  return createUIMessageStreamResponse({ stream });
  ```

#### Patch Changes

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

### [@mastra/auth-google@0.1.0](https://github.com/mastra-ai/mastra/blob/@mastra/auth-google@0.1.0/auth/google/CHANGELOG.md)
#### Minor Changes

- Added native Google Workspace authentication and group-based RBAC. ([#18160](https://github.com/mastra-ai/mastra/pull/18160))
  - `MastraAuthGoogle` lets developers authenticate users with Google accounts and restrict access to trusted Google Workspace domains.
  - `MastraRBACGoogle` lets developers map Google Workspace groups to Mastra permissions for role-based access.

  **Usage:**

  ```typescript
  import { MastraAuthGoogle, MastraRBACGoogle } from '@mastra/auth-google';

  const mastra = new Mastra({
    server: {
      auth: new MastraAuthGoogle({
        allowedDomains: ['example.com'],
      }),
      rbac: new MastraRBACGoogle({
        serviceAccount: {
          clientEmail: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL!,
          privateKey: process.env.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY!,
          subject: process.env.GOOGLE_WORKSPACE_ADMIN_EMAIL!,
        },
        roleMapping: {
          'admins@example.com': ['*'],
          'engineering@example.com': ['agents:*', 'workflows:*'],
          _default: [],
        },
      }),
    },
  });
  ```

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Improved auth package builds by removing the direct core dependency from auth providers while preserving the existing public auth APIs. ([#17142](https://github.com/mastra-ai/mastra/pull/17142))

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

- Enrich the agent-controller HTTP session surface so a client can render a status ([#18446](https://github.com/mastra-ai/mastra/pull/18446))
  line, read behavior settings, and scope threads per working directory.

  `GET /agent-controller/:controllerId/sessions/:resourceId` now also returns:
  - `omProgress` — the status-line slice of observational-memory progress
    (pending tokens vs. observation threshold, accumulated observations vs.
    reflection threshold, plus projected message removal / reflection savings)
  - `tokenUsage` — cumulative token usage for the current thread
  - `settings` — agent behavior settings (`yolo`, `thinkingLevel`,
    `notifications`, `smartEditing`)

  `GET /agent-controller/:controllerId/sessions/:resourceId/threads` accepts
  an optional `tags` query param (a JSON-encoded object). A single resourceId can
  be shared across git worktrees of the same repo (the id is derived from the git
  URL), so passing `tags` scopes the list to threads matching every tag (e.g.
  `{ projectPath }` for the working directory). Each returned thread now also
  includes the scoping `tags` it was stamped with at creation.

  The session event stream route (`.../stream`) now enqueues raw event objects
  and lets the server adapter handle SSE framing, fixing a double-framing bug
  where events were wrapped twice (`data: "data: {...}\n\n"\n\n`) and could not be
  parsed by clients.

  `@mastra/client-js` gains the matching types and reads:
  - `AgentControllerOMProgress` and `AgentControllerSessionSettings`, surfaced on
    `AgentControllerSessionState` (`omProgress`, `tokenUsage`, `settings`)
  - a `display_state_changed` event in `KnownAgentControllerEvent` carrying the
    status-line figures
  - `AgentControllerSession.listThreads()` now accepts either a number
    (back-compat) or `{ limit?, tags? }`

  Example:

  ```ts
  const session = client.getAgentController('code').session(resourceId);

  // Scope a worktree's thread list (and resumed session) to its own threads.
  await session.create({ tags: { projectPath: '/repo/worktree-a' } });
  const threads = await session.listThreads({ tags: { projectPath: '/repo/worktree-a' } });

  // Read the status-line figures and agent settings.
  const state = await session.state();
  state.omProgress; // observational-memory progress
  state.tokenUsage; // cumulative token usage
  state.settings; // { yolo, thinkingLevel, notifications, smartEditing }
  ```

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

- Scope harness session creation with tags so sessions sharing a resourceId can ([#18446](https://github.com/mastra-ai/mastra/pull/18446))
  each resume their own thread.

  `harness.createSession()` now accepts an optional `tags` record. The tags are
  (a) seeded into the new session's state, (b) stamped onto every thread the
  session creates (so thread listings can be filtered back to the session's
  scope), and (c) used to filter initial thread selection: a thread is a resume
  candidate only when its metadata matches every provided tag. Previously, initial
  thread selection only consulted the
  harness-global `initialState.projectPath`; on a multi-session server (where one
  Harness serves many scopes) a session could resume the most recently updated
  thread from a _different_ scope that shared the resourceId. Using a generic tag
  record (e.g. `{ projectPath }`) keeps room for future scoping dimensions without
  further API changes.

  The `@mastra/client-js` `HarnessSession.create()` method accepts `{ tags }`, and
  the `POST /harness/:id/sessions` route accepts a `tags` body field.

  ```ts
  // before: initial thread chosen by resourceId only
  const session = await harness.createSession({ resourceId, id, ownerId });

  // after: initial thread scoped to this worktree via a tag
  const session = await harness.createSession({
    resourceId,
    id,
    ownerId,
    tags: { projectPath: '/repo/worktree-a' },
  });
  ```

#### Patch Changes

- feat(playground): render ask_user tool as interactive question UI in Studio ([#18374](https://github.com/mastra-ai/mastra/pull/18374))

  Adds an `AskUserBadge` component that renders suspended `ask_user` tool calls
  as interactive prompts with clickable option buttons (single/multi-select) or
  free-text input. The user's answer is sent as `resumeData` through the existing
  `sendToolApproval` flow to properly resume the tool.

  Plumbing changes:
  - `sendToolApprovalBodySchema` now accepts optional `resumeData`
  - `agent.sendToolApproval()` passes custom `resumeData` when provided
  - `@mastra/client-js` and `@mastra/react` expose the new parameter

- Preserve original tool-call arguments when a tool result is persisted without its matching tool-call in the same message. Previously the arguments were saved as empty `{}`, which poisoned the model's context and caused it to emit invalid empty-argument tool calls after a few cycles. The adapter now recovers the original arguments from prior persisted messages, covering the server resume path, AG-UI hosts replaying client-tool results, and `@mastra/client-js` streaming recursion. ([#16981](https://github.com/mastra-ai/mastra/pull/16981))

  Also execute every streamed client tool call emitted in the same tool-calls step before continuing, instead of only continuing with one tool result. Fixes #16017 and #15576.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

- Add the experimental Signals observability experience to Studio. Introduces a route-driven enterprise topics/signals trace explorer (topic, subtopic, and trace panels) and a reusable scatter plot chart component in `@mastra/playground-ui`, with the reusable Signals UI and data placed behind the playground-ui EE export boundary. The Signals page is gated behind a server-injected `MASTRA_SIGNALS_UI` runtime flag: the Studio HTML exposes `window.MASTRA_SIGNALS_UI`, which the CLI and deployers populate from the `MASTRA_SIGNALS_UI` env var (default off). When disabled, the Signals sidebar item and `/signals` routes are not registered. ([#18138](https://github.com/mastra-ai/mastra/pull/18138))

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Fixed workflow runs preserving their original creation time when re-persisted in DynamoDB storage, including concurrent saves. ([#18004](https://github.com/mastra-ai/mastra/pull/18004))

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

- Make concurrent S3 and GCS mounts reliable in the same sandbox ([#18512](https://github.com/mastra-ai/mastra/pull/18512))

  Mounting several buckets at once, or restoring mounts after a pause/resume, could previously fail or pick up the wrong credentials because every mount shared one temporary credentials file and overwrote each other's. Each mount now gets its own credentials file, so they no longer interfere. (Azure already worked this way.)

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

- Added `checks`, a new namespace of micro-scorers for common eval assertions. ([#18392](https://github.com/mastra-ai/mastra/pull/18392))

  **What changed**
  - Added text checks: `includes`, `excludes`, `equals`, `matches`, and `similarity`.
  - Added tool checks: `calledTool`, `didNotCall`, `toolOrder`, `maxToolCalls`, `usedNoTools`, and `noToolErrors`.
  - You can now import checks from `@mastra/evals/checks`.

  **Example**

  ```ts
  import { checks } from '@mastra/evals/checks';

  const scorers = [
    checks.includes('sunny'),
    checks.calledTool('get_weather'),
    checks.toolOrder(['search', 'summarize']),
    checks.noToolErrors(),
  ];
  ```

#### Patch Changes

- **Gates and verdict for `runEvals`** ([#18394](https://github.com/mastra-ai/mastra/pull/18394))

  New optional `gates` field accepts scorers that must score 1.0 for the run to pass. Scorers can now use a `{ scorer, threshold }` form to set pass/fail thresholds. `threshold` accepts a number (minimum) or `{ min, max }` for range-based checks (e.g. hallucination where high = bad). The result includes `verdict`, `gateResults`, and `thresholdResults`. Fully backward compatible.

  ```ts
  import { runEvals } from '@mastra/core/evals';
  import { checks } from '@mastra/evals/checks';

  const result = await runEvals({
    data: [{ input: 'What is the weather?' }],
    target: weatherAgent,
    gates: [checks.calledTool('get_weather')],
    scorers: [
      { scorer: faithfulnessScorer, threshold: 0.7 },
      { scorer: hallucinationScorer, threshold: { max: 0.3 } },
    ],
  });

  result.verdict; // 'passed' | 'scored' | 'failed'
  ```

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Avoided reapplying unchanged messages when GitHub signals only emit subscription hint side effects. ([#18337](https://github.com/mastra-ai/mastra/pull/18337))

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Fixed `persistWorkflowSnapshot` resetting a workflow run's `createdAt` on every re-persist. The default execution engine re-persists a run's snapshot on every step, so `createdAt` drifted to the last activity time and jumped forward on suspend/resume. Re-persisting now preserves the original `createdAt` and only advances `updatedAt`, so `listWorkflowRuns` ordering, fromDate/toDate filters, and the creation time shown in Studio stay correct. ([#18004](https://github.com/mastra-ai/mastra/pull/18004))

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

- Fixed unhandled promise rejection when async buffered observation fails due to missing catch handler ([#18026](https://github.com/mastra-ai/mastra/pull/18026))

- Removed experimental flag from retrieval-mode observational memory. The retrieval API is now stable. ([#18324](https://github.com/mastra-ai/mastra/pull/18324))

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

- Fixed workflow snapshots and AI spans creating duplicate records instead of updating in place. Each workflow step previously inserted a new row, causing unbounded table growth and degraded read performance. ([#18460](https://github.com/mastra-ai/mastra/pull/18460))

- Fixed workflow runs preserving their original creation time when re-persisted in MySQL storage, including concurrent saves. ([#18004](https://github.com/mastra-ai/mastra/pull/18004))

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Fixed pricing lookup when a provider-reported response model does not match a known pricing entry but the configured model does. Cost estimation now falls back to the configured model before reporting `no_matching_model`. ([#16585](https://github.com/mastra-ai/mastra/pull/16585))

- Tool telemetry is now more informative for tools that transform their output before sending to the model. Tool-result spans in traces show the actual value the model received instead of being empty, and step input previews display the tool result content instead of an opaque `[tool-result]` placeholder. This makes it easier to debug tool behavior in Langfuse, Datadog, and other observability providers. ([#18417](https://github.com/mastra-ai/mastra/pull/18417))

### [@mastra/otel-bridge@1.4.0](https://github.com/mastra-ai/mastra/blob/@mastra/otel-bridge@1.4.0/observability/otel-bridge/CHANGELOG.md)
#### Minor Changes

- Added `tracerProvider` and `loggerProvider` options to `OtelBridgeConfig`, allowing spans and logs to be routed to non-global OpenTelemetry providers. ([#18185](https://github.com/mastra-ai/mastra/pull/18185))

  ```ts
  import { OtelBridge } from '@mastra/otel-bridge';

  const bridge = new OtelBridge({
    tracerProvider: myLangfuseTracerProvider,
    loggerProvider: myCustomLoggerProvider,
  });
  ```

  Both fields are optional and default to the global provider when omitted — no breaking changes.

#### Patch Changes

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

- Fixed workflow runs advancing their update time when re-persisted in PostgreSQL storage. ([#18004](https://github.com/mastra-ai/mastra/pull/18004))

- Fixed PgVector numeric range filters (`$gt`, `$gte`, `$lt`, `$lte`) so rows with non-numeric metadata values no longer fail the whole query. ([#18430](https://github.com/mastra-ai/mastra/pull/18430))

  A single document with a value like `{ price: 'N/A' }` used to make the entire query error out, breaking all range-filtered vector queries (and semantic recall using `semanticRecall.filter`) on that index. Rows whose value isn't a number are now skipped for numeric range checks instead, matching the behavior of the other Mastra vector stores. Numeric rows still match as expected.

  ```typescript
  await pgVector.upsert({
    indexName: 'products',
    vectors: [
      [1, 0],
      [0, 1],
    ],
    metadata: [{ price: 100 }, { price: 'N/A' }],
  });

  // Before: threw "invalid input syntax for type numeric: N/A"
  // After: returns only the row whose price is actually a number
  await pgVector.query({
    indexName: 'products',
    queryVector: [1, 0],
    topK: 10,
    filter: { price: { $gt: 50 } },
  });
  ```

- Negated numeric range filters (`$not` with `$gt`, `$gte`, `$lt`, `$lte`) now correctly exclude rows where the filtered field is missing or non-numeric. Previously these rows were incorrectly included in results. ([#18466](https://github.com/mastra-ai/mastra/pull/18466))

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

- Added a reusable environment variables editor with .env import, bulk paste support, and custom-row hook support. ([#18371](https://github.com/mastra-ai/mastra/pull/18371))

  ```tsx
  import { EnvironmentVariablesEditor, useEnvironmentVariablesEditor } from '@mastra/playground-ui';

  function SettingsEnvVars() {
    const editor = useEnvironmentVariablesEditor({
      initialRows: [{ key: 'PUBLIC_BASE_URL', value: 'https://example.com' }],
    });

    return (
      <EnvironmentVariablesEditor
        editor={editor}
        actions={
          <button type="button" onClick={() => editor.getEnvironmentVariablesForSubmit()}>
            Save
          </button>
        }
      />
    );
  }
  ```

- Added sticky row headers to DataList. ([#18222](https://github.com/mastra-ai/mastra/pull/18222))

  Use `sticky="start"` on the leading `DataList.TopCell` and render matching row cells with `DataList.RowHeaderCell`:

  ```tsx
  <DataList columns="auto auto auto" variant="lined">
    <DataList.Top>
      <DataList.TopCell sticky="start">Model</DataList.TopCell>
      <DataList.TopCell>Input</DataList.TopCell>
      <DataList.TopCell>Output</DataList.TopCell>
    </DataList.Top>
    <DataList.RowStatic>
      <DataList.RowHeaderCell>Model A</DataList.RowHeaderCell>
      <DataList.Cell>1,200</DataList.Cell>
      <DataList.Cell>800</DataList.Cell>
    </DataList.RowStatic>
  </DataList>
  ```

  Metrics tables now render directly through DataList so sticky row headers share the same header colors and hover treatment as the rest of the list system. Metrics tables also follow the default DataList variant, and sticky row headers use the same neutral header treatment as column headers.

  DataList now exposes `stickyHeaderBackground` to keep the top header and sticky row-header fill in sync, and forwards `mask` to the underlying ScrollArea so sticky-start tables can disable the left edge fade.

  Added `DataList.NumberCell` for right-aligned numeric columns. It bakes in the tabular-figure, compact metric-table styling, with a `highlight` prop for the emphasized value:

  ```tsx
  <DataList.NumberCell>1,200</DataList.NumberCell>
  <DataList.NumberCell highlight>$0.42</DataList.NumberCell>
  ```

  The metrics-specific `MetricsDataTable` wrapper was removed. Use DataList for DS-owned metrics table layouts.

- Add the experimental Signals observability experience to Studio. Introduces a route-driven enterprise topics/signals trace explorer (topic, subtopic, and trace panels) and a reusable scatter plot chart component in `@mastra/playground-ui`, with the reusable Signals UI and data placed behind the playground-ui EE export boundary. The Signals page is gated behind a server-injected `MASTRA_SIGNALS_UI` runtime flag: the Studio HTML exposes `window.MASTRA_SIGNALS_UI`, which the CLI and deployers populate from the `MASTRA_SIGNALS_UI` env var (default off). When disabled, the Signals sidebar item and `/signals` routes are not registered. ([#18138](https://github.com/mastra-ai/mastra/pull/18138))

#### Patch Changes

- Fixed overlay content components to support Base UI positioning options. ([#18431](https://github.com/mastra-ai/mastra/pull/18431))

- Added direct Playground UI subpaths for shared helpers so apps can avoid the root barrel when importing existing utility and rule builder APIs. ([#18502](https://github.com/mastra-ai/mastra/pull/18502))

  ```ts
  import { is401UnauthorizedError } from '@mastra/playground-ui/utils/errors';
  import type { JsonSchema } from '@mastra/playground-ui/utils/json-schema';
  import { RuleBuilder } from '@mastra/playground-ui/components/RuleBuilder';
  ```

- Show an unavailable state when the configured observability storage does not support listing logs, and stop polling the logs endpoint for that non-transient capability error. ([#18375](https://github.com/mastra-ai/mastra/pull/18375))

- Added a direct utility import for the class name helper so applications can avoid the root Playground UI barrel. ([#18458](https://github.com/mastra-ai/mastra/pull/18458))

  ```ts
  import { cn } from '@mastra/playground-ui/utils/cn';
  ```

- Added direct import paths for toast and Playground UI icons so apps can avoid the root Playground UI barrel when using high-traffic utilities and icon components. ([#18470](https://github.com/mastra-ai/mastra/pull/18470))

  ```ts
  import { Toaster } from '@mastra/playground-ui/components/Toaster';
  import { AgentIcon } from '@mastra/playground-ui/icons/AgentIcon';
  import { toast } from '@mastra/playground-ui/utils/toast';
  ```

- Move the Memory Studio (timeline, flamegraph, and observational-memory detail) into the agent chat view as an opt-in panel. ([#18272](https://github.com/mastra-ai/mastra/pull/18272))
  - The standalone Memory nav entry, `/memory` routes, and the separate thread/chat list are removed; the studio is now opened from the chat view and shown inside the Memory sidepanel, with the agent layout's left resizable panel expanding when the detail opens. Clicking the flamegraph timeline drives a replay cursor that highlights the matching observational-memory record. Marker types are imported from `@mastra/memory` instead of being redeclared in the UI so the studio stays in sync with the stream format.
  - `MemoryStudioPanel` gains an optional `contextWindow` prop so callers can supply authoritative message/observation token counts and thresholds; when provided these take precedence over values re-derived from message markers, keeping the panel's MESSAGES/OBSERVATIONS readout in sync with the observational-memory sidebar (marker-derived values remain the fallback for standalone usage).
  - `MemoryStudioPanel` now shows both Messages and Observations progress bars, matching the collapsed memory sidebar. The FlameGraph zoom range is lifted into the panel and filters the observation list: collapsing the range hides out-of-range observations and "Reset zoom" restores the full list. `FlameGraph` gains optional controlled `zoomRange`/`onZoomRangeChange` props (uncontrolled usage is unchanged).

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

- Added `tasks` as a first-class return value on `useChat`. Task state is incrementally updated from streaming task state signals and tool results — no message rescanning required. ([#18374](https://github.com/mastra-ai/mastra/pull/18374))

  Fixed step framing markers leaking into the chat UI. The MessageFactory now renders step-start parts as nothing by default instead of routing them to your fallback renderer, so internal step boundaries no longer show up as stray output. You can still opt in to rendering a step divider by supplying a StepStart renderer.

#### Patch Changes

- feat(playground): render ask_user tool as interactive question UI in Studio ([#18374](https://github.com/mastra-ai/mastra/pull/18374))

  Adds an `AskUserBadge` component that renders suspended `ask_user` tool calls
  as interactive prompts with clickable option buttons (single/multi-select) or
  free-text input. The user's answer is sent as `resumeData` through the existing
  `sendToolApproval` flow to properly resume the tool.

  Plumbing changes:
  - `sendToolApprovalBodySchema` now accepts optional `resumeData`
  - `agent.sendToolApproval()` passes custom `resumeData` when provided
  - `@mastra/client-js` and `@mastra/react` expose the new parameter

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

- `nodeRedisPreset` now adapts the three list operations (`llen`, `rpush`, `lrange`) to their camelCase forms on `node-redis` v4+ clients (`lLen`, `rPush`, `lRange`). Extends the same adapter pattern already used for `set` and `scan`. ioredis and Upstash users are unaffected (defaults remain lowercase, matching their APIs). ([#18408](https://github.com/mastra-ai/mastra/pull/18408))

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

- Fixed createTool type error when passing jsonSchema() or a Schema object from @ai-sdk/provider-utils as inputSchema — no more cast needed ([#17435](https://github.com/mastra-ai/mastra/pull/17435))

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

- Enrich the agent-controller HTTP session surface so a client can render a status ([#18446](https://github.com/mastra-ai/mastra/pull/18446))
  line, read behavior settings, and scope threads per working directory.

  `GET /agent-controller/:controllerId/sessions/:resourceId` now also returns:
  - `omProgress` — the status-line slice of observational-memory progress
    (pending tokens vs. observation threshold, accumulated observations vs.
    reflection threshold, plus projected message removal / reflection savings)
  - `tokenUsage` — cumulative token usage for the current thread
  - `settings` — agent behavior settings (`yolo`, `thinkingLevel`,
    `notifications`, `smartEditing`)

  `GET /agent-controller/:controllerId/sessions/:resourceId/threads` accepts
  an optional `tags` query param (a JSON-encoded object). A single resourceId can
  be shared across git worktrees of the same repo (the id is derived from the git
  URL), so passing `tags` scopes the list to threads matching every tag (e.g.
  `{ projectPath }` for the working directory). Each returned thread now also
  includes the scoping `tags` it was stamped with at creation.

  The session event stream route (`.../stream`) now enqueues raw event objects
  and lets the server adapter handle SSE framing, fixing a double-framing bug
  where events were wrapped twice (`data: "data: {...}\n\n"\n\n`) and could not be
  parsed by clients.

  `@mastra/client-js` gains the matching types and reads:
  - `AgentControllerOMProgress` and `AgentControllerSessionSettings`, surfaced on
    `AgentControllerSessionState` (`omProgress`, `tokenUsage`, `settings`)
  - a `display_state_changed` event in `KnownAgentControllerEvent` carrying the
    status-line figures
  - `AgentControllerSession.listThreads()` now accepts either a number
    (back-compat) or `{ limit?, tags? }`

  Example:

  ```ts
  const session = client.getAgentController('code').session(resourceId);

  // Scope a worktree's thread list (and resumed session) to its own threads.
  await session.create({ tags: { projectPath: '/repo/worktree-a' } });
  const threads = await session.listThreads({ tags: { projectPath: '/repo/worktree-a' } });

  // Read the status-line figures and agent settings.
  const state = await session.state();
  state.omProgress; // observational-memory progress
  state.tokenUsage; // cumulative token usage
  state.settings; // { yolo, thinkingLevel, notifications, smartEditing }
  ```

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

- Scope harness session creation with tags so sessions sharing a resourceId can ([#18446](https://github.com/mastra-ai/mastra/pull/18446))
  each resume their own thread.

  `harness.createSession()` now accepts an optional `tags` record. The tags are
  (a) seeded into the new session's state, (b) stamped onto every thread the
  session creates (so thread listings can be filtered back to the session's
  scope), and (c) used to filter initial thread selection: a thread is a resume
  candidate only when its metadata matches every provided tag. Previously, initial
  thread selection only consulted the
  harness-global `initialState.projectPath`; on a multi-session server (where one
  Harness serves many scopes) a session could resume the most recently updated
  thread from a _different_ scope that shared the resourceId. Using a generic tag
  record (e.g. `{ projectPath }`) keeps room for future scoping dimensions without
  further API changes.

  The `@mastra/client-js` `HarnessSession.create()` method accepts `{ tags }`, and
  the `POST /harness/:id/sessions` route accepts a `tags` body field.

  ```ts
  // before: initial thread chosen by resourceId only
  const session = await harness.createSession({ resourceId, id, ownerId });

  // after: initial thread scoped to this worktree via a tag
  const session = await harness.createSession({
    resourceId,
    id,
    ownerId,
    tags: { projectPath: '/repo/worktree-a' },
  });
  ```

#### Patch Changes

- feat(playground): render ask_user tool as interactive question UI in Studio ([#18374](https://github.com/mastra-ai/mastra/pull/18374))

  Adds an `AskUserBadge` component that renders suspended `ask_user` tool calls
  as interactive prompts with clickable option buttons (single/multi-select) or
  free-text input. The user's answer is sent as `resumeData` through the existing
  `sendToolApproval` flow to properly resume the tool.

  Plumbing changes:
  - `sendToolApprovalBodySchema` now accepts optional `resumeData`
  - `agent.sendToolApproval()` passes custom `resumeData` when provided
  - `@mastra/client-js` and `@mastra/react` expose the new parameter

- Fixed conditional workflows so that re-running or rehydrating a run (time travel) no longer leaves the wrong branch marked as active. When a paused or replayed run lands on a conditional, arms whose condition does not evaluate truthy are now correctly recorded as skipped instead of staying stuck in a running state. ([#18228](https://github.com/mastra-ai/mastra/pull/18228))

  The server workflow and schedule run-status response schemas now include the `'skipped'` status so they stay in sync with core's `WorkflowRunStatus`.

### [@mastra/slack@1.5.0](https://github.com/mastra-ai/mastra/blob/@mastra/slack@1.5.0/channels/slack/CHANGELOG.md)
#### Minor Changes

- Added `waitUntil` support for channels so background agent runs survive serverless responses. ([#17832](https://github.com/mastra-ai/mastra/pull/17832))

  Without `waitUntil`, the runtime freezes the invocation as soon as the webhook response returns, killing the agent run mid-flight and leaving the user with no reply.

  **Auto-detected (no config needed):**
  - **Cloudflare Workers** — reads `c.executionCtx.waitUntil`
  - **Netlify Functions** — reads `c.env.context.waitUntil`

  **Requires explicit config:**

  Vercel needs a `waitUntil` function passed directly because it exposes `waitUntil` via AsyncLocalStorage, not the request context. AWS Lambda doesn't need `waitUntil` — it waits for the event loop to drain naturally.

  ```ts
  import { waitUntil } from '@vercel/functions';
  import { SlackProvider } from '@mastra/slack';

  new SlackProvider({ waitUntil });
  ```

  ```ts
  new Agent({
    channels: {
      adapters: { slack: createSlackAdapter({ ... }) },
      waitUntil,
    },
  });
  ```

  For runtimes where `waitUntil` lives on the request context but isn't covered by the default, pass `resolveWaitUntil: (c) => fn | undefined` instead.

  ```ts
  new Agent({
    channels: {
      adapters: { slack: createSlackAdapter({ ... }) },
      resolveWaitUntil: c => c.var.runtime?.waitUntil,
    },
  });
  ```

  Resolution order: `waitUntil` → `resolveWaitUntil(c)` → core default.

#### Patch Changes

- Normalize trailing slashes in the Slack provider `baseUrl`. A `baseUrl` with a trailing slash (e.g. `MASTRA_BASE_URL=https://example.com/`) previously produced double-slash callback URLs like `https://example.com//slack/oauth/callback`, which broke the OAuth flow and webhook delivery. The trailing slash is now stripped, so callback URLs are always well-formed. ([#18483](https://github.com/mastra-ai/mastra/pull/18483))

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Rename the `Harness` class to `AgentController` and its associated `Harness*` types to `AgentController*` (e.g. `HarnessConfig` → `AgentControllerConfig`, `HarnessMode` → `AgentControllerMode`, `HarnessEvent` → `AgentControllerEvent`). ([#18505](https://github.com/mastra-ai/mastra/pull/18505))

  `@mastra/core`: A new canonical subpath `@mastra/core/agent-controller` exports the `AgentController` class plus the `AgentController*` types. The legacy `@mastra/core/harness` subpath remains available and re-exports the deprecated `Harness*` aliases, so existing `Harness` class and type usage continues to work unchanged. New code should import from `@mastra/core/agent-controller`. On `Mastra`, the hosted-controller API is exposed under agent-controller names — `getAgentController`, `getAgentControllerById`, `listAgentControllers`, and the `agentControllers` config key — while `getHarness`/`getHarnessById`/`listHarnesses` and the `harnesses` config key remain as deprecated aliases.

  `@mastra/server`: The controller session API is now served exclusively under `/agent-controller/...` (with `agent-controller:read` / `agent-controller:execute` permissions). The legacy `/harness/...` routes and `harness:*` permissions have been removed. List responses use the `agentControllers` key, session responses use `controllerId`, and path params use `:controllerId`.

  `@mastra/client-js`: `AgentController` and `AgentControllerSession` are now the canonical resource classes, with `MastraClient.getAgentController(id)` / `listAgentControllers()` targeting the `/agent-controller` routes and reading the canonical `agentControllers` / `controllerId` response keys. The deprecated `getHarness` / `listHarnesses` methods and `Harness` / `HarnessSession` classes have been removed. This is a breaking change for the recently released client.

  The `@mastra/core` peer dependency floor is raised to `>=1.47.0-0` so consumers must be on the release that introduces the canonical agent-controller surface. This applies to `@mastra/server`, `@mastra/deployer`, and `mastra` (CLI), and is propagated to the packages that depend on them: the deployers (`@mastra/deployer-cloud`, `@mastra/deployer-cloudflare`, `@mastra/deployer-netlify`, `@mastra/deployer-vercel`), the server adapters (`@mastra/express`, `@mastra/fastify`, `@mastra/hono`, `@mastra/koa`, `@mastra/nestjs`, `@mastra/next`, `@mastra/tanstack-start`), and `@mastra/temporal`.

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

- Fixed workflow runs preserving their original creation time when re-persisted in Upstash storage, including concurrent saves. ([#18004](https://github.com/mastra-ai/mastra/pull/18004))

### [@mastra/voyageai@0.3.0](https://github.com/mastra-ai/mastra/blob/@mastra/voyageai@0.3.0/embedders/voyageai/CHANGELOG.md)
#### Minor Changes

- Added support for the `voyage-context-4` contextualized chunk embedding model (preview). Each chunk is embedded with awareness of the other chunks in the same document, capturing both local detail and document-level context. Supports flexible output dimensions (256, 512, 1024, 2048). ([#18413](https://github.com/mastra-ai/mastra/pull/18413))

  ```typescript
  import { voyage, voyageContextualizedEmbedding } from '@mastra/voyageai';

  // Pre-configured model
  const result = await voyage.context4.doEmbed({
    values: [['Paragraph 1 from doc 1...', 'Paragraph 2 from doc 1...'], ['Content from doc 2...']],
    inputType: 'document',
  });

  // Or configure explicitly
  const model = voyageContextualizedEmbedding({ model: 'voyage-context-4', outputDimension: 512 });
  ```

### Other updated packages

The following packages were updated with dependency changes only:

- [@mastra/agent-builder@1.1.2](https://github.com/mastra-ai/mastra/blob/@mastra/agent-builder@1.1.2/packages/agent-builder/CHANGELOG.md)
- [@mastra/arize@1.3.2](https://github.com/mastra-ai/mastra/blob/@mastra/arize@1.3.2/observability/arize/CHANGELOG.md)
- [@mastra/arthur@0.4.2](https://github.com/mastra-ai/mastra/blob/@mastra/arthur@0.4.2/observability/arthur/CHANGELOG.md)
- [@mastra/braintrust@1.2.2](https://github.com/mastra-ai/mastra/blob/@mastra/braintrust@1.2.2/observability/braintrust/CHANGELOG.md)
- [@mastra/datadog@1.3.2](https://github.com/mastra-ai/mastra/blob/@mastra/datadog@1.3.2/observability/datadog/CHANGELOG.md)
- [@mastra/editor@0.13.2](https://github.com/mastra-ai/mastra/blob/@mastra/editor@0.13.2/packages/editor/CHANGELOG.md)
- [@mastra/laminar@1.3.2](https://github.com/mastra-ai/mastra/blob/@mastra/laminar@1.3.2/observability/laminar/CHANGELOG.md)
- [@mastra/langfuse@1.4.2](https://github.com/mastra-ai/mastra/blob/@mastra/langfuse@1.4.2/observability/langfuse/CHANGELOG.md)
- [@mastra/langsmith@1.3.2](https://github.com/mastra-ai/mastra/blob/@mastra/langsmith@1.3.2/observability/langsmith/CHANGELOG.md)
- [@mastra/longmemeval@1.1.2](https://github.com/mastra-ai/mastra/blob/@mastra/longmemeval@1.1.2/explorations/longmemeval/CHANGELOG.md)
- [@mastra/mcp-docs-server@1.2.2](https://github.com/mastra-ai/mastra/blob/@mastra/mcp-docs-server@1.2.2/packages/mcp-docs-server/CHANGELOG.md)
- [@mastra/opencode@0.1.2](https://github.com/mastra-ai/mastra/blob/@mastra/opencode@0.1.2/integrations/opencode/CHANGELOG.md)
- [@mastra/otel-exporter@1.3.2](https://github.com/mastra-ai/mastra/blob/@mastra/otel-exporter@1.3.2/observability/otel-exporter/CHANGELOG.md)
- [@mastra/posthog@1.1.2](https://github.com/mastra-ai/mastra/blob/@mastra/posthog@1.1.2/observability/posthog/CHANGELOG.md)
- [@mastra/sentry@1.2.2](https://github.com/mastra-ai/mastra/blob/@mastra/sentry@1.2.2/observability/sentry/CHANGELOG.md)
- [@mastra/voice-google-gemini-live@0.14.1](https://github.com/mastra-ai/mastra/blob/@mastra/voice-google-gemini-live@0.14.1/voice/google-gemini-live-api/CHANGELOG.md)
- [@mastra/voice-openai-realtime@0.13.1](https://github.com/mastra-ai/mastra/blob/@mastra/voice-openai-realtime@0.13.1/voice/openai-realtime-api/CHANGELOG.md)
- [@mastra/voice-xai-realtime@0.2.1](https://github.com/mastra-ai/mastra/blob/@mastra/voice-xai-realtime@0.2.1/voice/xai-realtime-api/CHANGELOG.md)