Skip to content

fix(auth-okta): build correct endpoint URLs for Okta org authorization servers#1

Open
noahjohnhay wants to merge 1384 commits into
mainfrom
nhay-claude/nifty-jennings-d20206
Open

fix(auth-okta): build correct endpoint URLs for Okta org authorization servers#1
noahjohnhay wants to merge 1384 commits into
mainfrom
nhay-claude/nifty-jennings-d20206

Conversation

@noahjohnhay
Copy link
Copy Markdown

Summary

MastraAuthOkta concatenates /v1/authorize (and /token, /keys, /logout) onto OKTA_ISSUER verbatim. For a custom authorization server (https://{domain}/oauth2/default) that yields the right endpoint, but for an Okta org authorization server (https://{domain}) it produces https://{domain}/v1/authorize, which Okta 404s. The correct org-server endpoint is https://{domain}/oauth2/v1/authorize.

Fix

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

Config Authorize URL (before) Authorize URL (after)
OKTA_ISSUER=https://{domain} (org) https://{domain}/v1/authorize (404) https://{domain}/oauth2/v1/authorize
OKTA_ISSUER=https://{domain}/oauth2/default (custom) https://{domain}/oauth2/default/v1/authorize https://{domain}/oauth2/default/v1/authorize (unchanged)

Both match what each Okta server type's discovery document advertises.

Test plan

  • Added regression coverage for org-server, custom-server, trailing-slash, and JWT iss-claim behavior in auth/okta/src/index.test.ts
  • pnpm --filter @mastra/auth-okta test — 35 passed

🤖 Generated with Claude Code

this.clientSecret = clientSecret;
this.issuer = issuer ?? `https://${domain}/oauth2/default`;
// Normalize trailing slashes so a stray `OKTA_ISSUER=https://domain/` doesn't produce `.../oauth2//v1/...`
this.issuer = (issuer ?? `https://${domain}/oauth2/default`).replace(/\/+$/, '');
LekoArts and others added 29 commits May 12, 2026 12:39
## Description

1 day will be the default for pnpm v11 but since we still use pnpm v10
let's set it ourselves for now.
The [minimumReleaseAge](https://pnpm.io/settings#minimumreleaseage)
setting defines the minimum number of minutes that must pass after a
version is published before pnpm will install it.

## Related issue(s)

<!-- Required: Link to the issue(s) this PR addresses, e.g. with: Fixes
mastra-ai#123. PRs without linked issues may be closed. -->

## Type of change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR
)

## Description

<!-- Required: Provide a brief description of the changes in this PR.
-->
- Fixed `foreach` state update and `foreach` bail in evented workflows
- Fixed suspend-resume in evented-workflows legacy stream.

## Related issue(s)

<!-- Required: Link to the issue(s) this PR addresses, e.g. with: Fixes
mastra-ai#123. PRs without linked issues may be closed. -->

## Type of change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [x] Test update

## Checklist

- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

This PR fixes how workflows handle loops and pause/resume features. When
a workflow loops through a list of items, it wasn't properly remembering
changes made inside the loop, and the pause/resume feature had some
debug code left in. The PR cleans up the tests to ensure they work
correctly with these fixes.

## Summary

This PR addresses three key issues in evented workflows:

### Core Fixes

1. **Fixed `foreach` state updates** - The `WorkflowEventProcessor` now
properly persists and carries forward `__state` updates during `foreach`
step handling. Previously, state mutations made inside a `foreach` body
weren't being persisted for subsequent iterations. The fix writes the
current state to storage under a special `stepId: '__state'` and
includes it in the step results.

2. **Fixed suspend-resume in evented-workflows legacy stream** - Removed
debugging code (a `console.dir()` statement) from the
`EventedRun.resume` method that was left in the codebase.

3. **Fixed `foreach` bail behavior** - Addressed bail functionality in
evented workflows (referenced in the PR description).

### Test Suite Improvements

Refactored the evented workflow test suite (`evented-workflow.test.ts`)
to use a single long-lived `Mastra` instance for all workflows. This
ensures tests that call `workflow.createRun()` directly run against an
engine with active event workers. The refactoring includes:
- Module-level tracking of the registered Mastra instance and registry
- Rebinding registry workflows back to the long-lived Mastra after each
test
- Suite lifecycle hooks to properly manage worker lifecycle and state
cleanup
- Updated imports to include the `WorkflowRegistry` type

### Changeset

Added a patch-level changeset documenting these fixes for the
`@mastra/core` package.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16436)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description

Add `@mastra/dsql` - a new storage provider for Amazon Aurora DSQL with
IAM authentication.

This package enables Mastra users to use Aurora DSQL's serverless,
distributed PostgreSQL-compatible database as a storage backend for
threads, messages, workflows, traces, and scores.

### Key Features

- **Full storage interface implementation**: threads, messages,
workflows, traces, spans, and evaluation scores
- **IAM-based authentication**: Uses
`@aws/aurora-dsql-node-postgres-connector` for automatic token
generation (no database passwords)
- **OCC retry logic**: Handles SQLSTATE `40001` (serialization failure)
with exponential backoff and jitter
- **Batch processing**: Splits large operations into batches of 3000
rows to respect Aurora DSQL's transaction limits
- **Async index creation**: Uses `CREATE INDEX ASYNC` with
`sys.wait_for_job()` for completion waiting
- **JSONB workaround**: Stores JSON as `TEXT` columns with `::jsonb`
casting at query time
- **Automatic performance indexes**: Creates composite indexes during
initialization for common query patterns
- **Connection pool management**: Default 55-min max lifetime to handle
Aurora DSQL's 60-minute connection limit

### Aurora DSQL-Specific Adaptations

- No PostgreSQL extensions supported (pgvector, PostGIS, etc.)
- No JSONB column types, foreign keys, triggers, or TRUNCATE
- Only btree indexes supported
- Single database (`postgres`) per cluster with schema-based isolation

### Package Structure

```
stores/dsql/
├── src/
│   ├── index.ts              # Main exports
│   ├── shared/               # Config, retry, and batch utilities
│   └── storage/
│       ├── index.ts          # DSQLStore main class
│       └── domains/          # Memory, observability, operations, scores, workflows
├── docs/                     # Internal technical documentation
└── package.json
```

## Related Issue(s)

mastra-ai#10929

## Type of Change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [x] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [x] Test update

## Checklist

- [x] I have made corresponding changes to the documentation (if
applicable)
  - Added dsql.mdx with full API reference
- [x] I have added tests that prove my fix is effective or that my
feature works
  - Integration tests using `@internal/storage-test-utils`
  - DSQL-specific tests for IAM auth, batch limits, JSONB handling, etc.
  - Performance index tests

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added Amazon Aurora DSQL storage provider with domain stores (memory,
workflows, observability, scores), IAM-backed connections, pooling,
OCC-aware retries, batching, and automatic performance index tooling.

* **Documentation**
* Expanded reference sidebar and added a comprehensive DSQL integration
guide and README covering setup, usage, index management, and
limitations.

* **Tests**
* Added unit, integration, performance suites and test utilities for
DSQL behaviors and indexing.

* **Chores**
* Added package, build, lint, TypeScript, and Vitest configs for the new
DSQL module.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Daniel Lew <danielshlomolew@gmail.com>
Co-authored-by: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>
Co-authored-by: Daniel Lew <51924260+DanielSLew@users.noreply.github.com>
Co-authored-by: Tyler Barnes <tylerdbarnes@gmail.com>
Adds an Integrations page for Mastra Code with a WezTerm/macOS guide for
hook-based notifications, click-to-focus behavior, and clearing stale
notifications when the terminal tab is focused manually.

The hook script includes short timeouts around external commands so it
does not block Mastra Code if WezTerm, osascript, sqlite3, or
terminal-notifier is slow.

After:

```json
{
  "Stop": [
    {
      "type": "command",
      "command": "bash \"$HOME/.mastracode/hooks/wezterm-mastracode-notify.sh\"",
      "description": "Notify when Mastra Code goes idle in an inactive WezTerm pane"
    }
  ]
}
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

This PR adds a guide that helps you get notifications on your Mac when
Mastra Code is idle—and when you click the notification, it brings
Mastra Code back into focus in your terminal. It's like a reminder that
something finished, and clicking the reminder takes you right back to
it.

---

## Changes

### Documentation Updates

**New Integrations Guide for WezTerm/macOS Terminal Notifications**
- Added a new terminal notifications section to the Mastra Code
documentation index, linking to the integration guide
- Created a comprehensive integration guide
(`terminal-notifications.mdx`) that documents:
- A hook-based setup for sending clickable macOS notifications when
Mastra Code emits the `Stop` event in an inactive WezTerm pane
  - Three supporting shell scripts:
- A script to focus the original WezTerm pane/tab and dismiss the active
notification
- A script to clear notifications when the pane becomes focused manually
- A main notification script that validates hook payloads, locates
pane/tab metadata via `wezterm` and `jq`, optionally fetches thread
titles via `sqlite3`, and displays notifications using
`terminal-notifier` (with callback to focus script) or `osascript` as
fallback
- Example `~/.mastracode/hooks.json` configuration entry for the `Stop`
hook event
  - Instructions for reloading and testing the setup
  - Notes on extending the integration to other hook events

The implementation uses short timeouts around external commands
(WezTerm, osascript, sqlite3, terminal-notifier) to prevent blocking
Mastra Code execution.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16465)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description

<!-- Required: Provide a brief description of the changes in this PR.
-->

## Related issue(s)

<!-- Required: Link to the issue(s) this PR addresses, e.g. with: Fixes
mastra-ai#123. PRs without linked issues may be closed. -->

## Type of change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5
When the playground builds its code, sometimes multiple copies of a
library called react-query get bundled together by mistake, which breaks
things. This PR tells the build system to only use one copy of
react-query so everything works properly.

## Overview
This PR fixes a Studio crash in the playground by deduplicating
`@tanstack/react-query` in the Vite build configuration. The issue
occurred when multiple React versions were installed, causing
`@tanstack/react-query` to be duplicated in the bundle, which split the
QueryClient context and resulted in "No QueryClient set" errors.

## Changes Made

### `.changeset/fix-playground-react-query-dedupe.md`
- Added a changeset documenting a patch version bump for the
`@internal/playground` package
- Documents the fix for the Studio crash caused by duplicated
`@tanstack/react-query`

### `packages/playground/vite.config.ts`
- Updated the Vite `resolve.dedupe` configuration to include
`@tanstack/react-query`
- Ensures only one instance of `@tanstack/react-query` is included in
the build output

## Impact
- Resolves the "No QueryClient set" error in the playground when
multiple React versions are present
- Maintains consistent QueryClient context across the entire playground
bundle

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16469)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…mastra-ai#16433)

## Summary

- **playground-ui (minor):** Added `pill-ghost` Tabs variant + `sticky`
TabList prop, `variant` prop on Combobox (`default`/`ghost`/`link`),
tighter `EntityHeader` (title + children share row), orientation-aware
`ScrollArea` overflow, redesigned `PanelSeparator` (pill-shaped handle).
Removed
`Threads`/`ThreadList`/`ThreadItem`/`ThreadLink`/`ThreadDeleteButton`
exports (no external usage).
- **playground (patch):** New local `thread-list` component replacing
removed `Threads` exports. Refreshed agent header / information /
playground view / version bar / page tabs and workflow information / run
list with refined layouts using DS primitives. Chat composer gets subtle
send animation and tuned border.

## Changesets

- `pretty-bananas-clean.md` — `@mastra/playground-ui` minor
- `kind-singers-type.md` — `mastra` patch

## Test plan

- [ ] `pnpm --filter @mastra/playground-ui build`
- [ ] `pnpm --filter mastra build`
- [ ] Storybook: verify `Tabs` (`pill-ghost`), `Combobox` (3 variants),
`EntityHeader`, `ScrollArea` (vertical + horizontal), `PanelSeparator`
hover/active
- [ ] Studio: chat threads list, new chat link, delete flow
- [ ] Studio: workflow information panel (sticky header + tabs, run
list)
- [ ] Studio: agent header / playground / version bar
- [ ] Studio: composer send animation triggers on submit
- [ ] `pnpm --filter playground exec playwright test
e2e/tests/agents/\$agentId/stream.spec.ts`

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5 (Explain Like I'm 5)

This PR cleans up the shared UI toolkit, moves the chat/thread list UI
into the playground app so it can evolve independently, adds a few nicer
visual options (new tab and combobox variants), and makes the chat
composer show a subtle send animation so interactions feel snappier.

---

## Changes Summary

### playground-ui (minor)
- UI features and fixes:
- Tabs: added `pill-ghost` variant; `TabList` gains optional `sticky`
prop.
- Combobox: introduced `variant` prop (`default | ghost | link`) and
refactored trigger styles into base + variant classes.
- EntityHeader: denser layout so title and children share a single row;
reduced padding.
- ScrollArea: orientation-aware overflow behavior and content min-size
fixes to enforce a single scroll axis.
- PanelSeparator: redesigned indicator to a pill-shaped handle with
enhanced hover/active/focus visuals.
- Removals:
- Removed thread-related exports and stories from the DS: `Threads`,
`ThreadList`, `ThreadItem`, `ThreadLink`, `ThreadDeleteButton`, and
`threads.stories.tsx`.
  - Stopped re-exporting Threads from package entrypoint.

### playground (patch)
- Thread UI migration:
- Added local `thread-list` primitives and re-export: `ThreadList`,
`ThreadListNewItem`, `ThreadListEmpty`, `ThreadListItems`,
`ThreadListItem`, `ThreadListSeparator`.
- Replaced all usages of the removed playground-ui Threads components
(chat threads, workflow run list) with the new local primitives; delete
actions wired via `onDelete` only when allowed.
- `ThreadList` nav now accepts an `aria-label` (defaults to "Threads");
workflow-run-list passes "Workflow runs".
- Guarded workflow-run-list against reading
`run.snapshot.status`/`timestamp` when `snapshot` may be null.
- Component & layout updates using DS primitives:
- Agent header: `AgentCombobox` rendered with `variant="ghost"` and
`size="sm"`; `AgentCombobox` now accepts a `size` prop.
- AgentInformation & WorkflowInformation: tabbed UIs wrapped in
`ScrollArea` with sticky headers for consistent scrolling.
- Agent playground view: replaced `Panel` with `CollapsiblePanel` for
left/right panes and adjusted sizing/collapse behavior.
  - Agent page tabs switched to `pill-ghost` variant.
- Workflow run list and related UIs refactored to `thread-list`
primitives; timestamp/status rendering tightened to avoid null reads.
- ZoomSlider and other panels updated to use shared Panel/Button styles.
- Chat composer and messages:
- Added composer send animation: new CSS keyframes and
`.composer-sending` class; composer triggers a send-pulse overlay on
submit.
  - Tweaked user message bubble sizing and composer border styling.
- Tests & stories:
- Added Vitest/jsdom tests for ScrollArea orientation/min-size behavior.
- Storybook: added `PillGhostVariant` for Tabs; removed Threads stories.
- Playwright e2e: expectation updated for thread-list item count after
reload.

---

## API / Export surface
- Added:
- `ComboboxVariant` type and functional `variant` prop (`default | ghost
| link`) with trigger style mapping.
  - `TabListProps.sticky?: boolean`.
  - `thread-list` primitives exported from the playground package.
- Removed:
- From `@mastra/playground-ui`: `Threads`, `ThreadList`, `ThreadItem`,
`ThreadLink`, `ThreadDeleteButton` (and related stories).
- No other public prop/type signatures changed across playground
components (most changes are internal/layout/composition).

---

## Changesets
- `@mastra/playground-ui` bumped as minor (pretty-bananas-clean.md).
- `mastra` bumped as patch (kind-singers-type.md).

---

## Test Plan (high level)
- Build `@mastra/playground-ui` and `mastra`.
- Storybook: verify Tabs (`pill-ghost`), Combobox variants,
EntityHeader, ScrollArea (vertical/horizontal/both), PanelSeparator
hover/active.
- Studio: chat threads list, new chat link, delete flow; workflow
information panel (sticky header + tabs, run list); agent
header/playground/version bar; composer send animation on submit.
- Playwright e2e: pnpm --filter playground exec playwright test
e2e/tests/agents/\$agentId/stream.spec.ts.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16433)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Greg Lobinski <32480082+greglobinski@users.noreply.github.com>
…astra-ai#14717)

## Problem

When using `agent.stream()` with `structuredOutput`, the message
persisted to thread history had its text set to `"[object
Object]\n[object Object]\n..."` instead of the actual response content.

Fixes mastra-ai#14659

## Root cause

In
`packages/core/src/agent/workflows/prepare-stream/map-results-step.ts`,
the stream path's `onFinish` callback computed `outputText` as:

```ts
messageList.get.all.core().map(m => m.content).join('\n')
```

`m.content` is an object (not a string), so `.join()` serialized it as
`"[object Object]\n..."` — silently corrupting thread history for every
`stream()` + `structuredOutput` call.

## Fix

Use `payload.text` for plain output and `JSON.stringify(payload.object)`
for structured output — matching the `generate` path's existing
behavior.

## How to test

```ts
const agent = new Agent({ model, structuredOutput: z.object({ answer: z.string() }) })
const stream = await agent.stream('What is 2+2?', { threadId, resourceId })
await stream.consumeStream()
// Thread message text should be JSON, not '[object Object]'
const messages = await memory.getMessages({ threadId })
expect(messages.at(-1)?.content).not.toContain('[object Object]')
```

All existing tests pass.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Fixed streaming agent persistence so assistant messages store readable
text from structured outputs instead of serialized object artifacts,
improving message consistency and reliability.

* **Tests**
* Added a regression test validating streaming with structured output
and memory persistence to prevent future regressions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ward Peeters <ward@coding-tech.com>
Co-authored-by: Abhi Aiyer <abhiaiyer91@gmail.com>
…2860)

## Description

Refactored the `executeForeach` function to use `fastq` (a
callback-based queue library) instead of the previous batch-based
approach with `Promise.all`. This change improves concurrency handling
by starting the next item as soon as any worker slot frees up, rather
than waiting for an entire batch to complete.

### Key Changes:
- Added `fastq` dependency (v1.19.1) to package.json
- Replaced the batch-based loop (`Promise.all` on slices) with a `fastq`
queue that maintains true fluid concurrency
- Refactored item processing into a worker function that handles
individual tasks
- Improved error and suspension handling to stop processing remaining
queued items when an error or suspension occurs
- Maintained backward compatibility with resume logic and suspend/error
behavior

### Benefits:
- **Better resource utilization**: Items start processing immediately
when a worker becomes available, rather than waiting for batch
boundaries
- **Cleaner code**: The queue-based approach is more intuitive for
concurrent task processing
- **Same semantics**: Error handling, suspension, and resume behavior
remain unchanged

## Type of Change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [x] Code refactoring
- [x] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works

https://claude.ai/code/session_01HRDs2f9fzXwwSruxV2jvwC

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Reworked foreach execution to a queue-driven, worker-based concurrency
model for more reliable per-item processing, clearer error and
suspension handling, safer resume behavior, and deterministic
finalization.

* **Chores**
* Added a new runtime dependency to support the updated concurrency
mechanism.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Fixes mastra-ai#9061

---------

Co-authored-by: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>
## Description

<!-- Required: Provide a brief description of the changes in this PR.
-->

## Related issue(s)

<!-- Required: Link to the issue(s) this PR addresses, e.g. with: Fixes
mastra-ai#123. PRs without linked issues may be closed. -->

Suspend and resume now works reliably for evented workflows that use
parallel steps, `.branch()`, `dountil`/`dowhile` loops, and nested
workflows — previously it only held up for simple linear flows.

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

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

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

## Type of change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [x] Test update

## Checklist

- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR fixes pausing and resuming for workflows so they reliably
continue from where they left off even when running parallel branches,
loops, or nested workflows. Previously, complex flows could crash, skip
suspend logic, or resume with incorrect data.

## Changes

### Reliability improvements to suspend/resume for evented workflows

- Parallel & .branch():
  - Multiple concurrently suspended branches are now all resumable.
  - Workflow stays suspended until every suspended branch resumes.
- Branch outputs are merged correctly; resuming one branch no longer can
prematurely complete the run.
  - Centralized aggregation logic prevents suspend/resume races.

- dountil / dowhile loops:
  - Suspending inside a loop body no longer crashes the run.
- After resume, subsequent iterations run with fresh state (resumeData
cleared) so iterations don't reuse stale resume data or enter infinite
loops.

