@mastra/core@1.35.0

mastra-ai/mastra@mastra/core@1.35.0May 18, 2026by PaulieScanlon

AI Summary

This release introduces FGA route policy coverage controls and resolver hooks, a new favorites storage domain for agents and skills, and improved token estimation for large file parts in Observational Memory. It also includes reliability fixes for workspaces, workflows, and route matching.

Key Highlights

  • FGA Route Policy Coverage + Resolver Hooks (Core/Server/WorkOS)
  • Favorites Storage Domain for Agents/Skills (Core + Storage Adapters)
  • Favorites Support Across Major Storage Backends
  • Observational Memory: Accurate Token Estimation for Large File Parts
  • Reliability Fixes for Workspaces, Workflows, and Route Matching

New Features

  • FGA route policy coverage and resolver hooks
  • FavoritesStorage domain with favorite/unfavorite functionality
  • Token estimation for non-image file parts (PDFs) using provider heuristics
  • Workspace PATCH bug fix (omitted fields no longer overwrite persisted values)
  • Scheduled workflow fixes and circular ESM import crash resolution

Full Release Notes

## Highlights

### FGA Route Policy Coverage + Resolver Hooks (Core/Server/WorkOS)
New FGA route policy coverage controls add guardrails for protected routes, with built-in resource route metadata resolution and resolver hooks so you can centrally map route → `{resourceType, resourceId, permission}` while still overriding per-route config.

### Favorites Storage Domain for Agents/Skills (Core + Storage Adapters)
A new `favorites` storage domain lets users favorite/unfavorite stored agents and skills, and adds `visibility` (`private|public`) and `favoriteCount` fields so listings can be filtered and ordered by favorite state (e.g., `pinFavoritedFor`, `favoritedOnly`).

### Favorites Support Across Major Storage Backends
Favorites is implemented across multiple adapters (@mastra/pg, @mastra/libsql, @mastra/mongodb, @mastra/clickhouse, @mastra/cloudflare), enabling consistent favoriting/visibility queries regardless of your chosen persistence layer.

### Observational Memory: Accurate Token Estimation for Large File Parts
`@mastra/memory` improves local token estimation for non-image file parts (notably large PDFs), preventing observational memory from missing thresholds and replaying excessive history when provider token-count endpoints aren’t available.

### Reliability Fixes for Workspaces, Workflows, and Route Matching
Multiple fixes improve correctness: workspace PATCH no longer overwrites stored values with `undefined`, scheduled workflows created via `@mastra/core/workflows` apply schedules correctly, NestJS adapter prefix matching is stricter, and a circular ESM import crash in `@mastra/core/workflows/workflow` is resolved.

### Breaking Changes
- None called out in this changelog.

## Changelog

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

- Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. ([#16485](https://github.com/mastra-ai/mastra/pull/16485))

  For example:

  ```ts
  import { MastraFGAWorkos } from '@mastra/auth-workos';
  import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee';
  import { createRoute } from '@mastra/server/server-adapter';

  const routeFGA = {
    'GET /billing/:accountId': {
      resourceType: 'account',
      resourceIdParam: 'accountId',
      permission: 'billing:read',
    },
  } satisfies Record<string, FGARouteConfig>;

  const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];

  const fga: IFGAProvider = new MastraFGAWorkos({
    apiKey: process.env.WORKOS_API_KEY!,
    clientId: process.env.WORKOS_CLIENT_ID!,
    requireForProtectedRoutes: true,
    auditProtectedRoutes: 'warn',
    resolveRouteFGA,
    validatePermissions: async permissions => {
      /* validate mappings */
    },
  });

  export const getProjectRoute = createRoute({
    method: 'GET',
    path: '/projects/:projectId',
    responseType: 'json',
    requiresAuth: true,
    fga: {
      resourceType: 'project',
      resourceIdParam: 'projectId',
      permission: 'projects:read',
    },
    handler: async () => {
      return { project: null };
    },
  });
  ```

- Added a favorites storage domain that lets users mark stored agents and skills as favorites, plus `visibility` (`'private' | 'public'`) and `favoriteCount` fields on stored agents and skills so callers can list, filter, and order by favorite state. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

  Existing rows without `visibility` or `favoriteCount` continue to work; the new fields and APIs are opt-in.

  **Example**

  ```ts
  const favorites = await storage.getStore('favorites');

  await favorites?.favorite({ userId: 'u1', entityType: 'agent', entityId: 'agent-123' });

  const favoritedIds = await favorites?.listFavoritedIds({ userId: 'u1', entityType: 'agent' });

  // List agents the user has favorited, surfaced first
  const { agents } = await storage.getStore('agents').list({
    pinFavoritedFor: 'u1',
    favoritedOnly: true,
  });
  ```

#### Patch Changes

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

- Fixed a workspace PATCH bug in the inmemory workspace adapter: omitted config fields in a PATCH no longer overwrite previously-persisted values with `undefined`. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

- Fixed active signals so they stay in the correct order when conversations are reloaded from memory. ([#16623](https://github.com/mastra-ai/mastra/pull/16623))

- Fixed scheduled workflows created from the public @mastra/core/workflows entry point so declared schedules are applied correctly. ([#16637](https://github.com/mastra-ai/mastra/pull/16637))

- Fixed a crash when importing `@mastra/core/workflows/workflow` from tests or apps, which previously failed with `TypeError: Class extends value undefined is not a constructor or null` (caused by a circular ESM import through the `workflows` barrel). ([#16661](https://github.com/mastra-ai/mastra/pull/16661))

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

- Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. ([#16485](https://github.com/mastra-ai/mastra/pull/16485))

  For example:

  ```ts
  import { MastraFGAWorkos } from '@mastra/auth-workos';
  import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee';
  import { createRoute } from '@mastra/server/server-adapter';

  const routeFGA = {
    'GET /billing/:accountId': {
      resourceType: 'account',
      resourceIdParam: 'accountId',
      permission: 'billing:read',
    },
  } satisfies Record<string, FGARouteConfig>;

  const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];

  const fga: IFGAProvider = new MastraFGAWorkos({
    apiKey: process.env.WORKOS_API_KEY!,
    clientId: process.env.WORKOS_CLIENT_ID!,
    requireForProtectedRoutes: true,
    auditProtectedRoutes: 'warn',
    resolveRouteFGA,
    validatePermissions: async permissions => {
      /* validate mappings */
    },
  });

  export const getProjectRoute = createRoute({
    method: 'GET',
    path: '/projects/:projectId',
    responseType: 'json',
    requiresAuth: true,
    fga: {
      resourceType: 'project',
      resourceIdParam: 'projectId',
      permission: 'projects:read',
    },
    handler: async () => {
      return { project: null };
    },
  });
  ```

#### Patch Changes

- Fixed WorkOS FGA missing resources so authorization checks deny access instead of surfacing provider errors. ([#16485](https://github.com/mastra-ai/mastra/pull/16485))

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

- Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

  **Example**

  ```ts
  const storage = new LibSQLStore({
    /* config */
  });
  const favorites = await storage.getStore('favorites');

  await favorites?.favorite({
    userId: 'user-1',
    entityType: 'agent',
    entityId: 'agent-42',
  });
  ```

#### Patch Changes

- Bumped `@mastra/core` peer dependency floor to `>=1.34.0-0` so the new `@mastra/core/storage/domains/favorites` subpath is available. Older `@mastra/core` versions don't ship the `FavoritesStorage` base class these adapters now extend. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

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

- Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

  **Example**

  ```ts
  const storage = new LibSQLStore({
    /* config */
  });
  const favorites = await storage.getStore('favorites');

  await favorites?.favorite({
    userId: 'user-1',
    entityType: 'agent',
    entityId: 'agent-42',
  });
  ```

#### Patch Changes

- Bumped `@mastra/core` peer dependency floor to `>=1.34.0-0` so the new `@mastra/core/storage/domains/favorites` subpath is available. Older `@mastra/core` versions don't ship the `FavoritesStorage` base class these adapters now extend. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

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

- Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

  **Example**

  ```ts
  const storage = new LibSQLStore({
    /* config */
  });
  const favorites = await storage.getStore('favorites');

  await favorites?.favorite({
    userId: 'user-1',
    entityType: 'agent',
    entityId: 'agent-42',
  });
  ```

#### Patch Changes

- Bumped `@mastra/core` peer dependency floor to `>=1.34.0-0` so the new `@mastra/core/storage/domains/favorites` subpath is available. Older `@mastra/core` versions don't ship the `FavoritesStorage` base class these adapters now extend. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

- Fixed a workspace PATCH bug: omitted config fields in a PATCH no longer overwrite previously-persisted values with `undefined`. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

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

- Fixed thread deletion so it also clears thread-scoped observational memory. ([#16628](https://github.com/mastra-ai/mastra/pull/16628))

- Fixed Observational Memory missing the observation threshold for messages with large file parts. Previously `TokenCounter`'s local/sync counting path only stringified the file descriptor (`type`, `mimeType`, `filename`) for non-image files, so a 100KB PDF looked like ~8 tokens to OM and the conversation kept replaying the full unobserved history past every reasonable threshold. ([#16562](https://github.com/mastra-ai/mastra/pull/16562))

  `TokenCounter` now estimates non-image file part tokens locally from the attachment's byte size and mime type using a per-provider heuristic, mirroring the existing local image-token estimator:
  - Anthropic PDFs ≈ `bytes / 3` (floor 1500)
  - Google PDFs ≈ `bytes / 20` (floor 258)
  - OpenAI / unknown PDFs ≈ `bytes / 4` (floor 500)
  - Text-ish mime types (`text/*`, JSON, XML, YAML) ≈ `bytes / 4`
  - Other binary ≈ `bytes / 4`

  URL-only file parts (no body to size) keep the previous descriptor-only local estimate. `countMessagesAsync()` continues to prefer provider token-count endpoints for supported providers; this change only improves the local fallback used when no provider endpoint is available.

  ```ts
  // Before: this PDF counted as ~8 tokens locally regardless of size, so OM never triggered.
  const part = {
    type: 'file',
    data: largePdfBase64,
    mimeType: 'application/pdf',
    filename: 'report.pdf',
  };
  // counter.countMessage(message) ≈ 8

  // After: estimated locally from byte size on the active provider.
  // counter.countMessage(message) ≈ tens of thousands of tokens
  //   → OM threshold trips as expected.
  ```

  The internal token-estimate cache version was bumped, which invalidates persisted estimates from older `@mastra/memory` releases on the next read; entries are recomputed automatically.

  Fixes https://github.com/mastra-ai/mastra/issues/16522

### [@mastra/mongodb@1.9.0](https://github.com/mastra-ai/mastra/blob/@mastra/mongodb@1.9.0/stores/mongodb/CHANGELOG.md)
#### Minor Changes

- Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

  **Example**

  ```ts
  const storage = new LibSQLStore({
    /* config */
  });
  const favorites = await storage.getStore('favorites');

  await favorites?.favorite({
    userId: 'user-1',
    entityType: 'agent',
    entityId: 'agent-42',
  });
  ```

#### Patch Changes

- Bumped `@mastra/core` peer dependency floor to `>=1.34.0-0` so the new `@mastra/core/storage/domains/favorites` subpath is available. Older `@mastra/core` versions don't ship the `FavoritesStorage` base class these adapters now extend. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

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

- Fixed NestJS route matching so configured prefixes are enforced and partial prefix matches are ignored. For example, a prefix of `/api` no longer matches `/apiish/agents`; only `/api` and `/api/*` are treated as Mastra routes. ([#16637](https://github.com/mastra-ai/mastra/pull/16637))

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

- Added favorites support to storage adapters so callers can favorite/unfavorite stored agents and skills, query favorite state alongside list results, and filter listings by visibility. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

  **Example**

  ```ts
  const storage = new LibSQLStore({
    /* config */
  });
  const favorites = await storage.getStore('favorites');

  await favorites?.favorite({
    userId: 'user-1',
    entityType: 'agent',
    entityId: 'agent-42',
  });
  ```

#### Patch Changes

- Fixed a workspace PATCH bug: omitted config fields in a PATCH no longer overwrite previously-persisted values with `undefined`. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

- Bumped `@mastra/core` peer dependency floor to `>=1.34.0-0` so the new `@mastra/core/storage/domains/favorites` subpath is available. Older `@mastra/core` versions don't ship the `FavoritesStorage` base class these adapters now extend. ([#16580](https://github.com/mastra-ai/mastra/pull/16580))

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

- Fixed the Observability traces page on storage providers that don't support the Branches list mode. The page now falls back to Traces mode, hides the Branches option in the filter, and shows a dismissible notice instead of a full-screen error. ([#16601](https://github.com/mastra-ai/mastra/pull/16601))

- Improved the Observability traces list to make the with-subtraces view more discoverable. ([#16643](https://github.com/mastra-ai/mastra/pull/16643))
  - **Added:** A **Level** column whose icon distinguishes top-level **Trace** rows from nested **Subtrace** rows.
  - **Added:** A tooltip legend on the **Level** header showing both icons side by side.
  - **Added:** A standalone **Show subtraces** toggle next to **Add Filter** — off keeps the default top-level view, on includes subtraces.
  - **Removed:** The **List mode** entry from the **Add Filter** menu (now driven by the toggle).

  **Usage:** Open Observability → Traces → switch **Show subtraces** on. The **Level** column updates: top-level rows keep the Trace icon, nested rows show the Subtrace (↳) icon. The toggle is hidden automatically when the active storage provider doesn't support subtraces.

- Removed the "Group traces by thread" option from the Observability traces page. The list now always displays a flat view of traces, without thread-id subheaders. To narrow results to a specific thread, use the Thread ID property in the Add filter menu (open Observability → Traces → Add filter → Thread ID → paste the threadId). ([#16633](https://github.com/mastra-ai/mastra/pull/16633))

- Polished the property filter dropdown so the chevron icon next to each option keeps the same shape when its side panel opens ([#16635](https://github.com/mastra-ai/mastra/pull/16635))

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

- Added FGA route policy coverage controls, built-in resource route metadata resolution, and resolver hooks. ([#16485](https://github.com/mastra-ai/mastra/pull/16485))

  For example:

  ```ts
  import { MastraFGAWorkos } from '@mastra/auth-workos';
  import type { FGARouteConfig, FGARouteResolver, IFGAProvider } from '@mastra/core/auth/ee';
  import { createRoute } from '@mastra/server/server-adapter';

  const routeFGA = {
    'GET /billing/:accountId': {
      resourceType: 'account',
      resourceIdParam: 'accountId',
      permission: 'billing:read',
    },
  } satisfies Record<string, FGARouteConfig>;

  const resolveRouteFGA: FGARouteResolver = ({ route }) => routeFGA[`${route.method} ${route.path}`];

  const fga: IFGAProvider = new MastraFGAWorkos({
    apiKey: process.env.WORKOS_API_KEY!,
    clientId: process.env.WORKOS_CLIENT_ID!,
    requireForProtectedRoutes: true,
    auditProtectedRoutes: 'warn',
    resolveRouteFGA,
    validatePermissions: async permissions => {
      /* validate mappings */
    },
  });

  export const getProjectRoute = createRoute({
    method: 'GET',
    path: '/projects/:projectId',
    responseType: 'json',
    requiresAuth: true,
    fga: {
      resourceType: 'project',
      resourceIdParam: 'projectId',
      permission: 'projects:read',
    },
    handler: async () => {
      return { project: null };
    },
  });
  ```

#### Patch Changes

### Other updated packages

The following packages were updated with dependency changes only:

- [@mastra/agent-builder@1.0.36](https://github.com/mastra-ai/mastra/blob/@mastra/agent-builder@1.0.36/packages/agent-builder/CHANGELOG.md)
- [@mastra/client-js@1.19.1](https://github.com/mastra-ai/mastra/blob/@mastra/client-js@1.19.1/client-sdks/client-js/CHANGELOG.md)
- [@mastra/deployer@1.35.0](https://github.com/mastra-ai/mastra/blob/@mastra/deployer@1.35.0/packages/deployer/CHANGELOG.md)
- [@mastra/deployer-cloud@1.35.0](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-cloud@1.35.0/deployers/cloud/CHANGELOG.md)
- [@mastra/deployer-cloudflare@1.1.36](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-cloudflare@1.1.36/deployers/cloudflare/CHANGELOG.md)
- [@mastra/deployer-netlify@1.1.12](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-netlify@1.1.12/deployers/netlify/CHANGELOG.md)
- [@mastra/deployer-vercel@1.1.30](https://github.com/mastra-ai/mastra/blob/@mastra/deployer-vercel@1.1.30/deployers/vercel/CHANGELOG.md)
- [@mastra/editor@0.8.1](https://github.com/mastra-ai/mastra/blob/@mastra/editor@0.8.1/packages/editor/CHANGELOG.md)
- [@mastra/express@1.3.22](https://github.com/mastra-ai/mastra/blob/@mastra/express@1.3.22/server-adapters/express/CHANGELOG.md)
- [@mastra/fastify@1.3.22](https://github.com/mastra-ai/mastra/blob/@mastra/fastify@1.3.22/server-adapters/fastify/CHANGELOG.md)
- [@mastra/hono@1.4.17](https://github.com/mastra-ai/mastra/blob/@mastra/hono@1.4.17/server-adapters/hono/CHANGELOG.md)
- [@mastra/koa@1.5.5](https://github.com/mastra-ai/mastra/blob/@mastra/koa@1.5.5/server-adapters/koa/CHANGELOG.md)
- [@mastra/longmemeval@1.0.41](https://github.com/mastra-ai/mastra/blob/@mastra/longmemeval@1.0.41/explorations/longmemeval/CHANGELOG.md)
- [@mastra/mcp-docs-server@1.1.38](https://github.com/mastra-ai/mastra/blob/@mastra/mcp-docs-server@1.1.38/packages/mcp-docs-server/CHANGELOG.md)
- [@mastra/opencode@0.0.38](https://github.com/mastra-ai/mastra/blob/@mastra/opencode@0.0.38/integrations/opencode/CHANGELOG.md)
- [@mastra/react@0.3.3](https://github.com/mastra-ai/mastra/blob/@mastra/react@0.3.3/client-sdks/react/CHANGELOG.md)
- [@mastra/temporal@0.1.5](https://github.com/mastra-ai/mastra/blob/@mastra/temporal@0.1.5/workflows/temporal/CHANGELOG.md)