github mastra-ai/mastra @mastra/core@1.28.0
April 24, 2026

latest release: @mastra/core@1.26.0
10 hours ago

Highlights

Modal Cloud Sandbox Provider (@mastra/modal)

New @mastra/modal adds a Modal-backed ModalSandbox for running workspace commands in an isolated cloud environment with pause/resume support—expanding Mastra’s deployment/execution options beyond local sandboxes.

Per-request Workspace Filesystem Resolver (Multi-tenant Routing)

Workspace’s filesystem option now supports a resolver function, enabling per-request filesystem selection/routing from a single Workspace instance—useful for multi-tenant setups and scoped permissions without spinning up multiple Workspaces.

Custom Language Server Registration in LSPConfig

You can now register additional language servers via lsp.servers (and override built-ins by ID), unlocking LSP-based inspection for languages beyond the default set (e.g., PHP, Ruby, Java, Kotlin, Swift, Elixir).

Vector Search “Works Out of the Box” (Indexing + Large File Chunking)

Workspace file indexing now auto-creates vector indices where required (e.g., LibSQL) and splits large files into overlapping chunks instead of skipping them—preventing empty vector stores and making search reliable with autoIndexPaths.

Streaming & Observational Memory Reliability + Better Usage Introspection

Multiple fixes prevent duplicated/replayed messages and tool outputs when observational memory is enabled (including disabling savePerStep in Harness in this mode), and agent.stream() callbacks now preserve provider-specific usage.raw so you can access cache metrics without wrapping streams.

Breaking Changes

  • None noted in this changelog.

Changelog

@mastra/core@1.28.0