- Nested workflows:
- Resuming a suspended step inside nested workflows now receives the
correct input (the previous step's output).
- The suspended-step path included in workflow results is corrected so
it can be passed directly to resume({ step }) across nesting levels.
- Nested resume publishes use a constructed prevResult derived from the
nested workflow snapshot when appropriate.

### Implementation details / touched files

- packages/core/src/workflows/evented/workflow-event-processor/index.ts
- Added protected aggregateBranchResults(...) to merge branch outputs
and handle combined suspend/resume behavior.
- processWorkflowSuspend now prepends inner step IDs into
__workflow_meta.path when propagating nested suspends.
- processWorkflowStepRun constructs nestedPrevResult for resumed inner
steps and handles loop-step suspended outcomes by early-publishing
workflow.step.end.
- processWorkflowStepEnd emits workflow-step-suspended watch event and
delegates branch aggregation to aggregateBranchResults.

- packages/core/src/workflows/evented/workflow-event-processor/loop.ts
- Loop control now builds reusable loopAgainData/loopEndData payloads
and clears resumeData/resumeSteps appropriately so resumed iterations
are treated as fresh runs.

- packages/core/src/workflows/evented/execution-engine.ts
- Cleans stepResults to drop entries with status 'skipped' so internal
bookkeeping doesn't leak into user-facing steps output.

- packages/core/src/workflows/evented/evented-workflow.test.ts
- Enabled previously skipped tests (cleared skipTests) except the
restart domain; resumeWorkflow helper now forwards forEachIndex into
run.resume(...).

- .changeset/ten-experts-check.md
  - Patch bump for @mastra/core documenting these reliability fixes.

Type: bug fix (with test updates)

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16476)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- Adds a list-mode switcher to **Observability → Traces** that toggles
between **Top-level traces only** (existing behavior, one row per
top-level run) and **All traces, nested too** (one row per anchor span —
every agent / workflow / tool / processor / scorer / RAG invocation,
including ones nested inside other runs).
- Reuses the existing trace list/timeline/span components — the detail
panel renders a branch subtree where the anchor span has a non-null
`parentSpanId` (the parent lives outside the returned spans).
- Frontend-only. Backend `listBranches` / `getBranch` endpoints already
exist in `client-js` and `@mastra/server`.

## Notable additions

- **`useBranch({ traceId, spanId, depth? })`** — wraps
`client.getBranch` for the per-branch subtree fetch.
- **`useTraceOrBranchSpans({ traceId, anchorSpanId, listMode })`** —
umbrella hook the page uses; routes between trace and branch data
sources based on the active mode.
- **`anchorSpanId` URL param** — branch identity. Kept stable while
intra-panel span navigation changes `spanId`, so the side span panel can
move around the subtree without re-fetching it.
- **Optional props** on existing exports (`TraceDataPanelView` →
`anchorSpanId`, `SpanDataPanelView` → `isAnchor`, `TracesListView` →
`featuredSpanId`, `formatHierarchicalSpans` → `anchorSpanId`) so a
subtree whose top span has a real parent renders correctly. All default
to the existing trace-mode behavior.
- **Virtualizer reset on mode toggle** via `key={url.listMode}` on
`<TracesListView>` — the virtualizer caches measurements from the
previous mode's row count and `isLoading` doesn't flash when both modes
are cached, so a fresh mount is the cleanest fix.

## Test plan

- [ ] `pnpm --filter @mastra/playground-ui vitest run
src/domains/traces` — 30 unit tests (including new
`format-hierarchical-spans` coverage for anchor-as-root and the existing
`useTraces` logic).
- [ ] `pnpm --filter @internal/playground test:e2e --
e2e/tests/observability/list-mode-switcher.spec.ts` — 6 behavior tests
covering default mode, toggle to branches, toggle back, URL clearing on
mode switch, branch detail rendering, and intra-panel span nav staying
on the same subtree.
- [ ] Manual in browser:
- [ ] Default load on `/observability` is "Top-level traces only" with
`/api/observability/traces` called.
- [ ] Switching to "All traces, nested too" surfaces nested rows, URL
gains `?listMode=branches`.
- [ ] Clicking a branch row opens the subtree detail (anchor +
descendants).
- [ ] Prev/next on the trace panel steps through branch rows (URL
updates `traceId` + `anchorSpanId`).
- [ ] Prev/next on the side span panel walks spans **within** the
current subtree (URL `spanId` changes; `anchorSpanId` stays; no
`getBranch` refetch).
- [ ] Switching back to "Top-level traces only" drops the nested rows
and clears anchor/selection params.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR adds a toggle to Observability → Traces so you can switch
between viewing only top-level runs (default) or every invocation
including nested calls; in the nested ("branches") mode each invocation
is a row and clicking a row opens a detail panel focused on that
invocation's subtree (the anchor span).

---

## Overview

Adds a list-mode switcher to Observability → Traces that toggles
between:
- "Top-level traces only" (default): one row per top-level run.
- "All traces, nested too" (branches): one row per invocation
(agents/workflows/tools/processors/scorers/RAG invocations), making
nested spans individual rows and enabling branch-focused detail panels.

Frontend-only changes that reuse existing list/timeline/span components
with opt-in props; backend endpoints (listBranches / getBranch) already
exist in client-js and @mastra/server.

---

## Key Changes

Hooks
- useBranch({ traceId, spanId, depth? }) — React Query wrapper around
client.getBranch to fetch a branch subtree; uses long staleTime once
returned spans have ended.
- useTraceOrBranchSpans({ traceId, anchorSpanId, listMode, depth }) —
chooses trace vs branch data source based on listMode and returns spans
+ loading/error state.

Components / API
- TraceDataPanelView: optional anchorSpanId prop to treat a span as the
displayed root for timeline and metadata.
- SpanDataPanelView: optional isAnchor prop to control anchor/root-only
trace-context metadata rendering (falls back to parentSpanId == null).
- TracesListView: supports featuredSpanId; rows now carry optional
spanId and virtualized item keys use `${traceId}:${spanId ?? ''}` so
branch rows with the same traceId are distinct.
- formatHierarchicalSpans(spans, anchorSpanId?): builds hierarchical
trees; when anchorSpanId is provided the anchor is treated as the
displayed root even if its parent is outside the provided set; orphan
spans (missing parent) surface as roots; the displayed root's endTime is
extended to the latest endedAt among its subtree so latency reflects the
full branch.
- TracesListModeToggle component: toolbar dropdown to switch between
traces and branches.

URL, state & navigation
- New URL param constant TRACE_ANCHOR_SPAN_ID_PARAM ('anchorSpanId') for
a branch-mode anchor that remains stable while intra-panel navigation
updates spanId.
- useTraceUrlState: adds anchorSpanIdParam, handleListModeChange (clears
selection on mode change), and expands handleTraceClick to (traceId,
spanId?, anchorSpanId?).
- useTraceListNavigation: branch-aware navigation using traceId + spanId
identity; only forwards spanId when in branches mode.
- Traces page (index): integrates the toggle, passes
anchorSpanId/featuredSpanId into list and panel logic; remounts
TracesListView on mode changes via key={url.listMode} to reset the
virtualizer; "Evaluate Trace" jumps to the anchor in branches mode.

Behavior
- Detail panel renders a branch subtree when the anchor span has a
non-null parentSpanId (the parent can live outside the fetched spans).
- Intra-panel navigation updates only spanId while preserving
anchorSpanId to avoid refetching the subtree.
- Toggling modes clears traceId/spanId/anchorSpanId to avoid mismatched
context.
- Virtualizer reset on mode toggle prevents stale measurements and
loading flashes.

---

## Tests

- ~30 unit tests for traces domain (including formatHierarchicalSpans
and branches-related behavior).
- 6 Playwright E2E specs covering default mode, toggling to branches and
back, URL param behavior (listMode, anchorSpanId), branch detail
rendering for anchors with outside parents, intra-panel navigation
staying within subtree, and selection clearing on mode switches.

---

## Notable Details

- Orphan spans whose parent isn't in the fetched set are surfaced as
roots to represent branch-subtree boundaries correctly.
- The displayed root/anchor endTime is extended to the subtree's latest
endedAt so timeline and latency reflect the full branch.
- Barrel exports updated to include new hooks and TracesListModeToggle.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- **chore(ci): rewrite uniet/e2e tests**
- **remove cache from publish**
- **cleanup prebuild**

## Description

<!-- Required: Provide a brief description of the changes in this PR.
-->

## Related issue(s)

<!-- Required: Link to the issue(s) this PR addresses, e.g. with: Fixes
mastra-ai#123. PRs without linked issues may be closed. -->

## Type of change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5 (Explain Like I'm 5)

This PR tidies up CI: it stops using a package-manager cache when
publishing, makes the prebuild step more secure and conditional around
the Turbo token, and adds a new reusable, sharded test workflow so unit
and E2E tests run in parallel and produce consistent reports.

---

## Changes Overview

### `.github/workflows/npm-publish.yml`
- Disable package-manager cache for `actions/setup-node@v5` by adding
`package-manager-cache: false` to the Node setup in
prerelease/stable/enter_prerelease/snapshot jobs.
- Effect: prevents actions/setup-node from using pnpm cache during
publish flows.

Lines changed: +4/-0

---

### `.github/workflows/prebuild.yml`
- Make `env.TURBO_CACHE` conditional: `local:rw` when
`secrets.TURBO_TOKEN` is empty, otherwise `remote:rw`.
- Reduce job permissions to `contents: read` (remove prior PR/status
writes).
- Replace custom PR status step with local
`./.github/actions/setup-pnpm-node` action for Node/pnpm setup.
- Conditionally cache `.turbo/cache` with `actions/cache@v4` only when
`TURBO_TOKEN` is empty.
- Keep the changes-detection gating (`has_code`) and call the
`test-suite` reusable workflow; when invoking `test-suite`, explicitly
set `permissions: actions: read, contents: read` and pass `TURBO_TOKEN`
and `TURBO_TEAM` secrets.

Lines changed: +26/-13

---

### `.github/workflows/test-suite.yml`
- Add a new reusable workflow (`workflow_call`) accepting `head_sha` and
`base_ref` inputs and optional `TURBO_TOKEN`/`TURBO_TEAM` secrets.
- Jobs:
- `unit-test`: 4-shard matrix for `unit:*` and `typecheck:*` Vitest
runs; builds repo (excludes examples/docs/explorations); produces blob
reports at `.vitest-reports/blob-unit-<shard>.json` and uploads
short-retention artifacts per shard.
- `e2e-test`: matrix over `packages/*`, normalizes package names to
`project_id`, runs per-package E2E Vitest (`e2e:<project>`), writes
`.vitest-reports/blob-e2e-<project_id>.json`, and uploads
short-retention artifacts.
- `merge-reports`: runs after test jobs, downloads all `vitest-blob-*`
artifacts with `merge-multiple: true` and runs `pnpm vitest
--merge-reports` to produce consolidated output and GitHub Actions
reporting.
- Applies the same conditional TURBO_CACHE pattern and conditional
`.turbo/cache` caching when `TURBO_TOKEN` is empty.
- Sets larger Node memory for unit and e2e workflows.

Lines changed: +182/-0

---

### `.github/workflows/vitest-changed.yml`
- No observable changes in the provided diff snapshot; the supplied diff
lacked new hunk content so effective modifications could not be
determined from the input.

Lines changed: +0/-656 (diff-only snapshot)

---

## Impact & Review Notes
- Caching: publish now avoids package-manager cache; prebuild
`.turbo/cache` is only used locally when `TURBO_TOKEN` is absent —
reviewers should confirm expected cache behavior with and without the
Turbo token/secret.
- Permissions: prebuild permissions are tightened to read-only for
contents; confirm no downstream steps relied on prior write permissions
(PR/status updates).
- Tests: new reusable test-suite introduces sharded/unit and per-package
E2E runs, changes artifact names/retention, and centralizes report
merging — verify artifact retention/consumption and naming expectations.
- The diff for `vitest-changed.yml` in the provided input was
inconclusive; if that file was intended to be removed or modified,
please confirm with full diff or repository state.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…stra-ai#16484)

`@mastra/koa@1.5.0` introduced a dispatcher-reuse optimization that
unconditionally reads `app.middleware.length` in
`getRouteDispatcherGroup`. Subclasses that forward a non-Koa app-like
object (e.g. a `koa-router`, a mounted sub-app, or a custom wrapper) to
`super.registerRoute(app, route, opts)` hit `undefined.length` and crash
at `MastraServer.init()` with `TypeError: Cannot read properties of
undefined (reading 'length')`.

This guards reuse on `Array.isArray(app.middleware)` and falls back to
registering a fresh dispatcher per route via `app.use` when the array
isn't there — the same per-route behavior the adapter had before 1.5.0.
Real Koa apps keep the optimization unchanged.

Before:

```ts
class CustomKoaMastraServer extends MastraServer {
  async registerCustomApiRoutes() {
    for (const route of this.mastra.getServer()?.apiRoutes ?? []) {
      await super.registerRoute(this.router as any, route, { prefix: this.prefix });
      // 💥 TypeError: Cannot read properties of undefined (reading 'length')
    }
  }
}
```

After: the same subclass initializes cleanly and routes register on the
forwarded target via `.use()`.

Covered by four new tests in `Route dispatcher > non-Koa app-like
targets` — each one was verified to fail against the unguarded
implementation and pass with the fix. Full koa adapter suite:
2,188/2,188 passing.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

The PR fixes a crash that happens when Koa server code tries to reuse
optimization shortcuts for router-like objects that don't have all the
expected properties. By checking if the property exists before using it,
the code now gracefully falls back to the slower (but safe) approach for
these unusual objects, while keeping the optimization for normal Koa
apps.

## Summary

This PR fixes a `TypeError` in `@mastra/koa@1.5.0` that occurs during
`MastraServer.init()` when a subclass forwards a non-Koa app-like object
(such as a koa-router instance, mounted sub-app, or custom wrapper) to
the `registerRoute` method. The dispatcher-reuse optimization introduced
in 1.5.0 unconditionally accessed `app.middleware.length`, which failed
when `app.middleware` was undefined or not an array.

## Changes

**server-adapters/koa/src/index.ts**
- Updated `getRouteDispatcherGroup()` to guard the dispatcher-reuse
optimization by checking `Array.isArray(app.middleware)` before
attempting to reuse an existing dispatcher group
- Falls back to registering a fresh dispatcher per route via `app.use()`
when the optimization is unavailable, matching the pre-1.5.0 behavior
- Real Koa apps with a proper `middleware` array retain the optimization

**server-adapters/koa/src/__tests__/koa-adapter.test.ts**
- Added four new test cases under "Route dispatcher > non-Koa app-like
targets" covering:
  - No crash when `middleware` is missing
  - Correct handling when `middleware` exists but is not an array
  - Fresh dispatcher creation per route when reuse is impossible
- End-to-end request handling with router-like targets mounted in a real
Koa app

**.changeset/ten-hats-slide.md**
- Documents the patch-level fix with an illustrative TypeScript example
demonstrating the previous crash and the corrected behavior

## Verification

The full koa adapter test suite reports 2,188/2,188 tests passing. All
four new tests were verified to fail on the unguarded implementation and
pass with the fix applied.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16484)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
)

## Summary

Wires MastraCode follow-up input through Agent signals so active chats
can accept new user context without waiting for the current run to
finish.

Active follow-ups render as pending until the stream echoes the stable
signal id back. When the echo arrives, the pending placeholder is
replaced with the streamed user message and the current assistant stream
is split so subsequent output renders below that user message.

Idle input uses the same signal path to start the thread.

## Stack

Previous PR: mastra-ai#16438 — Route system reminders through Agent signals

This is PR 3 of the Agent signals stack:

1. mastra-ai#16229 — Core Agent signal runtime, server routes, and client-js API
2. mastra-ai#16438 — Route system reminders through Agent signals
3. mastra-ai#16231 — Use Agent signals for MastraCode follow-up chat
4. mastra-ai#16338 — Use Agent signals for Studio/playground follow-up chat

## What changed

- Sends MastraCode follow-up messages through `harness.sendSignal()`.
- Tracks pending signal messages by signal id and clears them when the
stream echo arrives.
- Treats echoed signal user messages as stream boundaries so assistant
output after the echo renders below the user message.
- Keeps a single thread subscription as the stream owner to avoid double
rendering.
- Preserves `/new`, image follow-ups, slash-command behavior, and tool
approval/resume output on the signal path.

## Test plan

- `pnpm --filter ./packages/cli test src/mastra-tui-queueing.test.ts
src/render-messages.test.ts src/message.test.ts --bail 1 --reporter=dot`
- `pnpm --filter ./packages/cli check`
- `pnpm --filter ./packages/cli build:lib`



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

You can type new messages while the assistant is still talking; they
show up as "pending" immediately and become real messages once the
system confirms them, and the assistant's reply continues after those
confirmed messages.

## Overview

This PR routes MastraCode follow-up input through the Agent signal path
so active chats can accept new user context without waiting for the
current run to finish. Follow-ups render optimistically as pending until
a stable signal-id echo arrives; when echoed, the pending placeholder is
replaced by the confirmed user message and the assistant's stream is
split so subsequent assistant output renders after that message. Idle
input uses the same signal path to start threads. Existing
behaviors—/new, image follow-ups, slash commands, and tool
approval/resume—are preserved. A harness fix ensures idle signal
acceptance waits for the subscription stream's terminal chunk before
emitting synthetic end events.

## Core changes

- packages/core/src/harness/harness.ts
- Introduces per-(agent,resource,thread) agent-thread subscriptions and
a subscription-driven stream processor.
- Adds sendSignal(...) (returns {id, type, accepted: Promise<...>}) and
isCurrentThreadStreamActive(); getCurrentRunId() is subscription-aware.
- sendMessage delegates to sendSignal; subscription lifecycle cleaned up
on thread/resource changes; abort aborts active subscriptions.
- Stream chunk processing reworked (processSubscribedThreadStream /
processStreamChunk / finishStreamState).
- Tool approval/decline/resume flow updated to await agent methods and
route resumed streams through the subscription processor.

- Message merging & boundaries
- MessageMerger prevents merging across messages marked with
mastra.responseBoundary.
- MessageList adds markResponseMessageBoundary(...) and normalizes
createdAt for signal/input messages.
- Agentic execution marks response boundaries when interjections are
enqueued.

- Tests
- New/extended harness and agent signal tests validate signal
lifecycles, echoed data-user-message handling, idle vs active signal
behavior, interjections, persisted ordering when draining signals into
active runs, and waiting for subscription terminal chunks.

## MastraCode TUI changes

- Pending user-message lifecycle and rendering
(mastracode/src/tui/render-messages.ts, components/user-message.ts)
- Adds helpers: addPendingUserMessage, confirmPendingUserMessage,
removePendingUserMessage, clearPendingUserMessages.
- UserMessageComponent accepts a pending option (dimmed styling);
PendingUserMessage lifecycle reconciles echoed messages by id or
normalized text and deduplicates echoed slash-commands/images.
- During active streaming, incoming user messages are treated as
follow-ups and reconciled against pending entries to avoid duplicates.

- State and pruning (mastracode/src/tui/state.ts, prune-chat.ts)
- TUIState extended with pendingSignalMessageComponentsById: Map<string,
PendingSignalMessage>.
- pruneChatContainer removes pending entries when their components are
pruned.

- Input flow, optimistic signals & harness integration
(mastracode/src/tui/mastra-tui.ts)
- Submit flow: render optimistic user message, run UserPrompt hooks,
remove if blocked, then sendOptimisticSignal(...) to remap optimistic
IDs to real harness signal IDs.
- Helpers added for optimistic lifecycle, pending-thread creation, and
handling sendSignal accepted/rejected outcomes (replace pending or
remove optimistic).
- addChildBeforeFollowUps updated to insert new components before the
earliest pinned component among follow-ups and pending-signal
components.

- Keyboard and shortcuts (components/custom-editor.ts, setup.ts,
help-overlay.ts)
- Adds queueFollowUp action and Ctrl+F shortcut to explicitly queue
follow-ups while a run is active.
- Enter consistently triggers editor.onSubmit (completing autocomplete
first); Ctrl+F is the explicit "queue follow-up" during runs.
- Help text split to show "Send message" and "Queue follow-up (Ctrl+F)"
separately.

- Handlers and lifecycle (tui/handlers/message.ts,
tui/handlers/agent-lifecycle.ts)
- Echoed signal user messages are treated as stream boundaries so
assistant output after the echo renders below the user message.
- handleAgentAborted / handleAgentError now clear pending user messages
during cleanup.
- Goal continuation now sends a system-reminder via
harness.sendSignal(...) instead of firing a message.

## Tests updated

Widespread test additions/updates across MastraCode and core:
- Mastracode TUI tests: queueing, render-messages, keyboard shortcuts,
command-dispatch, prune-chat, custom-editor, goal flows, message
handlers.
- Core tests: harness signal-message lifecycle, signal-history
subscription behavior, agent signals interjection and persistence tests.
Coverage focuses on pending lifecycle, optimistic→pending remap,
deduplication of echoed slash commands/images, idle vs active signal
handling, assistant insertion after echoed user messages, and waiting
for subscription terminal chunks.

## Changesets & release notes

- Core: bumps and documents Harness thread subscriptions, stable signal
echoes, idle signal starts that allow optimistic rendering, and waiting
for subscription terminal chunks before emitting end events.
- Mastracode: documents signal-based follow-up support, pending UI
lifecycle, single subscription ownership to avoid duplicate rendering,
new keyboard shortcut for queueing follow-ups, and goal-reminder signal
behavior.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16231)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
## Summary
Adds two local workflow skills for improving PR review quality:

- `pr-splitter` helps break large, tangled PRs into smaller reviewable
PRs while tracking extraction and drift in a local scratchpad.
- `pr-explainer` helps generate approachable, self-contained HTML review
aids in `.pr-review/` that explain what changed, why it matters, how it
works, and how it was verified.
- Adds `.pr-review/` to `.gitignore` so generated local review pages are
not committed by default.

## Test plan
- Read both skill files to verify trigger descriptions and workflow
guidance.
- Ran `git diff --cached --check` before committing to verify the patch
has no whitespace errors.

No package changes, so no changeset was added.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR adds two simple helpers to make code reviews easier: one helps
split big, messy changes into bite-sized PRs, and the other generates
local HTML review pages that explain what changed and how it was tested.
It also tells Git to ignore the folder where those HTML review pages are
written.

## Changes

