Skip to content

Add Mesa as a workspace filesystem provider - #1

Draft
warrenbhw wants to merge 88 commits into
mainfrom
warrenbhw/mesa-filesystem-integration
Draft

Add Mesa as a workspace filesystem provider#1
warrenbhw wants to merge 88 commits into
mainfrom
warrenbhw/mesa-filesystem-integration

Conversation

@warrenbhw

Copy link
Copy Markdown

Adds a new @mastra/mesa workspace filesystem provider backed by Mesa repos. The provider mounts repos through the Mesa SDK, implements the Mastra workspace filesystem interface, and exposes Mesa-specific helpers like bash(), change, bookmark, and the mounted filesystem for advanced use.

Also adds docs, README usage, focused unit/integration test coverage, a changeset, and the lockfile/package workspace updates needed for @mesadev/sdk@0.38.0.

Validated with:

  • corepack pnpm --filter ./workspaces/mesa test:unit
  • corepack pnpm --filter ./workspaces/mesa build
  • corepack pnpm --filter ./workspaces/mesa lint
  • corepack pnpm --filter ./docs build

The docs build still reports existing unrelated broken-anchor warnings in agent/evals docs.

@warrenbhw
warrenbhw force-pushed the warrenbhw/mesa-filesystem-integration branch 13 times, most recently from 3898465 to 1c61cd3 Compare June 29, 2026 07:22
…ructure and scale it in CI (mastra-ai#18514)

## Summary

This PR does two things to the `packages/playground` Playwright E2E
suite: it standardizes every spec on a single BDD structure (enforced by
lint), and it speeds up the suite in CI by scaling out the shard matrix.

## 1. Standardize the E2E suite on a lint-enforced BDD structure

The Playwright E2E suite is restructured to use the same BDD shape
already used by the package's MSW Vitest tests, so both suites read the
same way and the structure is enforced by lint instead of relying on
convention.

The shape, applied to all 53 e2e specs:

- outer `test.describe` = the unit under test
- inner `test.describe('when …')` = exactly one precondition
- each `test` = exactly one observable outcome

What's included:

- **All 53 e2e specs restructured** into the BDD shape with no change to
what they assert and no change to the number of tests.
- **Lint enforcement**: a custom rule in
`packages/playground/eslint.config.js` flags any bare `test`/`it` whose
nearest enclosing `describe` title does not start with `when`. It
handles interpolated `when` titles and ignores legitimate `test.skip`
guards. The rule runs over `e2e/tests/**/*.spec.ts`, which were
previously not lint-covered at all.
- **Docs**: the BDD convention is now documented in
`.claude/skills/e2e-tests-studio/SKILL.md` and
`packages/playground/AGENTS.md`, so new E2E tests follow the same
structure.
- **Snapshot re-baselines**: three ARIA snapshots were regenerated 1:1
after the `describe` title renames changed their filenames; the
regenerated breadcrumb snapshots reflect the app's current accessible
name (`navigation "Breadcrumb"`), and the orphaned old files were
removed.

Verification:

- Lint: 0 errors (pre-existing warnings only); the new BDD rule was
verified firing on a deliberate flat probe and quiet on valid specs.
- Full Playwright suite: **277 passed / 34 skipped / 0 failed**,
including the re-baselined snapshot tests.

## 2. Scale the kitchen-sink E2E run in CI (shard matrix)

The playground E2E run was split across only **3 shards**. Each shard
already runs on its own runner with its own server and its own
`e2e-test-storage.db`, so cross-shard parallelism is already isolated
and safe — there just weren't enough shards. No changes to test
isolation, the kitchen-sink server, or the storage model.

- **Widened the shard matrix** in `.github/workflows/e2e-tests.yml`
(`e2e-kitchen-sink`). The job name and `--shard` argument already
interpolate `strategy.job-total`, so adding matrix entries was
sufficient — no command edits. `fail-fast: false` is kept so every shard
reports independently.
- **Scoped the per-shard `Build` step** to a turbo filter of just the
kitchen-sink runtime chain instead of a full `pnpm build`, shrinking the
cache-restore surface so adding shards stays cheap. The filtered set
resolves the full transitive graph and boots `mastra dev` cleanly
(verified in CI — no webServer startup timeout). The full build still
runs once in `prebuild` and warms the remote turbo cache.

The final shard count was **tuned from measured CI wall-clock**, not
guessed:

| Config | Long pole (slowest shard) | CI cost (shard-minutes) |
|---|---|---|
| 3 shards (baseline) | 5m46s | ~17m |
| **4 shards (settled)** | **5m25s** | ~19m |
| 6 shards | 4m55s | ~23m |

Going 3→6 only cut the long pole ~50s while adding ~6 shard-minutes of
CI cost, because the bottleneck is no longer test volume — it's
per-shard fixed overhead (deps, Playwright/Chromium install, cache
restore, server boot) plus file-level shard imbalance. **4 shards sits
at the knee of the curve**, capturing nearly all the achievable win at
lower CI cost, so the matrix is settled at `[1, 2, 3, 4]`. All shards
green, no retry-masked flakiness.

Out of scope (follow-up): the remaining ~2-min spread between fastest
and slowest shard comes from heavy specs (auth/RBAC, workflow-debug)
clustering on one shard. Reducing the long pole further needs test-level
balancing, not more shards.

## Notes

No changeset: this is test/tooling/CI/docs only, with no shipped package
behavior change.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR reorganizes the Playwright end-to-end tests so they read like
clear “stories” (setup → when it happens → expected result). It also
makes the biggest CI Playwright run faster by running fewer/building
less work per shard, while keeping the same test checks.

## Summary
- Standardized all `packages/playground` Playwright E2E specs (53 files)
to a consistent BDD-style structure:
  - outer `test.describe` = feature/page under test
  - inner `test.describe('when …')` = one precondition
  - leaf `test(...)` = one observable outcome
- assertions and test counts were preserved (structural/naming-only
changes)
- Added an ESLint rule (`e2e-bdd/test-needs-when-describe`) for
`e2e/tests/**/*.spec.{js,jsx,ts,tsx}` to error when a bare
`test()`/`it()` is not nested under a nearest `describe` whose title
starts with `when` (with guidance for linting configuration in
`eslint.config.js`).
- Documented the convention and templates in:
  - `.claude/skills/e2e-tests-studio/SKILL.md`
  - `packages/playground/AGENTS.md`
- Regenerated 3 ARIA snapshots whose filenames changed due to `describe`
title updates.
- CI performance improvements for the “kitchen-sink” Playwright run:
  - expanded sharding from 3 shards to 4 (`[1,2,3]` → `[1,2,3,4]`)
- narrowed the per-shard build step to a turbo-filtered build chain
(instead of a full build)

## Notable impact
- No behavioral assertion changes—tests were reorganized and re-grouped
to match the enforced BDD convention.
- Playwright suite result: **277 passed, 34 skipped, 0 failed**.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (anthropic/claude-opus-4-8) <noreply@mastra.ai>
Co-authored-by: Damien Schneider <74979845+damien-schneider@users.noreply.github.com>
@warrenbhw
warrenbhw force-pushed the warrenbhw/mesa-filesystem-integration branch 3 times, most recently from 68f17f8 to 2c6aa96 Compare June 29, 2026 07:58
## Description

Fixes a recurring lint formatting issue in `session.ts` that causes CI
failures across multiple PRs.

## 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: This change is a tiny cleanup in `session.ts` that makes one
boolean check easier for the linter to قبول, so CI stops failing on
formatting. It doesn’t change how tool-call resuming works.

### What changed
- Simplified the `isInteractive` assignment in `Session.resumeToolCall`
to a single-line boolean expression.
- Kept the existing behavior for detecting interactive built-in
suspended tools (`ask_user` / `request_access`) unchanged.

### Impact
- No public API changes.
- Non-breaking bug fix.
- Helps prevent recurring lint formatting failures in CI.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@warrenbhw
warrenbhw force-pushed the warrenbhw/mesa-filesystem-integration branch 2 times, most recently from 34a9874 to 80f5e70 Compare June 29, 2026 08:02
@warrenbhw
warrenbhw force-pushed the warrenbhw/mesa-filesystem-integration branch from 80f5e70 to b25f445 Compare June 29, 2026 08:06
intojhanurag and others added 7 commits June 29, 2026 13:49
…astra-ai#18569)

## Description

Inline skills created via `createSkill()` were not displayed in the
Mastra Dev Portal because the server only queried workspace-level
skills. This fix updates the server handlers to use the Agent's public
API (`agent.listSkills()` and `agent.getSkill()`) which merges both
inline and workspace skills.

- Replace `agent.getWorkspace().skills.list()` with `agent.listSkills()`
in `getSerializedSkillsFromAgent()`
- Replace `workspace.skills.get()` with `agent.getSkill()` in
`GET_AGENT_SKILL_ROUTE` handler
- Remove now-unnecessary workspace null checks since the Agent API
handles this internally

## Before
<img width="1512" height="982" alt="image"
src="https://github.com/user-attachments/assets/65f1bfed-26e6-42a8-b453-cd6371416031"
/>

## After
<img width="1512" height="982" alt="image"
src="https://github.com/user-attachments/assets/32c155d3-1676-4f42-84bc-24d6e093e726"
/>

## Related Issue(s)

Fixes mastra-ai#18568

## 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)
- [ ] 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 change makes sure skills added directly to an agent show up in the
Dev Portal too, not just skills that come from the workspace. It does
this by asking the agent itself for its skills, so the portal now sees
everything the agent can use.

### Summary of changes
- Updated server skill serialization to use `agent.listSkills()` so both
inline and workspace skills are included.
- Updated the agent skill lookup route to use
`agent.getSkill(identifier, { requestContext })`.
- Removed now-unnecessary workspace null/config checks from the skill
handler logic.
- Bumped `@mastra/server` in the changeset and clarified the Dev Portal
visibility fix.

<!-- 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
This change fixes some broken links in the docs by sending old Harness
page URLs to their new home. It also adds a few more redirects so people
who use the old pages still end up in the right place.

## Summary
- Updated `docs/vercel.json` redirect rules for `/docs/harness/*` paths.
- Moved the existing redirects into a new block later in the array.
- Kept the redirects permanent and expanded coverage to include:
  - `/docs/harness/overview`
  - `/docs/harness/session`
  - `/docs/harness/threads-and-state`
  - `/docs/harness/modes`
  - `/docs/harness/subagents`
  - `/docs/harness/tool-approvals`
- All of these now point to the corresponding `/docs/agent-controller/*`
pages.

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

## Description

Studio's Metrics tab refused to render for `PostgresStoreVNext` users,
showing "Metrics are not available with your current storage" even
though the backend fully implements metric queries and returns real
data. The UI gate used a hardcoded allowlist of observability storage
types that was never updated when Postgres v-next observability landed
in PR mastra-ai#16760.

- Added `ObservabilityStoragePostgresVNext` to the
`ANALYTICS_OBSERVABILITY_TYPES` set in the metrics page
- Updated the empty-state description to list Postgres v-next as a
supported backend
- Corrected `overview.mdx` and `automatic-metrics.mdx` docs that
incorrectly blanket-excluded PostgreSQL from metrics support

## Related Issue(s)

Fixes mastra-ai#18593

## 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)
- [x] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update

## Checklist

- [x] 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
Before this, the Studio “Metrics” tab hid itself for Postgres v-next
users even though the backend can fetch metrics. This PR adds Postgres
v-next to the allowlist and updates the messages/docs so the UI and
documentation agree with what actually works.

## Summary
- Updated the Studio Metrics UI gating logic to include
`ObservabilityStoragePostgresVNext`, so the Metrics tab renders for
Postgres v-next observability stores.
- Revised the Metrics empty-state and guidance to explicitly list
Postgres v-next as supported (and clarify when/why time-range filters
matter).
- Updated observability metrics documentation (`overview.mdx`,
`automatic-metrics.mdx`) to remove the blanket exclusion of PostgreSQL
and reflect Postgres v-next support.
- Added a changeset noting that the Studio Metrics tab now works for
`PostgresStoreVNext`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Abhi Aiyer <abhiaiyer91@gmail.com>
… values (mastra-ai#18529)

## Description

MCP stdio server `env` values were passed through verbatim, while HTTP
server headers already resolved `${VAR}` references from the environment
(mastra-ai#18240). This applies the same expansion to stdio `env`, so secrets can
be referenced from the host environment instead of being hardcoded in
`mcp.json`. The behavior now matches how Claude Code resolves values in
`.mcp.json`, which the config loader already documents as its goal.

`expandEnvValues` mirrors the existing `expandHeaderEnvVars` helper and
reuses `expandEnvVars`, so `${VAR}`, `${VAR:-default}`, and bare `$VAR`
forms all resolve consistently.

## Related issue(s)

Fixes mastra-ai#18528

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

- [x] 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

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

## ELI5
This change lets MCP stdio servers use secrets from your computer’s
environment variables instead of typing them into config files. So if
`mcp.json` says `"${GITHUB_TOKEN}"`, it now turns into the real token
before the server starts, just like HTTP config already did.

## Summary
- Expanded `${VAR}`, `${VAR:-default}`, and `$VAR` references in stdio
server `env` values using the host environment.
- Reused the existing environment expansion logic so stdio behavior now
matches HTTP header handling.
- Added a test covering stdio `env` expansion and env
cleanup/restoration.
- Added a changelog note describing the new stdio env interpolation
behavior.

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

## Description

`chatRoute` (and the underlying `handleChatStream`) from
`@mastra/ai-sdk` ignored any agent config edited through the Agent
Editor. It resolved the agent with a plain
`mastra.getAgentById(agentId)` and ran the bare, code-defined agent — so
anything a user changed in the editor (instructions, tools, model) was
silently dropped on this endpoint, while Studio served the edited config
correctly.

This is most visible when the instructions live entirely in the editor's
stored config (for example `MastraEditor({ source: 'code' })`, where the
instructions are removed from the `Agent({...})` definition and kept in
a per-agent JSON file). In that setup the endpoint sent **empty**
instructions to the model and the agent could not answer, even though
the exact same agent worked in Studio.

### Root cause

There are two paths that turn an agent ID into a runnable agent, and
only one of them applied the editor's stored config:

- **Studio** uses the built-in handler `getAgentFromMastra`
(`@mastra/server`), which, after finding the code agent, calls
`editor.agent.applyStoredOverrides(agent, { status: 'published' },
requestContext)`. The stored edits are layered on top of the code agent.
- **`chatRoute` / `handleChatStream`** (`@mastra/ai-sdk`) skipped that
step entirely unless the caller explicitly passed a `?versionId=` /
`?status=` query param. A normal request fell through to the bare code
agent.

So the instructions were never missing — they were sitting in the
editor's stored config, and `handleChatStream` simply never looked
there.

### The fix

`handleChatStream` now resolves the agent the same way Studio does. When
an editor is configured, it routes the agent through
`applyStoredOverrides`, defaulting to the **published** version and
threading `requestContext`. An explicit `agentVersion` (from query
params or route options) still takes precedence.

```ts
// before
const agentObj = agentVersion
  ? await mastra.getAgentById(agentId, agentVersion)
  : mastra.getAgentById(agentId); // bare code agent — editor edits ignored

// after
const baseAgent = mastra.getAgentById(agentId);
const editorAgent = mastra.getEditor?.()?.agent;
if (editorAgent) {
  agentObj = await editorAgent.applyStoredOverrides(
    baseAgent,
    agentVersion ?? { status: 'published' }, // explicit version wins, else published
    requestContext,
  );
}
```

When no editor is configured, behavior is unchanged:
`applyStoredOverrides` is never called, and the code agent runs exactly
as before. When an editor is configured but the agent has no stored
config, `applyStoredOverrides` returns the code agent untouched — so
non-editor and not-yet-edited agents are unaffected.

### Behavior, before vs after

| Path | Before | After |
| --- | --- | --- |
| Studio | edited instructions ✅ | edited instructions ✅ |
| `chatRoute` endpoint | bare code agent (empty/stale instructions) ❌ |
edited instructions ✅ |

## Related issue(s)

Fixes mastra-ai#18574

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

- [x] 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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
Before this change, the `/chat` endpoint sometimes ran the “original”
agent code instead of the edited agent settings you saved in the Editor.
Now it pulls the saved overrides (like instructions and tools) and uses
them when streaming chat, so the agent behaves the way Studio shows.

## Summary
- Updated `@mastra/ai-sdk` `chatRoute` / `handleChatStream` to resolve
the agent through `editor.agent.applyStoredOverrides` when an
`@mastra/editor` is configured.
- Defaulted stored-override resolution to the published agent version,
while honoring an explicitly provided `agentVersion` to take precedence.
- Threaded `requestContext` into `applyStoredOverrides` so override
resolution matches Studio behavior.
- Preserved the prior behavior when no editor is configured or when the
editor has no stored config.
- Added regression tests covering stored override resolution (including
published default), explicit `agentVersion`, `requestContext`
forwarding, and the no-editor/no-config fallbacks.
- Added a changeset to patch `@mastra/ai-sdk` with the fix.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Anurag Ojha <aojharaj2004@gmail.com>
…wName omitted (mastra-ai#18586)

## What

`WorkflowsInMemory.getWorkflowRunById` returns `null` when
`workflowName` is omitted, even though a run with the given `runId`
exists.

`workflowName` is optional in the storage contract (`base.ts`:
`getWorkflowRunById(args: { runId: string; workflowName?: string })`),
and the persistent adapters honor that. The pg and libsql adapters only
add the `workflow_name` condition `if (workflowName)` and otherwise
match by `run_id` alone. The in-memory store instead always ran:

```ts
const runs = Array.from(this.db.workflows.values()).filter(r => r.run_id === runId);
let run = runs.find(r => r.workflow_name === workflowName);
```

When `workflowName` is `undefined`, `r.workflow_name === undefined`
never matches a stored run (runs always carry a concrete name), so the
method returns `null`. This makes the in-memory store behave differently
from every persistent adapter for a contract-supported call.

## Change

Match by `runId`, only filter by `workflowName` when it is provided, and
return the most recent run (`createdAt DESC`) to match the pg/libsql
behavior (`ORDER BY createdAt DESC LIMIT 1`).

## Tests

Added unit tests covering the omitted-name case, the provided-name case
(correct run and a wrong name returning null), and an unknown `runId`. I
confirmed the new omitted-name test fails on the current code and passes
after the fix.

The wider storage suite and `workflows/state-reader` tests pass. Two
unrelated suites in `packages/core/src/storage` (`filesystem.test.ts`
and `bundle.test.ts`) fail on a clean checkout as well, due to a Windows
temp-dir lock and a build-dependent skip, so they are not from this
change.

Closes mastra-ai#18585

---

cc @taofeeq-deru who owns most of this store. Small fix, happy to drop
the createdAt ordering if you would rather keep the in-memory lookup
minimal.


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

## ELI5
This change fixes a lookup bug in the in-memory workflow store. If you
ask for a run by its ID and don’t give a workflow name, it now finds the
right run instead of saying “not found,” just like the other storage
backends.

### Summary
- Fixed `WorkflowsInMemory.getWorkflowRunById` so `workflowName` is
optional as the storage contract expects.
- When `workflowName` is omitted, the lookup now matches by `runId`
alone.
- When `workflowName` is provided, it still filters by both `runId` and
`workflowName`.
- If multiple runs share the same `runId`, the most recent matching run
is returned.
- Added tests covering:
  - omitted `workflowName`
  - provided `workflowName`
  - wrong `workflowName`
  - unknown `runId`
- Added a changeset documenting the behavior fix.

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

## What

The Zod v4 date handler in `@mastra/schema-compat` produces inverted
descriptions for `z.date().min()` and `z.date().max()`. The text
attached to the schema, which is sent to the model, states the opposite
and impossible bound.

In Zod v4 a date bound is stored as a comparison check:

- `z.date().min(d)` becomes a `greater_than` check with value `d`, so
the date must be newer than `d`.
- `z.date().max(d)` becomes a `less_than` check with value `d`, so the
date must be older than `d`.

`defaultZodDateHandler` in `schema-compatibility-v4.ts` mapped these the
wrong way around: the `less_than` branch emitted "newer than" and the
`greater_than` branch emitted "older than". The v3 handler in
`schema-compatibility-v3.ts` maps them correctly, so the v4 port had the
two branches swapped.

For `z.date().min(new Date('2020-01-01')).max(new Date('2030-01-01'))`:

- Before: `Date must be older than 2020-01-01T00:00:00.000Z (ISO), Date
must be newer than 2030-01-01T00:00:00.000Z (ISO)`
- After: `Date must be newer than 2020-01-01T00:00:00.000Z (ISO), Date
must be older than 2030-01-01T00:00:00.000Z (ISO)`

## Change

Swap the two messages in the v4 `defaultZodDateHandler` so they match
Zod semantics and the existing v3 handler, and rename the local
variables to match their actual bound.

## Tests

Added unit tests for the lower bound, the upper bound, and the combined
case, asserting both the correct phrasing and the absence of the
inverted phrasing. The Zod v4 check kinds were verified at runtime
(`min` yields `greater_than`, `max` yields `less_than`).

All new tests pass, the full `@mastra/schema-compat` suite passes (28
files), and typecheck and lint are clean.

Closes mastra-ai#18581

---

cc @TylerBarnes and @wardpeet who have worked on this file. Small fix,
happy to adjust the wording if you prefer different phrasing.


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

## ELI5
This update fixes date messages so they say the right thing. If a date
has a minimum, it now says it must be **newer** than that date; if it
has a maximum, it now says it must be **older** than that date.

### What changed
- Fixed `defaultZodDateHandler` in `schema-compatibility-v4.ts` so Zod
v4 date checks use the correct wording:
  - `.min()` → “Date must be newer than …”
  - `.max()` → “Date must be older than …”
- Kept the existing `Date format is date-time` text in generated
descriptions.
- Renamed local variables/comments to match the real lower/upper bound
meaning.
- Added tests covering:
  - lower-bound-only dates
  - upper-bound-only dates
  - combined `min` + `max` dates
  - correct phrasing and no inverted wording
- Added a changeset documenting the patch fix for
`@mastra/schema-compat`.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
caseyclements and others added 29 commits June 30, 2026 15:30
… vector domains (mastra-ai#18393)

Co-authored-by: Abhi Aiyer <abhiaiyer91@gmail.com>
Signed-off-by: Casey Clements <casey.clements@mongodb.com>
…tra-ai#18674)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ehindero Israel <ehindero2022@gmail.com>
Co-authored-by: Abhi Aiyer <abhi@mastra.ai>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…-ai#18556)

## Summary
- Add a metadata-only MessageList update path keyed by toolCallId.
- Use that path from background task onExecution hooks so lifecycle
metadata does not rewrite the model-visible tool invocation state.
- Cover the metadata-only behavior and durable background task hook with
focused tests.

Fixes mastra-ai#18551

## Tests
- pnpm build:core
- pnpm --filter ./packages/core test
src/agent/message-list/tests/update-tool-invocation.test.ts
- pnpm --filter ./packages/core test
src/agent/durable/workflows/steps/tool-call-bg.test.ts
- pnpm --filter ./packages/core check

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
When a background tool starts, the agent stores a “task started”
acknowledgement that the model should keep seeing. This PR saves the
background task bookkeeping (task id + timestamps) in message metadata
instead of overwriting the tool invocation itself, so the model-visible
tool result/ack doesn’t get lost.

## Summary
- Added `MessageList.updateMessageMetadataByToolCallId(toolCallId,
metadata)` to update only `content.metadata.backgroundTasks` on the
latest matching assistant `tool-invocation` (keyed by `toolCallId`),
merging tasks by `toolCallId` without changing the tool invocation
result/ack.
- Added `mergeBackgroundTasks(existing, incoming)` and reused it from
`updateToolInvocation(...)` so background-task metadata updates
deep-merge/preserve existing per-task fields instead of overwriting
them.
- Updated background-task `onExecution` hooks:
- Durable tool-call background workflow now records
`startedAt`/`suspendedAt`/`taskId` via
`updateMessageMetadataByToolCallId` and avoids downgrading/updating the
tool invocation state.
- Loop/tool-call background workflow similarly uses the metadata-only
update keyed by `toolCallId`.
- Expanded tests to cover:
- Metadata-only updates preserving the existing `tool-invocation` object
while updating only the targeted background task (and keeping unrelated
tasks intact).
- The durable `onExecution` path calling the metadata-only method (and
not `updateToolInvocation` in that scenario).
- `updateToolInvocation` deep-merging background-task metadata when
transitioning to `result`.
- Updated the `@mastra/core` changeset with a patch release documenting
the fix.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Ehindero Israel <ehindero2022@gmail.com>
Co-authored-by: Taofeeq Oluderu <oluderutaofeeq@gmail.com>
Co-authored-by: Ehindero Israel <ehindero@Ehinderos-MacBook-Pro.local>
Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
…astra-ai#18695)

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

Adds a new `structuredOutput.jsonPromptInjection: 'inline'` mode.

Before this PR, JSON prompt injection put the structured-output schema
instructions in the system prompt (`true` / `'system'`). That works, but
it changes the system prompt for each schema and can defeat provider
prompt caching.

This PR adds an inline mode that appends the JSON instructions to the
latest user message instead, keeping the leading system prompt stable
and more cache-friendly.

```ts
await agent.generate('Extract the task details', {
  structuredOutput: {
    schema: taskSchema,
    jsonPromptInjection: 'inline',
  },
});
```

Behavior by mode:

```ts
jsonPromptInjection: true      // inject into system prompt
jsonPromptInjection: 'system'  // inject into system prompt
jsonPromptInjection: 'inline'  // inject into latest user message
```

If there is no user message, inline mode adds a user message containing
the JSON instructions. Native `responseFormat` is still skipped whenever
prompt injection is enabled.

The structured-output fallback helpers preserve explicit `'inline'` /
`'system'` settings when retrying, instead of coercing every retry to
`true`.

## Test plan

- Stack rebase verified linear against mastra-ai#18651
- Covered by downstream memory/core checks in the stack

## Stack

- Previous: mastra-ai#18651
- Next: mastra-ai#18653


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This change lets the app add JSON “instructions” in a new “inline” way,
by attaching them to the user’s message instead of always putting them
in the system message. That helps keep the system prompt stable, which
can work better with providers that cache prompts.

## Summary
- Added support for `structuredOutput.jsonPromptInjection: 'inline'`
alongside `true` and `'system'`.
- Updated structured output handling so:
  - `true` / `'system'` inject JSON instructions into the system prompt
  - `'inline'` appends them to the latest user message
  - if there is no user message, inline mode creates one
- Kept native `responseFormat` disabled whenever prompt injection is
enabled.
- Preserved explicit `'inline'` and `'system'` settings during JSON
fallback retries.
- Added the new runtime feature flag `json-prompt-injection:inline`.
- Expanded tests to cover inline injection, system injection,
missing-user-message behavior, and feature flag exposure.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (crof/glm-5.2) <noreply@mastra.ai>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary

Adds the observational memory extractor pipeline.

Extractors let OM capture structured values alongside observations,
persist selected values into thread metadata, and carry extraction
failures through the OM lifecycle. Extractor instructions and schemas
can be static or resolved from runtime context, so extraction can adapt
to the active agent and thread.

```ts
observationalMemory: {
  enabled: true,
  observation: {
    extract: [
      new Extractor({
        slug: 'priority',
        name: 'Priority',
        schema: z.enum(['low', 'medium', 'high']),
        instructions: 'Extract the user priority when it changes.',
      }),
    ],
  },
}
```

## Test plan

- `pnpm --filter ./packages/memory test:unit -- --run
src/processors/observational-memory/__tests__/extractor.test.ts
src/processors/observational-memory/__tests__/observational-memory-api.test.ts
--reporter=dot --bail 1`
- `pnpm --filter ./packages/memory check`
- Stack rebase verified linear against mastra-ai#18652

## Stack

- Previous: mastra-ai#18652
- Next: mastra-ai#18654


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
ELI5: This PR makes “observational memory” not only remember what
happened, but also automatically pull out specific structured facts
(like a user’s priority), save them on the thread, and reuse them later.
It also adds an `Extractor` API so you can define how those facts are
detected, validated, and what happens when extraction fails.

### Summary
- Added a public `Extractor` API for Observational Memory with:
- **inline (XML/tag-based)** and **structured (Zod/schema-based)**
extraction modes
- slug validation, runtime-resolved/dynamic `instructions` and optional
`schema`
- per-extractor `onExtracted` hooks to normalize/transform values and
record failures safely.
- Extended Observational Memory configuration with extraction pipelines:
- `observationalMemory.observation.extract` and
`observationalMemory.reflection.extract`
- built-in extractors (current task, suggested response, thread title)
composed with user extractors.
- Implemented extraction end-to-end across observer/reflector:
- prompt generation includes extractor output sections and optional
“prior extracted values”
- parsing supports extracting values from model output while stripping
extractor sections
- structured extraction retries/fallback behavior is handled, and
extracted values are validated/merged.
- Persisted extracted values into thread observational memory metadata
under `threadOMMetadata.extracted`, including carry-forward of prior
extracted values into subsequent observer/reflector runs.
- Propagated extraction failures through the OM lifecycle (without
blocking successful extractions):
- added `extractionFailures` alongside `extractedValues` in OM end
markers and marker/telemetry payloads.
- Updated exports/types and documentation:
- new/extended types for `extractedValues` and `extractionFailures`
across OM configs, runners, and storage
- added Observational Memory docs sections for “Extractors” plus an
“Extractor API” reference.
- Added tests covering extractor logic and Observational Memory
persistence across:
- extractor unit behavior (inline/structured parsing, retries, hooks,
prompt composition)
- observational-memory API behavior (observe/reflect/buffer) and thread
metadata persistence
- marker schema changes and additional observational-memory lifecycle
assertions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (crof/glm-5.2) <noreply@mastra.ai>
Adds the first Mastra Code plugin system so users can install trusted
local or GitHub plugins and expose their tools inside the TUI.

Plugins can be a local path, or installed via a github url. When
installing from github it will clone it to the installed scope (project
or global) and MC will then periodically check if it needs to pull new
commits down.

For local plugins, it hot reloads tool changes, allowing MC to work on
its own tools via a plugin. It can modify the tool, call it, modify, etc
in a loop until it works properly.

```ts
import { defineMastraCodePlugin, createTool, z } from 'mastracode/plugin';

export default defineMastraCodePlugin({
  id: 'example.plugin',
  name: 'Example Plugin',
  tools: {
    example_tool: {
      tool: createTool({
        id: 'example_tool',
        description: 'Run an example plugin tool',
        inputSchema: z.object({ message: z.string() }),
        execute: async context => ({ message: context.message }),
      }),
    },
  },
});
```

Plugins can define tools, optional config, render hints, bundled slash
commands and skills, and plugin instructions. The `/plugins` UI handles
install, scaffold, details, config, enable/disable, and local/GitHub
source management. Local plugin edits reload at execution time, and
GitHub checkouts poll for updates while preserving local changes on
backup branches before resetting.

This also adds project-level plugin blocking, progress streaming for
plugin tools, subagent-style rendering for plugin tools that ask for it,
and a README note that plugins should only be installed from trusted
sources.

Smoke tested with Alexandria: the expert tool executes, config persists,
and bundled command/skill loading was tested earlier. Focused unit
tests, typecheck, and `pnpm build:mastracode` pass.

example plugin:
https://github.com/mastra-ai/alexandria/blob/main/.mastracode/plugins/sources/local/alexandria/src/index.ts#L101

<img width="675" height="103" alt="Screenshot 2026-06-29 at 5 07 24 PM"
src="https://github.com/user-attachments/assets/74db5628-e546-421b-9916-f68a9eed281d"
/>
<img width="890" height="328" alt="Screenshot 2026-06-29 at 5 07 39 PM"
src="https://github.com/user-attachments/assets/9b3c6add-1d98-4583-8e74-0a0f9fdbe99d"
/>
<img width="721" height="336" alt="Screenshot 2026-06-29 at 5 07 34 PM"
src="https://github.com/user-attachments/assets/f2d3e0be-c9b8-41ed-9208-56ff82f380ed"
/>
<img width="564" height="506" alt="Screenshot 2026-06-29 at 5 07 44 PM"
src="https://github.com/user-attachments/assets/6f082267-52c5-43a0-98af-3b3e06d4f582"
/>

<img width="2530" height="1156" alt="image (34)"
src="https://github.com/user-attachments/assets/e964bf8c-dbd3-4ead-b25e-3ea848ac93bf"
/>



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR adds a way for Mastra Code to load “trusted plugin” code so it
can learn new tools and commands. You can install plugins from your
computer or GitHub, manage them in the terminal UI, and see their tool
progress update live—plus reload plugins automatically when local code
changes or GitHub has updates.

## Summary
- Introduces the first Mastra Code plugin system:
- Public plugin API/types (`defineMastraCodePlugin`) and
`writeToolProgress` support
  - `.mastracode-plugin.json` manifest handling
- Local + GitHub install, local discovery, scoped plugin registries
(load/merge/save), and a full plugin loader pipeline
- A `PluginManager` that tracks active plugins, supports reloads, and
handles local hot-reload + GitHub polling/update with backup/dirtiness
safety
  - Project-level plugin blocking via `disabledPlugins`

- Wires plugins into the app runtime so plugin contributions work
everywhere:
- Merges plugin tool names into mode allowlists and dynamically builds
toolsets from loaded plugin tools
  - Appends plugin-provided instructions into generated agent prompts
- Exposes plugin-provided assets via TUI/session state
(skills/commands/instructions), and treats plugin command dirs as extra
high-priority slash-command sources

- Adds `/plugins` TUI management with end-to-end flows:
- Install new plugins, scaffold plugin projects, view details, configure
plugin config values (including model selection + API key prompting),
enable/disable, uninstall
- Install-source management (local path vs GitHub URL) with
trust-confirmation guidance
- Includes block/disabled handling in the UI and behavior
(hidden/conflicted/blocked states)

- Improves plugin tool UX in chat/TUI:
- Streams plugin tool progress from core → TUI using
`data-mastracode-tool-progress`, emitting tool updates and rendering
progress output
- Adds “subagent-style” rendering for tools that request it, including
static subagent component handling and replay for previously stored tool
calls
  - Supports local hot reloading and GitHub polling/update scenarios

- Tests + release notes:
- Adds unit tests for plugin loader/manager/registry/scaffold, plugin
instruction generation, tool-progress streaming, and tool rendering
precedence
  - Adds TUI unit tests for `/plugins`
- Adds E2E fixtures covering bundled commands/skills, tool streaming,
local hot reload, GitHub poll updates, blocking/config behavior, and
scaffold/install/execute flows
- Updates docs and adds a changeset publishing “Mastra Code” plugin
support.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (openai/gpt-5.5) <noreply@mastra.ai>
Adds lightweight timing feedback to the Mastra Code TUI so it is easier
to tell how long the agent has been working and how long it has been
idle.

Added e2e test and also smoke tested manually in the TUI: active timing,
completed timing, and delayed idle display all looked good.

<img width="343" height="129" alt="Screenshot 2026-06-29 at 10 27 52 AM"
src="https://github.com/user-attachments/assets/adb2118f-37f2-468f-8186-9995ff853366"
/>
<img width="410" height="125" alt="Screenshot 2026-06-29 at 10 27 56 AM"
src="https://github.com/user-attachments/assets/837dded7-9437-423b-8689-1dd12ea62ede"
/>
<img width="353" height="118" alt="Screenshot 2026-06-29 at 10 28 03 AM"
src="https://github.com/user-attachments/assets/efd570c1-c37c-4bfd-acbc-daed76e781f7"
/>
<img width="449" height="205" alt="Screenshot 2026-06-29 at 10 28 45 AM"
src="https://github.com/user-attachments/assets/5feb216d-dea7-4db3-820f-5f2ba311af7d"
/>
<img width="397" height="125" alt="Screenshot 2026-06-29 at 11 23 58 AM"
src="https://github.com/user-attachments/assets/e3f46809-bdcd-4e1f-ac9d-013115221659"
/>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR adds small “how long” timers to the Mastra Code terminal UI.
You’ll see how long the agent has been working, how long the last run
took when it finishes, and how long it’s been waiting/idle.

## Summary
- **Status line timing for agent runs**
- Shows **active elapsed time** while the agent is running (with
“stale”/delayed styling when the stream hasn’t updated recently).
- Shows **completed duration** with a **✓** for success and **×** for
errors (and no success icon for intentional/“aborted” outcomes).
- **Idle display driven by the last completed run**
- Idle text now derives from **`lastAgentRunEndedAt`** (and minimum
thresholds), rendering **“X idle”** after sufficient idle time.
- **Shared timing formatting utilities**
- Added `formatStatusDuration(ms)` plus new idle formatting helpers to
consistently render durations like **`1m1s`**, **`1m`**, and **`1m
idle`**.
- **State/timer wiring updates in the TUI**
- Added/used timing fields (`agentRunStartedAt`,
`agentRunLastStreamPartAt`, `lastAgentRunDurationMs`,
`lastAgentRunEndedAt`, `lastAgentRunEndReason`) and ensured the status
line is re-rendered at the right event boundaries.
- Replaced the prior idle-counter ticker with a unified **status timing
timer** that updates both active and idle timing and pauses/resumes
appropriately around message sending.
- **E2E/test support + scenario**
- Added an E2E hook (`onTuiCreated`) so scenarios can access the created
TUI instance.
  - Added a new `work-idle-status` scenario + fixture to verify:
    - active timing appears,
    - completed timing appears,
- idle timing transitions to **“1m idle”** after advancing simulated
time.
- **UI/behavior polish**
- Adjusted agent-abort handling so intentional aborts don’t produce
redundant “interrupted” error UI.

## Testing
- Manual smoke testing in the TUI (active timing, completed timing, and
delayed idle display).
- Added/updated unit tests for status-line timing rendering, idle timing
formatting/component API, stream-activity timing updates, and abort UI
behavior.
- Added an end-to-end TUI terminal scenario (`work-idle-status`) with a
new fixture to validate the timing labels and idle transition.
- Updated the `mastracode` changeset for a patch release describing the
new status-area timing behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

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

Adds a built-in WorkingMemoryExtractor and a simpler OM-managed working
memory setup.

With `manageWorkingMemory`, OM can extract working-memory updates from
observations and apply them after the turn. The sugar defaults working
memory to prompt-cache-friendly state signals and disables direct agent
tool management unless the user opts back in.

```ts
new Memory({
  options: {
    observationalMemory: {
      enabled: true,
      observation: {
        manageWorkingMemory: true,
        // extractors: [new WorkingMemoryExtractor()] <- added internally by manageWorkingMemory: true
      },
    },
    workingMemory: {
      enabled: true,
      // these are configured internally by observation.manageWorkingMemory: true
      // agentManaged: false, <- disables the agent managing working memory via tool calls and removes system instructions saying to do so
      // useStateSignals: true,
    },
  },
});
```



Users who still want both paths can opt in explicitly:

```ts
workingMemory: {
  enabled: true,
  agentManaged: true
}
```

<img width="1013" height="889" alt="Screenshot 2026-06-29 at 3 54 07 PM"
src="https://github.com/user-attachments/assets/cccb97dd-ac5c-4898-b78a-f84f6cdd3bb0"
/>
<img width="1015" height="871" alt="Screenshot 2026-06-29 at 3 25 57 PM"
src="https://github.com/user-attachments/assets/7d06499f-45dc-443a-ac28-f1aa65f4ceb9"
/>


## Test plan

- `pnpm --filter ./packages/memory test:unit -- --run
src/processors/observational-memory/__tests__/extractor.test.ts
src/processors/observational-memory/__tests__/observational-memory-api.test.ts
--reporter=dot --bail 1`
- `pnpm --filter ./packages/memory check`
- Stack rebase verified linear against mastra-ai#18653

## Stack

- Previous: mastra-ai#18653
- Next: mastra-ai#18655


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## ELI5
This PR helps Observational Memory automatically “catch” what changed in
your app’s working memory as it watches the conversation, then apply
those updates for the agent after the turn. So the agent doesn’t have to
remember to call the working-memory tool every time—and the default
setup is tuned to stay friendly to prompt caching.

## Summary
- Added a built-in `WorkingMemoryExtractor` and re-exported it from the
Observational Memory module.
- Introduced `observationalMemory.observation.manageWorkingMemory` to
let Observational Memory extract working-memory updates and apply them
after the turn.
- Added `workingMemory.agentManaged` to control whether the main agent
still receives working-memory tool/instructions (`agentManaged` defaults
to `true` when not overridden).
- When OM manages working memory (`manageWorkingMemory: true` with
`workingMemory.enabled: true`), defaults change to:
- `workingMemory.agentManaged: false` (OM applies updates instead of the
agent)
  - `workingMemory.useStateSignals: true` (prompt-cache-friendly)
- setting `workingMemory.agentManaged: true` preserves the “agent also
manages it” path.
- Updated memory behavior for tool exposure, system-message/tool
instruction wording, and merged thread config defaults to match the new
working-memory management rules.
- Extended unit/integration tests to verify extraction and persistence
for both markdown and JSON/schema-backed working memory modes, plus
`listTools()`/`getSystemMessage()`/merged-config behavior under the new
options.
- Updated documentation and a changeset to document the new
configuration flow and defaults (`manageWorkingMemory`, `agentManaged`,
and `useStateSignals`).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Mastra Code (crof/glm-5.2) <noreply@mastra.ai>
Add a new @mastra/mesa workspace filesystem package backed by Mesa repos.

Document the provider, include focused unit and integration test coverage, and add the release changeset.
@warrenbhw
warrenbhw force-pushed the warrenbhw/mesa-filesystem-integration branch from 909923d to 66036b2 Compare June 30, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.