Minor Changes

  • The Workspace filesystem option now accepts a resolver function in addition to a static instance. (#13150)

    Before: filesystem: WorkspaceFilesystem (static, same filesystem for every request)
    After: filesystem: WorkspaceFilesystem | (({ requestContext }) => WorkspaceFilesystem) (static or per-request)

    This enables per-request filesystem routing from a single Workspace — useful for multi-tenant setups, role-based access (e.g. admin vs user directories), and scoped filesystem permissions without creating separate Workspace instances.

  • Added support for custom language server registration with the servers field in LSPConfig. Previously, LSP inspection only worked with built-in server definitions for TypeScript, JavaScript, Python, Go, and Rust. You can now register additional language servers, such as PHP, Ruby, Java, Kotlin, Swift, or Elixir, by providing a CustomLSPServer definition. (#14969)

    Example:

    const workspace = new Workspace({
      lsp: {
        servers: {
          phpactor: {
            id: 'phpactor',
            name: 'Phpactor Language Server',
            languageIds: ['php'],
            extensions: ['.php'],
            markers: ['composer.json'],
            command: 'phpactor language-server',
          },
        },
      },
    });

    Custom servers are merged with built-in servers and can also override them by using the same ID. Closes #14828.

Patch Changes

  • Update provider registry and model documentation with latest models and providers (733bf53)

  • Fixed output processors returning undefined from processOutputStream causing an undefined chunk to be enqueued into the consumer stream. A processor that forgets to return part (or explicitly returns undefined) now drops that chunk, matching existing null behavior, instead of emitting a bogus value to downstream readers. (#15674)

    // Before: returning undefined emitted { value: undefined, done: false } to consumers
    // After:  returning undefined drops the chunk, same as returning null
    const processor = {
      id: 'my-processor',
      processOutputStream: async ({ part }) => {
        if (shouldDrop(part)) return; // implicit undefined — now safely dropped
        return part;
      },
    };
  • Fixed streamed tool results being replayed when observational memory runs mid-stream. (#15701)
    Fixed observational memory markers being saved as separate empty assistant messages.

  • Fixed false positive provider change detection in observational memory. Message metadata now uses the configured model ID instead of the API response model ID, ensuring consistency with step-start parts and preventing incorrect 'Model changed' activations when the provider returns versioned model names (e.g., gpt-5.4-2026-03-05 vs gpt-5.4). (#15681)

  • Fixed interaction between savePerStep and observational memory that caused message duplication. The saveStepMessages method redundantly re-added response messages to the message list on every step, duplicating them. Additionally, savePerStep is now force-disabled when observational memory is enabled, since OM handles its own per-step persistence and the two features conflict. (#15684)

  • Fixed rotated response message ids not propagating to the active output stream after error processor retries, which could split a single response across two ids on the API-error retry path. (#15702)

    Fixed processor-supplied options to writer.custom being dropped in the agentic execution step, so future options like transient now reach the underlying output writer.

  • Fixed agent.stream() callbacks so that onStepFinish and onFinish now preserve the provider-level usage.raw object on LanguageModelUsage. This lets consumers inspect provider-specific cache metrics (e.g., Anthropic and Bedrock prompt caching) directly from the callback payload without having to wrap the stream. (#15546)

    Closes #15510.

  • Add opt-in checkSkillFileMtime option to detect in-place SKILL.md edits during hot reload. (#15676)

    Previously, only directory mtime was checked for skill staleness, so editing a skill's name (to fix a validation error) or updating its description wouldn't trigger re-discovery until server restart.

    The option is off by default since it doubles stat() calls per skill during staleness checks. Recommended for local development only, not for cloud storage backends where stat() has higher latency.

    const myAgent = new Agent({
      workspace: {
        filesystem: new LocalFilesystem({ basePath: process.cwd() }),
        skills: ['./**/skills'],
        checkSkillFileMtime: true, // Enable for local dev
      },
    });
  • Added filterIncompleteToolCalls option to memory config. When set to false, suspended tool calls remain visible in the agent's prompt context, allowing the agent to see its own pending interactions in thread history. Defaults to true (current behavior). Useful for suspend/resume patterns with providers that support incomplete tool calls (e.g. Anthropic). (#14721)

  • Fixed workspace file indexing so vector search works out of the box. (#15011)

    • Large files that exceeded the embedding model token limit were previously silently skipped, leaving the vector store empty and causing search failures. Large files are now split into overlapping line-based chunks, each indexed separately with correct line-range tracking back to the original file.
    • The vector index is now created automatically before the first upsert. Previously, backends that require an explicit createIndex call (e.g. LibSQL) would leave the table uncreated, causing no such table errors on search. Workspaces with vectorStore + embedder + autoIndexPaths configured now work without any manual setup.
  • Disable savePerStep in Harness to prevent duplicate messages when observational memory is enabled (#15684)

    The savePerStep option in Harness caused message duplication when used alongside observational memory. This change temporarily disables savePerStep in the Harness runtime while we work on a permanent fix.

@mastra/agent-browser@0.2.1

Patch Changes

  • Standardize headless default to true across all browser providers. Each provider now resolves headless once in its constructor and passes it to the thread manager via the base class getter, removing duplicate fallback logic. (#15696)

  • Fixed browser_evaluate so expression scripts now return their computed value instead of undefined (for example, document.querySelectorAll('a').length). (#15689)

@mastra/browser-viewer@0.1.1

Patch Changes

  • Remove unused userDataDir config option from BrowserViewerConfig. (#15696)

  • Standardize headless default to true across all browser providers. Each provider now resolves headless once in its constructor and passes it to the thread manager via the base class getter, removing duplicate fallback logic. (#15696)

@mastra/gcs@0.2.1

Patch Changes

  • Fix toKey() to resolve "." and "./" as the root path (#14824)

    Both GCSFilesystem and S3Filesystem produced invalid object keys when called with path: "." (e.g. prefix/. instead of prefix/). Since the built-in mastra_workspace_list_files tool and Mastra Studio both default to path: ".", workspace directory listings returned empty results when backed by GCS or S3.

    toKey() now normalises "." and "./" to empty string before prepending the prefix, matching the existing behaviour of "/". Dotfiles like .env or .gitignore are unaffected.

@mastra/mcp@1.5.2

Patch Changes

  • Replace uuid with @lukeed/uuid and node:crypto (#15691)

@mastra/mcp-registry-registry@1.0.1

Patch Changes

  • Replace uuid with @lukeed/uuid and node:crypto (#15691)

@mastra/memory@1.17.1

Patch Changes

  • Fixed streamed tool results being replayed when observational memory runs mid-stream. (#15701)
    Fixed observational memory markers being saved as separate empty assistant messages.

@mastra/modal@0.2.0

Minor Changes

  • Added @mastra/modal — Modal cloud sandbox provider for Mastra workspaces. (#14486)

    Use ModalSandbox to run commands in an isolated Modal environment with pause/resume support:

    import { Workspace } from '@mastra/core/workspace';
    import { ModalSandbox } from '@mastra/modal';
    
    const workspace = new Workspace({
      sandbox: new ModalSandbox({
        tokenId: process.env.MODAL_TOKEN_ID!,
        tokenSecret: process.env.MODAL_TOKEN_SECRET!,
      }),
    });

Patch Changes

@mastra/mongodb@1.7.3

Patch Changes

  • Replace uuid with @lukeed/uuid and node:crypto (#15691)

@mastra/s3@0.4.1

Patch Changes

  • Fix toKey() to resolve "." and "./" as the root path (#14824)

    Both GCSFilesystem and S3Filesystem produced invalid object keys when called with path: "." (e.g. prefix/. instead of prefix/). Since the built-in mastra_workspace_list_files tool and Mastra Studio both default to path: ".", workspace directory listings returned empty results when backed by GCS or S3.

    toKey() now normalises "." and "./" to empty string before prepending the prefix, matching the existing behaviour of "/". Dotfiles like .env or .gitignore are unaffected.

@mastra/s3vectors@1.0.4

Patch Changes

  • Replace uuid with @lukeed/uuid and node:crypto (#15691)

@mastra/server@1.28.0

Patch Changes

  • Fixed non-Zod Standard Schema types (e.g. ArkType) being incorrectly called as lazy getters in resolveLazySchema, which caused Studio UI tool forms to receive validation errors instead of the actual JSON Schema (#15670)

  • Fix: Public origin resolution for AWS ALB deployments (#15666)

    Implement cascading header resolution in getPublicOrigin() to properly handle:

    • X-Forwarded-Host (traditional reverse proxies) → always HTTPS
    • Host header (AWS ALB with Preserve Host Header) → respect X-Forwarded-Proto or default HTTPS
    • request.url (local development) → fallback

    Fixes OAuth callback URLs being resolved to http:// instead of https:// when deployed behind AWS ALB with Preserve Host Header enabled.

@mastra/stagehand@0.2.1

Patch Changes

  • Standardize headless default to true across all browser providers. Each provider now resolves headless once in its constructor and passes it to the thread manager via the base class getter, removing duplicate fallback logic. (#15696)

Other updated packages

The following packages were updated with dependency changes only:

Don't miss a new mastra release

NewReleases is sending notifications on new releases.