**Added**
- `.claude/skills/pr-explainer/SKILL.md` — Workflow for creating
self-contained HTML "PR explainer" pages under `.pr-review/`. Covers git
inspection commands, recommended narrative/order and HTML section
structure, diagram guidance, focused diff-snippet formatting,
verification/proof requirements (example commands), a final checklist,
and expected default outputs (path, coverage, sections, evidence, and
untracked status).
- `.claude/skills/pr-splitter/SKILL.md` — Workflow for splitting large,
tangled PRs into multiple smaller PRs. Includes snapshotting the
original branch, inventorying changes by review unit, a local
uncommitted scratchpad template, strategies (stacked vs parallel,
foundation + follow-ups), safe extraction techniques (path-level and
hunk-level restore), independent verification steps, drift management
(rebasing dependent PRs, recording differences), a reviewer-facing PR
description template, common failure modes, and a required checklist.

**Modified**
- `.gitignore` — Added `.pr-review/` so generated local review pages are
not committed by default.

## Review Effort

Low to Medium — documentation-only changes. No code, dependency, or
changeset modifications. Total added documentation ≈ 260 lines across
the two skill files plus one .gitignore entry.

## Test Plan

Reviewer read both SKILL.md files to verify trigger descriptions and
workflow guidance; author ran `git diff --cached --check` before
committing to confirm no whitespace errors.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16456)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
mastra-ai#16493)

## Summary
- Flip the Observability Traces default from top-level traces to
**branches** (all traces, nested too). With no `listMode` in the URL,
the page now fetches `/api/observability/branches`.
- Remove the list-mode toggle from the local studio toolbar. The
component file is kept in `playground-ui` for now in case we want to
surface it elsewhere.
- URL contract still recognizes `?listMode=traces` for the
top-level-only view; `?listMode=branches` continues to parse but is now
redundant.

## Notes
- Changes live in
[use-trace-url-state.tsx](packages/playground-ui/src/domains/traces/hooks/use-trace-url-state.tsx)
(default + toggle handler) and
[use-traces.tsx](packages/playground-ui/src/domains/traces/hooks/use-traces.tsx)
(defensive default). The toggle render was removed from
[packages/playground/src/pages/traces/index.tsx](packages/playground/src/pages/traces/index.tsx).
- Deleted
`packages/playground/e2e/tests/observability/list-mode-switcher.spec.ts`
— every test in there asserted toggle behavior or the old default. Two
URL-driven tests (branch-detail rendering and intra-panel nav) lived in
the same file and went with it; happy to reinstate either as a new spec
if we want that coverage.

## Test plan
- [ ] `/observability` with a clean URL renders branch rows (nested
invocations visible) and the backend call is
`/api/observability/branches`
- [ ] `/observability?listMode=traces` still renders top-level traces
only and hits `/api/observability/traces`
- [ ] Toolbar no longer shows the "Top-level traces only / All traces,
nested too" dropdown
- [ ] Typecheck passes for `@mastra/playground-ui` and
`@internal/playground`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR makes the Observability page show all traces (including nested
ones) by default instead of only top-level traces. The toolbar dropdown
for switching modes was removed because the new default covers that
view; you can still force the old top-level-only view via the URL.

## Changes

### Default Observability Mode Changed to Branches
- `/observability` now defaults to "branches" mode (shows nested
traces). Previously the default was "traces" (top-level only).
- `?listMode=traces` still forces the top-level view;
`?listMode=branches` is still parsed but now redundant.

### Code Changes
-
packages/playground-ui/src/domains/traces/hooks/use-trace-url-state.tsx
- Treats `branches` as the default `listMode` when the
TRACE_LIST_MODE_PARAM is absent/invalid.
- `handleListModeChange` deletes the URL param when switching to
`branches` (default) and sets it when switching to other modes.
- packages/playground-ui/src/domains/traces/hooks/use-traces.tsx
- `fetchTracesFn` and the exported `useTraces` hook default `listMode`
to `'branches'` instead of `'traces'`.

### UI Removal
- packages/playground/src/pages/traces/index.tsx
- Removed rendering/import of `TracesListModeToggle` from the Traces
page toolbar.

### Tests
- Deleted
packages/playground/e2e/tests/observability/list-mode-switcher.spec.ts,
which contained tests asserting the previous toggle/default behavior and
endpoint selection (`/traces` vs `/branches`).

### Other
- Added a changeset for `@mastra/playground-ui` documenting this
behavioral change (patch).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ai#16495)

## Description

The `minimumReleaseAge: 1440` setting added to `pnpm-workspace.yaml` in
mastra-ai#16462 (as a supply-chain defense ahead of the pnpm v11 default) also
applies to our **own** snapshot/canary publishes. E2E flows that publish
a temporary `0.0.0-…-snapshot` version and then immediately try to
install it fail with:

```
ERR_PNPM_NO_MATURE_MATCHING_VERSION  No matching version found for @mastra/loggers@0.0.0-…-snapshot
The package was released just now and you have a minimumReleaseAge of 1440 minutes set.
```

Affected workflows on main:
- `E2E Tests / create-mastra` (pnpm dlx create-mastra@…-snapshot)
- `E2E Tests / no-bundling`
- `E2E Tests / monorepo`
- `E2E Tests / deployers`

## Fix

Add `minimumReleaseAgeExclude` listing first-party package patterns. The
supply-chain defense remains in effect for all third-party dependencies;
only our own packages bypass the 1-day wait.

```yaml
minimumReleaseAgeExclude:
  - '@mastra/*'
  - '@internal/*'
  - 'mastra'
  - 'create-mastra'
```

Setting reference: https://pnpm.io/settings#minimumreleaseageexclude
(available since pnpm 10.10).

## Test plan

- CI `E2E Tests` job should no longer fail with
`ERR_PNPM_NO_MATURE_MATCHING_VERSION` for `@mastra/*` / `@internal/*` /
`mastra` / `create-mastra` snapshot versions.
- Third-party packages still gated by the 1-day `minimumReleaseAge`.

## Type of change

- [x] Bug fix (non-breaking change that fixes an issue)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

Think of the project like a package store with a safety rule: "Don't
sell anything that just arrived today—wait a day to make sure it's
safe." The problem was this rule was also stopping the store from using
its own fresh products. The fix tells the store: "Apply this safety rule
to packages from other suppliers, but trust our own products and use
them right away."

## Overview

This PR resolves a CI issue where pnpm's `minimumReleaseAge: 1440`
setting (which enforces a 1-day maturity gate for package installation)
was incorrectly blocking the installation of the project's own snapshot
and canary builds during E2E tests. The fix adds a
`minimumReleaseAgeExclude` configuration to `pnpm-workspace.yaml` that
exempts first-party packages from this gate, while preserving the
supply-chain security protection for third-party dependencies.

## Changes

**pnpm-workspace.yaml**: Added `minimumReleaseAgeExclude` configuration
with the following excluded package patterns:
- `@mastra/*` (scoped packages)
- `@internal/*` (internal scoped packages)
- `mastra` (main package)
- `create-mastra` (create-mastra package)

This allows snapshot and canary builds to be installed immediately after
publishing, unblocking E2E test flows (create-mastra, no-bundling,
monorepo, deployers) that were previously failing with
`ERR_PNPM_NO_MATURE_MATCHING_VERSION`.

## Impact

- **Bug Fix**: Resolves CI failures for snapshot/canary E2E flows that
publish and immediately install temporary 0.0.0-…-snapshot versions
- **Security Preserved**: Third-party dependencies remain subject to the
1-day maturity requirement
- **Non-breaking**: This is a configuration-only change with no impact
on public APIs or breaking changes

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16495)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
## Summary

Adds Studio/playground support for Agent signal follow-up chat.

Threaded chat sends now go through the signal-aware `useChat` path,
while Studio keeps a passive subscription open for the active thread so
other tabs can observe in-flight streams. Follow-up messages show a
pending snippet until their signal echo arrives, then assistant chunks
continue below the echoed user message.

This also keeps the composer usable during active signal streams and
wires stop/cancel through subscription abort handling.

## Stack

Previous PR: mastra-ai#16231 — Use Agent signals for MastraCode follow-up chat

This is PR 4 of the Agent signals stack:

1. mastra-ai#16229 — Core Agent signal runtime, server routes, and client-js API
2. mastra-ai#16438 — Route system reminders through Agent signals
3. mastra-ai#16231 — Use Agent signals for MastraCode follow-up chat
4. mastra-ai#16338 — Use Agent signals for Studio/playground follow-up chat

## What changed

- Routes Studio threaded chat sends through Agent signals.
- Keeps a passive thread subscription open so additional tabs can
observe active streams.
- Tracks pending follow-up snippets until their matching
`data-user-message` signal echo arrives.
- Ensures assistant chunks after an echoed signal user message render
below that user message.
- Keeps the composer active during signal streams by intercepting
follow-up sends before assistant-ui blocks them.
- Finishes streaming UI state when cancel/abort happens.
- Preserves autoscroll follow behavior for rapid output while still
allowing intentional user scroll-up.

## Test plan

- `pnpm --filter ./client-sdks/react test
src/lib/ai-sdk/utils/toUIMessage.test.ts -- --bail 1 --reporter=dot`
- `pnpm --filter ./packages/playground-ui test -- --bail 1
--reporter=dot`
- `pnpm --filter ./client-sdks/client-js test
src/utils/process-mastra-stream.test.ts -- --bail 1 --reporter=dot`
- `pnpm --filter ./packages/server test
src/server/handlers/agents.test.ts -- --bail 1 --reporter=dot`
- `pnpm build:server`
- `pnpm build:playground-ui`
- `pnpm --filter ./packages/playground build`
- `pnpm build:cli`



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5 Explanation

This PR lets you send follow-up chat messages while an AI agent is still
responding, without breaking things. Your new message shows up as
pending until confirmed, other browser tabs can watch the response
stream live, and a bug that used to duplicate messages is now fixed.

---

## Overview

This PR adds Studio/playground support for Agent signal follow-up chat,
enabling users to send threaded messages while streaming is active.
Threaded chat sends are routed through a signal-aware path while Studio
passively subscribes to threads for multi-tab observation. Follow-up
messages render as pending until echoed by a data-user-message signal,
after which assistant chunks continue below. The composer remains usable
during active streams, and stop/cancel flows are wired through
subscription abort handling. A companion fix prevents chat message
duplication when sending during streaming.

## Changes

### Chat Hook Updates
- `useChat` hook now supports thread-based user-message signal streaming
when `threadId` is provided
- Added thread subscription management with abort/key/promise refs
- Implements fallback to legacy `streamUntilIdle` route when thread
signals are unsupported
- New callbacks: `onSignalSent`, `onSignalEcho`,
`onThreadSignalsUnsupported`
- Message deduplication by signal ID prevents duplicates when sending
during streaming
- Tool approval/decline output suppressed while thread subscription is
active
- Cancellation aborts active stream, closes thread subscription, and
finalizes assistant messages

### Message Processing Utilities
- Added `markStreamingPartsDone` and `finishStreamingAssistantMessage`
helpers to normalize streaming state
- Added `appendAssistantMessage` utility for consistent assistant
message building
- New `signalContentsToUserMessages` converts payload contents into UI
user messages
- Enhanced `toUIMessage` to handle `data-user-message` chunks with
deduplication by signal/message ID
- `data-user-message` handler converts chunk contents into multiple user
messages and finalizes preceding assistant streaming parts
- `start` chunk handler deduplicates using `payload.messageId` when
present
- `text-start` handler prevents duplicate streaming text parts via
`textId` checking
- `text-delta` handler creates new assistant message when none exists
- Comprehensive test coverage for signal echoing, deduplication, mixed
attachments, and abort behavior

### Playground UI Components
- New `ThreadRuntimeState` context tracks streaming/cancel behavior and
pending signals
- Thread composer now uses `useThreadRuntimeState` for streaming-aware
behavior
- Composer input disables submit while streaming (when not permitted),
enables Enter key during streaming only when allowed
- Composer renders animated "pending:" preview list above input when
pending messages exist
- Send/cancel button refactored: renders cancel button during streaming,
otherwise send button; cancel button works even when sending is
disallowed

### Runtime State Management
- `MastraRuntimeProvider` maintains thread-signal UI state with
`pendingSignals` and `threadSignalsUnsupported` flag
- Callbacks track pending signals on signal send/echo events
- Refactored cancel path to abort via abort controller, clear refs,
reset streaming state, and refresh UI
- Provider wraps `AssistantRuntimeProvider` with
`ThreadRuntimeStateProvider`, exposing streaming state and pending
signals to consumers

### Documentation
- Added Studio behavior clarification: follow-up messages during
streaming appear as pending until confirmed, with multi-tab observation
support

### Testing
- Expanded `toUIMessage` test coverage for signal echoing,
deduplication, and state transitions
- Updated thread subscription cancellation test to verify
abort/unsubscribe behavior
- Bug fix: streaming chat messages no longer duplicate when sending
during agent response, and previous response no longer marked as
streaming

### Type Exports
- `MastraChatProps`: added `threadId`, `onSignalSent`, `onSignalEcho`,
`onThreadSignalsUnsupported`
- `StreamArgs`: added optional `signalId`
- `PendingSignalMessage` type for tracking pending signals
- `ThreadRuntimeState` type for thread runtime context

## Impact

- Users can send follow-up messages while agent is streaming without UI
degradation
- Multi-tab support allows observing in-flight streams across browser
tabs
- Fixes message duplication and streaming state bugs from prior
streaming implementation
- Backwards compatible: falls back to legacy streaming when servers lack
signal-route support

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16338)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
…a + deploy commands (mastra-ai#15728)

## What this ships

End-to-end Mastra Observability onboarding from the CLI. After this PR,
a user who scaffolds a new Mastra project and says "yes" to
Observability is fully wired up to the hosted platform with no manual
steps.

## Changes

**`create-mastra` / `mastra init`**
- New "Enable Mastra Observability? (will open auth flow)" prompt, plus
`--observability` / `--no-observability` flags for non-interactive use
and `--observability-project <name>` to bypass the picker.
- When the user opts in, the CLI:
  1. Runs the browser login flow inline if the user isn't authenticated.
2. Resolves the current organization (force-prompts the picker when the
user belongs to multiple orgs, and persists the selection to
`~/.mastra/credentials.json`).
3. For `mastra init`: lists existing projects and lets the user pick one
or create a new one. For `create-mastra`: always creates a new project
using the local project name (no picker, no re-prompt).
4. Creates projects as observability-only on the platform
(`studioEnabled: false`, `serverEnabled: false`) so they aren't
mislabelled as Studio in the UI.
5. Mints an org-scoped access token (`POST /v1/auth/tokens`) named
`mastra observability – <projectName>`.
6. Writes `MASTRA_PLATFORM_ACCESS_TOKEN` + `MASTRA_PROJECT_ID` (and
`MASTRA_CLOUD_TRACES_ENDPOINT` when targeting a non-prod platform) to
`.env`.
- The generated project registers `MastraPlatformExporter` out of the
box.
- Falls back to a placeholder `.env` block with manual instructions if
provisioning fails, so init never hard-fails on network/auth issues.

**`mastra studio deploy` / `mastra server deploy`**
- Both commands now read `MASTRA_PROJECT_ID` and `MASTRA_ORG_ID` from
the project's `.env` / `.env.local` / `.env.production` before resolving
the target project, so projects scaffolded with Observability auto-link
to the existing platform project instead of creating a duplicate on
first deploy. Existing `process.env` values take precedence.
- On first deploy to an observability-only project, the platform flips
the matching runtime flag (`studioEnabled` / `serverEnabled`) so the
project shows up correctly in the UI.

**Refactor**
- Lifts `resolveCurrentOrg` from `commands/studio/projects.ts` to
`commands/auth/orgs.ts`, adds a `forcePrompt` option, and persists the
picked org to `~/.mastra/credentials.json`.

## Test plan

- `pnpm --filter ./packages/cli test` — green (includes new tests for
`provisionObservabilityProject`, `writeObservabilityEnv`,
`resolveCurrentOrg`, and `loadDeployEnvFromDotenv`).
- `pnpm --filter ./packages/cli typecheck` — clean.
- Verified end-to-end against the production platform: `create-mastra`
provisions an observability-only project, mints a token, writes `.env`,
and the resulting `MASTRA_PLATFORM_ACCESS_TOKEN` / `MASTRA_PROJECT_ID`
pair successfully ingests spans.

---------

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`main` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `main`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## @mastra/client-js@1.18.0-alpha.15

### Minor Changes

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

    ```ts
    const { sendMessage } = useChat({ agentId, resourceId, threadId });
await sendMessage({ message: 'Follow up while streaming', threadId });
    ```

### Patch Changes

-   Updated dependencies:
    -   @mastra/core@1.33.0-alpha.14

## @mastra/react@0.3.0-alpha.15

### Minor Changes

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

    ```ts
    const { sendMessage } = useChat({ agentId, resourceId, threadId });
await sendMessage({ message: 'Follow up while streaming', threadId });
    ```

### Patch Changes

- Fixed streaming chat messages so sending while an agent is running
does not duplicate assistant output or leave the previous response
marked as streaming.
([mastra-ai#16338](mastra-ai#16338))

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/client-js@1.18.0-alpha.15
    -   @mastra/core@1.33.0-alpha.14

## mastra@1.9.0-alpha.15

### Minor Changes

- Add "Enable Mastra Observability? (will open auth flow)" prompt to
`create-mastra` and `mastra init`.
([mastra-ai#15728](mastra-ai#15728))

When the user opts in, the CLI runs the interactive browser login flow
(if not already authenticated), lets them pick an existing project or
create a new one, mints a fresh organization access token, and writes
`MASTRA_PLATFORM_ACCESS_TOKEN` + `MASTRA_PROJECT_ID` to `.env`. The
generated project already registers a `MastraPlatformExporter`, so no
additional setup is needed to start sending traces.

`MASTRA_PLATFORM_ACCESS_TOKEN` replaces `MASTRA_CLOUD_ACCESS_TOKEN`. The
old name is still read by the exporter for backwards compatibility but
is deprecated.

If provisioning fails (e.g., the platform is unreachable), the command
falls back to writing placeholder env vars with instructions.

Both commands also accept `--observability` / `--no-observability` flags
for non-interactive use, and `--observability-project <name>` to bypass
the project picker.

### Patch Changes

- `mastra studio deploy` and `mastra server deploy` now read
`MASTRA_PROJECT_ID` and `MASTRA_ORG_ID` from the project's `.env` file,
so projects scaffolded with Mastra Observability auto-link to the
existing platform project on first deploy instead of creating a new one.
Existing `process.env` values still take precedence.
([mastra-ai#15728](mastra-ai#15728))

- Mastra Observability provisioning now creates new platform projects as
observability-only (no Studio or Server runtime attached). The first
`mastra studio deploy` or `mastra server deploy` flips the matching
runtime flag, so projects are no longer mislabelled as Studio in the
platform UI before any deploy has happened.
([mastra-ai#15728](mastra-ai#15728))

- `mastra studio deploy` and `mastra server deploy` now prompt you to
pick from existing projects in the selected organization instead of
silently matching by `package.json` name or always creating a new
project. ([mastra-ai#15728](mastra-ai#15728))

    **What changed**

- When existing projects are found, both commands show an interactive
selector listing them (plus a "Create new project" option).
- `--project <id-or-slug>` still bypasses the selector for
non-interactive use.
- `-y/--yes` auto-accepts only when there is exactly one project whose
name or slug matches the local `package.json` name; otherwise it errors
asking you to pass `--project`.
- Projects saved in `.mastra-project.json` for the same organization are
still auto-matched (no prompt).

This fixes deploys accidentally creating duplicate projects or targeting
the wrong existing project when the local package name happened to
collide.

-   Updated dependencies:
    -   @mastra/deployer@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## create-mastra@1.9.0-alpha.15

### Minor Changes

- Add "Enable Mastra Observability? (will open auth flow)" prompt to
`create-mastra` and `mastra init`.
([mastra-ai#15728](mastra-ai#15728))

When the user opts in, the CLI runs the interactive browser login flow
(if not already authenticated), lets them pick an existing project or
create a new one, mints a fresh organization access token, and writes
`MASTRA_PLATFORM_ACCESS_TOKEN` + `MASTRA_PROJECT_ID` to `.env`. The
generated project already registers a `MastraPlatformExporter`, so no
additional setup is needed to start sending traces.

`MASTRA_PLATFORM_ACCESS_TOKEN` replaces `MASTRA_CLOUD_ACCESS_TOKEN`. The
old name is still read by the exporter for backwards compatibility but
is deprecated.

If provisioning fails (e.g., the platform is unreachable), the command
falls back to writing placeholder env vars with instructions.

Both commands also accept `--observability` / `--no-observability` flags
for non-interactive use, and `--observability-project <name>` to bypass
the project picker.

### Patch Changes

- Mastra Observability provisioning now creates new platform projects as
observability-only (no Studio or Server runtime attached). The first
`mastra studio deploy` or `mastra server deploy` flips the matching
runtime flag, so projects are no longer mislabelled as Studio in the
platform UI before any deploy has happened.
([mastra-ai#15728](mastra-ai#15728))

## @mastra/server@1.33.0-alpha.14

### Minor Changes

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

    ```ts
    const { sendMessage } = useChat({ agentId, resourceId, threadId });
await sendMessage({ message: 'Follow up while streaming', threadId });
    ```

### Patch Changes

-   Updated dependencies:
    -   @mastra/core@1.33.0-alpha.14

## @mastra/deployer-cloud@1.33.0-alpha.14

### Patch Changes

-   Updated dependencies:
    -   @mastra/deployer@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/longmemeval@1.0.38-alpha.15

### Patch Changes

-   Updated dependencies:
    -   @mastra/core@1.33.0-alpha.14

## @mastra/opencode@0.0.35-alpha.15

### Patch Changes

-   Updated dependencies:
    -   @mastra/core@1.33.0-alpha.14

## mastracode@0.18.0-alpha.16

### Patch Changes

-   Updated dependencies:
    -   @mastra/core@1.33.0-alpha.14

## @mastra/deployer@1.33.0-alpha.14

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/server@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/mcp-docs-server@1.1.35-alpha.27

### Patch Changes

-   Updated dependencies:
    -   @mastra/core@1.33.0-alpha.14

## @mastra/playground-ui@27.0.0-alpha.15

### Patch Changes

- Changed the default Observability list mode to branches (all traces,
including nested). The query logic still recognizes `?listMode=traces`
to opt back into the top-level-only view.
([mastra-ai#16493](mastra-ai#16493))

    **Before**

    `/observability` → top-level traces only

    **After**

    `/observability` → branches (all traces, nested too)
    `/observability?listMode=traces` → top-level traces only

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92),
[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/react@0.3.0-alpha.15
    -   @mastra/client-js@1.18.0-alpha.15
    -   @mastra/core@1.33.0-alpha.14

## @mastra/express@1.3.19-alpha.14

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/server@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/fastify@1.3.19-alpha.14

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/server@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/hono@1.4.14-alpha.14

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/server@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/koa@1.5.2-alpha.14

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/server@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/nestjs@0.1.3-alpha.14

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92)]:
    -   @mastra/server@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/temporal@0.1.2-alpha.14

### Patch Changes

-   Updated dependencies:
    -   @mastra/deployer@1.33.0-alpha.14
    -   @mastra/core@1.33.0-alpha.14

## @mastra/core@1.33.0-alpha.14



## @internal/playground@1.9.0-alpha.15

### Patch Changes

- Updated dependencies
\[[`05dab92`](mastra-ai@05dab92),
[`05dab92`](mastra-ai@05dab92),
[`99a579b`](mastra-ai@99a579b)]:
    -   @mastra/react@0.3.0-alpha.15
    -   @mastra/client-js@1.18.0-alpha.15
    -   @mastra/playground-ui@27.0.0-alpha.15
    -   @mastra/core@1.33.0-alpha.14

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Description

<!-- Required: Provide a brief description of the changes in this PR.
-->

## Related issue(s)

<!-- Required: Link to the issue(s) this PR addresses, e.g. with: Fixes
mastra-ai#123. PRs without linked issues may be closed. -->

## Type of change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR stabilizes end-to-end tests by switching them to an internal LLM
test-mode that injects per-test dummy API keys, bumps timeouts for a few
slow Gemini tests, and updates CI to pull real credentials from GitHub
Secrets so tests can authenticate to external services.

## Changes

### Test setup: use internal LLM test-mode + dummy API keys
- Many e2e test files were updated to use internal test utilities
(getLLMTestMode + setupDummyApiKeys) instead of dotenv. Each now calls
setupDummyApiKeys(getLLMTestMode(), [...providers]) at module/test init
to provision provider-specific dummy keys. Examples of affected tests
include:
  - packages/core/src/agent/agent-gemini.e2e.test.ts
- packages/core/src/agent/__tests__/* (image-prompt,
prefill-error-recovery, reasoning-openai-summaries, save-and-errors,
stopWhen, tool-approval, tool-calls-finish-reason, tool-handling,
workspace-tools-openai)
  - packages/core/src/agent/* (agent-network, agent-processor)
-
packages/core/src/agent/durable/__tests__/durable-agent-background-tasks.e2e.test.ts
  - packages/core/src/background-tasks/background-tasks.e2e.test.ts
  - packages/core/src/llm/model/model.loop.e2e.test.ts
  - packages/core/src/processors/* (structured-output, token-accuracy)
  - packages/core/src/processors/provider-history-compat.e2e.test.ts
- packages/core/src/tools/* (provider-tools-ordering,
provider-tools-persistence, tool-builder/builder.e2e.test.ts)
- packages/core/src/voice/aisdk/__tests__/* (aisdk-voice,
composite-voice)
- packages/evals/src/scorers/llm/* (answer-similarity,
context-precision, context-relevance, faithfulness, hallucination,
noise-sensitivity, prompt-alignment, tool-call-accuracy, toxicity)
- The dotenv/config() usage was removed where present. Test logic and
assertions were preserved; no public API surface changes.

### Timeout adjustment for Gemini tests
- packages/core/src/agent/agent-gemini.e2e.test.ts:
  - Adds setupDummyApiKeys(getLLMTestMode(), ['google']).
- Increases per-test timeouts for three Gemini-related e2e tests from
40,000ms to 120,000ms.
  - No assertions changed.

### CI workflow: replace placeholders with GitHub Secrets
- .github/workflows/test-suite.yml:
- e2e-test job now sources environment variables from GitHub Secrets
instead of hardcoded placeholders.
- Secrets added/used include LLM keys and service credentials such as
OPENAI_API_KEY, OPENROUTER_API_KEY, ANTHROPIC_API_KEY,
GOOGLE_GENERATIVE_AI_API_KEY, COHERE_API_KEY, PINECONE_API_KEY,
ASTRA_DB_ENDPOINT/TOKEN, CLOUDFLARE_API_TOKEN/ACCOUNT_ID,
KV_REST_API_URL/KV_REST_API_TOKEN, etc., enabling CI to provide real
credentials for external integrations.

## Other notes
- Changes are test-only and CI configuration; no exported/public API
declarations were modified.
- PR description lacks implementation details and linked issues; the
single commit message ("fix") is generic and checklist items in the PR
body are unchecked.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16486)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This adds an experimental `A2AAgent` to `@mastra/core/a2a` so a Mastra
agent can use a remote A2A agent as a subagent.

The main story here is registering a remote agent card URL on a parent
agent and letting Mastra delegate to it through the normal subagent
path.

```ts
import { Agent } from '@mastra/core/agent';
import { A2AAgent } from '@mastra/core/a2a';

const supportAgent = new Agent({
  name: 'Support Agent',
  model,
  agents: {
    remoteWeather: new A2AAgent({
      url: 'https://example.com/.well-known/agent-card.json',
    }),
  },
});
```

`A2AAgent` handles agent card discovery/bootstrap, remote `generate` /
`resumeGenerate`, remote `stream` / `resumeStream`, suspend-resume
payloads, optional agent card verification, and the browser-safe shared
A2A imports used by `client-js` via `@mastra/core/a2a/client`.

I also tightened the stream path a bit while building this out:
malformed SSE frames are skipped instead of aborting the run,
non-transient 4xx responses are not retried, and the focused tests now
cover the subagent path plus those stream edge cases.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

This PR lets one AI agent use another AI agent as a helper. Instead of
doing all the work itself, an agent can send tasks to a remote AI agent
and get back the results—kind of like asking a friend for help instead
of doing something alone.

---

## Changes Summary

This PR introduces an experimental **A2AAgent** implementation that
enables Mastra agents to delegate work to remote A2A agents via a
subagent pattern. The implementation includes comprehensive testing,
stream robustness improvements, and a browser-safe client surface for
shared types.

### Core Implementation

- **A2AAgent** class added at `@mastra/core/a2a` implementing the
`SubAgent` interface
- Supports `generate()`, `resumeGenerate()`, `stream()`, and
`resumeStream()` methods for remote A2A execution over JSON-RPC
  - Auto-discovers and caches remote Agent Cards in memory
  - Supports optional agent card verification before execution
- Handles request retries with exponential backoff, timeouts, and
credentials
- Implements suspend–resume payload handling for multi-turn interactions

### Stream Handling & Robustness

- Malformed SSE frames are now skipped instead of aborting stream
processing
- Non-transient HTTP 4xx responses are no longer retried
- Stream consumption supports both SSE-based and buffered fallback modes
- Proper cleanup of stream locks and cancellation tokens

### Browser-Safe Client Surface

- New public export subpath `@mastra/core/a2a/client` provides
browser-compatible types and error classes
- Client-JS can now reuse A2A protocol types and errors without
Node-only runtime dependencies
- Reorganized type exports to separate client-safe interfaces from
internal implementation

### Testing & Validation

- 823-line comprehensive test suite for A2AAgent covering:
  - Agent card caching and verification behavior
  - Non-streaming and streaming execution paths
  - Resume/continuation scenarios
  - SSE stream edge cases and error handling
  - Subagent integration with parent agent memory
- 6-line regression test ensuring AgentBadge renders correctly for
subagent content

### Build & Configuration

- Updated `package.json` exports map with new `./a2a/client` subpath
(ESM and CJS targets with shared type definitions)
- Added `src/a2a/client.ts` to tsup build configuration
- Reorganized module exports: moved bulk type/error re-exports from main
index to client barrel

### Type System Additions

New public types in `@mastra/core/a2a/types`:
- `A2AAgentOptions` – configuration for agent card URL, verification,
fetch, timeout, retry, and credential handling
- `A2AAgentVerificationOptions` & `A2AAgentCardVerificationContext` –
optional card verification hooks
- `A2AAgentRunState` – tracks run/task/context identifiers and card URLs
- `A2AAgentResumePayload` – suspend–resume state for multi-turn
execution
- `A2AAgentGenerateResult` & `A2AAgentStreamResult` – typed results for
generation and streaming

### Client-JS Updates

- Updated imports across test files, resources, and utilities to use
`@mastra/core/a2a/client` instead of `@mastra/core/a2a`
- Improved agent card signature tampering in tests to use base64url
byte-flipping instead of character substitution

### UI Refinement

- `AgentBadge` no longer defaults badge to collapsed state; now
consistently renders open for both live and hydrated subagent content

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16348)

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Abhi Aiyer <abhiaiyer91@gmail.com>
## Summary
- start the platform login/token flow immediately after users opt in to
observability during create/init prompts
- reuse that authenticated state later when provisioning the
observability project
- add prompt-level coverage to ensure auth only starts for the yes path

## Test plan
- pnpm --filter mastra exec vitest run src/commands/init/utils.test.ts
src/commands/init/observability-provision.test.ts
- pnpm --filter mastra typecheck
- pnpm --filter mastra build:lib

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5 (Explain Like I'm 5)

When you answer "yes" to "Do you want monitoring?", the CLI now
immediately logs you into the platform so it's ready to set up
monitoring right away, instead of asking for credentials later.

---

## Changes Overview

### packages/cli/src/commands/init/utils.ts
- Added export: `promptForObservability()` — prompts the user and, if
they opt in, immediately runs the platform auth flow via `getToken()`
and returns `{ enabled: true, token }`.
- Added `ObservabilityPromptResult` type: `{ enabled?: boolean; token?:
string }`.
- Refactored `interactivePrompt` to delegate the observability step to
`promptForObservability()` and to include `observability` and
`observabilityToken` when flattening results.

### packages/cli/src/commands/init/utils.test.ts
- Added tests for `promptForObservability()` with mocks for
`@clack/prompts` and `../auth/credentials.js`.
- Test cases:
- When select returns "yes": resolves to `{ enabled: true, token:
'platform-token' }` and calls `getToken`.
- When select returns "no": resolves to `{ enabled: false }` and does
not call `getToken`.
- Consolidated async imports/setup for observability-related tests.

### packages/cli/src/commands/init/observability-provision.ts
- Updated `provisionObservabilityProject` signature to accept an
optional `token` parameter.
- If `token` is provided, uses it instead of calling `getToken()`;
otherwise falls back to `await getToken()`.

### packages/cli/src/commands/init/observability-provision.test.ts
- Added a test verifying that when a token is passed into provisioning,
`getToken` is not called and the provision flow uses the provided token
(calls `resolveCurrentOrg` with the token and `{ forcePrompt: true }`).

### packages/cli/src/commands/actions/init-project.ts and
packages/cli/src/commands/create/create.ts
- Thread `observabilityToken` from the interactive prompt result into
`init(...)` so the token obtained at prompt time is reused during
provisioning.

### packages/cli/src/commands/init/init.ts
- `init` now accepts an optional `observabilityToken` option and
forwards it to `provisionObservabilityProject({ token:
observabilityToken })`.

### .changeset/cli-observability-auth-prompt.md
- New changeset documenting the behavior: start platform auth
immediately when users opt into observability during create/init and
reuse the token for provisioning.

---

## Impact

- User experience: authentication is paired with the observability
opt-in prompt, eliminating a later credential prompt and enabling a
smoother provisioning flow.
- Behavior: the token obtained at opt-in is reused for observability
project provisioning; code paths that previously always called
`getToken()` now accept and prefer a supplied token.

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16502)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
)

## Summary
- Allow MastraPlatformExporter to use MASTRA_PLATFORM_ACCESS_TOKEN when
present, falling back to MASTRA_CLOUD_ACCESS_TOKEN
- Keep explicit accessToken config precedence over environment variables
- Update observability tests and the missing-token debug expectation

## Test plan
- pnpm --filter @mastra/observability exec vitest run
src/exporters/mastra-platform.test.ts src/registry.test.ts
- pnpm --filter @mastra/observability build

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR makes the observability exporter look for a second environment
variable (MASTRA_PLATFORM_ACCESS_TOKEN) to authenticate with the cloud
and prefer it over the older MASTRA_CLOUD_ACCESS_TOKEN when no explicit
token is configured.

## Changes

### Core Changes

**observability/mastra/src/exporters/mastra-platform.ts**
- Resolve `accessToken` from, in order: `config.accessToken`,
`MASTRA_PLATFORM_ACCESS_TOKEN`, then `MASTRA_CLOUD_ACCESS_TOKEN`.
- Update the debug message shown when no token is available to mention
both `MASTRA_PLATFORM_ACCESS_TOKEN` and `MASTRA_CLOUD_ACCESS_TOKEN`.

### Test Updates

**observability/mastra/src/exporters/mastra-platform.test.ts**
- Added four integration tests verifying Authorization header selection
when no explicit token is configured:
  - Uses `MASTRA_CLOUD_ACCESS_TOKEN` when only the cloud token is set.
- Uses `MASTRA_PLATFORM_ACCESS_TOKEN` when only the platform token is
set.
- Prefers `MASTRA_PLATFORM_ACCESS_TOKEN` when both env vars are present.
- Falls back to `MASTRA_CLOUD_ACCESS_TOKEN` when
`MASTRA_PLATFORM_ACCESS_TOKEN` is set to an empty string.
- Each test stubs environment variables with `vi.stubEnv`, exports a
span, flushes to trigger the HTTP call, asserts the mocked request's
`Authorization` header, and restores state in a `finally` block.

**observability/mastra/src/registry.test.ts**
- The missing-token test now stubs both `MASTRA_CLOUD_ACCESS_TOKEN` and
`MASTRA_PLATFORM_ACCESS_TOKEN` (the latter as empty) and updates the
expected DEBUG message to reference both environment variable names.

### Changeset

**.changeset/platform-observability-token-env.md**
- Added a patch changeset documenting support for
`MASTRA_PLATFORM_ACCESS_TOKEN` while retaining
`MASTRA_CLOUD_ACCESS_TOKEN` as a fallback.

## Notes
- No public API changes.
- Test plan:
- pnpm --filter @mastra/observability exec vitest run
src/exporters/mastra-platform.test.ts src/registry.test.ts
  - pnpm --filter @mastra/observability build

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16500)
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`main` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `main`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## @mastra/core@1.33.0-alpha.15

### Minor Changes

- Added experimental support for using remote A2A agents as Mastra
subagents. ([mastra-ai#16348](mastra-ai#16348))

    **What changed**

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

    **Example**

    ```ts
    import { Agent } from '@mastra/core/agent';
    import { A2AAgent } from '@mastra/core/a2a';

    const agent = new Agent({
      name: 'Support Agent',
instructions: 'Use the remote billing specialist for billing
questions.',
      model: 'openai/gpt-4o-mini',
      agents: {
        billingSpecialist: new A2AAgent({
url: 'https://billing.example.com/.well-known/agent-card.json',
        }),
      },
    });

const result = await agent.generate('Can you check the latest invoice
status?');
    ```

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

## @mastra/client-js@1.18.0-alpha.16

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15

## @mastra/react@0.3.0-alpha.16

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/client-js@1.18.0-alpha.16

## @mastra/deployer-cloud@1.33.0-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/deployer@1.33.0-alpha.15

## @mastra/longmemeval@1.0.38-alpha.16

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15

## @mastra/opencode@0.0.35-alpha.16

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15

## mastracode@0.18.0-alpha.17

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/arize@1.1.0-alpha.5

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/otel-exporter@1.1.0-alpha.5

## @mastra/arthur@0.2.10-alpha.5

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/otel-exporter@1.1.0-alpha.5

## @mastra/braintrust@1.1.0-alpha.4

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/datadog@1.2.0-alpha.5

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/laminar@1.1.0-alpha.4

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/langfuse@1.3.0-alpha.5

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/otel-exporter@1.1.0-alpha.5

## @mastra/langsmith@1.2.0-alpha.4

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/observability@1.12.0-alpha.4

### Patch Changes

- Support `MASTRA_PLATFORM_ACCESS_TOKEN` as the preferred environment
variable for `MastraPlatformExporter`, while retaining
`MASTRA_CLOUD_ACCESS_TOKEN` as a fallback for backward compatibility.
([mastra-ai#16500](mastra-ai#16500))

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15

## @mastra/otel-bridge@1.1.0-alpha.5

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/otel-exporter@1.1.0-alpha.5

## @mastra/otel-exporter@1.1.0-alpha.5

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/posthog@1.0.24-alpha.4

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15

## @mastra/sentry@1.0.23-alpha.5

### Patch Changes

- Updated dependencies
\[[`8781d45`](mastra-ai@8781d45),
[`105e454`](mastra-ai@105e454)]:
    -   @mastra/observability@1.12.0-alpha.4
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/otel-exporter@1.1.0-alpha.5

## mastra@1.9.0-alpha.16

### Patch Changes

- Start the Mastra Platform auth flow immediately after users opt in to
observability during `create mastra` and `mastra init`, then reuse that
token when provisioning the observability project.
([mastra-ai#16502](mastra-ai#16502))

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/deployer@1.33.0-alpha.15

## @mastra/deployer@1.33.0-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/server@1.33.0-alpha.15

## @mastra/mcp-docs-server@1.1.35-alpha.28

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15

## @mastra/playground-ui@27.0.0-alpha.16

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/client-js@1.18.0-alpha.16
    -   @mastra/react@0.3.0-alpha.16

## @mastra/server@1.33.0-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15

## @mastra/express@1.3.19-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/server@1.33.0-alpha.15

## @mastra/fastify@1.3.19-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/server@1.33.0-alpha.15

## @mastra/hono@1.4.14-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/server@1.33.0-alpha.15

## @mastra/koa@1.5.2-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/server@1.33.0-alpha.15

## @mastra/nestjs@0.1.3-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/server@1.33.0-alpha.15

## @mastra/temporal@0.1.2-alpha.15

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/deployer@1.33.0-alpha.15

## create-mastra@1.9.0-alpha.16



## @internal/playground@1.9.0-alpha.16

### Patch Changes

- Updated dependencies
\[[`105e454`](mastra-ai@105e454)]:
    -   @mastra/core@1.33.0-alpha.15
    -   @mastra/client-js@1.18.0-alpha.16
    -   @mastra/react@0.3.0-alpha.16
    -   @mastra/playground-ui@27.0.0-alpha.16

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…2E jobs (mastra-ai#16505)

## Problem

mastra-ai#16462 added `minimumReleaseAge: 1440` to `pnpm-workspace.yaml` ahead of
pnpm v11 default, as a supply-chain defense against newly-published
malicious third-party packages.

mastra-ai#16495 added `minimumReleaseAgeExclude` to exempt our own packages
(`@mastra/*`, `@internal/*`, `mastra`, `create-mastra`) so the gate did
not block our snapshot/canary E2E flows.

That fixed the in-workspace case but the snapshot E2E jobs are still
failing:

```
ERR_PNPM_NO_MATURE_MATCHING_VERSION  Version 0.0.0-no-bundling-test-…-snapshot
(released just now) of @mastra/loggers does not meet the minimumReleaseAge constraint
```

The reason: these jobs publish a snapshot to a **local registry**, then
install it inside a **freshly generated project under `/tmp/…`**. That
generated project is outside the workspace and has no
`pnpm-workspace.yaml`, so the workspace-level `minimumReleaseAgeExclude`
setting does not apply.

Affected jobs:

- `E2E monorepo`
- `E2E no-bundling`
- `E2E create-mastra`
- `E2E deployers`

## Fix

Set `NPM_CONFIG_MINIMUM_RELEASE_AGE_EXCLUDE='@mastra/* @internal/*
mastra create-mastra'` on each affected job Test step. This mirrors the
workspace-level `minimumReleaseAgeExclude` but applies inside the
generated `/tmp/…` project where the workspace config does not reach.

## Why this is the right scope

The `minimumReleaseAge` defense exists to prevent CI/dev installs from
immediately picking up malicious third-party publishes before the
ecosystem can detect them. The exclude env keeps that defense intact for
third-party deps while letting our own freshly-built snapshots install:

- Only our **own first-party packages** (`@mastra/*`, `@internal/*`,
`mastra`, `create-mastra`) are exempted
- **Third-party transitive deps** pulled in by the generated `/tmp/…`
project still go through the 1-day gate — a newly published malicious
npm package would still be blocked
- The exclude env is scoped to the `Test` step of these four jobs; every
other `pnpm install` in CI (workspace bootstrap, lint, etc.) is
unaffected
- The whole `/tmp/…` project is discarded at job end

Compared to alternatives:

- **`NPM_CONFIG_MINIMUM_RELEASE_AGE=0`** would also bypass the gate for
every third-party package the generated project transitively installs,
weakening supply-chain defense on the CI runner.
- **Patching the `mastra build` output template** to ship
`minimumReleaseAgeExclude` to every generated user project would leak
CI-test concerns into production code.
- **Dropping `minimumReleaseAge` entirely** would undo mastra-ai#16462
supply-chain defense for real workspace installs.

## Test plan

- `E2E Tests` on this PR should no longer fail with
`ERR_PNPM_NO_MATURE_MATCHING_VERSION` for our own snapshot versions.
- Workspace bootstrap, `Build`, and `Install dependencies` steps still
run with the gate in effect, so third-party packages newer than 24h are
still blocked everywhere else, including inside the generated `/tmp/…`
project.

Co-Authored-By: Mastra Code (anthropic/claude-opus-4-7)
<noreply@mastra.ai>

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
roaminro and others added 27 commits May 20, 2026 08:30
…ra-ai#16799)

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
…fresh (mastra-ai#16788)

## Summary

- Replace `@radix-ui/react-slider` with `@base-ui/react/slider` as the
underlying primitive
- Refreshed visuals: rounded thumb with neutral border + neutral inside,
opacity-based track that adapts to any surface, neutral filled indicator
(no accent / green color)
- Bigger interactive area: padded `Slider.Control` for a tall click
target + invisible `::after` halo on the thumb so it's easier to grab
- `cursor-pointer` on the control, `cursor-not-allowed` when disabled
- `onValueChange` / `onValueCommitted` wrapped to always emit
`number[]`, preserving the existing consumer API even though base-ui
returns `number | number[]` internally
- Drop unused `@radix-ui/react-slider` and `@radix-ui/react-tabs`
dependencies
- Stories expanded with `ThreeThumbs`, `PriceRange`, `Vertical`,
`States` showcase + a live-value `WithLabel`

## Where it's used

- `packages/playground/src/domains/agents/components/agent-settings.tsx`
(Temperature & Top P)
- `packages/playground/src/domains/workflows/workflow/zoom-slider.tsx`
(workflow zoom)

Both consumers pass `value={[number]}` and use `value[0]` in
`onValueChange` — no consumer changes required.

## Test plan

- [ ] Storybook (`packages/playground-ui`, port 6006): verify Default,
WithRange, ThreeThumbs, Disabled, Vertical, States stories
- [ ] Drag, click-on-track, and keyboard (Arrow keys, PageUp/Down) all
update the value
- [ ] Local studio (`pnpm dev:playground`) → open an agent → Model
Settings → Temperature & Top P sliders drag smoothly, value reflects in
the label, snaps to `n/a` at min
- [ ] Workflow graph zoom slider still controls zoom level
- [ ] Track remains visible on `surface2` / `surface3` / `surface4`
panels (opacity-based color)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

The slider you drag to change settings was rebuilt using a different,
simpler building block so it looks more neutral, is easier to grab, and
keeps working the same for the rest of the app.

## Overview

Migrates the playground Slider from `@radix-ui/react-slider` to
`@base-ui/react/slider`, applies a neutral visual refresh, increases hit
targets/usability, preserves the public API shape, removes unused Radix
deps, and expands Storybook coverage.

## Key Changes

- Component migration
  - Replaced Radix Slider primitive with Base UI Slider primitive.
- Converted from a React.forwardRef implementation to a plain functional
component.
  - Added a SliderProps type that normalizes callback signatures.

- API compatibility
- Wrapped onValueChange and onValueCommitted to always emit number[]
(preserves existing consumer expectations despite Base UI returning
number | number[]).

- Visual & interaction refresh
- Rounded thumb with neutral border and neutral fill; neutral filled
indicator (removed accent/green).
  - Track uses opacity so it adapts to surface backgrounds.
- Increased interactive area: padded Slider.Control and invisible
::after halo on the thumb to improve grabability.
  - cursor-pointer on Control; cursor-not-allowed when disabled.
  - Focus/interaction classes updated for Base UI.

- Stories & docs
  - Storybook stories expanded and updated:
    - Wider default width for many stories (w-[240px]).
    - WithLabel converted to a controlled example showing live value.
- New stories: PriceRange, Vertical, States, ThreeThumbs, and live-value
WithLabel.

- Dependencies & housekeeping
  - Removed unused `@radix-ui/react-slider` and `@radix-ui/react-tabs`.
  - Changeset added and formatting/prettier fixes applied.

## Files / Types Changed

- packages/playground-ui/src/ds/components/Slider/slider.tsx
  - New SliderProps type.
- Implementation switched to Base UI primitives; value normalization
logic added; multiple thumbs rendered from values array.

- packages/playground-ui/src/ds/components/Slider/slider.stories.tsx
  - Story updates and additions as listed above.

- packages/playground-ui/package.json
  - Removed Radix slider/tabs deps from dependencies.

- .changeset/thirty-shrimps-brake.md
  - Changeset documenting the migration.

## Consumers & Impact

- No breaking changes expected. Consumers already pass value={[number]}
and read value[0] in:
- packages/playground/src/domains/agents/components/agent-settings.tsx
(Temperature & Top P)
  - packages/playground/src/domains/workflows/workflow/zoom-slider.tsx
- Verify Storybook (port 6006) and interactions: drag, click-on-track,
keyboard, and slider visibility on surface2/3/4 panels.

## Test Plan / Review Notes

- Check Storybook stories: Default, WithRange, ThreeThumbs, Disabled,
Vertical, States, WithLabel/PriceRange.
- Validate drag, click-on-track, and keyboard input update values and
callbacks receive number[].
- Confirm sliders in local studio agent settings and workflow zoom
behave identically visually and functionally.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16788?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Add focused Vitest coverage for the agent builder hooks package
- Cover autosave, access gating, tool creation, visibility changes,
channel toasts, chat draft, save-agent, and model-policy filtering flows
- Add missing playground hook/domain modules needed by the agent builder
hook surface

## Test plan
- pnpm exec vitest run
src/domains/agent-builder/hooks/__tests__/builder-hooks.test.tsx
--coverage.enabled --coverage.provider=v8
--coverage.include='src/domains/agent-builder/hooks/**/*.{ts,tsx}'

## Notes
- Targeted hook coverage reports 100% statements, branches, functions,
and lines.
- packages/playground typecheck was checked earlier and currently fails
on existing unrelated playground TypeScript errors outside this hook
coverage work.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR adds many small pieces of code ("hooks") that make the
agent-builder UI work reliably — they auto-save edits, enforce who can
do what, let users create and attach skills/tools, manage visibility
changes, and filter which AI models are allowed. It also ships a
thorough test suite that verifies all those behaviors.

---

## Overview

Adds a comprehensive hook suite and supporting utilities for the agent
builder frontend, plus focused Vitest tests that exercise autosave,
access gating, tool creation/execution, visibility flows,
channel-toasts, chat drafts, save flows, and model-policy filtering.
Introduces domain helpers for model/provider filtering and small
auth/default helpers. Targeted test coverage for the hooks reports 100%
statements/branches/functions/lines.

---

## Test Coverage

- File:
packages/playground/src/domains/agent-builder/hooks/__tests__/builder-hooks.test.tsx
- Scope: Extensive Vitest + React Testing Library suite covering:
- Access control and create-route selection (useBuilderAgentAccess,
useCanCreateAgent)
- Form-backed tool execution and picker tooling (useAgentBuilderTool,
useAvailableAgentTools)
  - Skill creation tooling and workspace handling (useCreateSkillTool)
- Save and autosave flows (useSaveAgent, useAutosaveAgent,
useAutosaveSkill)
- Visibility-change dialogs and flows for agents/skills
(useVisibilityChangeDialog + wrappers)
- Small interaction hooks (useAutoScroll, useChatDraft,
useChannelConnectToast, useStarterUserMessage)
  - Channel-connect toast behavior and starter-user-message clearing
  - Dialog/filtering behavior for tool/agent selection and allowlists
- Test infra: MSW/React Query/JSdom wiring, react-hook-form test
wrapper, deferred promise helpers
- Coverage: 100% statements, branches, functions, lines (for targeted
hook files)

---

## New/Changed Hooks & Exports (high level)

Access & permissions
- useBuilderAgentAccess() — RBAC-aware builder access flags, builder
feature gating, structured denial reasons
- useCanCreateAgent() — Determines create-route and eligibility
(experimental UI flag or builder access)

Form & tools
- useAgentBuilderTool() — Tool that applies input into the builder
react-hook-form (name, description, tools, skills/agents/workflows,
models, workspaceId, browserEnabled)
- useCreateSkillTool() — Tool to create a stored skill (workspace
validation, visibility defaulting, form updates)
- useConnectChannelTool(), CONNECT_CHANNEL_TOOL_NAME — Inline
connectChannel tool skeleton

Autosave & persistence
- useSaveAgent() — Builds save payloads, calls mutation, shows toasts,
classifies model-policy errors
- useAutosaveAgent() — Debounced autosave for agent forms with status,
stale-request handling, retry/flush
- useAutosaveSkill() — Debounced autosave for skill edits, regenerates
file tree, status and error handling

Visibility & interactions
- useVisibilityChangeDialog() — Generic confirmation dialog hook for
visibility changes
- useVisibilityChange (agent/skill wrappers) — Agent/skill-specific
visibility-change wiring
- useChatDraft() — Draft state, trimmed submit, Enter/Shift handling
- useChannelConnectToast() — Shows success/error toast from URL params
(platform/status)
- useStarterUserMessage() — Captures starter message from router state
and clears it
- useAutoScroll() — Ref that scrolls a div to bottom when dependency
changes

Supporting data hooks & mutations
- useAvailableAgentTools() — Computes filtered AgentTool[] with picker
allowlist filtering and selection state
- useAgentBuilderAllowedModels() — Returns filtered providers and models
given policy and availability
- useUpdateSkill() — React Query mutation wrapper for stored-skill
updates
- useDefaultVisibility() — Returns 'private' as default visibility

Domain utilities
- packages/playground/src/domains/builder.ts:
- useBuilderFilteredProviders(), useBuilderFilteredModels() — Filter
providers/models by BuilderModelPolicy
- isModelNotAllowedError() — Detects/normalizes MODEL_NOT_ALLOWED errors
  - Re-exports: useBuilderModelPolicy, useBuilderPickerVisibility

---

## Notable implementation/test details

- Tests replace simple mocks with hoisted, comprehensive mocks for RBAC,
builder settings, LLM provider state, mutation hooks, and UI components.
- Autosave hooks use debouncing, request sequence refs to ignore stale
async results, retry and flush API, and brief "saved" display state
before returning to idle.
- useAutosaveSkill regenerates SKILL.md file content
(createInitialStructure/updateRootFolderName/updateNodeContent) before
sending updates.
- Visibility change flows use a reusable dialog hook that shows
success/error toasts and updates react-hook-form fields with
shouldDirty: false on success.
- Model policy shape change: allowed/default models now use modelId (was
model) — test expectations updated accordingly.

---

## Breaking changes / notes

- Model policy data shape updated from model → modelId for
allowed/default models (affects code/tests relying on prior shape).
- packages/playground typecheck still reports unrelated existing
TypeScript errors outside the scope of this work.

---

## Test command

pnpm exec vitest run
src/domains/agent-builder/hooks/__tests__/builder-hooks.test.tsx
--coverage.enabled --coverage.provider=v8
--coverage.include='src/domains/agent-builder/hooks/**/*.{ts,tsx}'

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16775?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
…r to Base UI (mastra-ai#16791)

## Summary

- **Add `ContextMenu`** — new design system component built on
`@base-ui/react/context-menu`. Namespaced API mirrors `DropdownMenu`
(`Trigger`, `Content`, `Item`, `CheckboxItem`, `RadioItem`, `Label`,
`Separator`, `Shortcut`, `Group`, `Sub`/`SubTrigger`/`SubContent`,
`RadioGroup`, `Portal`). `Item` supports a `variant="destructive"`.
Visual style matches `DropdownMenu` 1:1 via DS tokens.
- **Migrate `DropdownMenu`** from `@radix-ui/react-dropdown-menu` to
`@base-ui/react/menu`. Public API unchanged; `asChild` on `Trigger` is
preserved via a render-prop shim. `Item` gains the same `variant` prop.
No visual regressions.
- **Migrate `Popover`** from `@radix-ui/react-popover` to
`@base-ui/react/popover`. Public API unchanged; `HoverPopover` kept.
Internal consumer `property-filter-creator` switched from Radix-only
`onOpenAutoFocus`/`onCloseAutoFocus` to Base UI's
`initialFocus`/`finalFocus`.
- **Robustness** — `Label` no longer wraps Base UI's `GroupLabel` (which
requires a `Group` ancestor and threw `MenuGroupRootContext is missing`
when used standalone). It now renders a plain styled `<div>` matching
Radix's previous behavior. Smoke tests force-mount every menu part in an
open menu to catch any future Base UI context errors.
- **Disabled items** show `cursor-not-allowed` and neutralize
hover/focus/highlighted styles (instead of `pointer-events-none`, which
suppressed the disabled cursor visual).

## Test plan

- [x] `pnpm exec tsc --noEmit` on `@mastra/playground-ui` — exit 0
- [x] `pnpm exec tsc --noEmit` on `@mastra/playground` (consumer with
~273 usages) — exit 0
- [x] `pnpm build` on `@mastra/playground-ui` — OK
- [x] `pnpm test` on `@mastra/playground-ui` — 191/191 (4 new smoke
tests added)
- [x] `pnpm lint` — 0 errors (warnings pre-existing)
- [x] `pnpm exec prettier --check` on touched files — OK
- [ ] Manual: open Storybook (`pnpm storybook`) and check
`Elements/ContextMenu`, `Elements/DropdownMenu`, `Elements/Popover` —
visual + interactions (right-click, hover, focus, sub, checkbox, radio,
escape, disabled cursor, destructive)
- [ ] Manual: app smoke test — PropertyFilter, DataFilter,
DateTimePicker, traces list mode toggle (uses DropdownMenu/Popover)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR replaces Radix-based menus/popovers with a shared Base UI
implementation, adds a new right-click ContextMenu, and fixes several
focus/disabled/label edge-cases so menus and popovers behave more
reliably without forcing consumers to change how they use them.

## What Changed

**New Component: ContextMenu**
- Adds ContextMenu built on `@base-ui/react/context-menu` with a
namespaced API matching DropdownMenu (Trigger, Content, Item,
CheckboxItem, RadioItem, Label, Separator, Shortcut, Group,
Sub/SubTrigger/SubContent, RadioGroup, Portal).
- Item supports variant="destructive".
- Visual styling uses DS tokens.
- Storybook stories (6) and smoke tests that mount every part in an open
menu were added.

**Migration: DropdownMenu**
- Replaced `@radix-ui/react-dropdown-menu` with `@base-ui/react/menu`.
- Public API preserved; Trigger keeps asChild via a render-prop shim.
- Item gained inset/variant props (including destructive) and an
onSelect alias for compatibility with prior Radix onSelect semantics.
- Component props: added defaultOpen and retyped onOpenChange to
MenuPrimitive.Root's type; Content/SubContent accept Base UI positioning
props (align, alignOffset, side, sideOffset, container).
- Smoke tests mount all parts open to surface compatibility/context
issues.

**Migration: Popover**
- Replaced `@radix-ui/react-popover` with `@base-ui/react/popover`.
- Public API kept; PopoverTrigger supports asChild via render-prop shim;
HoverPopover preserved with updated prop typing.
- PopoverContent now uses Base UI Positioner/Popup and
data-[open]/data-[closed] state classes.
- Focus handling moved from Radix onOpenAutoFocus/onCloseAutoFocus to
Base UI initialFocus/finalFocus; PropertyFilter updated to set
initialFocus={false} and use finalFocus to avoid unwanted focus return.

**Robustness & Fixes**
- Label renders a styled div instead of wrapping Base UI’s GroupLabel to
avoid missing Group context errors.
- Disabled items now use cursor-not-allowed and neutralize interaction
styles (instead of pointer-events-none).
- Menu.Item and ContextMenu.Item invoke onClick and onSelect (alias) so
existing call sites using onSelect continue to work; regression tests
added asserting both fire once.
- Added comprehensive smoke tests for DropdownMenu and ContextMenu that
mount every subcomponent with defaultOpen.

**Changesets**
- Patch/minor changesets documenting: ContextMenu addition (minor),
DropdownMenu.Item variant (patch), and Popover focus-prop guidance
(patch).

## Tests & CI
- playground-ui types/build passed.
- playground-ui tests: 191/191 (includes 4 new smoke tests).
- Lint/Prettier checks passing.
- Storybook stories added; manual Storybook/app visual checks listed as
TODO.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16791?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…6803)

## Summary
- remove DuckDB sequence-backed defaults from observability cursor
columns and migrations
- write sequence cursor IDs explicitly in span, log, metric, score, and
feedback insert paths
- add regression coverage that ensures cursor schema avoids defaults and
a file-backed DuckDB database reopens after cursor migrations and cursor
writes
- add a patch changeset for @mastra/duckdb

## Why
The delta polling migration introduced ALTER COLUMN cursorId SET DEFAULT
nextval(...) for DuckDB observability tables. We reproduced that DuckDB
can fail WAL replay after dev server hot reload with this catalog
default. This keeps the delta polling behavior for new rows without
relying on fragile column defaults.

## Test plan
- pnpm --filter @mastra/duckdb test --
src/storage/domains/observability/index.test.ts --bail 1 --reporter=dot
- pnpm --filter @mastra/duckdb typecheck
- pnpm --filter @mastra/duckdb lint

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

DuckDB could crash after dev-server restarts because it relied on
automatic cursor ID defaults in the DB. This PR removes those fragile
defaults and makes the app explicitly generate and write cursor IDs on
insert so databases reopen and WAL replay no longer fail.

---

## Overview

Removes DuckDB sequence-backed DEFAULTs from observability cursorId
columns, updates all observability write paths to explicitly generate
and write cursor IDs using the corresponding sequences, adds a migration
helper to drop legacy cursor defaults, and adds regression tests
(including a file-backed reopen test). Also adds a patch changeset for
`@mastra/duckdb`.

## Changes

- DDL & migrations
(stores/duckdb/src/storage/domains/observability/ddl.ts)
- cursorId columns defined as nullable BIGINT with no DEFAULT
nextval(...).
- ALL_MIGRATIONS no longer sets column defaults; additive migrations add
cursorId as BIGINT only.
- Comments note that upgraded rows may have NULL cursorId and delta
polling relies on explicit nextval() on writes.

- Explicit cursor generation on insert paths
- tracing.ts (span events): include cursorId in COLUMNS and inject
nextval('span_events_cursor_id_seq') for inserted rows.
- logs.ts (log events): add cursorId to COLUMNS and batch inserts use
nextval('log_events_cursor_id_seq').
- metrics.ts (metric events): include cursorId and use
nextval('metric_events_cursor_id_seq') in batch inserts.
- scores.ts (score events): populate cursorId via
nextval('score_events_cursor_id_seq') for single and batch inserts.
- feedback.ts (feedback events): populate cursorId via
nextval('feedback_events_cursor_id_seq') for single and batch inserts.

- Migration helper & init changes
- Added dropLegacyCursorIdDefaults(db) (migration.ts) that queries
information_schema.columns and DROP DEFAULT for cursorId only when
present.
- ObservabilityStorageDuckDB.init() calls dropLegacyCursorIdDefaults(db)
after applying DDL + migrations.

- Tests & regression coverage (index.test.ts)
- Test that combined DDL + migrations SQL contains no cursorId DEFAULT
clauses.
- Simulate legacy DB defaults via ALTER TABLE ... SET DEFAULT
nextval(...) and assert init() removes them for observability tables.
- Assert init() skips drop when no defaults exist by verifying expected
executeBatch usage.
- Integration: file-backed DuckDB DDL/migrations → insert rows using
explicit nextval(...) cursor writes → close & reopen DB, asserting rows
persist and reopen succeeds.

- Changeset
- .changeset/fluffy-melons-divide.md: patch bump for `@mastra/duckdb`
documenting the fix.

## Reasoning & Impact

- Reason: DuckDB catalog defaults (ALTER COLUMN ... SET DEFAULT
nextval(...)) can cause WAL replay failures during dev hot reloads.
Relying on them is fragile.
- Impact: Prevents dev-server WAL replay crashes, preserves
delta-polling behavior by ensuring new rows receive monotonically
increasing cursor IDs via explicit nextval() calls, and provides a
migration path to remove legacy defaults.

## Tests / Validation

- New tests in
stores/duckdb/src/storage/domains/observability/index.test.ts cover
schema defaults removal, init behavior, and file-backed reopen after
explicit cursor writes.
- Recommended local checks included in PR: pnpm --filter
`@mastra/duckdb` test ...; pnpm --filter `@mastra/duckdb` typecheck;
pnpm --filter `@mastra/duckdb` lint.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16803?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
Co-authored-by: Eric Pinzur <2641606+epinzur@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Eric Pinzur <epinzur@gmail.com>
…pages (mastra-ai#16813)

## Summary

- Removed the `Primitives` and `Observability` overview pages
(`/primitives`, `/observability-overview`). Each only rendered a static
grid of links that already exist in the sidebar. After the studio shell
refresh dropped their nav entries, both pages became unreachable from
the UI, so this removes the pages, their routes in `App.tsx`, and the
stale `sectionNav` entries.
- Kept the `Evaluation` overview (`/evaluation`) — it is a real
dashboard, still a sidebar item, and remains in `sectionNav` for
breadcrumb resolution.
- Dropped the redundant outer padding/sizing on the
`RequestContextWrapper` (`p-5` + `h-full w-full overflow-y-auto`), which
doubled the `PageLayout` root's `p-6` and created a nested scroll
container.

## Test plan

- [ ] `/request-context` renders with a single page inset and one
scrollbar
- [ ] Sidebar `Primitives` and `Observability` sections still work;
their child links navigate correctly
- [ ] Visiting `/primitives` or `/observability-overview` directly no
longer resolves
- [ ] `pnpm --filter @internal/playground lint` passes

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

This PR cleans up the playground by removing two pages ("Primitives" and
"Observability" overviews) that were just showing lists of links—the
same links that are already easily accessible in the left sidebar. It
also fixes some extra padding that was making the page layout look
weird.

## Changes

### Removed Pages
- Deleted the `Primitives` overview page (`/primitives`) which rendered
a static grid of links to Agents, Prompts, Workflows, Processors, MCP
Servers, Tools, Workspaces, and Request Context
- Deleted the `Observability` overview page (`/observability-overview`)
which displayed Metrics and Traces sections as a link grid

### Route and Navigation Updates
- Removed imports and route definitions for both pages in `App.tsx`
- Removed redundant `sectionNav` entries for `Observability` and
`Primitives` overview routes
- Kept the `Evaluation` overview (`/evaluation`) in `sectionNav` since
it functions as a real dashboard with actual content

### Layout Improvements
- Removed redundant styling (`h-full w-full overflow-y-auto p-5`
classes) from `RequestContextWrapper` in the Request Context component,
as this duplicated padding already applied by the parent `PageLayout`
component and created an unnecessary nested scroll container

### Documentation
- Updated comment in `app-sidebar.tsx` to clarify exact/sub-path
matching behavior
- Added changeset documentation for the removal and styling fix

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16813?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
…-ai#16798)

## Summary

The ClickHouse v-next observability discovery endpoints (tags, services,
environments, entities, metric names, metric labels) were returning each
distinct value multiple times. The refreshable materialized views that
populate the `mastra_discovery_values` and `mastra_discovery_pairs`
helper tables use `REFRESH EVERY ... TO <pre-created table>`, which in
ClickHouse appends a fresh copy of its result set on every refresh.
Because the helper tables were plain `MergeTree`, each refresh cycle
layered on another full copy of every distinct value — so by the time a
user hit `GET /observability/discovery/tags` they saw every tag once per
refresh cycle that had run.

This PR switches the helper tables to `ReplacingMergeTree` with `ORDER
BY` covering every column so background merges collapse the
byte-identical duplicate rows, reconciles existing deployments on
startup by dropping and recreating any helper tables still on
`MergeTree`, and keeps `SELECT DISTINCT` on the read side so results
stay correct between refreshes and merges.

Closes mastra-ai#16747

## What changed

- `ddl.ts` — `DISCOVERY_VALUES_DDL` and `DISCOVERY_PAIRS_DDL` now use
`ReplacingMergeTree`.
- `index.ts` — New `reconcileDiscoveryTables()` helper runs early in
`init()`. It introspects `system.tables`; when a helper table is found
on the old engine it drops the refreshable MV first (so it cannot write
into the table mid-drop) then drops the table. The existing `CREATE
TABLE IF NOT EXISTS` and discovery MV bootstrap then recreate both with
the current definitions. No-op when the table is already on the expected
engine or doesn't exist yet.
- `discovery.ts` / `metrics.ts` — All discovery read queries use `SELECT
DISTINCT`. `ReplacingMergeTree` only dedupes during background merges,
so a row can briefly appear more than once between refresh cycles;
`DISTINCT` over the `ORDER BY` columns is effectively free at this
cardinality and keeps results stable regardless of merge timing.

## Testing

- Added an automated integration test `init() reconciles pre-existing
MergeTree discovery tables to ReplacingMergeTree` that creates an
isolated database, manually provisions the helper tables as `MergeTree`
to simulate a pre-fix deployment, runs `init()`, and asserts the tables
are now `ReplacingMergeTree` and that both refreshable MVs were
recreated.
- The 9 existing `discovery` integration tests were tightened to assert
uniqueness (`expect(result).toEqual([...new Set(result)])`) rather than
just presence. A second discovery refresh was added to the test setup so
duplicates would accumulate without the fix — verifying the test would
actually catch the regression.
- Verified the test suite fails as expected when `SELECT DISTINCT` is
reverted, then passes again once restored.
- Manually validated the migration path end-to-end against a ClickHouse
26.2 instance: provisioned the helper tables as `MergeTree`, ran
`init()`, confirmed `system.tables` reported `ReplacingMergeTree` for
both, and confirmed discovery reads still returned unique values after
the refresh.
- Full v-next test suite (170 tests) passing locally; lint and typecheck
clean.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

The observability filters were showing the same values multiple times.
This PR makes the DB helper tables automatically collapse duplicate
rows, adds a startup step to upgrade old helper tables if present, and
ensures queries only return unique values so the UI shows each value
once.

---

## Problem

Discovery endpoints (tags, services, environments, entities, metric
names, metric labels) could return duplicate values because refreshable
materialized views wrote into helper tables backed by MergeTree and
duplicates could appear between MV refresh and background consolidation
(issue `mastra-ai#16747`).

## Solution

1. Schema (DDL)
- Changed discovery helper tables (`DISCOVERY_VALUES_DDL`,
`DISCOVERY_PAIRS_DDL`) from MergeTree to ReplacingMergeTree and added
ORDER BY covering all logical columns so background merges collapse
byte-identical duplicates.

2. Runtime reconciliation
- Added reconcileDiscoveryTables(client), invoked early in init(), which
inspects system.tables and:
- Uses the existing isReplacingMergeTreeEngine() helper to accept
Shared/Replicated/*ReplacingMergeTree variants as already migrated,
- If a helper table uses a legacy engine, drops dependent refreshable
MV(s) first, drops the table, and lets subsequent CREATE TABLE IF NOT
EXISTS + MV bootstrap recreate them with ReplacingMergeTree.
- Silently no-ops if tables are absent or already correct.

3. Read-side stability
- Discovery and metrics read queries updated to use SELECT DISTINCT for
returned value fields (getEntityTypes, getEntityNames, getServiceNames,
getEnvironments, getTags, metric names/label keys/values) so results
remain unique between MV refresh and ReplacingMergeTree background
merges.

## Notable implementation & test details

- reconcileDiscoveryTables reuses isReplacingMergeTreeEngine() to
correctly recognize Shared/Replicated ReplacingMergeTree variants and
avoid unnecessary drops.
- Tests were strengthened to model the real intermediate window: after
MV refresh the suite injects duplicate rows (INSERT INTO t SELECT * FROM
t) to ensure duplicates exist on disk before ReplacingMergeTree merges,
and assertions require unique outputs (via new Set). Reverting the
DISTINCT in reads causes tests to fail as expected.
- The migration/reconcile test pre-creates helper tables with MergeTree
and refreshable MVs pointing at them so the MV-drop branch is exercised
end-to-end.
- Verified locally against ClickHouse 26.2.x; full v-next test suite
passes; lint and typecheck are clean.

## Files changed (high level)

- .changeset/shaky-needles-dance.md — changelog entry
- stores/clickhouse/src/storage/domains/observability/v-next/ddl.ts —
helper tables changed to ReplacingMergeTree with full ORDER BY
- stores/clickhouse/src/storage/domains/observability/v-next/index.ts —
added reconcileDiscoveryTables() and wired into init(), imported
isReplacingMergeTreeEngine
-
stores/clickhouse/src/storage/domains/observability/v-next/discovery.ts
& metrics.ts — queries updated to SELECT DISTINCT
-
stores/clickhouse/src/storage/domains/observability/v-next/index.test.ts
& migration.test.ts — integration & migration tests strengthened to seed
duplicates and validate reconciliation

## Tests & verification

- New integration test verifies pre-existing MergeTree helper tables +
refreshable MVs are reconciled to ReplacingMergeTree and MVs recreated.
- Discovery tests seed duplicate rows and assert uniqueness; they fail
if DISTINCT is removed.
- Verified locally against ClickHouse 26.2.x; v-next test suite, lint,
and typecheck pass.

Closes issue `mastra-ai#16747`.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16798?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
## Summary

- Migrates the `Dialog` design system component from
`@radix-ui/react-dialog` to `@base-ui/react/dialog`.
- **Public API unchanged.** `asChild` on `DialogTrigger` and
`DialogClose` is preserved through a render-prop shim (same pattern as
the `Popover`/`DropdownMenu` migrations). All exports kept: `Dialog`,
`DialogTrigger`, `DialogClose`, `DialogPortal`, `DialogOverlay`,
`DialogContent`, `DialogHeader`, `DialogFooter`, `DialogBody`,
`DialogTitle`, `DialogDescription`.
- Part mapping: Radix `Overlay` → Base UI `Backdrop`, `Content` →
`Portal` + `Popup`.
- `dialog.css` now matches both `data-open`/`data-closed` (Base UI) and
`data-state` (Radix) so the shared stylesheet keeps animating
`AlertDialog`, which is still on Radix.
- `CommandDialog` props narrow `children` to `ReactNode` — Base UI's
`Dialog.Root` widens `children` with a payload render-function overload
that `cmdk` can't accept.
- Adds `dialog.test.tsx` smoke tests: mounts every part in an open
dialog, verifies the `asChild` shim, trigger open, and `onOpenChange` on
both the built-in close button and an `asChild` `DialogClose`.

## Test plan

- [x] `pnpm exec tsc --noEmit` on `@mastra/playground-ui` — exit 0
- [x] `pnpm exec tsc --noEmit` on `@mastra/playground` (consumer, ~30
dialog files) — exit 0
- [x] `pnpm build` on `@mastra/playground-ui` — OK
- [x] `pnpm test` on `@mastra/playground-ui` — 200/200 (5 new)
- [x] `eslint` on `Dialog` + `Command` — clean
- [ ] Manual visual check of open/close animations in Studio (not run —
dev server lives on the main checkout)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR swaps the Dialog component's underlying library from Radix UI to
Base UI while keeping the same public API and behavior — like replacing
an engine but leaving the steering and controls unchanged.

---

## Overview

Migrates the Dialog design-system component from
`@radix-ui/react-dialog` to `@base-ui/react/dialog` while preserving
public exports, asChild behavior, and the visual open/close animations
so consumers are unaffected.

## Changes

### Core Migration
- Replaced Radix Dialog primitives with Base UI primitives (imported
from `@base-ui/react/dialog`).
- Kept public exports: Dialog, DialogTrigger, DialogClose, DialogPortal,
DialogOverlay, DialogContent, DialogHeader, DialogFooter, DialogBody,
DialogTitle, DialogDescription.
- Mapped Radix concepts to Base UI equivalents: Radix Overlay → Base UI
Backdrop; Radix Content → Base UI Portal + Popup.

### asChild Shim & Component Wrappers
- DialogTrigger and DialogClose implemented as explicit React.forwardRef
wrappers that preserve asChild by passing Base UI's render-prop when
children is a valid element (same shim pattern used for
Popover/DropdownMenu).
- DialogOverlay and DialogContent are forwardRef wrappers around Base UI
Backdrop/Popup with local prop typings and className handling.
- DialogContent includes the built-in close button using the primitive
Close's render prop (Button with X icon).

### Styling
- dialog.css updated so overlay/content animations respond to both Radix
data-state (open/closed) and Base UI data-open/data-closed attributes;
existing keyframes and animation timings retained so AlertDialog (still
on Radix) and the new Dialog share animations.

### Props & Typing
- CommandDialog props narrowed: children is now React.ReactNode (Omit of
Dialog props with children reintroduced) to avoid Base UI Dialog.Root's
render-function overload conflicting with cmdk.

### Tests
- Added dialog.test.tsx (Vitest + React Testing Library) covering:
  - Mounting every dialog part inside an open dialog.
  - DialogTrigger asChild renders child without nested button.
  - Clicking trigger opens dialog.
- onOpenChange called with false when closing via built-in close button
and via an asChild DialogClose.
- 5 new tests added; test suite passes locally.

## Validation
- Type-check: pnpm exec tsc --noEmit for `@mastra/playground-ui` and
`@mastra/playground` — passed.
- Build: `@mastra/playground-ui` builds successfully.
- Tests: all tests passing (includes the 5 new tests).
- ESLint: Dialog and Command files lint-clean.
- Manual visual check of open/close animations: not run.

## Notes for reviewers
- Focus review on dialog.tsx wrapper implementations (asChild
render-prop shims), dialog.css selector additions, and the CommandDialog
props change that tightens children to ReactNode.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16821?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and publish to npm
yourself or [setup this action to publish
automatically](https://github.com/changesets/action#with-publishing). If
you're not ready to do a release yet, that's fine, whenever you add more
changesets to main, this PR will be updated.

⚠️⚠️⚠️⚠️⚠️⚠️

`main` is currently in **pre mode** so this branch has prereleases
rather than normal releases. If you want to exit prereleases, run
`changeset pre exit` on `main`.

⚠️⚠️⚠️⚠️⚠️⚠️

# Releases
## @mastra/playground-ui@29.0.0-alpha.9

### Patch Changes

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

## @mastra/clickhouse@1.9.0-alpha.1

### Patch Changes

- Fixed duplicate entries in the ClickHouse v-next observability
discovery endpoints — tags, services, environments, entities, metric
names, and metric labels now return each value once. Existing
deployments are reconciled automatically on next startup; no manual
migration required.
([mastra-ai#16798](mastra-ai#16798))

## mastra@1.9.4-alpha.9



## create-mastra@1.9.4-alpha.9



## @internal/playground@1.9.4-alpha.9

### Patch Changes

- Removed the unused Primitives and Observability overview pages from
the studio. These pages only showed links that already exist in the
sidebar, so they were redundant and no longer reachable from the
navigation. Also fixed extra padding around the Request Context page
content. ([mastra-ai#16813](mastra-ai#16813))

- Updated dependencies
\[[`b699688`](mastra-ai@b699688)]:
    -   @mastra/playground-ui@29.0.0-alpha.9

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
mastra-ai#16808)

## Summary

The in-memory observability storage in `@mastra/core` had its own
bespoke test file that duplicated coverage every vNext storage adapter
needs. This PR extracts those tests into a shared
`createObservabilityVNextTests` factory in
`@internal/storage-test-utils`, so the in-memory store and DuckDB now
both validate against the same 46 portable contract tests. Running that
shared suite against in-memory surfaced four real behavior bugs in
`ObservabilityInMemory`, which are also fixed here.

## What changed

**Shared vNext observability suite (`@internal/storage-test-utils`)**
- New `createObservabilityVNextTests({ getStorage, capabilities })`
factory at `stores/_test-utils/src/domains/observability-vnext/`.
- The factory is invoked against `MockStore` from
`_test-utils/src/index.test.ts`, giving InMemoryStore continuous
contract coverage with no `@mastra/core` devDep coupling.
- `stores/duckdb` calls the same factory from its own observability test
file alongside its bespoke event-sourced / DDL / lazy-store tests.

**`@mastra/core` observability InMemory**
- `getSpans` now batch-fetches spans by id within a trace (previously
threw "not supported"), enabling the optimized `getBranch` path.
- `batchCreateLogs`, `batchCreateMetrics`,
`createScore`/`batchCreateScores`,
`createFeedback`/`batchCreateFeedback` now upsert by id instead of
appending duplicates on retry, preserving cursor ids so delta polling
does not re-emit the record.
- Discovery (`getEntityTypes`, `getEntityNames`, `getServiceNames`,
`getEnvironments`, `getTags`) now scans logs and metrics in addition to
spans.
- `getMetricTimeSeries` keys grouped series on the label tuple instead
of a `|`-joined string, so series whose display names collide (e.g.
`{segmentA: 'a', segmentB: 'b|c'}` vs `{segmentA: 'a|b', segmentB:
'c'}`) remain distinct.

**Tests**
- Deleted
`packages/core/src/storage/domains/observability/inmemory.test.ts`; the
34 contract tests live in the factory now.
- The two `extractBranchSpans` helper unit tests moved into
`packages/core/src/storage/domains/observability/tracing.test.ts` next
to the helper they cover.

## Test plan

- `pnpm --filter @mastra/core vitest run
src/storage/domains/observability` — 155 tests pass (7 files).
- `pnpm --filter @internal/storage-test-utils vitest run` — 547 tests
pass (factory 46 + the rest of the domain factories against MockStore).
- `pnpm --filter @mastra/duckdb-store vitest run
src/storage/domains/observability` — 125 tests pass (119 in
`index.test.ts`: 60 bespoke event-sourced/infra tests + 59 factory
tests).

🤖 Generated with [Mastra Code](https://mastra.ai/code)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR creates one shared checklist of observability tests that every
storage implementation must pass, then fixes bugs in the in-memory
storage so it passes the same suite as the DuckDB store.

## Overview

Consolidates vNext observability contract tests into a portable factory
(createObservabilityVNextTests) in `@internal/storage-test-utils` and
runs that factory against MockStore and DuckDB to provide a 46-test
(portable) contract suite. The change removes duplicate per-package
observability tests, lowers test-maintenance burden, and uncovered/fixed
behavioral bugs in the in-memory adapter.

## Changes

### New Test Factory & Utilities
- stores/_test-utils/src/domains/observability-vnext/index.ts
- Adds createObservabilityVNextTests with ObservabilityVNextCapabilities
and CreateObservabilityVNextTestsOptions to produce a comprehensive 46+
test suite covering delta polling, cursor pagination, batch operations,
retry idempotency (upsert-by-id), discovery endpoints,
aggregation/breakdown/time-series shapes, and collision-resistant metric
grouping.
- stores/_test-utils/src/domains/observability-vnext/data.ts
- Adds VNEXT_BASE_DATE and makeSpan for deterministic span fixtures;
re-exports EntityType and SpanType.
- stores/_test-utils/src/index.ts and index.test.ts
- Re-exports and wires the shared factory; runs the suite against
MockStore with fresh store instances per test.

### In-Memory Storage Fixes
- packages/core/src/storage/domains/observability/inmemory.ts
- Added getSpans to support batch span retrieval by id within a trace
(enables optimized getBranch path).
- Introduced upsertByIdField helper and switched batchCreateLogs,
batchCreateMetrics, createScore/batchCreateScores,
createFeedback/batchCreateFeedback to upsert-by-id to avoid duplicates
on retry and preserve cursor ids.
- Expanded discovery APIs (getEntityTypes, getEntityNames,
getServiceNames, getEnvironments, getTags) to scan logs and metrics in
addition to spans.
- Fixed getMetricTimeSeries to group series by the original label tuple
(not a `|`-joined string) to avoid key collisions.

### Tests & Consolidation
- Removed
packages/core/src/storage/domains/observability/inmemory.test.ts (now
covered by the shared factory).
- Moved two extractBranchSpans unit tests into
packages/core/src/storage/domains/observability/tracing.test.ts to keep
focused unit coverage.

### DuckDB Adapter Integration
- stores/duckdb/src/storage/domains/observability/index.test.ts
- Registers the shared factory for DuckDB observability (capabilities:
label 'DuckDB', preferredStrategy 'event-sourced'), reusing existing
setup/teardown.

### Commit/Typing Adjustments
- Various commits adjust TypeScript types and test fixtures to ensure
downstream consumers compile (nominal enums, labels: {}, narrower log
level types, optional/null toleration) and minor housekeeping (drops
changeset, test fixture tweaks).

### Documentation
- .changeset/yummy-lions-act.md documents the in-memory observability
behavioral fixes (batch span fetch, upsert semantics, discovery scope
expansion, metric series keying).

## Test Results
- `@mastra/core` observability: 155 tests pass
- `@internal/storage-test-utils`: 547 tests pass (including 46 factory
tests)
- `@mastra/duckdb-store` observability: 125 tests pass (59 factory tests
+ bespoke tests)

## Impact

Establishes a portable, shared vNext observability test contract,
reduces duplicated test maintenance and cross-package devDep coupling,
and fixes in-memory storage behaviors (batch span retrieval, idempotent
upserts, expanded discovery, and robust metric grouping) discovered by
running the shared suite.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16808?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
## Summary

The Filter menu on the Agent **Review** page (and Dataset Review)
crashed with `\`MenuPortal\` must be used within \`Menu\``.

**Root cause:** the recent migration of `DropdownMenu` to Base UI
(mastra-ai#16791). Three files still imported `Portal`/`SubContent` directly from
`@radix-ui/react-dropdown-menu` and rendered them inside the now Base-UI
`DropdownMenu`. The Radix `Portal` looks up the Radix `Menu` context,
which no longer exists → crash.

**Fix:** replace the local `PortalSubContent` helper with the design
system's `DropdownMenu.SubContent` (already Base UI `Portal` +
`Positioner` + `Popup`).

Files:
- `packages/playground-ui/.../DataFilter/select-data-filter.tsx`
- `packages/playground/.../agent-playground/agent-playground-review.tsx`
- `packages/playground/.../review/components/dataset-review.tsx`

Also memoized a pre-existing non-memoized `items` value in
`dataset-review.tsx` to satisfy the lint gate (behavior unchanged).

**Scope check:** grepped the whole repo — no remaining direct imports of
`@radix-ui/react-dropdown-menu` / `react-popover` /
`react-context-menu`, no use of the removed Popover
`onOpenAutoFocus`/`onCloseAutoFocus` props, and every
`DropdownMenu.Sub*` usage sits inside a valid `DropdownMenu` root. These
three files were the only source of this error class.

## Test plan

- [ ] Agents -> an agent -> **Review** tab -> **Filter** -> hover
**Status** / **Tags** submenus — opens without crashing
- [ ] Datasets -> a dataset -> **Review** -> **Filter** -> hover
**Status** / **Tags** submenus
- [ ] Verify menu items (checkboxes, radio, clear-all) still work

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

Filter menus were breaking on the Agent and Dataset review pages with an
error about "MenuPortal must be used within Menu." This happened because
the code was trying to use old dropdown menu components (from Radix UI)
with a new dropdown menu system (Base UI), which don't work together.
The fix was simple: replace those old components with the correct new
versions from the design system.

---

## Problem

Filter menus on the Agent Review and Dataset Review pages were crashing
with the error "MenuPortal must be used within Menu" after
`DropdownMenu` was migrated from Radix UI to Base UI.

## Root Cause

Three files were still importing `Portal`/`SubContent` directly from
`@radix-ui/react-dropdown-menu` and rendering them inside the Base UI
`DropdownMenu`. The Radix `Portal` component depended on Radix `Menu`
context, which no longer exists after the migration.

## Solution

Replaced the custom `PortalSubContent` helper component with the design
system's `DropdownMenu.SubContent` component (which properly composes
Base UI `Portal` + `Positioner` + `Popup`).

## Changes Made

**Modified files:**
-
`packages/playground-ui/src/ds/components/DataFilter/select-data-filter.tsx`
— Removed custom `PortalSubContent` wrapper; submenu content now uses
`DropdownMenu.SubContent`
-
`packages/playground/src/domains/agents/components/agent-playground/agent-playground-review.tsx`
— Replaced Radix-based `PortalSubContent` with `DropdownMenu.SubContent`
in Status and Tags nested dropdowns
- `packages/playground/src/domains/review/components/dataset-review.tsx`
— Removed custom Radix Portal-based helper; switched submenu content to
use `DropdownMenu.SubContent`; memoized `items` value to satisfy linting
requirements

**Added:**
- `packages/playground-ui/.changeset/cute-buckets-prove.md` — Changeset
documenting the patch fix

## Validation

Repository-wide grep confirms no remaining direct imports of deprecated
Radix UI dropdown menu components; all `DropdownMenu.Sub*` usage is
within valid `DropdownMenu` roots. The three modified files were the
only sources of this error class.

## Testing

- Agent Review page: open an agent → Review → Filter → hover Status/Tags
submenus (should open without crashing)
- Dataset Review page: open a dataset → Review → Filter → hover
Status/Tags submenus (should open without crashing)
- Menu items (checkboxes, radio, clear-all) continue to function
correctly

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16829?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- Bumps `@base-ui/react` from `^1.3.0` to `^1.5.0` in
`@mastra/playground-ui`.
- Pure dependency upgrade — no API or component changes.

## Note

Draft until the `pnpm-lock.yaml` update lands. `@base-ui/react@1.5.0`
was published <24h ago and is currently blocked by the repo's
`minimumReleaseAge` gate (1 day). The lockfile commit will be pushed
once the gate passes (~1h after opening), at which point this can be
marked ready.

## Test plan

- [ ] `pnpm install --filter @mastra/playground-ui` updates lockfile
cleanly
- [ ] `pnpm build` on `@mastra/playground-ui` — passes

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR upgrades a UI helper library (Base UI) used by the playground
from version 1.3 to 1.5 so popup components open/close faster; it's a
safe dependency bump with no API changes.

## Overview

- Bumps `@base-ui/react` from ^1.3.0 to ^1.5.0 in
packages/playground-ui.
- Adds a changeset to publish a patch release for
`@mastra/playground-ui` documenting the upgrade and popup performance
improvements.
- No exported/public API changes.

## Changes

- packages/playground-ui/package.json: dependency updated to
"`@base-ui/react`": "^1.5.0"
- .changeset/metal-boats-walk.md: notes patch release and improved popup
open/close performance

## Status Notes

- PR is currently draft. `@base-ui/react`@1.5.0 was published <24h ago
and the repo enforces a minimumReleaseAge gate (1 day). The pnpm
lockfile update has not been committed yet; it will be added once the
gate passes (≈1 hour after opening), at which point the PR can be marked
ready.

## Testing

- Expected checklist:
- pnpm install --filter `@mastra/playground-ui` updates lockfile cleanly
(lockfile commit pending gate)
- pnpm build on `@mastra/playground-ui` — should pass (no source
changes)

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16819?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Upgrades pi-tui so MastraCode picks up its throttled render scheduler.
This keeps the tui from locking up while large tool previews stream in,
especially big edit/write previews.

Also updates our keybinding calls for the newer pi-tui API:

```ts
getEditorKeybindings()
```

becomes:

```ts
getKeybindings()
```

and select actions now use the namespaced IDs like `tui.select.up`.

Tested with a large streamed edit, plus:

```bash
pnpm --filter mastracode check
pnpm --filter mastracode lint
```


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## ELI5

This PR makes MastraCode's terminal display faster and more responsive
when showing large previews by upgrading a library component. It also
updates how keyboard shortcuts work in the interface to match the latest
version of that library.

## Overview

This PR upgrades the `@mariozechner/pi-tui` dependency from `^0.60.0` to
`^0.73.1` to improve terminal UI rendering responsiveness. The upgrade
includes a throttled render scheduler that prevents the TUI from
freezing when large tool previews stream in. Additionally, the PR
updates all TUI components to use the new keybinding API introduced in
the newer version.

## Key Changes

### Dependency Update
- Upgraded `@mariozechner/pi-tui` from `^0.60.0` to `^0.73.1` in
`mastracode/package.json`

### Keybinding API Migration
All TUI components have been updated to use the new keybinding API:
- Replaced `getEditorKeybindings()` calls with `getKeybindings()`
- Updated keybinding action identifiers to use the new namespaced format
(e.g., `tui.select.up`, `tui.select.down`, `tui.select.confirm`,
`tui.select.cancel`)

### Updated Components
The following components were updated to use the new keybinding API:
- `api-key-dialog.ts`
- `ask-question-dialog.ts`
- `ask-question-inline.ts`
- `goal-cycles-dialog.ts`
- `login-dialog.ts`
- `login-mode-selector.ts`
- `login-selector.ts`
- `mcp-selector.ts`
- `model-selector.ts`
- `plan-approval-inline.ts`
- `thread-selector.ts`
- `tool-approval-dialog.ts`

### Test Updates
- Updated vitest mocks in `ask-question-inline-multiline.test.ts` and
`thread-selector.test.ts` to use the new `getKeybindings()` API and
namespaced action identifiers

### Changeset
Added a changeset entry documenting the improvement to MastraCode
rendering responsiveness during large streamed tool previews.

## Testing
Changes were tested with large streamed edits and validated by running:
- `pnpm --filter mastracode check`
- `pnpm --filter mastracode lint`

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16835?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
## Summary

- Migrates the `AlertDialog` design system component from
`@radix-ui/react-alert-dialog` to `@base-ui/react/alert-dialog`.
- **Public API unchanged.** `asChild` on `AlertDialog.Trigger` is
preserved through a render-prop shim. `AlertDialog.Action` and
`AlertDialog.Cancel` map to Base UI's `Close` part, keeping their
styling and close-on-click behavior.
- Part mapping: Radix `Overlay` → Base UI `Backdrop`, `Content` →
`Portal` + `Popup`, `Action`/`Cancel` → `Close`.
- Adds `alert-dialog.test.tsx` smoke tests: mounts every part open,
verifies the `asChild` shim, trigger open, and `onOpenChange` via
`Action` (plus its `onClick`) and `Cancel`.

## Stacked PR

> Based on `feat/dialog-base-ui` (mastra-ai#16821), not `main`. `AlertDialog`
shares `dialog.css` with `Dialog`; the Dialog PR adds the
`data-open`/`data-closed` selectors that Base UI relies on. Review/merge
mastra-ai#16821 first — GitHub will retarget this PR to `main` automatically once
it lands.

## Test plan

- [x] `pnpm exec tsc --noEmit` on `@mastra/playground-ui` — exit 0
- [x] `pnpm exec tsc --noEmit` on `@mastra/playground` — exit 0
- [x] `pnpm build` on `@mastra/playground-ui` — OK
- [x] `pnpm test` on `@mastra/playground-ui` — 205/205 (5 new)
- [x] `eslint` + `prettier` on `AlertDialog` — clean
- [ ] Manual visual check of open/close animations in Studio (not run —
dev server lives on the main checkout)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR replaces the underlying dialog implementation for AlertDialog
from Radix UI to Base UI so internals use a different library, but from
a developer/user perspective the dialog works the same as before.

---

## Summary

**Migration of AlertDialog component from Radix UI to Base UI**

This PR migrates the `AlertDialog` component in `@mastra/playground-ui`
from `@radix-ui/react-alert-dialog` to `@base-ui/react/alert-dialog`.
The external API remains backward compatible.

### Key Changes

- Replaces Radix primitives with Base UI equivalents:
  - Radix Overlay → Base UI Backdrop
  - Radix Content → Base UI Portal + Popup
  - Radix Action/Cancel → Base UI Close (preserving close-on-click)
- AlertDialogTrigger: now a React.forwardRef wrapper that implements an
asChild render-prop shim to preserve previous asChild behavior (avoids
extra DOM nesting).
- AlertDialogOverlay / AlertDialogContent: refactored to use Base UI
primitives and updated prop typings; `AlertDialogContent.displayName`
set explicitly to 'AlertDialogContent'.
- AlertDialogTitle / AlertDialogDescription: prop typings refactored to
use Omit<...Props, 'className'> for consistency.
- AlertDialogAction / AlertDialogCancel: now wrap Base UI Close
primitive and preserve Action onClick behavior while ensuring
onOpenChange is called when closing.

### Tests

- Adds alert-dialog.test.tsx (Vitest + React Testing Library) that:
  - Mounts all parts open without errors
  - Verifies asChild shim avoids nested button
  - Opens the dialog via the trigger
- Confirms onOpenChange(true/false) behavior and that Action’s onClick
is invoked when Action closes the dialog

### Styling & Stacking

- Reuses `dialog.css` and relies on data-open/data-closed selectors
introduced in the companion Dialog Base UI migration (PR `mastra-ai#16821`).
Review/merge `mastra-ai#16821` first.
- Manual visual check of animations was not performed.

### Status

- Type-checks and builds pass for `@mastra/playground-ui` and
`@mastra/playground`.
- Local build/tests for `@mastra/playground-ui` passed (205/205 tests; 5
new).
- ESLint and Prettier clean for AlertDialog.

### Breaking Changes

- None. Public API behavior is preserved (including Trigger.asChild and
close behavior of Action/Cancel).

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16824?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary

- Migrates the `Collapsible` design system component from
`@radix-ui/react-collapsible` to `@base-ui/react/collapsible`.
- **Public API unchanged.** `asChild` on `CollapsibleTrigger` is
preserved through a render-prop shim. `Collapsible` and
`CollapsibleContent` keep the same props.
- Part mapping: Radix `Root`/`Trigger`/`Content` → Base UI
`Root`/`Trigger`/`Panel`.
- **Smoother animation.** `CollapsibleContent` now animates with Base
UI's height-based transition (`--collapsible-panel-height`) instead of
the previous fade + slide, so the panel expands and collapses by actual
height.
- Chevron rotation moves from Radix's `data-state=open` to Base UI's
`data-panel-open` on the trigger.
- Adds `collapsible.test.tsx` smoke tests: mounts trigger + content
open, verifies the `asChild` shim, and trigger toggle.

## Test plan

- [x] `pnpm exec tsc --noEmit` on `@mastra/playground-ui` — exit 0
- [x] `pnpm exec tsc --noEmit` on `@mastra/playground` — exit 0
- [x] `pnpm build` on `@mastra/playground-ui` — OK
- [x] `pnpm test` on `@mastra/playground-ui` — 198/198 (3 new)
- [x] `eslint` + `prettier` on `Collapsible` — clean
- [ ] Manual visual check of the height animation in Studio (not run —
dev server lives on the main checkout)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR replaces the underlying library for the Collapsible component
(Radix → Base UI) while keeping the developer-facing API the same. The
panel now expands/collapses by actual height for a smoother animation
instead of the previous fade+slide.

## Changes

### Component Implementation (`collapsible.tsx`)
- Reimplemented Collapsible using `@base-ui/react/collapsible` (Root →
Root, Trigger → Trigger, Content → Panel).
- Preserved public API: `Collapsible`, `CollapsibleTrigger`,
`CollapsibleContent` props unchanged and `asChild` support preserved via
a render-prop shim on `CollapsibleTrigger`.
- Animation: replaced Radix fade+slide with Base UI height-based
transition using CSS variable `--collapsible-panel-height` and explicit
starting/ending height rules (`h-0`) for smooth expand/collapse.
- Layout fix: introduced an inner wrapper inside the Panel so consumer
padding/margin/border do not break the height animation (prevents
jump-to-0 issues).
- Chevron rotation/triggers updated to use Base UI’s `data-panel-open`
on the trigger (replaces Radix `data-state=open`).
- Updated component display names and forwardRef signatures to match
Base UI element types.

### Tests (`collapsible.test.tsx`)
- Added three smoke tests:
  - mounting an open Collapsible renders trigger + content,
- `asChild` renders a provided button element directly (no nested
buttons),
  - clicking the trigger toggles content visibility.
- Test run: `@mastra/playground-ui` tests pass (198/198, includes 3 new
tests).

## Public API
- No breaking changes: props and `asChild` behavior remain compatible
with prior Radix-based implementation.

## Validation
- Type checks and builds for `@mastra/playground-ui` and
`@mastra/playground` complete per checklist.
- ESLint and Prettier clean for Collapsible.
- Manual visual check in Studio not performed in this branch.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16825?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Description

Adds a Playground `tsc --noEmit` typecheck script and wires it into the
Quality Assurance workflow so TypeScript errors are caught outside the
Vite/esbuild build. Fixes the Playground type errors exposed by the new
gate, including route/component props, dataset editor payload typing,
memory/template/runtime typing, and shared Playground UI declaration
props. Validated with Playground typecheck, Playground lint, Playground
UI lint, Playground UI build, and `git diff --check`.

## Related Issue(s)

Fixes mastra-ai#15972

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [x] Test update

## Checklist

- [x] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

This PR makes the Playground run TypeScript's full type checker in CI so
TypeScript mistakes are caught before merging, and fixes the type errors
the new check found across Playground, Playground UI, client, and server
types.

---

## Overview

Adds a Playground `typecheck` script (tsc --noEmit), wires it into CI
(lint workflow + Turborepo), and backfills ~50+ TypeScript fixes so the
Playground typecheck passes. Updates shared Playground UI types and
related client/server schemas/types to keep the codebase type-safe.

## CI / Workflow

- .github/workflows/lint.yml: Adds a Typecheck step that runs `pnpm
turbo --filter ./packages/playground typecheck`.
- turbo.json: Adds a `typecheck` task (dependsOn: ["^build"]).
- packages/playground/turbo.json: Playground build now depends on
`typecheck`.

## Build / Typecheck config

- packages/playground/package.json: Adds `"typecheck": "tsc --noEmit -p
tsconfig.typecheck.json"` and some @types dev deps.
- packages/playground/tsconfig.typecheck.json: New config extending
tsconfig.app.json and excluding stories/tests.

## Type & Component Fixes

- Standardized React type usage (use of `import type`, ReactNode,
CSSProperties, HTMLAttributes/ThHTMLAttributes).
- Playground UI component prop typings updated: Table, Txt (adds
optional title), Icon, and others.
- Normalized handlers and signatures (CodeEditor onChange accepts
string|undefined -> '' conversion; explicit MouseEvent and boolean
typings; LoaderFunctionArgs for route loaders).
- Swapped some local component imports to `@mastra/playground-ui` types
(SchemaField) to centralize types.
- Minor UI/typing adjustments across many Playground components
(memoization, error normalization, prop additions).

## Data / Domain Typing Changes

- Introduced `expectedTrajectory` into dataset item types and
history/hooks; dataset edit/save flow updated to parse/submit it
conditionally.
- Template components: added optional props (imageURL, runId,
workflowInfo, linkComponent).
- Observational memory logic refined: an `observationalMemory` object is
treated as enabled unless explicitly `enabled: false`.

## API / Client / Server typings

- packages/server/src/server/schemas/agents.ts: providerSchema extended
with `envVar: string | string[]`, `connected: boolean`, optional
`docUrl`, and `models: string[]`.
- packages/server/src/server/schemas/datasets.ts:
itemVersionResponseSchema adds `expectedTrajectory?: unknown`.
- client-sdks/client-js: widened `Provider.envVar` and added
`expectedTrajectory?: unknown` in generated/handwritten types.

## Release / Changesets

- Changesets updated to publish patch bumps for @mastra/playground-ui,
@mastra/client-js, @mastra/server, and related internal packages.

## Miscellaneous

- Removed unused PDF extraction stub.
- Small nonfunctional tweaks (ScorerVersionCombobox variant, minor
refactors).
- Validated by: Playground typecheck, Playground lint, Playground UI
lint, Playground UI build, and `git diff --check`.

## Related Issue

Fixes mastra-ai#15972
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ward Peeters <ward@coding-tech.com>
…uto-suspend, consolidate to single path (mastra-ai#16805)

Co-authored-by: Abhi Aiyer <abhi@mastra.ai>
Co-authored-by: Abhi Aiyer <abhiaiyer91@gmail.com>
Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
…#16517)

## Summary

Migrates `AgentChannels` from per-message `agent.stream()` calls to the
new agent signals API. Each Mastra thread now shares a single
`agent.subscribeToThread()` subscription, and incoming platform messages
are delivered as `user-message` signals via `agent.sendSignal()`.

The previous design started a fresh `agent.stream()` for every inbound
message, which meant two messages arriving close together on the same
channel thread could spawn concurrent runs and produce interleaved or
duplicated output. With signals, a message that arrives while the agent
is already running is queued into the existing run instead of kicking
off a new one.

## What changed

- **`processChatMessage`** — replaces `agent.stream(...)` with
`ensureThreadSubscription()` + `agent.sendSignal(...)`. The signal
carries the user's text (and any inline file parts) as a
`CoreUserMessage`, with platform context attached via `requestContext`,
signal `attributes`, and signal `metadata`.
- **`ensureThreadSubscription`** — lazily opens one
`agent.subscribeToThread()` per Mastra thread and caches the wrapped
async iterable. Consumer errors evict the cache entry and unsubscribe.
- **`consumeAgentStream`** — now accepts an
`AsyncIterable<AgentChunkType>`, resets per-run rendering state
(`toolCalls`, `seedApprovalContext`) on `finish` / `error` / `abort`,
and filters out `data-*` signal echoes so we don't double-render the
user's own message.
- **Tool approval resume** — the resumed stream is discarded; rendering
is driven entirely by the cached thread subscription. Approval card
metadata is seeded into `pendingApprovalCards` so the existing card is
edited (not duplicated) when `tool-result` chunks arrive.
- **`buildEventContext` helper** — consolidates the `ChannelContext` /
`attributes` / `signalMetadata` triple that was previously hand-rolled
at both the message and tool-approval call sites. Each shape has a
distinct audience (input processors, the LLM, and stream/storage
consumers respectively), so all three are preserved.
- **`AgentChannels.close()`** — unsubscribes all cached subscriptions
and clears approval state for graceful shutdown.
- **Docs** — small accuracy fix in `reference/agents/channels.mdx` (the
page previously described the now-replaced `agent.stream()` path).

## Behavior change

Follow-up messages on a busy thread now deliver into the active run
instead of starting a new one. This is the intended fix for the conflict
scenario but is worth flagging for anyone who was relying on the old
"always start a new stream" behavior.

## Test plan

- `pnpm --filter ./packages/core vitest run channels` — all 84 channel
tests pass, including three new tests covering `close()` semantics
(clears cached subscriptions, safe when none exist, swallows individual
unsubscribe errors).
- `pnpm --filter ./packages/core typecheck` — clean.
- Manual smoke test against a Slack bot: back-to-back DMs, mention in a
channel, and a tool-approval round-trip all render correctly via the
shared subscription.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

Instead of starting a brand-new agent run for every incoming message
(which could make replies overlap or duplicate), the system now keeps
one shared agent subscription per chat thread and sends new messages
into that single running flow so replies stay ordered and consistent.

## Changes Overview

Migrates AgentChannels from spawning agent.stream() per inbound message
to using the agent signals API with a cached, per-Mastra-thread
agent.subscribeToThread() consumer. Platform messages are delivered as
`user-message` signals via agent.sendSignal(); messages that arrive
while a run is active are queued into the existing run instead of
starting concurrent streams.

## Key Changes

- Thread subscription caching: ensureThreadSubscription() lazily creates
and caches a wrapped agent.subscribeToThread() per mastraThreadId (uses
a placeholder to avoid races); consumer errors evict the cache and
unsubscribe.
- Signal-based routing: processChatMessage() composes signal-ready text
+ eligible file/link parts (respects inlineMedia/inlineLinks), refreshes
the Mastra thread snapshot, ensures the thread subscription, and sends
the user message with agent.sendSignal() (uses ifIdle wake behavior and
conditional auto-resume).
- Stream consumer: consumeAgentStream(AsyncIterable<AgentChunkType>)
consumes chunks from the subscription, filters out data-* subscription
echo chunks to avoid double-rendering the user's own message,
accumulates/flushed text, posts generated files, renders
tool-call/tool-result/tool-call-approval/tripwire/error/abort chunks,
and resets per-run rendering state (toolCalls, seedApprovalContext) on
finish/error/abort.
- Tool approval flow: resumed streams are processed via the cached
thread subscription (not rendered directly). Approval-card metadata is
seeded into pendingApprovalCards so existing cards are edited rather
than duplicated when tool-result chunks arrive; approve/deny handlers
now drain resumed streams so resumed runs complete and render.
- Event context helper: buildEventContext() consolidates ChannelContext,
attributes, and signalMetadata shapes used across handlers, LLM inputs,
and consumers.
- Public API: AgentChannels.close() unsubscribes all cached thread
subscriptions and clears pending approval state for tests and graceful
shutdown; tests added verifying close() behavior (cache drain, safe
no-op, resilience to failing unsubscribe handlers).
- Media handling: introduces DEFAULT_INLINE_MEDIA_TYPES =
['image/png','image/jpeg','image/webp','application/pdf'], updates
inline media/link emission and v5 mediaType on file parts, and falls
back to text when media fetch fails.
- Slack provider and payload handling: SlackProvider preserves existing
AgentChannels.channelConfig by merging the Slack adapter instead of
replacing it, and only parses request bodies as JSON when Content-Type
is application/json (fixes url_verification and form-urlencoded
interactive payload handling).
- Runtime resume fix: subscribeToThread()/thread-stream-runtime now
removes completed/aborted runIds from the seen set so the same runId can
be re-enqueued on resume and delivered to subscribers.
- Dependency & docs: bumped chat dependency to ^4.29.0; updated
reference docs to reflect signal-based routing; added changesets
documenting behavior and fixes.

## Tests & Validation

- All 84 channel tests pass (including three new close() tests).
- Typecheck clean.
- Manual Slack smoke tests (back-to-back DMs, channel mention,
tool-approval round-trip) validated the shared-subscription rendering
behavior.

## Notable Fixes

- Avoids concurrent agent runs and duplicated/interleaved output by
serializing delivery per Mastra thread.
- Fixes Slack interactive payload handling by conditional JSON parsing.
- Ensures resumed runs are fanned out to subscribers and drained on
approval/denial so runs complete and render correctly.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16517?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: CalebBarnes <CalebBarnes@users.noreply.github.com>
Co-authored-by: Mastra Code (anthropic/claude-opus-4-7) <noreply@mastra.ai>
…astra-ai#16839)

## Description

Fixes readability and alignment issues in the quiet mode task list. The
compact horizontal layout made text hard to scan because it used `dim`
(the lowest-contrast theme color) for nearly everything. Also replaced
`stripAnsi().length` with pi-tui's `visibleWidth()` for accurate
terminal column measurement — other TUI components already use this
function.

**Contrast changes:**
- Counter prefix (`1/5`): `dim` → `muted` — key progress info should be
readable at a glance
- Pending item text: `dim` → `muted` — items need to be scannable in the
dense horizontal layout
- Completed items: `success` (green) → `dim` with strikethrough — done
tasks should fade, not compete with in-progress items for attention

Both `muted` and `dim` are WCAG contrast-adapted through the theme
system (`ensureContrast()` at `TUI_MIN_CONTRAST = 5.5:1`), but `muted`
provides noticeably better readability in a packed layout.

**Alignment fix:**
- Replaced `stripAnsi(str).length` with `visibleWidth(str)` from
`@mariozechner/pi-tui` for width calculations in `formatQuietTaskLines`.
`stripAnsi().length` counts JS characters, which can diverge from
terminal display columns for Unicode icons (✓, ▶, ○). Other TUI
components (status-line, overlay, plan-approval) already use
`visibleWidth` for this reason.

## Related issue(s)

Reported by Daniel in [Slack
thread](https://kepler-bej6556.slack.com/archives/C0ACHFXNK7T/p1779290810208319?thread_ts=1779290810.208319&cid=C0ACHFXNK7T):
quiet mode task list has contrast and alignment issues.

## Type of change

- [x] Bug fix (non-breaking change that fixes an issue)

## Checklist

- [x] I have linked the related issue(s) in the description above
- [x] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] I have addressed all Coderabbit comments on this PR

Link to Devin session:
https://app.devin.ai/sessions/89b2b43bb1c5487db71680ae20307033
Requested by: @TylerBarnes

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

Makes the quiet-mode UI easier to read and stops icons/Unicode from
breaking alignment: counters and pending tasks are more visible,
completed tasks are de-emphasized, and line-wrapping measures the
terminal-visible width so glyphs don't misalign.

## Changes

### Bug Fix
- Fixes readability and horizontal alignment in quiet-mode task lists
and compact quiet tool displays.

### Contrast & Visual Changes
- Counter prefix (e.g., `1/5`) changed from `dim` → `muted`.
- Pending item text changed from `dim` → `muted`.
- Completed items changed from green (`success`) → `dim` with
strikethrough to de-emphasize.
- Compact tool argument background adjusted from `#0f0f0f` → `#141414`.
- Compact quiet rail/circle glyphs color selection now normalizes via a
new contrast helper so glyphs remain visible on near-black and brighter
terminal backgrounds.

All muted/dim choices remain contrast-adapted by the theme
(ensureContrast at TUI_MIN_CONTRAST = 5.5:1); new utilities provide
special handling for near-black terminal backgrounds.

### Alignment & Measurement
- Replace stripAnsi(str).length with visibleWidth(str) (from
`@mariozechner/pi-tui`) in formatQuietTaskLines so terminal-visible
column widths account for Unicode icons (✓, ▶, ○), preventing
misalignment and incorrect wrapping.

### Theme & Utilities
- Add exported helpers:
- ensureContrastUnlessNearBlack(fgHex: string, minRatio =
TUI_MIN_CONTRAST): string
- ensureTerminalGlyphContrast(fgHex: string, minRatio =
TUI_MIN_CONTRAST): string
These adapt foreground contrast differently on near-black backgrounds
vs. brighter backgrounds.

### Tests
- Add/extend tests for:
- ensureContrastUnlessNearBlack and ensureTerminalGlyphContrast behavior
across near-black and brighter backgrounds.
- Updated expected terminal color output for compact quiet tool badges
and rail/glyph contrast.
- Updated TUI component tests to match the new coloring and
truncation/continuation behavior.

### Files Modified (high-level)
- .changeset/large-olives-see.md — release notes
- mastracode/src/tui/components/task-progress.ts — use visibleWidth;
quiet-mode coloring & formatting updates
- mastracode/src/tui/components/tool-execution-enhanced.ts — compact
quiet styling refactor, rail/glyph color normalization,
COMPACT_TOOL_ARGS_BG change
- mastracode/src/tui/theme.ts — added near-black contrast helpers and
related constants
- mastracode/src/tui/__tests__/theme-contrast.test.ts — tests for new
contrast helpers
-
mastracode/src/tui/components/__tests__/tool-execution-enhanced.test.ts
— updated expected colors/outputs for compact quiet badges
- Other TUI tests updated to reflect adjusted
output/truncation/continuation behavior

## Type
Non-breaking bug fix (mastracode package)

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16839?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: tyler <tylerdbarnes@gmail.com>
Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
…stra-ai#16832)

## Description

The Metrics page date picker only offered fixed relative presets (last
24h/3d/7d/14d/30d) with no way to select an arbitrary date range, unlike
the Traces page. This swaps the plain dropdown for the shared
`DateTimeRangePicker` so users can filter metrics by a custom start/end
date and time, matching the Traces experience.

## Before
<img width="1512" height="982" alt="image"
src="https://github.com/user-attachments/assets/6f7146bf-5be8-4858-97e0-324e993d7008"
/>

## After
<img width="1512" height="982" alt="image"
src="https://github.com/user-attachments/assets/47945c69-8da9-4b2a-87b7-ea8741001b09"
/>

- Replaced the `SelectFieldBlock` dropdown in `DateRangeSelector` with
the shared `DateTimeRangePicker` (the same component the Traces page
uses), exposing the `Custom range…` calendar
- Added `toPickerPreset`/`fromPickerPreset` mappers to bridge the
metrics preset values (`24h`, `3d`, …) and the picker's values
(`last-24h`, `last-3d`, …)
- Bridged the picker's per-field `onDateChange` to the provider's
whole-range `setCustomRange`, with refs that guard the spurious change
fired on non-custom preset switches and merge the two field updates
(mirrors the Traces approach)
- Wired `dateFrom`/`dateTo` URL params in the Metrics page: parse them
into `customRange`, clear them when switching back to a relative preset,
and pass `customRange` + `onCustomRangeChange` into `MetricsProvider`
- The data layer already consumed `customRange`, so no query/aggregation
changes were needed

## Related Issue(s)

Fixes mastra-ai#16814

## Type of Change

- [ ] Bug fix (non-breaking change that fixes an issue)
- [x] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have made corresponding changes to the documentation (if
applicable)
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
The Metrics page date picker now lets you pick any exact start and end
date/time ("Custom range…") instead of only fixed presets like "Last 24
hours." The chosen custom range is reflected in the URL so it can be
shared/bookmarked and is cleared when you switch back to a relative
preset.

## Changes

### Metrics Date Range Picker UI Replacement
- Replaced the fixed-presets dropdown (`SelectFieldBlock` using
`DATE_PRESETS`) in DateRangeSelector with the shared
`DateTimeRangePicker`, exposing the "Custom range…" calendar.
- Added bidirectional preset mappers (`toPickerPreset` /
`fromPickerPreset`) to translate metrics preset values (`24h`, `3d`,
`7d`, `14d`, `30d`, `custom`) ↔ picker presets (`last-24h`, `last-3d`,
`last-7d`, `last-14d`, `last-30d`, `custom`).
- Bridged the picker's per-field `onDateChange` to the provider's
whole-range `setCustomRange` using refs (`presetRef`, `customRangeRef`)
so field updates are ignored during non-custom preset switches and
merged correctly for custom ranges (mirrors Traces behavior).

### Metrics Page URL Parameter Handling
- Introduced DATE_FROM_PARAM / DATE_TO_PARAM and parse
`dateFrom`/`dateTo` URL params into `customRange` when the selected
preset is `custom`.
- Added `onCustomRangeChange` to write/remove `dateFrom`/`dateTo` in the
URL and to clear those params automatically when switching away from a
relative preset.
- Passed `customRange` and `onCustomRangeChange` into `MetricsProvider`.

### Tests & Release
- Updated e2e tests to reflect the DateTimeRangePicker's button/menu UI
(selectors changed from combobox/options to button/menuitem).
- Added a changeset to release `@mastra/playground-ui` as a minor
version and updated release notes documenting custom date range support.

## Notes
- No data-layer query or aggregation changes were required; the data
layer already consumes `customRange`.

## Fixes
- Fixes issue `mastra-ai#16814` (adds the missing "Custom range" option to the
Metrics page).

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16832?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Anurag Ojha <aojharaj2004@gmail.com>
Co-authored-by: Damien Schneider <74979845+damien-schneider@users.noreply.github.com>
mastra-ai#16846)

Co-authored-by: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>
## Description
Adds post hog events to track when enterprise edition licenses are
invoked and what features they're invoked for.

## Related issue(s)
N/A

## Type of change
- [x] Telemetry change

## Checklist
- [ ] I have linked the related issue(s) in the description above
- [ ] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR

<!-- devin-review-badge-begin -->

---

<a href="https://app.devin.ai/review/mastra-ai/mastra/pull/16660"
target="_blank">
  <picture>
<source media="(prefers-color-scheme: dark)"
srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img
src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1"
alt="Open in Devin Review">
  </picture>
</a>
<!-- devin-review-badge-end -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5 (Explain Like I'm 5)

This PR makes the Enterprise edition quietly tell PostHog when an EE
license is checked and when EE-only features (RBAC/FGA) are used — but
it only sends hashed/anonymous identifiers so no raw license keys,
emails, or user-agents are leaked, and telemetry can be turned off.

## Overview

Adds PostHog-based, privacy-preserving EE telemetry to core that emits
two event types: ee_license_check and ee_feature_used. Telemetry uses
hashed license identifiers and a machine-level fallback distinctId,
includes non-sensitive system/license metadata, respects
MASTRA_TELEMETRY_DISABLED=1, and is implemented so telemetry failures
never affect auth/authorization flows.

## Key Changes

- Telemetry infra
  - New module: packages/core/src/telemetry/posthog.ts
- EEEventName type, isEETelemetryEnabled(), hashTelemetryValue(),
getEETelemetryFallbackDistinctId(), captureEEEvent(),
resetEETelemetryForTests().
- Lazily initializes a singleton PostHog client (posthog-node), attaches
system properties (os, node, machine_id, mastra_version), and swallows
errors so telemetry never breaks runtime behavior.

- License & feature tracking
  - packages/core/src/auth/ee/license.ts
- Added SafeLicenseSummary and getSafeLicenseSummary() producing
telemetry-safe licenseHash (truncated) and anonymousId, plus validity,
features, tier, and dev-environment flag.
  - packages/core/src/auth/ee/capabilities.ts
- buildCapabilities now invokes captureLicenseCheck to emit
ee_license_check with safe license summary, capability flags, and client
IP (x-forwarded-for / x-real-ip); emits ee_feature_used when RBAC access
is resolved (role/permission counts, org membership id). Telemetry is
try/catch-wrapped.
  - packages/core/src/auth/ee/fga-check.ts
- requireFGA emits ee_feature_used on successful FGA checks with
resource_type/id, permission, org membership id, and safe license
fields; telemetry failures are ignored.

- Tests
  - packages/core/src/telemetry/posthog.test.ts
- Mocks posthog-node to verify enable/disable behavior, fallback
distinctId/machine_id, and that capture errors are swallowed.
  - packages/core/src/auth/ee/__tests__/ee-telemetry.test.ts
- Verifies ee_license_check (IP parsing, validity, no leakage of
license/email/user-agent), ee_feature_used for RBAC and FGA (counts and
org membership), and that FGA still succeeds if telemetry capture
throws.

- Dependency & changelog
  - packages/core/package.json: added dependency posthog-node@^5.30.6.
- .changeset/easy-pumas-follow.md: documents EE telemetry behavior and
the disable switch.

## Notes for reviewers / Outstanding items

- Change type: telemetry (non-functional) but introduces a runtime
dependency on posthog-node and a hard-coded PostHog host/key in the
module.
- Telemetry is enabled by default for EE and can be disabled with
MASTRA_TELEMETRY_DISABLED=1; community users (no EE license) are
unaffected.
- Telemetry is implemented to avoid leaking raw secrets and to not
affect authorization/auth flows; tests cover key behaviors.
- Documentation: only the changeset was updated; no public docs or
related-issue link added. Some automated reviewer (Coderabbit) comments
remain unaddressed.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/mastra-ai/mastra/pull/16660?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
abhiaiyer91 pushed a commit that referenced this pull request May 20, 2026
…ments (mastra-ai#16117)

## Description

Tools that return AI SDK v5 `image-data` content blocks via
`toModelOutput` were having their base64 payloads stringified into the
observational-memory observer's text prompt, blowing past token limits
and producing degenerate observer output. This change extracts media
blocks out of tool-result outputs and passes them to the observer as
real attachments — mirroring the existing handling for user-side `image`
and `file` message parts.

- Added `mapToolResultBlockToAttachment` to translate AI SDK v5 content
blocks (`image-data`, `image-url`, `media`, `file-data`, `file-url`)
into `ObserverAttachmentPart` form, building
`data:<mediaType>;base64,<data>` URIs for inline blocks.
- Added `extractToolResultAttachments` which walks `{ type: 'content',
value: [...] }` tool outputs, hoists media blocks as attachments, and
replaces each block in the residual with `{ type, placeholder: '[Image
#N: ...]' }` so the JSON-stringified body stays small and shape-stable.
- Wired the extractor into the `tool-invocation` result branch of
`formatObserverMessage` so attachments flow through to
`buildObserverHistoryMessage` alongside the existing image/file part
path.
- Non-content outputs (`text`, `json`, `error-text`, `error-json`,
`execution-denied`) and unknown block types (`image-file-id`, `file-id`,
`custom`) pass through unchanged.
- Added three regression tests in `observational-memory.test.ts`:
image-data block becomes a placeholder in `formatMessagesForObserver`,
plain object tool results still serialize as JSON, and
`buildObserverHistoryMessage` emits a real `{ type: 'image' }`
attachment with the correct data URI and `mimeType`.

## Related Issue(s)

Fixes mastra-ai#15883

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [ ] I have made corresponding changes to the documentation (if
applicable)
- [x] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have addressed all Coderabbit comments on this PR

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5

Tools were embedding full images/files as huge text blobs, which made
messages too large and broke prompts. This PR pulls those images/files
out of the text and sends them as attachments, leaving small numbered
placeholders in the text to keep order and context without the bloat.

## Overview

Detects AI SDK v5 content blocks inside tool-result outputs (image-data,
image-url, media, file-data, file-url), hoists recognized media/file
blocks into ObserverAttachmentPart entries, and for inline binary blocks
builds data:<mediaType>;base64,<data> URIs. The extractor replaces the
original blocks in the residual content with compact numbered
placeholder blocks (e.g., { type, placeholder: '[Image #1: image/png]'
}) so the JSON-stringified body stays small and shape-stable. The
extractor is integrated into the tool-invocation result branch of
formatObserverMessage so attachments flow into
buildObserverHistoryMessage alongside existing user-attached image/file
parts. Non-content outputs (text, json, error-text, error-json,
execution-denied) and unknown/unsupported block types (image-file-id,
file-id, custom) pass through unchanged.

## Key changes

- Added mapToolResultBlockToAttachment: translates content blocks into
ObserverAttachmentPart (creates data: URIs for inline blocks).
- Added extractToolResultAttachments: walks content outputs ({ type:
'content', value: [...] }), hoists media/file blocks into attachments,
and replaces them with numbered placeholder blocks in the residual
content.
- Wired the extractor into formatObserverMessage/observer-agent so
hoisted attachments are appended to message attachments and the textual
"Tool Result …" parts contain placeholders instead of raw base64.
- Ensures image/file numbering counter is shared between user-attached
parts and tool-result attachments so placeholders are sequential and
stable.

## Tests

- Added regression tests in observational-memory.test.ts:
- formatMessagesForObserver produces placeholders for image-data and
ensures base64 does not appear in formatted text.
  - Plain-object tool results still serialize to JSON correctly.
- buildObserverHistoryMessage emits { type: 'image' } attachments with
correct data: URI and mimeType, and verifies shared numbering between
user-attached and tool-result images.
- Coverage includes image/file URL blocks, generic media blocks, and
image-data without mediaType.

## Docs / Changeset

- .changeset entry describing the fix to prevent token-limit failures by
hoisting media/file blocks into attachments and inserting placeholders
in text.

## Issue resolved

Fixes mastra-ai#15883 — observational memory now delivers tool-produced
image/file data as attachments with placeholder references in text,
preventing token-limit overflows while preserving positional context.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Tyler Barnes <tylerdbarnes@gmail.com>
Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
abhiaiyer91 pushed a commit that referenced this pull request May 20, 2026
…mastra-ai#14237)

This implements Phase 2 of the memory resource scoping, enforcing
resourceId isolation across Postgres and LibSQL storage drivers. It
references PR #1 which introduced the interface changes.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
ELI5: The PR makes thread lookups respect a "resource" label so a thread
from one tenant/resource can't be read by another. It updates
thread-getting methods across storage drivers and adds tests to ensure
the isolation works.

* **What changed**
* Enforce resourceId scoping for memory/thread retrieval across SQL
drivers (Postgres, LibSQL) and all memory-backed storage adapters by
extending getThreadById to accept an optional resourceId and return null
when the stored thread’s resourceId does not match.
* Updated the abstract MemoryStorage API (packages/core) and in-memory
implementation to include the optional resourceId parameter.
* Applied the same optional resourceId check in adapters for: Postgres,
LibSQL, ClickHouse, Cloudflare (D1, DO, KV), Convex, DynamoDB, Lance,
MongoDB, MSSQL, Redis, Upstash and related memory wrappers.
* Updated packages/memory to forward resourceId in Memory.getThreadById
and adjusted the wrapper signature accordingly.

* **Tests**
* Moved/added resourceId isolation tests into the shared threads test
suite (stores/_test-utils/src/domains/memory/threads.ts) verifying:
matching resource returns the thread, mismatched resource returns null,
and omission of resourceId remains backward-compatible.

* **Documentation / Release**
* Added changeset (.changeset/memory-isolation-phase-2.md) documenting
the change and bumping affected packages.

* **Notes for reviewers**
* Public API: MemoryStorage.getThreadById signature changed to accept
optional resourceId — adapter implementations were updated accordingly.
* Behaviour: When resourceId is provided, lookups are now
resource-scoped and will return null for non-matching resourceIds; when
omitted, existing behavior is preserved.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Sigmabrogz <bnb1000bnb@gmail.com>
Co-authored-by: Sigmabrogz <sigmabrogz@gmail.com>
Co-authored-by: Tyler Barnes <tylerdbarnes@gmail.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.